-custom: ['https://spi-inc.org/projects/systemd/', 'https://www.paypal.com/donate/?token=fBGzXDOyIGobZH3oEhYQlYlA61OMRXVnF9XXQqNNehRs-nliAU5XxozIh9z-hlmE-xXC-m']
+custom: ['https://spi-inc.org/projects/systemd/']
+---
+# vi: ts=2 sw=2 et:
+
extraction:
cpp:
prepare:
packages:
- - python3-pip
- - python3-setuptools
- - python3-wheel
- after_prepare:
- - pip3 install meson
- - export PATH="$HOME/.local/bin/:$PATH"
+ - libpwquality-dev
+ - libfdisk-dev
+ - libp11-kit-dev
+ - libssl-dev
python:
python_setup:
version: 3
Daniel Rusek <mail@asciiwolf.com>
Daniel Stekloff <dsteklof@us.ibm.com>
Daniel Șerbănescu <dasj19@users.noreply.github.com>
+Dann Frazier <dann.frazier@canonical.com>
Dave Reisner <dreisner@archlinux.org> <d@falconindy.com>
David Zeuthen <david@fubar.dk>
David Zeuthen <david@fubar.dk> <davidz@redhat.com>
Stuart McLaren <stuart.mclaren@hp.com>
Susant Sahani <ssahani@gmail.com> <145210+ssahani@users.noreply.github.com>
Susant Sahani <ssahani@gmail.com> <susant@redhat.com>
+Sylvain Plantefeve <sylvain.plantefeve@gmail.com>
Sébastien Bacher <seb128@ubuntu.com>
Tanu Kaskinen <TanuKaskinen@web>
Ted Ts'o <tytso@mit.edu>
Release=31
[Output]
-Format=raw_btrfs
+Format=gpt_ext4
Bootable=yes
-KernelCommandLine=printk.devkmsg=on
[Partitions]
RootSize=3G
gcc
gettext
git
+ glibc-minimal-langpack
gnu-efi
gnu-efi-devel
gnutls-devel
libblkid-devel
libcap-devel
libcurl-devel
+ libfdisk-devel
libgcrypt-devel
libidn2-devel
libmicrohttpd-devel
libmount-devel
+ libpwquality-devel
libseccomp-devel
libselinux-devel
- libtool
libxkbcommon-devel
libxslt
lz4
lz4-devel
m4
meson
+ openssl-devel
+ p11-kit-devel
pam-devel
pcre2-devel
pkgconfig
python3-lxml
qrencode-devel
tree
+ valgrind-devel
xz-devel
Packages=
+ coreutils
+ cryptsetup-libs
+ kmod-libs
+ e2fsprogs
libidn2
+ libseccomp
+ procps-ng
+ util-linux
BuildDirectory=mkosi.builddir
Cache=mkosi.cache
systemd System and Service Manager
+CHANGES WITH 245:
+
+ * A new tool "systemd-repart" has been added, that operates as an
+ idempotent declarative repartitioner for GPT partition tables.
+ Specifically, a set of partitions that must or may exist can be
+ configured via drop-in files, and during every boot the partition
+ table on disk is compared with these files, creating missing
+ partitions or growing existing ones based on configurable relative
+ and absolute size constraints. The tool is strictly incremental,
+ i.e. does not delete, shrink or move partitions, but only adds and
+ grows them. The primary use-case is OS images that ship in minimized
+ form, that on first boot are grown to the size of the underlying
+ block device or augmented with additional partitions. For example,
+ the root partition could be extended to cover the whole disk, or a
+ swap or /home partitions could be added on first boot. It can also be
+ used for systems that use an A/B update scheme but ship images with
+ just the A partition, with B added on first boot. The tool is
+ primarily intended to be run in the initrd, shortly before
+ transitioning into the host OS, but can also be run after the
+ transition took place. It automatically discovers the disk backing
+ the root file system, and should hence not require any additional
+ configuration besides the partition definition drop-ins. If no
+ configuration drop-ins are present, no action is taken.
+
+ * A new component "userdb" has been added, along with a small daemon
+ "systemd-userdb.service" and a client tool "userdbctl". The framework
+ allows defining rich user and group records in a JSON format,
+ extending on the classic "struct passwd" and "struct group"
+ structures. Various components in systemd have been updated to
+ process records in this format, including systemd-logind and
+ pam-systemd. The user records are intended to be extensible, and
+ allow setting various resource management, security and runtime
+ parameters that shall be applied to processes and sessions of the
+ user as they log in. This facility is intended to allow associating
+ such metadata directly with user/group records so that they can be
+ produced, extended and consumed in unified form. We hope that
+ eventually frameworks such as sssd will generate records this way, so
+ that for the first time resource management and various other
+ per-user settings can be configured in LDAP directories and then
+ provided to systemd (specifically to systemd-logind and pam-system)
+ to apply on login. For further details see:
+
+ https://systemd.io/USER_RECORD
+ https://systemd.io/GROUP_RECORD
+ https://systemd.io/USER_GROUP_API
+
+ * A small new service systemd-homed.service has been added, that may be
+ used to securely manage home directories with built-in encryption.
+ The complete user record data is unified with the home directory,
+ thus making home directories naturally migratable. Its primary
+ back-end is based on LUKS volumes, but fscrypt, plain directories,
+ and other storage schemes are also supported. This solves a couple of
+ problems we saw with traditional ways to manage home directories, in
+ particular when it comes to encryption. For further discussion of
+ this, see the video of Lennart's talk at AllSystemsGo! 2019:
+
+ https://media.ccc.de/v/ASG2019-164-reinventing-home-directories
+
+ For further details about the format and expectations on home
+ directories this new daemon makes, see:
+
+ https://systemd.io/HOME_DIRECTORY
+
+ * systemd-journald is now multi-instantiable. In addition to the main
+ instance systemd-journald.service there's now a template unit
+ systemd-journald@.service, with each instance defining a new named
+ log 'namespace' (whose name is specified via the instance part of the
+ unit name). A new unit file setting LogNamespace= has been added,
+ taking such a namespace name, that assigns services to the specified
+ log namespaces. As each log namespace is serviced by its own
+ independent journal daemon, this functionality may be used to improve
+ performance and increase isolation of applications, at the price of
+ losing global message ordering. Each instance of journald has a
+ separate set of configuration files, with possibly different disk
+ usage limitations and other settings.
+
+ journalctl now takes a new option --namespace= to show logs from a
+ specific log namespace. The sd-journal.h API gained
+ sd_journal_open_namespace() for opening the log stream of a specific
+ log namespace. systemd-journald also gained the ability to exit on
+ idle, which is useful in the context of log namespaces, as this means
+ log daemons for log namespaces can be activated automatically on
+ demand and will stop automatically when no longer used, minimizing
+ resource usage.
+
+ * When systemd-tmpfiles copies a file tree using the 'C' line type it
+ will now label every copied file according to the SELinux database.
+
+ * When systemd/PID 1 detects it is used in the initrd it will now boot
+ into initrd.target rather than default.target by default. This should
+ make it simpler to build initrds with systemd as for many cases the
+ only difference between a host OS image and an initrd image now is
+ the presence of the /etc/initrd-release file.
+
+ * A new kernel command line option systemd.cpu_affinity= is now
+ understood. It's equivalent to the CPUAffinity= option in
+ /etc/systemd/system.conf and allows setting the CPU mask for PID 1
+ itself and the default for all other processes.
+
+ * When systemd/PID 1 is reloaded (with systemctl daemon-reload or
+ equivalent), the SELinux database is now reloaded, ensuring that
+ sockets and other file system objects are generated taking the new
+ database into account.
+
+ * systemd/PID 1 accepts a new "systemd.show-status=error" setting, and
+ "quiet" has been changed to imply that instead of
+ "systemd.show-status=auto". In this mode, only messages about errors
+ and significant delays in boot are shown on the console.
+
+ * The sd-event.h API gained native support for the new Linux "pidfd"
+ concept. This permits watching processes using file descriptors
+ instead of PID numbers, which fixes a number of races and makes
+ process supervision more robust and efficient. All of systemd's
+ components will now use pidfds if the kernel supports it for process
+ watching, with the exception of PID 1 itself, unfortunately. We hope
+ to move PID 1 to exclusively using pidfds too eventually, but this
+ requires some more kernel work first. (Background: PID 1 watches
+ processes using waitid() with the P_ALL flag, and that does not play
+ together nicely with pidfds yet.)
+
+ * Closely related to this, the sd-event.h API gained two new calls
+ sd_event_source_send_child_signal() (for sending a signal to a
+ watched process) and sd_event_source_get_child_process_own() (for
+ marking a process so that it is killed automatically whenever the
+ event source watching it is freed).
+
+ * systemd-networkd gained support for configuring Token Bucket Filter
+ (TBF) parameters in its qdisc configuration support. Similarly,
+ support for Stochastic Fairness Queuing (SFQ), Controlled-Delay
+ Active Queue Management (CoDel), and Fair Queue (FQ) has been added.
+
+ * systemd-networkd gained support for Intermediate Functional Block
+ (IFB) network devices.
+
+ * systemd-networkd gained support for configuring multi-path IP routes,
+ using the new MultiPathRoute= setting in the [Route] section.
+
+ * systemd-networkd's DHCPv4 client has been updated to support a new
+ SendDecline= option. If enabled, duplicate address detection is done
+ after a DHCP offer is received from the server. If a conflict is
+ detected, the address is declined. The DHCPv4 client also gained
+ support for a new RouteMTUBytes= setting that allows to configure the
+ MTU size to be used for routes generated from DHCPv4 leases.
+
+ * The PrefixRoute= setting in systemd-networkd's [Address] section of
+ .network files has been deprecated, and replaced by AddPrefixRoute=,
+ with its sense inverted.
+
+ * The Gateway= setting of [Route] sections of .network files gained
+ support for a special new value "_dhcp". If set, the configured
+ static route uses the gateway host configured via DHCP.
+
+ * New User= and SuppressPrefixLength= settings have been implemented
+ for the [RoutingPolicyRule] section of .network files to configure
+ source routing based on UID ranges and prefix length, respectively.
+
+ * sd-bus gained a new API call sd_bus_message_sensitive() that marks a
+ D-Bus message object as "sensitive". Those objects are erased from
+ memory when they are freed. This concept is intended to be used for
+ messages that contain security sensitive data. A new flag
+ SD_BUS_VTABLE_SENSITIVE has been introduced as well to mark methods
+ in sd-bus vtables, causing any incoming and outgoing messages of
+ those methods to be implicitly marked as "sensitive".
+
+ * sd-bus gained a new API call sd_bus_message_dump() for dumping the
+ contents of a message (or parts thereof) to standard output for
+ debugging purposes.
+
+ * systemd-sysusers gained support for creating users with the primary
+ group named differently than the user.
+
+ * systemd-resolved's DNS-over-TLS support gained SNI validation.
+
+ * systemd-growfs (i.e. the x-systemd.growfs mount option in /etc/fstab)
+ gained support for growing XFS partitions. Previously it supported
+ only ext4 and btrfs partitions.
+
+ * The support for /etc/crypttab gained a new x-initrd.attach option. If
+ set, the specified encrypted volume is unlocked already in the
+ initrd. This concept corresponds to the x-initrd.mount option in
+ /etc/fstab.
+
+ * systemd-cryptsetup gained native support for unlocking encrypted
+ volumes utilizing PKCS#11 smartcards, i.e. for example to bind
+ encryption of volumes to YubiKeys. This is exposed in the new
+ pkcs11-uri= option in /etc/crypttab.
+
+ * The /etc/fstab support in systemd now supports two new mount options
+ x-systemd.{required,wanted}-by=, for explicitly configuring the units
+ that the specified mount shall be pulled in by, in place of
+ the usual local-fs.target/remote-fs.target.
+
+ * The https://systemd.io/ web site has been relaunched, directly
+ populated with most of the documentation included in the systemd
+ repository. systemd also acquired a new logo, thanks to Tobias
+ Bernard.
+
+ * systemd-udevd gained support for managing "alternative" network
+ interface names, as supported by new Linux kernels. For the first
+ time this permits assigning multiple (and longer!) names to a network
+ interface. systemd-udevd will now by default assign the names
+ generated via all supported naming schemes to each interface. This
+ may be further tweaked with .link files and the AlternativeName= and
+ AlternativeNamesPolicy= settings. Other components of systemd have
+ been updated to support the new alternative names wherever
+ appropriate. For example, systemd-nspawn will now generate
+ alternative interface names for the host-facing side of container
+ veth links based on the full container name without truncation.
+
+ * systemd-nspawn interface naming logic has been updated in another way
+ too: if the main interface name (i.e. as opposed to new-style
+ "alternative" names) based on the container name is truncated, a
+ simple hashing scheme is used to give different interface names to
+ multiple containers whose names all begin with the same prefix. Since
+ this changes the primary interface names pointing to containers if
+ truncation happens, the old scheme may still be requested by
+ selecting an older naming scheme, via the net.naming-scheme= kernel
+ command line option.
+
+ * PrivateUsers= in service files now works in services run by the
+ systemd --user per-user instance of the service manager.
+
+ * A new per-service sandboxing option ProtectClock= has been added that
+ locks down write access to the system clock. It takes away device
+ node access to /dev/rtc as well as the system calls that set the
+ system clock and the CAP_SYS_TIME and CAP_WAKE_ALARM capabilities.
+ Note that this option does not affect access to auxiliary services
+ that allow changing the clock, for example access to
+ systemd-timedated.
+
+ * The systemd-id128 tool gained a new "show" verb for listing or
+ resolving a number of well-known UUIDs/128bit IDs, currently mostly
+ GPT partition table types.
+
+ * The Discoverable Partitions Specification has been updated to support
+ /var and /var/tmp partition discovery. Support for this has been
+ added to systemd-gpt-auto-generator. For details see:
+
+ https://systemd.io/DISCOVERABLE_PARTITIONS
+
+ * "systemctl list-unit-files" has been updated to show a new column
+ with the suggested enablement state based on the vendor preset files
+ for the respective units.
+
+ * "systemctl" gained a new option "--with-dependencies". If specified
+ commands such as "systemctl status" or "systemctl cat" will now show
+ all specified units along with all units they depend on.
+
+ * networkctl gained support for showing per-interface logs in its
+ "status" output.
+
+ * systemd-networkd-wait-online gained support for specifying the maximum
+ operational state to wait for, and to wait for interfaces to
+ disappear.
+
+ * The [Match] section of .link and .network files now supports a new
+ option PermanentMACAddress= which may be used to check against the
+ permanent MAC address of a network device even if a randomized MAC
+ address is used.
+
+ * The [TrafficControlQueueingDiscipline] section in .network files has
+ been renamed to [NetworkEmulator] with the "NetworkEmulator" prefix
+ dropped from the individual setting names.
+
+ * Any .link and .network files that have an empty [Match] section (this
+ also includes empty and commented-out files) will now be
+ rejected. systemd-udev and systemd-networkd started warning about
+ such files in version 243.
+
+ * systemd-logind will now validate access to the operation of changing
+ the virtual terminal via a PolicyKit action. By default, only users
+ with at least one session on a local VT are granted permission.
+
+ * When systemd sets up PAM sessions that invoked service processes
+ shall run in, the pam_setcred() API is now invoked, thus permitting
+ PAM modules to set additional credentials for the processes.
+
+ * portablectl attach/detach verbs now accept --now and --enable options
+ to combine attachment with enablement and invocation, or detachment
+ with stopping and disablement.
+
+ Contributions from: AJ Bagwell, Alin Popa, Andreas Rammhold, Anita
+ Zhang, Ansgar Burchardt, Antonio Russo, Arian van Putten, Ashley Davis,
+ Balint Reczey, Bart Willems, Bastien Nocera, Benjamin Dahlhoff, Charles
+ (Chas) Williams, cheese1, Chris Down, Chris Murphy, Christian Ehrhardt,
+ Christian Göttsche, cvoinf, Daan De Meyer, Daniele Medri, Daniel Rusek,
+ Daniel Shahaf, Dann Frazier, Dan Streetman, Dariusz Gadomski, David
+ Michael, Dimitri John Ledkov, Emmanuel Bourg, Evgeny Vereshchagin,
+ ezst036, Felipe Sateler, Filipe Brandenburger, Florian Klink, Franck
+ Bui, Fran Dieguez, Frantisek Sumsal, Greg "GothAck" Miell, Guilhem
+ Lettron, Guillaume Douézan-Grard, Hans de Goede, HATAYAMA Daisuke, Iain
+ Lane, James Buren, Jan Alexander Steffens (heftig), Jérémy Rosen, Jin
+ Park, Jun'ichi Nomura, Kai Krakow, Kevin Kuehler, Kevin P. Fleming,
+ Lennart Poettering, Leonid Bloch, Leonid Evdokimov, lothrond, Luca
+ Boccassi, Lukas K, Lynn Kirby, Mario Limonciello, Mark Deneen, Matthew
+ Leeds, Michael Biebl, Michal Koutný, Michal Sekletár, Mike Auty, Mike
+ Gilbert, mtron, nabijaczleweli, Naïm Favier, Nate Jones, Norbert Lange,
+ Oliver Giles, Paul Davey, Paul Menzel, Peter Hutterer, Piotr Drąg, Rafa
+ Couto, Raphael, rhn, Robert Scheck, Rocka, Romain Naour, Ryan Attard,
+ Sascha Dewald, Shengjing Zhu, Slava Kardakov, Spencer Michaels, Sylvain
+ Plantefeve, Stanislav Angelovič, Susant Sahani, Thomas Haller, Thomas
+ Schmitt, Timo Schlüßler, Timo Wilken, Tobias Bernard, Tobias Klauser,
+ Tobias Stoeckmann, Topi Miettinen, tsia, WataruMatsuoka, Wieland
+ Hoffmann, Wilhelm Schuster, Will Fleming, xduugu, Yong Cong Sin, Yuri
+ Chornoivan, Yu Watanabe, Zach Smith, Zbigniew Jędrzejewski-Szmek, Zeyu
+ DONG
+
+ – Warsaw, 2020-03-06
+
CHANGES WITH 244:
* Support for the cpuset cgroups v2 controller has been added.
Contributions from: Aaron Barany, Adrian Bunk, Alan Jenkins, Albrecht
Lohofener, Andrej Valek, Anita Zhang, Arian van Putten, Balint Reczey,
Bastien Nocera, Ben Boeckel, Benjamin Robin, camoz, Chen Qi, Chris
- Chiu, Chris Down, Christian Kellner, Clinton Roy, Connor Reeder, Daniel
- Black, Daniele Medri, Dan Streetman, Dave Reisner, Dave Ross, David
- Art, David Tardon, Debarshi Ray, Dimitri John Ledkov, Dominick Grift,
- Donald Buczek, Douglas Christman, Eric DeVolder, EtherGraf, Evgeny
- Vereshchagin, Feldwor, Felix Riemann, Florian Dollinger, Francesco
- Pennica, Franck Bui, Frantisek Sumsal, Franz Pletz, frederik, Hans
- de Goede, Iago López Galeiras, Insun Pyo, Ivan Shapovalov, Iwan Timmer,
- Jack, Jakob Unterwurzacher, Jan Chren, Jan Klötzke, Jan Losinski, Jan
- Pokorný, Jan Synacek, Jan-Michael Brummer, Jeka Pats, Jeremy Soller,
- Jérémy Rosen, Jiri Pirko, Joe Lin, Joerg Behrmann, Joe Richey, Jóhann
- B. Guðmundsson, Johannes Christ, Johannes Schmitz, Jonathan Rouleau,
- Jorge Niedbalski, Kai Krakow, Kai Lüke, Karel Zak, Kashyap Chamarthy,
+ Chiu, Chris Down, Christian Göttsche, Christian Kellner, Clinton Roy,
+ Connor Reeder, Daniel Black, Daniel Lublin, Daniele Medri, Dan
+ Streetman, Dave Reisner, Dave Ross, David Art, David Tardon, Debarshi
+ Ray, Dimitri John Ledkov, Dominick Grift, Donald Buczek, Douglas
+ Christman, Eric DeVolder, EtherGraf, Evgeny Vereshchagin, Feldwor,
+ Felix Riemann, Florian Dollinger, Francesco Pennica, Franck Bui,
+ Frantisek Sumsal, Franz Pletz, frederik, Hans de Goede, Iago López
+ Galeiras, Insun Pyo, Ivan Shapovalov, Iwan Timmer, Jack, Jakob
+ Unterwurzacher, Jan Chren, Jan Klötzke, Jan Losinski, Jan Pokorný, Jan
+ Synacek, Jan-Michael Brummer, Jeka Pats, Jeremy Soller, Jérémy Rosen,
+ Jiri Pirko, Joe Lin, Joerg Behrmann, Joe Richey, Jóhann B. Guðmundsson,
+ Johannes Christ, Johannes Schmitz, Jonathan Rouleau, Jorge Niedbalski,
+ Jörg Thalheim, Kai Krakow, Kai Lüke, Karel Zak, Kashyap Chamarthy,
Krayushkin Konstantin, Lennart Poettering, Lubomir Rintel, Luca
Boccassi, Luís Ferreira, Marc-André Lureau, Markus Felten, Martin Pitt,
Matthew Leeds, Mattias Jernberg, Michael Biebl, Michael Olbrich,
Michael Prokop, Michael Stapelberg, Michael Zhivich, Michal Koutný,
Michal Sekletar, Mike Gilbert, Milan Broz, Miroslav Lichvar, mpe85,
Mr-Foo, Network Silence, Oliver Harley, pan93412, Paul Menzel, pEJipE,
- Peter A. Bigot, Philip Withnall, Piotr Drąg, Rafael Fontenelle, Roberto
- Santalla, Ronan Pigott, root, RussianNeuroMancer, Sebastian Jennen,
- shinygold, Shreyas Behera, Simon Schricker, Susant Sahani, Thadeu Lima
- de Souza Cascardo, Theo Ouzhinski, Thiebaud Weksteen, Thomas Haller,
- Thomas Weißschuh, Tomas Mraz, Tommi Rantala, Topi Miettinen, VD-Lycos,
- ven, Wieland Hoffmann, William A. Kennington III, William Wold, Xi
- Ruoyao, Yuri Chornoivan, Yu Watanabe, Zach Smith, Zbigniew
- Jędrzejewski-Szmek, Zhang Xianwei
+ Peter A. Bigot, Philip Withnall, Piotr Drąg, Rafael Fontenelle, Robert
+ Scheck, Roberto Santalla, Ronan Pigott, root, RussianNeuroMancer,
+ Sebastian Jennen, shinygold, Shreyas Behera, Simon Schricker, Susant
+ Sahani, Thadeu Lima de Souza Cascardo, Theo Ouzhinski, Thiebaud
+ Weksteen, Thomas Haller, Thomas Weißschuh, Tomas Mraz, Tommi Rantala,
+ Topi Miettinen, VD-Lycos, ven, Vladimir Yerilov, Wieland Hoffmann,
+ William A. Kennington III, William Wold, Xi Ruoyao, Yuri Chornoivan,
+ Yu Watanabe, Zach Smith, Zbigniew Jędrzejewski-Szmek, Zhang Xianwei
– Camerino, 2019-09-03
* A new fsck.repair= kernel option has been added to control
how fsck shall deal with unclean file systems at boot.
- * The (.ini) configuration file parser will now silently
- ignore sections whose name begins with "X-". This may be
- used to maintain application-specific extension sections in unit
- files.
+ * The (.ini) configuration file parser will now silently ignore
+ sections whose names begin with "X-". This may be used to maintain
+ application-specific extension sections in unit files.
* machined gained a new API to query the IP addresses of
registered containers. "machinectl status" has been updated
Features:
+* cryptsetup/homed: also support FIDO2 HMAC password logic for unlocking
+ devices. (see: https://github.com/mjec/fido2-hmac-secret)
+
+* systemd-gpt-auto should probably set x-systemd.growfs on the mounts it
+ creates
+
+* homed/userdb: distuingish passwords and recovery keys in the records, since
+ we probably want to use different PBKDF algorithms/settings for them:
+ passwords have low entropy but recovery keys should have good entropy key
+ hence we can make them quicker to work.
+
+* bootctl:
+ - teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
+ - teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
+ - make it operate on loopback files, dissecting enough to find ESP to operate on
+
+* by default, in systemd --user service bump the OOMAdjust to 100, as privs
+ allow so that systemd survives
+
+* honour specifiers in unit files that resolve to some very basic
+ /etc/os-release data, such as ID, VERSION_ID, BUILD_ID, VARIANT_ID.
+
+* cryptsetup: allow encoding key directly in /etc/crypttab, maybe with a
+ "base64:" prefix. Useful in particular for pkcs11 mode.
+
+* cryptsetup: reimplement the mkswap/mke2fs in cryptsetup-generator to use
+ systemd-makefs.service instead.
+
* socket units: allow creating a udev monitor socket with ListenDevices= or so,
with matches, then actviate app thorugh that passing socket oveer
+* unify on openssl:
+ - port sd_id128_get_machine_app_specific() over from khash
+ - port resolved over from libgcrypt (DNSSEC code)
+ - port journald + fsprg over from libgcrypt
+ - port importd over from libgcrypt
+ - when that's done: kill khash.c
+ - when that's done: kill gnutls support in resolved
+
* kill zenata, all hail weblate?
-* move discoverable partitions spec into markdown and our tree
+* when we resize disks (homed?) always round up to 4K sectors, not 512K
+
+* add growvol and makevol options for /etc/crypttab, similar to
+ x-systemd.growfs and x-systemd-makefs.
+
+* hook up the TPM to /etc/crypttab, with a new option that is similar to the
+ new PKCS#11 option in crypttab, and allows unlocking a LUKS volume via a key
+ unsealed from the TPM. Optionally, if TPM is not available fall back to
+ TPM-less mode, and set up linear DM mapping instead (inspired by kpartx), so
+ that the device paths stay the same, regardless if crypto is used or not.
+
+* systemd-repart: by default generate minimized partition tables (i.e. tables
+ that only covere the space actually used, excluding any free space at the
+ end), in order to maximize dd'ability. Requires libfdisk work, see
+ https://github.com/karelzak/util-linux/issues/907
+
+* systemd-repart: optionally, allow specifiying a path to initialize new
+ partitions from, i.e. an fs image file or a source device node. This would
+ then turn systemd-repart into a simple installer: with a few .repart files
+ you could replicate the host system on another device. a full installer would
+ then be: "systemd-repart /dev/sda && bootctl install /dev/sda &&
+ systemd-firstboot --image= …"
+
+* systemd-repart: MBR partition table support. Care needs to be taken regarding
+ Type=, so that partition definitions can sanely apply to both the GPT and the
+ MBR case. Idea: accept syntax "Type=gpt:home mbr:0x83" for setting the types
+ for the two partition types explicitly. And provide an internal mapping so
+ that "Type=linux-generic" maps to the right types for both partition tables
+ automatically.
+
+* systemd-repart: allow sizing partitions as factor of available RAM, so that
+ we can reasonably size swap partitions for hibernation.
+
+* systemd-repart: allow running mkfs before making partitions pop up +
+ encryption via LUKS to allow booting into an empty root with only /usr mounted in
+
+* systemd-repart: allow managing the gpt read-only partition flag + auto-mount flag
+
+* systemd-repart: allow disabling growing of specific partitions, or making
+ them (think ESP: we don't ever want to grow it, since we cannot resize vfat)
+
+* systemd-repart: add specifier expansion, add especifier that refers to root
+ device node of current system, /usr device node, and matching verity, so that
+ an installer can be made a "copy" installer of the booted OS
+
+* systemd-repart: make it a static checker during early boot for existence and
+ absence of other partitions for trusted boot environments
+
+* systemd-repart: when no configuration is found, exit early do not check
+ partition table, so that it is safe to run in the initrd on any system
+
+* systemd-repart: allow config of partition uuid
+
+* userdb: allow username prefix searches in varlink API
+
+* userdb: allow existence checks
+
+* pid: activation by journal search expression
+
+* when switching root from initrd to host, set the machine_id env var so that
+ if the host has no machine ID set yet we continue to use the random one the
+ initrd had set.
* sd-event: add native support for P_ALL waitid() watching, then move PID 1 to
it fo reaping assigned but unknown children. This needs to some special care
shouldn't operate in a volatile mode unless we got told so from a trusted
source.
-* look for /var/tmp automatically via gpt auto discovery
-
* figure out automatic partition discovery when combining writable root dir
with immutable /usr
right) become genuine first class citizens, and we gain automatic, sane JSON
output for them.
-* dissector: invoke fsck on the file systems we encounter, after all ext4 is
- still pretty popular (and we mount the ESP too with it after all, which is
- fat)
-
* systemd-firstboot: teach it dissector magic, so that you can point it to some
disk image and it will just set everything in it all behind the scenes.
user@.service, which returns the XDG_RUNTIME_DIR value, and make this
behaviour selectable via pam module option.
+* homed:
+ - when user tries to log into record signed by unrecognized key, automatically add key to our chain after polkit auth
+ - hook up machined/nspawn users with a varlink user query interface
+ - rollback when resize fails mid-operation
+ - GNOME's side for forget key on suspend (requires rework so that lock screen runs outside of uid)
+ - resize on login?
+ - fstrim on logout?
+ - shrink fs on logout?
+ - update LUKS password on login if we find there's a password that unlocks the JSON record but not the LUKS device.
+ - create on activate?
+ - properties: icon url?, preferred session type?, administrator bool (which translates to 'wheel' membership)?, address?, telephone?, vcard?, samba stuff?, parental controls?
+ - communicate clearly when usb stick is safe to remove. probably involves
+ beefing up logind to make pam session close hook synchronous and wait until
+ systemd --user is shut down.
+ - logind: maybe keep a "busy fd" as long as there's a non-released session around or the user@.service
+ - maybe make automatic, read-only, time-based reflink-copies of LUKS disk images (think: time machine)
+ - distuingish destroy / remove (i.e. currently we can unregister a user, unregister+remove their home directory, but not just remove their home directory)
+ - in systemd's PAMName= logic: query passwords with ssh-askpassword, so that we can make "loginctl set-linger" mode work
+ - fingerprint authentication, pattern authentication, …
+ - make sure "classic" user records can also be managed by homed
+ - description field for groups
+ - make size of $XDG_RUNTIME_DIR configurable in user record
+ - reuse pwquality magic in firstboot
+ - query password from kernel keyring first
+ - update even if record is "absent"
+ - add a "access mode" + "fstype" field to the "status" section of json identity records reflecting the actually used access mode and fstype, even on non-luks backends
+ - move acct mgmt stuff from pam_systemd_home to pam_systemd?
+ - when "homectl --pkcs11-token-uri=" is used, synthesize ssh-authorized-keys records for all keys we have private keys on the stick for
+ - make slice for users configurable (requires logind rework)
+ - logind: populate auto-login list bus property from PKCS#11 token
+ - when determining state of a LUKS home directory, check DM suspended sysfs file
+
* introduce a new per-process uuid, similar to the boot id, the machine id, the
invocation id, that is derived from process creds, specifically a hashed
combination of AT_RANDOM + getpid() + the starttime from
* introduce per-unit (i.e. per-slice, per-service) journal log size limits.
-* optionally, if a per-partition GPT flag is set for the root/home/… partitions
- format the partition on next boot and unset the flag, in order to implement
- factory reset. also, add a second flag that simply indicates whether such a
- scheme is supported. then, add a tool (or maybe beef up systemd-dissect) to
- show state of these flags, and optionally trigger such a factory reset on
- next boot by setting the flag.
-
* sd-boot: automatically load EFI modules from some drop-in dir, so that people
can add in file system drivers and such
* the a-posteriori stopping of units bound to units that disappeared logic
should be reworked: there should be a queue of units, and we should only
- enqeue stop jobs from a defer event that processes queue instead of
+ enqueue stop jobs from a defer event that processes queue instead of
right-away when we find a unit that is bound to one that doesn't exist
anymore. (similar to how the stop-unneeded queue has been reworked the same
way)
yogas can be recognized as "convertible" too, even if they predate the DMI
"convertible" form factor
-* Maybe add a small tool invoked early at boot, that adds in or resizes
- partitions automatically, to be used when the media used is actually larger
- than the image written onto it is.
-
* Maybe add PrivatePIDs= as new unit setting, and do minimal PID namespacing
after all. Be strict however, only support the equivalent of nspawn's
--as-pid2 switch, and sanely proxy sd_notify() messages dropping stuff such
"systemd-gdb" for attaching to the start-up of any system service in its
natural habitat.
-* maybe introduce gpt auto discovery for /var/tmp?
-
-* maybe add gpt-partition-based user management: each user gets his own
- LUKS-encrypted GPT partition with a new GPT type. A small nss module
- enumerates users via udev partition enumeration. UIDs are assigned in a fixed
- way: the partition index is added as offset to some fixed base uid. User name
- is stored in GPT partition name. A PAM module authenticates the user via the
- LUKS partition password. Benefits: strong per-user security, compatibility
- with stateless/read-only/verity-enabled root. (other idea: do this based on
- loopback files in /home, without GPT involvement)
-
-* gpt-auto logic: introduce support for discovering /var matching an image. For
- that, use a partition type UUID that is hashed from the OS name (as encoded
- in /etc/os-release), the architecture, and 4 new bits from the gpt flags
- field of the root partition. This way can easily support multiple OS
- installations on the same GPT partition table, without problems with
- unmatched /var partitions.
-
* gpt-auto logic: related to the above, maybe support a "secondary" root
partition, that is mounted to / and is writable, and where the actual root's
/usr is mounted into.
* merge ~/.local/share and ~/.local/lib into one similar /usr/lib and /usr/share....
-* systemd.show_status= should probably have a mode where only failed
- units are shown.
-
* add systemd.abort_on_kill or some other such flag to send SIGABRT instead of SIGKILL
(throughout the codebase, not only PID1)
- journald: when we drop syslog messages because the syslog socket is
full, make sure to write how many messages are lost as first thing
to syslog when it works again.
- - change systemd-journal-flush into a service that stays around during
- boot, and causes the journal to be moved back to /run on shutdown,
- so that we do not keep /var busy. This needs to happen synchronously,
- hence doing this via signals is not going to work.
- - optionally support running journald from the command line for testing purposes in external projects
- journald: allow per-priority and per-service retention times when rotating/vacuuming
- journald: make use of uid-range.h to managed uid ranges to split
journals in.
označava da je komunikacijski kanal mijenjan.
-- 4d4408cfd0d144859184d1e65d7c8a65
-Subject: DNSSEC pouzdano sidro je opozvano
+Subject: DNSSEC pouzdano sidrište je opozvano
Defined-By: systemd
Support: %SUPPORT_URL%
Documentation: man:systemd-resolved.service(8)
-A DNSSEC trust anchor has been revoked. A new trust anchor has to be
-configured, or the operating system needs to be updated, to provide an updated
-DNSSEC trust anchor.
+DNSSEC pouzdano sidrište je opozvano. Novo pouzdano sidrište mora biti
+podešeno, ili operativni sustav mora biti nadopunjen kako bi omogućio nadopunjeno
+DNSSEC pouzdano sidrište.
-#!/bin/bash -e
+#!/usr/bin/env bash
+set -e
cflags=CFLAGS="$CFLAGS"
cxxflags=CXXFLAGS="$CXXFLAGS"
---
-title: The Boot Loader Interface
+title: Boot Loader Interface
category: Booting
layout: default
---
6. If a boot menu entry encapsulates a reboot into EFI firmware setup feature,
it should use the identifier `reboot-to-firmware-setup` (or
`auto-reboot-to-firmware-setup` in case it is automatically discovered).
+
+## Links
+
+[Boot Loader Specification](https://systemd.io/BOOT_LOADER_INTERFACE)<br>
+[Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)<br>
+[systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)<br>
+[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)<br>
+[systemd-gpt-auto-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html)
---
-title: The Boot Loader Specification
+title: Boot Loader Specification
category: Booting
layout: default
---
Everything described below is located on a placeholder file system `$BOOT`. The installer program should pick `$BOOT` according to the following rules:
-* On disks with MBR disk labels
- * If the OS is installed on a disk with MBR disk label, and a partition with the MBR type id of 0xEA already exists it should be used as `$BOOT`.
- * Otherwise, if the OS is installed on a disk with MBR disk label, a new partition with MBR type id of 0xEA shall be created, of a suitable size (let's say 500MB), and it should be used as `$BOOT`.
-* On disks with GPT disk labels
- * If the OS is installed on a disk with GPT disk label, and a partition with the GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172` already exists, it should be used as `$BOOT`.
- * Otherwise, if the OS is installed on a disk with GPT disk label, and an ESP partition (i.e. with the GPT type UID of `c12a7328-f81f-11d2-ba4b-00a0c93ec93b`) already exists and is large enough (let's say 250MB`) and otherwise qualifies, it should be used as `$BOOT`.
- * Otherwise, if the OS is installed on a disk with GPT disk label, and if the ESP partition already exists but is too small, a new suitably sized (let's say 500MB) partition with GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172` shall be created and it should be used as `$BOOT`.
- * Otherwise, if the OS is installed on a disk with GPT disk label, and no ESP partition exists yet, a new suitably sized (let's say 500MB) ESP should be created and should be used as `$BOOT`.
+* On disks with an MBR partition table:
+ * If the OS is installed on a disk with an MBR partition table, and a partition with the type id of 0xEA already exists it should be used as `$BOOT`.
+ * Otherwise, if the OS is installed on a disk with an MBR partition table, a new partition with type id of 0xEA shall be created, of a suitable size (let's say 500MB), and it should be used as `$BOOT`.
+* On disks with GPT (GUID Partition Table)
+ * If the OS is installed on a disk with GPT, and an Extended Boot Loader Partition or XBOOTLDR partition for short, i.e. a partition with GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172`, already exists, it should be used as `$BOOT`.
+ * Otherwise, if the OS is installed on a disk with GPT, and an EFI System Partition or ESP for short, i.e. a partition with GPT type UID of `c12a7328-f81f-11d2-ba4b-00a0c93ec93b`) already exists and is large enough (let's say 250MB) and otherwise qualifies, it should be used as `$BOOT`.
+ * Otherwise, if the OS is installed on a disk with GPT, and if the ESP partition already exists but is too small, a new suitably sized (let's say 500MB) XBOOTLDR partition shall be created and used as `$BOOT`.
+ * Otherwise, if the OS is installed on a disk with GPT, and no ESP partition exists yet, a new suitably sized (let's say 500MB) ESP should be created and used as `$BOOT`.
This placeholder file system shall be determined during _installation time_, and an fstab entry may be created. It should be mounted to either `/boot/` or `/efi/`. Additional locations like `/boot/efi/`, with `/boot/` being a separate file system, might be supported by implementations. This is not recommended because the mounting of `$BOOT` is then dependent on and requires the mounting of the intermediate file system.
shall be considered and displayed. This allows image builders to put together
images that transparently support multiple different architectures.
+Note that the `$BOOT` partition is not supposed to be exclusive territory of
+this specification. This specification only defines semantics of the `/loader/`
+directory inside the file system (see below), but it doesn't intend to define
+ownership of the whole file system exclusively. Boot loaders, firmware, and
+other software implementating this specification may choose to place other
+files and directories in the same file system. For example, boot loaders that
+implement this specification might install their own boot code into the `$BOOT`
+partition. On systems where `$BOOT` is the ESP this is a particularly common
+setup. Implementations of this specification must be able to operate correctly
+if files or directories other than `/loader/` are found in the top level
+directory. Implementations that add their own files or directories to the file
+systems should use well-named directories, to make name collisions between
+multiple users of the file system unlikely.
+
### Type #1 Boot Loader Specification Entries
We define two directories below `$BOOT`:
## Links
+[GUID Partition Table](https://en.wikipedia.org/wiki/GUID_Partition_Table)<br>
+[Boot Loader Interface](https://systemd.io/BOOT_LOADER_INTERFACE)<br>
+[Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)<br>
[systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)<br>
-[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)
+[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)<br>
+[systemd-gpt-auto-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html)
poor implementations of the components interfacing with systemd of current
container managers.
-Before you read on, please make sure you read the low-level [kernel
-documentation about
-cgroup v2](https://www.kernel.org/doc/Documentation/cgroup-v2.txt). This
-documentation then adds in the higher-level view from systemd.
+Before you read on, please make sure you read the low-level kernel
+documentation about the
+[unified cgroup hierarchy](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html).
+This document then adds in the higher-level view from systemd.
This document augments the existing documentation we already have:
---
-title: The systemd Community Conduct Guidelines
+title: systemd Community Conduct Guidelines
category: Contributing
layout: default
---
---
-title: The Container Interface
+title: Container Interface
category: Interfaces
layout: default
---
--- /dev/null
+---
+title: Discoverable Partitions Specification
+category: Concepts
+layout: default
+---
+# The Discoverable Partitions Specification
+
+_TL;DR: Let's automatically discover, mount and enable the root partition,
+`/home/`, `/srv/`, `/var/` and `/var/tmp/` and the swap partitions based on
+GUID Partition Tables (GPT)!_
+
+The GUID Partition Table (GPT) is mandatory on EFI systems. It allows
+identification of partition types with UUIDs. So far Linux has made little use
+of this, and mostly just defined one UUID for file system/data partitions and
+another one for swap partitions. With this specification, we introduce
+additional partition types to enable automatic discovery of partitions and
+their intended mountpoint. This has many benefits:
+
+* OS installers can automatically discover and make sense of partitions of
+ existing Linux installations.
+* The OS can discover and mount the necessary file systems with a non-existing
+ or incomplete `/etc/fstab` file and without the `root=` kernel command line
+ option.
+* Container managers (such as nspawn and libvirt-lxc) can decode and set up
+ file systems contained in GPT disk images automatically and mount them to the
+ right places, thus allowing booting the same, identical images on bare-metal
+ and in Linux containers. This enables true, natural portability of disk
+ images between physical machines and Linux containers.
+* As a help to administrators and users partition manager tools can show more
+ descriptive information about partitions tables.
+
+Note that the OS side of this specification is currently implemented in
+[systemd](http://systemd.io/) 211 and newer in the
+[systemd-auto-gpt-generator(8)](http://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html)
+generator tool. Note that automatic discovery of the root only works if the
+boot loader communicates this information to the OS, by implementing the [Boot
+Loader
+Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
+
+## Defined Partition Type UUIDs
+
+| Partition Type UUID | Name | Allowed File Systems | Explanation |
+|---------------------|------|----------------------|-------------|
+| `44479540-f297-41b2-9af7-d131d5f0458a` | _Root Partition (x86)_ | Any native, optionally in LUKS | On systems with matching architecture, the first partition with this type UUID on the disk containing the active EFI ESP is automatically mounted to the root directory <tt>/</tt>. If the partition is encrypted with LUKS or has dm-verity integrity data (see below), the device mapper file will be named `/dev/mapper/root`. |
+| `4f68bce3-e8cd-4db1-96e7-fbcaf984b709` | _Root Partition (x86-64)_ | ditto | ditto |
+| `69dad710-2ce4-4e3c-b16c-21a1d49abed3` | _Root Partition (32-bit ARM)_ | ditto | ditto |
+| `b921b045-1df0-41c3-af44-4c6f280d3fae` | _Root Partition (64-bit ARM/AArch64)_ | ditto | ditto |
+| `993d8d3d-f80e-4225-855a-9daf8ed7ea97` | _Root Partition (Itanium/IA-64)_ | ditto | ditto |
+| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | On systems with matching architecture, contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128bit of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128bit of it, so that the root partition and its verity partition can be discovered easily, simply by specifying the root hash. |
+| `2c7357ed-ebd2-46d9-aec1-23d437ec2bf5` | _Root Verity Partition (x86-64)_ | ditto | ditto |
+| `7386cdf2-203c-47a9-a498-f2ecce45a2d6` | _Root Verity Partition (32-bit ARM)_ | ditto | ditto |
+| `df3300ce-d69f-4c92-978c-9bfb0f38d820` | _Root Verity Partition (64-bit ARM/AArch64)_ | ditto | ditto |
+| `86ed10d5-b607-45bb-8957-d350f23d0571` | _Root Verity Partition (Itanium/IA-64)_ | ditto | ditto |
+| `933ac7e1-2eb4-4f13-b844-0e14e2aef915` | _Home Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/home/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/home`. |
+| `3b8f8425-20e0-4f3b-907f-1a25a76f98e8` | _Server Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/srv/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/srv`. |
+| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bit of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
+| `7ec6f557-3bc5-4aca-b293-16ef5df639d1` | _Temporary Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/tmp/`. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/tmp`. Note that the intended mount point is indeed `/var/tmp/`, not `/tmp/`. The latter is typically maintained in memory via <tt>tmpfs</tt> and does not require a partition on disk. In some cases it might be desirable to make `/tmp/` persistent too, in which case it is recommended to make it a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID. |
+| `0657fd6d-a4ab-43c4-84e5-0933c84b4f4f` | _Swap_ | Swap | All swap partitions on the disk containing the root partition are automatically enabled. |
+| `c12a7328-f81f-11d2-ba4b-00a0c93ec93b` | _EFI System Partition_ | VFAT | The ESP used for the current boot is automatically mounted to `/efi/` (or `/boot/` as fallback), unless a different partition is mounted there (possibly via `/etc/fstab`, or because the Extended Boot Loader Partition — see below — exists) or the directory is non-empty on the root disk. This partition type is defined by the [UEFI Specification](http://www.uefi.org/specifications). |
+| `bc13c2ff-59e6-4262-a352-b275fd6f7172` | _Extended Boot Loader Partition_ | Typically VFAT | The Extended Boot Loader Partition (XBOOTLDR) used for the current boot is automatically mounted to <tt>/boot/</tt>, unless a different partition is mounted there (possibly via <tt>/etc/fstab</tt>) or the directory is non-empty on the root disk. This partition type is defined by the [Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION). |
+| `0fc63daf-8483-4772-8e79-3d69d8477de4` | _Other Data Partitions_ | Any native, optionally in LUKS | No automatic mounting takes place for other Linux data partitions. This partition type should be used for all partitions that carry Linux file systems. The installer needs to mount them explicitly via entries in <tt>/etc/fstab</tt>. Optionally, these partitions may be encrypted with LUKS. |
+
+Other GPT type IDs might be used on Linux, for example to mark software RAID or
+LVM partitions. The definitions of those GPT types is outside of the scope of
+this specification.
+
+[systemd-id128(1)](http://www.freedesktop.org/software/systemd/man/systemd-i128.html)
+may be used to list those UUIDs.
+
+## Partition Names
+
+For partitions of the types listed above it is recommended to use
+human-friendly, descriptive partition names in the GPT partition table, for
+example "*Home*", "*Server* *Data*", "*Fedora* *Root*" and similar, possibly
+localized.
+
+## Partition Flags
+
+For the root, server data, home, variable data, temporary data and swap
+partitions, the partition flag bit 63 ("*no-auto*") may be used to turn off
+auto-discovery for the specific partition. If set, the partition will not be
+automatically mounted or enabled.
+
+For the root, server data, home, variable data and temporary data partitions,
+the partition flag bit 60 ("*read-only*") may be used to mark a partition for
+read-only mounts only. If set, the partition will be mounted read-only instead
+of read-write. Note that the variable data partition and the temporary data
+partition will generally not be able to serve their purpose if marked
+read-only, since by their very definition they are supposed to be mutable. (The
+home and server data partitions are generally assumed to be mutable as well,
+but the requirement for them is not equally strong.) Because of that, while the
+read-only flag is defined and supported, it's almost never a good idea to
+actually use it for these partitions.
+
+Note that these two flag definitions happen to map nicely to the ones used by
+Microsoft Basic Data Partitions.
+
+## Suggested Mode of Operation
+
+An *installer* that repartitions the hard disk _should_ use the above UUID
+partition types for appropriate partitions it creates.
+
+An *installer* which supports a "manual partitioning" interface _may_ choose to
+pre-populate the interface with swap, `/home/`, `/srv/`, `/var/tmp/` partitions
+of pre-existing Linux installations, identified with the GPT type UUIDs
+above. The installer should not pre-populate such an interface with any
+identified root or `/var/` partition unless the intention is to overwrite an
+existing operating system that might be installed.
+
+An *installer* _may_ omit creating entries in `/etc/fstab` for root, `/home/`,
+`/srv/`, `/var/`, `/var/tmp` and for the swap partitions if they use these UUID
+partition types, and are the first partitions on the disk of each type. If the
+ESP shall be mounted to `/efi/` (or `/boot/`), it may additionally omit
+creating the entry for it in `/etc/fstab`. If an extended boot partition is
+used, or if the EFI partition shall not be mounted to `/efi/` or `/boot/`, it
+_must_ create `/etc/fstab` entries for them. If other partitions are used (for
+example for `/usr/` or `/var/lib/mysql/`), the installer _must_ register these
+in `/etc/fstab`. The `root=` parameter passed to the kernel by the boot loader
+may be omitted if the root partition is the first one on the disk of its type.
+If the root partition is not the first one on the disk, the `root=` parameter
+_must_ be passed to the kernel by the boot loader. An installer that mounts a
+root, `/home/`, `/srv/`, `/var/`, or `/var/tmp/` file system with the partition
+types defined as above which contains a LUKS header _must_ call the device
+mapper device "root", "home", "srv", "var" or "tmp", respectively. This is
+necessary to ensure that the automatic discovery will never result in different
+device mapper names than any static configuration by the installer, thus
+eliminating possible naming conflicts and ambiguities.
+
+An *operating* *system* _should_ automatically discover and mount the first
+root partition that does not have the no-auto flag set (as described above) by
+scanning the disk containing the currently used EFI ESP. It _should_
+automatically discover and mount the first `/home/`, `/srv/`, `/var/`,
+`/var/tmp/` and swap partitions that do not have the no-auto flag set by
+scanning the disk containing the discovered root partition. It should
+automatically discover and mount the partition containing the currently used
+EFI ESP to `/efi/` (or `/boot/` as fallback). It should automatically discover
+and mount the partition containing the currently used Extended Boot Loader
+Partition to `/boot/`. It _should not_ discover or automatically mount
+partitions with other UUID partition types, or partitions located on other
+disks, or partitions with the no-auto flag set. User configuration shall
+always override automatic discovery and mounting. If a root, `/home/`,
+`/srv/`, `/boot/`, `/var/`, `/var/tmp/`, `/efi/`, `/boot/` or swap partition is
+listed in `/etc/fstab` or with `root=` on the kernel command line, it _must_
+take precedence over automatically discovered partitions. If a `/home/`,
+`/srv/`, `/boot/`, `/var/`, `/var/tmp/`, `/efi/` or `/boot/` directory is found
+to be populated already in the root partition, the automatic discovery _must
+not_ mount any discovered file system over it.
+
+A *container* *manager* should automatically discover and mount the root,
+`/home/`, `/srv/`, `/var/`, `/var/tmp/` partitions inside a container disk
+image. It may choose to mount any discovered ESP and/or XBOOOTLDR partition to
+`/efi/` or `/boot/`. It should ignore any swap should they be included in a
+container disk image.
+
+If a btrfs file system is automatically discovered and mounted by the operating
+system/container manager it will be mounted with its *default* subvolume. The
+installer should make sure to set the default subvolume correctly using "btrfs
+subvolume set-default".
+
+## Sharing of File Systems between Installations
+
+If two Linux-based operating systems are installed on the same disk, the scheme
+above suggests that they may share the swap, `/home/`, `/srv/`, `/var/tmp/`,
+ESP, XBOOTLDR. However, they should each have their own root and `/var/`
+partition.
+
+## Frequently Asked Questions
+
+### Why are you taking my `/etc/fstab` away?
+
+We are not. `/etc/fstab` always overrides automatic discovery and is indeed
+mentioned in the specifications. We are simply trying to make the boot and
+installation processes of Linux a bit more robust and self-descriptive.
+
+### Why did you only define the root partition for x86, x86-64, ARM, ARM64, ia64?
+
+The automatic discovery of the root partition is defined to operate on the disk
+containing the current EFI System Partition (ESP). Since EFI only exists on
+x86, x86-64, ia64, and ARM so far, we only defined root partition UUIDs for
+these architectures. Should EFI become more common on other architectures, we
+can define additional UUIDs for them.
+
+### Why define distinct root partition UUIDs for the various architectures?
+
+This allows disk images that may be booted on multiple architectures to use
+discovery of the appropriate root partition on each architecture.
+
+### Doesn't this break multi-boot scenarios?
+
+No, it doesn't. The specification says that installers may not stop creating
+`/etc/fstab` or stop including `root=` on the kernel command line, unless the used
+partitions are the first ones of their type on the disk. Additionally,
+`/etc/fstab` and `root=` both override automatic discovery. Multi-boot is hence
+well supported, since it doesn't change anything for anything but the first
+installation.
+
+That all said, it's not expected that generic installers generally stop setting
+`root=` and creating `/etc/fstab` anyway. The option to drop these configuration
+bits is primarily something for appliance-like devices. However, generic
+installers should *still* set the right GPT partition types for the partitions
+they create so that container managers, partition tools and administrators can
+benefit. Phrased differently, this specification introduces A) the
+*recommendation* to use the newly defined partition types to tag things
+properly and B) the *option* to then drop `root=` and `/etc/fstab`. While we
+advertise A) to *all* installers, we only propose B) for simpler,
+appliance-like installations.
+
+### What partitioning tools will create a DPS-compliant partition table?
+
+As of util-linux 2.25.2, the fdisk tool provides type codes to create the root,
+home, and swap partitions that the DPS expects, but the gdisk tool (version
+0.8.10) and its variants do not support creation of a root file system with a
+matching type code. By default, fdisk will create an old-style MBR, not a GPT,
+so typing 'l' to list partition types will not show the choices that the root
+partition with the correct UUID. You must first create an empty GPT and then
+type 'l' in order for the DPS-compliant type codes to be available.
appropriate path under /run. This variable is also set by the manager when
RuntimeDirectory= is used, see systemd.exec(5).
+* `$SYSTEMD_CRYPT_PREFIX` — if set configures the hash method prefix to use for
+ UNIX crypt() when generating passwords. By default the system's "preferred
+ method" is used, but this can be overridden with this environment
+ variable. Takes a prefix such as `$6$` or `$y$`. (Note that this is only
+ honoured on systems built with libxcrypt and is ignored on systems using
+ glibc's original, internal crypt() implementation.)
+
systemctl:
* `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID1's private D-Bus
--- /dev/null
+---
+title: JSON Group Records
+category: Interfaces
+layout: default
+---
+
+# JSON Group Records
+
+Long story short: JSON Group Records are to `struct group` what [JSON User
+Records](https://systemd.io/USER_RECORD.md) are to `struct passwd`.
+
+Conceptually, much of what applies to JSON user records also applies to JSON
+group records. They also consist of seven sections, with similar properties and
+they carry some identical (or at least very similar) fields.
+
+## Fields in the `regular` section
+
+`groupName` → A string with the UNIX group name. Matches the `gr_name` field of
+UNIX/glibc NSS `struct group`, or the shadow structure `struct sgrp`'s
+`sg_namp` field.
+
+`realm` → The "realm" the group belongs to, conceptually identical to the same
+field of user records. A string in DNS domain name syntax.
+
+`disposition` → The disposition of the group, conceptually identical to the
+same field of user records. A string.
+
+`service` → A string, an identifier for the service managing this group record
+(this field is typically in reverse domain name syntax.)
+
+`lastChangeUSec` → An unsigned 64bit integer, a timestamp (in µs since the UNIX
+epoch 1970) of the last time the group record has been modified. (Covers only
+the `regular`, `perMachine` and `privileged` sections).
+
+`gid` → An unsigned integer in the range 0…4294967295: the numeric UNIX group
+ID (GID) to use for the group. This corresponds to the `gr_gid` field of
+`struct group`.
+
+`members` → An array of strings, listing user names that are members of this
+group. Note that JSON user records also contain a `memberOf` field, or in other
+words a group membership can either be denoted in the JSON user record or in
+the JSON group record, or in both. The list of memberships should be determined
+as the combination of both lists (plus optionally others). If a user is listed
+as member of a group and doesn't exist it should be ignored. This field
+corresponds to the `gr_mem` field of `struct group` and the `sg_mem` field of
+`struct sgrp`.
+
+`administrators` → Similarly, an array of strings, listing user names that
+shall be considered "administrators" of this group. This field corresponds to
+the `sg_adm` field of `struct sgrp`.
+
+`privileged`/`perMachine`/`binding`/`status`/`signature`/`secret` → The
+objects/arrays for the other six group record sections. These are organized the
+same way as for the JSON user records, and have the same semantics.
+
+## Fields in the `privileged` section
+
+The following fields are defined:
+
+`hashedPassword` → An array of strings with UNIX hashed passwords; see the
+matching field for user records for details. This field corresponds to the
+`sg_passwd` field of `struct sgrp` (and `gr_passwd` of `struct group` in a
+way).
+
+## Fields in the `perMachine` section
+
+`matchMachineId`/`matchHostname` → Strings, match expressions similar as for
+user records, see the user record documentation for details.
+
+The following fields are defined for the `perMachine` section and are defined
+equivalent to the fields of the same name in the `regular` section, and
+override those:
+
+`gid`, `members`, `administrators`
+
+## Fields in the `binding` section
+
+The following fields are defined for the `binding` section, and are equivalent
+to the fields of the same name in the `regular` and `perMachine` sections:
+
+`gid`
+
+## Fields in the `status` section
+
+The following fields are defined in the `status` section, and are mostly
+equivalent to the fields of the same name in the `regular` section, though with
+slightly different conceptual semantics, see the same fields in the user record
+documentation:
+
+`service`
+
+## Fields in the `signature` section
+
+The fields in this section are defined identically to those in the matching
+section in the user record.
+
+## Fields in the `secret` section
+
+Currently no fields are defined in this section for group records.
+
+## Mapping to `struct group` and `struct sgrp`
+
+When mapping classic UNIX group records (i.e. `struct group` and `struct sgrp`)
+to JSON group records the following mappings should be applied:
+
+| Structure | Field | Section | Field | Condition |
+|----------------|-------------|--------------|------------------|----------------------------|
+| `struct group` | `gr_name` | `regular` | `groupName` | |
+| `struct group` | `gr_passwd` | `privileged` | `password` | (See notes below) |
+| `struct group` | `gr_gid` | `regular` | `gid` | |
+| `struct group` | `gr_mem` | `regular` | `members` | |
+| `struct sgrp` | `sg_namp` | `regular` | `groupName` | |
+| `struct sgrp` | `sg_passwd` | `privileged` | `password` | (See notes below) |
+| `struct sgrp` | `sg_adm` | `regular` | `administrators` | |
+| `struct sgrp` | `sg_mem` | `regular` | `members` | |
+
+At this time almost all Linux machines employ shadow passwords, thus the
+`gr_passwd` field in `struct group` is set to `"x"`, and the actual password
+is stored in the shadow entry `struct sgrp`'s field `sg_passwd`.
+
+## Extending These Records
+
+The same logic and recommendations apply as for JSON user records.
+
+## Examples
+
+A reasonable group record for a system group might look like this:
+
+```json
+{
+ "groupName" : "systemd-resolve",
+ "gid" : 193,
+ "status" : {
+ "6b18704270e94aa896b003b4340978f1" : {
+ "service" : "io.systemd.NameServiceSwitch"
+ }
+ }
+}
+```
+
+And here's a more complete one for a regular group:
+
+```json
+{
+ "groupName" : "grobie",
+ "binding" : {
+ "6b18704270e94aa896b003b4340978f1" : {
+ "gid" : 60232
+ }
+ },
+ "disposition" : "regular",
+ "status" : {
+ "6b18704270e94aa896b003b4340978f1" : {
+ "service" : "io.systemd.Home"
+ }
+ }
+}
+```
For more details on building fuzzers and integrating with OSS-Fuzz, visit:
-- https://github.com/google/oss-fuzz/blob/master/docs/new_project_guide.md
-- https://llvm.org/docs/LibFuzzer.html
-- https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md
-- https://chromium.googlesource.com/chromium/src/testing/libfuzzer/+/HEAD/efficient_fuzzer.md
+- [Setting up a new project - OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/new-project-guide/)
+- [Tutorials - OSS-Fuzz](https://google.github.io/oss-fuzz/reference/useful-links/#tutorials)
--- /dev/null
+---
+title: Home Directories
+category: Concepts
+layout: default
+---
+
+# Home Directories
+
+[`systemd-homed.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html)
+manages home directories of regular ("human") users. Each directory it manages
+encapsulates both the data store and the user record of the user so that it
+comprehensively describes the user account, and is thus naturally portable
+between systems without any further, external metadata. This document describes
+the format used by these home directories, in context of the storage mechanism
+used.
+
+## General Structure
+
+Inside of the home directory a file `~/.identity` contains the JSON formatted
+user record of the user. It follows the format defined in [`JSON User
+Records`](https://systemd.io/USER_RECORD). It is recommended to bring the
+record into 'normalized' form (i.e. all objects should contain their fields
+sorted alphabetically by their key) before storing it there, though this is not
+required nor enforced. Since the user record is cryptographically signed the
+user cannot make modifications to the file on their own (at least not without
+corrupting it, or knowing the private key used for signing the record). Note
+that user records are stored here without their `binding`, `status` and
+`secret` sections, i.e. only with the sections included in the signature plus
+the signature section itself.
+
+## Storage Mechanism: Plain Directory/`btrfs` Subvolume
+
+If the plain directory or `btrfs` subvolume storage mechanism of
+`systemd-homed` is used (i.e. `--storage=directory` or `--storage=subvolume` on
+the
+[`homectl(1)`](https://www.freedesktop.org/software/systemd/man/homectl.html)
+command line) the home directory requires no special set-up besides including
+the user record in the `~/.identity` file.
+
+It is recommended to name home directories managed this way by
+`systemd-homed.service` by the user name, suffixed with `.homedir` (example:
+`lennart.homedir` for a user `lennart`) but this is not enforced. When the user
+is logged in the directory is generally mounted to `/home/$USER` (in our
+example: `/home/lennart`), thus dropping the suffix while the home directory is
+active. `systemd-homed` will automatically discover home directories named this
+way in `/home/*.homedir` and synthesize NSS user records for them as they show
+up.
+
+## Storage Mechanism: `fscrypt` Directories
+
+This storage mechanism is mostly identical to the plain directory storage
+mechanism, except that the home directory is encrypted using `fscrypt`. (Use
+`--storage=fscrypt` on the `homectl` command line.) Key management is
+implemented via extended attributes on the directory itself: for each password
+an extended attribute `trusted.fscrypt_slot0`, `trusted.fscrypt_slot1`,
+`trusted.fscrypt_slot2`, … is maintained. It's value contains a colon-separated
+pair of Base64 encoded data fields. The first field contains a salt value, the
+second field the encrypted volume key. The latter is encrypted using AES256 in
+counter mode, using a key derived from the password via PBKDF2-HMAC-SHA512
+together with the salt value. The construction is similar to what LUKS does for
+`dm-crypt` encrypted volumes. Note that extended attributes are not encrypted
+by `fscrypt` and hence are suitable for carry the key slots. Moreover, by using
+extended attributes the slots are directly attached to the directory and an
+independent sidecar key database is not required.
+
+## Storage Mechanism: `cifs` Home Directories
+
+In this storage mechanism the home directory is mounted from a CIFS server and
+service at login, configured inside the user record. (Use `--storage=cifs` on
+the `homectl` command line.) The local password of the user is used to log into
+the CIFS service. The directory share needs to contain the user record in
+`~/.identity` as well. Note that this means that the user record needs to be
+registered locally before it can be mounted for the first time, since CIFS
+domain and server information needs to be known *before* the mount. Note that
+for all other storage mechanisms it is entirely sufficient if the directories
+or storage artifacts are placed at the right locations — all information to
+activate them can be derived automatically from their mere availability.
+
+## Storage Mechanism: `luks` Home Directories
+
+This is the most advanced and most secure storage mechanism and consists of a
+Linux file system inside a LUKS2 volume inside a loopback file (or on removable
+media). (Use `--storage=luks` on the `homectl` command line.) Specifically:
+
+* The image contains a GPT partition table. For now it should only contain a
+ single partition, and that partition must have the type UUID
+ `773f91ef-66d4-49b5-bd83-d683bf40ad16`. It's partition label must be the
+ user name.
+
+* This partition must contain a LUKS2 volume, whose label must be the user
+ name. The LUKS2 volume must contain a LUKS2 token field of type
+ `systemd-homed`. The JSON data of this token must have a `record` field,
+ containing a string with base64-encoded data. This data is the JSON user
+ record, in the same serialization as in `~/.identity`, though encrypted. The
+ JSON data of this token must also have an `iv` field, which contains a
+ base64-encoded binary initialization vector for the encryption. The
+ encryption used is the same as the LUKS2 volume itself uses, unlocked by the
+ same volume key, but based on its own IV.
+
+* Inside of this LUKS2 volume must be a Linux file system, one of `ext4`,
+ `btrfs` and `xfs`. The file system label must be the user name.
+
+* This file system should contain a single directory named after the user. This
+ directory will become the home directory of the user when activated. It
+ contains a second copy of the user record in the `~/.identity` file, like in
+ the other storage mechanisms.
+
+The image file should either reside in a directory `/home/` on the system,
+named after the user, suffixed with `.home`. When activated the container home
+directory is mounted to the same path, though with the `.home` suffix dropped —
+unless a different mount point is defined in the user record. (e.g.: the
+loopback file `/home/waldo.home` is mounted to `/home/waldo` while activated.)
+When the image is stored on removable media (such as a USB stick) the image
+file can be directly `dd`'ed onto it, the format is unchanged. The GPT envelope
+should ensure the image is properly recognizable as a home directory both when
+used in a loopback file and on a removable USB stick. (Note that when mounting
+a home directory from an USB stick it too defaults to a directory in `/home/`,
+named after the username, with no further suffix.)
+
+Rationale for the GPT partition table envelope: this way the image is nicely
+discoverable and recognizable already by partition managers as a home
+directory. Moreover, when copied onto a USB stick the GPT envelope makes sure
+the stick is properly recognizable as a portable home directory
+medium. (Moreover it allows to embed additional partitions later on, for
+example for allowing a multi-purpose USB stick that contains both a home
+directory and a generic storage volume.)
+
+Rationale for including the encrypted user record in the the LUKS2 header:
+Linux kernel file system implementations are generally not robust towards
+maliciously formatted file systems; there's a good chance that file system
+images can be used as attack vectors, exploiting the kernel. Thus it is
+necessary to validate the home directory image *before* mounting it and
+establishing a minimal level of trust. Since the user record data is
+cryptographically signed and user records not signed with a recognized private
+key are not accepted a minimal level of trust between the system and the home
+directory image is established.
+
+Rationale for storing the home directory one level below to root directory of
+the contained file system: this way special directories such as `lost+found/`
+do not show up in the user's home directory.
+
+## Algorithm
+
+Regardless of the storage mechanism used, an activated home directory
+necessarily involves a mount point to be established. In case of the
+directory-based storage mechanisms (`directory`, `subvolume` and `fscrypt`)
+this is a bind mount, in case of `cifs` this is a CIFS network mount, and in
+case of the LUKS2 backend a regular block device mount of the file system
+contained in the LUKS2 image. By requiring a mount for all cases (even for
+those that already are a directory) a clear logic is defined to distuingish
+active and inactive home directories, so that the directories become
+inaccessible under their regular path the instant they are
+deactivated. Moreover, the `nosuid`, `nodev` and `noexec` flags configured in
+the user record are applied when the bind mount is established.
+
+During activation, the user records retained on the host, the user record
+stored in the LUKS2 header (in case of the LUKS2 storage mechanism) and the
+user record stored inside the home directory in `~/.identity` are
+compared. Activation is only permitted if they match the same user and are
+signed by a recognized key. When the three instances differ in `lastChangeUSec`
+field, the newest record wins, and is propagated to the other two locations.
+
+During activation the file system checker (`fsck`) appropriate for the
+selected file system is automatically invoked, ensuring the file system is in a
+healthy state before it is mounted.
+
+If the UID assigned to a user does not match the owner of the home directory in
+the file system, the home directory is automatically and recursively `chown()`ed
+to the correct UID.
+
+Depending on the `discard` setting of the user record either the backing
+loopback file is `fallocate()`ed during activation, or the mounted file system
+is `FITRIM`ed after mounting, to ensure the setting is correctly enforced.
* The initrd should mount `/run/` as a tmpfs and pass it pre-mounted when
jumping into the main system when executing systemd. The mount options should
- be `mode=755,nodev,nosuid,strictatime`
+ be `mode=755,nodev,nosuid,strictatime`.
* It's highly recommended that the initrd also mounts `/usr/` (if split off) as
appropriate and passes it pre-mounted to the main system, to avoid the
layout: default
---
-# Interface Stability Promise
+# Interface Portability and Stability Promise
systemd provides various interfaces developers and programs might rely on. Starting with version 26 (the first version released with Fedora 15) we promise to keep a number of them stable and compatible for the future.
* Some of the **"special" unit names** and their semantics. To be precise the ones that are necessary for normal services, and not those required only for early boot and late shutdown, with very few exceptions. To list them here: `basic.target`, `shutdown.target`, `sockets.target`, `network.target`, `getty.target`, `graphical.target`, `multi-user.target`, `rescue.target`, `emergency.target`, `poweroff.target`, `reboot.target`, `halt.target`, `runlevel[1-5].target`.
-* **The D-Bus interfaces of the main service daemon and other daemons**. We try to always preserve backwards compatiblity, and intentational breakage is never introduced. Nevertheless, when we find bugs that mean that the existing interface was not useful, or when the implementation did something different than stated by the documentation and the implemented behaviour is not useful, we will fix the implementation and thus introduce a change in behaviour. But the API (parameter counts and types) is never changed, and existing attributes and methods will not be removed.
+* **The D-Bus interfaces of the main service daemon and other daemons**. We try to always preserve backwards compatibility, and intentional breakage is never introduced. Nevertheless, when we find bugs that mean that the existing interface was not useful, or when the implementation did something different than stated by the documentation and the implemented behaviour is not useful, we will fix the implementation and thus introduce a change in behaviour. But the API (parameter counts and types) is never changed, and existing attributes and methods will not be removed.
* For a more comprehensive and authoritative list, consult the chart below.
Note that this is a promise, not an eternal guarantee. These are our intentions, but if in the future there are very good reasons to change or get rid of an interface we have listed above as stable, then we might take the liberty to do so, despite this promise. However, if we do this, then we'll do our best to provide a smooth and reasonably long transition phase.
-# Interface Portability And Stability Chart
+## Interface Portability And Stability Chart
systemd provides a number of APIs to applications. Below you'll find a table detailing which APIs are considered stable and how portable they are.
Note that not all of these interfaces are our invention (but most), we just adopted them in systemd to make them more prominently implemented. For example, we adopted many Debian facilities in systemd to push it into the other distributions as well.
-
---
-
And now, here's the list of (hopefully) all APIs that we have introduced with systemd:
| API | Type | Covered by Interface Stability Promise | Fully documented | Known External Consumers | Reimplementable Independently | Known Other Implementations | systemd Implementation portable to other OSes or non-systemd distributions |
| [$NOTIFY_SOCKET Daemon Notifications](https://www.freedesktop.org/software/systemd/man/sd_notify.html) | Environment | yes | yes | a few, including udev | yes | - | no |
| [argv[0][0]='@' Logic](https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons) | `/proc` marking | yes | yes | mdadm | yes | - | no |
| [Unit file format](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) | File format | yes | yes | numerous | no | - | no |
+| [Network](https://www.freedesktop.org/software/systemd/man/systemd.network.html) & [Netdev file format](https://www.freedesktop.org/software/systemd/man/systemd.netdev.html) | File format | yes | yes | no | no | - | no |
+| [Link file format](https://www.freedesktop.org/software/systemd/man/systemd.link.html) | File format | yes | yes | no | no | - | no |
| [Journal File Format](https://www.freedesktop.org/wiki/Software/systemd/journal-files) | File format | yes | yes | - | maybe | - | no |
| [Journal Export Format](https://www.freedesktop.org/wiki/Software/systemd/export) | File format | yes | yes | - | yes | - | no |
| [Cooperation in cgroup tree](https://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups) | Treaty | yes | yes | libvirt | yes | libvirt | no |
Of course, one last thing I can't make myself not ask you before we finish here, and before you start reimplementing these APIs in your distribution: are you sure it's time well spent if you work on reimplementing all this code instead of just spending it on adopting systemd on your distro as well?
-## Independent operation of systemd programs
+## Independent Operation of systemd Programs
-Some programs in the systemd suite are indended to operate independently of the
+Some programs in the systemd suite are intended to operate independently of the
running init process (or even without an init process, for example when
creating system installation chroots). They can be safely called on systems with
a different init process or for example in package installation scriptlets.
Many other programs support operation without the system manager except when
the specific functionality requires such communication. For example
`journalctl` operates almost independently, but will query the boot id when
-`--boot` option is used. `systemd-journal-remote`, `systemd-journal-upload`,
-`systemd-journal-gatewayd`, `coredumpctl`, `busctl`, `systemctl --root` also
-fall into this category.
+`--boot` option is used; it also requires `systemd-journald` (and thus
+`systemd`) to be running for options like `--flush` and `--sync`.
+`systemd-journal-remote`, `systemd-journal-upload`, `systemd-journal-gatewayd`,
+`coredumpctl`, `busctl`, `systemctl --root` also fall into this category of
+mostly-independent programs.
an image with a partition table understood by the Linux kernel with only a
single partition defined, or alternatively, a GPT partition table with a set
of properly marked partitions following the [Discoverable Partitions
- Specification](https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/).
+ Specification](https://systemd.io/DISCOVERABLE_PARTITIONS).
3. The image must at least contain one matching unit file, with the right name
prefix and suffix (see above). The unit file is searched in the usual paths,
Processes (run by the root user) whose first character of the zeroth command
line argument is `@` are excluded from the killing spree, much the same way as
kernel threads are excluded too. Thus, a daemon which wants to take advantage
-of this logic needs to place the following at the top of its main() function:
+of this logic needs to place the following at the top of its `main()` function:
```c
-…
-[0][0] = '@';
-…
+...
+argv[0][0] = '@';
+...
```
And that's already it. Note that this functionality is only to be used by
#include <unistd.h>
int main(int argc, char *argv[]) {
- …
+ ...
if (access("/etc/initrd-release", F_OK) >= 0)
argv[0][0] = '@';
- …
+ ...
}
```
program consult this blog story: [Socket
Activation](http://0pointer.de/blog/projects/socket-activation.html)
-* Consider having a look at the [initrd Interface of systemd](http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface)
+* Consider having a look at the [initrd Interface of systemd](https://systemd.io/INITRD_INTERFACE/).
---
-title: Reporting of security vulnerabilities
+title: Reporting of Security Vulnerabilities
category: Contributing
layout: default
---
-# Reporting of security vulnerabilities
+# Reporting of Security Vulnerabilities
If you discover a security vulnerability, we'd appreciate a non-public disclosure. The [issue tracker](https://github.com/systemd/systemd/issues) and [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) are fully public. If you need to reach systemd developers in a non-public way, report the issue to the [systemd-security@redhat.com](mailto:systemd-security@redhat.com) mailing list. The disclosure will be coordinated with distributions.
---
-title: Testing systemd using sanitizers
+title: Testing systemd Using Sanitizers
category: Contributing
layout: default
---
-# 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).
---
-title: What settings are currently available for transient units?
+title: What Settings Are Currently Available For Transient Units?
category: Interfaces
layout: default
---
-# What settings are currently available for transient units?
+# What Settings Are Currently Available For Transient Units?
Our intention is to make all settings that are available as unit file settings
also available for transient units, through the D-Bus API. At the moment,
✓ PrivateUsers=
✓ ProtectSystem=
✓ ProtectHome=
+✓ ProtectClock=
✓ MountFlags=
✓ MountAPIVFS=
✓ Personality=
---
-title: Users, Groups, UIDs and GIDs on `systemd` Systems
+title: Users, Groups, UIDs and GIDs on systemd Systems
category: Concepts
layout: default
---
-# Users, Groups, UIDs and GIDs on `systemd` Systems
+# Users, Groups, UIDs and GIDs on systemd Systems
Here's a summary of the requirements `systemd` (and Linux) make on UID/GID
assignments and their ranges.
`systemd` defines a number of special UID ranges:
-1. 61184…65519 → UIDs for dynamic users are allocated from this range (see the
+1. 60001…60513 → UIDs for home directories managed by
+ [`systemd-homed.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html). UIDs
+ from this range are automatically assigned to any home directory discovered,
+ and persisted locally on first login. On different systems the same user
+ might get different UIDs assigned in case of conflict, though it is
+ attempted to make UID assignments stable, by deriving them from a hash of
+ the user name.
+
+2. 61184…65519 → UIDs for dynamic users are allocated from this range (see the
`DynamicUser=` documentation in
[`systemd.exec(5)`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html)). This
range has been chosen so that it is below the 16bit boundary (i.e. below
user record resolving works correctly without those users being in
`/etc/passwd`.
-2. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of
+3. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of
per-container UID ranges. When the `--private-users=pick` switch is used (or
`-U`) then it will automatically find a so far unused 16bit subrange of this
range and assign it to the container. The range is picked so that the upper
| 5 | `tty` group | `systemd` | `/etc/passwd` |
| 6…999 | System users | Distributions | `/etc/passwd` |
| 1000…60000 | Regular users | Distributions | `/etc/passwd` + LDAP/NIS/… |
-| 60001…61183 | Unused | | |
+| 60001…60513 | Human Users (homed) | `systemd` | `nss-systemd`
+| 60514…61183 | Unused | | |
| 61184…65519 | Dynamic service users | `systemd` | `nss-systemd` |
| 65520…65533 | Unused | | |
| 65534 | `nobody` user | Linux | `/etc/passwd` + `nss-systemd` |
--- /dev/null
+---
+title: User/Group Record Lookup API via Varlink
+category: Interfaces
+layout: default
+---
+
+# User/Group Record Lookup API via Varlink
+
+JSON User/Group Records (as described in the [JSON User
+Records](https://systemd.io/USER_RECORD) and [JSON Group
+Records](https://systemd.io/GROUP_RECORD) documents) that are defined on the
+local system may be queried with a [Varlink](https://varlink.org/) API. This
+API takes both the role of what
+[`getpwnam(3)`](http://man7.org/linux/man-pages/man3/getpwnam.3.html) and
+related calls are for `struct passwd`, as well as the interfaces modules
+implementing the [glibc Name Service Switch
+(NSS)](https://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html)
+expose. Or in other words, it both allows applications to efficiently query
+user/group records from local services, and allows local subsystems to provide
+user/group records efficiently to local applications.
+
+This simple API only exposes only three method calls, and requires only a small
+subset of the Varlink functionality.
+
+## Why Varlink?
+
+The API described in this document is based on a simple subset of the
+mechanisms described by [Varlink](https://varlink.org/). The choice of
+preferring Varlink over D-Bus and other IPCs in this context was made for three
+reasons:
+
+1. User/Group record resolution should work during early boot and late shutdown
+ without special handling. This is very hard to do with D-Bus, as the broker
+ service for D-Bus generally runs as regular system daemon and is hence only
+ available at the latest boot stage.
+
+2. The JSON user/group records are native JSON data, hence picking an IPC
+ system that natively operates with JSON data is natural and clean.
+
+3. IPC systems such as D-Bus do not provide flow control and are thus unusable
+ for streaming data. They are useful to pass around short control messages,
+ but as soon as potentially many and large objects shall be transferred,
+ D-Bus is not suitable, as any such streaming of messages would be considered
+ flooding in D-Bus' logic, and thus possibly result in termination of
+ communication. Since the APIs defined in this document need to support
+ enumerating potentially large numbers of users and groups, D-Bus is simply
+ not an appropriate option.
+
+## Concepts
+
+Each subsystem that needs to define users and groups on the local system is
+supposed to implement this API, and offer its interfaces on a Varlink
+`AF_UNIX`/`SOCK_STREAM` file system socket bound into the
+`/run/systemd/userdb/` directory. When a client wants to look up a user or
+group record, it contacts all sockets bound in this directory in parallel, and
+enqueues the same query to each. The first positive reply is then returned to
+the application, or if all fail the last seen error is returned
+instead. (Alternatively a special Varlink service is available,
+`io.systemd.Multiplexer` which acts as frontend and will do the parallel
+queries on behalf of the client, drastically simplifying client
+development. This service is not available during earliest boot and final
+shutdown phases.)
+
+Unlike with glibc NSS there's no order or programmatic expression language
+defined in which queries are issued to the various services. Instead, all
+queries are always enqueued in parallel to all defined services, in order to
+make look-ups efficient, and the simple rule of "first successful lookup wins"
+is unconditionally followed for user and group look-ups (though not for
+membership lookups, see below).
+
+This simple scheme only works safely as long as every service providing
+user/group records carefully makes sure not to answer with conflicting
+records. This API does not define any mechanisms for dealing with user/group
+name/ID collisions during look-up nor during record registration. It assumes
+the various subsystems that want to offer user and group records to the rest of
+the system have made sufficiently sure in advance that their definitions do not
+collide with those of other services. Clients are not expected to merge
+multiple definitions for the same user or group, and will also not be able to
+detect conflicts and suppress such conflicting records.
+
+It is recommended to name the sockets in the directory in reverse domain name
+notation, but this is neither required nor enforced.
+
+## Well-Known Services
+
+Any subsystem that wants to provide user/group records can do so, simply by
+binding a socket in the aforementioned directory. By default two
+services are listening there, that have special relevance:
+
+1. `io.systemd.NameServiceSwitch` → This service makes the classic UNIX/glibc
+ NSS user/group records available as JSON User/Group records. Any such
+ records are automatically converted as needed, and possibly augmented with
+ information from the shadow databases.
+
+2. `io.systemd.Multiplexer` → This service multiplexes client queries to all
+ other running services. It's supposed to simplify client development: in
+ order to look up or enumerate user/group records it's sufficient to talk to
+ one service instead of all of them in parallel. Note that it is not availabe
+ during earliest boot and final shutdown phases, hence for programs running
+ in that context it is preferable to implement the parallel lookup
+ themselves.
+
+Both these services are implemented by the same daemon
+`systemd-userdbd.service`.
+
+Note that these services currently implement a subset of Varlink only. For
+example, introspection is not available, and the resolver logic is not used.
+
+## Other Services
+
+The `systemd` project provides two other services implementing this
+interface. Specifically:
+
+1. `io.systemd.DynamicUser` → This service is implemented by the service
+ manager itself, and provides records for the users and groups synthesized
+ via `DynamicUser=` in unit files.
+
+2. `io.systemd.Home` → This service is implemented by `systemd-homed.service`
+ and provides records for the users and groups defined by the home
+ directories it manages.
+
+Other projects are invited to implement these services too. For example it
+would make sense for LDAP/ActiveDirectory projects to implement these
+interfaces, which would provide them a way to do per-user resource management
+enforced by systemd and defined directly in LDAP directories.
+
+## Compatibility with NSS
+
+Two-way compatibility with classic UNIX/glibc NSS user/group records is
+provided. When using the Varlink API, lookups into databases provided only via
+NSS (and not natively via Varlink) are handled by the
+`io.systemd.NameServiceSwitch` service (see above). When using the NSS API
+(i.e. `getpwnam()` and friends) the `nss-systemd` module will automatically
+synthesize NSS records for users/groups natively defined via a Varlink
+API. Special care is taken to avoid recursion between these two compatibility
+mechanisms.
+
+Subsystems that shall provide user/group records to the system may choose
+between offering them via an NSS module or via a this Varlink API, either way
+all records are accessible via both APIs, due to the bidirectional
+forwarding. It is also possible to provide the same records via both APIs
+directly, but in that case the compatibility logic must be turned off. There
+are mechanisms in place for this, please contact the systemd project for
+details, as these are currently not documented.
+
+## Caching of User Records
+
+This API defines no concepts for caching records. If caching is desired it
+should be implemented in the subsystems that provide the user records, not in
+the clients consuming them.
+
+## Method Calls
+
+```
+interface io.systemd.UserDatabase
+
+method GetUserRecord(
+ uid : ?int,
+ userName : ?string,
+ service : string
+) -> (
+ record : object,
+ incomplete : boolean
+)
+
+method GetGroupRecord(
+ gid : ?int,
+ groupName : ?string,
+ service : string
+) -> (
+ record : object,
+ incomplete : boolean
+)
+
+method GetMemberships(
+ userName : ?string,
+ groupName : ?string,
+ service : string
+) -> (
+ userName : string,
+ groupName : string
+)
+
+error NoRecordFound()
+error BadService()
+error ServiceNotAvailable()
+error ConflictingRecordFound()
+```
+
+The `GetUserRecord` method looks up or enumerates a user record. If the `uid`
+parameter is set it specifies the numeric UNIX UID to search for. If the
+`userName` parameter is set it specifies the name of the user to search
+for. Typically, only one of the two parameters are set, depending whether a
+look-up by UID or by name is desired. However, clients may also specify both
+parameters, in which case a record matching both will be returned, and if only
+one exists that matches one of the two parameters but not the other an error of
+`ConflictingRecordFound` is returned. If neither of the two parameters are set
+the whole user database is enumerated. In this case the method call needs to be
+made with `more` set, so that multiple method call replies may be generated as
+effect, each carrying one user record.
+
+The `service` parameter is mandatory and should be set to the service name
+being talked to (i.e. to the same name as the `AF_UNIX` socket path, with the
+`/run/systemd/userdb/` prefix removed). This is useful to allow implementation
+of multiple services on the same socket (which is used by
+`systemd-userdbd.service`).
+
+The method call returns one or more user records, depending which type of query is
+used (see above). The record is returned in the `record` field. The
+`incomplete` field indicates whether the record is complete. Services providing
+user record lookup should only pass the `privileged` section of user records to
+clients that either match the user the record is about or to sufficiently
+privileged clients, for all others the section must be removed so that no
+sensitive data is leaked this way. The `incomplete` parameter should indicate
+whether the record has been modified like this or not (i.e. it is `true` if a
+`privileged` section existed in the user record and was removed, and `false` if
+no `privileged` section existed or one existed but hasn't been removed).
+
+If no user record matching the specified UID or name is known the error
+`NoRecordFound` is returned (this is also returned if neither UID nor name are
+specified, and hence enumeration requested but the subsystem currently has no
+users defined).
+
+If a method call with an incorrectly set `service` field is received
+(i.e. either not set at all, or not to the service's own name) a `BadService`
+error is generated. Finally, `ServiceNotAvailable` should be returned when the
+backing subsystem is not operational for some reason and hence no information
+about existence or non-existence of a record can be returned nor any user
+record at all. (The `service` field is defined in order to allow implementation
+of daemons that provide multiple distinct user/group services over the same
+`AF_UNIX` socket: in order to correctly determine which service a client wants
+to talk to the client needs to provide the name in each request.)
+
+The `GetGroupRecord` method call works analogously but for groups.
+
+The `GetMemberships` method call may be used to inquire about group
+memberships. The `userName` and `groupName` arguments take what the name
+suggests. If one of the two is specified all matching memberships are returned,
+if neither is specified all known memberships of any user and any group are
+returned. The return value is a pair of user name and group name, where the
+user is a member of the group. If both arguments are specified the specified
+membership will be tested for, but no others, and the pair is returned if it is
+defined. Unless both arguments are specified the method call needs to be made
+with `more` set, so that multiple replies can be returned (since typically
+there are are multiple members per group and also multiple groups a user is
+member of). As with `GetUserRecord` and `GetGroupRecord` the `service`
+parameter needs to contain the name of the service being talked to, in order to
+allow implementation of multiple service within the same IPC socket. In case no
+matching membership is known `NoRecordFound` is returned. The other two errors
+are also generated in the same cases as for `GetUserRecord` and
+`GetGroupRecord`.
+
+Unlike with `GetUserRecord` and `GetGroupRecord` the lists of memberships
+returned by services are always combined. Thus unlike the other two calls a
+membership lookup query has to wait for the last simultaneous query to complete
+before the complete list is acquired.
+
+Note that only the `GetMemberships` call is authoritative about memberships of
+users in groups. i.e. it should not be considered sufficient to check the
+`memberOf` field of user records and the `members` field of group records to
+acquire the full list of memberships. The full list can only bet determined by
+`GetMemberships`, and as mentioned requires merging of these lists of all local
+services. Result of this is that it can be one service that defines a user A,
+and another service that defines a group B, and a third service that declares
+that A is a member of B.
+
+And that's really all there is to it.
--- /dev/null
+---
+title: JSON User Records
+category: Interfaces
+layout: default
+---
+
+# JSON User Records
+
+systemd optionally processes user records that go beyond the classic UNIX (or
+glibc NSS) `struct passwd`. Various components of systemd are able to provide
+and consume records in a more extensible format of a dictionary of key/value
+pairs, encoded as JSON. Specifically:
+
+1. [`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html)
+ manages `human` user home directories and embeds these JSON records
+ directly in the home directory images (see [Home
+ Directories](https://systemd.io/HOME_DIRECTORY) for details).
+
+2. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+ processes these JSON records for users that log in, and applies various
+ settings to the activated session, including environment variables, nice
+ levels and more.
+
+3. [`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html)
+ processes these JSON records of users that log in, and applies various
+ resource management settings to the per-user slice units it manages. This
+ allows setting global limits on resource consumption by a specific user.
+
+4. [`nss-systemd`](https://www.freedesktop.org/software/systemd/man/nss-systemd.html)
+ is a glibc NSS module that synthesizes classic NSS records from these JSON
+ records, providing full backwards compatibility with the classic UNIX APIs
+ both for look-up and enumeration.
+
+5. The service manager (PID 1) exposes dynamic users (i.e. users synthesized as
+ effect of `DynamicUser=` in service unit files) as these advanced JSON
+ records, making them discoverable to the rest of the system.
+
+6. [`systemd-userdbd.service`](https://www.freedesktop.org/software/systemd/man/systemd-userdbd.service.html)
+ is a small service that can translate UNIX/glibc NSS records to these JSON
+ user records. It also provides a unified [Varlink](https://varlink.org/) API
+ for querying and enumerating records of this type, optionally acquiring them
+ from various other services.
+
+JSON user records may contain various fields that are not available in `struct
+passwd`, and are extensible for other applications. For example, the record may
+contain information about:
+
+1. Additional security credentials (PKCS#11 security token information,
+ biometrical authentication information, SSH public key information)
+
+2. Additional user metadata, such as a picture, email address, location string,
+ preferred language or timezone
+
+3. Resource Management settings (such as CPU/IO weights, memory and tasks
+ limits, classic UNIX resource limits or nice levels)
+
+4. Runtime parameters such as environment variables or the `nodev`, `noexec`,
+ `nosuid` flags to use for the home directory
+
+5. Information about where to mount the home directory from
+
+And various other things. The record is intended to be extensible, for example
+the following extensions are envisioned:
+
+1. Windows network credential information
+
+2. Information about default IMAP, SMTP servers to use for this user
+
+3. Parental control information to enforce on this user
+
+4. Default parameters for backup applications and similar
+
+Similar to JSON User Records there are also [JSON Group
+Records](https://systemd.io/GROUP_RECORD) that encapsulate UNIX groups.
+
+JSON User Records may be transferred or written to disk in various protocols
+and formats. To inquire about such records defined on the local system use the
+[User/Group Lookup API via Varlink](https://systemd.io/USER_GROUP_API).
+
+## Why JSON?
+
+JSON is nicely extensible and widely used. In particular it's easy to
+synthesize and process with numerous programming languages. It's particularly
+popular in the web communities, which hopefully should make it easy to link
+user credential data from the web and from local systems more closely together.
+
+## General Structure
+
+The JSON user records generated and processed by systemd follow a general
+structure, consisting of seven distinct "sections". Specifically:
+
+1. Various fields are placed at the top-level of user record (the `regular`
+ section). These are generally fields that shall apply unconditionally to the
+ user in all contexts, are portable and not security sensitive.
+
+2. A number of fields are located in the `privileged` section (a sub-object of
+ the user record). Fields contained in this object are security sensitive,
+ i.e. contain information that the user and the administrator should be able
+ to see, but other users should not. In many ways this matches the data
+ stored in `/etc/shadow` in classic Linux user accounts, i.e. includes
+ password hashes and more. Algorithmically, when a user record is passed to
+ an untrusted client, by monopolizing such sensitive records in a single
+ object field we can easily remove it from view.
+
+3. A number of fields are located in objects inside the `perMachine` section
+ (an array field of the user record). Primarily these are resource
+ management-related fields, as those tend to make sense on a specific system
+ only, e.g. limiting a user's memory use to 1G only makes sense on a specific
+ system that has more than 1G of memory. Each object inside the `perMachine`
+ array comes with a `matchMachineId` or `matchHostname` field which indicate
+ which systems to apply the listed settings to. Note that many fields
+ accepted in the `perMachine` section can also be set at the top level (the
+ `regular` section), where they define the fallback if no matching object in
+ `perMachine` is found.
+
+4. Various fields are located in the `binding` section (a sub-sub-object of the
+ user record; an intermediary object is inserted which is keyed by the
+ machine ID of the host). Fields included in this section "bind" the object
+ to a specific system. They generally include non-portable information about
+ paths or UID assignments, that are true on a specific system, but not
+ necessarily on others, and which are managed automatically by some user
+ record manager (such as `systemd-homed`). Data in this section is considered
+ part of the user record only in the local context, and is generally not
+ ported to other systems. Due to that it is not included in the reduced user
+ record the cryptographic signature defined in the `signature` section is
+ calculated on. In `systemd-homed` this section is also removed when the
+ user's record is stored in the `~/.identity` file in the home directory, so
+ that every system with access to the home directory can manage these
+ `binding` fields individually. Typically, the binding section is persisted
+ to the local disk.
+
+5. Various fields are located in the `status` section (a sub-sub-object of the
+ user record, also with an intermediary object between that is keyed by the
+ machine ID, similar to the way the `binding` section is organized). This
+ section is augmented during runtime only, and never persisted to disk. The
+ idea is that this section contains information about current runtime
+ resource usage (for example: currently used disk space of the user), that
+ changes dynamically but is otherwise immediately associated with the user
+ record and for many purposes should be considered to be part of the user
+ record.
+
+6. The `signature` section contains one or more cryptographic signatures of a
+ reduced version of the user record. This is used to ensure that only user
+ records defined by a specific source are accepted on a system, by validating
+ the signature against the set of locally accepted signature public keys. The
+ signature is calculated from the JSON user record with all sections removed,
+ except for `regular`, `privileged`, `perMachine`. Specifically, `binding`,
+ `status`, `signature` itself and `secret` are removed first and thus not
+ covered by the signature. This section is optional, and is only used when
+ cryptographic validation of user records is required (as it is by
+ `systemd-homed.service` for example).
+
+7. The `secret` section contains secret user credentials, such as password or
+ PIN information. This data is never persisted, and never returned when user
+ records are inquired by a client, privileged or not. This data should only
+ be included in a user record very briefly, for example when certain very
+ specific operations are executed. For example, in tools such as
+ `systemd-homed` this section may be included in user records, when creating
+ a new home directory, as passwords and similar credentials need to be
+ provided to encrypt the home directory with.
+
+Here's a tabular overview of the sections and their properties:
+
+| Section | Included in Signature | Persistent | Security Sensitive | Contains Host-Specific Data |
+|------------|-----------------------|------------|--------------------|-----------------------------|
+| regular | yes | yes | no | no |
+| privileged | yes | yes | yes | no |
+| perMachine | yes | yes | no | yes |
+| binding | no | yes | no | yes |
+| status | no | no | no | yes |
+| signature | no | yes | no | no |
+| secret | no | no | yes | no |
+
+Note that services providing user records to the local system are free to
+manage only a subset of these sections and never include the others in
+them. For example, a service that has no concept of signed records (for example
+because the records it manages are inherently trusted anyway) does not have to
+bother with the `signature` section. A service that only defines records in a
+strictly local context and without signatures doesn't have to deal with the
+`perMachine` or `binding` sections and can include its data exclusively in the
+regular section. A service that uses a separate, private channel for
+authenticating users (or that doesn't have a concept of authentication at all)
+does not need to to be concerned with the `secret` section of user records, as
+the fields included therein are only useful when executing authentication
+operations natively against JSON user records.
+
+The `systemd-homed` manager uses all seven sections for various
+purposes. Inside the home directories (and if the LUKS2 backend is used, also
+in the LUKS2 header) a user record containing the `regular`, `privileged`,
+`perMachine` and `signature` sections is stored. `systemd-homed` also stores a
+version of the record on the host, with the same four sections and augmented
+with an additional, fifth `binding` section. When a local client enquires about
+a user record managed by `systemd-homed` the service will add in some
+additional information about the user and home directory in the `status`
+section — this version is only transferred via IPC and never written to
+disk. Finally the `secret` section is used during authentication operations via
+IPC to transfer the user record along with its authentication tokens in one go.
+
+## Fields in the `regular` section
+
+As mentioned, the `regular` section's fields are placed at the top level
+object. The following fields are currently defined:
+
+`userName` → The UNIX user name for this record. Takes a string with a valid
+UNIX user name. This field is the only mandatory field, all others are
+optional. Corresponds with the `pw_name` field of of `struct passwd` and the
+`sp_namp` field of `struct spwd` (i.e. the shadow user record stored in
+`/etc/shadow`).
+
+`realm` → The "realm" a user is defined in. This concept allows distinguishing
+users with the same name that originate in different organizations or
+installations. This should take a string in DNS domain syntax, but doesn't have
+to refer to an actual DNS domain (though it is recommended to use one for
+this). The idea is that the user `lpoetter` in the `redhat.com` realm might be
+distinct from the same user in the `poettering.hq` realm. User records for the
+same user name that have different realm fields are considered referring to
+different users. When updating a user record it is required that any new
+version has to match in both `userName` and `realm` field. This field is
+optional, when unset the user should not be considered part of any realm. A
+user record with a realm set is never compatible (for the purpose of updates,
+see above) with a user record without one set, even if the `userName` field matches.
+
+`realName` → The real name of the user, a string. This should contain the user's
+real ("human") name, and corresponds loosely to the GECOS field of classic UNIX
+user records. When converting a `struct passwd` to a JSON user record this
+field is initialized from GECOS (i.e. the `pw_gecos` field), and vice versa
+when converting back. That said, unlike GECOS this field is supposed to contain
+only the real name and no other information.
+
+`emailAddress` → The email address of the user, formatted as
+string. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+initializes the `$EMAIL` environment variable from this value for all login
+sessions.
+
+`iconName` → The name of an icon picked by the user, for example for the
+purpose of an avatar. This must be a string, and should follow the semantics
+defined in the [Icon Naming
+Specification](https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html).
+
+`location` → A free-form location string describing the location of the user,
+if that is applicable. It's probably wise to use a location string processable
+by geo-location subsystems, but this is not enforced nor required. Example:
+`Berlin, Germany` or `Basement, Room 3a`.
+
+`disposition` → A string, one of `intrinsic`, `system`, `dynamic`, `regular`,
+`container`, `reserved`. If specified clarifies the disposition of the user,
+i.e. the context it is defined in. For regular, "human" users this should be
+`regular`, for system users (i.e. users that system services run under, and
+similar) this should be `system`. The `intrinsic` disposition should be used
+only for the two users that have special meaning to the OS kernel itself,
+i.e. the `root` and `nobody` users. The `container` string should be used for
+users that are used by an OS container, and hence will show up in `ps` listings
+and such, but are only defined in container context. Finally `reserved` should
+be used for any users outside of these use-cases. Note that this property is
+entirely optional and applications are assumed to be able to derive the
+disposition of a user automatically from a record even in absence of this
+field, based on other fields, for example the numeric UID. By setting this
+field explicitly applications can override this default determination.
+
+`lastChangeUSec` → An unsigned 64bit integer value, referring to a timestamp in µs
+since the epoch 1970, indicating when the user record (specifically, any of the
+`regular`, `privileged`, `perMachine` sections) was last changed. This field is
+used when comparing two records of the same user to identify the newer one, and
+is used for example for automatic updating of user records, where appropriate.
+
+`lastPasswordChangeUSec` → Similar, also an unsigned 64bit integer value,
+indicating the point in time the password (or any authentication token) of the
+user was last changed. This corresponds to the `sp_lstchg` field of `struct
+spwd`, i.e. the matching field in the user shadow database `/etc/shadow`,
+though provides finer resolution.
+
+`shell` → A string, referring to the shell binary to use for terminal logins of
+this user. This corresponds with the `pw_shell` field of `struct passwd`, and
+should contain an absolute file system path. For system users not suitable for
+terminal log-in this field should not be set.
+
+`umask` → The `umask` to set for the user's login sessions. Takes an
+integer. Note that usually on UNIX the umask is noted in octal, but JSON's
+integers are generally written in decimal, hence in this context we denote it
+umask in decimal too. The specified value should be in the valid range for
+umasks, i.e. 0000…0777 (in octal as typical in UNIX), or 0…511 (in decimal, how
+it actually appears in the JSON record). This `umask` is automatically set by
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+for all login sessions of the user.
+
+`environment` → An array of strings, each containing an environment variable
+and its value to set for the user's login session, in a format compatible with
+[`putenv()`](http://man7.org/linux/man-pages/man3/putenv.3.html). Any
+environment variable listed here is automatically set by
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+for all login sessions of the user.
+
+`timeZone` → A string indicating a preferred timezone to use for the user. When
+logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the `$TZ` environment variable from this
+string. The string should be a `tzdata` compatible location string, for
+example: `Europe/Berlin`.
+
+`preferredLanguage` → A string indicating the preferred language/locale for the
+user. When logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the `$LANG` environment variable from this
+string. The string hence should be in a format compatible with this environment
+variable, for example: `de_DE.UTF8`.
+
+`niceLevel` → An integer value in the range -20…19. When logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the login process' nice level to this value with,
+which is then inherited by all the user's processes, see
+[`setpriority()`](http://man7.org/linux/man-pages/man2/setpriority.2.html) for
+more information.
+
+`resourceLimits` → An object, where each key refers to a Linux resource limit
+(such as `RLIMIT_NOFILE` and similar). Their values should be an object with
+two keys `cur` and `max` for the soft and hard resource limit. When logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the login process' resource limits to these
+values, which is then inherited by all the user's processes, see
+[`setrlimit()`](http://man7.org/linux/man-pages/man2/setrlimit.2.html) for more
+information.
+
+`locked` → A boolean value. If true the user account is locked, the user may
+not log in. If this field is missing it should be assumed to be false,
+i.e. logins are permitted. This field corresponds to the `sp_expire` field of
+`struct spwd` (i.e. the `/etc/shadow` data for a user) being set to zero or
+one.
+
+`notBeforeUSec` → An unsigned 64bit integer value, indicating a time in µs since
+the UNIX epoch (1970) before which the record should be considered invalid for
+the purpose of logging in.
+
+`notAfterUSec` → Similar, but indicates the point in time *after* which logins
+shall not be permitted anymore. This corresponds to the `sp_expire` field of
+`struct spwd`, when it is set to a value larger than one, but provides finer
+granularity.
+
+`storage` → A string, one of `classic`, `luks`, `directory`, `subvolume`,
+`fscrypt`, `cifs`. Indicates the storage mechanism for the user's home
+directory. If `classic` the home directory is a plain directory as in classic
+UNIX. When `directory`, the home directory is a regular directory, but the
+`~/.identity` file in it contains the user's user record, so that the directory
+is self-contained. Similar, `subvolume` is a `btrfs` subvolume that also
+contains a `~/.identity` user record; `fscrypt` is an `fscrypt`-encrypted
+directory, also containing the `~/.identity` user record; `luks` is a per-user
+LUKS volume that is mounted as home directory, and `cifs` a home directory
+mounted from a Windows File Share. The five latter types are primarily used by
+`systemd-homed` when managing home directories, but may be used if other
+managers are used too. If this is not set `classic` is the implied default.
+
+`diskSize` → An unsigned 64bit integer, indicating the intended home directory
+disk space in bytes to assign to the user. Depending on the selected storage
+type this might be implement differently: for `luks` this is the intended size
+of the file system and LUKS volume, while for the others this likely translates
+to classic file system quota settings.
+
+`diskSizeRelative` → Similar to `diskSize` but takes a relative value, but
+specifies a fraction of the available disk space on the selected storage medium
+to assign to the user. This unsigned integer value is normalized to 2^32 =
+100%.
+
+`skeletonDirectory` → Takes a string with the absolute path to the skeleton
+directory to populate a new home directory from. This is only used when a home
+directory is first created, and defaults to `/etc/skel` if not defined.
+
+`accessMode` → Takes an unsigned integer in the range 0…511 indicating the UNIX
+access mask for the home directory when it is first created.
+
+`tasksMax` → Takes an unsigned 64bit integer indicating the maximum number of
+tasks the user may start in parallel during system runtime. This value is
+enforced on all tasks (i.e. processes and threads) the user starts or that are
+forked off these processes regardless if the change user identity (for example
+by setuid binaries/`su`/`sudo` and
+similar). [`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html)
+enforces this by setting the `TasksMax` slice property for the user's slice
+`user-$UID.slice`.
+
+`memoryHigh`/`memoryMax` → These take unsigned 64bit integers indicating upper
+memory limits for all processes of the user (plus all processes forked off them
+that might have changed user identity), in bytes. Enforced by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html),
+similar to `tasksMax`.
+
+`cpuWeight`/`ioWeight` → These take unsigned integers in the range 1…10000
+(defaults to 100) and configure the CPU and IO scheduling weights for the
+user's processes as a whole. Also enforced by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html),
+similar to `tasksMax`, `memoryHigh` and `memoryMax`.
+
+`mountNoDevices`/`mountNoSuid`/`mountNoExecute` → Three booleans that control
+the `nodev`, `nosuid`, `noexec` mount flags of the user's home
+directories. Note that these booleans are only honored if the home directory
+is managed by a subsystem such as `systemd-homed.service` that automatically
+mounts home directories on login.
+
+`cifsDomain` → A string indicating the Windows File Sharing domain (CIFS) to
+use. This is generally useful, but particularly when `cifs` is used as storage
+mechanism for the user's home directory, see above.
+
+`cifsUserName` → A string indicating the Windows File Sharing user name (CIFS)
+to associate this user record with. This is generally useful, but particularly
+useful when `cifs` is used as storage mechanism for the user's home directory,
+see above.
+
+`cifsService` → A string indicating the Windows File Share service (CIFS) to
+mount as home directory of the user on login.
+
+`imagePath` → A string with an absolute file system path to the file, directory
+or block device to use for storage backing the home directory. If the `luks`
+storage is used this refers to the loopback file or block device node to store
+the LUKS volume on. For `fscrypt`, `directory`, `subvolume` this refers to the
+directory to bind mount as home directory on login. Not defined for `classic`
+or `cifs`.
+
+`homeDirectory` → A string with an absolute file system path to the home
+directory. This is where the image indicated in `imagePath` is mounted to on
+login and thus indicates the application facing home directory while the home
+directory is active, and is what the user's `$HOME` environment variable is set
+to during log-in. It corresponds to the `pw_dir` field of `struct passwd`.
+
+`uid` → An unsigned integer in the range 0…4294967295: the numeric UNIX user ID (UID) to
+use for the user. This corresponds to the `pw_uid` field of `struct passwd`.
+
+`gid` → An unsigned integer in the range 0…4294967295: the numeric UNIX group
+ID (GID) to use for the user. This corresponds to the `pw_gid` field of
+`struct passwd`.
+
+`memberOf` → An array of strings, each indicating a UNIX group this user shall
+be a member of. The listed strings must be valid group names, but it is not
+required that all groups listed exist in all contexts: any entry for which no
+group exists should be silently ignored.
+
+`fileSystemType` → A string, one of `ext4`, `xfs`, `btrfs` (possibly others) to
+use as file system for the user's home directory. This is primarily relevant
+when the storage mechanism used is `luks` as a file system to use inside the
+LUKS container must be selected.
+
+`partitionUuid` → A string containing a lower-case, text-formatted UUID, referencing
+the GPT partition UUID the home directory is located in. This is primarily
+relevant when the storage mechanism used is `luks`.
+
+`luksUuid` → A string containing a lower-case, text-formatted UUID, referencing
+the LUKS volume UUID the home directory is located in. This is primarily
+relevant when the storage mechanism used is `luks`.
+
+`fileSystemUuid` → A string containing a lower-case, text-formatted UUID,
+referencing the file system UUID the home directory is located in. This is
+primarily relevant when the storage mechanism used is `luks`.
+
+`luksDiscard` → A boolean. If true and `luks` storage is used controls whether
+the loopback block devices, LUKS and the file system on top shall be used in
+`discard` mode, i.e. erased sectors should always be returned to the underlying
+storage. If false and `luks` storage is used turns this behavior off. In
+addition, depending on this setting an `FITRIM` or `fallocate()` operation is
+executed to make sure the image matches the selected option.
+
+`luksCipher` → A string, indicating the cipher to use for the LUKS storage mechanism.
+
+`luksCipherMode` → A string, selecting the cipher mode to use for the LUKS storage mechanism.
+
+`luksVolumeKeySize` → An unsigned integer, indicating the volume key length in
+bytes to use for the LUKS storage mechanism.
+
+`luksPbkdfHashAlgorithm` → A string, selecting the hash algorithm to use for
+the PBKDF operation for the LUKS storage mechanism.
+
+`luksPbkdfType` → A string, indicating the PBKDF type to use for the LUKS storage mechanism.
+
+`luksPbkdfTimeCostUSec` → An unsigned 64bit integer, indicating the intended
+time cost for the PBKDF operation, when the LUKS storage mechanism is used, in
+µs.
+
+`luksPbkdfMemoryCost` → An unsigned 64bit integer, indicating the intended
+memory cost for the PBKDF operation, when LUKS storage is used, in bytes.
+
+`luksPbkdfParallelThreads` → An unsigned 64bit integer, indicating the intended
+required parallel threads for the PBKDF operation, when LUKS storage is used.
+
+`service` → A string declaring the service that defines or manages this user
+record. It is recommended to use reverse domain name notation for this. For
+example, if `systemd-homed` manages a user a string of `io.systemd.Home` is
+used for this.
+
+`rateLimitIntervalUSec` → An unsigned 64bit integer that configures the
+authentication rate limiting enforced on the user account. This specifies a
+timer interval (in µs) within which to count authentication attempts. When the
+counter goes above the value configured n `rateLimitIntervalBurst` log-ins are
+temporarily refused until the interval passes.
+
+`rateLimitIntervalBurst` → An unsigned 64bit integer, closely related to
+`rateLimitIntervalUSec`, that puts a limit on authentication attempts within
+the configured time interval.
+
+`enforcePasswordPolicy` → A boolean. Configures whether to enforce the system's
+password policy when creating the home directory for the user or changing the
+user's password. By default the policy is enforced, but if this field is false
+it is bypassed.
+
+`autoLogin` → A boolean. If true the user record is marked as suitable for
+auto-login. Systems are supposed to automatically log in a user marked this way
+during boot, if there's exactly one user on it defined this way.
+
+`stopDelayUSec` → An unsigned 64bit integer, indicating the time in µs the
+per-user service manager is kept around after the user fully logged out. This
+value is honored by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html). If
+set to zero the per-user service manager is immediately terminated when the
+user logs out, and longer values optimize high-frequency log-ins as the
+necessary work to set up and tear down a log-in is reduced if the service
+manager stays running.
+
+`killProcesses` → A boolean. If true all processes of the user are
+automatically killed when the user logs out. This is enforced by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html). If
+false any processes left around when the user logs out are left running.
+
+`passwordChangeMinUSec`/`passwordChangeMaxUSec` → An unsigned 64bit integer,
+encoding how much time has to pass at least/at most between password changes of
+the user. This corresponds with the `sp_min` and `sp_max` fields of `struct
+spwd` (i.e. the `/etc/shadow` entries of the user), but offers finer
+granularity.
+
+`passwordChangeWarnUSec` → An unsigned 64bit integer, encoding how much time to
+warn the user before their password expires, in µs. This corresponds with the
+`sp_warn` field of `struct spwd`.
+
+`passwordChangeInactiveUSec` → An unsigned 64bit integer, encoding how much
+time has to pass after the password expired that the account is
+deactivated. This corresponds with the `sp_inact` field of `struct spwd`.
+
+`passwordChangeNow` → A boolean. If true the user has to change their password
+on next login. This corresponds with the `sp_lstchg` field of `struct spwd`
+being set to zero.
+
+`pkcs11TokenUri` → An array of strings, each with an RFC 7512 compliant PKCS#11
+URI referring to security token (or smart card) of some form, that shall be
+associated with the user and may be used for authentication. The URI is used to
+search for an X.509 certificate and associated private key that may be used to
+decrypt an encrypted secret key that is used to unlock the user's account (see
+below). It's undefined how precise the URI is: during log-in it is tested
+against all plugged in security tokens and if there's exactly one matching
+private key found with it it is used.
+
+`privileged` → An object, which contains the fields of the `privileged` section
+of the user record, see below.
+
+`perMachine` → An array of objects, which contain the `perMachine` section of
+the user record, and thus fields to apply on specific systems only, see below.
+
+`binding` → An object, keyed by machine IDs formatted as strings, pointing
+to objects that contain the `binding` section of the user record,
+i.e. additional fields that bind the user record to a specific machine, see
+below.
+
+`status` → An object, keyed by machine IDs formatted as strings, pointing to
+objects that contain the `status` section of the user record, i.e. additional
+runtime fields that expose the current status of the user record on a specific
+system, see below.
+
+`signature` → An array of objects, which contain cryptographic signatures of
+the user record, i.e. the fields of the `signature` section of the user record,
+see below.
+
+`secret` → An object, which contains the fields of the `secret` section of the
+user record, see below.
+
+## Fields in the `privileged` section
+
+As mentioned, the `privileged` section is encoded in a sub-object of the user
+record top-level object, in the `privileged` field. Any data included in this
+object shall only be visible to the administrator and the user themselves, and
+be suppressed implicitly when other users get access to a user record. It thus
+takes the role of the `/etc/shadow` records for each user, which has similarly
+restrictive access semantics. The following fields are currently defined:
+
+`passwordHint` → A user-selected password hint in free-form text. This should
+be a string like "What's the name of your first pet?", but is entirely for the
+user to choose.
+
+`hashPassword` → An array of strings, each containing a hashed UNIX password
+string, in the format
+[`crypt(3)`](http://man7.org/linux/man-pages/man3/crypt.3.html) generates. This
+corresponds with `sp_pwdp` field of `struct spwd` (and in a way the `pw_passwd`
+field of `struct passwd`).
+
+`sshAuthorizedKeys` → An array of strings, each listing an SSH public key that
+is authorized to access the account. The strings should follow the same format
+as the lines in the traditional `~/.ssh/authorized_key` file.
+
+`pkcs11EncryptedKey` → An array of objects. Each element of the array should be
+an object consisting of three string fields: `uri` shall contain a PKCS#11
+security token URI, `data` shall contain a Base64 encoded encrypted key and
+`hashedPassword` shall contain a UNIX password hash to test the key
+against. Authenticating with a security token against this account shall work
+as follows: the encrypted secret key is converted from its Base64
+representation into binary, then decrypted with the PKCS#11 `C_Decrypt()`
+function of the PKCS#11 module referenced by the specified URI, using the
+private key found on the same token. The resulting decrypted key is then
+Base64-encoded and tested against the specified UNIX hashed password. The
+Base64-enceded decrypted key may also be used to unlock further resources
+during log-in, for example the LUKS or `fscrypt` storage backend. It is
+generally recommended that for each entry in `pkcs11EncryptedKey` there's also
+a matching one in `pkcs11TokenUri` and vice versa, with the same URI, appearing
+in the same order, but this should not be required by applications processing
+user records.
+
+## Fields in the `perMachine` section
+
+As mentioned, the `perMachine` section contains settings that shall apply to
+specific systems only. This is primarily interesting for resource management
+properties as they tend to require a per-system focus, however they may be used
+for other purposes too.
+
+The `perMachine` field in the top-level object is an array of objects. When
+processing the user record first the various fields on the top-level object
+should be used. Then this array should be iterated in order, and the various
+settings be applied that match either the indicated machine ID or host
+name. There may be multiple array entries that match a specific system, in
+which case all the object's setting should be applied. If the same option is
+set in the top-level object as in a per-machine object the latter wins and
+entirely undoes the setting in the top-level object (i.e. no merging of
+properties that are arrays themselves is done). If the same option is set in
+multiple per-machine objects the one specified later in the array wins (and
+here too no merging of individual fields is done, the later field always wins
+in full).
+
+The following fields are defined in this section:
+
+`matchMachineId` → An array of strings with each a formatted 128bit ID in
+hex. If any of the specified IDs match the system's local machine ID
+(i.e. matches `/etc/machine-id`) the fields in this object are honored.
+
+`matchHostname` → An array of string with a each a valid hostname. If any of
+the specified hostnames match the system's local hostname, the fields in this
+object are honored. If both `matchHostname` and `matchMachineId` are used
+within the same array entry, the object is honored when either match succeeds,
+i.e. the two match types are combined in OR, not in AND.
+
+These two are the only two fields specific to this section. All other fields
+that may be used in this section are identical to the equally named ones in the
+`regular` section (i.e. at the top-level object). Specifically, these are:
+
+`iconName`, `location`, `shell`, `umask`, `environment`, `timeZone`,
+`preferredLanguage`, `niceLevel`, `resourceLimits`, `locked`, `notBeforeUSec`,
+`notAfterUSec`, `storage`, `diskSize`, `diskSizeRelative`, `skeletonDirectory`,
+`accessMode`, `tasksMax`, `memoryHigh`, `memoryMax`, `cpuWeight`, `ioWeight`,
+`mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
+`cifsUserName`, `cifsService`, `imagePath`, `uid`, `gid`, `memberOf`,
+`fileSystemType`, `partitionUuid`, `luksUuid`, `fileSystemUuid`, `luksDiscard`,
+`luksCipher`, `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
+`luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
+`luksPbkdfParallelThreads`, `rateLimitIntervalUSec`, `rateLimitBurst`,
+`enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`, `killProcesses`,
+`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
+`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`.
+
+## Fields in the `binding` section
+
+As mentioned, the `binding` section contains additional fields about the user
+record, that bind it to the local system. These fields are generally used by a
+local user manager (such as `systemd-homed.service`) to add in fields that make
+sense in a local context but not necessarily in a global one. For example, a
+user record that contains no `uid` field in the regular section is likely
+extended with one in the `binding` section to assign a local UID if no global
+UID is defined.
+
+All fields in the `binding` section only make sense in a local context and are
+suppressed when the user record is ported between systems. The `binding` section
+is generally persisted on the system but not in the home directories themselves
+and the home directory is supposed to be fully portable and thus not contain
+the information that `binding` is supposed to contain that binds the portable
+record to a specific system.
+
+The `binding` sub-object on the top-level user record object is keyed by the
+machine ID the binding is intended for, which point to an object with the
+fields of the bindings. These fields generally match fields that may also be
+defined in the `regular` and `perMachine` sections, however override
+both. Usually, the `binding` value should not contain settings different from
+those set via `regular` or `perMachine`, however this might happen if some
+settings are not supported locally (think: `fscrypt` is recorded as intended
+storage mechanism in the `regular` section, but the local kernel does not
+support `fscrypt`, hence `directory` was chosen as implicit fallback), or have
+been changed in the `regular` section through updates (e.g. a home directory
+was created with `luks` as storage mechanism but later the user record was
+updated to prefer `subvolume`, which however doesn't change the actual storage
+used already which is pinned in the `binding` section).
+
+The following fields are defined in the `binding` section. They all have an
+identical format and override their equally named counterparts in the `regular`
+and `perMachine` sections:
+
+`imagePath`, `homeDirectory`, `partitionUuid`, `luksUuid`, `fileSystemUuid`,
+`uid`, `gid`, `storage`, `fileSystemType`, `luksCipher`, `luksCipherMode`,
+`luksVolumeKeySize`.
+
+## Fields in the `status` section
+
+As mentioned, the `status` section contains additional fields about the user
+record that are exclusively acquired during runtime, and that expose runtime
+metrics of the user and similar metadata that shall not be persisted but are
+only acquired "on-the-fly" when requested.
+
+This section is arranged similarly to the `binding` section: the `status`
+sub-object of the top-level user record object is keyed by the machine ID,
+which points to the object with the fields defined here. The following fields
+are defined:
+
+`diskUsage` → An unsigned 64bit integer. The currently used disk space of the
+home directory in bytes. This value might be determined in different ways,
+depending on the selected storage mechanism. For LUKS storage this is the file
+size of the loopback file or block device size. For the
+directory/subvolume/fscrypt storage this is the current disk space used as
+reported by the file system quota subsystem.
+
+`diskFree` → An unsigned 64bit integer, denoting the number of "free" bytes in
+the disk space allotment, i.e. usually the difference between the disk size as
+reported by `diskSize` and the used already as reported in `diskFree`, but
+possibly skewed by metadata sizes, disk compression and similar.
+
+`diskSize` → An unsigned 64bit integer, denoting the disk space currently
+allotted to the user, in bytes. Depending on the storage mechanism this can mean
+different things (see above). In contrast to the top-level field of the same
+(or the one in the `perMachine` section), this field reports the current size
+allotted to the user, not the intended one. The values may differ when user
+records are updated without the home directory being re-sized.
+
+`diskCeiling`/`diskFloor` → Unsigned 64bit integers indicating upper and lower
+bounds when changing the `diskSize` value, in bytes. These values are typically
+derived from the underlying data storage, and indicate in which range the home
+directory may be re-sized in, i.e. in which sensible range the `diskSize` value
+should be kept.
+
+`state` → A string indicating the current state of the home directory. The
+precise set of values exposed here are up to the service managing the home
+directory to define (i.e. are up to the service identified with the `service`
+field below). However, it is recommended to stick to a basic vocabulary here:
+`inactive` for a home directory currently not mounted, `absent` for a home
+directory that cannot be mounted currently because it does not exist on the
+local system, `active` for a home directory that is currently mounted and
+accessible.
+
+`service` → A string identifying the service that manages this user record. For
+example `systemd-homed.service` sets this to `io.systemd.Home` to all user
+records it manages. This is particularly relevant to define clearly the context
+in which `state` lives, see above. Note that this field also exists on the
+top-level object (i.e. in the `regular` section), which it overrides. The
+`regular` field should be used if conceptually the user record can only be
+managed by the specified service, and this `status` field if it can
+conceptually be managed by different managers, but currently is managed by the
+specified one.
+
+`signedLocally` → A boolean. If true indicates that the user record is signed
+by a public key for which the private key is available locally. This means that
+the user record may be modified locally as it can be re-signed with the private
+key. If false indicates that the user record is signed by a public key
+recognized by the local manager but whose private key is not available
+locally. This means the user record cannot be modified locally as it couldn't
+be signed afterwards.
+
+`goodAuthenticationCounter` → An unsigned 64bit integer. This counter is
+increased by one on every successful authentication attempt, i.e. an
+authentication attempt where a security token of some form was presented and it
+was correct.
+
+`badAuthenticationCounter` → An unsigned 64bit integer. This counter is
+increased by one on every unsuccessfully authentication attempt, i.e. an
+authentication attempt where a security token of some form was presented and it
+was incorrect.
+
+`lastGoodAuthenticationUSec` → An unsigned 64bit integer, indicating the time
+of the last successful authentication attempt in µs since the UNIX epoch (1970).
+
+`lastBadAuthenticationUSec` → Similar, but the timestamp of the last
+unsuccessfully authentication attempt.
+
+`rateLimitBeginUSec` → An unsigned 64bit integer: the µs timestamp since the
+UNIX epoch (1970) where the most recent rate limiting interval has been
+started, as configured with `rateLimitIntervalUSec`.
+
+`rateLimitCount` → An unsigned 64bit integer, counting the authentication
+attempts in the current rate limiting interval, see above. If this counter
+grows beyond the value configured in `rateLimitBurst` authentication attempts
+are temporarily refused.
+
+`removable` → A boolean value. If true the manager of this user record
+determined the home directory being on removable media. If false it was
+determined the home directory is in internal built-in media. (This is used by
+`systemd-logind.service` to automatically pick the right default value for
+`stopDelayUSec` if the field is not explicitly specified: for home directories
+on removable media the delay is selected very low to minimize the chance the
+home directory remains in unclean state if the storage device is removed from
+the system by the user).
+
+## Fields in the `signature` section
+
+As mentioned, the `signature` section of the user record may contain one or
+more cryptographic signatures of the user record. Like all others, this section
+is optional, and only used when cryptographic validation of user records shall
+be used. Specifically, all user records managed by `systemd-homed.service` will
+carry such signatures and the service refuses managing user records that come
+without signature or with signatures not recognized by any locally defined
+public key.
+
+The `signature` field in the top-level user record object is an array of
+objects. Each object encapsulates one signature and has two fields: `data` and
+`key` (both are strings). The `data` field contains the actual signature,
+encoded in base64, the `key` field contains a copy of the public key whose
+private key was used to make the signature, in PEM format. Currently only
+signatures with Ed25519 keys are defined.
+
+Before signing the user record should be brought into "normalized" form,
+i.e. the keys in all objects should be sorted alphabetically. All redundant
+white-space and newlines should be removed and the JSON text then signed.
+
+The signatures only cover the `regular`, `perMachine` and `privileged` sections
+of the user records, all other sections (include `signature` itself), are
+removed before the signature is calculated.
+
+Rationale for signing and threat model: while a multi-user operating system
+like Linux strives for being sufficiently secure even after a user acquired a
+local login session reality tells us this is not the case. Hence it is
+essential to restrict carefully which users may gain access to a system and
+which ones shall not. A minimal level of trust must be established between
+system, user record and the user themselves before a log-in request may be
+permitted. In particular if the home directory is provided in its own LUKS2
+encapsulated file system it is essential this trust is established before the
+user logs in (and hence the file system mounted), since file system
+implementations on Linux are well known to be relatively vulnerable to rogue
+disk images. User records and home directories in many context are expected to
+be something shareable between multiple systems, and the transfer between them
+might not happen via exclusively trusted channels. Hence it's essential that
+the user record is not manipulated between uses. Finally, resource management
+(which may be done by the various fields of the user record) is security
+sensitive, since it should forcefully lock the user into the assigned resource
+usage and not allow them to use more. The requirement of being able to trust
+the user record data combined with the potential transfer over untrusted
+channels suggest a cryptographic signature mechanism where only user records
+signed by a recognized key are permitted to log in locally.
+
+Note that other mechanisms for establishing sufficient trust exist too, and are
+perfectly valid as well. For example, systems like LDAP/ActiveDirectory
+generally insist on user record transfer from trusted servers via encrypted TLS
+channels only. Or traditional UNIX users created locally in `/etc/passwd` never
+exist outside of the local trusted system, hence transfer and trust in the
+source are not an issue. The major benefit of operating with signed user
+records is that they are self-sufficiently trusted, not relying on a secure
+channel for transfer, and thus being compatible with a more distributed model
+of home directory transfer, including on USB sticks and such.
+
+## Fields in the `secret` section
+
+As mentioned, the `secret` section of the user record should never be persisted
+nor transferred across machines. It is only defined in short-lived operations,
+for example when a user record is first created or registered, as the secret
+key data needs to be available to derive encryption keys from and similar.
+
+The `secret` field of the top-level user record contains the following fields:
+
+`password` → an array of strings, each containing a plain text password.
+
+`pkcs11Pin` → an array of strings, each containing a plain text PIN, suitable
+for unlocking PKCS#11 security tokens that require that.
+
+`pkcs11ProtectedAuthenticationPathPermitted` → a boolean. If set to true allows
+the receiver to use the PKCS#11 "protected authentication path" (i.e. a
+physical button/touch element on the security token) for authenticating the
+user. If false or unset authentication this way shall not be attempted.
+
+## Mapping to `struct passwd` and `struct spwd`
+
+When mapping classic UNIX user records (i.e. `struct passwd` and `struct spwd`)
+to JSON user records the following mappings should be applied:
+
+| Structure | Field | Section | Field | Condition |
+|-----------------|-------------|--------------|------------------------------|----------------------------|
+| `struct passwd` | `pw_name` | `regular` | `userName` | |
+| `struct passwd` | `pw_passwd` | `privileged` | `password` | (See notes below) |
+| `struct passwd` | `pw_uid` | `regular` | `uid` | |
+| `struct passwd` | `pw_gid` | `regular` | `gid` | |
+| `struct passwd` | `pw_gecos` | `regular` | `realName` | |
+| `struct passwd` | `pw_dir` | `regular` | `homeDirectory` | |
+| `struct passwd` | `pw_shell` | `regular` | `shell` | |
+| `struct spwd` | `sp_namp` | `regular` | `userName` | |
+| `struct spwd` | `sp_pwdp` | `privileged` | `password` | (See notes below) |
+| `struct spwd` | `sp_lstchg` | `regular` | `lastPasswordChangeUSec` | (if `sp_lstchg` > 0) |
+| `struct spwd` | `sp_lstchg` | `regular` | `passwordChangeNow` | (if `sp_lstchg` == 0) |
+| `struct spwd` | `sp_min` | `regular` | `passwordChangeMinUSec` | |
+| `struct spwd` | `sp_max` | `regular` | `passwordChangeMaxUSec` | |
+| `struct spwd` | `sp_warn` | `regular` | `passwordChangeWarnUSec` | |
+| `struct spwd` | `sp_inact` | `regular` | `passwordChangeInactiveUSec` | |
+| `struct spwd` | `sp_expire` | `regular` | `locked` | (if `sp_expire` in [0, 1]) |
+| `struct spwd` | `sp_expire` | `regular` | `notAfterUSec` | (if `sp_expire` > 1) |
+
+At this time almost all Linux machines employ shadow passwords, thus the
+`pw_passwd` field in `struct passwd` is set to `"x"`, and the actual password
+is stored in the shadow entry `struct spwd`'s field `sp_pwdp`.
+
+## Extending These Records
+
+User records following this specifications are supposed to be extendable for
+various applications. In general, subsystems are free to introduce their own
+keys, as long as:
+
+* Care should be taken to place the keys in the right section, i.e. the most
+ appropriate for the data field.
+
+* Care should be taken to avoid namespace clashes. Please prefix your fields
+ with a short identifier of your project to avoid ambiguities and
+ incompatibilities.
+
+* This specification is supposed to be a living specification. If you need
+ additional fields, please consider submitting them upstream for inclusion in
+ this specification. If they are reasonably universally useful, it would be
+ best to list them here.
+
+## Examples
+
+The shortest valid user record looks like this:
+
+```json
+{
+ "userName" : "u"
+}
+```
+
+A reasonable user record for a system user might look like this:
+
+```json
+{
+ "userName" : "httpd",
+ "uid" : 473,
+ "gid" : 473,
+ "disposition" : "system",
+ "locked" : true
+}
+```
+
+A fully featured user record associated with a home directory managed by
+`systemd-homed.service` might look like this:
+
+```json
+{
+ "autoLogin" : true,
+ "binding" : {
+ "15e19cf24e004b949ddaac60c74aa165" : {
+ "fileSystemType" : "ext4",
+ "fileSystemUuid" : "758e88c8-5851-4a2a-b88f-e7474279c111",
+ "gid" : 60232,
+ "homeDirectory" : "/home/grobie",
+ "imagePath" : "/home/grobie.home",
+ "luksCipher" : "aes",
+ "luksCipherMode" : "xts-plain64",
+ "luksUuid" : "e63581ba-79fb-4226-b9de-1888393f7573",
+ "luksVolumeKeySize" : 32,
+ "partitionUuid" : "41f9ce04-c827-4b74-a981-c669f93eb4dc",
+ "storage" : "luks",
+ "uid" : 60232
+ }
+ },
+ "disposition" : "regular",
+ "enforcePasswordPolicy" : false,
+ "lastChangeUSec" : 1565950024279735,
+ "memberOf" : [
+ "wheel"
+ ],
+ "privileged" : {
+ "hashedPassword" : [
+ "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
+ ]
+ },
+ "signature" : [
+ {
+ "data" : "LU/HeVrPZSzi3MJ0PVHwD5m/xf51XDYCrSpbDRNBdtF4fDVhrN0t2I2OqH/1yXiBidXlV0ptMuQVq8KVICdEDw==",
+ "key" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/QT6kQWOAMhDJf56jBmszEQQpJHqDsGDMZOdiptBgRk=\n-----END PUBLIC KEY-----\n"
+ }
+ ],
+ "userName" : "grobie",
+ "status" : {
+ "15e19cf24e004b949ddaac60c74aa165" : {
+ "goodAuthenticationCounter" : 16,
+ "lastGoodAuthenticationUSec" : 1566309343044322,
+ "rateLimitBeginUSec" : 1566309342340723,
+ "rateLimitCount" : 1,
+ "state" : "inactive",
+ "service" : "io.systemd.Home",
+ "diskSize" : 161118667776,
+ "diskCeiling" : 190371729408,
+ "diskFloor" : 5242880,
+ "signedLocally" : true
+ }
+ }
+}
+```
+
+When `systemd-homed.service` manages a home directory it will also include a
+version of the user record in the home directory itself in the `~/.identity`
+file. This version lacks the `binding` and `status` sections which are used for
+local management of the user, but are not intended to be portable between
+systems. It would hence look like this:
+
+```json
+{
+ "autoLogin" : true,
+ "disposition" : "regular",
+ "enforcePasswordPolicy" : false,
+ "lastChangeUSec" : 1565950024279735,
+ "memberOf" : [
+ "wheel"
+ ],
+ "privileged" : {
+ "hashedPassword" : [
+ "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
+ ]
+ },
+ "signature" : [
+ {
+ "data" : "LU/HeVrPZSzi3MJ0PVHwD5m/xf51XDYCrSpbDRNBdtF4fDVhrN0t2I2OqH/1yXiBidXlV0ptMuQVq8KVICdEDw==",
+ "key" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/QT6kQWOAMhDJf56jBmszEQQpJHqDsGDMZOdiptBgRk=\n-----END PUBLIC KEY-----\n"
+ }
+ ],
+ "userName" : "grobie",
+}
+```
<footer class="site-footer">
- <p>© systemd, 2019</p>
+ <p>© systemd, 2020</p>
<p><a href="https://github.com/systemd/systemd">Website source</a></p>
</footer>
Other parts include a logging daemon, utilities to control basic system configuration like the hostname, date, locale, maintain a list of logged-in users and running containers and virtual machines, system accounts, runtime directories and settings, and daemons to manage simple network configuration, network time synchronization, log forwarding, and name resolution.
-See the introductory blog story and three status updates for a longer introduction. Also see the [Wikipedia article](https://en.wikipedia.org/wiki/systemd).
-
---
{% assign by_category = site.pages | group_by:"category" %}
{% endif %}
{% endfor %}
+## See also
+
+* [Introductory blog story](http://0pointer.de/blog/projects/systemd.html)
+* [Three](http://0pointer.de/blog/projects/systemd-update.html) [status](http://0pointer.de/blog/projects/systemd-update-2.html) [updates](http://0pointer.de/blog/projects/systemd-update-3.html)
+* The [Wikipedia article](https://en.wikipedia.org/wiki/systemd)
+
+---
+
+<pre style="color:white; background-color:black; font-size:smaller; padding:6pt 8pt">
+Welcome to <span style="color:blue">Fedora 20 (Heisenbug)</span>!
+
+[ <span style="color:green">OK</span> ] Reached target Remote File Systems.
+[ <span style="color:green">OK</span> ] Listening on Delayed Shutdown Socket.
+[ <span style="color:green">OK</span> ] Listening on /dev/initctl Compatibility Named Pipe.
+[ <span style="color:green">OK</span> ] Reached target Paths.
+[ <span style="color:green">OK</span> ] Reached target Encrypted Volumes.
+[ <span style="color:green">OK</span> ] Listening on Journal Socket.
+ Mounting Huge Pages File System...
+ Mounting POSIX Message Queue File System...
+ Mounting Debug File System...
+ Starting Journal Service...
+[ <span style="color:green">OK</span> ] Started Journal Service.
+ Mounting Configuration File System...
+ Mounting FUSE Control File System...
+[ <span style="color:green">OK</span> ] Created slice Root Slice.
+[ <span style="color:green">OK</span> ] Created slice User and Session Slice.
+[ <span style="color:green">OK</span> ] Created slice System Slice.
+[ <span style="color:green">OK</span> ] Reached target Slices.
+[ <span style="color:green">OK</span> ] Reached target Swap.
+ Mounting Temporary Directory...
+[ <span style="color:green">OK</span> ] Reached target Local File Systems (Pre).
+ Starting Load Random Seed...
+ Starting Load/Save Random Seed...
+[ <span style="color:green">OK</span> ] Mounted Huge Pages File System.
+[ <span style="color:green">OK</span> ] Mounted POSIX Message Queue File System.
+[ <span style="color:green">OK</span> ] Mounted Debug File System.
+[ <span style="color:green">OK</span> ] Mounted Configuration File System.
+[ <span style="color:green">OK</span> ] Mounted FUSE Control File System.
+[ <span style="color:green">OK</span> ] Mounted Temporary Directory.
+[ <span style="color:green">OK</span> ] Started Load Random Seed.
+[ <span style="color:green">OK</span> ] Started Load/Save Random Seed.
+[ <span style="color:green">OK</span> ] Reached target Local File Systems.
+ Starting Recreate Volatile Files and Directories...
+ Starting Trigger Flushing of Journal to Persistent Storage...
+[ <span style="color:green">OK</span> ] Started Recreate Volatile Files and Directories.
+ Starting Update UTMP about System Reboot/Shutdown...
+[ <span style="color:green">OK</span> ] Started Trigger Flushing of Journal to Persistent Storage.
+[ <span style="color:green">OK</span> ] Started Update UTMP about System Reboot/Shutdown.
+[ <span style="color:green">OK</span> ] Reached target System Initialization.
+[ <span style="color:green">OK</span> ] Reached target Timers.
+[ <span style="color:green">OK</span> ] Listening on D-Bus System Message Bus Socket.
+[ <span style="color:green">OK</span> ] Reached target Sockets.
+[ <span style="color:green">OK</span> ] Reached target Basic System.
+ Starting Permit User Sessions...
+ Starting D-Bus System Message Bus...
+[ <span style="color:green">OK</span> ] Started D-Bus System Message Bus.
+ Starting Login Service...
+ Starting Cleanup of Temporary Directories...
+[ <span style="color:green">OK</span> ] Started Permit User Sessions.
+[ <span style="color:green">OK</span> ] Started Cleanup of Temporary Directories.
+ Starting Console Getty...
+[ <span style="color:green">OK</span> ] Started Console Getty.
+[ <span style="color:green">OK</span> ] Reached target Login Prompts.
+[ <span style="color:green">OK</span> ] Started Login Service.
+[ <span style="color:green">OK</span> ] Reached target Multi-User System.
+
+Fedora release 20 (Heisenbug)
+Kernel 3.9.2-200.fc18.x86_64 on an x86_64 (console)
+
+fedora login:
+</pre>
# This file is part of systemd.
passwd: compat mymachines systemd
-group: compat mymachines systemd
+group: compat [SUCCESS=merge] mymachines [SUCCESS=merge] systemd
shadow: compat
hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
# You really want to adjust this to your local distribution. If you use this
# unmodified you are not building systems safely and securely.
-auth sufficient pam_unix.so nullok try_first_pass
-auth required pam_deny.so
+auth sufficient pam_unix.so
+-auth sufficient pam_systemd_home.so
+auth required pam_deny.so
-account required pam_nologin.so
-account sufficient pam_unix.so
-account required pam_permit.so
+account required pam_nologin.so
+-account sufficient pam_systemd_home.so
+account sufficient pam_unix.so
+account required pam_permit.so
-password sufficient pam_unix.so nullok sha512 shadow try_first_pass try_authtok
-password required pam_deny.so
+-password sufficient pam_systemd_home.so
+password sufficient pam_unix.so sha512 shadow try_first_pass try_authtok
+password required pam_deny.so
--session optional pam_keyinit.so revoke
--session optional pam_loginuid.so
--session optional pam_systemd.so
-session sufficient pam_unix.so
+-session optional pam_keyinit.so revoke
+-session optional pam_loginuid.so
+-session optional pam_systemd_home.so
+-session optional pam_systemd.so
+session required pam_unix.so
- sudo apt-get update -y
- sudo apt-get build-dep -y systemd
- sudo apt-get install -y python3-pip
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older
-# python 3.5
-# # See: https://github.com/mesonbuild/meson/issues/6427
-#
-- pip3 install meson==0.52.1 ninja
+- sudo apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
+- 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
ID_OUI_FROM_DATABASE=HITACHI, LTD.
OUI:000088*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:000089*
ID_OUI_FROM_DATABASE=CAYMAN SYSTEMS INC.
ID_OUI_FROM_DATABASE=Bri-Link Technologies Co., Ltd
OUI:00010F*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:000110*
ID_OUI_FROM_DATABASE=Gotham Networks
ID_OUI_FROM_DATABASE=Chr. Mayr GmbH & Co. KG
OUI:000480*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:000481*
ID_OUI_FROM_DATABASE=Econolite Control Products, Inc.
ID_OUI_FROM_DATABASE=Airocon, Inc.
OUI:00051E*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:00051F*
ID_OUI_FROM_DATABASE=Taijin Media Co., Ltd.
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:000533*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:000534*
ID_OUI_FROM_DATABASE=Northstar Engineering Ltd.
ID_OUI_FROM_DATABASE=ACKSYS Communications & systems
OUI:000991*
- ID_OUI_FROM_DATABASE=GE Fanuc Automation Manufacturing, Inc.
+ ID_OUI_FROM_DATABASE=Intelligent Platforms, LLC.
OUI:000992*
ID_OUI_FROM_DATABASE=InterEpoch Technology,INC.
ID_OUI_FROM_DATABASE=Scientific Research Corporation
OUI:000A0D*
- ID_OUI_FROM_DATABASE=FCI Deutschland GmbH
+ ID_OUI_FROM_DATABASE=Amphenol
OUI:000A0E*
ID_OUI_FROM_DATABASE=Invivo Research Inc.
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:000C32*
- ID_OUI_FROM_DATABASE=Avionic Design Development GmbH
+ ID_OUI_FROM_DATABASE=Avionic Design GmbH
OUI:000C33*
ID_OUI_FROM_DATABASE=Compucase Enterprise Co. Ltd.
ID_OUI_FROM_DATABASE=FreeHand Systems, Inc.
OUI:000CDB*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:000CDC*
ID_OUI_FROM_DATABASE=BECS Technology, Inc
ID_OUI_FROM_DATABASE=Infinico Corporation
OUI:000EF3*
- ID_OUI_FROM_DATABASE=Smarthome
+ ID_OUI_FROM_DATABASE=Smartlabs, Inc.
OUI:000EF4*
ID_OUI_FROM_DATABASE=Kasda Networks Inc
ID_OUI_FROM_DATABASE=LeapComm Communication Technologies Inc.
OUI:001258*
- ID_OUI_FROM_DATABASE=Activis Polska
+ ID_OUI_FROM_DATABASE=TechVoIP Sp z o.o.
OUI:001259*
ID_OUI_FROM_DATABASE=THERMO ELECTRON KARLSRUHE
ID_OUI_FROM_DATABASE=IFOTEC
OUI:0012F2*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:0012F3*
ID_OUI_FROM_DATABASE=connectBlue AB
ID_OUI_FROM_DATABASE=Contemporary Research Corp
OUI:0014C9*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:0014CA*
ID_OUI_FROM_DATABASE=Key Radio Systems Limited
ID_OUI_FROM_DATABASE=Xipher Technologies, LLC
OUI:001977*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:001978*
ID_OUI_FROM_DATABASE=Datum Systems, Inc.
ID_OUI_FROM_DATABASE=Scan Engineering Telecom
OUI:001B85*
- ID_OUI_FROM_DATABASE=MAN Diesel SE
+ ID_OUI_FROM_DATABASE=MAN Energy Solutions
OUI:001B86*
ID_OUI_FROM_DATABASE=Bosch Access Systems GmbH
ID_OUI_FROM_DATABASE=Netio Technologies Co., Ltd
OUI:001BED*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:001BEE*
ID_OUI_FROM_DATABASE=Nokia Danmark A/S
ID_OUI_FROM_DATABASE=Motorola - BSG
OUI:002438*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:002439*
ID_OUI_FROM_DATABASE=Digital Barriers Advanced Technologies
ID_OUI_FROM_DATABASE=RICOH COMPANY,LTD.
OUI:002674*
- ID_OUI_FROM_DATABASE=Electronic Solutions, Inc.
+ ID_OUI_FROM_DATABASE=Hunter Douglas
OUI:002675*
ID_OUI_FROM_DATABASE=Aztech Electronics Pte Ltd
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:0027F8*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:00289F*
ID_OUI_FROM_DATABASE=Semptian Co., Ltd.
OUI:005D73*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:005E0C*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
OUI:005F86*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
ID_OUI_FROM_DATABASE=Dialogic Corporation
OUI:006069*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:00606A*
ID_OUI_FROM_DATABASE=MITSUBISHI WIRELESS COMMUNICATIONS. INC.
ID_OUI_FROM_DATABASE=Kayser-Threde GmbH
OUI:0060DF*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:0060E0*
ID_OUI_FROM_DATABASE=AXIOM TECHNOLOGY CO., LTD.
OUI:0064A6*
ID_OUI_FROM_DATABASE=Maquet CardioVascular
+OUI:006619*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:00664B*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:0068EB*
ID_OUI_FROM_DATABASE=HP Inc.
+OUI:00692D*
+ ID_OUI_FROM_DATABASE=Sunnovo International Limited
+
OUI:0069670*
ID_OUI_FROM_DATABASE=Annapurna labs
OUI:008865*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:0088BA*
+ ID_OUI_FROM_DATABASE=NC&C
+
OUI:008A96*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:0094A1*
ID_OUI_FROM_DATABASE=F5 Networks, Inc.
+OUI:0094EC*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:009569*
ID_OUI_FROM_DATABASE=LSD Science and Technology Co.,Ltd.
OUI:009EC8*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+OUI:009EEE*
+ ID_OUI_FROM_DATABASE=Positivo Tecnologia S.A.
+
OUI:00A000*
ID_OUI_FROM_DATABASE=CENTILLION NETWORKS, INC.
OUI:00AD63*
ID_OUI_FROM_DATABASE=Dedicated Micros Malta LTD
+OUI:00ADD5*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:00AECD*
ID_OUI_FROM_DATABASE=Pensando Systems
OUI:00B78D*
ID_OUI_FROM_DATABASE=Nanjing Shining Electric Automation Co., Ltd
+OUI:00B7A8*
+ ID_OUI_FROM_DATABASE=Heinzinger electronic GmbH
+
OUI:00B810*
ID_OUI_FROM_DATABASE=Yichip Microelectronics (Hangzhou) Co.,Ltd
ID_OUI_FROM_DATABASE=TALX CORPORATION
OUI:00E052*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:00E053*
ID_OUI_FROM_DATABASE=CELLPORT LABS, INC.
OUI:04209A*
ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company
+OUI:042144*
+ ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd
+
OUI:04214C*
ID_OUI_FROM_DATABASE=Insight Energy Ventures LLC
OUI:045FA7*
ID_OUI_FROM_DATABASE=Shenzhen Yichen Technology Development Co.,LTD
+OUI:045FB9*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:046169*
ID_OUI_FROM_DATABASE=MEDIA GLOBAL LINKS CO., LTD.
OUI:048C03*
ID_OUI_FROM_DATABASE=ThinPAD Technology (Shenzhen)CO.,LTD
+OUI:048C16*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:048C9A*
ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
OUI:04BD88*
ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+OUI:04BDBF*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:04BF6D*
ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
OUI:04F13E*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:04F169*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:04F17D*
ID_OUI_FROM_DATABASE=Tarana Wireless
OUI:04F4BC*
ID_OUI_FROM_DATABASE=Xena Networks
+OUI:04F5F4*
+ ID_OUI_FROM_DATABASE=Proxim Wireless
+
OUI:04F7E4*
ID_OUI_FROM_DATABASE=Apple, Inc.
ID_OUI_FROM_DATABASE=Xyplex, Inc.
OUI:080088*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:080089*
ID_OUI_FROM_DATABASE=Kinetics
OUI:08028E*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:080342*
+ ID_OUI_FROM_DATABASE=Palo Alto Networks
+
OUI:080371*
ID_OUI_FROM_DATABASE=KRG CORPORATE
OUI:086A0A*
ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP
+OUI:086BD1*
+ ID_OUI_FROM_DATABASE=Shenzhen SuperElectron Technology Co.,Ltd.
+
OUI:086BD7*
ID_OUI_FROM_DATABASE=Silicon Laboratories
OUI:08863B*
ID_OUI_FROM_DATABASE=Belkin International Inc.
+OUI:0887C6*
+ ID_OUI_FROM_DATABASE=INGRAM MICRO SERVICES
+
OUI:088C2C*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:08A95A*
ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
+OUI:08AA55*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:08ACA5*
ID_OUI_FROM_DATABASE=Benu Video, Inc.
OUI:08AF78*
ID_OUI_FROM_DATABASE=Totus Solutions, Inc.
+OUI:08B055*
+ ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP
+
OUI:08B258*
ID_OUI_FROM_DATABASE=Juniper Networks
OUI:08BEAC*
ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd.
+OUI:08BFA0*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:08C021*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:08C0EB*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
OUI:08C5E1*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
ID_OUI_FROM_DATABASE=SHENZHEN BILIAN ELECTRONIC CO.,LTD
OUI:08EA44*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:08EB29*
ID_OUI_FROM_DATABASE=Jiangsu Huitong Group Co.,Ltd.
OUI:08F2F4*
ID_OUI_FROM_DATABASE=Net One Partners Co.,Ltd.
+OUI:08F458*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:08F4AB*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:0C1420*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:0C14D2*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
OUI:0C1539*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:0C2755*
ID_OUI_FROM_DATABASE=Valuable Techologies Limited
+OUI:0C29EF*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:0C2A69*
ID_OUI_FROM_DATABASE=electric imp, incorporated
OUI:0C3021*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:0C35FE*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:0C3747*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:0C3796*
+ ID_OUI_FROM_DATABASE=BIZLINK TECHNOLOGY, INC.
+
OUI:0C37DC*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:0C4885*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+OUI:0C48C6*
+ ID_OUI_FROM_DATABASE=CELESTICA INC.
+
OUI:0C4933*
ID_OUI_FROM_DATABASE=Sichuan Jiuzhou Electronic Technology Co., Ltd.
OUI:0C8126*
ID_OUI_FROM_DATABASE=Juniper Networks
+OUI:0C817D*
+ ID_OUI_FROM_DATABASE=EEP Elektro-Elektronik Pranjic GmbH
+
OUI:0C8230*
ID_OUI_FROM_DATABASE=SHENZHEN MAGNUS TECHNOLOGIES CO.,LTD
OUI:0C826A*
ID_OUI_FROM_DATABASE=Wuhan Huagong Genuine Optics Technology Co., Ltd
+OUI:0C839A*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:0C8411*
ID_OUI_FROM_DATABASE=A.O. Smith Water Products
OUI:1005CA*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:1005E1*
+ ID_OUI_FROM_DATABASE=Nokia
+
+OUI:100645*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:1007230*
ID_OUI_FROM_DATABASE=RippleTek Tech Ltd
OUI:102959*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:1029AB*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:102AB3*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
OUI:103711*
ID_OUI_FROM_DATABASE=Simlink AS
+OUI:103917*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:1039E9*
ID_OUI_FROM_DATABASE=Juniper Networks
OUI:104FA8*
ID_OUI_FROM_DATABASE=Sony Corporation
+OUI:105072*
+ ID_OUI_FROM_DATABASE=Sercomm Corporation.
+
OUI:105172*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:106FEF*
ID_OUI_FROM_DATABASE=Ad-Sol Nissin Corp
+OUI:1070FD*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
OUI:1071F9*
ID_OUI_FROM_DATABASE=Cloud Telecomputers, LLC
OUI:10C37B*
ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+OUI:10C3AB*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:10C586*
ID_OUI_FROM_DATABASE=BIO SOUND LAB CO., LTD.
OUI:10CDB6*
ID_OUI_FROM_DATABASE=Essential Products, Inc.
+OUI:10CE45*
+ ID_OUI_FROM_DATABASE=Miromico AG
+
OUI:10CEA9*
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:111111*
ID_OUI_FROM_DATABASE=Private
+OUI:140152*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:1402EC*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
OUI:14157C*
ID_OUI_FROM_DATABASE=TOKYO COSMOS ELECTRIC CO.,LTD.
+OUI:14169D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:14169E*
ID_OUI_FROM_DATABASE=Wingtech Group (HongKong)Limited
OUI:1446E4*
ID_OUI_FROM_DATABASE=AVISTEL
+OUI:14472D*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:144802*
ID_OUI_FROM_DATABASE=THE YEOLRIM Co.,Ltd.
ID_OUI_FROM_DATABASE=nyantec GmbH
OUI:145E45*
- ID_OUI_FROM_DATABASE=Kaleao Limited
+ ID_OUI_FROM_DATABASE=Bamboo Systems Group
OUI:145F94*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:147590*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:147740*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:14780B*
ID_OUI_FROM_DATABASE=Varex Imaging Deutschland AG
OUI:14ADCA*
ID_OUI_FROM_DATABASE=China Mobile Iot Limited company
+OUI:14AE850*
+ ID_OUI_FROM_DATABASE=Kayamatics Limited
+
+OUI:14AE851*
+ ID_OUI_FROM_DATABASE=Henfred Technology Co., Ltd.
+
+OUI:14AE852*
+ ID_OUI_FROM_DATABASE=Qingdao iTechene Technologies Co., Ltd.
+
+OUI:14AE853*
+ ID_OUI_FROM_DATABASE=IFLYTEK CO.,LTD.
+
+OUI:14AE854*
+ ID_OUI_FROM_DATABASE=CENTERVUE SPA
+
+OUI:14AE855*
+ ID_OUI_FROM_DATABASE=AZ-TECHNOLOGY SDN BHD
+
+OUI:14AE856*
+ ID_OUI_FROM_DATABASE=TMG TE GmbH
+
+OUI:14AE857*
+ ID_OUI_FROM_DATABASE=SHENZHEN HONOR ELECTRONIC CO.,LTD
+
+OUI:14AE858*
+ ID_OUI_FROM_DATABASE=Trimble LEM
+
+OUI:14AE859*
+ ID_OUI_FROM_DATABASE=Veo Technologies
+
+OUI:14AE85A*
+ ID_OUI_FROM_DATABASE=MTA Systems
+
+OUI:14AE85B*
+ ID_OUI_FROM_DATABASE=NTC SOFT
+
+OUI:14AE85C*
+ ID_OUI_FROM_DATABASE=IO Industries Inc.
+
+OUI:14AE85D*
+ ID_OUI_FROM_DATABASE=iSolution Technologies Co.,Ltd.
+
+OUI:14AE85E*
+ ID_OUI_FROM_DATABASE=Sercomm Corporation.
+
OUI:14AEDB*
ID_OUI_FROM_DATABASE=VTech Telecommunications Ltd.
OUI:14DDE5*
ID_OUI_FROM_DATABASE=MPMKVVCL
+OUI:14DE39*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:14E4EC*
ID_OUI_FROM_DATABASE=mLogic LLC
OUI:184644*
ID_OUI_FROM_DATABASE=Home Control Singapore Pte Ltd
+OUI:1848CA*
+ ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
+
OUI:1848D8*
ID_OUI_FROM_DATABASE=Fastback Networks
OUI:184E94*
ID_OUI_FROM_DATABASE=MESSOA TECHNOLOGIES INC.
+OUI:184ECB*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:184F32*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
OUI:185680*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:185869*
+ ID_OUI_FROM_DATABASE=Sailer Electronic Co., Ltd
+
OUI:185933*
ID_OUI_FROM_DATABASE=Cisco SPVTG
OUI:1868CB*
ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+OUI:1869D8*
+ ID_OUI_FROM_DATABASE=HANGZHOU AIXIANGJI TECHNOLOGY CO., LTD
+
OUI:1869DA*
ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
OUI:186D99*
ID_OUI_FROM_DATABASE=Adanis Inc.
+OUI:186F2D*
+ ID_OUI_FROM_DATABASE=Shenzhen Sundray Technologies Company Limited
+
OUI:18703B*
ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
OUI:187C81*
ID_OUI_FROM_DATABASE=Valeo Vision Systems
+OUI:187EB9*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:187ED5*
ID_OUI_FROM_DATABASE=shenzhen kaism technology Co. Ltd
OUI:188219*
ID_OUI_FROM_DATABASE=Alibaba Cloud Computing Ltd.
+OUI:18828C*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
OUI:188331*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:1889DF*
ID_OUI_FROM_DATABASE=CerebrEX Inc.
+OUI:188A6A*
+ ID_OUI_FROM_DATABASE=AVPro Global Hldgs
+
OUI:188B15*
ID_OUI_FROM_DATABASE=ShenZhen ZhongRuiJing Technology co.,LTD
OUI:189C5D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:189E2C*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:189EFC*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:18D949*
ID_OUI_FROM_DATABASE=Qvis Labs, LLC
+OUI:18D98F*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:18D9EF*
ID_OUI_FROM_DATABASE=Shuttle Inc.
OUI:1C0042*
ID_OUI_FROM_DATABASE=NARI Technology Co., Ltd.
+OUI:1C0219*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
+OUI:1C05B7*
+ ID_OUI_FROM_DATABASE=Chongqing Trantor Technology Co., Ltd.
+
OUI:1C0656*
ID_OUI_FROM_DATABASE=IDY Corporation
OUI:1C12B0*
ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+OUI:1C1338*
+ ID_OUI_FROM_DATABASE=Kimball Electronics Group, LLC
+
OUI:1C1386*
ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
OUI:1C1FD4*
ID_OUI_FROM_DATABASE=LifeBEAM Technologies LTD
+OUI:1C1FF1*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:1C20DB*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:1C63B7*
ID_OUI_FROM_DATABASE=OpenProducts 237 AB
+OUI:1C63BF*
+ ID_OUI_FROM_DATABASE=SHENZHEN BROADTEL TELECOM CO.,LTD
+
OUI:1C6499*
ID_OUI_FROM_DATABASE=Comtrend Corporation
OUI:1C973D*
ID_OUI_FROM_DATABASE=PRICOM Design
+OUI:1C97C5*
+ ID_OUI_FROM_DATABASE=Ynomia Pty Ltd
+
OUI:1C98EC*
ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
OUI:204E7F*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:2050E7*
+ ID_OUI_FROM_DATABASE=AMPAK Technology,Inc.
+
OUI:2053CA*
ID_OUI_FROM_DATABASE=Risk Technology Ltd
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
OUI:206C8A*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
+
+OUI:206D31*
+ ID_OUI_FROM_DATABASE=FIREWALLA INC
OUI:206E9C*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:208058*
ID_OUI_FROM_DATABASE=Ciena Corporation
+OUI:20826A*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:2082C0*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
OUI:209BCD*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:209EF7*
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
+
OUI:20A2E4*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:20E791*
ID_OUI_FROM_DATABASE=Siemens Healthcare Diagnostics, Inc
+OUI:20E874*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:20E882*
ID_OUI_FROM_DATABASE=zte corporation
OUI:24166D*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:24169D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:24181D*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
OUI:2442BC*
ID_OUI_FROM_DATABASE=Alinco,incorporated
+OUI:2443E2*
+ ID_OUI_FROM_DATABASE=DASAN Network Solutions
+
OUI:244427*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:2462AB*
ID_OUI_FROM_DATABASE=Espressif Inc.
+OUI:2462CE*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
OUI:2464EF*
ID_OUI_FROM_DATABASE=CYG SUNRI CO.,LTD.
OUI:246880*
ID_OUI_FROM_DATABASE=Braveridge.co.,ltd.
+OUI:2468B0*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:24693E*
ID_OUI_FROM_DATABASE=innodisk Corporation
OUI:24C86E*
ID_OUI_FROM_DATABASE=Chaney Instrument Co.
+OUI:24C8D3*
+ ID_OUI_FROM_DATABASE=McWane India Pvt Ltd
+
OUI:24C9A1*
ID_OUI_FROM_DATABASE=Ruckus Wireless
OUI:24CF21*
ID_OUI_FROM_DATABASE=Shenzhen State Micro Technology Co., Ltd
+OUI:24D0DF*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:24D13F*
ID_OUI_FROM_DATABASE=MEXUS CO.,LTD
ID_OUI_FROM_DATABASE=Hangzhou BroadLink Technology Co.,Ltd
OUI:24E124*
- ID_OUI_FROM_DATABASE=Xiamen Ursaconn Technology Co. , Ltd.
+ ID_OUI_FROM_DATABASE=Xiamen Ursalink Technology Co., Ltd.
OUI:24E271*
ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd.
OUI:2811A5*
ID_OUI_FROM_DATABASE=Bose Corporation
+OUI:2811EC*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:281471*
ID_OUI_FROM_DATABASE=Lantis co., LTD.
OUI:283166*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+OUI:28317E*
+ ID_OUI_FROM_DATABASE=Hongkong Nano IC Technologies Co., Ltd
+
OUI:2832C5*
ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
OUI:2852F9*
ID_OUI_FROM_DATABASE=Zhongxin Intelligent Times (Shenzhen) Co., Ltd.
+OUI:285471*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:28565A*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
OUI:2856C1*
- ID_OUI_FROM_DATABASE=Harman International
+ ID_OUI_FROM_DATABASE=Harman/Becker Automotive Systems GmbH
OUI:285767*
ID_OUI_FROM_DATABASE=Dish Technologies Corp
OUI:2863BD*
ID_OUI_FROM_DATABASE=APTIV SERVICES US, LLC
+OUI:2864B0*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:28656B*
ID_OUI_FROM_DATABASE=Keystone Microtech Corporation
OUI:28E347*
ID_OUI_FROM_DATABASE=Liteon Technology Corporation
+OUI:28E34E*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:28E476*
ID_OUI_FROM_DATABASE=Pi-Coral
OUI:28EF01*
ID_OUI_FROM_DATABASE=Private
+OUI:28F033*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:28F076*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:28F606*
ID_OUI_FROM_DATABASE=Syes srl
+OUI:28FA7A*
+ ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd.
+
OUI:28FAA0*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
OUI:2C1984*
ID_OUI_FROM_DATABASE=IDN Telecom, Inc.
+OUI:2C1A01*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:2C1A31*
ID_OUI_FROM_DATABASE=Electronics Company Limited
OUI:2C97B1*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:2C97ED*
+ ID_OUI_FROM_DATABASE=Sony Imaging Products & Solutions Inc.
+
OUI:2C9924*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:2C9EFC*
ID_OUI_FROM_DATABASE=CANON INC.
+OUI:2C9FFB*
+ ID_OUI_FROM_DATABASE=Wistron Neweb Corporation
+
OUI:2CA02F*
ID_OUI_FROM_DATABASE=Veroguard Systems Pty Ltd
OUI:2CC407*
ID_OUI_FROM_DATABASE=machineQ
+OUI:2CC546*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:2CC548*
ID_OUI_FROM_DATABASE=IAdea Corporation
OUI:2CE871*
ID_OUI_FROM_DATABASE=Alert Metalguard ApS
+OUI:2CEA7F*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:2CEDEB*
ID_OUI_FROM_DATABASE=Alpheus Digital Company Limited
OUI:304F75*
ID_OUI_FROM_DATABASE=DASAN Network Solutions
+OUI:305075*
+ ID_OUI_FROM_DATABASE=GN Audio A/S
+
OUI:3050FD*
ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd
OUI:307ECB*
ID_OUI_FROM_DATABASE=SFR
+OUI:30809B*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:308454*
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
ID_OUI_FROM_DATABASE=Power Electronics International Inc.
OUI:30B216*
- ID_OUI_FROM_DATABASE=ABB AG - Power Grids - Grid Automation
+ ID_OUI_FROM_DATABASE=ABB Power Grids Germany AG – Grid Automation
+
+OUI:30B237*
+ ID_OUI_FROM_DATABASE=GD Midea Air-Conditioning Equipment Co.,Ltd.
OUI:30B3A2*
ID_OUI_FROM_DATABASE=Shenzhen Heguang Measurement & Control Technology Co.,Ltd
OUI:30B7D4*
ID_OUI_FROM_DATABASE=Hitron Technologies. Inc
+OUI:30B9B0*
+ ID_OUI_FROM_DATABASE=Intracom Asia Co., Ltd
+
OUI:30C01B*
ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
OUI:30FC68*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+OUI:30FCEB*
+ ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+
OUI:30FD11*
ID_OUI_FROM_DATABASE=MACROTECH (USA) INC.
OUI:34145F*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:3414B5*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:341513*
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:343759*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:343794*
+ ID_OUI_FROM_DATABASE=Hamee Corp.
+
OUI:3438AF*
ID_OUI_FROM_DATABASE=Inlab Software GmbH
OUI:344F69*
ID_OUI_FROM_DATABASE=EKINOPS SAS
+OUI:345180*
+ ID_OUI_FROM_DATABASE=TCL King Electrical Appliances (Huizhou) Co., Ltd
+
OUI:3451AA*
ID_OUI_FROM_DATABASE=JID GLOBAL
OUI:346FED*
ID_OUI_FROM_DATABASE=Enovation Controls
+OUI:347146*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:347563*
ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
OUI:3476C5*
ID_OUI_FROM_DATABASE=I-O DATA DEVICE,INC.
+OUI:347839*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:347877*
ID_OUI_FROM_DATABASE=O-Net Communications (Shenzhen) Limited
ID_OUI_FROM_DATABASE=Ericsson AB
OUI:348584*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:34862A*
ID_OUI_FROM_DATABASE=Heinz Lackmann GmbH & Co KG
OUI:34CE94*
ID_OUI_FROM_DATABASE=Parsec (Pty) Ltd
+OUI:34CFF6*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:34D09B*
ID_OUI_FROM_DATABASE=MobilMAX Technology Inc.
OUI:380118*
ID_OUI_FROM_DATABASE=ULVAC,Inc.
+OUI:380146*
+ ID_OUI_FROM_DATABASE=SHENZHEN BILIAN ELECTRONIC CO.,LTD
+
OUI:380195*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:3816D1*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:381730*
+ ID_OUI_FROM_DATABASE=Ulrich Lippert GmbH & Co KG
+
OUI:381766*
ID_OUI_FROM_DATABASE=PROMZAKAZ LTD.
OUI:3822D6*
ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
+OUI:3822E2*
+ ID_OUI_FROM_DATABASE=HP Inc.
+
OUI:38256B*
ID_OUI_FROM_DATABASE=Microsoft Mobile Oy
OUI:38839A*
ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
+OUI:388479*
+ ID_OUI_FROM_DATABASE=Cisco Meraki
+
OUI:388602*
ID_OUI_FROM_DATABASE=Flexoptix GmbH
OUI:38EAA7*
ID_OUI_FROM_DATABASE=Hewlett Packard
+OUI:38EB47*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:38EC0D*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:38F597*
ID_OUI_FROM_DATABASE=home2net GmbH
+OUI:38F601*
+ ID_OUI_FROM_DATABASE=Solid State Storage Technology Corporation
+
OUI:38F708*
ID_OUI_FROM_DATABASE=National Resource Management, Inc.
OUI:3C57D5*
ID_OUI_FROM_DATABASE=FiveCo
+OUI:3C58C2*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:3C591E*
ID_OUI_FROM_DATABASE=TCL King Electrical Appliances (Huizhou) Co., Ltd
OUI:3C5CC4*
ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+OUI:3C5CF1*
+ ID_OUI_FROM_DATABASE=eero inc.
+
OUI:3C5EC3*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:3C7A8A*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:3C7D0A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:3C7DB1*
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:3C7F6F*
ID_OUI_FROM_DATABASE=Telechips, Inc.
+OUI:3C806B*
+ ID_OUI_FROM_DATABASE=Hunan Voc Acoustics Technology Co., Ltd.
+
OUI:3C80AA*
ID_OUI_FROM_DATABASE=Ransnet Singapore Pte Ltd
OUI:3CB17F*
ID_OUI_FROM_DATABASE=Wattwatchers Pty Ld
+OUI:3CB53D*
+ ID_OUI_FROM_DATABASE=HUNAN GOKE MICROELECTRONICS CO.,LTD
+
OUI:3CB6B7*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
OUI:3CF5CC*
ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+OUI:3CF652*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:3CF72A*
ID_OUI_FROM_DATABASE=Nokia Corporation
OUI:40040C*
ID_OUI_FROM_DATABASE=A&T
+OUI:400589*
+ ID_OUI_FROM_DATABASE=T-Mobile, USA
+
OUI:4006A0*
ID_OUI_FROM_DATABASE=Texas Instruments
ID_OUI_FROM_DATABASE=INTAI TECHNOLOGY CORP.
OUI:4018B1*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:4018D7*
ID_OUI_FROM_DATABASE=Smartronix, Inc.
OUI:402B50*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:402B69*
+ ID_OUI_FROM_DATABASE=Kumho Electric Inc.
+
OUI:402BA1*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
OUI:402E28*
ID_OUI_FROM_DATABASE=MiXTelematics
+OUI:402E71*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
+OUI:402F86*
+ ID_OUI_FROM_DATABASE=LG Innotek
+
OUI:403004*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:404022*
ID_OUI_FROM_DATABASE=ZIV
+OUI:404028*
+ ID_OUI_FROM_DATABASE=ZIV
+
OUI:40406B*
ID_OUI_FROM_DATABASE=Icomera
ID_OUI_FROM_DATABASE=Spreadtrum Communications (Shanghai) Co., Ltd.
OUI:40476A*
- ID_OUI_FROM_DATABASE=AG Acquisition Corp. d.b.a. ASTRO Gaming
+ ID_OUI_FROM_DATABASE=Astro Gaming
OUI:4048FD0*
ID_OUI_FROM_DATABASE=BEIJING C&W ELECTRONICS(GROUP)CO.,LTD
OUI:406231*
ID_OUI_FROM_DATABASE=GIFA
+OUI:406234*
+ ID_OUI_FROM_DATABASE=Telink Semiconductor (Shanghai) Co., Ltd.
+
OUI:4062B6*
ID_OUI_FROM_DATABASE=Tele system communication
OUI:40B30E*
ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+OUI:40B31E*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
OUI:40B395*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:40B6B1*
ID_OUI_FROM_DATABASE=SUNGSAM CO,.Ltd
+OUI:40B6E7*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:40B7F3*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:40BC60*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:40BC68*
+ ID_OUI_FROM_DATABASE=Wuhan Funshion Online Technologies Co.,Ltd
+
OUI:40BC73*
ID_OUI_FROM_DATABASE=Cronoplast S.L.
OUI:40DC9D*
ID_OUI_FROM_DATABASE=HAJEN
+OUI:40DEAD*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:40DF02*
ID_OUI_FROM_DATABASE=LINE BIZ Plus
OUI:40EACE*
ID_OUI_FROM_DATABASE=FOUNDER BROADBAND NETWORK SERVICE CO.,LTD
+OUI:40EC99*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:40ECF8*
ID_OUI_FROM_DATABASE=Siemens AG
OUI:40F4EC*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:40F520*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:40F52E*
ID_OUI_FROM_DATABASE=Leica Microsystems (Schweiz) AG
OUI:440CFD*
ID_OUI_FROM_DATABASE=NetMan Co., Ltd.
+OUI:4410FE*
+ ID_OUI_FROM_DATABASE=Huizhou Foryou General Electronics Co., Ltd.
+
OUI:441102*
ID_OUI_FROM_DATABASE=EDMI Europe Ltd
OUI:4459E3*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:445CE9*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:445D5E*
ID_OUI_FROM_DATABASE=SHENZHEN Coolkit Technology CO.,LTD
OUI:44746C*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
+OUI:447654*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:44783E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:448C52*
ID_OUI_FROM_DATABASE=KTIS CO., Ltd
+OUI:448DBF*
+ ID_OUI_FROM_DATABASE=Rhino Mobility LLC
+
OUI:448E12*
ID_OUI_FROM_DATABASE=DT Research, Inc.
OUI:44C56F*
ID_OUI_FROM_DATABASE=NGN Easy Satfinder (Tianjin) Electronic Co., Ltd
+OUI:44C65D*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:44C69B*
ID_OUI_FROM_DATABASE=Wuhan Feng Tian Information Network CO.,LTD
OUI:486DBB*
ID_OUI_FROM_DATABASE=Vestel Elektronik San ve Tic. A.Ş.
+OUI:486E70*
+ ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd.
+
OUI:486E73*
ID_OUI_FROM_DATABASE=Pica8, Inc.
OUI:487ADA*
ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
+OUI:487AF6*
+ ID_OUI_FROM_DATABASE=NCS ELECTRICAL SDN BHD
+
+OUI:487AFF*
+ ID_OUI_FROM_DATABASE=ESSYS
+
OUI:487B5E*
- ID_OUI_FROM_DATABASE=Social Mobile
+ ID_OUI_FROM_DATABASE=SMT TELECOMM HK
OUI:487B6B*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:488EEF*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:488F5A*
+ ID_OUI_FROM_DATABASE=Routerboard.com
+
OUI:489153*
ID_OUI_FROM_DATABASE=Weinmann Geräte für Medizin GmbH + Co. KG
OUI:48A2B7*
ID_OUI_FROM_DATABASE=Kodofon JSC
+OUI:48A2E6*
+ ID_OUI_FROM_DATABASE=Resideo
+
OUI:48A380*
ID_OUI_FROM_DATABASE=Gionee Communication Equipment Co.,Ltd.
OUI:48B620*
ID_OUI_FROM_DATABASE=ROLI Ltd.
+OUI:48B8A3*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:48B8DE*
ID_OUI_FROM_DATABASE=HOMEWINS TECHNOLOGY CO.,LTD.
OUI:4C09D4*
ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation
+OUI:4C0A3D*
+ ID_OUI_FROM_DATABASE=ADNACOM INC.
+
OUI:4C0B3A*
ID_OUI_FROM_DATABASE=TCT mobile ltd
OUI:4C32D9*
ID_OUI_FROM_DATABASE=M Rutty Holdings Pty. Ltd.
+OUI:4C3329*
+ ID_OUI_FROM_DATABASE=Sweroam
+
OUI:4C334E*
ID_OUI_FROM_DATABASE=HIGHTECH
OUI:4C3FD3*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:4C4088*
+ ID_OUI_FROM_DATABASE=SANSHIN ELECTRONICS CO.,LTD.
+
+OUI:4C4576*
+ ID_OUI_FROM_DATABASE=China Mobile(Hangzhou) Information Technology Co.,Ltd.
+
OUI:4C48DA*
ID_OUI_FROM_DATABASE=Beijing Autelan Technology Co.,Ltd
ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
OUI:4CA003*
- ID_OUI_FROM_DATABASE=T-21 Technologies LLC
+ ID_OUI_FROM_DATABASE=VITEC
OUI:4CA161*
ID_OUI_FROM_DATABASE=Rain Bird Corporation
OUI:4CA56D*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:4CA64D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:4CA74B*
ID_OUI_FROM_DATABASE=Alcatel Lucent
OUI:4CB8B5*
ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
+OUI:4CB911*
+ ID_OUI_FROM_DATABASE=Raisecom Technology CO.,LTD
+
OUI:4CB9C8*
ID_OUI_FROM_DATABASE=CONET CO., LTD.
OUI:4CC452*
ID_OUI_FROM_DATABASE=Shang Hai Tyd. Electon Technology Ltd.
+OUI:4CC53E*
+ ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
+
OUI:4CC602*
ID_OUI_FROM_DATABASE=Radios, Inc.
OUI:4CCC6A*
ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD.
+OUI:4CCE2D*
+ ID_OUI_FROM_DATABASE=Danlaw Inc
+
OUI:4CD08A*
ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
OUI:4CFBFE*
ID_OUI_FROM_DATABASE=Sercomm Japan Corporation
+OUI:4CFCAA*
+ ID_OUI_FROM_DATABASE=Tesla,Inc.
+
OUI:4CFF12*
ID_OUI_FROM_DATABASE=Fuze Entertainment Co., ltd
OUI:50206B*
ID_OUI_FROM_DATABASE=Emerson Climate Technologies Transportation Solutions
+OUI:5021EC*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:502267*
ID_OUI_FROM_DATABASE=PixeLINK
OUI:5033F0*
ID_OUI_FROM_DATABASE=YICHEN (SHENZHEN) TECHNOLOGY CO.LTD
+OUI:50382F*
+ ID_OUI_FROM_DATABASE=ASE Group Chung-Li
+
OUI:503955*
ID_OUI_FROM_DATABASE=Cisco SPVTG
OUI:5045F7*
ID_OUI_FROM_DATABASE=Liuhe Intelligence Technology Ltd.
+OUI:50464A*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:50465D*
ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
OUI:5061D6*
ID_OUI_FROM_DATABASE=Indu-Sol GmbH
+OUI:5061F6*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
OUI:5062550*
ID_OUI_FROM_DATABASE=Ufanet SC
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:50EB1A*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:50EB71*
ID_OUI_FROM_DATABASE=Intel Corporate
ID_OUI_FROM_DATABASE=Teltronic AG
OUI:540384*
- ID_OUI_FROM_DATABASE=Hangkong Nano IC Technologies Co., Ltd
+ ID_OUI_FROM_DATABASE=Hongkong Nano IC Technologies Co., Ltd
OUI:5403F5*
ID_OUI_FROM_DATABASE=EBN Technology Corp.
OUI:54098D*
ID_OUI_FROM_DATABASE=deister electronic GmbH
+OUI:540DF9*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
+OUI:540E2D*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:541031*
ID_OUI_FROM_DATABASE=SMARTO
OUI:542160*
ID_OUI_FROM_DATABASE=Alula
+OUI:54219D*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:5422F8*
ID_OUI_FROM_DATABASE=zte corporation
OUI:54489C*
ID_OUI_FROM_DATABASE=CDOUBLES ELECTRONICS CO. LTD.
+OUI:5448E6*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co.,Ltd
+
OUI:544A00*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:546D52*
ID_OUI_FROM_DATABASE=TOPVIEW OPTRONICS CORP.
+OUI:5471DD*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:54724F*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:547FA8*
ID_OUI_FROM_DATABASE=TELCO systems, s.r.o.
+OUI:547FBC*
+ ID_OUI_FROM_DATABASE=iodyne
+
OUI:547FEE*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:548998*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:548ABA*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:548CA0*
ID_OUI_FROM_DATABASE=Liteon Technology Corporation
OUI:54AE27*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:54AED0*
+ ID_OUI_FROM_DATABASE=DASAN Networks, Inc.
+
OUI:54B121*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:5850E6*
ID_OUI_FROM_DATABASE=Best Buy Corporation
+OUI:5850ED*
+ ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+
OUI:58528A*
ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation
ID_OUI_FROM_DATABASE=Danfoss Solar Inverters
OUI:5859C2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:585FF6*
ID_OUI_FROM_DATABASE=zte corporation
OUI:58946B*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:5894B2*
+ ID_OUI_FROM_DATABASE=BrainCo
+
OUI:5894CF*
ID_OUI_FROM_DATABASE=Vertex Standard LMR, Inc.
OUI:58A839*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:58A87B*
+ ID_OUI_FROM_DATABASE=Fitbit, Inc.
+
OUI:58AC78*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:5C0E8B*
ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
+OUI:5C0FFB*
+ ID_OUI_FROM_DATABASE=Amino Communications Ltd
+
OUI:5C1193*
ID_OUI_FROM_DATABASE=Seal One AG
OUI:5C22C4*
ID_OUI_FROM_DATABASE=DAE EUN ELETRONICS CO., LTD
+OUI:5C2316*
+ ID_OUI_FROM_DATABASE=Squirrels Research Labs LLC
+
OUI:5C2443*
ID_OUI_FROM_DATABASE=O-Sung Telecom Co., Ltd.
OUI:5C546D*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:5C5578*
+ ID_OUI_FROM_DATABASE=iryx corp
+
OUI:5C56ED*
ID_OUI_FROM_DATABASE=3pleplay Electronics Private Limited
OUI:5C6B4F*
ID_OUI_FROM_DATABASE=Hello Inc.
+OUI:5C6BD7*
+ ID_OUI_FROM_DATABASE=Foshan VIOMI Electric Appliance Technology Co. Ltd.
+
OUI:5C6D20*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
OUI:5CA48A*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:5CA62D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:5CA86A*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:5CB15F*
ID_OUI_FROM_DATABASE=Oceanblue Cloud Technology Limited
+OUI:5CB29E*
+ ID_OUI_FROM_DATABASE=ASCO Power Technologies
+
OUI:5CB395*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:5CB901*
ID_OUI_FROM_DATABASE=Hewlett Packard
+OUI:5CBA2C*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
OUI:5CBA37*
ID_OUI_FROM_DATABASE=Microsoft Corporation
OUI:5CBD9E*
ID_OUI_FROM_DATABASE=HONGKONG MIRACLE EAGLE TECHNOLOGY(GROUP) LIMITED
+OUI:5CC1D7*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:5CC213*
ID_OUI_FROM_DATABASE=Fr. Sauter AG
OUI:601D91*
ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+OUI:601D9D*
+ ID_OUI_FROM_DATABASE=Sichuan AI-Link Technology Co., Ltd.
+
OUI:601E02*
ID_OUI_FROM_DATABASE=EltexAlatau
OUI:606720*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:60684E*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:606944*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:6077E2*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:607ECD*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:607EDD*
ID_OUI_FROM_DATABASE=Microsoft Mobile Oy
ID_OUI_FROM_DATABASE=Hipad Intelligent Technology Co., Ltd.
OUI:609C9F*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:609E64*
ID_OUI_FROM_DATABASE=Vivonic GmbH
OUI:60D819*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:60D89C*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
OUI:60D9A0*
ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd.
OUI:60DB2A*
ID_OUI_FROM_DATABASE=HNS
+OUI:60DE35*
+ ID_OUI_FROM_DATABASE=GITSN, Inc.
+
OUI:60DE44*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:60F3DA*
ID_OUI_FROM_DATABASE=Logic Way GmbH
+OUI:60F43A*
+ ID_OUI_FROM_DATABASE=Edifier International
+
OUI:60F445*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:640980*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+OUI:6409AC*
+ ID_OUI_FROM_DATABASE=TCT mobile ltd
+
OUI:640B4A*
ID_OUI_FROM_DATABASE=Digital Telecom Technology Limited
OUI:645AED*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:645CF3*
+ ID_OUI_FROM_DATABASE=ParanTek Inc.
+
OUI:645D86*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:645DD7*
ID_OUI_FROM_DATABASE=Shenzhen Lifesense Medical Electronics Co., Ltd.
+OUI:645E2C*
+ ID_OUI_FROM_DATABASE=IRay Technology Co., Ltd.
+
OUI:645EBE*
ID_OUI_FROM_DATABASE=Yahoo! JAPAN
OUI:646223*
ID_OUI_FROM_DATABASE=Cellient Co., Ltd.
+OUI:6462660*
+ ID_OUI_FROM_DATABASE=MiiVii Dynamics Technology CO.,LTD
+
+OUI:6462661*
+ ID_OUI_FROM_DATABASE=Annapurna labs
+
+OUI:6462662*
+ ID_OUI_FROM_DATABASE=Protectli
+
+OUI:6462663*
+ ID_OUI_FROM_DATABASE=FaceHeart Inc.
+
+OUI:6462664*
+ ID_OUI_FROM_DATABASE=Redstone Systems, Inc.
+
+OUI:6462665*
+ ID_OUI_FROM_DATABASE=Bühler AG
+
+OUI:6462666*
+ ID_OUI_FROM_DATABASE=Pass & Seymour, Inc d/b/a Legrand
+
+OUI:6462667*
+ ID_OUI_FROM_DATABASE=Shenzhen C & D Electronics Co., Ltd.
+
+OUI:6462668*
+ ID_OUI_FROM_DATABASE=Leontech Limited
+
+OUI:6462669*
+ ID_OUI_FROM_DATABASE=Chunghwa System Integration Co., Ltd.
+
+OUI:646266A*
+ ID_OUI_FROM_DATABASE=Sensoro Co., Ltd.
+
+OUI:646266B*
+ ID_OUI_FROM_DATABASE=Signal Hound
+
+OUI:646266C*
+ ID_OUI_FROM_DATABASE=Jiangsu Aisida Electronic Co.,Ltd
+
+OUI:646266D*
+ ID_OUI_FROM_DATABASE=Kobol Innovations Pte. Ltd.
+
+OUI:646266E*
+ ID_OUI_FROM_DATABASE=Shenzhen Jie Shi Lian Industrial Co., LTD
+
OUI:64628A*
ID_OUI_FROM_DATABASE=evon GmbH
OUI:646E6C*
ID_OUI_FROM_DATABASE=Radio Datacom LLC
+OUI:646E97*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
OUI:646EEA*
ID_OUI_FROM_DATABASE=Iskratel d.o.o.
OUI:64A837*
ID_OUI_FROM_DATABASE=Juni Korea Co., Ltd
+OUI:64A965*
+ ID_OUI_FROM_DATABASE=Linkflow Co., Ltd.
+
OUI:64AE0C*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:64BC11*
ID_OUI_FROM_DATABASE=CombiQ AB
+OUI:64BC58*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:64C2DE*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
OUI:64DC01*
ID_OUI_FROM_DATABASE=Static Systems Group PLC
+OUI:64DDE9*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:64DE1C*
ID_OUI_FROM_DATABASE=Kingnetic Pte Ltd
OUI:64E161*
ID_OUI_FROM_DATABASE=DEP Corp.
+OUI:64E172*
+ ID_OUI_FROM_DATABASE=Shenzhen Qihoo Intelligent Technology Co.,Ltd
+
OUI:64E599*
ID_OUI_FROM_DATABASE=EFM Networks
OUI:64E84F*
ID_OUI_FROM_DATABASE=Serialway Communication Technology Co. Ltd
+OUI:64E881*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
OUI:64E892*
ID_OUI_FROM_DATABASE=Morio Denki Co., Ltd.
OUI:64F242*
ID_OUI_FROM_DATABASE=Gerdes Aktiengesellschaft
+OUI:64F2FB*
+ ID_OUI_FROM_DATABASE=Hangzhou Ezviz Software Co.,Ltd.
+
OUI:64F50E*
ID_OUI_FROM_DATABASE=Kinion Technology Company Limited
OUI:64F69D*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:64F6BB*
+ ID_OUI_FROM_DATABASE=Fibocom Wireless Inc.
+
OUI:64F6F7*
ID_OUI_FROM_DATABASE=Anhui Dynamic Power Co., Ltd.
OUI:6831FE*
ID_OUI_FROM_DATABASE=Teladin Co.,Ltd.
+OUI:68332C*
+ ID_OUI_FROM_DATABASE=KENSTEL NETWORKS LIMITED
+
OUI:683489*
ID_OUI_FROM_DATABASE=LEA Professional
OUI:684898*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:6849B2*
+ ID_OUI_FROM_DATABASE=CARLO GAVAZZI LTD
+
+OUI:684A76*
+ ID_OUI_FROM_DATABASE=eero inc.
+
OUI:684AAE*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:686975*
ID_OUI_FROM_DATABASE=Angler Labs Inc
+OUI:6869CA*
+ ID_OUI_FROM_DATABASE=Hitachi, Ltd.
+
OUI:6869F2*
ID_OUI_FROM_DATABASE=ComAp s.r.o.
OUI:688F84*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:688FC9*
+ ID_OUI_FROM_DATABASE=Zhuolian (Shenzhen) Communication Co., Ltd
+
OUI:6891D00*
ID_OUI_FROM_DATABASE=Central Railway Manufacturing
OUI:68B983*
ID_OUI_FROM_DATABASE=b-plus GmbH
+OUI:68B9D3*
+ ID_OUI_FROM_DATABASE=Shenzhen Trolink Technology CO, LTD
+
OUI:68BC0C*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:68D482*
ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
+OUI:68D79A*
+ ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc.
+
OUI:68D925*
ID_OUI_FROM_DATABASE=ProSys Development Services
OUI:68E166*
ID_OUI_FROM_DATABASE=Private
+OUI:68E209*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:68E41F*
ID_OUI_FROM_DATABASE=Unglaube Identech GmbH
OUI:6C0B84*
ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd.
+OUI:6C0D34*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:6C0E0D*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
OUI:6C160E*
ID_OUI_FROM_DATABASE=ShotTracker
+OUI:6C1632*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:6C1811*
ID_OUI_FROM_DATABASE=Decatur Electronics
OUI:6C2F2C*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:6C2F8A*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:6C310E*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:6C4A39*
ID_OUI_FROM_DATABASE=BITA
+OUI:6C4A85*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:6C4B7F*
ID_OUI_FROM_DATABASE=Vossloh-Schwabe Deutschland GmbH
OUI:6C6126*
ID_OUI_FROM_DATABASE=Rinicom Holdings
+OUI:6C61F4*
+ ID_OUI_FROM_DATABASE=SFR
+
OUI:6C626D*
ID_OUI_FROM_DATABASE=Micro-Star INT'L CO., LTD
OUI:6C641A*
ID_OUI_FROM_DATABASE=Penguin Computing
+OUI:6C6A77*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:6C6CD3*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:6C6D09*
+ ID_OUI_FROM_DATABASE=Kyowa Electronics Co.,Ltd.
+
OUI:6C6EFE*
ID_OUI_FROM_DATABASE=Core Logic Inc.
OUI:6CD71F*
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+OUI:6CD94C*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:6CDC6A*
ID_OUI_FROM_DATABASE=Promethean Limited
OUI:6CDD30*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:6CDDBC*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:6CDFFB0*
ID_OUI_FROM_DATABASE=Shenzhen HDCVT Technology
OUI:6CF5E8*
ID_OUI_FROM_DATABASE=Mooredoll Inc.
+OUI:6CF712*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:6CF97C*
ID_OUI_FROM_DATABASE=Nanoptix Inc.
OUI:70037E*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+OUI:70039F*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:700433*
ID_OUI_FROM_DATABASE=California Things Inc.
OUI:701E68*
ID_OUI_FROM_DATABASE=Hanna Instruments, Inc.
+OUI:701F3C*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:701F53*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:703811*
- ID_OUI_FROM_DATABASE=Invensys Rail
+ ID_OUI_FROM_DATABASE=Siemens Mobility Limited
OUI:7038B4*
ID_OUI_FROM_DATABASE=Low Tech Solutions
OUI:7048F7*
ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
+OUI:704A0E*
+ ID_OUI_FROM_DATABASE=AMPAK Technology,Inc.
+
OUI:704AAE*
ID_OUI_FROM_DATABASE=Xstream Flow (Pty) Ltd
OUI:706173*
ID_OUI_FROM_DATABASE=Calantec GmbH
+OUI:70617B*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:7062B8*
ID_OUI_FROM_DATABASE=D-Link International
OUI:70661B*
ID_OUI_FROM_DATABASE=Sonova AG
+OUI:706655*
+ ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
+
OUI:706879*
ID_OUI_FROM_DATABASE=Saijo Denki International Co., Ltd.
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:7076DD*
- ID_OUI_FROM_DATABASE=Oxyguard International A/S
+ ID_OUI_FROM_DATABASE=OxyGuard Internation A/S
OUI:7076F0*
ID_OUI_FROM_DATABASE=LevelOne Communications (India) Private Limited
OUI:708BCD*
ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
+OUI:708CB6*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:708D09*
ID_OUI_FROM_DATABASE=Nokia Corporation
+OUI:708F47*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:70918F*
ID_OUI_FROM_DATABASE=Weber-Stephen Products LLC
OUI:70B3D502A*
ID_OUI_FROM_DATABASE=BAE Systems Surface Ships Limited
+OUI:70B3D502B*
+ ID_OUI_FROM_DATABASE=Scorpion Precision Industry (HK)CO. Ltd.
+
OUI:70B3D502D*
ID_OUI_FROM_DATABASE=NEXTtec srl
OUI:70B3D5037*
ID_OUI_FROM_DATABASE=EIFFAGE ENERGIE ELECTRONIQUE
+OUI:70B3D5038*
+ ID_OUI_FROM_DATABASE=DONG IL VISION Co., Ltd.
+
OUI:70B3D5039*
ID_OUI_FROM_DATABASE=DoWoo Digitech
OUI:70B3D5046*
ID_OUI_FROM_DATABASE=Shenzhen Rihuida Electronics Co,. Ltd
+OUI:70B3D5047*
+ ID_OUI_FROM_DATABASE=OOO ORION-R
+
OUI:70B3D5048*
ID_OUI_FROM_DATABASE=AvMap srlu
OUI:70B3D506C*
ID_OUI_FROM_DATABASE=AppTek
+OUI:70B3D506D*
+ ID_OUI_FROM_DATABASE=Panoramic Power
+
OUI:70B3D506E*
ID_OUI_FROM_DATABASE=GLOBAL-KING INTERNATIONAL CO., LTD.
OUI:70B3D50DA*
ID_OUI_FROM_DATABASE=Aquavision Distribution Ltd
+OUI:70B3D50DB*
+ ID_OUI_FROM_DATABASE=Cryptotronix LLC
+
OUI:70B3D50DC*
ID_OUI_FROM_DATABASE=Talleres de Escoriaza
OUI:70B3D50F3*
ID_OUI_FROM_DATABASE=MonsoonRF, Inc.
+OUI:70B3D50F4*
+ ID_OUI_FROM_DATABASE=Visual Robotics
+
OUI:70B3D50F6*
ID_OUI_FROM_DATABASE=KSE GmbH
OUI:70B3D5115*
ID_OUI_FROM_DATABASE=Welltec Corp.
+OUI:70B3D5117*
+ ID_OUI_FROM_DATABASE=SysCom Automationstechnik GmbH
+
OUI:70B3D5119*
ID_OUI_FROM_DATABASE=Private
OUI:70B3D512F*
ID_OUI_FROM_DATABASE=DSP4YOU LTd
+OUI:70B3D5130*
+ ID_OUI_FROM_DATABASE=MG s.r.l.
+
OUI:70B3D5131*
ID_OUI_FROM_DATABASE=Inova Design Solutions Ltd
OUI:70B3D5159*
ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+OUI:70B3D515A*
+ ID_OUI_FROM_DATABASE=ENABLER LTD.
+
OUI:70B3D515B*
ID_OUI_FROM_DATABASE=Armstrong International, Inc.
OUI:70B3D5189*
ID_OUI_FROM_DATABASE=DAVE SRL
+OUI:70B3D518A*
+ ID_OUI_FROM_DATABASE=NSP Europe Ltd
+
OUI:70B3D518B*
ID_OUI_FROM_DATABASE=Aplex Technology Inc.
OUI:70B3D518E*
ID_OUI_FROM_DATABASE=NIPPON SEIKI CO., LTD.
+OUI:70B3D518F*
+ ID_OUI_FROM_DATABASE=Newtec A/S
+
OUI:70B3D5190*
ID_OUI_FROM_DATABASE=Fantom Wireless, Inc.
OUI:70B3D5192*
ID_OUI_FROM_DATABASE=ASPT, INC.
+OUI:70B3D5193*
+ ID_OUI_FROM_DATABASE=ERA TOYS LIMITED
+
OUI:70B3D5194*
ID_OUI_FROM_DATABASE=Husty M.Styczen J.Hupert Sp.J.
OUI:70B3D51A6*
ID_OUI_FROM_DATABASE=Robotelf Technologies (Chengdu) Co., Ltd.
+OUI:70B3D51A7*
+ ID_OUI_FROM_DATABASE=Elk Solutions, LLC
+
OUI:70B3D51A8*
ID_OUI_FROM_DATABASE=STC Rainbow Ltd.
OUI:70B3D51BE*
ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC
+OUI:70B3D51BF*
+ ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH
+
OUI:70B3D51C0*
ID_OUI_FROM_DATABASE=W. H. Leary Co., Inc.
OUI:70B3D51C2*
ID_OUI_FROM_DATABASE=CENSIS, Uiversity of Glasgow
+OUI:70B3D51C3*
+ ID_OUI_FROM_DATABASE=Shanghai Tiancheng Communication Technology Corporation
+
OUI:70B3D51C4*
ID_OUI_FROM_DATABASE=Smeg S.p.A.
OUI:70B3D51CE*
ID_OUI_FROM_DATABASE=Clear Flow by Antiference
+OUI:70B3D51CF*
+ ID_OUI_FROM_DATABASE=Dalcnet srl
+
OUI:70B3D51D0*
ID_OUI_FROM_DATABASE=Shenzhen INVT Electric Co.,Ltd
OUI:70B3D51D4*
ID_OUI_FROM_DATABASE=Brinkmann Audio GmbH
+OUI:70B3D51D5*
+ ID_OUI_FROM_DATABASE=MIVO Technology AB
+
OUI:70B3D51D6*
ID_OUI_FROM_DATABASE=MacGray Services
OUI:70B3D51E1*
ID_OUI_FROM_DATABASE=TEX COMPUTER SRL
+OUI:70B3D51E2*
+ ID_OUI_FROM_DATABASE=Shenzhen CAMERAY ELECTRONIC CO., LTD
+
OUI:70B3D51E3*
ID_OUI_FROM_DATABASE=Hatel Elektronik LTD. STI.
OUI:70B3D51E6*
ID_OUI_FROM_DATABASE=Sanmina Israel
+OUI:70B3D51E7*
+ ID_OUI_FROM_DATABASE=DogWatch Inc
+
OUI:70B3D51E8*
ID_OUI_FROM_DATABASE=Gogo BA
OUI:70B3D51F1*
ID_OUI_FROM_DATABASE=DIEHL Connectivity Solutions
+OUI:70B3D51F2*
+ ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+
OUI:70B3D51F3*
ID_OUI_FROM_DATABASE=Smart Energy Code Company Limited
OUI:70B3D5243*
ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA
+OUI:70B3D5244*
+ ID_OUI_FROM_DATABASE=DAT Informatics Pvt Ltd
+
OUI:70B3D5245*
ID_OUI_FROM_DATABASE=Newtec A/S
OUI:70B3D5248*
ID_OUI_FROM_DATABASE=GL TECH CO.,LTD
+OUI:70B3D5249*
+ ID_OUI_FROM_DATABASE=Kospel S.A.
+
OUI:70B3D524A*
ID_OUI_FROM_DATABASE=Unmukti Technology Pvt Ltd
OUI:70B3D525B*
ID_OUI_FROM_DATABASE=GID Industrial
+OUI:70B3D525C*
+ ID_OUI_FROM_DATABASE=ARCLAN'SYSTEM
+
OUI:70B3D525D*
ID_OUI_FROM_DATABASE=Mimo Networks
OUI:70B3D5281*
ID_OUI_FROM_DATABASE=ITG.CO.LTD
+OUI:70B3D5282*
+ ID_OUI_FROM_DATABASE=SAMBO HITECH
+
OUI:70B3D5283*
ID_OUI_FROM_DATABASE=TextNinja Co.
OUI:70B3D52C0*
ID_OUI_FROM_DATABASE=Sensative AB
+OUI:70B3D52C1*
+ ID_OUI_FROM_DATABASE=Avlinkpro
+
OUI:70B3D52C2*
ID_OUI_FROM_DATABASE=Quantum Detectors
OUI:70B3D532D*
ID_OUI_FROM_DATABASE=Hanwell Technology Co., Ltd.
+OUI:70B3D532E*
+ ID_OUI_FROM_DATABASE=A&T Corporation
+
OUI:70B3D532F*
ID_OUI_FROM_DATABASE=Movidius SRL
OUI:70B3D5330*
ID_OUI_FROM_DATABASE=iOne
+OUI:70B3D5331*
+ ID_OUI_FROM_DATABASE=Firecom, Inc.
+
OUI:70B3D5332*
ID_OUI_FROM_DATABASE=InnoSenT
OUI:70B3D5334*
ID_OUI_FROM_DATABASE=Dokuen Co. Ltd.
+OUI:70B3D5335*
+ ID_OUI_FROM_DATABASE=Jonsa Australia Pty Ltd
+
OUI:70B3D5336*
ID_OUI_FROM_DATABASE=Synaccess Networks Inc.
OUI:70B3D5368*
ID_OUI_FROM_DATABASE=White Matter LLC
+OUI:70B3D5369*
+ ID_OUI_FROM_DATABASE=ALVAT s.r.o.
+
OUI:70B3D536A*
ID_OUI_FROM_DATABASE=Becton Dickinson
OUI:70B3D5384*
ID_OUI_FROM_DATABASE=Sensohive Technologies
+OUI:70B3D5385*
+ ID_OUI_FROM_DATABASE=Kamacho Scale Co., Ltd.
+
OUI:70B3D5387*
ID_OUI_FROM_DATABASE=GWF MessSysteme AG
OUI:70B3D53C0*
ID_OUI_FROM_DATABASE=DK-Technologies A/S
+OUI:70B3D53C1*
+ ID_OUI_FROM_DATABASE=thingdust AG
+
OUI:70B3D53C2*
ID_OUI_FROM_DATABASE=Cellular Specialties, Inc.
OUI:70B3D53D5*
ID_OUI_FROM_DATABASE=oxynet Solutions
+OUI:70B3D53D6*
+ ID_OUI_FROM_DATABASE=Ariston Thermo s.p.a.
+
OUI:70B3D53D7*
ID_OUI_FROM_DATABASE=Remote Sensing Solutions, Inc.
OUI:70B3D53DF*
ID_OUI_FROM_DATABASE=MultiDyne
+OUI:70B3D53E0*
+ ID_OUI_FROM_DATABASE=Gogo Business Aviation
+
OUI:70B3D53E1*
ID_OUI_FROM_DATABASE=Barnstormer Softworks
OUI:70B3D53FB*
ID_OUI_FROM_DATABASE=Liberty Reach
+OUI:70B3D53FC*
+ ID_OUI_FROM_DATABASE=TangRen C&S CO., Ltd
+
OUI:70B3D53FE*
ID_OUI_FROM_DATABASE=Mentor Graphics
OUI:70B3D540B*
ID_OUI_FROM_DATABASE=QUERCUS TECHNOLOGIES, S.L.
+OUI:70B3D540D*
+ ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
+
OUI:70B3D540E*
ID_OUI_FROM_DATABASE=Liaoyun Information Technology Co., Ltd.
OUI:70B3D544B*
ID_OUI_FROM_DATABASE=Open System Solutions Limited
+OUI:70B3D544D*
+ ID_OUI_FROM_DATABASE=Vessel Technology Ltd
+
OUI:70B3D544E*
ID_OUI_FROM_DATABASE=Solace Systems Inc.
OUI:70B3D5465*
ID_OUI_FROM_DATABASE=ENERGISME
+OUI:70B3D5466*
+ ID_OUI_FROM_DATABASE=SYLink Technologie
+
+OUI:70B3D5467*
+ ID_OUI_FROM_DATABASE=GreenWake Technologies
+
OUI:70B3D5469*
ID_OUI_FROM_DATABASE=Gentec Systems Co.
OUI:70B3D5472*
ID_OUI_FROM_DATABASE=Quadio Devices Private Limited
+OUI:70B3D5473*
+ ID_OUI_FROM_DATABASE=KeyProd
+
OUI:70B3D5475*
ID_OUI_FROM_DATABASE=EWATTCH
OUI:70B3D5480*
ID_OUI_FROM_DATABASE=Emergency Lighting Products Limited
+OUI:70B3D5481*
+ ID_OUI_FROM_DATABASE=STEP sarl
+
OUI:70B3D5482*
ID_OUI_FROM_DATABASE=Aeryon Labs Inc
OUI:70B3D54B4*
ID_OUI_FROM_DATABASE=Hi Tech Systems Ltd
+OUI:70B3D54B5*
+ ID_OUI_FROM_DATABASE=Toolplanet Co., Ltd.
+
OUI:70B3D54B6*
ID_OUI_FROM_DATABASE=VEILUX INC.
OUI:70B3D54C9*
ID_OUI_FROM_DATABASE=Elsist Srl
+OUI:70B3D54CA*
+ ID_OUI_FROM_DATABASE=PCB Piezotronics
+
OUI:70B3D54CC*
ID_OUI_FROM_DATABASE=FRESENIUS MEDICAL CARE
OUI:70B3D54D2*
ID_OUI_FROM_DATABASE=Biotage Sweden AB
+OUI:70B3D54D3*
+ ID_OUI_FROM_DATABASE=Hefei STAROT Technology Co.,Ltd
+
OUI:70B3D54D4*
ID_OUI_FROM_DATABASE=Nortek Global HVAC
OUI:70B3D54F2*
ID_OUI_FROM_DATABASE=COMPAL ELECTRONICS, INC.
+OUI:70B3D54F3*
+ ID_OUI_FROM_DATABASE=XPS ELETRONICA LTDA
+
OUI:70B3D54F4*
ID_OUI_FROM_DATABASE=WiTagg, Inc
OUI:70B3D550E*
ID_OUI_FROM_DATABASE=Micro Trend Automation Co., LTD
+OUI:70B3D550F*
+ ID_OUI_FROM_DATABASE=LLC Sarov Innovative Technologies (WIZOLUTION)
+
OUI:70B3D5510*
ID_OUI_FROM_DATABASE=PSL ELEKTRONİK SANAYİ VE TİCARET A.S.
OUI:70B3D5528*
ID_OUI_FROM_DATABASE=Aplex Technology Inc.
+OUI:70B3D5529*
+ ID_OUI_FROM_DATABASE=Inventeq B.V.
+
OUI:70B3D552A*
ID_OUI_FROM_DATABASE=Dataflex International BV
OUI:70B3D552E*
ID_OUI_FROM_DATABASE=Swissponic Sagl
+OUI:70B3D552F*
+ ID_OUI_FROM_DATABASE=R.C. Systems Inc
+
OUI:70B3D5530*
ID_OUI_FROM_DATABASE=iSiS-Ex Limited
ID_OUI_FROM_DATABASE=Tempris GmbH
OUI:70B3D553A*
- ID_OUI_FROM_DATABASE=Pano0ramic Power
+ ID_OUI_FROM_DATABASE=Panoramic Power
OUI:70B3D553B*
ID_OUI_FROM_DATABASE=Mr.Loop
OUI:70B3D553D*
ID_OUI_FROM_DATABASE=ACCEL CORP
+OUI:70B3D553E*
+ ID_OUI_FROM_DATABASE=Asiga Pty Ltd
+
+OUI:70B3D553F*
+ ID_OUI_FROM_DATABASE=Abbott Diagnostics Technologies AS
+
+OUI:70B3D5541*
+ ID_OUI_FROM_DATABASE=Nanjing Pingguang Electronic Technology Co., Ltd
+
OUI:70B3D5542*
ID_OUI_FROM_DATABASE=RTDS Technologies Inc.
OUI:70B3D5589*
ID_OUI_FROM_DATABASE=Cityntel OU
+OUI:70B3D558A*
+ ID_OUI_FROM_DATABASE=ITK Dr. Kassen GmbH
+
OUI:70B3D558C*
ID_OUI_FROM_DATABASE=OPTSYS
OUI:70B3D55AB*
ID_OUI_FROM_DATABASE=Sea Air and Land Communications Ltd
+OUI:70B3D55AC*
+ ID_OUI_FROM_DATABASE=LM-Instruments Oy
+
OUI:70B3D55AD*
ID_OUI_FROM_DATABASE=Profotech
OUI:70B3D55AE*
ID_OUI_FROM_DATABASE=TinTec Co., Ltd.
+OUI:70B3D55AF*
+ ID_OUI_FROM_DATABASE=JENG IoT BV
+
OUI:70B3D55B0*
ID_OUI_FROM_DATABASE=Qxperts Italia S.r.l.
OUI:70B3D55C5*
ID_OUI_FROM_DATABASE=Haag-Streit AG
+OUI:70B3D55C6*
+ ID_OUI_FROM_DATABASE=C4I Systems Ltd
+
OUI:70B3D55C8*
ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
OUI:70B3D55D1*
ID_OUI_FROM_DATABASE=Software Motor Corp
+OUI:70B3D55D2*
+ ID_OUI_FROM_DATABASE=Contec Americas Inc.
+
OUI:70B3D55D3*
ID_OUI_FROM_DATABASE=Supracon AG
OUI:70B3D55F6*
ID_OUI_FROM_DATABASE=FreeFlight Systems
+OUI:70B3D55F7*
+ ID_OUI_FROM_DATABASE=JFA Electronics Industry and Commerce EIRELI
+
OUI:70B3D55F8*
ID_OUI_FROM_DATABASE=Forcite Helmet Systems Pty Ltd
OUI:70B3D5600*
ID_OUI_FROM_DATABASE=Stellwerk GmbH
+OUI:70B3D5601*
+ ID_OUI_FROM_DATABASE=Tricom Research Inc.
+
OUI:70B3D5602*
ID_OUI_FROM_DATABASE=Quantum Opus, LLC
OUI:70B3D5613*
ID_OUI_FROM_DATABASE=Suprock Technologies
+OUI:70B3D5614*
+ ID_OUI_FROM_DATABASE=QUALITTEQ LLC
+
OUI:70B3D5615*
ID_OUI_FROM_DATABASE=JSC OTZVUK
OUI:70B3D5628*
ID_OUI_FROM_DATABASE=MECT SRL
+OUI:70B3D562A*
+ ID_OUI_FROM_DATABASE=DOGA
+
OUI:70B3D562B*
ID_OUI_FROM_DATABASE=Silicann Systems GmbH
OUI:70B3D562C*
ID_OUI_FROM_DATABASE=OOO NTC Rotek
+OUI:70B3D562D*
+ ID_OUI_FROM_DATABASE=elements
+
+OUI:70B3D562E*
+ ID_OUI_FROM_DATABASE=LINEAGE POWER PVT LTD.,
+
OUI:70B3D562F*
ID_OUI_FROM_DATABASE=BARCO, s.r.o.
OUI:70B3D5638*
ID_OUI_FROM_DATABASE=Parkalot Denmark ApS
+OUI:70B3D5639*
+ ID_OUI_FROM_DATABASE=DORLET SAU
+
OUI:70B3D563A*
ID_OUI_FROM_DATABASE=DAVE SRL
OUI:70B3D5640*
ID_OUI_FROM_DATABASE=Electronic Equipment Company Pvt. Ltd.
+OUI:70B3D5641*
+ ID_OUI_FROM_DATABASE=Burk Technology
+
OUI:70B3D5642*
ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
OUI:70B3D565E*
ID_OUI_FROM_DATABASE=Season Electronics Ltd
+OUI:70B3D565F*
+ ID_OUI_FROM_DATABASE=Axnes AS
+
OUI:70B3D5660*
ID_OUI_FROM_DATABASE=Smart Service Technologies CO., LTD
ID_OUI_FROM_DATABASE=CT Company
OUI:70B3D5669*
- ID_OUI_FROM_DATABASE=Pano0ramic Power
+ ID_OUI_FROM_DATABASE=Panoramic Power
OUI:70B3D566A*
ID_OUI_FROM_DATABASE=Private
OUI:70B3D5682*
ID_OUI_FROM_DATABASE=Rosslare Enterprises Limited
+OUI:70B3D5683*
+ ID_OUI_FROM_DATABASE=DECYBEN
+
OUI:70B3D5684*
ID_OUI_FROM_DATABASE=LECO Corporation
OUI:70B3D5697*
ID_OUI_FROM_DATABASE=Alazar Technologies Inc.
+OUI:70B3D5699*
+ ID_OUI_FROM_DATABASE=Flextronics International Kft
+
OUI:70B3D569A*
ID_OUI_FROM_DATABASE=Altaneos
OUI:70B3D56BC*
ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG
+OUI:70B3D56BD*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
OUI:70B3D56BE*
ID_OUI_FROM_DATABASE=VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD
OUI:70B3D56C1*
ID_OUI_FROM_DATABASE=Labtronik s.r.l.
+OUI:70B3D56C2*
+ ID_OUI_FROM_DATABASE=TEX COMPUTER SRL
+
OUI:70B3D56C3*
ID_OUI_FROM_DATABASE=BEIJING ZGH SECURITY RESEARCH INSTITUTE CO., LTD
OUI:70B3D56C5*
ID_OUI_FROM_DATABASE=CJSC «Russian telecom equipment company» (CJSC RTEC)
+OUI:70B3D56C6*
+ ID_OUI_FROM_DATABASE=Abbott Diagnostics Technologies AS
+
OUI:70B3D56C7*
ID_OUI_FROM_DATABASE=Becton Dickinson
OUI:70B3D56D6*
ID_OUI_FROM_DATABASE=KMtronic Ltd.
+OUI:70B3D56D7*
+ ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+
OUI:70B3D56D8*
ID_OUI_FROM_DATABASE=Shanghai YuanAn Environmental Protection Technology Co.,Ltd
OUI:70B3D5743*
ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG
+OUI:70B3D5744*
+ ID_OUI_FROM_DATABASE=PHYZHON Health Inc
+
OUI:70B3D5745*
ID_OUI_FROM_DATABASE=TMSI LLC
OUI:70B3D577E*
ID_OUI_FROM_DATABASE=Blue Marble Communications, Inc.
+OUI:70B3D577F*
+ ID_OUI_FROM_DATABASE=Microchip Technology Germany II GmbH&Co.KG
+
OUI:70B3D5780*
ID_OUI_FROM_DATABASE=NIDEC LEROY-SOMER
OUI:70B3D578C*
ID_OUI_FROM_DATABASE=Survalent Technology Corporation
+OUI:70B3D578D*
+ ID_OUI_FROM_DATABASE=AVL DiTEST GmbH
+
OUI:70B3D578E*
ID_OUI_FROM_DATABASE=effectas GmbH
OUI:70B3D57DA*
ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
+OUI:70B3D57DB*
+ ID_OUI_FROM_DATABASE=aquila biolabs GmbH
+
OUI:70B3D57DC*
ID_OUI_FROM_DATABASE=Software Systems Plus
OUI:70B3D57FB*
ID_OUI_FROM_DATABASE=db Broadcast Products Ltd
+OUI:70B3D57FC*
+ ID_OUI_FROM_DATABASE=Surion (Pty) Ltd
+
OUI:70B3D57FD*
ID_OUI_FROM_DATABASE=SYS TEC electronic GmbH
OUI:70B3D5800*
ID_OUI_FROM_DATABASE=HeadsafeIP PTY LTD
+OUI:70B3D5801*
+ ID_OUI_FROM_DATABASE=Glory Technology Service Inc.
+
OUI:70B3D5802*
ID_OUI_FROM_DATABASE=Qingdao CNR HITACH Railway Signal&communication co.,ltd
OUI:70B3D5857*
ID_OUI_FROM_DATABASE=RCH ITALIA SPA
+OUI:70B3D5858*
+ ID_OUI_FROM_DATABASE=Hubbell Power Systems
+
OUI:70B3D585A*
ID_OUI_FROM_DATABASE=BRUSHIES
OUI:70B3D5868*
ID_OUI_FROM_DATABASE=U-JIN Mesco Co., Ltd.
+OUI:70B3D5869*
+ ID_OUI_FROM_DATABASE=chargeBIG
+
OUI:70B3D586A*
ID_OUI_FROM_DATABASE=Stealth Communications
OUI:70B3D5871*
ID_OUI_FROM_DATABASE=Oso Technologies
+OUI:70B3D5872*
+ ID_OUI_FROM_DATABASE=Nippon Safety co,ltd
+
OUI:70B3D5873*
ID_OUI_FROM_DATABASE=Vishay Nobel AB
OUI:70B3D588D*
ID_OUI_FROM_DATABASE=LG Electronics
+OUI:70B3D588E*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
OUI:70B3D588F*
ID_OUI_FROM_DATABASE=Quaesta Instruments, LLC
OUI:70B3D5897*
ID_OUI_FROM_DATABASE=EFG CZ spol. s r.o.
+OUI:70B3D5898*
+ ID_OUI_FROM_DATABASE=Salupo Sas
+
OUI:70B3D5899*
ID_OUI_FROM_DATABASE=Viotec USA
OUI:70B3D58A6*
ID_OUI_FROM_DATABASE=CRDE
+OUI:70B3D58A7*
+ ID_OUI_FROM_DATABASE=Tucsen Photonics Co., Ltd.
+
OUI:70B3D58A8*
ID_OUI_FROM_DATABASE=megatec electronic GmbH
OUI:70B3D58E6*
ID_OUI_FROM_DATABASE=Mothonic AB
+OUI:70B3D58E7*
+ ID_OUI_FROM_DATABASE=REO AG
+
OUI:70B3D58EA*
ID_OUI_FROM_DATABASE=JLCooper Electronics
OUI:70B3D5920*
ID_OUI_FROM_DATABASE=SLAT
+OUI:70B3D5922*
+ ID_OUI_FROM_DATABASE=Adcole Maryland Aerospace
+
OUI:70B3D5923*
ID_OUI_FROM_DATABASE=eumig industrie-tv GmbH
OUI:70B3D5943*
ID_OUI_FROM_DATABASE=Abbott Medical Optics Inc.
+OUI:70B3D5944*
+ ID_OUI_FROM_DATABASE=Chromateq
+
OUI:70B3D5945*
ID_OUI_FROM_DATABASE=Symboticware Incorporated
OUI:70B3D595C*
ID_OUI_FROM_DATABASE=Wilson Electronics
+OUI:70B3D595D*
+ ID_OUI_FROM_DATABASE=GIORDANO CONTROLS SPA
+
OUI:70B3D595E*
ID_OUI_FROM_DATABASE=BLOCKSI LLC
OUI:70B3D5978*
ID_OUI_FROM_DATABASE=Satixfy Israel Ltd.
+OUI:70B3D5979*
+ ID_OUI_FROM_DATABASE=eSMART Technologies SA
+
OUI:70B3D597A*
ID_OUI_FROM_DATABASE=Orion Corporation
OUI:70B3D597C*
ID_OUI_FROM_DATABASE=Nu-Tek Power Controls and Automation
+OUI:70B3D597D*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
OUI:70B3D597E*
ID_OUI_FROM_DATABASE=Public Joint Stock Company Morion
OUI:70B3D597F*
ID_OUI_FROM_DATABASE=BISTOS.,Co.,Ltd
+OUI:70B3D5980*
+ ID_OUI_FROM_DATABASE=Beijing Yourong Runda Rechnology Development Co.Ltd.
+
OUI:70B3D5981*
ID_OUI_FROM_DATABASE=Zamir Recognition Systems Ltd.
OUI:70B3D5A08*
ID_OUI_FROM_DATABASE=BioBusiness
+OUI:70B3D5A09*
+ ID_OUI_FROM_DATABASE=Smart Embedded Systems
+
OUI:70B3D5A0A*
ID_OUI_FROM_DATABASE=CAPSYS
OUI:70B3D5A69*
ID_OUI_FROM_DATABASE=Leviathan Solutions Ltd.
+OUI:70B3D5A6A*
+ ID_OUI_FROM_DATABASE=Privafy, Inc
+
OUI:70B3D5A6B*
ID_OUI_FROM_DATABASE=xmi systems
ID_OUI_FROM_DATABASE=Sadel S.p.A.
OUI:70B3D5A75*
- ID_OUI_FROM_DATABASE=Taejin InforTech
+ ID_OUI_FROM_DATABASE=Taejin InfoTech
OUI:70B3D5A76*
ID_OUI_FROM_DATABASE=Pietro Fiorentini
+OUI:70B3D5A77*
+ ID_OUI_FROM_DATABASE=SPX Radiodetection
+
OUI:70B3D5A78*
ID_OUI_FROM_DATABASE=Bionics co.,ltd.
OUI:70B3D5A7E*
ID_OUI_FROM_DATABASE=QUICCO SOUND Corporation
+OUI:70B3D5A7F*
+ ID_OUI_FROM_DATABASE=AUDIO VISUAL DIGITAL SYSTEMS
+
OUI:70B3D5A80*
ID_OUI_FROM_DATABASE=EVCO SPA
OUI:70B3D5A82*
ID_OUI_FROM_DATABASE=Telefrank GmbH
+OUI:70B3D5A83*
+ ID_OUI_FROM_DATABASE=SHENZHEN HUINENGYUAN Technology Co., Ltd
+
OUI:70B3D5A84*
ID_OUI_FROM_DATABASE=SOREL GmbH Mikroelektronik
OUI:70B3D5AD2*
ID_OUI_FROM_DATABASE=Wart-Elektronik
+OUI:70B3D5AD3*
+ ID_OUI_FROM_DATABASE=WARECUBE,INC
+
+OUI:70B3D5AD4*
+ ID_OUI_FROM_DATABASE=INVISSYS
+
OUI:70B3D5AD5*
ID_OUI_FROM_DATABASE=Birdland Audio
OUI:70B3D5B02*
ID_OUI_FROM_DATABASE=Nordic Automation Systems AS
+OUI:70B3D5B03*
+ ID_OUI_FROM_DATABASE=Sprintshield d.o.o.
+
OUI:70B3D5B04*
ID_OUI_FROM_DATABASE=Herrmann Datensysteme GmbH
OUI:70B3D5B11*
ID_OUI_FROM_DATABASE=CAB S.R.L.
-OUI:70B3D5B12*
- ID_OUI_FROM_DATABASE=SFR
-
OUI:70B3D5B13*
ID_OUI_FROM_DATABASE=Omwave
OUI:70B3D5B1A*
ID_OUI_FROM_DATABASE=Aaronia AG
+OUI:70B3D5B1B*
+ ID_OUI_FROM_DATABASE=Technology Link Corporation
+
OUI:70B3D5B1D*
ID_OUI_FROM_DATABASE=Safelet BV
OUI:70B3D5B35*
ID_OUI_FROM_DATABASE=Rexxam Co.,Ltd.
+OUI:70B3D5B36*
+ ID_OUI_FROM_DATABASE=Cetitec GmbH
+
OUI:70B3D5B37*
ID_OUI_FROM_DATABASE=CODEC Co., Ltd.
OUI:70B3D5B40*
ID_OUI_FROM_DATABASE=Wuhan Xingtuxinke ELectronic Co.,Ltd
+OUI:70B3D5B41*
+ ID_OUI_FROM_DATABASE=T&M Media Pty Ltd
+
OUI:70B3D5B43*
ID_OUI_FROM_DATABASE=ZAO ZEO
OUI:70B3D5B74*
ID_OUI_FROM_DATABASE=OnYield Inc Ltd
+OUI:70B3D5B75*
+ ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG
+
OUI:70B3D5B76*
ID_OUI_FROM_DATABASE=ATL-SD
OUI:70B3D5B94*
ID_OUI_FROM_DATABASE=Cygnetic Technologies (Pty) Ltd
+OUI:70B3D5B96*
+ ID_OUI_FROM_DATABASE=Oculii
+
OUI:70B3D5B97*
ID_OUI_FROM_DATABASE=Canam Technology, Inc.
OUI:70B3D5C08*
ID_OUI_FROM_DATABASE=Talleres de Escoriaza SA
+OUI:70B3D5C09*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
OUI:70B3D5C0A*
ID_OUI_FROM_DATABASE=Infosocket Co., Ltd.
OUI:70B3D5C17*
ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC
+OUI:70B3D5C18*
+ ID_OUI_FROM_DATABASE=Sanmina Israel
+
OUI:70B3D5C1A*
ID_OUI_FROM_DATABASE=Xylon
OUI:70B3D5C2A*
ID_OUI_FROM_DATABASE=Array Telepresence
+OUI:70B3D5C2B*
+ ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+
OUI:70B3D5C2C*
ID_OUI_FROM_DATABASE=Dromont S.p.A.
OUI:70B3D5C2F*
ID_OUI_FROM_DATABASE=ATBiS Co.,Ltd
+OUI:70B3D5C31*
+ ID_OUI_FROM_DATABASE=German Power GmbH
+
OUI:70B3D5C32*
ID_OUI_FROM_DATABASE=INFRASAFE/ ADVANTOR SYSTEMS
OUI:70B3D5C35*
ID_OUI_FROM_DATABASE=Vibrationmaster
+OUI:70B3D5C36*
+ ID_OUI_FROM_DATABASE=Knowledge Resources GmbH
+
OUI:70B3D5C37*
ID_OUI_FROM_DATABASE=Keycom Corp.
OUI:70B3D5C51*
ID_OUI_FROM_DATABASE=Innotas Elektronik GmbH
+OUI:70B3D5C52*
+ ID_OUI_FROM_DATABASE=sensorway
+
OUI:70B3D5C53*
ID_OUI_FROM_DATABASE=S Labs sp. z o.o.
OUI:70B3D5C70*
ID_OUI_FROM_DATABASE=Magnetek
+OUI:70B3D5C72*
+ ID_OUI_FROM_DATABASE=Scharco Elektronik GmbH
+
OUI:70B3D5C73*
ID_OUI_FROM_DATABASE=C.D.N.CORPORATION
OUI:70B3D5C81*
ID_OUI_FROM_DATABASE=DSP DESIGN
+OUI:70B3D5C82*
+ ID_OUI_FROM_DATABASE=Sicon srl
+
OUI:70B3D5C83*
ID_OUI_FROM_DATABASE=CertusNet Inc.
OUI:70B3D5C93*
ID_OUI_FROM_DATABASE=GMI Ltd
+OUI:70B3D5C94*
+ ID_OUI_FROM_DATABASE=Vars Technology
+
OUI:70B3D5C95*
ID_OUI_FROM_DATABASE=Chengdu Meihuan Technology Co., Ltd
OUI:70B3D5C97*
ID_OUI_FROM_DATABASE=CSINFOTEL
+OUI:70B3D5C98*
+ ID_OUI_FROM_DATABASE=Trust Automation
+
+OUI:70B3D5C99*
+ ID_OUI_FROM_DATABASE=Remote Diagnostic Technologies Ltd
+
+OUI:70B3D5C9A*
+ ID_OUI_FROM_DATABASE=Todd Digital Limited
+
OUI:70B3D5C9B*
ID_OUI_FROM_DATABASE=Tieto Sweden AB
OUI:70B3D5CBA*
ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+OUI:70B3D5CBB*
+ ID_OUI_FROM_DATABASE=Postmark Incorporated
+
OUI:70B3D5CBC*
ID_OUI_FROM_DATABASE=Procon Electronics Pty Ltd
OUI:70B3D5D29*
ID_OUI_FROM_DATABASE=Sportzcast
+OUI:70B3D5D2A*
+ ID_OUI_FROM_DATABASE=ITsynergy Ltd
+
OUI:70B3D5D2B*
ID_OUI_FROM_DATABASE=StreamPlay Oy Ltd
OUI:70B3D5D76*
ID_OUI_FROM_DATABASE=attocube systems AG
+OUI:70B3D5D77*
+ ID_OUI_FROM_DATABASE=Portrait Displays, Inc.
+
OUI:70B3D5D79*
ID_OUI_FROM_DATABASE=GOMA ELETTRONICA SpA
OUI:70B3D5D9E*
ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
+OUI:70B3D5D9F*
+ ID_OUI_FROM_DATABASE=Digital Solutions JSC
+
OUI:70B3D5DA1*
ID_OUI_FROM_DATABASE=Qprel srl
OUI:70B3D5DAD*
ID_OUI_FROM_DATABASE=GD Mission Systems
+OUI:70B3D5DAE*
+ ID_OUI_FROM_DATABASE=LGE
+
OUI:70B3D5DAF*
ID_OUI_FROM_DATABASE=INNOVATIVE CONCEPTS AND DESIGN LLC
OUI:70B3D5DB8*
ID_OUI_FROM_DATABASE=SISTEM SA
+OUI:70B3D5DBB*
+ ID_OUI_FROM_DATABASE=Fuhr GmbH Filtertechnik
+
OUI:70B3D5DBC*
ID_OUI_FROM_DATABASE=Gamber Johnson-LLC
OUI:70B3D5DE0*
ID_OUI_FROM_DATABASE=eCozy GmbH
+OUI:70B3D5DE1*
+ ID_OUI_FROM_DATABASE=Duplomatic MS spa
+
OUI:70B3D5DE2*
ID_OUI_FROM_DATABASE=ACD Elekronik GmbH
OUI:70B3D5E00*
ID_OUI_FROM_DATABASE=Jeaway CCTV Security Ltd,.
+OUI:70B3D5E01*
+ ID_OUI_FROM_DATABASE=EarTex
+
OUI:70B3D5E02*
ID_OUI_FROM_DATABASE=YEHL & JORDAN LLC
OUI:70B3D5E10*
ID_OUI_FROM_DATABASE=Leidos
+OUI:70B3D5E12*
+ ID_OUI_FROM_DATABASE=SNK, Inc.
+
OUI:70B3D5E14*
ID_OUI_FROM_DATABASE=Automata Spa
OUI:70B3D5E23*
ID_OUI_FROM_DATABASE=Smith Meter, Inc.
+OUI:70B3D5E24*
+ ID_OUI_FROM_DATABASE=Gogo Business Aviation
+
OUI:70B3D5E25*
ID_OUI_FROM_DATABASE=GJD Manufacturing
OUI:70B3D5E30*
ID_OUI_FROM_DATABASE=QUISS AG
+OUI:70B3D5E31*
+ ID_OUI_FROM_DATABASE=NEUROPHET, Inc.
+
OUI:70B3D5E32*
ID_OUI_FROM_DATABASE=HERUTU ELECTRONICS CORPORATION
OUI:70B3D5E33*
ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH
+OUI:70B3D5E34*
+ ID_OUI_FROM_DATABASE=Gamber Johnson-LLC
+
OUI:70B3D5E35*
ID_OUI_FROM_DATABASE=Nanospeed Technologies Limited
OUI:70B3D5E4C*
ID_OUI_FROM_DATABASE=IAI-Israel Aerospace Industries MBT
+OUI:70B3D5E4D*
+ ID_OUI_FROM_DATABASE=Vulcan Wireless Inc.
+
OUI:70B3D5E4E*
ID_OUI_FROM_DATABASE=Midfin Systems
OUI:70B3D5E5B*
ID_OUI_FROM_DATABASE=Argosy Labs Inc.
+OUI:70B3D5E5C*
+ ID_OUI_FROM_DATABASE=Walton Hi-Tech Industries Ltd.
+
OUI:70B3D5E5D*
ID_OUI_FROM_DATABASE=Boffins Technologies AB
OUI:70B3D5E71*
ID_OUI_FROM_DATABASE=SiS Technology
+OUI:70B3D5E72*
+ ID_OUI_FROM_DATABASE=KDT Corp.
+
OUI:70B3D5E74*
ID_OUI_FROM_DATABASE=Exfrontier Co., Ltd.
OUI:70B3D5EA4*
ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
+OUI:70B3D5EA5*
+ ID_OUI_FROM_DATABASE=LOTES TM OOO
+
OUI:70B3D5EA6*
ID_OUI_FROM_DATABASE=Galios
OUI:70B3D5EBE*
ID_OUI_FROM_DATABASE=Sierra Pacific Innovations Corp
+OUI:70B3D5EBF*
+ ID_OUI_FROM_DATABASE=AUTOMATICA Y REGULACION S.A.
+
OUI:70B3D5EC1*
ID_OUI_FROM_DATABASE=Xafax Nederland bv
OUI:70B3D5EE5*
ID_OUI_FROM_DATABASE=Beijing Hzhytech Technology Co.Ltd
+OUI:70B3D5EE6*
+ ID_OUI_FROM_DATABASE=Vaunix Technology Corporation
+
OUI:70B3D5EE7*
ID_OUI_FROM_DATABASE=BLUE-SOLUTIONS CANADA INC.
OUI:70B3D5EEA*
ID_OUI_FROM_DATABASE=Dameca a/s
+OUI:70B3D5EEB*
+ ID_OUI_FROM_DATABASE=shenzhen suofeixiang technology Co.,Ltd
+
OUI:70B3D5EEC*
ID_OUI_FROM_DATABASE=Impolux GmbH
OUI:70B3D5F08*
ID_OUI_FROM_DATABASE=Szabo Software & Engineering UK Ltd
+OUI:70B3D5F09*
+ ID_OUI_FROM_DATABASE=Mictrotrac Retsch GmbH
+
OUI:70B3D5F0A*
ID_OUI_FROM_DATABASE=Neuronal Innovation Control S.L.
OUI:70B3D5F27*
ID_OUI_FROM_DATABASE=NIRIT- Xinwei Telecom Technology Co., Ltd.
+OUI:70B3D5F28*
+ ID_OUI_FROM_DATABASE=Yi An Electronics Co., Ltd
+
OUI:70B3D5F29*
ID_OUI_FROM_DATABASE=SamabaNova Systems
OUI:70B3D5F3C*
ID_OUI_FROM_DATABASE=Gigaray
+OUI:70B3D5F3D*
+ ID_OUI_FROM_DATABASE=KAYA Instruments
+
OUI:70B3D5F3E*
ID_OUI_FROM_DATABASE=ООО РОНЕКС
OUI:70B3D5F45*
ID_OUI_FROM_DATABASE=Norbit ODM AS
+OUI:70B3D5F47*
+ ID_OUI_FROM_DATABASE=TXMission Ltd.
+
OUI:70B3D5F48*
ID_OUI_FROM_DATABASE=HEITEC AG
OUI:70B3D5F63*
ID_OUI_FROM_DATABASE=Ars Products
+OUI:70B3D5F64*
+ ID_OUI_FROM_DATABASE=silicom
+
OUI:70B3D5F65*
ID_OUI_FROM_DATABASE=MARKUS LABS
OUI:70B3D5F68*
ID_OUI_FROM_DATABASE=AL ZAJEL MODERN TELECOMM
+OUI:70B3D5F69*
+ ID_OUI_FROM_DATABASE=Copper Labs, Inc.
+
+OUI:70B3D5F6A*
+ ID_OUI_FROM_DATABASE=Guan Show Technologe Co., Ltd.
+
OUI:70B3D5F6C*
ID_OUI_FROM_DATABASE=VisioGreen
OUI:70B3D5F9E*
ID_OUI_FROM_DATABASE=International Center for Elementary Particle Physics, The University of Tokyo
+OUI:70B3D5F9F*
+ ID_OUI_FROM_DATABASE=M.A.C. Solutions (UK) Ltd
+
OUI:70B3D5FA0*
ID_OUI_FROM_DATABASE=TIAMA
OUI:70B3D5FA7*
ID_OUI_FROM_DATABASE=Nordson Corporation
+OUI:70B3D5FA8*
+ ID_OUI_FROM_DATABASE=Munters
+
OUI:70B3D5FA9*
ID_OUI_FROM_DATABASE=CorDes, LLC
OUI:70B3D5FB0*
ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA
+OUI:70B3D5FB1*
+ ID_OUI_FROM_DATABASE=TOMEI TSUSHIN KOGYO CO,.LTD
+
OUI:70B3D5FB2*
ID_OUI_FROM_DATABASE=KJ3 Elektronik AB
OUI:70B3D5FC2*
ID_OUI_FROM_DATABASE=HUNTER LIBERTY CORPORATION
+OUI:70B3D5FC3*
+ ID_OUI_FROM_DATABASE=myUpTech AB
+
OUI:70B3D5FC5*
ID_OUI_FROM_DATABASE=Eltwin A/S
OUI:70CA4D*
ID_OUI_FROM_DATABASE=Shenzhen lnovance Technology Co.,Ltd.
+OUI:70CA97*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
OUI:70CA9B*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:70F087*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:70F096*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:70F11C*
ID_OUI_FROM_DATABASE=Shenzhen Ogemray Technology Co.,Ltd
OUI:741F79*
ID_OUI_FROM_DATABASE=YOUNGKOOK ELECTRONICS CO.,LTD
+OUI:7422BB*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:742344*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
OUI:743A65*
ID_OUI_FROM_DATABASE=NEC Corporation
+OUI:743AEF*
+ ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD.
+
OUI:743C18*
ID_OUI_FROM_DATABASE=Taicang T&W Electronics
OUI:744401*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:74452D*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:74458A*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:745798*
ID_OUI_FROM_DATABASE=TRUMPF Laser GmbH + Co. KG
+OUI:7458F3*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:745909*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
ID_OUI_FROM_DATABASE=Bestek Corp.
OUI:748EF8*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:748F1B*
ID_OUI_FROM_DATABASE=MasterImage 3D
OUI:74AC5F*
ID_OUI_FROM_DATABASE=Qiku Internet Network Scientific (Shenzhen) Co., Ltd.
+OUI:74ACB9*
+ ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc.
+
OUI:74ADB7*
ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
OUI:74B587*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:74B6B6*
+ ID_OUI_FROM_DATABASE=eero inc.
+
OUI:74B91E*
ID_OUI_FROM_DATABASE=Nanjing Bestway Automation System Co., Ltd
OUI:74C63B*
ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
+OUI:74C929*
+ ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
+
OUI:74C99A*
ID_OUI_FROM_DATABASE=Ericsson AB
OUI:782A79*
ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+OUI:782B46*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:782BCB*
ID_OUI_FROM_DATABASE=Dell Inc.
OUI:784F9B*
ID_OUI_FROM_DATABASE=Juniper Networks
+OUI:78507C*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:78510C*
ID_OUI_FROM_DATABASE=LiveU Ltd.
OUI:785262*
ID_OUI_FROM_DATABASE=Shenzhen Hojy Software Co., Ltd.
+OUI:78530D*
+ ID_OUI_FROM_DATABASE=Shenzhen Skyworth Digital Technology CO., Ltd
+
OUI:785364*
ID_OUI_FROM_DATABASE=SHIFT GmbH
OUI:785712*
ID_OUI_FROM_DATABASE=Mobile Integration Workgroup
+OUI:785773*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:785860*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED
OUI:787D53*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:787E61*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:78818F*
ID_OUI_FROM_DATABASE=Server Racks Australia Pty Ltd
+OUI:7881CE*
+ ID_OUI_FROM_DATABASE=China Mobile Iot Limited company
+
OUI:78843C*
ID_OUI_FROM_DATABASE=Sony Corporation
OUI:7894B4*
ID_OUI_FROM_DATABASE=Sercomm Corporation.
+OUI:7894E8*
+ ID_OUI_FROM_DATABASE=Radio Bridge
+
OUI:789682*
ID_OUI_FROM_DATABASE=zte corporation
ID_OUI_FROM_DATABASE=DAEYEON Control&Instrument Co,.Ltd
OUI:78A6E1*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:78A714*
ID_OUI_FROM_DATABASE=Amphenol
OUI:78A873*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:78AA82*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:78AB60*
ID_OUI_FROM_DATABASE=ABB Australia
OUI:78ABBB*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:78AC44*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
OUI:78ACBF*
ID_OUI_FROM_DATABASE=Igneous Systems
OUI:78B84B*
ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD
+OUI:78B8D6*
+ ID_OUI_FROM_DATABASE=Zebra Technologies Inc.
+
OUI:78BAD0*
ID_OUI_FROM_DATABASE=Shinybow Technology Co. Ltd.
ID_OUI_FROM_DATABASE=Philips Healthcare PCCI
OUI:7C95B1*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:7C95F3*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:7C9A9B*
ID_OUI_FROM_DATABASE=VSE valencia smart energy
+OUI:7C9EBD*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:7CA15D*
ID_OUI_FROM_DATABASE=GN ReSound A/S
OUI:7CAB25*
ID_OUI_FROM_DATABASE=MESMO TECHNOLOGY INC.
+OUI:7CAB60*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:7CACB2*
ID_OUI_FROM_DATABASE=Bosch Software Innovations GmbH
OUI:7CD95C*
ID_OUI_FROM_DATABASE=Google, Inc.
+OUI:7CD9A0*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:7CD9FE*
ID_OUI_FROM_DATABASE=New Cosmos Electric Co., Ltd.
OUI:7CDD90*
ID_OUI_FROM_DATABASE=Shenzhen Ogemray Technology Co., Ltd.
+OUI:7CDFA1*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:7CE044*
ID_OUI_FROM_DATABASE=NEON Inc
OUI:7CEF18*
ID_OUI_FROM_DATABASE=Creative Product Design Pty. Ltd.
+OUI:7CEF61*
+ ID_OUI_FROM_DATABASE=STR Elektronik Josef Schlechtinger GmbH
+
OUI:7CEF8A*
ID_OUI_FROM_DATABASE=Inhon International Ltd.
OUI:8020E1*
ID_OUI_FROM_DATABASE=BVBA DPTechnics
+OUI:8020FD*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:802275*
ID_OUI_FROM_DATABASE=Beijing Beny Wave Technology Co Ltd
+OUI:8022A7*
+ ID_OUI_FROM_DATABASE=NEC Platforms, Ltd.
+
OUI:802689*
ID_OUI_FROM_DATABASE=D-Link International
OUI:80CF41*
ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd.
+OUI:80CFA2*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:80D019*
ID_OUI_FROM_DATABASE=Embed, Inc
OUI:842C80*
ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd.
+OUI:842E14*
+ ID_OUI_FROM_DATABASE=Silicon Laboratories
+
OUI:842E27*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:843E79*
ID_OUI_FROM_DATABASE=Shenzhen Belon Technology CO.,LTD
+OUI:843E92*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:843F4E*
ID_OUI_FROM_DATABASE=Tri-Tech Manufacturing, Inc.
OUI:84A24D*
ID_OUI_FROM_DATABASE=Birds Eye Systems Private Limited
+OUI:84A3B5*
+ ID_OUI_FROM_DATABASE=Propulsion systems
+
OUI:84A423*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
OUI:84AA9C*
ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
+OUI:84AB1A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:84ACA4*
ID_OUI_FROM_DATABASE=Beijing Novel Super Digital TV Technology Co., Ltd
OUI:84C9C6*
ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
+OUI:84CCA8*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:84CD62*
ID_OUI_FROM_DATABASE=ShenZhen IDWELL Technology CO.,Ltd
OUI:84D4C8*
ID_OUI_FROM_DATABASE=Widex A/S
+OUI:84D6C5*
+ ID_OUI_FROM_DATABASE=SolarEdge Technologies
+
OUI:84D6D0*
ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
OUI:88299C*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:882B94*
+ ID_OUI_FROM_DATABASE=MADOKA SYSTEM Co.,Ltd.
+
OUI:882BD7*
ID_OUI_FROM_DATABASE=ADDÉNERGIE TECHNOLOGIES
OUI:88366C*
ID_OUI_FROM_DATABASE=EFM Networks
+OUI:8836CF*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:883A30*
ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
OUI:883FD3*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:884033*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:88403B*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:885BDD*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:885C47*
ID_OUI_FROM_DATABASE=Alcatel Lucent
ID_OUI_FROM_DATABASE=Racktivity
OUI:889471*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:88947E*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
OUI:88964E*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:889655*
+ ID_OUI_FROM_DATABASE=Zitte corporation
+
OUI:889676*
ID_OUI_FROM_DATABASE=TTC MARCONI s.r.o.
OUI:889E33*
ID_OUI_FROM_DATABASE=TCT mobile ltd
+OUI:889E68*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:889F6F*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:88C36E*
ID_OUI_FROM_DATABASE=Beijing Ereneben lnformation Technology Limited
+OUI:88C397*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co., Ltd
+
OUI:88C3B3*
ID_OUI_FROM_DATABASE=SOVICO
OUI:8C09F4*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:8C0C87*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:8C0C90*
ID_OUI_FROM_DATABASE=Ruckus Wireless
OUI:8C3AE3*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+OUI:8C3B32*
+ ID_OUI_FROM_DATABASE=Microfan B.V.
+
OUI:8C3BAD*
ID_OUI_FROM_DATABASE=NETGEAR
OUI:8C59C3*
ID_OUI_FROM_DATABASE=ADB Italia
+OUI:8C59DC*
+ ID_OUI_FROM_DATABASE=ASR Microelectronics (Shanghai) Co., Ltd.
+
OUI:8C5A25*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:8C5F48*
ID_OUI_FROM_DATABASE=Continental Intelligent Transportation Systems LLC
+OUI:8C5FAD*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:8C5FDF*
ID_OUI_FROM_DATABASE=Beijing Railway Signal Factory
OUI:8C6422*
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
+OUI:8C683A*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:8C6878*
ID_OUI_FROM_DATABASE=Nortek-AS
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
OUI:8C7CFF*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:8C7EB3*
ID_OUI_FROM_DATABASE=Lytro, Inc.
OUI:8C839D*
ID_OUI_FROM_DATABASE=SHENZHEN XINYUPENG ELECTRONIC TECHNOLOGY CO., LTD
+OUI:8C83DF*
+ ID_OUI_FROM_DATABASE=Nokia
+
OUI:8C83E1*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:900A3A*
ID_OUI_FROM_DATABASE=PSG Plastic Service GmbH
+OUI:900A84*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
OUI:900BC1*
ID_OUI_FROM_DATABASE=Sprocomm Technologies CO.,Ltd
OUI:901711*
ID_OUI_FROM_DATABASE=Hagenuk Marinekommunikation GmbH
+OUI:90173F*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:90179B*
ID_OUI_FROM_DATABASE=Nanomegas
OUI:905692*
ID_OUI_FROM_DATABASE=Autotalks Ltd.
+OUI:9056FC*
+ ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED
+
OUI:905851*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
OUI:905C44*
ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc.
+OUI:905D7C*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:905F2E*
ID_OUI_FROM_DATABASE=TCT mobile ltd
OUI:90735A*
ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+OUI:90749D*
+ ID_OUI_FROM_DATABASE=IRay Technology Co., Ltd.
+
OUI:907841*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:908C09*
ID_OUI_FROM_DATABASE=Total Phase
+OUI:908C43*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:908C44*
ID_OUI_FROM_DATABASE=H.K ZONGMU TECHNOLOGY CO., LTD.
OUI:9097F3*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:909838*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:909864*
ID_OUI_FROM_DATABASE=Impex-Sat GmbH&Co KG
OUI:909A77*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:909C4A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:909D7D*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:90ADF7*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+OUI:90ADFC*
+ ID_OUI_FROM_DATABASE=Telechips, Inc.
+
OUI:90AE1B*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
OUI:90B832*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:90B8D0*
ID_OUI_FROM_DATABASE=Joyent, Inc.
OUI:90E2BA*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:90E2FC0*
+ ID_OUI_FROM_DATABASE=Pars Ertebat Afzar Co.
+
+OUI:90E2FC1*
+ ID_OUI_FROM_DATABASE=Yite technology
+
+OUI:90E2FC2*
+ ID_OUI_FROM_DATABASE=ShenZhen Temwey Innovation Technology Co.,Ltd.
+
+OUI:90E2FC3*
+ ID_OUI_FROM_DATABASE=Shenzhen Hisource Technology Development CO.,Ltd.
+
+OUI:90E2FC4*
+ ID_OUI_FROM_DATABASE=Dongguan Kangyong electronics technology Co. Ltd
+
+OUI:90E2FC5*
+ ID_OUI_FROM_DATABASE=TOTALONE TECHNOLOGY CO., LTD.
+
+OUI:90E2FC6*
+ ID_OUI_FROM_DATABASE=Sindoh Techno Co., Ltd.
+
+OUI:90E2FC7*
+ ID_OUI_FROM_DATABASE=Fair Winds Digital srl
+
+OUI:90E2FC8*
+ ID_OUI_FROM_DATABASE=bitsensing Inc.
+
+OUI:90E2FC9*
+ ID_OUI_FROM_DATABASE=Huddly AS
+
+OUI:90E2FCA*
+ ID_OUI_FROM_DATABASE=Power Engineering & Manufacturing, Inc.
+
+OUI:90E2FCB*
+ ID_OUI_FROM_DATABASE=Shenzhen Dingsheng Intelligent Technology Co., Ltd
+
+OUI:90E2FCC*
+ ID_OUI_FROM_DATABASE=Stanley Security
+
OUI:90E2FCD*
ID_OUI_FROM_DATABASE=Beijing Lanxum Computer Technology CO.,LTD.
+OUI:90E2FCE*
+ ID_OUI_FROM_DATABASE=DevCom spol. s r.o.
+
OUI:90E6BA*
ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
OUI:90EC50*
ID_OUI_FROM_DATABASE=C.O.B.O. SPA
+OUI:90EC77*
+ ID_OUI_FROM_DATABASE=silicom
+
+OUI:90EEC7*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:90EED9*
ID_OUI_FROM_DATABASE=UNIVERSAL DE DESARROLLOS ELECTRÓNICOS, SA
OUI:9405B6*
ID_OUI_FROM_DATABASE=Liling FullRiver Electronics & Technology Ltd
+OUI:9405BB0*
+ ID_OUI_FROM_DATABASE=Qingdao Maotran Electronics co., ltd
+
+OUI:9405BB1*
+ ID_OUI_FROM_DATABASE=Dongguan Kingtron Electronics Tech Co., Ltd
+
+OUI:9405BB2*
+ ID_OUI_FROM_DATABASE=Dongguan CXWE Technology Co.,Ltd.
+
+OUI:9405BB3*
+ ID_OUI_FROM_DATABASE=Neurik AG
+
+OUI:9405BB4*
+ ID_OUI_FROM_DATABASE=Shenzhen Baolijie Technology Co., Ltd.
+
+OUI:9405BB5*
+ ID_OUI_FROM_DATABASE=Chengdu Zhongheng Network Co.,Ltd.
+
+OUI:9405BB6*
+ ID_OUI_FROM_DATABASE=ZIGPOS GmbH
+
+OUI:9405BB7*
+ ID_OUI_FROM_DATABASE=LTE-X, Inc
+
+OUI:9405BB8*
+ ID_OUI_FROM_DATABASE=iungo
+
+OUI:9405BB9*
+ ID_OUI_FROM_DATABASE=Zimmer GmbH
+
+OUI:9405BBA*
+ ID_OUI_FROM_DATABASE=SolarEdge Technologies
+
+OUI:9405BBB*
+ ID_OUI_FROM_DATABASE=AUSTAR HEARING SCIENCE AND TECHNILIGY(XIAMEN)CO.,LTD
+
+OUI:9405BBC*
+ ID_OUI_FROM_DATABASE=LAO INDUSTRIA LTDA
+
+OUI:9405BBD*
+ ID_OUI_FROM_DATABASE=Sunthink S&T Development Co.,Ltd
+
+OUI:9405BBE*
+ ID_OUI_FROM_DATABASE=BAE Systems
+
OUI:940937*
ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
OUI:9441C1*
ID_OUI_FROM_DATABASE=Mini-Cam Limited
+OUI:94434D*
+ ID_OUI_FROM_DATABASE=Ciena Corporation
+
OUI:944444*
ID_OUI_FROM_DATABASE=LG Innotek
OUI:94885E*
ID_OUI_FROM_DATABASE=Surfilter Network Technology Co., Ltd.
+OUI:948AC6*
+ ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd.
+
OUI:948B03*
ID_OUI_FROM_DATABASE=EAGET Innovation and Technology Co., Ltd.
OUI:94BF80*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:94BF94*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:94BF95*
ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd
OUI:94CA0F*
ID_OUI_FROM_DATABASE=Honeywell Analytics
+OUI:94CC040*
+ ID_OUI_FROM_DATABASE=Hangzhou Yongkong Technology Co., Ltd.
+
+OUI:94CC041*
+ ID_OUI_FROM_DATABASE=GOCOAX, INC
+
+OUI:94CC042*
+ ID_OUI_FROM_DATABASE=Nanjing Yacer Communication Technology Co. Ltd.
+
+OUI:94CC043*
+ ID_OUI_FROM_DATABASE=Shenzhen Link technology Co.,Ltd
+
+OUI:94CC044*
+ ID_OUI_FROM_DATABASE=ProConnections, Inc.
+
+OUI:94CC045*
+ ID_OUI_FROM_DATABASE=SHENZHEN SANRAY TECHNOLOGY CO.,LTD
+
+OUI:94CC046*
+ ID_OUI_FROM_DATABASE=Sam Nazarko Trading Ltd
+
+OUI:94CC047*
+ ID_OUI_FROM_DATABASE=Gowing Business And Contracting Wenzhou Co., LTD
+
+OUI:94CC048*
+ ID_OUI_FROM_DATABASE=CircuitWerkes, Inc.
+
+OUI:94CC049*
+ ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic Co., LTD.
+
+OUI:94CC04A*
+ ID_OUI_FROM_DATABASE=hyBee Inc.
+
+OUI:94CC04B*
+ ID_OUI_FROM_DATABASE=Shandong free optical technology co., ltd.
+
+OUI:94CC04C*
+ ID_OUI_FROM_DATABASE=Shanxi Baixin Information Technology Co., Ltd.
+
+OUI:94CC04D*
+ ID_OUI_FROM_DATABASE=Hanzhuo Information Technology(Shanghai) Ltd.
+
+OUI:94CC04E*
+ ID_OUI_FROM_DATABASE=SynchronicIT BV
+
OUI:94CCB9*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:94DB49*
ID_OUI_FROM_DATABASE=SITCORP
+OUI:94DB56*
+ ID_OUI_FROM_DATABASE=Sony Home Entertainment&Sound Products Inc
+
OUI:94DBC9*
ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
OUI:94E36D*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:94E4BA*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:94E6F7*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:94E711*
ID_OUI_FROM_DATABASE=Xirka Dama Persada PT
+OUI:94E7EA*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:94E848*
ID_OUI_FROM_DATABASE=FYLDE MICRO LTD
OUI:94E98C*
ID_OUI_FROM_DATABASE=Nokia
+OUI:94E9EE*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:94EAEA*
ID_OUI_FROM_DATABASE=TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO
OUI:94FB29*
ID_OUI_FROM_DATABASE=Zebra Technologies Inc.
+OUI:94FBA78*
+ ID_OUI_FROM_DATABASE=Silver-I Co.,LTD.
+
+OUI:94FBA79*
+ ID_OUI_FROM_DATABASE=Shanghai Hyco Genyong Technology Co., Ltd.
+
+OUI:94FBA7D*
+ ID_OUI_FROM_DATABASE=Creotech Instruments S.A.
+
OUI:94FBB2*
ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
OUI:94FEF4*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:98006A*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:980074*
ID_OUI_FROM_DATABASE=Raisecom Technology CO., LTD
OUI:980D2E*
ID_OUI_FROM_DATABASE=HTC Corporation
+OUI:980D51*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:980D67*
ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
OUI:985945*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:985949*
+ ID_OUI_FROM_DATABASE=LUXOTTICA GROUP S.P.A.
+
OUI:985AEB*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:987E46*
ID_OUI_FROM_DATABASE=Emizon Networks Limited
+OUI:987ECA*
+ ID_OUI_FROM_DATABASE=Inventus Power Eletronica do Brasil LTDA
+
+OUI:9880EE*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:988217*
ID_OUI_FROM_DATABASE=Disruptive Ltd
OUI:989C57*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:989D5D*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:989E63*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:98C845*
ID_OUI_FROM_DATABASE=PacketAccess
+OUI:98C8B8*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
OUI:98CA33*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:98DCD9*
ID_OUI_FROM_DATABASE=UNITEC Co., Ltd.
+OUI:98DD5B*
+ ID_OUI_FROM_DATABASE=TAKUMI JAPAN LTD
+
OUI:98DDEA*
ID_OUI_FROM_DATABASE=Infinix mobility limited
ID_OUI_FROM_DATABASE=Cosesy ApS
OUI:98ED5C*
- ID_OUI_FROM_DATABASE=Tesla Motors, Inc
+ ID_OUI_FROM_DATABASE=Tesla,Inc.
OUI:98EECB*
ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation
OUI:98FA9B*
ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd
+OUI:98FAA7*
+ ID_OUI_FROM_DATABASE=INNONET
+
OUI:98FAE3*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
OUI:9C2EA1*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+OUI:9C2F4E*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:9C2F73*
ID_OUI_FROM_DATABASE=Universal Tiancheng Technology (Beijing) Co., Ltd.
ID_OUI_FROM_DATABASE=HK ELEPHONE Communication Tech Co.,Limited
OUI:9C431EE*
- ID_OUI_FROM_DATABASE=Midas Technology DBA Phoenix Audio Technologies
+ ID_OUI_FROM_DATABASE=Midas Technology, Inc. dba Stem Audio / Phoenix Au
OUI:9C443D*
ID_OUI_FROM_DATABASE=CHENGDU XUGUANG TECHNOLOGY CO, LTD
ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
OUI:9C5D12*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:9C5D95*
ID_OUI_FROM_DATABASE=VTC Electronics Corp.
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
OUI:9C611D*
- ID_OUI_FROM_DATABASE=Omni-ID USA, Inc.
+ ID_OUI_FROM_DATABASE=Panasonic Corporation of North America
OUI:9C6121*
ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD
OUI:9CBB98*
ID_OUI_FROM_DATABASE=Shen Zhen RND Electronic Co.,LTD
+OUI:9CBD6E*
+ ID_OUI_FROM_DATABASE=DERA Co., Ltd
+
OUI:9CBD9D*
ID_OUI_FROM_DATABASE=SkyDisk, Inc.
OUI:9CC950*
ID_OUI_FROM_DATABASE=Baumer Holding
+OUI:9CC9EB*
+ ID_OUI_FROM_DATABASE=NETGEAR
+
OUI:9CCAD9*
ID_OUI_FROM_DATABASE=Nokia Corporation
OUI:9CEBE8*
ID_OUI_FROM_DATABASE=BizLink (Kunshan) Co.,Ltd
+OUI:9CEDFA*
+ ID_OUI_FROM_DATABASE=EVUlution AG
+
OUI:9CEFD5*
ID_OUI_FROM_DATABASE=Panda Wireless, Inc.
+OUI:9CF029*
+ ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+
OUI:9CF387*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:A0239F*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:A027B6*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:A028330*
ID_OUI_FROM_DATABASE=GERSYS GmbH
OUI:A04246*
ID_OUI_FROM_DATABASE=IT Telecom Co., Ltd.
+OUI:A043B0*
+ ID_OUI_FROM_DATABASE=Hangzhou BroadLink Technology Co.,Ltd
+
OUI:A043DB*
ID_OUI_FROM_DATABASE=Sitael S.p.A.
OUI:A04EA7*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:A04F85*
+ ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+
OUI:A04FD4*
ID_OUI_FROM_DATABASE=ADB Broadband Italia
OUI:A0DE05*
ID_OUI_FROM_DATABASE=JSC Irbis-T
+OUI:A0DE0F*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:A0DF15*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:A0FE91*
ID_OUI_FROM_DATABASE=AVAT Automation GmbH
+OUI:A0FF70*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
OUI:A400E2*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:A407B6*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:A40801*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:A408EA*
ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
OUI:A41588*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:A416E7*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:A41731*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
OUI:A42983*
ID_OUI_FROM_DATABASE=Boeing Defence Australia
+OUI:A42985*
+ ID_OUI_FROM_DATABASE=Sichuan AI-Link Technology Co., Ltd.
+
OUI:A429B7*
ID_OUI_FROM_DATABASE=bluesky
OUI:A44F29F*
ID_OUI_FROM_DATABASE=Private
+OUI:A45006*
+ ID_OUI_FROM_DATABASE=SHENZHEN HUACHUANG SHIDAI TECHNOLOGYCO.,LTD
+
OUI:A45046*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
OUI:A4B197*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:A4B1C1*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:A4B1E9*
ID_OUI_FROM_DATABASE=Technicolor
OUI:A4C494*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:A4C54E*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:A4C64F*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:A4CF12*
ID_OUI_FROM_DATABASE=Espressif Inc.
+OUI:A4CFD2*
+ ID_OUI_FROM_DATABASE=Ubee Interactive Co., Limited
+
OUI:A4D094*
ID_OUI_FROM_DATABASE=Erwin Peters Systemtechnik GmbH
ID_OUI_FROM_DATABASE=Malldon Technology Limited
OUI:A4DA22A*
- ID_OUI_FROM_DATABASE=Abetechs GmbH
+ ID_OUI_FROM_DATABASE=Grundig
OUI:A4DA22B*
ID_OUI_FROM_DATABASE=Klashwerks Inc.
OUI:A80180*
ID_OUI_FROM_DATABASE=IMAGO Technologies GmbH
+OUI:A80577*
+ ID_OUI_FROM_DATABASE=Netlist, Inc.
+
OUI:A80600*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:A84041*
ID_OUI_FROM_DATABASE=Dragino Technology Co., Limited
+OUI:A84122*
+ ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co.,Ltd.
+
OUI:A84481*
ID_OUI_FROM_DATABASE=Nokia Corporation
OUI:A86A6F*
ID_OUI_FROM_DATABASE=RIM
+OUI:A86ABB*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:A86AC1*
ID_OUI_FROM_DATABASE=HanbitEDS Co., Ltd.
OUI:A8A089*
ID_OUI_FROM_DATABASE=Tactical Communications
+OUI:A8A097*
+ ID_OUI_FROM_DATABASE=ScioTeq bvba
+
OUI:A8A159*
ID_OUI_FROM_DATABASE=ASRock Incorporation
OUI:AC11D3*
ID_OUI_FROM_DATABASE=Suzhou HOTEK Video Technology Co. Ltd
+OUI:AC1203*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:AC1461*
ID_OUI_FROM_DATABASE=ATAW Co., Ltd.
OUI:AC1E92*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,LTD
+OUI:AC1ED0*
+ ID_OUI_FROM_DATABASE=Temic Automotive Philippines Inc.
+
OUI:AC1F6B*
ID_OUI_FROM_DATABASE=Super Micro Computer, Inc.
OUI:AC3613*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:AC3651*
+ ID_OUI_FROM_DATABASE=Jiangsu Hengtong Terahertz Technology Co., Ltd.
+
OUI:AC3743*
ID_OUI_FROM_DATABASE=HTC Corporation
OUI:AC3870*
ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd.
+OUI:AC3A67*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:AC3A7A*
ID_OUI_FROM_DATABASE=Roku, Inc.
OUI:AC3C0B*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:AC3C8E*
+ ID_OUI_FROM_DATABASE=Flextronics Computing(Suzhou)Co.,Ltd.
+
OUI:AC3CB4*
ID_OUI_FROM_DATABASE=Nilan A/S
OUI:AC482D*
ID_OUI_FROM_DATABASE=Ralinwi Nanjing Electronic Technology Co., Ltd.
+OUI:AC4A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
+OUI:AC4A67*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:AC4AFE*
ID_OUI_FROM_DATABASE=Hisense Broadband Multimedia Technology Co.,Ltd.
+OUI:AC4B1E*
+ ID_OUI_FROM_DATABASE=Integri-Sys.Com LLC
+
OUI:AC4BC8*
ID_OUI_FROM_DATABASE=Juniper Networks
OUI:AC5F3E*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
+OUI:AC6089*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:AC60B6*
ID_OUI_FROM_DATABASE=Ericsson AB
OUI:AC676F*
ID_OUI_FROM_DATABASE=Electrocompaniet A.S.
+OUI:AC67B2*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:AC6B0F*
ID_OUI_FROM_DATABASE=CADENCE DESIGN SYSTEMS INC
OUI:AC7A4D*
ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
+OUI:AC7A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:AC7BA1*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:ACC1EE*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+OUI:ACC25D*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
OUI:ACC2EC*
ID_OUI_FROM_DATABASE=CLT INT'L IND. CORP.
OUI:ACCB09*
ID_OUI_FROM_DATABASE=Hefcom Metering (Pty) Ltd
+OUI:ACCB51*
+ ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+
OUI:ACCC8E*
ID_OUI_FROM_DATABASE=Axis Communications AB
OUI:B0435D*
ID_OUI_FROM_DATABASE=NuLEDs, Inc.
+OUI:B04502*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:B04515*
ID_OUI_FROM_DATABASE=mira fitness,LLC.
OUI:B0935B*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:B09575*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
OUI:B0958E*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
OUI:B0AE25*
ID_OUI_FROM_DATABASE=Varikorea
+OUI:B0B194*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:B0B28F*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
OUI:B0B448*
ID_OUI_FROM_DATABASE=Texas Instruments
+OUI:B0B5C3*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
OUI:B0B5E8*
ID_OUI_FROM_DATABASE=Ruroc LTD
OUI:B0CA68*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:B0CCFE*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:B0CE18*
ID_OUI_FROM_DATABASE=Zhejiang shenghui lighting co.,Ltd
OUI:B0E39D*
ID_OUI_FROM_DATABASE=CAT SYSTEM CO.,LTD.
+OUI:B0E4D5*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
OUI:B0E50E*
ID_OUI_FROM_DATABASE=NRG SYSTEMS INC
OUI:B40832*
ID_OUI_FROM_DATABASE=TC Communications
+OUI:B40931*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:B40AC6*
ID_OUI_FROM_DATABASE=DEXON Systems Ltd.
OUI:B41513*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:B4157E*
+ ID_OUI_FROM_DATABASE=Celona Inc.
+
OUI:B41780*
ID_OUI_FROM_DATABASE=DTI Group Ltd
OUI:B4218A*
ID_OUI_FROM_DATABASE=Dog Hunter LLC
+OUI:B42200*
+ ID_OUI_FROM_DATABASE=Brother Industries, LTD.
+
OUI:B424E7*
ID_OUI_FROM_DATABASE=Codetek Technology Co.,Ltd
OUI:B43E3B*
ID_OUI_FROM_DATABASE=Viableware, Inc
+OUI:B440A4*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:B4417A*
ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
OUI:B46D83*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:B46E08*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:B47356*
ID_OUI_FROM_DATABASE=Hangzhou Treebear Networking Co., Ltd.
OUI:B47F5E*
ID_OUI_FROM_DATABASE=Foresight Manufacture (S) Pte Ltd
+OUI:B48107*
+ ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD
+
OUI:B481BF*
ID_OUI_FROM_DATABASE=Meta-Networks, LLC
OUI:B4B017*
ID_OUI_FROM_DATABASE=Avaya Inc
+OUI:B4B055*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:B4B15A*
ID_OUI_FROM_DATABASE=Siemens AG Energy Management Division
OUI:B4CD27*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:B4CE40*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:B4CEF6*
ID_OUI_FROM_DATABASE=HTC Corporation
OUI:B4EF04*
ID_OUI_FROM_DATABASE=DAIHAN Scientific Co., Ltd.
+OUI:B4EF1C*
+ ID_OUI_FROM_DATABASE=360 AI Technology Co.Ltd
+
OUI:B4EF39*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:B4F0AB*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:B4F18C*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:B4F1DA*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
OUI:B80018*
ID_OUI_FROM_DATABASE=Htel
+OUI:B802A4*
+ ID_OUI_FROM_DATABASE=Aeonsemi, Inc.
+
OUI:B80305*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:B82CA0*
ID_OUI_FROM_DATABASE=Resideo
+OUI:B82FCB*
+ ID_OUI_FROM_DATABASE=CMS Electracom
+
OUI:B830A8*
ID_OUI_FROM_DATABASE=Road-Track Telematics Development
OUI:B8634D*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:B86392*
+ ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+
OUI:B863BC*
ID_OUI_FROM_DATABASE=ROBOTIS, Co, Ltd
ID_OUI_FROM_DATABASE=NXP (China) Management Ltd.
OUI:B87CF2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:B88198*
ID_OUI_FROM_DATABASE=Intel Corporate
OUI:B88E3A*
ID_OUI_FROM_DATABASE=Infinite Technologies JLT
+OUI:B88E82*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:B88EC6*
ID_OUI_FROM_DATABASE=Stateless Networks
OUI:B88FB4*
ID_OUI_FROM_DATABASE=JABIL CIRCUIT ITALIA S.R.L
+OUI:B89047*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:B891C9*
ID_OUI_FROM_DATABASE=Handreamnet
OUI:B8C68E*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:B8C6AA*
+ ID_OUI_FROM_DATABASE=Earda Technologies co Ltd
+
OUI:B8C716*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
OUI:B8CDA7*
ID_OUI_FROM_DATABASE=Maxeler Technologies Ltd.
+OUI:B8CEF6*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
OUI:B8D06F*
ID_OUI_FROM_DATABASE=GUANGZHOU HKUST FOK YING TUNG RESEARCH INSTITUTE
OUI:B8D49D*
ID_OUI_FROM_DATABASE=M Seven System Ltd.
+OUI:B8D4E7*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
OUI:B8D50B*
ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd
OUI:B8DF6B*
ID_OUI_FROM_DATABASE=SpotCam Co., Ltd.
+OUI:B8E3B1*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
+OUI:B8E3EE*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
OUI:B8E589*
ID_OUI_FROM_DATABASE=Payter BV
OUI:B8EF8B*
ID_OUI_FROM_DATABASE=SHENZHEN CANNICE TECHNOLOGY CO.,LTD
+OUI:B8F009*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:B8F080*
ID_OUI_FROM_DATABASE=SPS, INC.
OUI:BC0543*
ID_OUI_FROM_DATABASE=AVM GmbH
+OUI:BC0963*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:BC0DA5*
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:BC0F64*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:BC0F9A*
+ ID_OUI_FROM_DATABASE=D-Link International
+
OUI:BC0FA7*
ID_OUI_FROM_DATABASE=Ouster
OUI:BC1665*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:BC1695*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:BC16F5*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:BC2D98*
ID_OUI_FROM_DATABASE=ThinGlobal LLC
+OUI:BC2DEF*
+ ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd.
+
OUI:BC2E48*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:BC325F*
ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
+OUI:BC33AC*
+ ID_OUI_FROM_DATABASE=Silicon Laboratories
+
OUI:BC34000*
ID_OUI_FROM_DATABASE=Redvision CCTV
OUI:BC4101*
ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
+OUI:BC428C*
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
+
OUI:BC4377*
ID_OUI_FROM_DATABASE=Hang Zhou Huite Technology Co.,ltd.
OUI:BC4760*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:BC4A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:BC4B79*
ID_OUI_FROM_DATABASE=SensingTek
OUI:BC52B7*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:BC542F*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:BC5436*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:BC54FC*
ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.
+OUI:BC5A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:BC5C4C*
ID_OUI_FROM_DATABASE=ELECOM CO.,LTD.
OUI:BC79AD*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:BC7ABF*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:BC7DD1*
ID_OUI_FROM_DATABASE=Radio Data Comms
ID_OUI_FROM_DATABASE=devolo AG
OUI:BCF310*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:BCF5AC*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
OUI:BCFED9*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:BCFF21*
+ ID_OUI_FROM_DATABASE=Smart Code(shenzhen)Technology Co.,Ltd
+
OUI:BCFFAC*
ID_OUI_FROM_DATABASE=TOPCON CORPORATION
OUI:C014B8*
ID_OUI_FROM_DATABASE=Nokia
+OUI:C01692*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
OUI:C0174D*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:C09134*
ID_OUI_FROM_DATABASE=ProCurve Networking by HP
+OUI:C095DA*
+ ID_OUI_FROM_DATABASE=NXP India Private Limited
+
OUI:C09727*
ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
OUI:C09AD0*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:C09BF40*
+ ID_OUI_FROM_DATABASE=Annapurna labs
+
+OUI:C09BF41*
+ ID_OUI_FROM_DATABASE=Connected Space Management
+
+OUI:C09BF42*
+ ID_OUI_FROM_DATABASE=Hitachi High-Tech Materials Corporation
+
+OUI:C09BF43*
+ ID_OUI_FROM_DATABASE=Osprey Video, Inc
+
+OUI:C09BF44*
+ ID_OUI_FROM_DATABASE=JSC NPK ATRONIK
+
+OUI:C09BF45*
+ ID_OUI_FROM_DATABASE=Infiot Inc.
+
+OUI:C09BF46*
+ ID_OUI_FROM_DATABASE=LTD Delovoy Office
+
+OUI:C09BF47*
+ ID_OUI_FROM_DATABASE=Big Dutchman International GmbH
+
+OUI:C09BF48*
+ ID_OUI_FROM_DATABASE=SHENZHEN WINS ELECTRONIC TECHNOLOGY CO., LTD
+
+OUI:C09BF49*
+ ID_OUI_FROM_DATABASE=Alcatraz AI Inc.
+
+OUI:C09BF4A*
+ ID_OUI_FROM_DATABASE=Inveo
+
+OUI:C09BF4B*
+ ID_OUI_FROM_DATABASE=NUCTECH COMPANY LIMITED
+
+OUI:C09BF4C*
+ ID_OUI_FROM_DATABASE=Pinpark Inc.
+
+OUI:C09BF4D*
+ ID_OUI_FROM_DATABASE=The Professional Monitor Company Ltd
+
+OUI:C09BF4E*
+ ID_OUI_FROM_DATABASE=Continental Automotive Component Malaysia Sdn.Bhd.
+
OUI:C09C04*
ID_OUI_FROM_DATABASE=Shaanxi GuoLian Digital TV Technology Co.,Ltd.
OUI:C0A600*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:C0A66D*
+ ID_OUI_FROM_DATABASE=Inspur Group Co., Ltd.
+
OUI:C0A8F0*
ID_OUI_FROM_DATABASE=Adamson Systems Engineering
OUI:C0AC54*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:C0B101*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:C0B339*
ID_OUI_FROM_DATABASE=Comigo Ltd.
ID_OUI_FROM_DATABASE=D-Link International
OUI:C413E2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:C4143C*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:C43018*
ID_OUI_FROM_DATABASE=MCS Logic Inc.
+OUI:C432D1*
+ ID_OUI_FROM_DATABASE=Farlink Technology Limited
+
OUI:C43306*
ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
OUI:C44202*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:C44268*
+ ID_OUI_FROM_DATABASE=CRESTRON ELECTRONICS, INC.
+
OUI:C4438F*
ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
OUI:C488E5*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:C489ED*
+ ID_OUI_FROM_DATABASE=Solid Optics EU N.V.
+
OUI:C48A5A*
ID_OUI_FROM_DATABASE=JFCONTROL
OUI:C4A366*
ID_OUI_FROM_DATABASE=zte corporation
+OUI:C4A402*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:C4A81D*
ID_OUI_FROM_DATABASE=D-Link International
ID_OUI_FROM_DATABASE=Spica international
OUI:C4F57C*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:C4F5A5*
ID_OUI_FROM_DATABASE=Kumalift Co., Ltd.
OUI:C80718*
ID_OUI_FROM_DATABASE=TDSi
+OUI:C80739*
+ ID_OUI_FROM_DATABASE=NAKAYO Inc
+
OUI:C80873*
ID_OUI_FROM_DATABASE=Ruckus Wireless
OUI:C85663*
ID_OUI_FROM_DATABASE=Sunflex Europe GmbH
+OUI:C858C0*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:C85A9F*
ID_OUI_FROM_DATABASE=zte corporation
ID_OUI_FROM_DATABASE=Beijing Haitai Fangyuan High Technology Co,.Ltd.
OUI:C8665D*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:C8675E*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:C869CD*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:C86F1D*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:C87125*
+ ID_OUI_FROM_DATABASE=Johnson Outdoors Marine Electronics d/b/a Minnkota
+
OUI:C87248*
ID_OUI_FROM_DATABASE=Aplicom Oy
OUI:C87E75*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:C88314*
+ ID_OUI_FROM_DATABASE=Tempo Communications
+
OUI:C88439*
ID_OUI_FROM_DATABASE=Sunrise Technologies
OUI:C88B47*
ID_OUI_FROM_DATABASE=Nolangroup S.P.A con Socio Unico
+OUI:C88BE8*
+ ID_OUI_FROM_DATABASE=Masimo Corporation
+
OUI:C88D83*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:C8BCC8*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:C8BCE5*
+ ID_OUI_FROM_DATABASE=Sense Things Japan INC.
+
OUI:C8BE19*
ID_OUI_FROM_DATABASE=D-Link International
OUI:C8D719*
ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC
+OUI:C8D778*
+ ID_OUI_FROM_DATABASE=BSH Hausgeraete GmbH
+
OUI:C8D779*
ID_OUI_FROM_DATABASE=QING DAO HAIER TELECOM CO.,LTD.
OUI:CC0DEC*
ID_OUI_FROM_DATABASE=Cisco SPVTG
+OUI:CC0DF2*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
OUI:CC10A3*
ID_OUI_FROM_DATABASE=Beijing Nan Bao Technology Co., Ltd.
OUI:CC40D0*
ID_OUI_FROM_DATABASE=NETGEAR
+OUI:CC418E*
+ ID_OUI_FROM_DATABASE=MSA Innovation
+
OUI:CC43E3*
ID_OUI_FROM_DATABASE=Trump s.a.
ID_OUI_FROM_DATABASE=Carnegie Technologies
OUI:CC4E24*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:CC4EEC*
ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
OUI:CC7EE7*
ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company
+OUI:CC7F75*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
+OUI:CC7F76*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
OUI:CC81DA*
ID_OUI_FROM_DATABASE=Phicomm (Shanghai) Co., Ltd.
OUI:CCA614*
ID_OUI_FROM_DATABASE=AIFA TECHNOLOGY CORP.
+OUI:CCA7C1*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
+OUI:CCAB2C*
+ ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
+
OUI:CCAF78*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
OUI:CCD3E2*
ID_OUI_FROM_DATABASE=Jiangsu Yinhe Electronics Co.,Ltd.
+OUI:CCD42E*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
OUI:CCD4A1*
ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
OUI:CCF957*
ID_OUI_FROM_DATABASE=u-blox AG
+OUI:CCF9E4*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:CCF9E8*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:D003DF*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:D003EB*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:D00401*
ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
OUI:D03E5C*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:D03FAA*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:D041C9*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
OUI:D063B4*
ID_OUI_FROM_DATABASE=SolidRun Ltd.
+OUI:D06544*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:D065CA*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:D0B33F*
ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
+OUI:D0B45D*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:D0B498*
ID_OUI_FROM_DATABASE=Robert Bosch LLC Automotive Electronics
OUI:D0D2B0*
ID_OUI_FROM_DATABASE=Apple, Inc.
+OUI:D0D3E0*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
OUI:D0D3FC*
ID_OUI_FROM_DATABASE=Mios, Ltd.
OUI:D41A3F*
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+OUI:D41AC8*
+ ID_OUI_FROM_DATABASE=Nippon Printer Engineering
+
OUI:D41C1C*
ID_OUI_FROM_DATABASE=RCF S.P.A.
OUI:D4224E*
ID_OUI_FROM_DATABASE=Alcatel Lucent
+OUI:D422CD*
+ ID_OUI_FROM_DATABASE=Xsens Technologies B.V.
+
OUI:D42493*
ID_OUI_FROM_DATABASE=GW Technologies Co.,Ltd
OUI:D45297*
ID_OUI_FROM_DATABASE=nSTREAMS Technologies, Inc.
+OUI:D452EE*
+ ID_OUI_FROM_DATABASE=BSkyB Ltd
+
OUI:D45383*
ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
OUI:D45DDF*
ID_OUI_FROM_DATABASE=PEGATRON CORPORATION
+OUI:D45EEC*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Electronics Co., Ltd.
+
OUI:D45F25*
ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
OUI:D48890*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:D48A39*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:D48CB5*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:D4BBC8*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+OUI:D4BBE6*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:D4BD1E*
ID_OUI_FROM_DATABASE=5VT Technologies,Taiwan LTd.
ID_OUI_FROM_DATABASE=Symbolic IO
OUI:D4CFF9*
- ID_OUI_FROM_DATABASE=Shenzhen Sen5 Technology Co., Ltd.
+ ID_OUI_FROM_DATABASE=Shenzhen SEI Robotics Co.,Ltd
OUI:D4D184*
ID_OUI_FROM_DATABASE=ADB Broadband Italia
OUI:D4D252*
ID_OUI_FROM_DATABASE=Intel Corporate
+OUI:D4D2D6*
+ ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED
+
OUI:D4D2E5*
ID_OUI_FROM_DATABASE=BKAV Corporation
OUI:D4D919*
ID_OUI_FROM_DATABASE=GoPro
+OUI:D4DACD*
+ ID_OUI_FROM_DATABASE=BSkyB Ltd
+
+OUI:D4DC09*
+ ID_OUI_FROM_DATABASE=Mist Systems, Inc.
+
OUI:D4DCCD*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:D806D1*
ID_OUI_FROM_DATABASE=Honeywell Fire System (Shanghai) Co,. Ltd.
+OUI:D807B6*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
OUI:D80831*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
ID_OUI_FROM_DATABASE=B&W Group Ltd
OUI:D81FCC*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
OUI:D8209F*
ID_OUI_FROM_DATABASE=Cubro Acronet GesmbH
OUI:D84710*
ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd.
+OUI:D84732*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
OUI:D848EE*
ID_OUI_FROM_DATABASE=Hangzhou Xueji Technology Co., Ltd.
OUI:D84B2A*
ID_OUI_FROM_DATABASE=Cognitas Technologies, Inc.
+OUI:D84C90*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
+OUI:D84DB9*
+ ID_OUI_FROM_DATABASE=Wu Qi Technologies,Inc.
+
OUI:D84FB8*
ID_OUI_FROM_DATABASE=LG ELECTRONICS
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:D854A2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:D85575*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:D85DFB*
ID_OUI_FROM_DATABASE=Private
+OUI:D85F77*
+ ID_OUI_FROM_DATABASE=Telink Semiconductor (Shanghai) Co., Ltd.
+
OUI:D860B0*
ID_OUI_FROM_DATABASE=bioMérieux Italia S.p.A.
OUI:D8760A*
ID_OUI_FROM_DATABASE=Escort, Inc.
+OUI:D8787F*
+ ID_OUI_FROM_DATABASE=Ubee Interactive Co., Limited
+
OUI:D878E5*
ID_OUI_FROM_DATABASE=KUHN SA
OUI:D8912A*
ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
+OUI:D89136*
+ ID_OUI_FROM_DATABASE=Dover Fueling Solutions
+
OUI:D89341*
ID_OUI_FROM_DATABASE=General Electric Global Research
OUI:D8A756*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:D8A8C8*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:D8A98B*
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:D8C4E9*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:D8C561*
+ ID_OUI_FROM_DATABASE=CommFront Communications Pte Ltd
+
OUI:D8C691*
ID_OUI_FROM_DATABASE=Hichan Technology Corp.
OUI:DC330D*
ID_OUI_FROM_DATABASE=QING DAO HAIER TELECOM CO.,LTD.
+OUI:DC333D*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:DC3350*
ID_OUI_FROM_DATABASE=TechSAT GmbH
OUI:DC35F1*
- ID_OUI_FROM_DATABASE=Positivo Informática SA.
+ ID_OUI_FROM_DATABASE=Positivo Tecnologia S.A.
OUI:DC3714*
ID_OUI_FROM_DATABASE=Apple, Inc.
ID_OUI_FROM_DATABASE=Suritel
OUI:DC44271*
- ID_OUI_FROM_DATABASE=Tesla Motors, Inc
+ ID_OUI_FROM_DATABASE=Tesla,Inc.
OUI:DC44272*
ID_OUI_FROM_DATABASE=Skywave Technology Co,.Ltd.
OUI:DC6AEA*
ID_OUI_FROM_DATABASE=Infinix mobility limited
+OUI:DC6B12*
+ ID_OUI_FROM_DATABASE=worldcns inc.
+
OUI:DC6DCD*
ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
OUI:DC962C*
ID_OUI_FROM_DATABASE=NST Audio Ltd
+OUI:DC9840*
+ ID_OUI_FROM_DATABASE=Microsoft Corporation
+
OUI:DC9914*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
ID_OUI_FROM_DATABASE=Allied Telesis, Inc.
OUI:E01C41*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:E01CEE*
ID_OUI_FROM_DATABASE=Bravo Tech, Inc.
OUI:E02202*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:E023FF*
+ ID_OUI_FROM_DATABASE=Fortinet, Inc.
+
OUI:E0247F*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:E0D31A*
ID_OUI_FROM_DATABASE=EQUES Technology Co., Limited
+OUI:E0D462*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
+OUI:E0D4E8*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:E0D55E*
ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD.
OUI:E0DDC0*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+OUI:E0E0FC*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:E0E5CF*
ID_OUI_FROM_DATABASE=Texas Instruments
OUI:E0F5CA*
ID_OUI_FROM_DATABASE=CHENG UEI PRECISION INDUSTRY CO.,LTD.
+OUI:E0F6B5*
+ ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
+
OUI:E0F847*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:E425E9*
ID_OUI_FROM_DATABASE=Color-Chip
+OUI:E42686*
+ ID_OUI_FROM_DATABASE=DWnet Technologies(Suzhou) Corporation
+
OUI:E42771*
ID_OUI_FROM_DATABASE=Smartlabs
OUI:E438F2*
ID_OUI_FROM_DATABASE=Advantage Controls
+OUI:E43A65*
+ ID_OUI_FROM_DATABASE=MofiNetwork Inc
+
OUI:E43A6E*
ID_OUI_FROM_DATABASE=Shenzhen Zeroone Technology CO.,LTD
OUI:E45D75*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:E45E37*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
OUI:E46059*
ID_OUI_FROM_DATABASE=Pingtek Co., Ltd.
OUI:E482CC*
ID_OUI_FROM_DATABASE=Jumptronic GmbH
+OUI:E48326*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:E48399*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
OUI:E4907E*
ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+OUI:E490FD*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:E4922A*
ID_OUI_FROM_DATABASE=DBG HOLDINGS LIMITED
OUI:E4E0C5*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:E4E112*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
OUI:E4E130*
ID_OUI_FROM_DATABASE=TCT mobile ltd
OUI:E4F365*
ID_OUI_FROM_DATABASE=Time-O-Matic, Inc.
+OUI:E4F3C4*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
OUI:E4F3E3*
ID_OUI_FROM_DATABASE=Shanghai iComhome Co.,Ltd.
OUI:E8986D*
ID_OUI_FROM_DATABASE=Palo Alto Networks
+OUI:E898C2*
+ ID_OUI_FROM_DATABASE=ZETLAB Company
+
OUI:E8995A*
ID_OUI_FROM_DATABASE=PiiGAB, Processinformation i Goteborg AB
OUI:E8B2FE*
ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
+OUI:E8B4700*
+ ID_OUI_FROM_DATABASE=DongGuan Ramaxel Memory Technology
+
+OUI:E8B4701*
+ ID_OUI_FROM_DATABASE=Autocom Diagnostic Partner AB
+
+OUI:E8B4702*
+ ID_OUI_FROM_DATABASE=internet domain name system beijing engineering research center ltd
+
+OUI:E8B4703*
+ ID_OUI_FROM_DATABASE=Webfleet Solutions B.V.
+
+OUI:E8B4704*
+ ID_OUI_FROM_DATABASE=YAWATA ELECTRIC INDUSTRIAL CO.,LTD.
+
+OUI:E8B4705*
+ ID_OUI_FROM_DATABASE=Alperia Fiber srl
+
+OUI:E8B4706*
+ ID_OUI_FROM_DATABASE=Elcoma
+
+OUI:E8B4707*
+ ID_OUI_FROM_DATABASE=Tibit Communications
+
+OUI:E8B4708*
+ ID_OUI_FROM_DATABASE=DEHN SE + Co KG
+
+OUI:E8B4709*
+ ID_OUI_FROM_DATABASE=Miltek Industries Pte Ltd
+
+OUI:E8B470A*
+ ID_OUI_FROM_DATABASE=plc2 Design GmbH
+
+OUI:E8B470B*
+ ID_OUI_FROM_DATABASE=Digifocus Technology Inc.
+
+OUI:E8B470C*
+ ID_OUI_FROM_DATABASE=Anduril Industries
+
+OUI:E8B470D*
+ ID_OUI_FROM_DATABASE=Medica Corporation
+
+OUI:E8B470E*
+ ID_OUI_FROM_DATABASE=UNICACCES GROUPE
+
OUI:E8B4AE*
ID_OUI_FROM_DATABASE=Shenzhen C&D Electronics Co.,Ltd
OUI:E8E8B7*
ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
+OUI:E8E98E*
+ ID_OUI_FROM_DATABASE=SOLAR controls s.r.o.
+
OUI:E8EA6A*
ID_OUI_FROM_DATABASE=StarTech.com
OUI:EC3091*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:EC316D*
+ ID_OUI_FROM_DATABASE=Hansgrohe
+
OUI:EC3586*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:EC63E5*
ID_OUI_FROM_DATABASE=ePBoard Design LLC
+OUI:EC63ED*
+ ID_OUI_FROM_DATABASE=Hyundai Autoever Corp.
+
OUI:EC64E7*
ID_OUI_FROM_DATABASE=MOCACARE Corporation
OUI:EC6C9F*
ID_OUI_FROM_DATABASE=Chengdu Volans Technology CO.,LTD
+OUI:EC6CB5*
+ ID_OUI_FROM_DATABASE=zte corporation
+
OUI:EC6F0B*
ID_OUI_FROM_DATABASE=FADU, Inc.
OUI:EC74BA*
ID_OUI_FROM_DATABASE=Hirschmann Automation and Control GmbH
+OUI:EC7949*
+ ID_OUI_FROM_DATABASE=FUJITSU LIMITED
+
OUI:EC79F2*
ID_OUI_FROM_DATABASE=Startel
OUI:EC9681*
ID_OUI_FROM_DATABASE=2276427 Ontario Inc
+OUI:EC97B2*
+ ID_OUI_FROM_DATABASE=SUMEC Machinery & Electric Co.,Ltd.
+
OUI:EC986C*
ID_OUI_FROM_DATABASE=Lufft Mess- und Regeltechnik GmbH
OUI:F00786*
ID_OUI_FROM_DATABASE=Shandong Bittel Electronics Co., Ltd
+OUI:F008D1*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
OUI:F008F1*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
OUI:F00FEC*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+OUI:F01090*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
OUI:F010AB*
ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd.
OUI:F0321A*
ID_OUI_FROM_DATABASE=Mita-Teknik A/S
+OUI:F033E5*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:F03404*
ID_OUI_FROM_DATABASE=TCT mobile ltd
OUI:F0407B*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+OUI:F041C6*
+ ID_OUI_FROM_DATABASE=Heat Tech Company, Ltd.
+
OUI:F041C80*
ID_OUI_FROM_DATABASE=LINPA ACOUSTIC TECHNOLOGY CO.,LTD
OUI:F07BCB*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
+OUI:F07CC7*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
OUI:F07D68*
ID_OUI_FROM_DATABASE=D-Link Corporation
OUI:F08173*
ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+OUI:F08175*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
OUI:F081AF*
ID_OUI_FROM_DATABASE=IRZ AUTOMATION TECHNOLOGIES LTD
ID_OUI_FROM_DATABASE=Guangzhou Blue Cheetah Intelligent Technology Co., Ltd.
OUI:F09CE9*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:F09E63*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:F0B014*
ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH
+OUI:F0B022*
+ ID_OUI_FROM_DATABASE=TOHO Electronics INC.
+
OUI:F0B052*
ID_OUI_FROM_DATABASE=Ruckus Wireless
OUI:F0F669*
ID_OUI_FROM_DATABASE=Motion Analysis Corporation
+OUI:F0F6C1*
+ ID_OUI_FROM_DATABASE=Sonos, Inc.
+
OUI:F0F755*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:F40321*
ID_OUI_FROM_DATABASE=BeNeXt B.V.
+OUI:F4032A*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
OUI:F4032F*
ID_OUI_FROM_DATABASE=Reduxio Systems
OUI:F417B8*
ID_OUI_FROM_DATABASE=AirTies Wireless Networks
+OUI:F419E2*
+ ID_OUI_FROM_DATABASE=Volterra
+
OUI:F41BA1*
ID_OUI_FROM_DATABASE=Apple, Inc.
OUI:F42E7F*
ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+OUI:F4308B*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
OUI:F430B9*
ID_OUI_FROM_DATABASE=Hewlett Packard
OUI:F45214*
ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+OUI:F45420*
+ ID_OUI_FROM_DATABASE=TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO
+
OUI:F45433*
ID_OUI_FROM_DATABASE=Rockwell Automation
OUI:F47190*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:F47335*
+ ID_OUI_FROM_DATABASE=Logitech Far East
+
OUI:F473CA*
ID_OUI_FROM_DATABASE=Conversion Sound Inc.
OUI:F48771*
ID_OUI_FROM_DATABASE=Infoblox
+OUI:F487C5*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:F48B32*
ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
OUI:F490CA*
ID_OUI_FROM_DATABASE=Tensorcom
+OUI:F490CB0*
+ ID_OUI_FROM_DATABASE=Epitel, Inc.
+
+OUI:F490CB1*
+ ID_OUI_FROM_DATABASE=DELEM BV
+
+OUI:F490CB2*
+ ID_OUI_FROM_DATABASE=ICE Gateway GmbH
+
+OUI:F490CB3*
+ ID_OUI_FROM_DATABASE=Ricker Lyman Robotic
+
+OUI:F490CB4*
+ ID_OUI_FROM_DATABASE=OmniNet
+
+OUI:F490CB5*
+ ID_OUI_FROM_DATABASE=Avilution
+
+OUI:F490CB6*
+ ID_OUI_FROM_DATABASE=Airbeam Wireless Technologies Inc.
+
+OUI:F490CB7*
+ ID_OUI_FROM_DATABASE=TEQ SA
+
+OUI:F490CB8*
+ ID_OUI_FROM_DATABASE=Beijing Penslink Co., Ltd.
+
+OUI:F490CB9*
+ ID_OUI_FROM_DATABASE=Fractyl Labs
+
+OUI:F490CBA*
+ ID_OUI_FROM_DATABASE=Private
+
+OUI:F490CBB*
+ ID_OUI_FROM_DATABASE=A-dec Inc.
+
+OUI:F490CBC*
+ ID_OUI_FROM_DATABASE=Cheetah Medical
+
+OUI:F490CBD*
+ ID_OUI_FROM_DATABASE=Simavita (Aust) Pty Ltd
+
+OUI:F490CBE*
+ ID_OUI_FROM_DATABASE=RSAE Labs Inc
+
OUI:F490EA*
ID_OUI_FROM_DATABASE=Deciso B.V.
OUI:F4911E*
ID_OUI_FROM_DATABASE=ZHUHAI EWPE INFORMATION TECHNOLOGY INC
+OUI:F492BF*
+ ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc.
+
OUI:F4939F*
ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co., Ltd.
OUI:F4A294*
ID_OUI_FROM_DATABASE=EAGLE WORLD DEVELOPMENT CO., LIMITED
+OUI:F4A4D6*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:F4A52A*
ID_OUI_FROM_DATABASE=Hawa Technologies Inc
OUI:F4B72A*
ID_OUI_FROM_DATABASE=TIME INTERCONNECT LTD
+OUI:F4B78D*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
OUI:F4B7B3*
ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
OUI:F4D9FB*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:F4DBE3*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
OUI:F4DBE6*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:F4EAB5*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
OUI:F4EB38*
ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+OUI:F4EB9F*
+ ID_OUI_FROM_DATABASE=Ellu Company 2019 SL
+
OUI:F4EC38*
ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
OUI:F84F57*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+OUI:F84FAD*
+ ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD
+
OUI:F8501C*
ID_OUI_FROM_DATABASE=Tianjin Geneuo Technology Co.,Ltd
OUI:F8AE27*
ID_OUI_FROM_DATABASE=John Deere Electronic Solutions
+OUI:F8AF05*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
OUI:F8AFDB*
ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
OUI:FC372B*
ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD
+OUI:FC3964*
+ ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED
+
OUI:FC3CE9*
ID_OUI_FROM_DATABASE=Tsingtong Technologies Co, Ltd.
OUI:FC3D93*
ID_OUI_FROM_DATABASE=LONGCHEER TELECOMMUNICATION LIMITED
+OUI:FC3DA5*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
OUI:FC3F7C*
ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
OUI:FC6FB7*
ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
+OUI:FC71FA*
+ ID_OUI_FROM_DATABASE=Trane Technologies
+
OUI:FC7516*
ID_OUI_FROM_DATABASE=D-Link International
OUI:FC94E3*
ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+OUI:FC956A*
+ ID_OUI_FROM_DATABASE=OCTAGON SYSTEMS CORP.
+
OUI:FC9947*
ID_OUI_FROM_DATABASE=Cisco Systems, Inc
OUI:FCDE90*
ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+OUI:FCE14F*
+ ID_OUI_FROM_DATABASE=BRK Brands, Inc.
+
OUI:FCE186*
ID_OUI_FROM_DATABASE=A3M Co., LTD
acpi:PEGA*:
ID_VENDOR_FROM_DATABASE=Pegatron Corporation
+acpi:PHYT*:
+ ID_VENDOR_FROM_DATABASE=Phytium Technology Co. Ltd.
+
acpi:QCOM*:
ID_VENDOR_FROM_DATABASE=Qualcomm Inc
---- 20-acpi-vendor.hwdb.base 2019-11-29 14:29:51.816965218 +0100
-+++ 20-acpi-vendor.hwdb 2019-11-29 14:29:51.821964280 +0100
+--- 20-acpi-vendor.hwdb.base 2020-03-06 12:40:11.417307950 +0100
++++ 20-acpi-vendor.hwdb 2020-03-06 12:40:11.433308177 +0100
@@ -3,6 +3,8 @@
# Data imported from:
# https://uefi.org/uefi-pnp-export
acpi:AMDI*:
ID_VENDOR_FROM_DATABASE=AMD
-@@ -283,6 +282,9 @@
+@@ -286,6 +285,9 @@
acpi:AAA*:
ID_VENDOR_FROM_DATABASE=Avolites Ltd
acpi:AAE*:
ID_VENDOR_FROM_DATABASE=Anatek Electronics Inc.
-@@ -310,6 +312,9 @@
+@@ -313,6 +315,9 @@
acpi:ABO*:
ID_VENDOR_FROM_DATABASE=D-Link Systems Inc
acpi:ABS*:
ID_VENDOR_FROM_DATABASE=Abaco Systems, Inc.
-@@ -355,7 +360,7 @@
+@@ -358,7 +363,7 @@
acpi:ACO*:
ID_VENDOR_FROM_DATABASE=Allion Computer Inc.
ID_VENDOR_FROM_DATABASE=Aspen Tech Inc
acpi:ACR*:
-@@ -628,6 +633,9 @@
+@@ -631,6 +636,9 @@
acpi:AMT*:
ID_VENDOR_FROM_DATABASE=AMT International Industry
acpi:AMX*:
ID_VENDOR_FROM_DATABASE=AMX LLC
-@@ -676,6 +684,9 @@
+@@ -679,6 +687,9 @@
acpi:AOA*:
ID_VENDOR_FROM_DATABASE=AOpen Inc.
acpi:AOE*:
ID_VENDOR_FROM_DATABASE=Advanced Optics Electronics, Inc.
-@@ -685,6 +696,9 @@
+@@ -688,6 +699,9 @@
acpi:AOT*:
ID_VENDOR_FROM_DATABASE=Alcatel
acpi:APC*:
ID_VENDOR_FROM_DATABASE=American Power Conversion
-@@ -860,7 +874,7 @@
+@@ -863,7 +877,7 @@
ID_VENDOR_FROM_DATABASE=Alps Electric Inc
acpi:AUO*:
acpi:AUR*:
ID_VENDOR_FROM_DATABASE=Aureal Semiconductor
-@@ -940,6 +954,9 @@
+@@ -943,6 +957,9 @@
acpi:AXE*:
ID_VENDOR_FROM_DATABASE=Axell Corporation
acpi:AXI*:
ID_VENDOR_FROM_DATABASE=American Magnetics
-@@ -1090,6 +1107,9 @@
+@@ -1093,6 +1110,9 @@
acpi:BML*:
ID_VENDOR_FROM_DATABASE=BIOMED Lab
acpi:BMS*:
ID_VENDOR_FROM_DATABASE=BIOMEDISYS
-@@ -1102,6 +1122,9 @@
+@@ -1105,6 +1125,9 @@
acpi:BNO*:
ID_VENDOR_FROM_DATABASE=Bang & Olufsen
acpi:BNS*:
ID_VENDOR_FROM_DATABASE=Boulder Nonlinear Systems
-@@ -1342,6 +1365,9 @@
+@@ -1345,6 +1368,9 @@
acpi:CHA*:
ID_VENDOR_FROM_DATABASE=Chase Research PLC
acpi:CHD*:
ID_VENDOR_FROM_DATABASE=ChangHong Electric Co.,Ltd
-@@ -1495,6 +1521,9 @@
+@@ -1498,6 +1524,9 @@
acpi:COD*:
ID_VENDOR_FROM_DATABASE=CODAN Pty. Ltd.
acpi:COI*:
ID_VENDOR_FROM_DATABASE=Codec Inc.
-@@ -1901,7 +1930,7 @@
+@@ -1904,7 +1933,7 @@
ID_VENDOR_FROM_DATABASE=Dragon Information Technology
acpi:DJE*:
acpi:DJP*:
ID_VENDOR_FROM_DATABASE=Maygay Machines, Ltd
-@@ -2233,6 +2262,9 @@
+@@ -2236,6 +2265,9 @@
acpi:EIN*:
ID_VENDOR_FROM_DATABASE=Elegant Invention
acpi:EKA*:
ID_VENDOR_FROM_DATABASE=MagTek Inc.
-@@ -2494,6 +2526,9 @@
+@@ -2497,6 +2529,9 @@
acpi:FCG*:
ID_VENDOR_FROM_DATABASE=First International Computer Ltd
acpi:FCS*:
ID_VENDOR_FROM_DATABASE=Focus Enhancements, Inc.
-@@ -2867,7 +2902,7 @@
+@@ -2870,7 +2905,7 @@
ID_VENDOR_FROM_DATABASE=General Standards Corporation
acpi:GSM*:
acpi:GSN*:
ID_VENDOR_FROM_DATABASE=Grandstream Networks, Inc.
-@@ -2968,6 +3003,9 @@
+@@ -2971,6 +3006,9 @@
acpi:HEC*:
ID_VENDOR_FROM_DATABASE=Hisense Electric Co., Ltd.
acpi:HEL*:
ID_VENDOR_FROM_DATABASE=Hitachi Micro Systems Europe Ltd
-@@ -3097,6 +3135,9 @@
+@@ -3100,6 +3138,9 @@
acpi:HSD*:
ID_VENDOR_FROM_DATABASE=HannStar Display Corp
acpi:HSM*:
ID_VENDOR_FROM_DATABASE=AT&T Microelectronics
-@@ -3220,6 +3261,9 @@
+@@ -3223,6 +3264,9 @@
acpi:ICI*:
ID_VENDOR_FROM_DATABASE=Infotek Communication Inc
acpi:ICM*:
ID_VENDOR_FROM_DATABASE=Intracom SA
-@@ -3316,6 +3360,9 @@
+@@ -3319,6 +3363,9 @@
acpi:IKE*:
ID_VENDOR_FROM_DATABASE=Ikegami Tsushinki Co. Ltd.
acpi:IKS*:
ID_VENDOR_FROM_DATABASE=Ikos Systems Inc
-@@ -3361,6 +3408,9 @@
+@@ -3364,6 +3411,9 @@
acpi:IMT*:
ID_VENDOR_FROM_DATABASE=Inmax Technology Corporation
acpi:INA*:
ID_VENDOR_FROM_DATABASE=Inventec Corporation
-@@ -3868,6 +3918,9 @@
+@@ -3871,6 +3921,9 @@
acpi:LAN*:
ID_VENDOR_FROM_DATABASE=Sodeman Lancom Inc
acpi:LAS*:
ID_VENDOR_FROM_DATABASE=LASAT Comm. A/S
-@@ -3913,6 +3966,9 @@
+@@ -3916,6 +3969,9 @@
acpi:LED*:
ID_VENDOR_FROM_DATABASE=Long Engineering Design Inc
acpi:LEG*:
ID_VENDOR_FROM_DATABASE=Legerity, Inc
-@@ -3928,6 +3984,9 @@
+@@ -3931,6 +3987,9 @@
acpi:LGC*:
ID_VENDOR_FROM_DATABASE=Logic Ltd
acpi:LGI*:
ID_VENDOR_FROM_DATABASE=Logitech Inc
-@@ -3982,6 +4041,9 @@
+@@ -3985,6 +4044,9 @@
acpi:LND*:
ID_VENDOR_FROM_DATABASE=Land Computer Company Ltd
acpi:LNK*:
ID_VENDOR_FROM_DATABASE=Link Tech Inc
-@@ -4016,7 +4078,7 @@
+@@ -4019,7 +4081,7 @@
ID_VENDOR_FROM_DATABASE=Design Technology
acpi:LPL*:
acpi:LSC*:
ID_VENDOR_FROM_DATABASE=LifeSize Communications
-@@ -4192,6 +4254,9 @@
+@@ -4195,6 +4257,9 @@
acpi:MCX*:
ID_VENDOR_FROM_DATABASE=Millson Custom Solutions Inc.
acpi:MDA*:
ID_VENDOR_FROM_DATABASE=Media4 Inc
-@@ -4429,6 +4494,9 @@
+@@ -4432,6 +4497,9 @@
acpi:MOM*:
ID_VENDOR_FROM_DATABASE=Momentum Data Systems
acpi:MOS*:
ID_VENDOR_FROM_DATABASE=Moses Corporation
-@@ -4654,6 +4722,9 @@
+@@ -4657,6 +4725,9 @@
acpi:NAL*:
ID_VENDOR_FROM_DATABASE=Network Alchemy
acpi:NAT*:
ID_VENDOR_FROM_DATABASE=NaturalPoint Inc.
-@@ -5158,6 +5229,9 @@
+@@ -5161,6 +5232,9 @@
acpi:PCX*:
ID_VENDOR_FROM_DATABASE=PC Xperten
acpi:PDM*:
ID_VENDOR_FROM_DATABASE=Psion Dacom Plc.
-@@ -5221,9 +5295,6 @@
+@@ -5224,9 +5298,6 @@
acpi:PHE*:
ID_VENDOR_FROM_DATABASE=Philips Medical Systems Boeblingen GmbH
acpi:PHL*:
ID_VENDOR_FROM_DATABASE=Philips Consumer Electronics Company
-@@ -5311,9 +5382,6 @@
+@@ -5314,9 +5385,6 @@
acpi:PNL*:
ID_VENDOR_FROM_DATABASE=Panelview, Inc.
acpi:PNR*:
ID_VENDOR_FROM_DATABASE=Planar Systems, Inc.
-@@ -5449,15 +5517,9 @@
+@@ -5452,15 +5520,9 @@
acpi:PTS*:
ID_VENDOR_FROM_DATABASE=Plain Tree Systems Inc
acpi:PVG*:
ID_VENDOR_FROM_DATABASE=Proview Global Co., Ltd
-@@ -5773,9 +5835,6 @@
+@@ -5776,9 +5838,6 @@
acpi:RTI*:
ID_VENDOR_FROM_DATABASE=Rancho Tech Inc
acpi:RTL*:
ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Company Ltd
-@@ -5941,9 +6000,6 @@
+@@ -5944,9 +6003,6 @@
acpi:SEE*:
ID_VENDOR_FROM_DATABASE=SeeColor Corporation
acpi:SEI*:
ID_VENDOR_FROM_DATABASE=Seitz & Associates Inc
-@@ -6400,6 +6456,9 @@
+@@ -6403,6 +6459,9 @@
acpi:SVD*:
ID_VENDOR_FROM_DATABASE=SVD Computer
acpi:SVI*:
ID_VENDOR_FROM_DATABASE=Sun Microsystems
-@@ -6484,6 +6543,9 @@
+@@ -6487,6 +6546,9 @@
acpi:SZM*:
ID_VENDOR_FROM_DATABASE=Shenzhen MTC Co., Ltd
acpi:TAA*:
ID_VENDOR_FROM_DATABASE=Tandberg
-@@ -6574,6 +6636,9 @@
+@@ -6577,6 +6639,9 @@
acpi:TDG*:
ID_VENDOR_FROM_DATABASE=Six15 Technologies
acpi:TDM*:
ID_VENDOR_FROM_DATABASE=Tandem Computer Europe Inc
-@@ -6616,6 +6681,9 @@
+@@ -6619,6 +6684,9 @@
acpi:TEV*:
ID_VENDOR_FROM_DATABASE=Televés, S.A.
acpi:TEZ*:
ID_VENDOR_FROM_DATABASE=Tech Source Inc.
-@@ -6730,9 +6798,6 @@
+@@ -6733,9 +6801,6 @@
acpi:TNC*:
ID_VENDOR_FROM_DATABASE=TNC Industrial Company Ltd
acpi:TNM*:
ID_VENDOR_FROM_DATABASE=TECNIMAGEN SA
-@@ -7039,14 +7104,14 @@
+@@ -7042,14 +7107,14 @@
acpi:UNC*:
ID_VENDOR_FROM_DATABASE=Unisys Corporation
acpi:UNI*:
ID_VENDOR_FROM_DATABASE=Uniform Industry Corp.
-@@ -7081,6 +7146,9 @@
+@@ -7084,6 +7149,9 @@
acpi:USA*:
ID_VENDOR_FROM_DATABASE=Utimaco Safeware AG
acpi:USD*:
ID_VENDOR_FROM_DATABASE=U.S. Digital Corporation
-@@ -7324,9 +7392,6 @@
+@@ -7327,9 +7395,6 @@
acpi:WAL*:
ID_VENDOR_FROM_DATABASE=Wave Access
acpi:WAV*:
ID_VENDOR_FROM_DATABASE=Wavephore
-@@ -7451,7 +7516,7 @@
+@@ -7454,7 +7519,7 @@
ID_VENDOR_FROM_DATABASE=WyreStorm Technologies LLC
acpi:WYS*:
acpi:WYT*:
ID_VENDOR_FROM_DATABASE=Wooyoung Image & Information Co.,Ltd.
-@@ -7465,9 +7530,6 @@
+@@ -7468,9 +7533,6 @@
acpi:XDM*:
ID_VENDOR_FROM_DATABASE=XDM Ltd.
acpi:XES*:
ID_VENDOR_FROM_DATABASE=Extreme Engineering Solutions, Inc.
-@@ -7498,9 +7560,6 @@
+@@ -7501,9 +7563,6 @@
acpi:XNT*:
ID_VENDOR_FROM_DATABASE=XN Technologies, Inc.
acpi:XQU*:
ID_VENDOR_FROM_DATABASE=SHANGHAI SVA-DAV ELECTRONICS CO., LTD
-@@ -7567,6 +7626,9 @@
+@@ -7570,6 +7629,9 @@
acpi:ZBX*:
ID_VENDOR_FROM_DATABASE=Zebax Technologies
pci:v0000018Ad00000106*
ID_MODEL_FROM_DATABASE=FPC-0106TX misprogrammed [RTL81xx]
+pci:v000001DE*
+ ID_VENDOR_FROM_DATABASE=Oxide Computer Company
+
pci:v0000021B*
ID_VENDOR_FROM_DATABASE=Compaq Computer Corporation
pci:v00001000d0000005Dsv000017AAsd00001053*
ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkServer RAID 720ix)
+pci:v00001000d0000005Dsv00001BD4sd00000014*
+ ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (12G SAS3108 2G)
+
+pci:v00001000d0000005Dsv00001BD4sd00000015*
+ ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (12G SAS3108 4G)
+
pci:v00001000d0000005Dsv00001D49sd00000600*
ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkSystem RAID 730-8i 1GB Cache PCIe 12Gb Adapter)
pci:v00001000d00000072sv00001028sd00001F22*
ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (PERC H200 Internal Tape Adapter)
+pci:v00001000d00000072sv00001734sd00001177*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (HBA Ctrl SAS 6G 0/1 [D2607])
+
+pci:v00001000d00000072sv00001BD4sd0000000D*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IT)
+
+pci:v00001000d00000072sv00001BD4sd0000000E*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IR)
+
+pci:v00001000d00000072sv00001BD4sd0000000F*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IT SA5248)
+
+pci:v00001000d00000072sv00001BD4sd00000010*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IR SA5248)
+
pci:v00001000d00000072sv00008086sd0000350F*
ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (RMS2LL040 RAID Controller)
pci:v00001000d00000087sv00001590sd00000044*
ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (H220i)
+pci:v00001000d00000087sv00001BD4sd00000009*
+ ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (6G SAS2308IR)
+
+pci:v00001000d00000087sv00001BD4sd0000000A*
+ ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (6G SAS2308IT)
+
pci:v00001000d00000087sv00008086sd00003000*
ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (RS25GB008 RAID Controller)
pci:v00001000d00000097sv000015D9sd00000808*
ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (AOC-S3008L-L8e)
+pci:v00001000d00000097sv00001BD4sd0000000B*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IR)
+
+pci:v00001000d00000097sv00001BD4sd0000000C*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IT)
+
pci:v00001000d00000097sv00001BD4sd00000011*
ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (Inspur 12Gb 8i-3008 IT SAS HBA)
+pci:v00001000d00000097sv00001BD4sd00000012*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12Gb SAS3008IR UDM)
+
+pci:v00001000d00000097sv00001BD4sd00000026*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IT RACK)
+
+pci:v00001000d00000097sv00001BD4sd00000027*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IMR RACK)
+
+pci:v00001000d00000097sv00001BD4sd00000028*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IR RACK)
+
pci:v00001000d000000AB*
ID_MODEL_FROM_DATABASE=SAS3516 Fusion-MPT Tri-Mode RAID On Chip (ROC)
pci:v00001002d00001308*
ID_MODEL_FROM_DATABASE=Kaveri HDMI/DP Audio Controller
+pci:v00001002d00001308sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=Kaveri HDMI/DP Audio Controller (Z50-75)
+
pci:v00001002d00001309*
ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6/R7 Graphics]
+pci:v00001002d00001309sv000017AAsd00003830*
+ ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6/R7 Graphics] (Z50-75)
+
pci:v00001002d0000130A*
ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6 Graphics]
pci:v00001002d000015D8sv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=Picasso (Pavilion Laptop 15-cw1xxx)
+pci:v00001002d000015D8sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Picasso (ThinkPad E595)
+
pci:v00001002d000015DD*
ID_MODEL_FROM_DATABASE=Raven Ridge [Radeon Vega Series / Radeon Vega Mobile Series]
pci:v00001002d000015DEsv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=Raven/Raven2/Fenghuang HDMI/DP Audio Controller (Pavilion Laptop 15-cw1xxx)
+pci:v00001002d000015DEsv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven/Raven2/Fenghuang HDMI/DP Audio Controller (ThinkPad E595)
+
pci:v00001002d000015DF*
ID_MODEL_FROM_DATABASE=Raven/Raven2/Fenghuang/Renoir Cryptographic Coprocessor
ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (A785GM-M)
pci:v00001002d00004383sv0000103Csd00001611*
- ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (Pavilion DM1Z-3000)
+ ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (Pavilion dm1z-3000)
pci:v00001002d00004383sv0000103Csd0000280A*
ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (DC5750 Microtower)
pci:v00001002d00004391*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode]
+pci:v00001002d00004391sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (ProLiant MicroServer N36L)
+
pci:v00001002d00004391sv0000103Csd00001611*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (Pavilion DM1Z-3000)
pci:v00001002d00004396sv00001019sd00002120*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (A785GM-M)
+pci:v00001002d00004396sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (ProLiant MicroServer N36L)
+
pci:v00001002d00004396sv0000103Csd00001611*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (Pavilion DM1Z-3000)
pci:v00001002d00004397sv00001019sd00002120*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (A785GM-M)
+pci:v00001002d00004397sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (ProLiant MicroServer N36L)
+
pci:v00001002d00004397sv0000103Csd00001611*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (Pavilion DM1Z-3000)
pci:v00001002d0000439Csv00001019sd00002120*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (A785GM-M)
+pci:v00001002d0000439Csv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (ProLiant MicroServer N36L)
+
pci:v00001002d0000439Csv00001043sd000082EF*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (M3A78-EH Motherboard)
pci:v00001002d0000439Dsv00001019sd00002120*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (A785GM-M)
+pci:v00001002d0000439Dsv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (ProLiant MicroServer N36L)
+
pci:v00001002d0000439Dsv0000103Csd00001611*
ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (Pavilion DM1Z-3000)
ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon R7 350)
pci:v00001002d00006611*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM]
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM]
pci:v00001002d00006611sv00001028sd0000210B*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R5 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R5 240 OEM)
+
+pci:v00001002d00006611sv00001642sd00001869*
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon 520 OEM)
pci:v00001002d00006611sv0000174Bsd00004248*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 240 OEM)
pci:v00001002d00006611sv0000174Bsd0000A240*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 240 OEM)
pci:v00001002d00006611sv0000174Bsd0000D340*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 340 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 340 OEM)
pci:v00001002d00006611sv00001B0Asd000090D3*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 240 OEM)
pci:v00001002d00006613*
ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340]
ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile]
pci:v00001002d00006665sv000017AAsd00001309*
- ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile] (Radeon R7 M260DX)
+ ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile] (Z50-75 Radeon R7 M260DX)
pci:v00001002d00006665sv000017AAsd0000368F*
ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile] (Radeon R5 A230)
ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580)
pci:v00001002d000067DFsv0000148Csd00002372*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480 [Red Dragon])
pci:v00001002d000067DFsv0000148Csd00002373*
ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
pci:v00001002d000067DFsv00001849sd00005001*
ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Phantom Gaming X RX 580 OC)
+pci:v00001002d000067DFsv00001849sd00005030*
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Phantom Gaming D Radeon RX580 8G OC)
+
pci:v00001002d000067DFsv00001DA2sd0000E353*
ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570 Pulse 4GB)
pci:v00001002d00007310*
ID_MODEL_FROM_DATABASE=Navi 10
+pci:v00001002d00007312*
+ ID_MODEL_FROM_DATABASE=Navi 10 [Radeon Pro W5700]
+
pci:v00001002d0000731F*
- ID_MODEL_FROM_DATABASE=Navi 10 [Radeon RX 5700 / 5700 XT]
+ ID_MODEL_FROM_DATABASE=Navi 10 [Radeon RX 5600 OEM/5600 XT / 5700/5700 XT]
pci:v00001002d00007340*
- ID_MODEL_FROM_DATABASE=Navi 14 [Radeon RX 5500 / 5500M]
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon RX 5500/5500M / Pro 5500M]
+
+pci:v00001002d00007341*
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon Pro W5500]
+
+pci:v00001002d00007347*
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon Pro W5500M]
+
+pci:v00001002d0000734F*
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon Pro W5300M]
pci:v00001002d00007833*
ID_MODEL_FROM_DATABASE=RS350 Host Bridge
pci:v00001002d00009712*
ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4225/4250]
+pci:v00001002d00009712sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4225/4250] (ProLiant MicroServer N36L)
+
pci:v00001002d00009713*
ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4100]
pci:v00001002d00009832*
ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330]
+pci:v00001002d00009832sv00001849sd00009832*
+ ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330] (QC5000-ITX/PH)
+
pci:v00001002d00009833*
ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330E]
pci:v00001002d00009840*
ID_MODEL_FROM_DATABASE=Kabini HDMI/DP Audio
+pci:v00001002d00009840sv00001849sd00009840*
+ ID_MODEL_FROM_DATABASE=Kabini HDMI/DP Audio (QC5000-ITX/PH)
+
pci:v00001002d00009850*
ID_MODEL_FROM_DATABASE=Mullins [Radeon R3 Graphics]
pci:v00001022d00001480*
ID_MODEL_FROM_DATABASE=Starship/Matisse Root Complex
+pci:v00001022d00001480sv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=Starship/Matisse Root Complex (X570-A PRO motherboard)
+
pci:v00001022d00001481*
ID_MODEL_FROM_DATABASE=Starship/Matisse IOMMU
pci:v00001022d00001487*
ID_MODEL_FROM_DATABASE=Starship/Matisse HD Audio Controller
+pci:v00001022d00001487sv00001462sd00009C37*
+ ID_MODEL_FROM_DATABASE=Starship/Matisse HD Audio Controller (X570-A PRO motherboard)
+
pci:v00001022d00001488*
ID_MODEL_FROM_DATABASE=Starship Reserved SSP
pci:v00001022d0000149C*
ID_MODEL_FROM_DATABASE=Matisse USB 3.0 Host Controller
+pci:v00001022d0000149Csv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=Matisse USB 3.0 Host Controller (X570-A PRO motherboard)
+
pci:v00001022d00001510*
ID_MODEL_FROM_DATABASE=Family 14h Processor Root Complex
pci:v00001022d00001536*
ID_MODEL_FROM_DATABASE=Family 16h Processor Root Complex
+pci:v00001022d00001536sv00001849sd00001536*
+ ID_MODEL_FROM_DATABASE=Family 16h Processor Root Complex (QC5000-ITX/PH)
+
pci:v00001022d00001537*
ID_MODEL_FROM_DATABASE=Kabini/Mullins PSP-Platform Security Processor
pci:v00001022d000015DF*
ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) Platform Security Processor
+pci:v00001022d000015DFsv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) Platform Security Processor (ThinkPad E595)
+
pci:v00001022d000015E0*
ID_MODEL_FROM_DATABASE=Raven USB 3.1
pci:v00001022d000015E0sv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=Raven USB 3.1 (Pavilion Laptop 15-cw1xxx)
+pci:v00001022d000015E0sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven USB 3.1 (ThinkPad E595)
+
pci:v00001022d000015E1*
ID_MODEL_FROM_DATABASE=Raven USB 3.1
pci:v00001022d000015E1sv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=Raven USB 3.1 (Pavilion Laptop 15-cw1xxx)
+pci:v00001022d000015E1sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven USB 3.1 (ThinkPad E595)
+
pci:v00001022d000015E2*
ID_MODEL_FROM_DATABASE=Raven/Raven2/FireFlight/Renoir Audio Processor
+pci:v00001022d000015E2sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven/Raven2/FireFlight/Renoir Audio Processor (ThinkPad E595)
+
pci:v00001022d000015E3*
ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller
pci:v00001022d000015E3sv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller (Pavilion Laptop 15-cw1xxx)
+pci:v00001022d000015E3sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller (ThinkPad E595)
+
pci:v00001022d000015E4*
ID_MODEL_FROM_DATABASE=Raven/Raven2/Renoir Sensor Fusion Hub
pci:v00001022d000043D5*
ID_MODEL_FROM_DATABASE=400 Series Chipset USB 3.1 XHCI Controller
+pci:v00001022d000057A3*
+ ID_MODEL_FROM_DATABASE=Matisse PCIe GPP Bridge
+
+pci:v00001022d000057A4*
+ ID_MODEL_FROM_DATABASE=Matisse PCIe GPP Bridge
+
+pci:v00001022d000057AD*
+ ID_MODEL_FROM_DATABASE=Matisse Switch Upstream
+
pci:v00001022d00007006*
ID_MODEL_FROM_DATABASE=AMD-751 [Irongate] System Controller
pci:v00001022d00007801sv0000103Csd0000194E*
ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (ProBook 455 G1 Notebook)
+pci:v00001022d00007801sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (Z50-75)
+
+pci:v00001022d00007801sv00001849sd00007801*
+ ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (QC5000-ITX/PH)
+
pci:v00001022d00007802*
ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode]
pci:v00001022d00007807sv0000103Csd00001985*
ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (Pavilion 17-e163sg Notebook PC)
+pci:v00001022d00007807sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (Z50-75)
+
+pci:v00001022d00007807sv00001849sd00007807*
+ ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (QC5000-ITX/PH)
+
pci:v00001022d00007808*
ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller
pci:v00001022d00007808sv0000103Csd00001985*
ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (Pavilion 17-e163sg Notebook PC)
+pci:v00001022d00007808sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (Z50-75)
+
+pci:v00001022d00007808sv00001849sd00007808*
+ ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (QC5000-ITX/PH)
+
pci:v00001022d00007809*
ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller
pci:v00001022d00007809sv0000103Csd0000194E*
ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (ProBook 455 G1 Notebook)
+pci:v00001022d00007809sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (Z50-75)
+
pci:v00001022d0000780A*
ID_MODEL_FROM_DATABASE=Kabini/Mullins SATA Raid/AHCI Mode (DotHill driver)
pci:v00001022d0000780Bsv0000103Csd00001985*
ID_MODEL_FROM_DATABASE=FCH SMBus Controller (Pavilion 17-e163sg Notebook PC)
+pci:v00001022d0000780Bsv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (Z50-75)
+
+pci:v00001022d0000780Bsv00001849sd0000780B*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (QC5000-ITX/PH)
+
pci:v00001022d0000780C*
ID_MODEL_FROM_DATABASE=FCH IDE Controller
pci:v00001022d0000780Dsv00001043sd00008444*
ID_MODEL_FROM_DATABASE=FCH Azalia Controller (F2A85-M Series)
+pci:v00001022d0000780Dsv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH Azalia Controller (Z50-75)
+
+pci:v00001022d0000780Dsv00001849sd00008892*
+ ID_MODEL_FROM_DATABASE=FCH Azalia Controller (QC5000-ITX/PH)
+
pci:v00001022d0000780E*
ID_MODEL_FROM_DATABASE=FCH LPC Bridge
pci:v00001022d0000780Esv0000103Csd00001985*
ID_MODEL_FROM_DATABASE=FCH LPC Bridge (Pavilion 17-e163sg Notebook PC)
+pci:v00001022d0000780Esv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (Z50-75)
+
+pci:v00001022d0000780Esv00001849sd0000780E*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (QC5000-ITX/PH)
+
pci:v00001022d0000780F*
ID_MODEL_FROM_DATABASE=FCH PCI Bridge
pci:v00001022d00007814sv0000103Csd00001985*
ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (Pavilion 17-e163sg Notebook PC)
+pci:v00001022d00007814sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (Z50-75)
+
+pci:v00001022d00007814sv00001849sd00007814*
+ ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (QC5000-ITX/PH)
+
pci:v00001022d00007900*
ID_MODEL_FROM_DATABASE=FCH SATA Controller [IDE mode]
pci:v00001022d00007901sv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (Pavilion Laptop 15-cw1xxx)
+pci:v00001022d00007901sv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (X570-A PRO motherboard)
+
pci:v00001022d00007902*
ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode]
pci:v00001022d0000790Bsv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=FCH SMBus Controller (Pavilion Laptop 15-cw1xxx)
+pci:v00001022d0000790Bsv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (X570-A PRO motherboard)
+
+pci:v00001022d0000790Bsv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (ThinkPad E595)
+
pci:v00001022d0000790E*
ID_MODEL_FROM_DATABASE=FCH LPC Bridge
pci:v00001022d0000790Esv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=FCH LPC Bridge (Pavilion Laptop 15-cw1xxx)
+pci:v00001022d0000790Esv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (X570-A PRO motherboard)
+
+pci:v00001022d0000790Esv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (ThinkPad E595)
+
pci:v00001022d0000790F*
ID_MODEL_FROM_DATABASE=FCH PCI Bridge
pci:v00001022d00009601sv00001019sd00002120*
ID_MODEL_FROM_DATABASE=RS880 Host Bridge (A785GM-M)
+pci:v00001022d00009601sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS880 Host Bridge (ProLiant MicroServer N36L)
+
pci:v00001022d00009601sv00001043sd000083A2*
ID_MODEL_FROM_DATABASE=RS880 Host Bridge (M4A785-M Mainboard)
pci:v00001022d00009603*
ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (ext gfx port 0)
+pci:v00001022d00009603sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (ext gfx port 0) (ProLiant MicroServer N36L)
+
pci:v00001022d00009604*
ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 0)
pci:v00001022d00009606*
ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (PCIE port 2)
+pci:v00001022d00009606sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (PCIE port 2) (ProLiant MicroServer N36L)
+
pci:v00001022d00009607*
ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 3)
pci:v0000103Cd00009602*
ID_MODEL_FROM_DATABASE=AMD RS780/RS880 PCI to PCI bridge (int gfx)
+pci:v0000103Cd00009602sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=AMD RS780/RS880 PCI to PCI bridge (int gfx) (ProLiant MicroServer N36L)
+
pci:v0000103E*
ID_VENDOR_FROM_DATABASE=Solliday Engineering
pci:v00001057d00004806*
ID_MODEL_FROM_DATABASE=CPX8216
+pci:v00001057d0000480B*
+ ID_MODEL_FROM_DATABASE=MPC7410
+
pci:v00001057d00004D68*
ID_MODEL_FROM_DATABASE=20268
pci:v00001077d00002281sv00001077sd000002F0*
ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLE2770 Single Port 32GFC PCIe Gen4 x8 Adapter)
+pci:v00001077d00002281sv00001077sd000002F2*
+ ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLogic 1x32Gb QLE2770 FC HBA)
+
+pci:v00001077d00002281sv00001077sd000002F3*
+ ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLogic 2x32Gb QLE2772 FC HBA)
+
pci:v00001077d00002281sv00001590sd000002D3*
ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (SN1610Q - 1P Enhanced 32GFC Single Port Fibre Channel Host Bus Adapter)
pci:v00001077d00008070sv00001077sd00000057*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (2x25GE QL41232HxCU NIC)
+pci:v00001077d00008070sv00001077sd00000065*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (QLogic 4x10GE QL41154HQRJ CNA)
+
+pci:v00001077d00008070sv00001077sd00000066*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (QLogic 4x10GE QL41154HQCU CNA)
+
+pci:v00001077d00008070sv00001077sd00000068*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p SFP+ QL41132HLCU-HC Adapter)
+
+pci:v00001077d00008070sv00001077sd00000069*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p BASE-T QL41132HQRJ-HC OCP3 Adapter)
+
+pci:v00001077d00008070sv00001077sd00000070*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p BASE-T QL41132HLRJ-HC Adapter)
+
+pci:v00001077d00008070sv00001077sd00000071*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p SFP+ QL41132HQCU-HC OCP3 Adapter)
+
+pci:v00001077d00008070sv00001077sd00000072*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 4p SFP+ QL41134HLCU-HC Adapter)
+
+pci:v00001077d00008070sv00001077sd00000073*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10/25GbE 2p SFP28 QL41232HQCU-HC OCP3 Adapter)
+
+pci:v00001077d00008070sv00001077sd00000074*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10/25GbE 2p SFP28 QL41232HLCU-HC Adapter)
+
pci:v00001077d00008070sv00001590sd0000021A*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2P QL41162HLRJ-HP Adapter)
pci:v00001077d00008084sv00001077sd0000000F*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (2x25GE QL41262HMKR CNA)
+pci:v00001077d00008084sv00001077sd00000065*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (QLogic 4x10GE QL41154HQRJ CNA)
+
+pci:v00001077d00008084sv00001077sd00000066*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (QLogic 4x10GE QL41154HQCU CNA)
+
pci:v00001077d00008084sv00001590sd0000021A*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (10GbE 2P QL41162HLRJ-HP Adapter)
pci:v00001077d00008090sv00001077sd00000057*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (2x25GE QL41232HxCU NIC)
+pci:v00001077d00008090sv00001077sd00000065*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (QLogic 4x10GE QL41154HQRJ CNA)
+
+pci:v00001077d00008090sv00001077sd00000066*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (QLogic 4x10GE QL41154HQCU CNA)
+
pci:v00001077d00008090sv00001590sd0000021A*
ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (10GbE 2P QL41162HLRJ-HP Adapter)
pci:v000010DEd00000FBB*
ID_MODEL_FROM_DATABASE=GM204 High Definition Audio Controller
+pci:v000010DEd00000FBC*
+ ID_MODEL_FROM_DATABASE=GM107 High Definition Audio Controller [GeForce 940MX]
+
pci:v000010DEd00000FC0*
ID_MODEL_FROM_DATABASE=GK107 [GeForce GT 640 OEM]
pci:v000010DEd000010F7*
ID_MODEL_FROM_DATABASE=TU102 High Definition Audio Controller
+pci:v000010DEd000010F8*
+ ID_MODEL_FROM_DATABASE=TU104 HD Audio Controller
+
pci:v000010DEd000010F9*
ID_MODEL_FROM_DATABASE=TU106 High Definition Audio Controller
pci:v000010DEd00001427*
ID_MODEL_FROM_DATABASE=GM206M [GeForce GTX 965M]
+pci:v000010DEd00001427sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=GM206M [GeForce GTX 965M] (OMEN-17-w001nv)
+
pci:v000010DEd00001430*
ID_MODEL_FROM_DATABASE=GM206GL [Quadro M2000]
pci:v000010DEd0000179C*
ID_MODEL_FROM_DATABASE=GM107 [GeForce 940MX]
+pci:v000010DEd0000179Csv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=GM107 [GeForce 940MX] (Acer Aspire E5-575G)
+
pci:v000010DEd000017C2*
ID_MODEL_FROM_DATABASE=GM200 [GeForce GTX TITAN X]
pci:v000010DEd00001AEB*
ID_MODEL_FROM_DATABASE=TU116 High Definition Audio Controller
+pci:v000010DEd00001AED*
+ ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1650 SUPER]
+
pci:v000010DEd00001B00*
ID_MODEL_FROM_DATABASE=GP102 [TITAN X]
pci:v000010DEd00001C30*
ID_MODEL_FROM_DATABASE=GP106GL [Quadro P2000]
+pci:v000010DEd00001C31*
+ ID_MODEL_FROM_DATABASE=GP106GL [Quadro P2200]
+
pci:v000010DEd00001C35*
ID_MODEL_FROM_DATABASE=GP106
pci:v000010DEd00001C82*
ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 Ti]
+pci:v000010DEd00001C82sv00001043sd00008613*
+ ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 Ti] (PH-GTX1050TI-4G)
+
+pci:v000010DEd00001C82sv00001458sd00003763*
+ ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 Ti] (GV-N105TOC-4GD)
+
pci:v000010DEd00001C83*
ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 3GB]
pci:v000010DEd00001C92*
ID_MODEL_FROM_DATABASE=GP107M [GeForce GTX 1050 Mobile]
+pci:v000010DEd00001C94*
+ ID_MODEL_FROM_DATABASE=GP107M [GeForce MX350]
+
pci:v000010DEd00001CA7*
ID_MODEL_FROM_DATABASE=GP107GL
pci:v000010DEd00001CBC*
ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P600 Mobile]
+pci:v000010DEd00001CBD*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P620]
+
pci:v000010DEd00001CCC*
ID_MODEL_FROM_DATABASE=GP107BM [GeForce GTX 1050 Ti Mobile]
pci:v000010DEd00001CCD*
ID_MODEL_FROM_DATABASE=GP107BM [GeForce GTX 1050 Mobile]
+pci:v000010DEd00001CFA*
+ ID_MODEL_FROM_DATABASE=GP107GL [Quadro P2000]
+
+pci:v000010DEd00001CFB*
+ ID_MODEL_FROM_DATABASE=GP107GL [Quadro P1000]
+
pci:v000010DEd00001D01*
ID_MODEL_FROM_DATABASE=GP108 [GeForce GT 1030]
pci:v000010DEd00001D13*
ID_MODEL_FROM_DATABASE=GP108M [GeForce MX250]
+pci:v000010DEd00001D16*
+ ID_MODEL_FROM_DATABASE=GP108M [GeForce MX330]
+
pci:v000010DEd00001D33*
ID_MODEL_FROM_DATABASE=GP108GLM [Quadro P500 Mobile]
+pci:v000010DEd00001D34*
+ ID_MODEL_FROM_DATABASE=GP108GLM [Quadro P520]
+
pci:v000010DEd00001D52*
ID_MODEL_FROM_DATABASE=GP108BM [GeForce MX250]
ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM2 16GB]
pci:v000010DEd00001DB2*
- ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100-DGXS-16GB]
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 DGXS 16GB]
pci:v000010DEd00001DB3*
ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 FHHL 16GB]
pci:v000010DEd00001DB7*
ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 DGXS 32GB]
+pci:v000010DEd00001DB8*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM3 32GB]
+
+pci:v000010DEd00001DB8sv000010DEsd0000131D*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM3 32GB] (Tesla V100-SXM3-32GB-H)
+
pci:v000010DEd00001DBA*
ID_MODEL_FROM_DATABASE=GV100GL [Quadro GV100]
pci:v000010DEd00001DBAsv000010DEsd000012EB*
ID_MODEL_FROM_DATABASE=GV100GL [Quadro GV100] (TITAN V CEO Edition)
+pci:v000010DEd00001DF0*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla PG500-216]
+
+pci:v000010DEd00001DF2*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla PG503-216]
+
+pci:v000010DEd00001DF5*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM2 16GB]
+
+pci:v000010DEd00001DF6*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100S PCIe 32GB]
+
pci:v000010DEd00001E02*
ID_MODEL_FROM_DATABASE=TU102 [TITAN RTX]
pci:v000010DEd00001E30sv000010DEsd000012BA*
ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000] (Quadro RTX 6000)
+pci:v000010DEd00001E37*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16]
+
+pci:v000010DEd00001E37sv000010DEsd00001347*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16] (GRID RTX T10-8)
+
+pci:v000010DEd00001E37sv000010DEsd00001348*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16] (GRID RTX T10-4)
+
+pci:v000010DEd00001E37sv000010DEsd00001370*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16] (GRID RTX T10-16)
+
pci:v000010DEd00001E38*
ID_MODEL_FROM_DATABASE=TU102GL
pci:v000010DEd00001E3E*
ID_MODEL_FROM_DATABASE=TU102GL
+pci:v000010DEd00001E78*
+ ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000]
+
+pci:v000010DEd00001E78sv000010DEsd000013D8*
+ ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000] (Quadro RTX 8000)
+
+pci:v000010DEd00001E78sv000010DEsd000013D9*
+ ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000] (Quadro RTX 6000)
+
pci:v000010DEd00001E81*
ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2080 SUPER]
pci:v000010DEd00001E87*
ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2080 Rev. A]
+pci:v000010DEd00001E89*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2060]
+
pci:v000010DEd00001E90*
ID_MODEL_FROM_DATABASE=TU104M [GeForce RTX 2080 Mobile]
pci:v000010DEd00001EBE*
ID_MODEL_FROM_DATABASE=TU104GL
+pci:v000010DEd00001EC2*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2070 SUPER]
+
+pci:v000010DEd00001EC7*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2070 SUPER]
+
pci:v000010DEd00001ED0*
ID_MODEL_FROM_DATABASE=TU104BM [GeForce RTX 2080 Mobile]
pci:v000010DEd00001F36*
ID_MODEL_FROM_DATABASE=TU106GLM [Quadro RTX 3000 Mobile / Max-Q]
+pci:v000010DEd00001F42*
+ ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2060 SUPER]
+
+pci:v000010DEd00001F47*
+ ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2060 SUPER]
+
pci:v000010DEd00001F50*
ID_MODEL_FROM_DATABASE=TU106BM [GeForce RTX 2070 Mobile]
pci:v000010DEd00001F82*
ID_MODEL_FROM_DATABASE=TU117 [GeForce GTX 1650]
+pci:v000010DEd00001F91*
+ ID_MODEL_FROM_DATABASE=TU117M [GeForce GTX 1650 Mobile / Max-Q]
+
pci:v000010DEd00001F92*
ID_MODEL_FROM_DATABASE=TU117M [GeForce GTX 1650 Mobile]
+pci:v000010DEd00001F96*
+ ID_MODEL_FROM_DATABASE=TU117M [GeForce GTX 1650 Mobile / Max-Q]
+
pci:v000010DEd00001FAE*
ID_MODEL_FROM_DATABASE=TU117GL
pci:v000010DEd00002184*
ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660]
+pci:v000010DEd00002187*
+ ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1650 SUPER]
+
pci:v000010DEd00002191*
ID_MODEL_FROM_DATABASE=TU116M [GeForce GTX 1660 Ti Mobile]
pci:v000010DEd000021BF*
ID_MODEL_FROM_DATABASE=TU116GL
+pci:v000010DEd000021C4*
+ ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660 SUPER]
+
pci:v000010DEd000021D1*
ID_MODEL_FROM_DATABASE=TU116BM [GeForce GTX 1660 Ti Mobile]
pci:v000010ECd0000522Asv0000103Csd00008079*
ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader (EliteBook 840 G3)
+pci:v000010ECd0000522Asv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader (OMEN-17-w001nv)
+
+pci:v000010ECd0000522Asv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader (ThinkPad E595)
+
pci:v000010ECd00005249*
ID_MODEL_FROM_DATABASE=RTS5249 PCI Express Card Reader
pci:v000010ECd00005287*
ID_MODEL_FROM_DATABASE=RTL8411B PCI Express Card Reader
+pci:v000010ECd00005287sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=RTL8411B PCI Express Card Reader (Acer Aspire E5-575G)
+
pci:v000010ECd00005288*
ID_MODEL_FROM_DATABASE=RTS5288 PCI Express Card Reader
ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Presario C700)
pci:v000010ECd00008139sv00001043sd00001045*
- ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (L8400B or L3C/S notebook)
+ ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (L8400B, L3C/S, X58LE notebook)
pci:v000010ECd00008139sv00001043sd00008109*
ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (P5P800-MX Mainboard)
pci:v000010ECd00008168sv00001019sd00008168*
ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (RTL8111/8168 PCI Express Gigabit Ethernet controller)
+pci:v000010ECd00008168sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Acer Aspire E5-575G)
+
pci:v000010ECd00008168sv00001028sd00000283*
ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Vostro 220)
pci:v000010ECd00008168sv0000103Csd00002A6F*
ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Asus IPIBL-LB Motherboard)
+pci:v000010ECd00008168sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (OMEN-17-w001nv)
+
pci:v000010ECd00008168sv0000103Csd00008615*
ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Pavilion Laptop 15-cw1xxx)
pci:v000010ECd00008168sv00001462sd00007522*
ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (X58 Pro-E)
+pci:v000010ECd00008168sv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (X570-A PRO motherboard)
+
pci:v000010ECd00008168sv00001775sd000011CC*
ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (CC11/CL11)
+pci:v000010ECd00008168sv000017AAsd00003814*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Z50-75)
+
+pci:v000010ECd00008168sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (ThinkPad E595)
+
pci:v000010ECd00008168sv00001849sd00008168*
ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard (one of many))
pci:v000010ECd0000B723sv000010ECsd00008739*
ID_MODEL_FROM_DATABASE=RTL8723BE PCIe Wireless Network Adapter (Dell Wireless 1801)
+pci:v000010ECd0000B723sv000017AAsd0000B736*
+ ID_MODEL_FROM_DATABASE=RTL8723BE PCIe Wireless Network Adapter (Z50-75)
+
pci:v000010ECd0000B822*
ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter
pci:v000010ECd0000B822sv0000103Csd0000831B*
ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter (Realtek RTL8822BE 802.11ac 2 × 2 Wi-Fi + Bluetooth 4.2 Combo Adapter (MU-MIMO supported))
+pci:v000010ECd0000B822sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter (ThinkPad E595)
+
+pci:v000010ECd0000B822sv000017AAsd0000B023*
+ ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter (ThinkPad E595)
+
pci:v000010ECd0000C821*
ID_MODEL_FROM_DATABASE=RTL8821CE 802.11ac PCIe Wireless Network Adapter
+pci:v000010ECd0000C822*
+ ID_MODEL_FROM_DATABASE=RTL8822CE 802.11ac PCIe Wireless Network Adapter
+
pci:v000010ECd0000D723*
ID_MODEL_FROM_DATABASE=RTL8723DE 802.11b/g/n PCIe Adapter
ID_MODEL_FROM_DATABASE=EMU20k1 [Sound Blaster X-Fi Series] (X-Fi XtremeMusic)
pci:v00001102d00000006*
- ID_MODEL_FROM_DATABASE=EMU10k1X [SB Live! Value/OEM Series]
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1]
+
+pci:v00001102d00000006sv00001102sd00001001*
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1] (SB0680 Sound Blaster 5.1)
+
+pci:v00001102d00000006sv00001102sd00001003*
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1] (SB0203 SB Live! 5.1 (Dell))
+
+pci:v00001102d00000006sv00001102sd00001004*
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1] (TP0033 Ectiva Audio 5.1)
pci:v00001102d00000007*
ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series]
ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970TX/2TX Gigabit Ethernet Adapter)
pci:v00001148d00004320*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001]
pci:v00001148d00004320sv00001148sd00000121*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8001 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8001 Adapter)
pci:v00001148d00004320sv00001148sd00000221*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8002 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8002 Adapter)
pci:v00001148d00004320sv00001148sd00000321*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8003 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8003 Adapter)
pci:v00001148d00004320sv00001148sd00000421*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8004 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8004 Adapter)
pci:v00001148d00004320sv00001148sd00000621*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8006 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8006 Adapter)
pci:v00001148d00004320sv00001148sd00000721*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8007 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8007 Adapter)
pci:v00001148d00004320sv00001148sd00000821*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8008 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8008 Adapter)
pci:v00001148d00004320sv00001148sd00000921*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8009 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8009 Adapter)
pci:v00001148d00004320sv00001148sd00001121*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8011 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8011 Adapter)
pci:v00001148d00004320sv00001148sd00001221*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8012 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8012 Adapter)
pci:v00001148d00004320sv00001148sd00003221*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9521 V2.0 10/100/1000Base-T Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9521 V2.0 10/100/1000Base-T Adapter)
pci:v00001148d00004320sv00001148sd00005021*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter)
pci:v00001148d00004320sv00001148sd00005041*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter)
pci:v00001148d00004320sv00001148sd00005043*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
pci:v00001148d00004320sv00001148sd00005051*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
pci:v00001148d00004320sv00001148sd00005061*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
pci:v00001148d00004320sv00001148sd00005071*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter)
pci:v00001148d00004320sv00001148sd00009521*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9521 10/100/1000Base-T Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9521 10/100/1000Base-T Adapter)
+
+pci:v00001148d00004320sv00001259sd00002916*
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (AT-2916T)
pci:v00001148d00004400*
ID_MODEL_FROM_DATABASE=SK-9Dxx Gigabit Ethernet Adapter
ID_MODEL_FROM_DATABASE=SK-9Mxx Gigabit Ethernet Adapter
pci:v00001148d00009000*
- ID_MODEL_FROM_DATABASE=SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022]
+
+pci:v00001148d00009000sv00001148sd00002100*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45)
+
+pci:v00001148d00009000sv00001148sd00002200*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S22 10/100/1000Base-T Dual Port Server Adapter, PCI-X, 2 Copper RJ-45)
+
+pci:v00001148d00009000sv00001148sd00002210*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9P22 10/100/1000 Base-T Dual Port PMC card)
+
+pci:v00001148d00009000sv00001148sd00002220*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (TPMC-GBE-CO)
+
+pci:v00001148d00009000sv00001148sd00008100*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S81 1000Base-SX Server Adapter,PCI-X, Fiber SX/LC)
+
+pci:v00001148d00009000sv00001148sd00008200*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S82 1000Base-SX Dual Port Server Adapter, PCI-X, 2 Fiber SX/LC)
+
+pci:v00001148d00009000sv00001148sd00008210*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9P82 1000 Base-SX Dual Port PMC card)
+
+pci:v00001148d00009000sv00001148sd00008220*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (TPMC-GBE-FI)
+
+pci:v00001148d00009000sv00001148sd00009100*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S91 1000Base-LX Server Adapter,PCI-X, Fiber LX/LC)
+
+pci:v00001148d00009000sv00001148sd00009200*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S92 1000Base-LX Dual Port Server Adapter, PCI-X, 2 Fiber LX/LC)
+
+pci:v00001148d00009000sv00001259sd00002973*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (AT-2971SX v2 Gigabit Adapter)
+
+pci:v00001148d00009000sv00001259sd00002974*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (AT-2971T v2 Gigabit Adapter)
+
+pci:v00001148d00009000sv00001259sd00002978*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (AT-2971LX Gigabit Adapter)
pci:v00001148d00009843*
ID_MODEL_FROM_DATABASE=[Fujitsu] Gigabit Ethernet
pci:v00001180d00000476sv0000103Csd000030C1*
ID_MODEL_FROM_DATABASE=RL5c476 II (Compaq 6910p)
+pci:v00001180d00000476sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=RL5c476 II (X58LE)
+
pci:v00001180d00000476sv00001043sd00001237*
ID_MODEL_FROM_DATABASE=RL5c476 II (A6J-Q008)
pci:v00001180d00000592sv0000103Csd000030CF*
ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Pavilion dv95xx/96xx/97xx/98xx series)
+pci:v00001180d00000592sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (X58LE)
+
pci:v00001180d00000592sv00001043sd00001237*
ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (A6J-Q008)
pci:v00001180d00000822sv0000103Csd000030CF*
ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Pavilion dv9668eg Laptop)
+pci:v00001180d00000822sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (X58LE)
+
pci:v00001180d00000822sv00001043sd00001237*
ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (A6J-Q008)
pci:v00001180d00000843sv0000103Csd000030CF*
ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Pavilion dv9500/9600/9700 series)
+pci:v00001180d00000843sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (X58LE)
+
pci:v00001180d00000843sv00001183sd00000843*
ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Alienware Aurora m9700)
pci:v000011ABd00004381*
ID_MODEL_FROM_DATABASE=Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB]
+pci:v000011ABd00004381sv00001259sd00002803*
+ ID_MODEL_FROM_DATABASE=Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB] (AT-2814FX)
+
+pci:v000011ABd00004381sv00001259sd00002804*
+ ID_MODEL_FROM_DATABASE=Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB] (AT-2874xx)
+
pci:v000011ABd00004611*
ID_MODEL_FROM_DATABASE=GT-64115 System Controller
ID_VENDOR_FROM_DATABASE=Power I/O, Inc.
pci:v00001227*
- ID_VENDOR_FROM_DATABASE=Tech-Source
+ ID_VENDOR_FROM_DATABASE=EIZO Rugged Solutions
pci:v00001227d00000006*
ID_MODEL_FROM_DATABASE=Raptor GFX 8P
pci:v0000144Dd0000A822sv00001028sd00001FFA*
ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172Xa/172Xb (Express Flash PM1725b 12.8TB AIC)
+pci:v0000144Dd0000A824*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller PM173X
+
pci:v0000144E*
ID_VENDOR_FROM_DATABASE=OLITEC
pci:v000014E4d0000165Fsv00001028sd000004F7*
ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe (PowerEdge R320 server)
+pci:v000014E4d0000165Fsv00001028sd000008FD*
+ ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe (PowerEdge R6515/R7515 LOM)
+
pci:v000014E4d0000165Fsv00001028sd000008FF*
ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe (PowerEdge Rx5xx LOM Board)
pci:v000014E4d00004464*
ID_MODEL_FROM_DATABASE=BCM4364 802.11ac Wireless Network Adapter
+pci:v000014E4d00004488*
+ ID_MODEL_FROM_DATABASE=BCM4377b Wireless Network Adapter
+
pci:v000014E4d00004610*
ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 PCI to SB Bridge
pci:v000015B3d00000281*
ID_MODEL_FROM_DATABASE=NPS-600 Flash Recovery
+pci:v000015B3d00000538*
+ ID_MODEL_FROM_DATABASE=MT2910 Family [ConnectX-7 Flash Recovery]
+
+pci:v000015B3d00000539*
+ ID_MODEL_FROM_DATABASE=MT2910 Family [ConnectX-7 Secure Flash Recovery]
+
pci:v000015B3d00001002*
ID_MODEL_FROM_DATABASE=MT25400 Family [ConnectX-2 Virtual Function]
ID_MODEL_FROM_DATABASE=MT28860
pci:v000015B3d00001021*
- ID_MODEL_FROM_DATABASE=MT28861
+ ID_MODEL_FROM_DATABASE=MT2910 Family [ConnectX-7]
pci:v000015B3d00001974*
ID_MODEL_FROM_DATABASE=MT28800 Family [ConnectX-5 PCIe Bridge]
pci:v000015B3d00004117sv00001BD4sd00000039*
ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (SN10XMP2P25)
+pci:v000015B3d00004117sv00001BD4sd0000003A*
+ ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (25G SFP28 SP EO251FM9 Adapter)
+
pci:v000015B3d00004117sv00001BD4sd0000004D*
ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (SN10XMP2P25,YZPC-01191-101)
pci:v0000168Cd00000042*
ID_MODEL_FROM_DATABASE=QCA9377 802.11ac Wireless Network Adapter
+pci:v0000168Cd00000042sv000011ADsd000008A6*
+ ID_MODEL_FROM_DATABASE=QCA9377 802.11ac Wireless Network Adapter (Qualcomm Atheros QCA9377 802.11ac Wireless Network Adapter)
+
pci:v0000168Cd00000046*
ID_MODEL_FROM_DATABASE=QCA9984 802.11ac Wave 2 Wireless Network Adapter
pci:v000017D3d00001884*
ID_MODEL_FROM_DATABASE=ARC-1884 series PCIe 3.0 to SAS/SATA 12/6Gb RAID Controller
+pci:v000017D3d0000188A*
+ ID_MODEL_FROM_DATABASE=ARC-1886 series PCIe 4.0 to NVMe/SAS/SATA 16/12/6Gb RAID Controller
+
pci:v000017D5*
ID_VENDOR_FROM_DATABASE=Exar Corp.
ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet
pci:v00001969d00001048sv00001043sd00008226*
- ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
pci:v00001969d00001062*
ID_MODEL_FROM_DATABASE=AR8132 Fast Ethernet
pci:v00001987d00005012*
ID_MODEL_FROM_DATABASE=E12 NVMe Controller
+pci:v00001987d00005016*
+ ID_MODEL_FROM_DATABASE=E16 PCIe4 NVMe Controller
+
pci:v00001989*
ID_VENDOR_FROM_DATABASE=Montilio Inc.
pci:v000019E5d00000200*
ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE)
+pci:v000019E5d00000200sv000019E5sd0000D139*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE) (Hi1822 SP572 (2*100GE))
+
+pci:v000019E5d00000200sv000019E5sd0000D13D*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE) (Hi1822 SC371 (2*100GE))
+
pci:v000019E5d00000202*
ID_MODEL_FROM_DATABASE=Hi1822 Family (2*32G FC)
+pci:v000019E5d00000202sv000019E5sd0000D302*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*32G FC) (Hi1822 SP521 (2*32G FC))
+
+pci:v000019E5d00000202sv000019E5sd0000D304*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*32G FC) (Hi1822 SP526 (2*32G FC))
+
pci:v000019E5d00000203*
ID_MODEL_FROM_DATABASE=Hi1822 Family (2*16G FC)
+pci:v000019E5d00000203sv000019E5sd0000D301*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*16G FC) (Hi1822 SP520 (2*16G FC))
+
+pci:v000019E5d00000203sv000019E5sd0000D305*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*16G FC) (Hi1822 SP525 (2*16G FC))
+
pci:v000019E5d00000205*
ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE)
+pci:v000019E5d00000205sv000019E5sd0000DF27*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE) (Hi1822 MZ731 MEZZ (2*100GE))
+
pci:v000019E5d00000206*
ID_MODEL_FROM_DATABASE=Hi1822 Family (2*25GE)
+pci:v000019E5d00000206sv000019E5sd0000D138*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*25GE) (Hi1822 SP582 (2*25GE))
+
+pci:v000019E5d00000206sv000019E5sd0000D13A*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*25GE) (Hi1822 SC381 (2*25GE))
+
pci:v000019E5d00000210*
ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE)
+pci:v000019E5d00000210sv000019E5sd0000DF2E*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 MZ532 MEZZ (4*25GE))
+
pci:v000019E5d00000211*
ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE)
+pci:v000019E5d00000211sv000019E5sd0000D12F*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP571 (4*25GE))
+
+pci:v000019E5d00000211sv000019E5sd0000D137*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP581 (4*25GE))
+
+pci:v000019E5d00000211sv000019E5sd0000D142*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP583 (4*25GE))
+
pci:v000019E5d00000212*
ID_MODEL_FROM_DATABASE=Hi1822 Family (2*8G FC)
+pci:v000019E5d00000212sv000019E5sd0000D303*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*8G FC) (Hi1822 SP522 (2*8G FC))
+
+pci:v000019E5d00000212sv000019E5sd0000D306*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*8G FC) (Hi1822 SP523 (2*8G FC))
+
pci:v000019E5d00001710*
ID_MODEL_FROM_DATABASE=iBMA Virtual Network Adapter
pci:v000019E5d00001822*
ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE)
+pci:v000019E5d00001822sv000019E5sd0000D129*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP570 (4*25GE))
+
+pci:v000019E5d00001822sv000019E5sd0000D136*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP580 (4*25GE))
+
+pci:v000019E5d00001822sv000019E5sd0000D141*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP583 (4*25GE))
+
pci:v000019E5d0000371E*
ID_MODEL_FROM_DATABASE=Hi1822 Family Virtual Bridge
pci:v00001AB9*
ID_VENDOR_FROM_DATABASE=Espia Srl
+pci:v00001AC1*
+ ID_VENDOR_FROM_DATABASE=Global Unichip Corp.
+
+pci:v00001AC1d0000089A*
+ ID_MODEL_FROM_DATABASE=Coral Edge TPU
+
pci:v00001AC8*
ID_VENDOR_FROM_DATABASE=Aeroflex Gaisler
pci:v00001AEAd00006601*
ID_MODEL_FROM_DATABASE=AU6601 PCI-E Flash card reader controller
+pci:v00001AEAd00006621*
+ ID_MODEL_FROM_DATABASE=AU6621 PCI-E Flash card reader controller
+
+pci:v00001AEAd00006625*
+ ID_MODEL_FROM_DATABASE=AU6625 PCI-E Flash card reader controller
+
pci:v00001AEC*
ID_VENDOR_FROM_DATABASE=Wolfson Microelectronics
pci:v00001B6Fd00007052*
ID_MODEL_FROM_DATABASE=EJ188/EJ198 USB 3.0 Host Controller
+pci:v00001B6Fd00007052sv00001849sd00007052*
+ ID_MODEL_FROM_DATABASE=EJ188/EJ198 USB 3.0 Host Controller (QC5000-ITX/PH)
+
pci:v00001B73*
ID_VENDOR_FROM_DATABASE=Fresco Logic
ID_MODEL_FROM_DATABASE=FBC1CG Capture 1x100Gb
pci:v00001C2Cd000000A9*
- ID_MODEL_FROM_DATABASE=FBC2XGHH Capture 2x10Gb
+ ID_MODEL_FROM_DATABASE=FBC2XGHH Capture 2x10Gb [Latina]
pci:v00001C2Cd000000AD*
ID_MODEL_FROM_DATABASE=FBC2CGG3HL Capture 2x100Gb [Padua]
pci:v00001C5Cd00001285*
ID_MODEL_FROM_DATABASE=PC300 NVMe Solid State Drive 1TB
+pci:v00001C5Cd00001327*
+ ID_MODEL_FROM_DATABASE=BC501 NVMe Solid State Drive 512GB
+
pci:v00001C5Cd00001504*
ID_MODEL_FROM_DATABASE=SC300 512GB M.2 2280 SATA Solid State Drive
pci:v00001C5F*
ID_VENDOR_FROM_DATABASE=Beijing Memblaze Technology Co. Ltd.
+pci:v00001C5Fd0000000D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 520/526 AIC
+
+pci:v00001C5Fd0000003D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 920/926 AIC
+
+pci:v00001C5Fd0000010D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 520/526 U.2
+
+pci:v00001C5Fd0000013D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 920/926 U.2
+
pci:v00001C5Fd00000540*
ID_MODEL_FROM_DATABASE=PBlaze4 NVMe SSD
+pci:v00001C5Fd00000550*
+ ID_MODEL_FROM_DATABASE=PBlaze5 700/900
+
+pci:v00001C5Fd00000555*
+ ID_MODEL_FROM_DATABASE=PBlaze5 510/516
+
+pci:v00001C5Fd00000557*
+ ID_MODEL_FROM_DATABASE=PBlaze5 910/916
+
pci:v00001C63*
ID_VENDOR_FROM_DATABASE=Science and Research Centre of Computer Technology (JSC "NICEVT")
pci:v00001DBFd00000401*
ID_MODEL_FROM_DATABASE=StarDragon4800 PCI Express Root Port
+pci:v00001DC5*
+ ID_VENDOR_FROM_DATABASE=FADU Inc.
+
+pci:v00001DCD*
+ ID_VENDOR_FROM_DATABASE=Liqid Inc.
+
pci:v00001DD8*
ID_VENDOR_FROM_DATABASE=Pensando Systems Inc
pci:v00001DF3d00000203sv00001DF3sd00000003*
ID_MODEL_FROM_DATABASE=ACE-NIC100 Programmable Network Accelerator (ENA2100F)
+pci:v00001DF3d00000203sv00001DF3sd00000004*
+ ID_MODEL_FROM_DATABASE=ACE-NIC100 Programmable Network Accelerator (ENA2040F)
+
pci:v00001DF3d00000204*
ID_MODEL_FROM_DATABASE=ACE-NIC-NID Programmable Network Accelerator
pci:v00001E26*
ID_VENDOR_FROM_DATABASE=Fujitsu Client Computing Limited
+pci:v00001E36*
+ ID_VENDOR_FROM_DATABASE=Shanghai Enflame Technology Co. Ltd
+
+pci:v00001E36d00000001*
+ ID_MODEL_FROM_DATABASE=T10 [CloudBlazer]
+
pci:v00001E38*
- ID_VENDOR_FROM_DATABASE=Thinci, Inc
+ ID_VENDOR_FROM_DATABASE=Blaize, Inc
pci:v00001E3D*
ID_VENDOR_FROM_DATABASE=Burlywood, Inc
pci:v00001E57*
ID_VENDOR_FROM_DATABASE=Beijing Panyi Technology Co., Ltd
+pci:v00001E57d00000100*
+ ID_MODEL_FROM_DATABASE=The device has already been deleted.
+
+pci:v00001E57d00000100sv00000000sd00000100*
+ ID_MODEL_FROM_DATABASE=The device has already been deleted. (PY8800 64GB Accelerator)
+
pci:v00001E6B*
ID_VENDOR_FROM_DATABASE=Axiado Corp.
+pci:v00001E89*
+ ID_VENDOR_FROM_DATABASE=ID Quantique SA
+
+pci:v00001E89d00000002*
+ ID_MODEL_FROM_DATABASE=Quantis-PCIe-40M
+
+pci:v00001E89d00000003*
+ ID_MODEL_FROM_DATABASE=Quantis-PCIe-240M
+
+pci:v00001E94*
+ ID_VENDOR_FROM_DATABASE=Calian SED
+
pci:v00001FC0*
ID_VENDOR_FROM_DATABASE=Ascom (Finland) Oy
pci:v00008086d00000154sv00001043sd00001517*
ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (Zenbook Prime UX31A)
+pci:v00008086d00000154sv000010CFsd000016BF*
+ ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (LIFEBOOK E752)
+
pci:v00008086d00000155*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port
pci:v00008086d00000166sv00001043sd00002103*
ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller (N56VZ)
+pci:v00008086d00000166sv000010CFsd000016C1*
+ ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller (LIFEBOOK E752)
+
pci:v00008086d0000016A*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller
pci:v00008086d0000101Esv00008086sd0000101E*
ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (PRO/1000 MT Mobile Connection)
+pci:v00008086d0000101F*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller V710 for 5GBASE-T
+
pci:v00008086d00001026*
ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller
pci:v00008086d00001050*
ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller
+pci:v00008086d00001050sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (ThinkCentre S50)
+
pci:v00008086d00001050sv00001028sd0000019D*
ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (Dimension 3000)
pci:v00008086d00001503sv00001043sd0000849C*
ID_MODEL_FROM_DATABASE=82579V Gigabit Network Connection (P8P67 Deluxe Motherboard)
+pci:v00008086d00001503sv000010CFsd0000161C*
+ ID_MODEL_FROM_DATABASE=82579V Gigabit Network Connection (LIFEBOOK E752)
+
pci:v00008086d00001507*
ID_MODEL_FROM_DATABASE=Ethernet Express Module X520-P2
pci:v00008086d00001528sv00001170sd00000052*
ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2
+pci:v00008086d00001528sv000015D9sd00000734*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (AOC-STG-I2T)
+
pci:v00008086d00001528sv000017AAsd00001073*
ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (ThinkServer X540-T2 AnyFabric)
pci:v00008086d00001563sv0000193Dsd00001009*
ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (560T-L)
+pci:v00008086d00001563sv0000193Dsd00001011*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (UN-NIC-ETH563T-sL-2P)
+
pci:v00008086d00001563sv00008086sd00000001*
ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T2)
pci:v00008086d0000158Bsv00001137sd000002B4*
ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710 OCP 2.0)
+pci:v00008086d0000158Bsv00001374sd00000230*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71))
+
+pci:v00008086d0000158Bsv00001374sd00000231*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71EU))
+
+pci:v00008086d0000158Bsv00001374sd00000234*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71))
+
+pci:v00008086d0000158Bsv00001374sd00000235*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71EU))
+
+pci:v00008086d0000158Bsv00001374sd00000238*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71L))
+
+pci:v00008086d0000158Bsv00001374sd00000239*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71LEU))
+
+pci:v00008086d0000158Bsv00001374sd0000023A*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71L))
+
+pci:v00008086d0000158Bsv00001374sd0000023B*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71LEU))
+
pci:v00008086d0000158Bsv00001590sd00000000*
ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710-2)
pci:v00008086d000015F0*
ID_MODEL_FROM_DATABASE=JHL7540 Thunderbolt 3 USB Controller [Titan Ridge DD 2018]
+pci:v00008086d000015F4*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (15) I219-LM
+
+pci:v00008086d000015F5*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (15) I219-V
+
pci:v00008086d000015F6*
ID_MODEL_FROM_DATABASE=I210 Gigabit Ethernet Connection
+pci:v00008086d000015F9*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (14) I219-LM
+
+pci:v00008086d000015FA*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (14) I219-V
+
+pci:v00008086d000015FB*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (13) I219-LM
+
+pci:v00008086d000015FC*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (13) I219-V
+
pci:v00008086d000015FF*
ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T
pci:v00008086d00001903sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (XPS 15 9550)
+pci:v00008086d00001903sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (OMEN-17-w001nv)
+
pci:v00008086d00001903sv000017AAsd0000225D*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (ThinkPad T480)
pci:v00008086d00001910sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (XPS 15 9550)
+pci:v00008086d00001910sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (OMEN-17-w001nv)
+
pci:v00008086d00001911*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model
pci:v00008086d0000191Bsv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=HD Graphics 530 (XPS 15 9550)
+pci:v00008086d0000191Bsv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=HD Graphics 530 (OMEN-17-w001nv)
+
pci:v00008086d0000191D*
ID_MODEL_FROM_DATABASE=HD Graphics P530
pci:v00008086d000019E2*
ID_MODEL_FROM_DATABASE=Atom Processor C3000 Series QuickAssist Technology
+pci:v00008086d00001A1C*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (17) I219-LM
+
+pci:v00008086d00001A1D*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (17) I219-V
+
+pci:v00008086d00001A1E*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (16) I219-LM
+
+pci:v00008086d00001A1F*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (16) I219-V
+
pci:v00008086d00001A21*
ID_MODEL_FROM_DATABASE=82840 840 [Carmel] Chipset Host Bridge (Hub A)
pci:v00008086d00001E03sv00001043sd00001517*
ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (Zenbook Prime UX31A)
+pci:v00008086d00001E03sv000010CFsd000016E2*
+ ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (LIFEBOOK E752)
+
pci:v00008086d00001E03sv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (NP300E5C series laptop)
pci:v00008086d00001E10sv00001043sd000084CA*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 1 (P8H77-I Motherboard)
+pci:v00008086d00001E10sv000010CFsd000016E9*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 1 (LIFEBOOK E752)
+
pci:v00008086d00001E10sv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 1 (NP300E5C series laptop)
pci:v00008086d00001E14*
ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 3
+pci:v00008086d00001E14sv000010CFsd000016E9*
+ ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 3 (LIFEBOOK E752)
+
pci:v00008086d00001E16*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 4
pci:v00008086d00001E1E*
ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8
+pci:v00008086d00001E1Esv000010CFsd000016E9*
+ ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8 (LIFEBOOK E752)
+
pci:v00008086d00001E1Esv00001849sd00001E1E*
ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8 (Motherboard)
pci:v00008086d00001E20sv00001043sd00008445*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family High Definition Audio Controller (P8Z77-V LX Motherboard)
+pci:v00008086d00001E20sv000010CFsd00001757*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family High Definition Audio Controller (LIFEBOOK E752)
+
pci:v00008086d00001E20sv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family High Definition Audio Controller (NP300E5C series laptop)
pci:v00008086d00001E22sv00001043sd000084CA*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family SMBus Controller (P8 series motherboard)
+pci:v00008086d00001E22sv000010CFsd000016E6*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family SMBus Controller (LIFEBOOK E752)
+
pci:v00008086d00001E22sv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family SMBus Controller (NP300E5C series laptop)
pci:v00008086d00001E26sv00001043sd000084CA*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #1 (P8 series motherboard)
+pci:v00008086d00001E26sv000010CFsd000016E8*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #1 (LIFEBOOK E752)
+
pci:v00008086d00001E26sv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #1 (NP300E5C series laptop)
pci:v00008086d00001E2Dsv00001043sd000084CA*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #2 (P8 series motherboard)
+pci:v00008086d00001E2Dsv000010CFsd000016E8*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #2 (LIFEBOOK E752)
+
pci:v00008086d00001E2Dsv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #2 (NP300E5C series laptop)
pci:v00008086d00001E31sv00001043sd000084CA*
ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (P8 series motherboard)
+pci:v00008086d00001E31sv000010CFsd000016EE*
+ ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (LIFEBOOK E752)
+
pci:v00008086d00001E31sv000017AAsd000021F3*
ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (ThinkPad T430)
pci:v00008086d00001E3Asv00001043sd000084CA*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family MEI Controller #1 (P8 series motherboard)
+pci:v00008086d00001E3Asv000010CFsd000016EA*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family MEI Controller #1 (LIFEBOOK E752)
+
pci:v00008086d00001E3Asv0000144Dsd0000C652*
ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family MEI Controller #1 (NP300E5C series laptop)
pci:v00008086d00001E59sv00001043sd00001517*
ID_MODEL_FROM_DATABASE=HM76 Express Chipset LPC Controller (Zenbook Prime UX31A)
+pci:v00008086d00001E59sv000010CFsd000016E0*
+ ID_MODEL_FROM_DATABASE=HM76 Express Chipset LPC Controller (LIFEBOOK E752)
+
pci:v00008086d00001E5A*
ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller
pci:v00008086d00002448sv0000103Csd000030C1*
ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Compaq 6910p)
+pci:v00008086d00002448sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (X58LE)
+
pci:v00008086d00002448sv0000104Dsd0000902D*
ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (VAIO VGN-NR120E)
pci:v00008086d000024D2*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1
+pci:v00008086d000024D2sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (ThinkCentre S50)
+
pci:v00008086d000024D2sv00001014sd000002DD*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (eServer xSeries server mainboard)
pci:v00008086d000024D3*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller
+pci:v00008086d000024D3sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (ThinkCentre S50)
+
pci:v00008086d000024D3sv00001014sd000002DD*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (eServer xSeries server mainboard)
pci:v00008086d000024D4*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2
+pci:v00008086d000024D4sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (ThinkCentre S50)
+
pci:v00008086d000024D4sv00001014sd000002DD*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (eServer xSeries server mainboard)
pci:v00008086d000024D5sv0000100Asd0000147B*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Abit IS7-E motherboard)
+pci:v00008086d000024D5sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (ThinkCentre S50)
+
pci:v00008086d000024D5sv00001028sd00000168*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Precision Workstation 670 Mainboard)
pci:v00008086d000024D7*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3
+pci:v00008086d000024D7sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (ThinkCentre S50)
+
pci:v00008086d000024D7sv00001014sd000002ED*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (xSeries server mainboard)
pci:v00008086d000024DB*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller
+pci:v00008086d000024DBsv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (ThinkCentre S50)
+
pci:v00008086d000024DBsv00001014sd000002DD*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (eServer xSeries server mainboard)
pci:v00008086d000024DD*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller
+pci:v00008086d000024DDsv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (ThinkCentre S50)
+
pci:v00008086d000024DDsv00001014sd000002DD*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (eServer xSeries server mainboard)
pci:v00008086d000024DE*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4
+pci:v00008086d000024DEsv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (ThinkCentre S50)
+
pci:v00008086d000024DEsv00001014sd000002ED*
ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (xSeries server mainboard)
pci:v00008086d00002572*
ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller
+pci:v00008086d00002572sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (ThinkCentre S50)
+
pci:v00008086d00002572sv00001028sd0000019D*
ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (Dimension 3000)
ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (Compaq 500B Microtower)
pci:v00008086d000027B8sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
pci:v00008086d000027B8sv0000107Bsd00005048*
ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (E4500)
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (Compaq 500B Microtower)
pci:v00008086d000027C0sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
pci:v00008086d000027C0sv0000107Bsd00005048*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (E4500)
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (A6J-Q008)
pci:v00008086d000027C8sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
pci:v00008086d000027C8sv00001043sd000083AD*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Eee PC 1015PX)
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (A6J-Q008)
pci:v00008086d000027C9sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
pci:v00008086d000027C9sv00001043sd000083AD*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Eee PC 1015PX)
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (A6J-Q008)
pci:v00008086d000027CAsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
pci:v00008086d000027CAsv00001043sd000083AD*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Eee PC 1015PX)
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (A6J-Q008)
pci:v00008086d000027CBsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
pci:v00008086d000027CBsv00001043sd000083AD*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Eee PC 1015PX)
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (A6J-Q008)
pci:v00008086d000027CCsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
pci:v00008086d000027CCsv00001043sd000083AD*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Eee PC 1015PX)
pci:v00008086d000027D8sv00001043sd0000817F*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5LD2-VM Mainboard (Realtek ALC 882 codec))
+pci:v00008086d000027D8sv00001043sd00008249*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5B-MX/WiFi-AP)
+
pci:v00008086d000027D8sv00001043sd00008290*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5KPL-VM Motherboard)
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Compaq 500B Microtower)
pci:v00008086d000027DAsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
pci:v00008086d000027DAsv0000105Bsd00000D7C*
ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (D270S/D250S Motherboard)
ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (A6J-Q008)
pci:v00008086d000027DFsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
pci:v00008086d000027DFsv0000107Bsd00005048*
ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (E4500)
pci:v00008086d00002815sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Presario C700)
+pci:v00008086d00002815sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (X58LE)
+
pci:v00008086d00002815sv0000104Dsd00009005*
ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Vaio VGN-FZ260E)
pci:v00008086d00002829sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Presario C700)
+pci:v00008086d00002829sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (X58LE)
+
pci:v00008086d00002829sv0000104Dsd00009005*
ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Vaio VGN-FZ260E)
pci:v00008086d00002830sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Presario C700)
+pci:v00008086d00002830sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (X58LE)
+
pci:v00008086d00002830sv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (P5B)
pci:v00008086d00002831sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Presario C700)
+pci:v00008086d00002831sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (X58LE)
+
pci:v00008086d00002831sv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (P5B)
pci:v00008086d00002832sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Presario C700)
+pci:v00008086d00002832sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (X58LE)
+
pci:v00008086d00002832sv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (P5B)
pci:v00008086d00002834sv0000103Csd000030CC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Pavilion dv6700)
+pci:v00008086d00002834sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (X58LE)
+
pci:v00008086d00002834sv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (P5B)
pci:v00008086d00002835sv0000103Csd000030CC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Pavilion dv6700)
+pci:v00008086d00002835sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (X58LE)
+
pci:v00008086d00002835sv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (P5B)
pci:v00008086d00002836sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Presario C700)
+pci:v00008086d00002836sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (X58LE)
+
pci:v00008086d00002836sv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (P5B)
pci:v00008086d0000283Asv0000103Csd000030CC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Pavilion dv6700)
+pci:v00008086d0000283Asv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (X58LE)
+
pci:v00008086d0000283Asv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (P5B)
pci:v00008086d0000283Esv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Presario C700)
+pci:v00008086d0000283Esv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (X58LE)
+
pci:v00008086d0000283Esv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (P5B)
pci:v00008086d0000283Fsv0000103Csd000030C1*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (Compaq 6910p)
+pci:v00008086d0000283Fsv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (X58LE)
+
pci:v00008086d0000283Fsv0000104Dsd0000902D*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (VAIO VGN-NR120E)
pci:v00008086d00002841sv0000103Csd000030C1*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (Compaq 6910p)
+pci:v00008086d00002841sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (X58LE)
+
pci:v00008086d00002841sv0000104Dsd0000902D*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (VAIO VGN-NR120E)
pci:v00008086d00002843*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3
+pci:v00008086d00002843sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 (X58LE)
+
pci:v00008086d00002843sv0000104Dsd0000902D*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 (VAIO VGN-NR120E)
pci:v00008086d00002845*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4
+pci:v00008086d00002845sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4 (X58LE)
+
pci:v00008086d00002845sv000017AAsd000020AD*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4 (ThinkPad T61/R61)
pci:v00008086d0000284Bsv00001043sd00001339*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (M51S series)
+pci:v00008086d0000284Bsv00001043sd000017F3*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (X58LE)
+
pci:v00008086d0000284Bsv00001043sd000081EC*
ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (P5B)
pci:v00008086d00002850sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Presario C700)
+pci:v00008086d00002850sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (X58LE)
+
pci:v00008086d00002850sv0000104Dsd00009005*
ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Vaio VGN-FZ260E)
pci:v00008086d00002850sv0000E4BFsd0000CC47*
ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (CCG-RUMBA)
+pci:v00008086d000028C0*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
pci:v00008086d00002912*
ID_MODEL_FROM_DATABASE=82801IH (ICH9DH) LPC Interface Controller
pci:v00008086d00002970*
ID_MODEL_FROM_DATABASE=82946GZ/PL/GL Memory Controller Hub
+pci:v00008086d00002970sv00001043sd0000823B*
+ ID_MODEL_FROM_DATABASE=82946GZ/PL/GL Memory Controller Hub (P5B-MX/WiFi-AP)
+
pci:v00008086d00002971*
ID_MODEL_FROM_DATABASE=82946GZ/PL/GL PCI Express Root Port
pci:v00008086d00002972*
ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller
+pci:v00008086d00002972sv00001043sd0000823B*
+ ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller (P5B-MX/WiFi-AP)
+
pci:v00008086d00002973*
ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller
pci:v00008086d00002A00sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Presario C700)
+pci:v00008086d00002A00sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (X58LE)
+
pci:v00008086d00002A00sv0000104Dsd00009005*
ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Vaio VGN-FZ260E)
pci:v00008086d00002A02sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (Presario C700)
+pci:v00008086d00002A02sv00001043sd000014E2*
+ ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (X58LE)
+
pci:v00008086d00002A02sv0000104Dsd0000902D*
ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (VAIO VGN-NR120E)
pci:v00008086d00002A03sv0000103Csd000030D9*
ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (Presario C700)
+pci:v00008086d00002A03sv00001043sd000014E2*
+ ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (X58LE)
+
pci:v00008086d00002A03sv0000104Dsd0000902D*
ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (VAIO VGN-NR120E)
pci:v00008086d00003B42sv00001028sd0000040B*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (Latitude E6510)
+pci:v00008086d00003B42sv0000103Csd00001521*
+ ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (EliteBook 8540p)
+
pci:v00008086d00003B42sv0000144Dsd0000C06A*
ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (R730 Laptop)
pci:v00008086d00003E30*
ID_MODEL_FROM_DATABASE=8th Gen Core 8-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S]
+pci:v00008086d00003E33*
+ ID_MODEL_FROM_DATABASE=8th/9th Gen Core Processor Host Bridge/DRAM Registers [Coffee Lake]
+
pci:v00008086d00003E34*
ID_MODEL_FROM_DATABASE=Coffee Lake HOST and DRAM Controller
pci:v00008086d0000444E*
ID_MODEL_FROM_DATABASE=Turbo Memory Controller
+pci:v00008086d0000467F*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
+pci:v00008086d00004C3D*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
pci:v00008086d00005001*
ID_MODEL_FROM_DATABASE=LE80578
pci:v00008086d00005904*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+pci:v00008086d00005904sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (Aspire E5-575G)
+
pci:v00008086d00005904sv000017AAsd00002247*
ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (ThinkPad T570)
pci:v00008086d00005916*
ID_MODEL_FROM_DATABASE=HD Graphics 620
+pci:v00008086d00005916sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=HD Graphics 620 (Aspire E5-575G)
+
pci:v00008086d00005916sv000017AAsd00002248*
ID_MODEL_FROM_DATABASE=HD Graphics 620 (ThinkPad T570)
pci:v00008086d000096A1*
ID_MODEL_FROM_DATABASE=Integrated RAID
+pci:v00008086d00009A0B*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
pci:v00008086d00009B41*
ID_MODEL_FROM_DATABASE=UHD Graphics
pci:v00008086d00009D03*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode]
+pci:v00008086d00009D03sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Acer Aspire E5-575G)
+
pci:v00008086d00009D03sv00001028sd000006DC*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Latitude E7470)
pci:v00008086d00009D21*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC
+pci:v00008086d00009D21sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Acer Aspire E5-575G)
+
pci:v00008086d00009D21sv00001028sd000006DC*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Latitude E7470)
pci:v00008086d00009D23*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus
+pci:v00008086d00009D23sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Acer Aspire E5-575G)
+
pci:v00008086d00009D23sv00001028sd000006DC*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Latitude E7470)
pci:v00008086d00009D2F*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller
+pci:v00008086d00009D2Fsv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Acer Aspire E5-575G)
+
pci:v00008086d00009D2Fsv00001028sd000006DC*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Latitude E7470)
pci:v00008086d00009D31*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem
+pci:v00008086d00009D31sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Acer Aspire E5-575G)
+
pci:v00008086d00009D31sv00001028sd000006DC*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Latitude E7470)
pci:v00008086d00009D3A*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1
+pci:v00008086d00009D3Asv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Acer Aspire E5-575G)
+
pci:v00008086d00009D3Asv00001028sd000006DC*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Latitude E7470)
pci:v00008086d00009D58*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller
+pci:v00008086d00009D58sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (Acer Aspire E5-575G)
+
pci:v00008086d00009D58sv000017AAsd00002247*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (ThinkPad T570)
pci:v00008086d00009D60*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0
+pci:v00008086d00009D60sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (Acer Aspire E5-575G)
+
pci:v00008086d00009D60sv00001028sd000006F3*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (Latitude 3570)
pci:v00008086d00009D71*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio
+pci:v00008086d00009D71sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (Acer Aspire E5-575G)
+
pci:v00008086d00009D71sv000017AAsd0000224F*
ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (ThinkPad X1 Carbon 5th Gen)
pci:v00008086d00009DB1*
ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #10
+pci:v00008086d00009DB2*
+ ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #1
+
pci:v00008086d00009DB4*
ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #13
pci:v00008086d00009DE0*
ID_MODEL_FROM_DATABASE=Cannon Point-LP MEI Controller #1
+pci:v00008086d00009DE3*
+ ID_MODEL_FROM_DATABASE=Cannon Point-LP Keyboard and Text (KT) Redirection
+
pci:v00008086d00009DE8*
ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO I2C Controller #0
pci:v00008086d0000A103sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=HM170/QM170 Chipset SATA Controller [AHCI Mode] (XPS 15 9550)
+pci:v00008086d0000A103sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=HM170/QM170 Chipset SATA Controller [AHCI Mode] (OMEN-17-w001nv)
+
pci:v00008086d0000A105*
ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode]
pci:v00008086d0000A121sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Power Management Controller (XPS 15 9550)
+pci:v00008086d0000A121sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Power Management Controller (OMEN-17-w001nv)
+
pci:v00008086d0000A122*
ID_MODEL_FROM_DATABASE=Sunrise Point-H cAVS
pci:v00008086d0000A123sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SMBus (XPS 15 9550)
+pci:v00008086d0000A123sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SMBus (OMEN-17-w001nv)
+
pci:v00008086d0000A124*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SPI Controller
pci:v00008086d0000A12Fsv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller (XPS 15 9550)
+pci:v00008086d0000A12Fsv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller (OMEN-17-w001nv)
+
pci:v00008086d0000A130*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB Device Controller (OTG)
pci:v00008086d0000A131sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Thermal Subsystem (XPS 15 9550)
+pci:v00008086d0000A131sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Thermal Subsystem (OMEN-17-w001nv)
+
pci:v00008086d0000A133*
ID_MODEL_FROM_DATABASE=Sunrise Point-H Northpeak ACPI Function
pci:v00008086d0000A13Asv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #1 (XPS 15 9550)
+pci:v00008086d0000A13Asv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #1 (OMEN-17-w001nv)
+
pci:v00008086d0000A13B*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #2
pci:v00008086d0000A14Esv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=HM170 Chipset LPC/eSPI Controller (XPS 15 9550)
+pci:v00008086d0000A14Esv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=HM170 Chipset LPC/eSPI Controller (OMEN-17-w001nv)
+
pci:v00008086d0000A14F*
ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
pci:v00008086d0000A160sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #0 (XPS 15 9550)
+pci:v00008086d0000A160sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #0 (OMEN-17-w001nv)
+
pci:v00008086d0000A161*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #1
pci:v00008086d0000A170sv00001028sd000006E4*
ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family HD Audio Controller (XPS 15 9550)
+pci:v00008086d0000A170sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family HD Audio Controller (OMEN-17-w001nv)
+
pci:v00008086d0000A171*
ID_MODEL_FROM_DATABASE=CM238 HD Audio Controller
pci:v00008086d0000A324*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH SPI Controller
+pci:v00008086d0000A328*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH Serial IO UART Host Controller
+
pci:v00008086d0000A32C*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #21
pci:v00008086d0000A363*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH Active Management Technology - SOL
+pci:v00008086d0000A364*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH HECI Controller #2
+
pci:v00008086d0000A368*
ID_MODEL_FROM_DATABASE=Cannon Lake PCH Serial IO I2C Controller #0
pci:v00008086d0000F1A6*
ID_MODEL_FROM_DATABASE=SSD Pro 7600p/760p/E 6100p Series
+pci:v00008086d0000F1A6sv00008086sd0000390B*
+ ID_MODEL_FROM_DATABASE=SSD Pro 7600p/760p/E 6100p Series (Intel Corporation SSD Pro 7600p/760p/E 6100p Series [NVM Express])
+
pci:v00008086d0000F1A8*
ID_MODEL_FROM_DATABASE=SSD 660P Series
pci:v00008088*
ID_VENDOR_FROM_DATABASE=Beijing Wangxun Technology Co., Ltd.
+pci:v00008088d00000101*
+ ID_MODEL_FROM_DATABASE=WX1860A2 Gigabit Ethernet Controller
+
+pci:v00008088d00000101sv00008088sd00000201*
+ ID_MODEL_FROM_DATABASE=WX1860A2 Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200T)
+
+pci:v00008088d00000102*
+ ID_MODEL_FROM_DATABASE=WX1860A2S Gigabit Ethernet Controller
+
+pci:v00008088d00000102sv00008088sd00000210*
+ ID_MODEL_FROM_DATABASE=WX1860A2S Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200T-S)
+
+pci:v00008088d00000103*
+ ID_MODEL_FROM_DATABASE=WX1860A4 Gigabit Ethernet Controller
+
+pci:v00008088d00000103sv00008088sd00000401*
+ ID_MODEL_FROM_DATABASE=WX1860A4 Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400T)
+
+pci:v00008088d00000103sv00008088sd00000440*
+ ID_MODEL_FROM_DATABASE=WX1860A4 Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400-OCP)
+
+pci:v00008088d00000104*
+ ID_MODEL_FROM_DATABASE=WX1860A4S Gigabit Ethernet Controller
+
+pci:v00008088d00000104sv00008088sd00000410*
+ ID_MODEL_FROM_DATABASE=WX1860A4S Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400T-S)
+
+pci:v00008088d00000105*
+ ID_MODEL_FROM_DATABASE=WX1860AL2 Gigabit Ethernet Controller
+
+pci:v00008088d00000105sv00008088sd00000202*
+ ID_MODEL_FROM_DATABASE=WX1860AL2 Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200HT)
+
+pci:v00008088d00000106*
+ ID_MODEL_FROM_DATABASE=WX1860AL2S Gigabit Ethernet Controller
+
+pci:v00008088d00000106sv00008088sd00000220*
+ ID_MODEL_FROM_DATABASE=WX1860AL2S Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200HT-S)
+
+pci:v00008088d00000107*
+ ID_MODEL_FROM_DATABASE=WX1860AL4 Gigabit Ethernet Controller
+
+pci:v00008088d00000107sv00008088sd00000402*
+ ID_MODEL_FROM_DATABASE=WX1860AL4 Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400HT)
+
+pci:v00008088d00000108*
+ ID_MODEL_FROM_DATABASE=WX1860AL4S Gigabit Ethernet Controller
+
+pci:v00008088d00000108sv00008088sd00000420*
+ ID_MODEL_FROM_DATABASE=WX1860AL4S Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400HT-S)
+
pci:v00008088d00001001*
ID_MODEL_FROM_DATABASE=Ethernet Controller RP1000 for 10GbE SFP+
pci:v0000D161d0000B410*
ID_MODEL_FROM_DATABASE=Wildcard B410 quad-BRI card
+pci:v0000D209*
+ ID_VENDOR_FROM_DATABASE=Ultimarc
+
+pci:v0000D209d00001500*
+ ID_MODEL_FROM_DATABASE=PAC Drive
+
+pci:v0000D209d000015A2*
+ ID_MODEL_FROM_DATABASE=SpinTrak
+
+pci:v0000D209d00001601*
+ ID_MODEL_FROM_DATABASE=AimTrak
+
pci:v0000D4D4*
ID_VENDOR_FROM_DATABASE=Dy4 Systems Inc
pci:v0000F1D0d0000CFEE*
ID_MODEL_FROM_DATABASE=Xena LS/SD-22-DA/SD-DA
+pci:v0000F1D0d0000DAFE*
+ ID_MODEL_FROM_DATABASE=Corvid 1
+
pci:v0000F1D0d0000DAFF*
ID_MODEL_FROM_DATABASE=KONA LHi
+pci:v0000F1D0d0000DB00*
+ ID_MODEL_FROM_DATABASE=IoExpress
+
pci:v0000F1D0d0000DB01*
ID_MODEL_FROM_DATABASE=Corvid22
+pci:v0000F1D0d0000DB02*
+ ID_MODEL_FROM_DATABASE=Kona 3G
+
+pci:v0000F1D0d0000DB03*
+ ID_MODEL_FROM_DATABASE=Corvid 3G
+
+pci:v0000F1D0d0000DB04*
+ ID_MODEL_FROM_DATABASE=Kona 3G QUAD
+
+pci:v0000F1D0d0000DB05*
+ ID_MODEL_FROM_DATABASE=Kona LHe+
+
+pci:v0000F1D0d0000DB06*
+ ID_MODEL_FROM_DATABASE=IoXT
+
+pci:v0000F1D0d0000DB07*
+ ID_MODEL_FROM_DATABASE=Kona 3G P2P
+
+pci:v0000F1D0d0000DB08*
+ ID_MODEL_FROM_DATABASE=Kona 3G QUAD P2P
+
pci:v0000F1D0d0000DB09*
ID_MODEL_FROM_DATABASE=Corvid 24
+pci:v0000F1D0d0000DB11*
+ ID_MODEL_FROM_DATABASE=T-Tap
+
pci:v0000F1D0d0000DCAF*
ID_MODEL_FROM_DATABASE=Kona HD
pci:v0000F1D0d0000DFEE*
ID_MODEL_FROM_DATABASE=Xena HD-DA
+pci:v0000F1D0d0000EB07*
+ ID_MODEL_FROM_DATABASE=Io4K
+
+pci:v0000F1D0d0000EB0A*
+ ID_MODEL_FROM_DATABASE=Io4K UFC
+
+pci:v0000F1D0d0000EB0B*
+ ID_MODEL_FROM_DATABASE=Kona 4
+
+pci:v0000F1D0d0000EB0C*
+ ID_MODEL_FROM_DATABASE=Kona 4 UFC
+
pci:v0000F1D0d0000EB0D*
ID_MODEL_FROM_DATABASE=Corvid 88
pci:v0000F1D0d0000EB0E*
ID_MODEL_FROM_DATABASE=Corvid 44
+pci:v0000F1D0d0000EB16*
+ ID_MODEL_FROM_DATABASE=Corvid HEVC
+
+pci:v0000F1D0d0000EB16sv000010CFsd00001049*
+ ID_MODEL_FROM_DATABASE=Corvid HEVC (M31)
+
+pci:v0000F1D0d0000EB18*
+ ID_MODEL_FROM_DATABASE=Corvid HB-R
+
+pci:v0000F1D0d0000EB1A*
+ ID_MODEL_FROM_DATABASE=Kona IP 1SFP
+
+pci:v0000F1D0d0000EB1C*
+ ID_MODEL_FROM_DATABASE=Kona IP 2SFP
+
pci:v0000F1D0d0000EB1D*
+ ID_MODEL_FROM_DATABASE=Io4KPlus
+
+pci:v0000F1D0d0000EB1E*
+ ID_MODEL_FROM_DATABASE=IoIP
+
+pci:v0000F1D0d0000EB1F*
ID_MODEL_FROM_DATABASE=Kona 5
+pci:v0000F1D0d0000EB23*
+ ID_MODEL_FROM_DATABASE=Kona 1
+
+pci:v0000F1D0d0000EB24*
+ ID_MODEL_FROM_DATABASE=Kona HDMI
+
+pci:v0000F1D0d0000EB25*
+ ID_MODEL_FROM_DATABASE=Corvid 44 12g
+
pci:v0000F1D0d0000EFAC*
ID_MODEL_FROM_DATABASE=Xena SD-MM/SD-22-MM
usb:v0085p0600*
ID_MODEL_FROM_DATABASE=eBook Reader
+usb:v0102*
+ ID_VENDOR_FROM_DATABASE=miniSTREAK
+
usb:v0105*
ID_VENDOR_FROM_DATABASE=Trust International B.V.
usb:v03EBp800C*
ID_MODEL_FROM_DATABASE=Airspy HF+
+usb:v03EBpFF01*
+ ID_MODEL_FROM_DATABASE=WootingOne
+
usb:v03EBpFF02*
ID_MODEL_FROM_DATABASE=WootingTwo
usb:v03F0pC202*
ID_MODEL_FROM_DATABASE=PhotoSmart 8200 series
+usb:v03F0pC211*
+ ID_MODEL_FROM_DATABASE=Deskjet 2540 series
+
usb:v03F0pC302*
ID_MODEL_FROM_DATABASE=DeskJet D2300
usb:v0403p8372*
ID_MODEL_FROM_DATABASE=FT8U100AX Serial Port
+usb:v0403p8508*
+ ID_MODEL_FROM_DATABASE=Selectronic SP PRO
+
usb:v0403p87D0*
ID_MODEL_FROM_DATABASE=Cressi Dive Computer Interface
usb:v0403p9E90*
ID_MODEL_FROM_DATABASE=Marvell OpenRD Base/Client
+usb:v0403p9F08*
+ ID_MODEL_FROM_DATABASE=CIB-1894 Conclusion SmartLink Box:
+
usb:v0403p9F80*
ID_MODEL_FROM_DATABASE=Ewert Energy Systems CANdapter
usb:v040Bp0A68*
ID_MODEL_FROM_DATABASE=Func MS-3 gaming mouse [WT6573F MCU]
+usb:v040Bp2000*
+ ID_MODEL_FROM_DATABASE=wired Keyboard [Dynex DX-WRK1401]
+
usb:v040Bp2367*
ID_MODEL_FROM_DATABASE=Human Interface Device [HP CalcPad 200 Calculator and Numeric Keypad]
usb:v0451*
ID_VENDOR_FROM_DATABASE=Texas Instruments, Inc.
+usb:v0451p0422*
+ ID_MODEL_FROM_DATABASE=TUSB422 Port Controller with Power Delivery
+
usb:v0451p1234*
ID_MODEL_FROM_DATABASE=Bluetooth Device
usb:v0451p1446*
ID_MODEL_FROM_DATABASE=TUSB2040/2070 Hub
+usb:v0451p16A2*
+ ID_MODEL_FROM_DATABASE=CC Debugger
+
usb:v0451p16A6*
ID_MODEL_FROM_DATABASE=BM-USBD1 BlueRobin RF heart rate sensor receiver
+usb:v0451p16A8*
+ ID_MODEL_FROM_DATABASE=CC2531 ZigBee
+
+usb:v0451p16AE*
+ ID_MODEL_FROM_DATABASE=CC2531 Dongle
+
usb:v0451p2036*
ID_MODEL_FROM_DATABASE=TUSB2036 Hub
usb:v0451pE01C*
ID_MODEL_FROM_DATABASE=Data Collection Sled [Nspire Lab Cradle, Nspire Datatracker Cradle]
+usb:v0451pE01E*
+ ID_MODEL_FROM_DATABASE=Nspire\99 CX Navigator\99 Access Point
+
usb:v0451pE01F*
ID_MODEL_FROM_DATABASE=Python Adapter (firmware install mode)
usb:v0458p0006*
ID_MODEL_FROM_DATABASE=Easy Mouse+
+usb:v0458p0007*
+ ID_MODEL_FROM_DATABASE=Trackbar Emotion
+
usb:v0458p000B*
ID_MODEL_FROM_DATABASE=NetMouse Wheel(P+U)
usb:v045Ep00D1*
ID_MODEL_FROM_DATABASE=Optical Mouse with Tilt Wheel
+usb:v045Ep00D2*
+ ID_MODEL_FROM_DATABASE=Notebook Optical Mouse with Tilt Wheel
+
usb:v045Ep00DA*
ID_MODEL_FROM_DATABASE=eHome Infrared Receiver
usb:v045Ep07FD*
ID_MODEL_FROM_DATABASE=Nano Transceiver 1.1
+usb:v045Ep0810*
+ ID_MODEL_FROM_DATABASE=LifeCam HD-3000
+
usb:v045Ep0900*
ID_MODEL_FROM_DATABASE=Surface Dock Hub
usb:v0461p4D81*
ID_MODEL_FROM_DATABASE=Dell N889 Optical Mouse
+usb:v0461p4D8A*
+ ID_MODEL_FROM_DATABASE=HP Multimedia Keyboard
+
usb:v0461p4D91*
ID_MODEL_FROM_DATABASE=Laser mouse M-D16DL
usb:v0461p4E04*
ID_MODEL_FROM_DATABASE=Lenovo Keyboard KB1021
+usb:v0461p4E6F*
+ ID_MODEL_FROM_DATABASE=Acer Wired Keyboard Model KBAY211
+
usb:v0463*
ID_VENDOR_FROM_DATABASE=MGE UPS Systems
usb:v046Ap010D*
ID_MODEL_FROM_DATABASE=MX-Board 3.0 Keyboard
+usb:v046Ap0180*
+ ID_MODEL_FROM_DATABASE=Strait 3.0
+
usb:v046ApB090*
ID_MODEL_FROM_DATABASE=Keyboard
usb:v046Dp0A5B*
ID_MODEL_FROM_DATABASE=G933 Wireless Headset Dongle
+usb:v046Dp0A5D*
+ ID_MODEL_FROM_DATABASE=G933 Headset Battery Charger
+
usb:v046Dp0A66*
ID_MODEL_FROM_DATABASE=[G533 Wireless Headset Dongle]
usb:v046DpC246*
ID_MODEL_FROM_DATABASE=Gaming Mouse G300
+usb:v046DpC247*
+ ID_MODEL_FROM_DATABASE=G100S Optical Gaming Mouse
+
usb:v046DpC248*
ID_MODEL_FROM_DATABASE=G105 Gaming Keyboard
usb:v046DpC328*
ID_MODEL_FROM_DATABASE=Corded Keyboard K280e
+usb:v046DpC32B*
+ ID_MODEL_FROM_DATABASE=G910 Orion Spark Mechanical Keyboard
+
usb:v046DpC332*
ID_MODEL_FROM_DATABASE=G502 Proteus Spectrum Optical Mouse
usb:v046DpC534*
ID_MODEL_FROM_DATABASE=Unifying Receiver
+usb:v046DpC537*
+ ID_MODEL_FROM_DATABASE=Cordless Mouse Receiver
+
+usb:v046DpC53A*
+ ID_MODEL_FROM_DATABASE=PowerPlay Wireless Charging System
+
usb:v046DpC603*
ID_MODEL_FROM_DATABASE=3Dconnexion Spacemouse Plus XT
usb:v047Fp0101*
ID_MODEL_FROM_DATABASE=Bulk Driver
+usb:v047Fp02EE*
+ ID_MODEL_FROM_DATABASE=BT600
+
usb:v047Fp0301*
ID_MODEL_FROM_DATABASE=Bulk Driver
usb:v047FpC00E*
ID_MODEL_FROM_DATABASE=Blackwire C310 headset
+usb:v047FpC03B*
+ ID_MODEL_FROM_DATABASE=HD1
+
usb:v0480*
ID_VENDOR_FROM_DATABASE=Toshiba America Inc
usb:v0480p0821*
ID_MODEL_FROM_DATABASE=Canvio Advance 2TB model DTC920
+usb:v0480p0900*
+ ID_MODEL_FROM_DATABASE=MQ04UBF100
+
usb:v0480pA006*
ID_MODEL_FROM_DATABASE=External Disk 1.5TB
usb:v0483pA171*
ID_MODEL_FROM_DATABASE=ThermaData WiFi
+usb:v0483pA2E0*
+ ID_MODEL_FROM_DATABASE=BMeasure instrument
+
usb:v0483pDF11*
ID_MODEL_FROM_DATABASE=STM Device in DFU Mode
usb:v048Dp1172*
ID_MODEL_FROM_DATABASE=Flash Drive
+usb:v048Dp1234*
+ ID_MODEL_FROM_DATABASE=Mass storage
+
usb:v048Dp1336*
ID_MODEL_FROM_DATABASE=SD/MMC Cardreader
usb:v04A9p10E3*
ID_MODEL_FROM_DATABASE=PIXMA iX6850 Printer
+usb:v04A9p12FE*
+ ID_MODEL_FROM_DATABASE=Printer in service mode
+
usb:v04A9p1404*
ID_MODEL_FROM_DATABASE=W6400PG
usb:v04A9p180B*
ID_MODEL_FROM_DATABASE=PIXMA MG3000 series
+usb:v04A9p1856*
+ ID_MODEL_FROM_DATABASE=PIXMA TS6250
+
usb:v04A9p1900*
ID_MODEL_FROM_DATABASE=CanoScan LiDE 90
usb:v04A9p2635*
ID_MODEL_FROM_DATABASE=MPC190
+usb:v04A9p2636*
+ ID_MODEL_FROM_DATABASE=LBP3200
+
usb:v04A9p2637*
ID_MODEL_FROM_DATABASE=iR C6800
usb:v04A9p2651*
ID_MODEL_FROM_DATABASE=iR 3100C EUR
+usb:v04A9p2654*
+ ID_MODEL_FROM_DATABASE=LBP3600
+
usb:v04A9p2655*
ID_MODEL_FROM_DATABASE=FP-L170/MF350/L380/L398
usb:v04A9p2656*
ID_MODEL_FROM_DATABASE=iR1510-1670 CAPT Printer
+usb:v04A9p2657*
+ ID_MODEL_FROM_DATABASE=LBP3210
+
usb:v04A9p2659*
ID_MODEL_FROM_DATABASE=MF8100
ID_MODEL_FROM_DATABASE=iR105PLUS
usb:v04A9p266A*
- ID_MODEL_FROM_DATABASE=CAPT Device
+ ID_MODEL_FROM_DATABASE=LBP3000
usb:v04A9p266B*
ID_MODEL_FROM_DATABASE=iR8070
ID_MODEL_FROM_DATABASE=iR 2570C EUR
usb:v04A9p2679*
- ID_MODEL_FROM_DATABASE=CAPT Device
+ ID_MODEL_FROM_DATABASE=LBP5000
usb:v04A9p267A*
ID_MODEL_FROM_DATABASE=iR2016
usb:v04A9p267D*
ID_MODEL_FROM_DATABASE=MF7100 series
+usb:v04A9p267E*
+ ID_MODEL_FROM_DATABASE=LBP3300
+
usb:v04A9p2684*
ID_MODEL_FROM_DATABASE=MF3200 series
usb:v04A9p268A*
ID_MODEL_FROM_DATABASE=LC310/L390/L408S
+usb:v04A9p268B*
+ ID_MODEL_FROM_DATABASE=LBP3500
+
usb:v04A9p268C*
ID_MODEL_FROM_DATABASE=iR C6870
usb:v04A9p2691*
ID_MODEL_FROM_DATABASE=iR7105
+usb:v04A9p26A1*
+ ID_MODEL_FROM_DATABASE=LBP5300
+
usb:v04A9p26A3*
ID_MODEL_FROM_DATABASE=MF4100 series
+usb:v04A9p26A4*
+ ID_MODEL_FROM_DATABASE=LBP5100
+
usb:v04A9p26B0*
ID_MODEL_FROM_DATABASE=MF4600 series
usb:v04A9p26B6*
ID_MODEL_FROM_DATABASE=FAX-L140/L130
+usb:v04A9p26B9*
+ ID_MODEL_FROM_DATABASE=LBP3310
+
+usb:v04A9p26BA*
+ ID_MODEL_FROM_DATABASE=LBP5050
+
usb:v04A9p26DA*
- ID_MODEL_FROM_DATABASE=LBP3010B printer
+ ID_MODEL_FROM_DATABASE=LBP3010/LBP3018/LBP3050
+
+usb:v04A9p26DB*
+ ID_MODEL_FROM_DATABASE=LBP3100/LBP3108/LBP3150
usb:v04A9p26E6*
ID_MODEL_FROM_DATABASE=iR1024
+usb:v04A9p26EA*
+ ID_MODEL_FROM_DATABASE=LBP9100C
+
+usb:v04A9p26EE*
+ ID_MODEL_FROM_DATABASE=MF4320-4350
+
+usb:v04A9p26F1*
+ ID_MODEL_FROM_DATABASE=LBP7200C
+
+usb:v04A9p26FF*
+ ID_MODEL_FROM_DATABASE=LBP6300
+
usb:v04A9p271A*
ID_MODEL_FROM_DATABASE=LBP6000
+usb:v04A9p271B*
+ ID_MODEL_FROM_DATABASE=LBP6200
+
+usb:v04A9p271C*
+ ID_MODEL_FROM_DATABASE=LBP7010C/7018C
+
usb:v04A9p2736*
ID_MODEL_FROM_DATABASE=I-SENSYS MF4550d
usb:v04A9p2737*
ID_MODEL_FROM_DATABASE=MF4410
+usb:v04A9p2771*
+ ID_MODEL_FROM_DATABASE=LBP6020
+
+usb:v04A9p2796*
+ ID_MODEL_FROM_DATABASE=LBP6230/6240
+
usb:v04A9p3041*
ID_MODEL_FROM_DATABASE=PowerShot S10
usb:v04B0p0430*
ID_MODEL_FROM_DATABASE=D7100
+usb:v04B0p0436*
+ ID_MODEL_FROM_DATABASE=D810
+
usb:v04B0p043F*
ID_MODEL_FROM_DATABASE=D5600
usb:v04B4p4624*
ID_MODEL_FROM_DATABASE=DS-Xtreme Flash Card
+usb:v04B4p4717*
+ ID_MODEL_FROM_DATABASE=West Bridge
+
usb:v04B4p5201*
ID_MODEL_FROM_DATABASE=Combi Keyboard-Hub (Hub)
ID_VENDOR_FROM_DATABASE=Nisca Corp.
usb:v09C3*
- ID_VENDOR_FROM_DATABASE=ActivCard, Inc.
+ ID_VENDOR_FROM_DATABASE=HID Global
usb:v09C3p0007*
ID_MODEL_FROM_DATABASE=Reader V2
usb:v09C3p0014*
ID_MODEL_FROM_DATABASE=ActivIdentity ActivKey SIM USB Token
+usb:v09C3p0028*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p0029*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002A*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002B*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002C*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002E*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
usb:v09C4*
ID_VENDOR_FROM_DATABASE=ACTiSYS Corp.
usb:v2237p4161*
ID_MODEL_FROM_DATABASE=eReader White
+usb:v224F*
+ ID_VENDOR_FROM_DATABASE=APDM
+
+usb:v224Fp0001*
+ ID_MODEL_FROM_DATABASE=Access Point
+
+usb:v224Fp0002*
+ ID_MODEL_FROM_DATABASE=Docking Station
+
+usb:v224Fp0004*
+ ID_MODEL_FROM_DATABASE=V2 Opal ACM
+
+usb:v224Fp0005*
+ ID_MODEL_FROM_DATABASE=V2 Opal
+
+usb:v224Fp0006*
+ ID_MODEL_FROM_DATABASE=V2 Docking Station
+
+usb:v224Fp0007*
+ ID_MODEL_FROM_DATABASE=V2 Access Point ACM
+
+usb:v224Fp0008*
+ ID_MODEL_FROM_DATABASE=V2 Access Point
+
usb:v225D*
ID_VENDOR_FROM_DATABASE=Morpho
usb:v2548p1002*
ID_MODEL_FROM_DATABASE=CEC Adapter
+usb:v25B5*
+ ID_VENDOR_FROM_DATABASE=FlatFrog
+
+usb:v25B5p0002*
+ ID_MODEL_FROM_DATABASE=Multitouch 3200
+
usb:v2632*
ID_VENDOR_FROM_DATABASE=TwinMOS
KEYBOARD_KEY_0213=f22
KEYBOARD_KEY_0214=f23
+###########################################################
+# Olimex
+###########################################################
+
+# Teres-I
+evdev:input:b0003v15BAp003C*
+ KEYBOARD_KEY_70066=sleep # Fn+F1
+ KEYBOARD_KEY_700f6=wlan # Fn+F2
+ KEYBOARD_KEY_700c7=f21 # Fn+F3 touchpad toggle
+ KEYBOARD_KEY_7006f=brightnessdown # Fn+F7
+ KEYBOARD_KEY_70070=brightnessup # Fn+F8
+ KEYBOARD_KEY_7006e=switchvideomode # Fn+F9
+
###########################################################
# OLPC
###########################################################
evdev:name:SIPODEV USB Composite Device:dmi:bvn*:bvr*:bd*:svnVIOS:pnLTH17:pvr*
KEYBOARD_KEY_70073=f21 # Touchpad toggle
+###########################################################
+# WeiHeng
+###########################################################
+
+# P325J
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnINET:pnP325J:pvr*
+ KEYBOARD_KEY_76=f21 # Touchpad toggle
+
###########################################################
# Zepto
###########################################################
#
# Allowed properties are:
# ACCEL_MOUNT_MATRIX=<matrix>
+# PROXIMITY_NEAR_LEVEL=<value>
#
# where <matrix> is a mount-matrix in the format specified in the IIO
# subsystem[1]. The default, when unset, is equivalent to:
# ACCEL_MOUNT_MATRIX=1, 0, 0; 0, 1, 0; 0, 0, 1
# eg. the identity matrix.
+# and <value> is an integer value above which an object is considered
+# close by a proximity sensor:
+# PROXIMITY_NEAR_LEVEL=100
#
# [1]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfc57732ad38f93ae6232a3b4e64fd077383a0f1
#
sensor:modalias:acpi:KIOX0009*:dmi:*:svnAcer:pnOneS1003:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
-
+
sensor:modalias:acpi:BOSC0200*:dmi:*:svnAcer*:pnSwitchSW312-31:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnT100TA*
- ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
-
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pnT200TA*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnTP201SA*
- ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
-
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pn*E205SA*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
sensor:modalias:acpi:INVN6500*:dmi:*svn*ASUSTeK*:*pn*TP300LA*
- ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
-
sensor:modalias:acpi:INVN6500*:dmi:*svn*ASUSTeK*:*pn*TP300LD*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP300LJ*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP500LAB*
sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP500LB*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
# Chuwi Hi10 (CWI1515)
-sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrP02A_C106.60E:*:svnDefaultstring:pnDefaultstring:*
+sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrP02A_C106.60E:*:svnDefaultstring:pnDefaultstring:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
# Chuwi Hi10 Pro
sensor:modalias:acpi:KIOX000A*:dmi:*:svncube:pni1-TF:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+# Cube i7
+sensor:modalias:acpi:SMO8500*:dmi:*:svncube:pni7:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
# Cube i7 Stylus
sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni7Stylus:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
sensor:modalias:acpi:NCPE0388*:dmi:*:rnLenovoYOGA510-14IKB:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, 1
-sensor:modalias:acpi:BOSC0200:BOSC0200:dmi:*ThinkPadYoga11e3rdGen*
+sensor:modalias:acpi:BOSC0200*:dmi:*ThinkPadYoga11e3rdGen*
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
# Miix3-1030
#########################################
# Medion
#########################################
+
+# Medion Akoya E1239T MD60568
+sensor:modalias:acpi:KIOX0009*:dmi:*:svnMEDION:pnE1239TMD60568:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
+# Medion Akoya E2212T MD99720
sensor:modalias:acpi:SMO8500*:dmi:*:svnMEDION:pnAkoyaE2212TMD99720:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
sensor:modalias:acpi:KIOX000A*:dmi:*:svnTMAX:pnTM101W610L:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
+# Nuvision Encite Split 11. NES11-C432SSA
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnNuvision:pnNES11:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
#########################################
# Onda
#########################################
<tr class="even"><td>Amazon Corporation</td><td>AMZN</td><td>02/06/2019</td> </tr>
<tr class="odd"><td>ASEM S.p.A.</td><td>ASEM</td><td>04/29/2019</td> </tr>
<tr class="even"><td>Fujitsu Limited</td><td>FUJI</td><td>06/18/2019</td> </tr>
+ <tr class="odd"><td>Phytium Technology Co. Ltd.</td><td>PHYT</td><td>02/14/2020</td> </tr>
</tbody>
</table>
</body>
Komagane Nagano 399-4117\r
JP\r
\r
-F4-EA-B5 (hex) Aerohive Networks Inc.\r
-F4EAB5 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
8C-5B-F0 (hex) ARRIS Group, Inc.\r
8C5BF0 (base 16) ARRIS Group, Inc.\r
6450 Sequence Drive\r
Piscataway NJ 08554\r
US\r
\r
-00-86-9C (hex) Palo Alto Networks\r
-00869C (base 16) Palo Alto Networks\r
- 4401 Great America Parkway\r
- Santa Clara CA 95054\r
- US\r
-\r
94-D9-B3 (hex) TP-LINK TECHNOLOGIES CO.,LTD.\r
94D9B3 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.\r
Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
Rueil Malmaison Cedex hauts de seine 92848\r
FR\r
\r
-08-30-6B (hex) Palo Alto Networks\r
-08306B (base 16) Palo Alto Networks\r
- 4401 Great America Parkway\r
- Santa Clara CA 95054\r
- US\r
-\r
10-CD-B6 (hex) Essential Products, Inc.\r
10CDB6 (base 16) Essential Products, Inc.\r
380 Portage Avenue\r
New Taipei City Taiwan 231\r
TW\r
\r
-00-06-31 (hex) Calix Inc.\r
-000631 (base 16) Calix Inc.\r
- 2777 Orchard Parkway\r
- San Jose CA 95134\r
- US\r
-\r
BC-2F-3D (hex) vivo Mobile Communication Co., Ltd.\r
BC2F3D (base 16) vivo Mobile Communication Co., Ltd.\r
#283,BBK Road\r
Longhua New District, Shenzhen Guangdong 518012\r
CN\r
\r
-54-03-84 (hex) Hangkong Nano IC Technologies Co., Ltd\r
-540384 (base 16) Hangkong Nano IC Technologies Co., Ltd\r
- Rm. 19C, Lockhart Ctr., 301-307 Lockhart Rd., Wan Chai, Hong Kong\r
- Hong Kong Hong Kong 999077\r
- CN\r
-\r
78-C1-A7 (hex) zte corporation\r
78C1A7 (base 16) zte corporation\r
12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
Karlsruhe 76131\r
DE\r
\r
-4C-A0-03 (hex) T-21 Technologies LLC\r
-4CA003 (base 16) T-21 Technologies LLC\r
- 3733 University Blvd West Suite 307B\r
- Jacksonville FL 32217\r
- US\r
-\r
08-3F-BC (hex) zte corporation\r
083FBC (base 16) zte corporation\r
12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
Wuhan City Hubei Province 430074\r
CN\r
\r
-40-47-6A (hex) AG Acquisition Corp. d.b.a. ASTRO Gaming\r
-40476A (base 16) AG Acquisition Corp. d.b.a. ASTRO Gaming\r
- 655 4th St.\r
- San Francisco CA 94107\r
- US\r
-\r
00-1F-A7 (hex) Sony Interactive Entertainment Inc.\r
001FA7 (base 16) Sony Interactive Entertainment Inc.\r
1-7-1 Konan\r
Hangzhou Zhejiang 310052\r
CN\r
\r
-00-19-77 (hex) Aerohive Networks Inc.\r
-001977 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
-08-EA-44 (hex) Aerohive Networks Inc.\r
-08EA44 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
EC-3E-F7 (hex) Juniper Networks\r
EC3EF7 (base 16) Juniper Networks\r
1133 Innovation Way\r
Brentwood Essex 08854\r
GB\r
\r
-CC-4E-24 (hex) Brocade Communications Systems, Inc.\r
-CC4E24 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-E0-52 (hex) Brocade Communications Systems, Inc.\r
-00E052 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
00-14-A4 (hex) Hon Hai Precision Ind. Co.,Ltd.\r
0014A4 (base 16) Hon Hai Precision Ind. Co.,Ltd.\r
Building D21,No.1, East Zone 1st Road\r
Dongguan 523808\r
CN\r
\r
-00-01-0F (hex) Brocade Communications Systems, Inc.\r
-00010F (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-08-00-88 (hex) Brocade Communications Systems, Inc.\r
-080088 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
00-34-FE (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
0034FE (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
Seongnam-si Gyeonggi-do 463-400\r
KR\r
\r
-D4-CF-F9 (hex) Shenzhen Sen5 Technology Co., Ltd.\r
-D4CFF9 (base 16) Shenzhen Sen5 Technology Co., Ltd.\r
- 3F (East), Block D, SDG Infoport, No.2 Kefeng Road,Nanshan District\r
- Shenzhen Guangdong 518057\r
- CN\r
-\r
48-82-44 (hex) Life Fitness / Div. of Brunswick\r
488244 (base 16) Life Fitness / Div. of Brunswick\r
10601 W. Belmont Ave\r
24-33-6C (hex) Private\r
24336C (base 16) Private\r
\r
-B8-7C-F2 (hex) Aerohive Networks Inc.\r
-B87CF2 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
20-25-64 (hex) PEGATRON CORPORATION\r
202564 (base 16) PEGATRON CORPORATION\r
5F No. 76, Ligong St., Beitou District\r
seoul 137-874\r
KR\r
\r
-00-12-58 (hex) Activis Polska\r
-001258 (base 16) Activis Polska\r
- Swierzawska 5\r
- Poznan Wielkopolska 60-321\r
- PL\r
-\r
00-12-50 (hex) Tokyo Aircaft Instrument Co., Ltd.\r
001250 (base 16) Tokyo Aircaft Instrument Co., Ltd.\r
1-35-1, Izumi-Honcho\r
Shenzhen Guangdong 518045\r
CN\r
\r
-98-DF-82 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-98DF82 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
- No.555 Qianmo Road, Binjiang District\r
- Hangzhou Zhejiang 310052\r
- CN\r
-\r
64-66-24 (hex) Sagemcom Broadband SAS\r
646624 (base 16) Sagemcom Broadband SAS\r
250, route de l'Empereur\r
NO.68, Qinghe Middle Street Haidian District, Beijing 100085\r
CN\r
\r
+D8-4D-B9 (hex) Wu Qi Technologies,Inc.\r
+D84DB9 (base 16) Wu Qi Technologies,Inc.\r
+ 14/F, 107 Middle Road, Xiantao Big Data Valley, Yubei District\r
+ Chongqing Chongqing 401120\r
+ CN\r
+\r
+A0-4F-85 (hex) LG Electronics (Mobile Communications)\r
+A04F85 (base 16) LG Electronics (Mobile Communications)\r
+ 60-39, Gasan-dong, Geumcheon-gu\r
+ Seoul 153-801\r
+ KR\r
+\r
+D8-07-B6 (hex) TP-LINK TECHNOLOGIES CO.,LTD.\r
+D807B6 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.\r
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+ Shenzhen Guangdong 518057\r
+ CN\r
+\r
+64-6E-97 (hex) TP-LINK TECHNOLOGIES CO.,LTD.\r
+646E97 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.\r
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+ Shenzhen Guangdong 518057\r
+ CN\r
+\r
+78-50-7C (hex) Juniper Networks\r
+78507C (base 16) Juniper Networks\r
+ 1133 Innovation Way\r
+ Sunnyvale CA 94089\r
+ US\r
+\r
+00-12-58 (hex) TechVoIP Sp z o.o.\r
+001258 (base 16) TechVoIP Sp z o.o.\r
+ Os. Boleslawa Chrobrego 117 \r
+ Poznan Wielkopolska 60-681\r
+ PL\r
+\r
+6C-16-32 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+6C1632 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+2C-1A-01 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+2C1A01 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+34-78-39 (hex) zte corporation\r
+347839 (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+24-16-9D (hex) Cisco Systems, Inc\r
+24169D (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+F4-19-E2 (hex) Volterra\r
+F419E2 (base 16) Volterra\r
+ 2550 Great America Way #350\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+4C-A0-03 (hex) VITEC\r
+4CA003 (base 16) VITEC\r
+ 99 rue Pierre Semard\r
+ Chatillon 92320\r
+ FR\r
+\r
+64-F2-FB (hex) Hangzhou Ezviz Software Co.,Ltd.\r
+64F2FB (base 16) Hangzhou Ezviz Software Co.,Ltd.\r
+ Room 302, Unit B, Building 2, 399 Danfeng Road,Binjiang District\r
+ Hangzhou Zhejiang 310051\r
+ CN\r
+\r
+30-80-9B (hex) New H3C Technologies Co., Ltd\r
+30809B (base 16) New H3C Technologies Co., Ltd\r
+ 466 Changhe Road, Binjiang District\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+74-22-BB (hex) Huawei Device Co., Ltd.\r
+7422BB (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+6C-0D-34 (hex) Nokia\r
+6C0D34 (base 16) Nokia\r
+ 600 March Road\r
+ Kanata Ontario K2K 2E6\r
+ CA\r
+\r
+4C-45-76 (hex) China Mobile(Hangzhou) Information Technology Co.,Ltd.\r
+4C4576 (base 16) China Mobile(Hangzhou) Information Technology Co.,Ltd.\r
+ No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District\r
+ Hangzhou Zhejiang 310000\r
+ CN\r
+\r
+B4-40-A4 (hex) Apple, Inc.\r
+B440A4 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+48-B8-A3 (hex) Apple, Inc.\r
+48B8A3 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+F4-DB-E3 (hex) Apple, Inc.\r
+F4DBE3 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+BC-42-8C (hex) ALPS ELECTRIC CO., LTD.\r
+BC428C (base 16) ALPS ELECTRIC CO., LTD.\r
+ nishida 6-1 \r
+ Kakuda-City Miyagi-Pref 981-1595\r
+ JP\r
+\r
+F0-7C-C7 (hex) Juniper Networks\r
+F07CC7 (base 16) Juniper Networks\r
+ 1133 Innovation Way\r
+ Sunnyvale CA 94089\r
+ US\r
+\r
+D4-5E-EC (hex) Beijing Xiaomi Electronics Co., Ltd.\r
+D45EEC (base 16) Beijing Xiaomi Electronics Co., Ltd.\r
+ Building C, QingHe ShunShiJiaYe Technology Park, #66 ZhuFang Rd, HaiDian District\r
+ Beijing Beijing 10085\r
+ CN\r
+\r
+74-C9-29 (hex) Zhejiang Dahua Technology Co., Ltd.\r
+74C929 (base 16) Zhejiang Dahua Technology Co., Ltd.\r
+ No.1199,Waterfront Road \r
+ Hangzhou Zhejiang 310053\r
+ CN\r
+\r
+D4-CF-F9 (hex) Shenzhen SEI Robotics Co.,Ltd\r
+D4CFF9 (base 16) Shenzhen SEI Robotics Co.,Ltd\r
+ 501,Productivity Building A, #5 Hi-Tech Middle 2nd Road\r
+ Shenzhen Guangdong 518057\r
+ CN\r
+\r
+5C-B2-9E (hex) ASCO Power Technologies\r
+5CB29E (base 16) ASCO Power Technologies\r
+ 160 Park Avenue\r
+ Florham Park NJ 07932\r
+ US\r
+\r
+9C-C9-EB (hex) NETGEAR\r
+9CC9EB (base 16) NETGEAR\r
+ 350 East Plumeria Drive\r
+ San Jose CA 95134\r
+ US\r
+\r
+94-CC-04 (hex) IEEE Registration Authority\r
+94CC04 (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
+\r
+90-EC-77 (hex) silicom\r
+90EC77 (base 16) silicom\r
+ 14 Atir-Yeda St/\r
+ Kfar-Sava Israel 44000\r
+ IL\r
+\r
+88-C3-97 (hex) Beijing Xiaomi Mobile Software Co., Ltd\r
+88C397 (base 16) Beijing Xiaomi Mobile Software Co., Ltd\r
+ The Rainbow City Office Building, 68 Qinghe Middle Street Haidian District\r
+ Beijing Beijing 100085\r
+ CN\r
+\r
+F0-F6-C1 (hex) Sonos, Inc.\r
+F0F6C1 (base 16) Sonos, Inc.\r
+ 614 Chapala St\r
+ Santa Barbara CA 93101\r
+ US\r
+\r
+60-68-4E (hex) Samsung Electronics Co.,Ltd\r
+60684E (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+80-20-FD (hex) Samsung Electronics Co.,Ltd\r
+8020FD (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+B4-CE-40 (hex) Samsung Electronics Co.,Ltd\r
+B4CE40 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+54-03-84 (hex) Hongkong Nano IC Technologies Co., Ltd\r
+540384 (base 16) Hongkong Nano IC Technologies Co., Ltd\r
+ Rm. 19C, Lockhart Ctr., 301-307 Lockhart Rd., Wan Chai, Hong Kong\r
+ Hong Kong Hong Kong 999077\r
+ CN\r
+\r
+04-BD-BF (hex) Samsung Electronics Co.,Ltd\r
+04BDBF (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+BC-7A-BF (hex) Samsung Electronics Co.,Ltd\r
+BC7ABF (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+B4-09-31 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+B40931 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+94-E7-EA (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+94E7EA (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+94-E4-BA (hex) Huawei Device Co., Ltd.\r
+94E4BA (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+34-71-46 (hex) Huawei Device Co., Ltd.\r
+347146 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+2C-C5-46 (hex) Huawei Device Co., Ltd.\r
+2CC546 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+0C-83-9A (hex) Huawei Device Co., Ltd.\r
+0C839A (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+E0-E0-FC (hex) Huawei Device Co., Ltd.\r
+E0E0FC (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+E0-D4-E8 (hex) Intel Corporate\r
+E0D4E8 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+30-50-75 (hex) GN Audio A/S\r
+305075 (base 16) GN Audio A/S\r
+ Lautrupbjerg 7\r
+ Ballerup DK-2750\r
+ DK\r
+\r
+F4-B7-8D (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+F4B78D (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+A4-16-E7 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+A416E7 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+30-B9-B0 (hex) Intracom Asia Co., Ltd\r
+30B9B0 (base 16) Intracom Asia Co., Ltd\r
+ 4F., No77, Sec. 1, Xintai 5th Rd., Xizhi Dist.\r
+ New Taipei City Taiwan 221\r
+ TW\r
+\r
+0C-35-FE (hex) Fiberhome Telecommunication Technologies Co.,LTD\r
+0C35FE (base 16) Fiberhome Telecommunication Technologies Co.,LTD\r
+ No.5 DongXin Road\r
+ Wuhan Hubei 430074\r
+ CN\r
+\r
+8C-83-DF (hex) Nokia\r
+8C83DF (base 16) Nokia\r
+ 600 March Road\r
+ Kanata Ontario K2K 2E6\r
+ CA\r
+\r
+AC-4B-1E (hex) Integri-Sys.Com LLC\r
+AC4B1E (base 16) Integri-Sys.Com LLC\r
+ 9130 South Dadeland Bvld. Suite 1509\r
+ Miami FL 33156\r
+ US\r
+\r
+B0-E4-D5 (hex) Google, Inc.\r
+B0E4D5 (base 16) Google, Inc.\r
+ 1600 Amphitheatre Parkway\r
+ Mountain View CA 94043\r
+ US\r
+\r
+D4-DA-CD (hex) BSkyB Ltd\r
+D4DACD (base 16) BSkyB Ltd\r
+ 130 Kings Road\r
+ Brentwood Essex 08854\r
+ GB\r
+\r
+68-69-CA (hex) Hitachi, Ltd.\r
+6869CA (base 16) Hitachi, Ltd.\r
+ 27-18, Minami Oi 6-chome, Shinagawa-ku\r
+ Tokyo 140-8572\r
+ JP\r
+\r
+AC-4A-56 (hex) Cisco Systems, Inc\r
+AC4A56 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+F4-EA-B5 (hex) Extreme Networks, Inc.\r
+F4EAB5 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+00-19-77 (hex) Extreme Networks, Inc.\r
+001977 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+08-EA-44 (hex) Extreme Networks, Inc.\r
+08EA44 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+B0-B5-C3 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+B0B5C3 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+ NO.18 HAIBIN ROAD,\r
+ DONG GUAN GUANG DONG 523860\r
+ CN\r
+\r
+70-4A-0E (hex) AMPAK Technology,Inc.\r
+704A0E (base 16) AMPAK Technology,Inc.\r
+ 3F, No.15-1 Zhonghua Road, Hsinchu Industrail Park, Hukou,\r
+ Hsinchu Hsinchu,Taiwan R.O.C. 30352\r
+ TW\r
+\r
+08-30-6B (hex) Palo Alto Networks\r
+08306B (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+00-86-9C (hex) Palo Alto Networks\r
+00869C (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+4C-B9-11 (hex) Raisecom Technology CO.,LTD\r
+4CB911 (base 16) Raisecom Technology CO.,LTD\r
+ No. 11, East Area, No. 10 Block, East Xibeiwang Road\r
+ Beijing 100094\r
+ CN\r
+\r
+40-47-6A (hex) Astro Gaming\r
+40476A (base 16) Astro Gaming\r
+ 340 Bryant St., Suite 101\r
+ San Francisco CA 94107\r
+ US\r
+\r
+4C-CE-2D (hex) Danlaw Inc\r
+4CCE2D (base 16) Danlaw Inc\r
+ 23700 research Dr.\r
+ Farmington Hills MI 48335\r
+ US\r
+\r
+BC-0F-9A (hex) D-Link International\r
+BC0F9A (base 16) D-Link International\r
+ 1 Internal Business Park, #03-12,The Synergy, Singapore\r
+ Singapore Singapore 609917\r
+ SG\r
+\r
+30-B2-37 (hex) GD Midea Air-Conditioning Equipment Co.,Ltd.\r
+30B237 (base 16) GD Midea Air-Conditioning Equipment Co.,Ltd.\r
+ Midea Global Innovation Center,Beijiao Town,Shunde\r
+ Foshan Guangdong 528311\r
+ CN\r
+\r
+BC-5A-56 (hex) Cisco Systems, Inc\r
+BC5A56 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+B8-7C-F2 (hex) Extreme Networks, Inc.\r
+B87CF2 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+88-2B-94 (hex) MADOKA SYSTEM Co.,Ltd.\r
+882B94 (base 16) MADOKA SYSTEM Co.,Ltd.\r
+ 2-105 Hanasakidai Moriyama-ku\r
+ Nagoya 463-0808\r
+ JP\r
+\r
+7C-EF-61 (hex) STR Elektronik Josef Schlechtinger GmbH\r
+7CEF61 (base 16) STR Elektronik Josef Schlechtinger GmbH\r
+ Auf dem Ohl 9\r
+ Wenden 57482\r
+ DE\r
+\r
+64-A9-65 (hex) Linkflow Co., Ltd.\r
+64A965 (base 16) Linkflow Co., Ltd.\r
+ 54, Nonhyeon-ro 2-gil, Gangnam-gu\r
+ Seoul 06313\r
+ KR\r
+\r
+24-62-CE (hex) Aruba, a Hewlett Packard Enterprise Company\r
+2462CE (base 16) Aruba, a Hewlett Packard Enterprise Company\r
+ 3333 Scott Blvd\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+68-E2-09 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+68E209 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+40-05-89 (hex) T-Mobile, USA\r
+400589 (base 16) T-Mobile, USA\r
+ 3625 132nd Ave SE\r
+ BELLEVUE WA 98006\r
+ US\r
+\r
+C0-9B-F4 (hex) IEEE Registration Authority\r
+C09BF4 (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
+\r
+F4-30-8B (hex) Xiaomi Communications Co Ltd\r
+F4308B (base 16) Xiaomi Communications Co Ltd\r
+ The Rainbow City of China Resources\r
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085\r
+ CN\r
+\r
+DC-6B-12 (hex) worldcns inc.\r
+DC6B12 (base 16) worldcns inc.\r
+ 174, Namjo-ro 1-gil, Jocheon-eup\r
+ Jeju-si Jeju-do 63335\r
+ KR\r
+\r
+70-03-9F (hex) Espressif Inc.\r
+70039F (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+A0-DE-0F (hex) Huawei Device Co., Ltd.\r
+A0DE0F (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+F4-87-C5 (hex) Huawei Device Co., Ltd.\r
+F487C5 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+08-00-88 (hex) Brocade Communications Systems LLC\r
+080088 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-01-0F (hex) Brocade Communications Systems LLC\r
+00010F (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+08-B0-55 (hex) ASKEY COMPUTER CORP\r
+08B055 (base 16) ASKEY COMPUTER CORP\r
+ 10F,No.119,JIANKANG RD,ZHONGHE DIST\r
+ NEW TAIPEI TAIWAN 23585\r
+ TW\r
+\r
+04-5F-B9 (hex) Cisco Systems, Inc\r
+045FB9 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+CC-4E-24 (hex) Brocade Communications Systems LLC\r
+CC4E24 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-E0-52 (hex) Brocade Communications Systems LLC\r
+00E052 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+98-DF-82 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+98DF82 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+3C-F6-52 (hex) zte corporation\r
+3CF652 (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+5C-0F-FB (hex) Amino Communications Ltd\r
+5C0FFB (base 16) Amino Communications Ltd\r
+ 1010 Cambourne Business Park\r
+ Cambourne Cambs CB23 6DP\r
+ GB\r
+\r
+74-58-F3 (hex) Amazon Technologies Inc.\r
+7458F3 (base 16) Amazon Technologies Inc.\r
+ P.O Box 8102\r
+ Reno NV 89507\r
+ US\r
+\r
+00-06-31 (hex) Calix Inc.\r
+000631 (base 16) Calix Inc.\r
+ 2777 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+08-AA-55 (hex) Motorola Mobility LLC, a Lenovo Company\r
+08AA55 (base 16) Motorola Mobility LLC, a Lenovo Company\r
+ 222 West Merchandise Mart Plaza\r
+ Chicago IL 60654\r
+ US\r
+\r
+C8-8B-E8 (hex) Masimo Corporation\r
+C88BE8 (base 16) Masimo Corporation\r
+ 40 Parker\r
+ Irvine CA 92618\r
+ US\r
+\r
+40-40-28 (hex) ZIV\r
+404028 (base 16) ZIV\r
+ Polígono Parque Tecnológico, 210\r
+ ZAMUDIO VIZCAYA 48170\r
+ ES\r
+\r
+54-21-9D (hex) Samsung Electronics Co.,Ltd\r
+54219D (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+A8-05-77 (hex) Netlist, Inc.\r
+A80577 (base 16) Netlist, Inc.\r
+ 175 Technology\r
+ Irvine CA 92618\r
+ US\r
+\r
+F0-41-C6 (hex) Heat Tech Company, Ltd.\r
+F041C6 (base 16) Heat Tech Company, Ltd.\r
+ 221A, Tikhookeanskaya st.\r
+ Khabarovsk 680033\r
+ RU\r
+\r
+E4-3A-65 (hex) MofiNetwork Inc\r
+E43A65 (base 16) MofiNetwork Inc\r
+ 11 Boynton Cir\r
+ Markham Ontario L6C 1A8\r
+ CA\r
+\r
9C-FF-C2 (hex) AVI Systems GmbH\r
9CFFC2 (base 16) AVI Systems GmbH\r
Dr. Franz Wilhelmstraße 2A\r
Shenzhen Guangdong 518057\r
CN\r
\r
-F8-4D-FC (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-F84DFC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
- No.555,qianmo road\r
- Hangzhou Zhejiang 310052\r
- CN\r
-\r
50-2B-98 (hex) Es-tech International\r
502B98 (base 16) Es-tech International\r
228-70, Saneop-ro 155beon-gil, Gwonseon-gu, Suwon-si, Gyeonggi-do, Korea\r
Kwai Chung New Territories 000\r
CN\r
\r
-BC-F3-10 (hex) Aerohive Networks Inc.\r
-BCF310 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
B4-1D-2B (hex) Shenzhen YOUHUA Technology Co., Ltd\r
B41D2B (base 16) Shenzhen YOUHUA Technology Co., Ltd\r
Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District\r
Dongguan 523808\r
CN\r
\r
-08-66-1F (hex) Palo Alto Networks\r
-08661F (base 16) Palo Alto Networks\r
- 4401 Great America Parkway\r
- Santa Clara CA 95054\r
- US\r
-\r
64-FB-50 (hex) RoomReady/Zdi, Inc.\r
64FB50 (base 16) RoomReady/Zdi, Inc.\r
2200 N. Main Street\r
wuhan hubei 430000\r
CN\r
\r
-0C-B9-37 (hex) Ubee Interactive Co., Limited\r
-0CB937 (base 16) Ubee Interactive Co., Limited\r
- Room 1607 Dominion Centre, 43 Queen’s Road East\r
- Wanchai Hong Kong 302\r
- HK\r
-\r
EC-8A-C7 (hex) Fiberhome Telecommunication Technologies Co.,LTD\r
EC8AC7 (base 16) Fiberhome Telecommunication Technologies Co.,LTD\r
No.5 DongXin Road\r
Dongguan 523808\r
CN\r
\r
-28-80-A2 (hex) Novatel Wireless Solutions, Inc.\r
-2880A2 (base 16) Novatel Wireless Solutions, Inc.\r
- 9605 Scranton Road Suite 200\r
- San Diego 92121\r
- US\r
-\r
78-81-02 (hex) Sercomm Corporation.\r
788102 (base 16) Sercomm Corporation.\r
3F,No.81,Yu-Yih Rd.,Chu-Nan Chen\r
New Taipei City Taiwan 235\r
TW\r
\r
-14-5E-45 (hex) Kaleao Limited\r
-145E45 (base 16) Kaleao Limited\r
- Sheraton House, Castle Park\r
- Cambridge CAMBRIDGESHIRE CB3 0AX\r
- GB\r
-\r
54-D7-51 (hex) Proximus\r
54D751 (base 16) Proximus\r
Bld du Roi Albert II 27\r
DONG GUAN GUANG DONG 523860\r
CN\r
\r
-00-15-FF (hex) Novatel Wireless Solutions, Inc.\r
-0015FF (base 16) Novatel Wireless Solutions, Inc.\r
- 9605 Scranton Road Suite 200\r
- San Diego 92121\r
- US\r
-\r
D4-6E-0E (hex) TP-LINK TECHNOLOGIES CO.,LTD.\r
D46E0E (base 16) TP-LINK TECHNOLOGIES CO.,LTD.\r
Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
Zory 44-240\r
PL\r
\r
-D4-1D-71 (hex) Palo Alto Networks\r
-D41D71 (base 16) Palo Alto Networks\r
- 4401 Great America Parkway\r
- Santa Clara CA 95054\r
- US\r
-\r
00-21-D1 (hex) Samsung Electronics Co.,Ltd\r
0021D1 (base 16) Samsung Electronics Co.,Ltd\r
#94-1, Imsoo-Dong\r
Shenzhen Guangdong Province 518112\r
CN\r
\r
-DC-35-F1 (hex) Positivo Informática SA.\r
-DC35F1 (base 16) Positivo Informática SA.\r
- Rua João Bettega, 5200\r
- Curitiba Paraná 80730000\r
- BR\r
-\r
00-24-FF (hex) QLogic Corporation\r
0024FF (base 16) QLogic Corporation\r
26650 Aliso Viejo Parkway\r
Dongguan Guangdong 523808 \r
CN\r
\r
-C8-66-5D (hex) Aerohive Networks Inc.\r
-C8665D (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
C8-47-8C (hex) Beken Corporation\r
C8478C (base 16) Beken Corporation\r
Building 41, Capital of Tech Leaders, 1387 Zhangdong Road, Zhangjiang High-Tech Park, Pudong New District\r
Shanghai 201203\r
CN\r
\r
-D8-54-A2 (hex) Aerohive Networks Inc.\r
-D854A2 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
9C-EF-D5 (hex) Panda Wireless, Inc.\r
9CEFD5 (base 16) Panda Wireless, Inc.\r
15559 Union Ave, Suite 300\r
Sunnyvale CA 94089\r
US\r
\r
-E0-1C-41 (hex) Aerohive Networks Inc.\r
-E01C41 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
CC-A4-62 (hex) ARRIS Group, Inc.\r
CCA462 (base 16) ARRIS Group, Inc.\r
6450 Sequence Drive\r
Shenzhen Guangdong 518057\r
CN\r
\r
-D8-1F-CC (hex) Brocade Communications Systems, Inc.\r
-D81FCC (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
F4-31-C3 (hex) Apple, Inc.\r
F431C3 (base 16) Apple, Inc.\r
1 Infinite Loop\r
Chongqing Chongqing 401332\r
CN\r
\r
-00-27-F8 (hex) Brocade Communications Systems, Inc.\r
-0027F8 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-74-8E-F8 (hex) Brocade Communications Systems, Inc.\r
-748EF8 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-24-38 (hex) Brocade Communications Systems, Inc.\r
-002438 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-14-C9 (hex) Brocade Communications Systems, Inc.\r
-0014C9 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
14-E6-E4 (hex) TP-LINK TECHNOLOGIES CO.,LTD.\r
14E6E4 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.\r
Building 24 (floors 1,3,4,5) and 28 (floors1-4) Central Science and Technology Park,Shennan Rd, Nanshan,\r
Chongqing Chongqing 401332\r
CN\r
\r
-50-EB-1A (hex) Brocade Communications Systems, Inc.\r
-50EB1A (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
00-18-82 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
001882 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
F1-20,Section F,Huawei Base,Bantian, Longgang District\r
Norrköping 60221\r
SE\r
\r
-30-B2-16 (hex) ABB AG - Power Grids - Grid Automation\r
-30B216 (base 16) ABB AG - Power Grids - Grid Automation\r
- Kallstadter Strasse 1\r
- Mannheim 68309\r
- DE\r
-\r
80-20-AF (hex) Trade FIDES, a.s.\r
8020AF (base 16) Trade FIDES, a.s.\r
Dornych 57\r
Zirndorf 90513\r
DE\r
\r
-00-26-74 (hex) Electronic Solutions, Inc.\r
-002674 (base 16) Electronic Solutions, Inc.\r
- 1355 Horizon Avenue\r
- Lafayette CO 80026\r
- US\r
-\r
00-26-73 (hex) RICOH COMPANY,LTD.\r
002673 (base 16) RICOH COMPANY,LTD.\r
810 Shimoimaizumi\r
Austin TX 78730\r
US\r
\r
-00-09-91 (hex) GE Fanuc Automation Manufacturing, Inc.\r
-000991 (base 16) GE Fanuc Automation Manufacturing, Inc.\r
- Route 606 & Route 29N\r
- Charlottesville Virginia 22911\r
- US\r
-\r
00-09-67 (hex) Tachyon, Inc\r
000967 (base 16) Tachyon, Inc\r
9339 Carroll Park Drive\r
Taipei Taiwan 112\r
TW\r
\r
-84-9A-40 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-849A40 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
- No.555 Qianmo Road, Binjiang District\r
- Hangzhou Zhejiang 310052\r
- CN\r
-\r
04-B1-A1 (hex) Samsung Electronics Co.,Ltd\r
04B1A1 (base 16) Samsung Electronics Co.,Ltd\r
#94-1, Imsoo-Dong\r
Qingdao Shandong 266510\r
CN\r
\r
-48-7B-5E (hex) Social Mobile\r
-487B5E (base 16) Social Mobile\r
- 16400 NW 2nd AVE suite 201, #201\r
- Miami FL 33169\r
- US\r
-\r
44-A5-6E (hex) NETGEAR\r
44A56E (base 16) NETGEAR\r
350 East Plumeria Drive\r
Padova 35129\r
IT\r
\r
-48-4E-FC (hex) ARRIS Group, Inc.\r
-484EFC (base 16) ARRIS Group, Inc.\r
- 6450 Sequence Drive\r
- San Diego CA 92121\r
- US\r
-\r
2C-57-41 (hex) Cisco Systems, Inc\r
2C5741 (base 16) Cisco Systems, Inc\r
80 West Tasman Drive\r
San Jose CA 94568\r
US\r
\r
+A8-4D-4A (hex) Audiowise Technology Inc.\r
+A84D4A (base 16) Audiowise Technology Inc.\r
+ 2F, No 1-1, Innovation RD I, Hsinchu Science Park\r
+ Hsincu Taiwan 30076\r
+ TW\r
+\r
+78-94-E8 (hex) Radio Bridge\r
+7894E8 (base 16) Radio Bridge\r
+ 8601 73rd Ave N, Suite 38\r
+ Brooklyn Park MN 55428\r
+ US\r
+\r
+48-4E-FC (hex) ARRIS Group, Inc.\r
+484EFC (base 16) ARRIS Group, Inc.\r
+ 6450 Sequence Drive\r
+ San Diego CA 92121\r
+ US\r
+\r
B0-B3-53 (hex) IEEE Registration Authority\r
B0B353 (base 16) IEEE Registration Authority\r
445 Hoes Lane\r
Piscataway NJ 08554\r
US\r
\r
-A8-4D-4A (hex) Audiowise Technology Inc.\r
-A84D4A (base 16) Audiowise Technology Inc.\r
- 2F, No 1-1, Innovation RD I, Hsinchu Science Park\r
- Hsincu Taiwan 30076\r
+54-7F-BC (hex) iodyne\r
+547FBC (base 16) iodyne\r
+ 35 Miller Ave #175\r
+ Mill Valley CA 94941\r
+ US\r
+\r
+7C-DF-A1 (hex) Espressif Inc.\r
+7CDFA1 (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+98-00-6A (hex) zte corporation\r
+98006A (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+00-26-74 (hex) Hunter Douglas\r
+002674 (base 16) Hunter Douglas\r
+ One Duette Way\r
+ Broomfield CO 80020\r
+ US\r
+\r
+1C-97-C5 (hex) Ynomia Pty Ltd\r
+1C97C5 (base 16) Ynomia Pty Ltd\r
+ 153 Tooronga Rd\r
+ Glen Iris 3146\r
+ AU\r
+\r
+5C-C1-D7 (hex) Samsung Electronics Co.,Ltd\r
+5CC1D7 (base 16) Samsung Electronics Co.,Ltd\r
+ 129, Samsung-ro, Youngtongl-Gu\r
+ Suwon Gyeonggi-Do 16677\r
+ KR\r
+\r
+38-01-46 (hex) SHENZHEN BILIAN ELECTRONIC CO.,LTD\r
+380146 (base 16) SHENZHEN BILIAN ELECTRONIC CO.,LTD\r
+ NO.268? Fuqian Rd, Jutang community, Guanlan Town, Longhua New district\r
+ shenzhen guangdong 518000\r
+ CN\r
+\r
+88-96-55 (hex) Zitte corporation\r
+889655 (base 16) Zitte corporation\r
+ 4F Yokohama Kusunoki-cho Building,4-7 Kusunoki-cho,Nishi-ku\r
+ Yokohama Kanagawa 2200003\r
+ JP\r
+\r
+F4-A4-D6 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+F4A4D6 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+FC-E1-4F (hex) BRK Brands, Inc.\r
+FCE14F (base 16) BRK Brands, Inc.\r
+ 3901 Liberty Street\r
+ Aurora IL 60504\r
+ US\r
+\r
+74-B6-B6 (hex) eero inc.\r
+74B6B6 (base 16) eero inc.\r
+ 660 3rd Street\r
+ San Francisco CA 94107\r
+ US\r
+\r
+EC-97-B2 (hex) SUMEC Machinery & Electric Co.,Ltd.\r
+EC97B2 (base 16) SUMEC Machinery & Electric Co.,Ltd.\r
+ 198# ChangJiang Road, XuanWu District, 17F, SUMEC Building\r
+ Nanjing JiangSu 210018\r
+ CN\r
+\r
+28-FA-7A (hex) Zhejiang Tmall Technology Co., Ltd.\r
+28FA7A (base 16) Zhejiang Tmall Technology Co., Ltd.\r
+ Ali Center,No.3331 Keyuan South RD (Shenzhen bay), Nanshan District, Shenzhen Guangdong province\r
+ Shenzhen GuangDong 518000\r
+ CN\r
+\r
+84-2E-14 (hex) Silicon Laboratories\r
+842E14 (base 16) Silicon Laboratories\r
+ 7000 W. William Cannon Dr.\r
+ Austin TX 78735\r
+ US\r
+\r
+10-05-E1 (hex) Nokia\r
+1005E1 (base 16) Nokia\r
+ 600 March Road\r
+ Kanata Ontario K2K 2E6\r
+ CA\r
+\r
+08-F4-58 (hex) Huawei Device Co., Ltd.\r
+08F458 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+5C-BA-2C (hex) Hewlett Packard Enterprise\r
+5CBA2C (base 16) Hewlett Packard Enterprise\r
+ 8000 Foothills Blvd.\r
+ Roseville CA 95747\r
+ US\r
+\r
+34-37-94 (hex) Hamee Corp.\r
+343794 (base 16) Hamee Corp.\r
+ Square O2 2-12-10 Sakae-cho\r
+ Odawara Kanagawa 250-0011\r
+ JP\r
+\r
+40-EC-99 (hex) Intel Corporate\r
+40EC99 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+6C-D9-4C (hex) vivo Mobile Communication Co., Ltd.\r
+6CD94C (base 16) vivo Mobile Communication Co., Ltd.\r
+ #283,BBK Road\r
+ Wusha,Chang'An DongGuan City,Guangdong, 523860\r
+ CN\r
+\r
+EC-31-6D (hex) Hansgrohe\r
+EC316D (base 16) Hansgrohe\r
+ Auestraße 5-9\r
+ Schiltach 77761\r
+ DE\r
+\r
+BC-54-2F (hex) Intel Corporate\r
+BC542F (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+44-10-FE (hex) Huizhou Foryou General Electronics Co., Ltd.\r
+4410FE (base 16) Huizhou Foryou General Electronics Co., Ltd.\r
+ North Shangxia Road, Dongjiang Hi-tech Industry Park\r
+ Huizhou Guangdong 516000\r
+ CN\r
+\r
+7C-AB-60 (hex) Apple, Inc.\r
+7CAB60 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+44-C6-5D (hex) Apple, Inc.\r
+44C65D (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+18-7E-B9 (hex) Apple, Inc.\r
+187EB9 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+4C-A6-4D (hex) Cisco Systems, Inc\r
+4CA64D (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+CC-7F-75 (hex) Cisco Systems, Inc\r
+CC7F75 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+20-E8-74 (hex) Apple, Inc.\r
+20E874 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+D0-3F-AA (hex) Apple, Inc.\r
+D03FAA (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+0C-B9-37 (hex) Ubee Interactive Co., Limited\r
+0CB937 (base 16) Ubee Interactive Co., Limited\r
+ Flat/RM 1202, 12/F, AT Tower \r
+ North Point Hong Kong 180\r
+ HK\r
+\r
+D4-DC-09 (hex) Mist Systems, Inc.\r
+D4DC09 (base 16) Mist Systems, Inc.\r
+ 1601 South De Anza Blvd, Suite 248\r
+ Cupertino CA 95014\r
+ US\r
+\r
+00-88-BA (hex) NC&C\r
+0088BA (base 16) NC&C\r
+ Gurogu\r
+ Seoul 08390\r
+ KR\r
+\r
+F4-73-35 (hex) Logitech Far East\r
+F47335 (base 16) Logitech Far East\r
+ #2 Creation Rd. 4,\r
+ Hsinchu 300\r
TW\r
\r
+90-AD-FC (hex) Telechips, Inc.\r
+90ADFC (base 16) Telechips, Inc.\r
+ 19F~23F,Luther Bldg.42, Olympic-ro 35da-gil, Songpa-gu,\r
+ Seoul Seoul 05510\r
+ KR\r
+\r
+5C-A6-2D (hex) Cisco Systems, Inc\r
+5CA62D (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+40-2B-69 (hex) Kumho Electric Inc.\r
+402B69 (base 16) Kumho Electric Inc.\r
+ 309, Bongmu-ro, Namsa-myeon, Cheoin-gu\r
+ Yongin-si Gyeonggi-do 17118\r
+ KR\r
+\r
+E8-E9-8E (hex) SOLAR controls s.r.o.\r
+E8E98E (base 16) SOLAR controls s.r.o.\r
+ Brojova 25\r
+ Plzen 32600\r
+ CZ\r
+\r
+64-F6-BB (hex) Fibocom Wireless Inc.\r
+64F6BB (base 16) Fibocom Wireless Inc.\r
+ 5/F,TowerA,Technology Building 2,1057 Nanhai Blvd, Nanshan\r
+ Shenzhen 518000 Guangdong\r
+ CN\r
+\r
+BC-16-95 (hex) zte corporation\r
+BC1695 (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+DC-35-F1 (hex) Positivo Tecnologia S.A.\r
+DC35F1 (base 16) Positivo Tecnologia S.A.\r
+ João Bettega, 5200\r
+ Curitiba Paraná 81350-000\r
+ BR\r
+\r
+A4-08-01 (hex) Amazon Technologies Inc.\r
+A40801 (base 16) Amazon Technologies Inc.\r
+ P.O Box 8102\r
+ Reno NV 89507\r
+ US\r
+\r
+AC-1E-D0 (hex) Temic Automotive Philippines Inc.\r
+AC1ED0 (base 16) Temic Automotive Philippines Inc.\r
+ Bagsakan Road, FTI estate\r
+ Taguig 1630\r
+ PH\r
+\r
+2C-EA-7F (hex) Dell Inc.\r
+2CEA7F (base 16) Dell Inc.\r
+ One Dell Way\r
+ Round Rock TX 78682\r
+ US\r
+\r
+34-51-80 (hex) TCL King Electrical Appliances (Huizhou) Co., Ltd\r
+345180 (base 16) TCL King Electrical Appliances (Huizhou) Co., Ltd\r
+ 10F, TCL Multimedia Building, TCL International E City, No.1001 Zhongshanyuan Rd., Nanshan District\r
+ Shenzhen Guangdong 518052\r
+ CN\r
+\r
+A4-CF-D2 (hex) Ubee Interactive Co., Limited\r
+A4CFD2 (base 16) Ubee Interactive Co., Limited\r
+ Flat/RM 1202, 12/F, AT Tower, 180 Electric Road\r
+ North Point 00000\r
+ HK\r
+\r
+A8-A0-97 (hex) ScioTeq bvba\r
+A8A097 (base 16) ScioTeq bvba\r
+ President Kennedypark 35A\r
+ Kortrijk 8500\r
+ BE\r
+\r
+08-6B-D1 (hex) Shenzhen SuperElectron Technology Co.,Ltd.\r
+086BD1 (base 16) Shenzhen SuperElectron Technology Co.,Ltd.\r
+ 1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city\r
+ Shenzhen Guangdong 518000\r
+ CN\r
+\r
+AC-3A-67 (hex) Cisco Systems, Inc\r
+AC3A67 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+3C-B5-3D (hex) HUNAN GOKE MICROELECTRONICS CO.,LTD\r
+3CB53D (base 16) HUNAN GOKE MICROELECTRONICS CO.,LTD\r
+ No.9, East 10th Road(South), Xingsha, Changsha\r
+ Changsha HUNAN 410131 \r
+ CN\r
+\r
+98-0D-51 (hex) Huawei Device Co., Ltd.\r
+980D51 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+00-AD-D5 (hex) Huawei Device Co., Ltd.\r
+00ADD5 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+90-5D-7C (hex) New H3C Technologies Co., Ltd\r
+905D7C (base 16) New H3C Technologies Co., Ltd\r
+ 466 Changhe Road, Binjiang District\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+98-FA-A7 (hex) INNONET\r
+98FAA7 (base 16) INNONET\r
+ C-417, Munjeong Hyundai Knowledge Industry Center, Beobwon-ro 11-gil-7\r
+ Songpa-gu Seoul 05836\r
+ KR\r
+\r
+48-7B-5E (hex) SMT TELECOMM HK\r
+487B5E (base 16) SMT TELECOMM HK\r
+ Unit C 8/F Charmhill Centre 50 Hillwood RD.\r
+ Tsim Sha Tsui Kowloon 999077\r
+ HK\r
+\r
+B8-E3-B1 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+B8E3B1 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+00-B7-A8 (hex) Heinzinger electronic GmbH\r
+00B7A8 (base 16) Heinzinger electronic GmbH\r
+ Anton Jakob Str.4\r
+ Rosenheim BY 83026\r
+ DE\r
+\r
+34-CF-F6 (hex) Intel Corporate\r
+34CFF6 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+EC-79-49 (hex) FUJITSU LIMITED\r
+EC7949 (base 16) FUJITSU LIMITED\r
+ 403, Kosugi-cho 1-chome, Nakahara-ku\r
+ Kawasaki Kanagawa 211-0063\r
+ JP\r
+\r
+D4-D2-D6 (hex) FN-LINK TECHNOLOGY LIMITED\r
+D4D2D6 (base 16) FN-LINK TECHNOLOGY LIMITED\r
+ A Building,HuiXin industial park,No 31, YongHe road, Fuyong town, Bao'an District\r
+ SHENZHEN GUANGDONG 518100\r
+ CN\r
+\r
+10-50-72 (hex) Sercomm Corporation.\r
+105072 (base 16) Sercomm Corporation.\r
+ 3F,No.81,Yu-Yih Rd.,Chu-Nan Chen\r
+ Miao-Lih Hsuan 115\r
+ TW\r
+\r
+90-0A-84 (hex) Mellanox Technologies, Inc.\r
+900A84 (base 16) Mellanox Technologies, Inc.\r
+ 350 Oakmead Parkway, Suite 100 \r
+ Sunnyvale CA 94085\r
+ US\r
+\r
+AC-4A-67 (hex) Cisco Systems, Inc\r
+AC4A67 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+70-CA-97 (hex) Ruckus Wireless\r
+70CA97 (base 16) Ruckus Wireless\r
+ 350 West Java Drive\r
+ Sunnyvale CA 94089\r
+ US\r
+\r
+30-B2-16 (hex) ABB Power Grids Germany AG – Grid Automation\r
+30B216 (base 16) ABB Power Grids Germany AG – Grid Automation\r
+ Kallstadter Strasse 1\r
+ Mannheim 68309\r
+ DE\r
+\r
+00-09-91 (hex) Intelligent Platforms, LLC.\r
+000991 (base 16) Intelligent Platforms, LLC.\r
+ 2500 Austin Drive\r
+ Charlottesville VA 22911\r
+ US\r
+\r
+C8-66-5D (hex) Extreme Networks, Inc.\r
+C8665D (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+E0-1C-41 (hex) Extreme Networks, Inc.\r
+E01C41 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+D8-54-A2 (hex) Extreme Networks, Inc.\r
+D854A2 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+08-66-1F (hex) Palo Alto Networks\r
+08661F (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+BC-F3-10 (hex) Extreme Networks, Inc.\r
+BCF310 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+28-54-71 (hex) Huawei Device Co., Ltd.\r
+285471 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+B8-8E-82 (hex) Huawei Device Co., Ltd.\r
+B88E82 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+DC-33-3D (hex) Huawei Device Co., Ltd.\r
+DC333D (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+D8-78-7F (hex) Ubee Interactive Co., Limited\r
+D8787F (base 16) Ubee Interactive Co., Limited\r
+ Flat/RM 1202, 12/F, AT Tower, 180 Electric Road\r
+ North Point 00000\r
+ HK\r
+\r
+D4-1D-71 (hex) Palo Alto Networks\r
+D41D71 (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+08-87-C6 (hex) INGRAM MICRO SERVICES\r
+0887C6 (base 16) INGRAM MICRO SERVICES\r
+ 100 CHEMIN DE BAILLOT\r
+ MONTAUBAN 82000\r
+ FR\r
+\r
+78-57-73 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+785773 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+AC-60-89 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+AC6089 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+84-3E-92 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+843E92 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+70-8C-B6 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+708CB6 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+50-46-4A (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+50464A (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+C4-A4-02 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+C4A402 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+00-15-FF (hex) Novatel Wireless Solutions, Inc.\r
+0015FF (base 16) Novatel Wireless Solutions, Inc.\r
+ 9710 Scranton Rd., Suite 200\r
+ San Diego CA 92121\r
+ US\r
+\r
+28-80-A2 (hex) Novatel Wireless Solutions, Inc.\r
+2880A2 (base 16) Novatel Wireless Solutions, Inc.\r
+ 9710 Scranton Rd., Suite 200\r
+ San Diego CA 92121\r
+ US\r
+\r
+78-2B-46 (hex) Intel Corporate\r
+782B46 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+68-33-2C (hex) KENSTEL NETWORKS LIMITED\r
+68332C (base 16) KENSTEL NETWORKS LIMITED\r
+ 34D SECTOR 57 HSIIDC INDUSTRIAL AREA PHASE 4\r
+ KUNDLI HARYANA 131028\r
+ IN\r
+\r
+14-5E-45 (hex) Bamboo Systems Group\r
+145E45 (base 16) Bamboo Systems Group\r
+ Sheraton House, Castle Park\r
+ Cambridge CAMBRIDGESHIRE CB3 0AX\r
+ GB\r
+\r
+70-61-7B (hex) Cisco Systems, Inc\r
+70617B (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+98-59-49 (hex) LUXOTTICA GROUP S.P.A.\r
+985949 (base 16) LUXOTTICA GROUP S.P.A.\r
+ Piazzale Cadrona, 3\r
+ Milano MI 20132\r
+ IT\r
+\r
+AC-67-B2 (hex) Espressif Inc.\r
+AC67B2 (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+9C-BD-6E (hex) DERA Co., Ltd\r
+9CBD6E (base 16) DERA Co., Ltd\r
+ Zhichun road NO7 Building B Room1203 Haidian District\r
+ Beijing 100191\r
+ CN\r
+\r
+4C-33-29 (hex) Sweroam\r
+4C3329 (base 16) Sweroam\r
+ Stortorget 16\r
+ Orebro N/A 70211\r
+ SE\r
+\r
+64-E1-72 (hex) Shenzhen Qihoo Intelligent Technology Co.,Ltd\r
+64E172 (base 16) Shenzhen Qihoo Intelligent Technology Co.,Ltd\r
+ Room 201,Block A.No.1,Qianwan Road1 Qianhai Shenzhen-HONGKONG Cooperation Zone\r
+ Shenzhen Guangdong 5181000\r
+ CN\r
+\r
+48-8F-5A (hex) Routerboard.com\r
+488F5A (base 16) Routerboard.com\r
+ Mikrotikls SIA\r
+ Riga Riga LV1009\r
+ LV\r
+\r
+10-06-45 (hex) Sagemcom Broadband SAS\r
+100645 (base 16) Sagemcom Broadband SAS\r
+ 250, route de l'Empereur\r
+ Rueil Malmaison Cedex hauts de seine 92848\r
+ FR\r
+\r
+00-14-C9 (hex) Brocade Communications Systems LLC\r
+0014C9 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-24-38 (hex) Brocade Communications Systems LLC\r
+002438 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+74-8E-F8 (hex) Brocade Communications Systems LLC\r
+748EF8 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-27-F8 (hex) Brocade Communications Systems LLC\r
+0027F8 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+50-EB-1A (hex) Brocade Communications Systems LLC\r
+50EB1A (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+F8-4D-FC (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+F84DFC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+84-9A-40 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+849A40 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+D8-1F-CC (hex) Brocade Communications Systems LLC\r
+D81FCC (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+C4-89-ED (hex) Solid Optics EU N.V.\r
+C489ED (base 16) Solid Optics EU N.V.\r
+ De Huchtstraat 35\r
+ Almere Flevoland 1327 EC\r
+ NL\r
+\r
+60-F4-3A (hex) Edifier International\r
+60F43A (base 16) Edifier International\r
+ Suit 2207, 22nd floor, Tower II, Lippo centre, 89 Queensway\r
+ Hong Kong 070\r
+ CN\r
+\r
+58-A8-7B (hex) Fitbit, Inc.\r
+58A87B (base 16) Fitbit, Inc.\r
+ 199 Fremont Street, 14th Fl\r
+ San Francisco CA 94105\r
+ US\r
+\r
+5C-6B-D7 (hex) Foshan VIOMI Electric Appliance Technology Co. Ltd.\r
+5C6BD7 (base 16) Foshan VIOMI Electric Appliance Technology Co. Ltd.\r
+ No.2 North Xinxi Fourth Road, Xiashi Village Committee,Lunjiao Sub-district Office, Shunde District\r
+ Foshan Guandong 528308\r
+ CN\r
+\r
+18-48-CA (hex) Murata Manufacturing Co., Ltd.\r
+1848CA (base 16) Murata Manufacturing Co., Ltd.\r
+ 1-10-1, Higashikotari\r
+ Nagaokakyo-shi Kyoto 617-8555\r
+ JP\r
+\r
+90-EE-C7 (hex) Samsung Electronics Co.,Ltd\r
+90EEC7 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+10-29-AB (hex) Samsung Electronics Co.,Ltd\r
+1029AB (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+18-4E-CB (hex) Samsung Electronics Co.,Ltd\r
+184ECB (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+80-22-A7 (hex) NEC Platforms, Ltd.\r
+8022A7 (base 16) NEC Platforms, Ltd.\r
+ 2-3 Kandatsukasamachi\r
+ Chiyodaku Tokyo 101-8532\r
+ JP\r
+\r
+64-E8-81 (hex) Aruba, a Hewlett Packard Enterprise Company\r
+64E881 (base 16) Aruba, a Hewlett Packard Enterprise Company\r
+ 3333 Scott Blvd\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+98-9D-5D (hex) Technicolor CH USA Inc.\r
+989D5D (base 16) Technicolor CH USA Inc.\r
+ 5030 Sugarloaf Parkway Bldg 6 \r
+ Lawrenceville GA 30044\r
+ US\r
+\r
+5C-23-16 (hex) Squirrels Research Labs LLC\r
+5C2316 (base 16) Squirrels Research Labs LLC\r
+ 8050 Freedom Ave NW Suite B\r
+ North Canton OH 44720\r
+ US\r
+\r
+04-21-44 (hex) Sunitec Enterprise Co.,Ltd\r
+042144 (base 16) Sunitec Enterprise Co.,Ltd\r
+ 3F.,No.98-1,Mincyuan Rd.Sindian City\r
+ Taipei County 231 231141\r
+ CN\r
+\r
+A0-27-B6 (hex) Samsung Electronics Co.,Ltd\r
+A027B6 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+10-39-17 (hex) Samsung Electronics Co.,Ltd\r
+103917 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+98-80-EE (hex) Samsung Electronics Co.,Ltd\r
+9880EE (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+CC-0D-F2 (hex) Motorola Mobility LLC, a Lenovo Company\r
+CC0DF2 (base 16) Motorola Mobility LLC, a Lenovo Company\r
+ 222 West Merchandise Mart Plaza\r
+ Chicago IL 60654\r
+ US\r
+\r
+94-BF-94 (hex) Juniper Networks\r
+94BF94 (base 16) Juniper Networks\r
+ 1133 Innovation Way\r
+ Sunnyvale CA 94089\r
+ US\r
+\r
+94-43-4D (hex) Ciena Corporation\r
+94434D (base 16) Ciena Corporation\r
+ 7035 Ridge Road\r
+ Hanover MD 21076\r
+ US\r
+\r
84-80-94 (hex) Meter, Inc.\r
848094 (base 16) Meter, Inc.\r
148 Townsend St\r
Wuhan Hubei 430074\r
CN\r
\r
-BC-BA-C2 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-BCBAC2 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
- No.555 Qianmo Road, Binjiang District\r
- Hangzhou Zhejiang 310052\r
- CN\r
-\r
44-D5-F2 (hex) IEEE Registration Authority\r
44D5F2 (base 16) IEEE Registration Authority\r
445 Hoes Lane\r
Dallas TX 75243\r
US\r
\r
-98-8B-0A (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-988B0A (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
- No.555 Qianmo Road, Binjiang District\r
- Hangzhou Zhejiang 310052\r
- CN\r
-\r
A4-97-5C (hex) VTech Telecommunications Ltd.\r
A4975C (base 16) VTech Telecommunications Ltd.\r
23/F,Tai Ping Industrial Centre ,Block 1\r
Hsichu Taiwan 300\r
TW\r
\r
-98-ED-5C (hex) Tesla Motors, Inc\r
-98ED5C (base 16) Tesla Motors, Inc\r
- 3500 Deer Creek Road\r
- Palo Alto CA 94304\r
- US\r
-\r
78-70-52 (hex) Welotec GmbH\r
787052 (base 16) Welotec GmbH\r
zum Hagenbach 7\r
SAN DIEGO CA 92121\r
US\r
\r
-34-85-84 (hex) Aerohive Networks Inc.\r
-348584 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
00-19-C2 (hex) Equustek Solutions, Inc.\r
0019C2 (base 16) Equustek Solutions, Inc.\r
#286 - 5489 Byrne Road,\r
Aliso Viejo CA 92656\r
US\r
\r
-E4-A7-49 (hex) Palo Alto Networks\r
-E4A749 (base 16) Palo Alto Networks\r
- 4401 Great America Pkwy\r
- Santa Clara CA 95054\r
- US\r
-\r
14-5B-E1 (hex) nyantec GmbH\r
145BE1 (base 16) nyantec GmbH\r
Europaplatz 2\r
Shanghai Shanghai 201203\r
CN\r
\r
-20-6C-8A (hex) Aerohive Networks Inc.\r
-206C8A (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
3C-FA-43 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
3CFA43 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
San Diego CA 92121\r
US\r
\r
-40-18-B1 (hex) Aerohive Networks Inc.\r
-4018B1 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
8C-09-F4 (hex) ARRIS Group, Inc.\r
8C09F4 (base 16) ARRIS Group, Inc.\r
6450 Sequence Drive\r
Brentwood Essex 08854\r
GB\r
\r
-C4-F5-7C (hex) Brocade Communications Systems, Inc.\r
-C4F57C (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
14-B9-68 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
14B968 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
Dongguan 523808\r
CN\r
\r
-00-12-F2 (hex) Brocade Communications Systems, Inc.\r
-0012F2 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-05-1E (hex) Brocade Communications Systems, Inc.\r
-00051E (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
08-3E-8E (hex) Hon Hai Precision Ind. Co.,Ltd.\r
083E8E (base 16) Hon Hai Precision Ind. Co.,Ltd.\r
Building D21,No.1, East Zone 1st Road\r
Kunshan Jiangsu 215300\r
CN\r
\r
-88-5B-DD (hex) Aerohive Networks Inc.\r
-885BDD (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
08-74-F6 (hex) Winterhalter Gastronom GmbH\r
0874F6 (base 16) Winterhalter Gastronom GmbH\r
Tettnanger Straße 72\r
Bradenton Florida 34203\r
US\r
\r
-70-38-11 (hex) Invensys Rail\r
-703811 (base 16) Invensys Rail\r
- PO Box 79\r
- Chippenham Wiltshire SN15 1JD\r
- GB\r
-\r
08-81-F4 (hex) Juniper Networks\r
0881F4 (base 16) Juniper Networks\r
1133 Innovation Way\r
Portadown Armagh BT63 5LF\r
GB\r
\r
-60-54-64 (hex) Eyedro Green Solutions Inc.\r
-605464 (base 16) Eyedro Green Solutions Inc.\r
- 151 Charles St W\r
- Kitchener Ontario N2G1H6\r
- CA\r
-\r
C8-FE-30 (hex) Bejing DAYO Mobile Communication Technology Ltd.\r
C8FE30 (base 16) Bejing DAYO Mobile Communication Technology Ltd.\r
Room 712, ULO Park Building No. 601E\r
WARRENDALE PA 15086\r
US\r
\r
-00-E0-EC (hex) CELESTICA INC.\r
-00E0EC (base 16) CELESTICA INC.\r
- 844 DON MILLS ROAD\r
- NORTH YORK ONTARIO M3C 1V7 \r
- CA\r
-\r
00-E0-6C (hex) Ultra Electronics Command & Control Systems\r
00E06C (base 16) Ultra Electronics Command & Control Systems\r
Knaves Beech Business Centre\r
CHATSWORTH CA 91311\r
US\r
\r
-00-20-A6 (hex) Proxim Wireless\r
-0020A6 (base 16) Proxim Wireless\r
- 1561 Buckeye Drive\r
- Milpitas CA 95035\r
- US\r
-\r
00-20-2C (hex) WELLTRONIX CO., LTD.\r
00202C (base 16) WELLTRONIX CO., LTD.\r
3F, NO. 36-1, HWANG HSI STREET\r
ISSY LES MOULINEAUX 92130\r
FR\r
\r
-80-30-49 (hex) Liteon Technology Corporation\r
-803049 (base 16) Liteon Technology Corporation\r
- 4F, 90, Chien 1 Road\r
- New Taipei City Taiwan 23585\r
+2C-F0-5D (hex) Micro-Star INTL CO., LTD.\r
+2CF05D (base 16) Micro-Star INTL CO., LTD.\r
+ No.69, Lide St.,\r
+ New Taipei City Taiwan 235\r
TW\r
\r
+94-3B-B0 (hex) New H3C Technologies Co., Ltd\r
+943BB0 (base 16) New H3C Technologies Co., Ltd\r
+ 466 Changhe Road, Binjiang District\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
90-43-E2 (hex) Cornami, Inc\r
9043E2 (base 16) Cornami, Inc\r
300 Orchard City Dr, Suite 131\r
Campbell CA 95008\r
US\r
\r
+80-30-49 (hex) Liteon Technology Corporation\r
+803049 (base 16) Liteon Technology Corporation\r
+ 4F, 90, Chien 1 Road\r
+ New Taipei City Taiwan 23585\r
+ TW\r
+\r
+E8-49-43 (hex) YUGE Information technology Co. Ltd\r
+E84943 (base 16) YUGE Information technology Co. Ltd\r
+ Room 303, Building No. 6, ShengRong Rd. 88, Pudong, Shanghai\r
+ Shanghai 201203\r
+ CN\r
+\r
+50-14-08 (hex) AiNET\r
+501408 (base 16) AiNET\r
+ 11700 MONTGOMERY RD\r
+ BELTSVILLE MD 20705-1159\r
+ US\r
+\r
28-9A-F7 (hex) ADVA Optical Networking Ltd.\r
289AF7 (base 16) ADVA Optical Networking Ltd.\r
ADVAntage House\r
York YO30 4RY\r
GB\r
\r
-E8-49-43 (hex) YUGE Information technology Co. Ltd\r
-E84943 (base 16) YUGE Information technology Co. Ltd\r
- Room 303, Building No. 6, ShengRong Rd. 88, Pudong, Shanghai\r
+B0-B1-94 (hex) zte corporation\r
+B0B194 (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+10-C3-AB (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+10C3AB (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+28-11-EC (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+2811EC (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+E4-26-86 (hex) DWnet Technologies(Suzhou) Corporation\r
+E42686 (base 16) DWnet Technologies(Suzhou) Corporation\r
+ No.8,Tangzhuang Road, Suzhou Industrial Park, Jiangsu, China\r
+ Suzhou 21500\r
+ CN\r
+\r
+00-69-2D (hex) Sunnovo International Limited\r
+00692D (base 16) Sunnovo International Limited\r
+ 1717 Haitai Building\r
+ Beijing Beijing 100083\r
+ CN\r
+\r
+38-EB-47 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+38EB47 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+0C-37-96 (hex) BIZLINK TECHNOLOGY, INC.\r
+0C3796 (base 16) BIZLINK TECHNOLOGY, INC.\r
+ 47211 BAYSIDE PARKWAY\r
+ Fremont CA 94538\r
+ US\r
+\r
+F4-03-2A (hex) Amazon Technologies Inc.\r
+F4032A (base 16) Amazon Technologies Inc.\r
+ P.O Box 8102\r
+ Reno NV 89507\r
+ US\r
+\r
+14-77-40 (hex) Huawei Device Co., Ltd.\r
+147740 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+B4-15-7E (hex) Celona Inc.\r
+B4157E (base 16) Celona Inc.\r
+ 10061, Bubb Road Suite 300\r
+ Cupertino CA 95014\r
+ US\r
+\r
+18-D9-8F (hex) Huawei Device Co., Ltd.\r
+18D98F (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+64-5E-2C (hex) IRay Technology Co., Ltd.\r
+645E2C (base 16) IRay Technology Co., Ltd.\r
+ 11th Guiyang St.,\r
+ Yantai Shandong 264000\r
+ CN\r
+\r
+00-E0-EC (hex) CELESTICA INC.\r
+00E0EC (base 16) CELESTICA INC.\r
+ 1900-5140 Yonge Street PO Box 42 \r
+ Toronto Ontario M2N 6L7\r
+ CA\r
+\r
+70-38-11 (hex) Siemens Mobility Limited\r
+703811 (base 16) Siemens Mobility Limited\r
+ 17 Langley Park Way\r
+ Chippenham Wiltshire SN15 1GG\r
+ GB\r
+\r
+64-62-66 (hex) IEEE Registration Authority\r
+646266 (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
+\r
+48-7A-F6 (hex) NCS ELECTRICAL SDN BHD\r
+487AF6 (base 16) NCS ELECTRICAL SDN BHD\r
+ NO. 20, 22, 24, 26, JALAN 1/3, RAWANG INTEGRATED INDUSTRIAL PARK, 48000 Rawang Selangor, MALAYSIA\r
+ RAWANG SELANGOR 48000\r
+ MY\r
+\r
+48-6E-70 (hex) Zhejiang Tmall Technology Co., Ltd.\r
+486E70 (base 16) Zhejiang Tmall Technology Co., Ltd.\r
+ Ali Center,No.3331 Keyuan South RD (Shenzhen bay), Nanshan District, Shenzhen Guangdong province\r
+ Shenzhen GuangDong 518000\r
+ CN\r
+\r
+60-1D-9D (hex) Sichuan AI-Link Technology Co., Ltd.\r
+601D9D (base 16) Sichuan AI-Link Technology Co., Ltd.\r
+ Anzhou, Industrial Park\r
+ Mianyang Sichuan 622650\r
+ CN\r
+\r
+D8-5F-77 (hex) Telink Semiconductor (Shanghai) Co., Ltd.\r
+D85F77 (base 16) Telink Semiconductor (Shanghai) Co., Ltd.\r
+ No. 1500 Zuchongzhi Rd, Building #3\r
Shanghai 201203\r
CN\r
\r
-2C-F0-5D (hex) Micro-Star INTL CO., LTD.\r
-2CF05D (base 16) Micro-Star INTL CO., LTD.\r
- No.69, Lide St.,\r
- New Taipei City Taiwan 235\r
+2C-97-ED (hex) Sony Imaging Products & Solutions Inc.\r
+2C97ED (base 16) Sony Imaging Products & Solutions Inc.\r
+ konan 1-7-1\r
+ minato-ku Tokyo 108-0075\r
+ JP\r
+\r
+20-82-6A (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+20826A (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+ NO.18 HAIBIN ROAD,\r
+ DONG GUAN GUANG DONG 523860\r
+ CN\r
+\r
+B8-90-47 (hex) Apple, Inc.\r
+B89047 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+90-9C-4A (hex) Apple, Inc.\r
+909C4A (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+90-8C-43 (hex) Apple, Inc.\r
+908C43 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+18-8A-6A (hex) AVPro Global Hldgs\r
+188A6A (base 16) AVPro Global Hldgs\r
+ 2222 E 52nd Steeet N\r
+ Sioux Falls SD 57104\r
+ US\r
+\r
+3C-7D-0A (hex) Apple, Inc.\r
+3C7D0A (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+D4-8A-39 (hex) Samsung Electronics Co.,Ltd\r
+D48A39 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+E4-F3-C4 (hex) Samsung Electronics Co.,Ltd\r
+E4F3C4 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+9C-2F-4E (hex) zte corporation\r
+9C2F4E (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+F0-10-90 (hex) New H3C Technologies Co., Ltd\r
+F01090 (base 16) New H3C Technologies Co., Ltd\r
+ 466 Changhe Road, Binjiang District\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+08-BF-A0 (hex) Samsung Electronics Co.,Ltd\r
+08BFA0 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+70-1F-3C (hex) Samsung Electronics Co.,Ltd\r
+701F3C (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+20-50-E7 (hex) AMPAK Technology,Inc.\r
+2050E7 (base 16) AMPAK Technology,Inc.\r
+ 3F, No.15-1 Zhonghua Road, Hsinchu Industrail Park, Hukou,\r
+ Hsinchu Hsinchu,Taiwan R.O.C. 30352\r
TW\r
\r
-94-3B-B0 (hex) New H3C Technologies Co., Ltd\r
-943BB0 (base 16) New H3C Technologies Co., Ltd\r
+D8-A8-C8 (hex) zte corporation\r
+D8A8C8 (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+38-22-E2 (hex) HP Inc.\r
+3822E2 (base 16) HP Inc.\r
+ 10300 Energy Dr\r
+ Spring TX 77389\r
+ US\r
+\r
+08-C0-EB (hex) Mellanox Technologies, Inc.\r
+08C0EB (base 16) Mellanox Technologies, Inc.\r
+ 350 Oakmead Parkway, Suite 100 \r
+ Sunnyvale CA 94085\r
+ US\r
+\r
+E4-E1-12 (hex) Texas Instruments\r
+E4E112 (base 16) Texas Instruments\r
+ 12500 TI Blvd\r
+ Dallas TX 75243\r
+ US\r
+\r
+34-14-B5 (hex) Texas Instruments\r
+3414B5 (base 16) Texas Instruments\r
+ 12500 TI Blvd\r
+ Dallas TX 75243\r
+ US\r
+\r
+D0-03-EB (hex) Texas Instruments\r
+D003EB (base 16) Texas Instruments\r
+ 12500 TI Blvd\r
+ Dallas TX 75243\r
+ US\r
+\r
+94-DB-56 (hex) Sony Home Entertainment&Sound Products Inc\r
+94DB56 (base 16) Sony Home Entertainment&Sound Products Inc\r
+ Sony City Osaki 2-10-1 Osaki Shinagawa-ku\r
+ Tokyo Japan 141-8610\r
+ JP\r
+\r
+88-9E-68 (hex) Technicolor CH USA Inc.\r
+889E68 (base 16) Technicolor CH USA Inc.\r
+ 5030 Sugarloaf Parkway Bldg 6 \r
+ Lawrenceville GA 30044\r
+ US\r
+\r
+64-09-AC (hex) TCT mobile ltd\r
+6409AC (base 16) TCT mobile ltd\r
+ No.86 hechang 7th road, zhongkai, Hi-Tech District\r
+ Hui Zhou Guang Dong 516006\r
+ CN\r
+\r
+74-3A-EF (hex) Kaonmedia CO., LTD.\r
+743AEF (base 16) Kaonmedia CO., LTD.\r
+ 884-3, Seongnam-daero, Bundang-gu\r
+ Seongnam-si Gyeonggi-do 13517\r
+ KR\r
+\r
+54-48-E6 (hex) Beijing Xiaomi Mobile Software Co.,Ltd\r
+5448E6 (base 16) Beijing Xiaomi Mobile Software Co.,Ltd\r
+ Xiaomi Campus, No. 33 Xi erqi Middle Road, Haidian District\r
+ Beijing Beijing 100085\r
+ CN\r
+\r
+18-9E-2C (hex) Huawei Device Co., Ltd.\r
+189E2C (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+5C-55-78 (hex) iryx corp\r
+5C5578 (base 16) iryx corp\r
+ 14 Hughes\r
+ Irvine CA 92618\r
+ US\r
+\r
+6C-F7-12 (hex) Nokia\r
+6CF712 (base 16) Nokia\r
+ Karaportti 3\r
+ Espoo Finland 02610\r
+ FI\r
+\r
+4C-C5-3E (hex) Zyxel Communications Corporation\r
+4CC53E (base 16) Zyxel Communications Corporation\r
+ No. 6 Innovation Road II, Science Park\r
+ Hsichu Taiwan 300\r
+ TW\r
+\r
+90-56-FC (hex) TECNO MOBILE LIMITED\r
+9056FC (base 16) TECNO MOBILE LIMITED\r
+ ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG\r
+ Hong Kong Hong Kong 999077\r
+ HK\r
+\r
+B8-63-92 (hex) GUANGDONG GENIUS TECHNOLOGY CO., LTD.\r
+B86392 (base 16) GUANGDONG GENIUS TECHNOLOGY CO., LTD.\r
+ No.168, Middle Road Of East Gate\r
+ Xiaobian Community Chang'an Town 523851\r
+ CN\r
+\r
+68-B9-D3 (hex) Shenzhen Trolink Technology CO, LTD\r
+68B9D3 (base 16) Shenzhen Trolink Technology CO, LTD\r
+ 201 B building 4 shijie, Chashu industry 505 block, Baoan airport Sanwei community, Hangcheng street Baoan area.\r
+ Shenzhen GuangDong 518000\r
+ CN\r
+\r
+B8-C6-AA (hex) Earda Technologies co Ltd\r
+B8C6AA (base 16) Earda Technologies co Ltd\r
+ Block A,Lianfeng Creative Park, #2 Jisheng Rd., Nansha District\r
+ Guangzhou Guangdong 511455\r
+ CN\r
+\r
+54-AE-D0 (hex) DASAN Networks, Inc. \r
+54AED0 (base 16) DASAN Networks, Inc. \r
+ DASAN Tower, 49, Daewangpangyo-ro, 644 Beon-gil, Bundang-gu\r
+ Seongnam-si Gyeonggi-do 13493\r
+ KR\r
+\r
+10-70-FD (hex) Mellanox Technologies, Inc.\r
+1070FD (base 16) Mellanox Technologies, Inc.\r
+ 350 Oakmead Parkway, Suite 100 \r
+ Sunnyvale CA 94085\r
+ US\r
+\r
+38-F6-01 (hex) Solid State Storage Technology Corporation\r
+38F601 (base 16) Solid State Storage Technology Corporation\r
+ 21F, 392, Ruey Kuang Road, Neihu\r
+ Taipei 11492\r
+ TW\r
+\r
+40-DE-AD (hex) Juniper Networks\r
+40DEAD (base 16) Juniper Networks\r
+ 1133 Innovation Way\r
+ Sunnyvale CA 94089\r
+ US\r
+\r
+40-F5-20 (hex) Espressif Inc.\r
+40F520 (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+78-AA-82 (hex) New H3C Technologies Co., Ltd\r
+78AA82 (base 16) New H3C Technologies Co., Ltd\r
466 Changhe Road, Binjiang District\r
Hangzhou Zhejiang 310052\r
CN\r
\r
-50-14-08 (hex) AiNET\r
-501408 (base 16) AiNET\r
- 11700 MONTGOMERY RD\r
- BELTSVILLE MD 20705-1159\r
+20-6C-8A (hex) Extreme Networks, Inc.\r
+206C8A (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+34-85-84 (hex) Extreme Networks, Inc.\r
+348584 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+40-18-B1 (hex) Extreme Networks, Inc.\r
+4018B1 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+88-5B-DD (hex) Extreme Networks, Inc.\r
+885BDD (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+98-ED-5C (hex) Tesla,Inc.\r
+98ED5C (base 16) Tesla,Inc.\r
+ 3500 Deer Creek Road\r
+ Palo Alto CA 94304\r
+ US\r
+\r
+C4-42-68 (hex) CRESTRON ELECTRONICS, INC.\r
+C44268 (base 16) CRESTRON ELECTRONICS, INC.\r
+ 15 Volvo Drive\r
+ Rockleigh NJ 07647\r
+ US\r
+\r
+54-71-DD (hex) Huawei Device Co., Ltd.\r
+5471DD (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+E4-A7-49 (hex) Palo Alto Networks\r
+E4A749 (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+84-D6-C5 (hex) SolarEdge Technologies\r
+84D6C5 (base 16) SolarEdge Technologies\r
+ 1 Abba Eban St.\r
+ Herzelia 46725\r
+ IL\r
+\r
+D8-91-36 (hex) Dover Fueling Solutions\r
+D89136 (base 16) Dover Fueling Solutions\r
+ Industrieweg 5\r
+ Bladel NBR 5531AD\r
+ NL\r
+\r
+C8-83-14 (hex) Tempo Communications\r
+C88314 (base 16) Tempo Communications\r
+ 1390 Aspen Way\r
+ Vista CA 92081\r
+ US\r
+\r
+B8-E3-EE (hex) Universal Electronics, Inc.\r
+B8E3EE (base 16) Universal Electronics, Inc.\r
+ 201 E. Sandpointe Ave\r
+ Santa Ana CA 92707\r
+ US\r
+\r
+24-C8-D3 (hex) McWane India Pvt Ltd\r
+24C8D3 (base 16) McWane India Pvt Ltd\r
+ 483, Kamaraj Road, Upplipalayam\r
+ Coimbatore Tamil Nadu 641015\r
+ IN\r
+\r
+60-54-64 (hex) Eyedro Green Solutions Inc.\r
+605464 (base 16) Eyedro Green Solutions Inc.\r
+ 130 Weber St W, Suite 201\r
+ Kitchener Ontario N2H4A2\r
+ CA\r
+\r
+FC-95-6A (hex) OCTAGON SYSTEMS CORP.\r
+FC956A (base 16) OCTAGON SYSTEMS CORP.\r
+ 7403 Church Ranch Blvd\r
+ Westminster CO 80021\r
+ US\r
+\r
+40-B3-1E (hex) Universal Electronics, Inc.\r
+40B31E (base 16) Universal Electronics, Inc.\r
+ 201 E. Sandpointe Ave\r
+ Santa Ana CA 92707\r
+ US\r
+\r
+88-40-33 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+884033 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+E0-F6-B5 (hex) Nintendo Co.,Ltd\r
+E0F6B5 (base 16) Nintendo Co.,Ltd\r
+ 11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU\r
+ KYOTO KYOTO 601-8501\r
+ JP\r
+\r
+AC-7A-56 (hex) Cisco Systems, Inc\r
+AC7A56 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+44-8D-BF (hex) Rhino Mobility LLC\r
+448DBF (base 16) Rhino Mobility LLC\r
+ 8 The Green, Suite A\r
+ Dover DE 19901\r
+ US\r
+\r
+94-8A-C6 (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+948AC6 (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+ No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing.\r
+ Chongqing China 401120\r
+ CN\r
+\r
+C0-A6-6D (hex) Inspur Group Co., Ltd.\r
+C0A66D (base 16) Inspur Group Co., Ltd.\r
+ No.1036 Langchao Rd.\r
+ Jinan Shandong 250101\r
+ CN\r
+\r
+70-F0-96 (hex) Cisco Systems, Inc\r
+70F096 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+00-20-A6 (hex) Proxim Wireless\r
+0020A6 (base 16) Proxim Wireless\r
+ 2114 Ringwood Ave\r
+ San Jose CA 95131\r
+ US\r
+\r
+58-50-ED (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+5850ED (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+00-05-1E (hex) Brocade Communications Systems LLC\r
+00051E (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
US\r
\r
+98-8B-0A (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+988B0A (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+BC-BA-C2 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+BCBAC2 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+AC-CB-51 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ACCB51 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+E0-D4-62 (hex) Huawei Device Co., Ltd.\r
+E0D462 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+90-98-38 (hex) Huawei Device Co., Ltd.\r
+909838 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+84-CC-A8 (hex) Espressif Inc.\r
+84CCA8 (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+C4-F5-7C (hex) Brocade Communications Systems LLC\r
+C4F57C (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-12-F2 (hex) Brocade Communications Systems LLC\r
+0012F2 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+6C-2F-8A (hex) Samsung Electronics Co.,Ltd\r
+6C2F8A (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+F0-B0-22 (hex) TOHO Electronics INC.\r
+F0B022 (base 16) TOHO Electronics INC.\r
+ 1-13-21 Tanashioda, Chuo-Ku\r
+ Sagamihara-City Kanagawa 252-0245\r
+ JP\r
+\r
+98-7E-CA (hex) Inventus Power Eletronica do Brasil LTDA\r
+987ECA (base 16) Inventus Power Eletronica do Brasil LTDA\r
+ Av Buriti, 4285 Distrito Industrial\r
+ Manaus Amazonas 69075000\r
+ BR\r
+\r
+2C-9F-FB (hex) Wistron Neweb Corporation\r
+2C9FFB (base 16) Wistron Neweb Corporation\r
+ No.20,Park Avenue II,Hsinchu Science Park\r
+ Hsin-Chu R.O.C. 308\r
+ TW\r
+\r
+50-38-2F (hex) ASE Group Chung-Li\r
+50382F (base 16) ASE Group Chung-Li\r
+ No 550,Chung-Hwa Road Section1\r
+ Chung-Li , Taoyuan Taoyuan 32016\r
+ TW\r
+\r
+0C-14-D2 (hex) China Mobile Group Device Co.,Ltd.\r
+0C14D2 (base 16) China Mobile Group Device Co.,Ltd.\r
+ 32 Xuanwumen West Street,Xicheng District\r
+ Beijing 100053\r
+ CN\r
+\r
F8-D0-27 (hex) Seiko Epson Corporation\r
F8D027 (base 16) Seiko Epson Corporation\r
2070 Kotobuki Koaka\r
Pleasanton CA 94588\r
US\r
\r
-CC-BE-59 (hex) Calix Inc.\r
-CCBE59 (base 16) Calix Inc.\r
- 2777 Orchard Parkway\r
- San Jose CA 95134\r
- US\r
-\r
F8-A3-4F (hex) zte corporation\r
F8A34F (base 16) zte corporation\r
12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
Giheung-gu, Yongin-City Kyungki-do 446-599\r
KR\r
\r
-C8-67-5E (hex) Aerohive Networks Inc.\r
-C8675E (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
94-86-CD (hex) SEOUL ELECTRONICS&TELECOM\r
9486CD (base 16) SEOUL ELECTRONICS&TELECOM\r
709, Namkwangcentrex 440-4, Cheongcheon-dong, \r
Hangzhou Zhejiang 310052\r
CN\r
\r
-F0-9C-E9 (hex) Aerohive Networks Inc.\r
-F09CE9 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
-9C-5D-12 (hex) Aerohive Networks Inc.\r
-9C5D12 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
-C4-13-E2 (hex) Aerohive Networks Inc.\r
-C413E2 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
68-DB-CA (hex) Apple, Inc.\r
68DBCA (base 16) Apple, Inc.\r
1 Infinite Loop\r
Chongqing Chongqing 401332\r
CN\r
\r
-00-04-80 (hex) Brocade Communications Systems, Inc.\r
-000480 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-0C-DB (hex) Brocade Communications Systems, Inc.\r
-000CDB (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-1B-ED (hex) Brocade Communications Systems, Inc.\r
-001BED (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-05-33 (hex) Brocade Communications Systems, Inc.\r
-000533 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-60-69 (hex) Brocade Communications Systems, Inc.\r
-006069 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
08-18-1A (hex) zte corporation\r
08181A (base 16) zte corporation\r
12/F ZTE Plaza,Keji Road South,Hi-Tech Industrial Park,Nanshan District,\r
Nagaokakyo-shi Kyoto 617-8555\r
JP\r
\r
-00-60-DF (hex) Brocade Communications Systems, Inc.\r
-0060DF (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-00-00-88 (hex) Brocade Communications Systems, Inc.\r
-000088 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
F4-55-9C (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
F4559C (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
Building 17,Nangang Industrial Park, Tangtou 3nd Industrial Estate,Shiyan Baoan District \r
MIAMI FLORIDA 33178\r
US\r
\r
-60-9C-9F (hex) Brocade Communications Systems, Inc.\r
-609C9F (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
A8-82-7F (hex) CIBN Oriental Network(Beijing) CO.,Ltd\r
A8827F (base 16) CIBN Oriental Network(Beijing) CO.,Ltd\r
Floor 18B,Block B(International Resources Building),No.18B(Wanda Plaza),Shijingshan Road,Shijingshan,Beijing(100043)\r
Mountain View CA 94041\r
US\r
\r
-9C-61-1D (hex) Omni-ID USA, Inc.\r
-9C611D (base 16) Omni-ID USA, Inc.\r
- 1200 Ridgeway Ave\r
- Rochester NY 14615\r
- US\r
-\r
48-91-53 (hex) Weinmann Geräte für Medizin GmbH + Co. KG\r
489153 (base 16) Weinmann Geräte für Medizin GmbH + Co. KG\r
Kronsaalsweg 40\r
South Lawrence MA 01843\r
US\r
\r
-00-0C-32 (hex) Avionic Design Development GmbH\r
-000C32 (base 16) Avionic Design Development GmbH\r
- Sthamerstrasse 24a\r
- Hamburg 22397\r
- DE\r
-\r
00-0C-33 (hex) Compucase Enterprise Co. Ltd.\r
000C33 (base 16) Compucase Enterprise Co. Ltd.\r
225 Lane 54, An Ho Road, Section 2nd.\r
San Jose CA 95119\r
US\r
\r
-00-0A-0D (hex) FCI Deutschland GmbH\r
-000A0D (base 16) FCI Deutschland GmbH\r
- Holzhauser Strasse 175\r
- 13509 Berlin \r
- DE\r
-\r
00-09-A9 (hex) Ikanos Communications\r
0009A9 (base 16) Ikanos Communications\r
47709 Fremont Blvd\r
Roseville CA 95747\r
US\r
\r
-90-B8-32 (hex) Aerohive Networks Inc.\r
-90B832 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
28-BD-89 (hex) Google, Inc.\r
28BD89 (base 16) Google, Inc.\r
1600 Amphitheatre Parkway\r
REDMOND 98052\r
US\r
\r
+CC-D4-2E (hex) Arcadyan Corporation\r
+CCD42E (base 16) Arcadyan Corporation\r
+ No.8, Sec.2, Guangfu Rd.\r
+ Hsinchu City Hsinchu 30071\r
+ TW\r
+\r
C8-53-E1 (hex) Beijing Bytedance Network Technology Co., Ltd\r
C853E1 (base 16) Beijing Bytedance Network Technology Co., Ltd\r
No.1 Building, Zhonghang Square, West Road of the Northern 3rd Circuit, Haidian Distrct\r
Beijing Beijing 100098\r
CN\r
\r
+14-16-9D (hex) Cisco Systems, Inc\r
+14169D (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+48-A2-E6 (hex) Resideo\r
+48A2E6 (base 16) Resideo\r
+ 2 Corporate Center Dr.\r
+ Melville NY 11747\r
+ US\r
+\r
90-E2-FC (hex) IEEE Registration Authority\r
90E2FC (base 16) IEEE Registration Authority\r
445 Hoes Lane\r
Piscataway NJ 08554\r
US\r
\r
+F0-08-D1 (hex) Espressif Inc.\r
+F008D1 (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+58-94-B2 (hex) BrainCo\r
+5894B2 (base 16) BrainCo\r
+ ????????????????1107?\r
+ ??? ??? 518000\r
+ CN\r
+\r
+B0-95-75 (hex) TP-LINK TECHNOLOGIES CO.,LTD.\r
+B09575 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.\r
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+ Shenzhen Guangdong 518057\r
+ CN\r
+\r
+B4-B0-55 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+B4B055 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+04-8C-16 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+048C16 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+98-DD-5B (hex) TAKUMI JAPAN LTD\r
+98DD5B (base 16) TAKUMI JAPAN LTD\r
+ 3-9-3 Uchiyama building 7F Nishishinbashi\r
+ Minato-ku Tokyo Tokyo 1050003\r
+ JP\r
+\r
+3C-5C-F1 (hex) eero inc.\r
+3C5CF1 (base 16) eero inc.\r
+ 660 3rd Street\r
+ San Francisco CA 94107\r
+ US\r
+\r
+14-AE-85 (hex) IEEE Registration Authority\r
+14AE85 (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
+\r
+90-74-9D (hex) IRay Technology Co., Ltd.\r
+90749D (base 16) IRay Technology Co., Ltd.\r
+ 11th Guiyang St.,\r
+ Yantai Shandong 264000\r
+ CN\r
+\r
+8C-3B-32 (hex) Microfan B.V.\r
+8C3B32 (base 16) Microfan B.V.\r
+ Industriestraat 23\r
+ Horst Limburg 5961 PH\r
+ NL\r
+\r
+D0-D3-E0 (hex) Aruba, a Hewlett Packard Enterprise Company\r
+D0D3E0 (base 16) Aruba, a Hewlett Packard Enterprise Company\r
+ 3333 Scott Blvd\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+3C-58-C2 (hex) Intel Corporate\r
+3C58C2 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+CC-F9-E4 (hex) Intel Corporate\r
+CCF9E4 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+64-5C-F3 (hex) ParanTek Inc.\r
+645CF3 (base 16) ParanTek Inc.\r
+ 3F, 40-15 Gilju-Ro, 411 Beon-Gil\r
+ Wonmi-Gu, Bucheon City Gyeonggi-Do 14488\r
+ KR\r
+\r
+B0-CC-FE (hex) Huawei Device Co., Ltd.\r
+B0CCFE (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+54-0D-F9 (hex) Huawei Device Co., Ltd.\r
+540DF9 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+00-66-19 (hex) Huawei Device Co., Ltd.\r
+006619 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+FC-39-64 (hex) ITEL MOBILE LIMITED\r
+FC3964 (base 16) ITEL MOBILE LIMITED\r
+ RM B3 & B4 BLOCK B, KO FAI INDUSTRIAL BUILDING NO.7 KO FAI ROAD, YAU TONG, KLN, H.K\r
+ Hong Kong KOWLOON 999077\r
+ HK\r
+\r
+E4-5E-37 (hex) Intel Corporate\r
+E45E37 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+14-47-2D (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+14472D (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+ NO.18 HAIBIN ROAD,\r
+ DONG GUAN GUANG DONG 523860\r
+ CN\r
+\r
+E4-90-FD (hex) Apple, Inc.\r
+E490FD (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+84-AB-1A (hex) Apple, Inc.\r
+84AB1A (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+20-6D-31 (hex) FIREWALLA INC\r
+206D31 (base 16) FIREWALLA INC\r
+ 75 E. Santa Clara St. STE 600\r
+ San Jose CA 95113\r
+ US\r
+\r
+D0-65-44 (hex) Apple, Inc.\r
+D06544 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+18-6F-2D (hex) Shenzhen Sundray Technologies Company Limited\r
+186F2D (base 16) Shenzhen Sundray Technologies Company Limited\r
+ 5th Floor, Block A4, Nanshan ipark,NO.1001 Xue Yuan Road, Nanshan District, Shenzhen 518055, P.R. China\r
+ Shenzhen Guangdong 518057\r
+ CN\r
+\r
+F8-4F-AD (hex) Hui Zhou Gaoshengda Technology Co.,LTD\r
+F84FAD (base 16) Hui Zhou Gaoshengda Technology Co.,LTD\r
+ No.75,Zhongkai High-Tech Development District,Huizhou\r
+ Hui Zhou Guangdong 516006\r
+ CN\r
+\r
+4C-0A-3D (hex) ADNACOM INC.\r
+4C0A3D (base 16) ADNACOM INC.\r
+ 200-5050 Kingsway\r
+ Burnaby BC V5H 4H2\r
+ CA\r
+\r
+3C-80-6B (hex) Hunan Voc Acoustics Technology Co., Ltd.\r
+3C806B (base 16) Hunan Voc Acoustics Technology Co., Ltd.\r
+ State Industrialpark, Jiulong Development Zone, Yanling County\r
+ Zhuzhou Hunan 412500\r
+ CN\r
+\r
+60-DE-35 (hex) GITSN, Inc.\r
+60DE35 (base 16) GITSN, Inc.\r
+ #601~602, Daerung Post Tower 1, 288, Digital-ro\r
+ Guro-gu Seoul 08390\r
+ KR\r
+\r
+28-31-7E (hex) Hongkong Nano IC Technologies Co., Ltd\r
+28317E (base 16) Hongkong Nano IC Technologies Co., Ltd\r
+ Rm. 19C, Lockhart Ctr., 301-307 Lockhart Rd., Wan Chai, Hong Kong.\r
+ Hongkong 999077\r
+ CN\r
+\r
+A8-41-22 (hex) China Mobile (Hangzhou) Information Technology Co.,Ltd.\r
+A84122 (base 16) China Mobile (Hangzhou) Information Technology Co.,Ltd.\r
+ No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District\r
+ Hangzhou Zhejiang 310000\r
+ CN\r
+\r
+6C-DD-BC (hex) Samsung Electronics Co.,Ltd\r
+6CDDBC (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+CC-7F-76 (hex) Cisco Systems, Inc\r
+CC7F76 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+40-BC-68 (hex) Wuhan Funshion Online Technologies Co.,Ltd\r
+40BC68 (base 16) Wuhan Funshion Online Technologies Co.,Ltd\r
+ 5th Floor,Financial Port Building A9,No.77 Optical Valley Avenue, East Lake High-Tech Development Zone, Wuhan\r
+ Wuhan CN/Hubei 430000\r
+ CN\r
+\r
+DC-98-40 (hex) Microsoft Corporation\r
+DC9840 (base 16) Microsoft Corporation\r
+ One Microsoft Way\r
+ REDMOND 98052\r
+ US\r
+\r
+44-76-54 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+447654 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+7C-D9-A0 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+7CD9A0 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+F0-33-E5 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+F033E5 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+B4-F1-8C (hex) Huawei Device Co., Ltd.\r
+B4F18C (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+B8-CE-F6 (hex) Mellanox Technologies, Inc.\r
+B8CEF6 (base 16) Mellanox Technologies, Inc.\r
+ 350 Oakmead Parkway, Suite 100 \r
+ Sunnyvale CA 94085\r
+ US\r
+\r
+B8-02-A4 (hex) Aeonsemi, Inc.\r
+B802A4 (base 16) Aeonsemi, Inc.\r
+ Cassia Court, Suite 716, 10 Market Street\r
+ Camana Bay Grand Cayman KY1-9006\r
+ KY\r
+\r
+E4-83-26 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+E48326 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+94-05-BB (hex) IEEE Registration Authority\r
+9405BB (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
+\r
+8C-5F-AD (hex) Fiberhome Telecommunication Technologies Co.,LTD\r
+8C5FAD (base 16) Fiberhome Telecommunication Technologies Co.,LTD\r
+ No.5 DongXin Road\r
+ Wuhan Hubei 430074\r
+ CN\r
+\r
+AC-C2-5D (hex) Fiberhome Telecommunication Technologies Co.,LTD\r
+ACC25D (base 16) Fiberhome Telecommunication Technologies Co.,LTD\r
+ No.5 DongXin Road\r
+ Wuhan Hubei 430074\r
+ CN\r
+\r
+8C-0C-87 (hex) Nokia\r
+8C0C87 (base 16) Nokia\r
+ 600 March Road\r
+ Kanata Ontario K2K 2E6\r
+ CA\r
+\r
+18-69-D8 (hex) HANGZHOU AIXIANGJI TECHNOLOGY CO., LTD\r
+1869D8 (base 16) HANGZHOU AIXIANGJI TECHNOLOGY CO., LTD\r
+ 7 Floor, 3 Blvd., More Centre, 87 Gudun Rd., Xihu District\r
+ Hangzhou Zhejiang 310012\r
+ CN\r
+\r
+C4-32-D1 (hex) Farlink Technology Limited\r
+C432D1 (base 16) Farlink Technology Limited\r
+ Flat A&B,9/F,Wing Cheong Factory Building,121 King Lam Street,Cheung Sha Wan,Hong Kong.\r
+ Hongkong 0000\r
+ HK\r
+\r
+CC-41-8E (hex) MSA Innovation\r
+CC418E (base 16) MSA Innovation\r
+ 1100 Cranberry Woods Road\r
+ Cranberry Township PA 16066\r
+ US\r
+\r
+6C-6A-77 (hex) Intel Corporate\r
+6C6A77 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+CC-A7-C1 (hex) Google, Inc.\r
+CCA7C1 (base 16) Google, Inc.\r
+ 1600 Amphitheatre Parkway\r
+ Mountain View CA 94043\r
+ US\r
+\r
+38-84-79 (hex) Cisco Meraki\r
+388479 (base 16) Cisco Meraki\r
+ 500 Terry A. Francois Blvd\r
+ San Francisco 94158\r
+ US\r
+\r
+7C-9E-BD (hex) Espressif Inc.\r
+7C9EBD (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+1C-02-19 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+1C0219 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+ NO.18 HAIBIN ROAD,\r
+ DONG GUAN GUANG DONG 523860\r
+ CN\r
+\r
+C8-D7-78 (hex) BSH Hausgeraete GmbH\r
+C8D778 (base 16) BSH Hausgeraete GmbH\r
+ Im Gewerbepark B10\r
+ Regensburg 93059\r
+ DE\r
+\r
+9C-61-1D (hex) Panasonic Corporation of North America\r
+9C611D (base 16) Panasonic Corporation of North America\r
+ 1200 Ridgeway Ave\r
+ Rochester NY 14615\r
+ US\r
+\r
+C0-95-DA (hex) NXP India Private Limited\r
+C095DA (base 16) NXP India Private Limited\r
+ 1st Floor, Muttha Towers, Don Bosco Marg, Off Airport Road, Yerwada\r
+ Pune Maharashtra 411006\r
+ IN\r
+\r
+B4-22-00 (hex) Brother Industries, LTD.\r
+B42200 (base 16) Brother Industries, LTD.\r
+ 15-1, Naeshirocho, Mizuho-ku\r
+ NAGOYA 4678561\r
+ JP\r
+\r
+68-49-B2 (hex) CARLO GAVAZZI LTD\r
+6849B2 (base 16) CARLO GAVAZZI LTD\r
+ BLB042, Bulebel Industrial Estate \r
+ Zejtun ZTN 3000\r
+ MT\r
+\r
+48-7A-FF (hex) ESSYS\r
+487AFF (base 16) ESSYS\r
+ gaetbeol-ro\r
+ Incheon 21999\r
+ KR\r
+\r
+40-B6-E7 (hex) Huawei Device Co., Ltd.\r
+40B6E7 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+D0-B4-5D (hex) Huawei Device Co., Ltd.\r
+D0B45D (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+88-36-CF (hex) Huawei Device Co., Ltd.\r
+8836CF (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+A4-C5-4E (hex) Huawei Device Co., Ltd.\r
+A4C54E (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+D4-BB-E6 (hex) Huawei Device Co., Ltd.\r
+D4BBE6 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+00-9E-EE (hex) Positivo Tecnologia S.A.\r
+009EEE (base 16) Positivo Tecnologia S.A.\r
+ João Bettega, 5200\r
+ Curitiba Paraná 81350-000\r
+ BR\r
+\r
+90-B8-32 (hex) Extreme Networks, Inc.\r
+90B832 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+40-2F-86 (hex) LG Innotek\r
+402F86 (base 16) LG Innotek\r
+ 26, Hanamsandan 5beon-ro\r
+ Gwangju Gwangsan-gu 506-731\r
+ KR\r
+\r
+D4-22-CD (hex) Xsens Technologies B.V.\r
+D422CD (base 16) Xsens Technologies B.V.\r
+ Pantheon 6-a\r
+ Enschede 7521 PR\r
+ NL\r
+\r
+A4-B1-C1 (hex) Intel Corporate\r
+A4B1C1 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+C8-67-5E (hex) Extreme Networks, Inc.\r
+C8675E (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+9C-5D-12 (hex) Extreme Networks, Inc.\r
+9C5D12 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+F0-9C-E9 (hex) Extreme Networks, Inc.\r
+F09CE9 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+C4-13-E2 (hex) Extreme Networks, Inc.\r
+C413E2 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+F4-EB-9F (hex) Ellu Company 2019 SL\r
+F4EB9F (base 16) Ellu Company 2019 SL\r
+ Paseo de la Castellana 144 14B\r
+ Madrid Madrid 28046\r
+ ES\r
+\r
+E8-98-C2 (hex) ZETLAB Company\r
+E898C2 (base 16) ZETLAB Company\r
+ Savelkinsky passage, 4\r
+ Zelenograd Moscow 124482\r
+ RU\r
+\r
+D4-1A-C8 (hex) Nippon Printer Engineering\r
+D41AC8 (base 16) Nippon Printer Engineering\r
+ 2660 Katsuyama\r
+ Fujikawaguchiko-town Yamanashi-Pref. 401-0310\r
+ JP\r
+\r
+50-61-F6 (hex) Universal Electronics, Inc.\r
+5061F6 (base 16) Universal Electronics, Inc.\r
+ 201 E. Sandpointe Ave\r
+ Santa Ana CA 92707\r
+ US\r
+\r
+00-0C-32 (hex) Avionic Design GmbH\r
+000C32 (base 16) Avionic Design GmbH\r
+ Wragekamp 10\r
+ Hamburg 22397\r
+ DE\r
+\r
+00-0A-0D (hex) Amphenol\r
+000A0D (base 16) Amphenol\r
+ Holzhauser Strasse 175\r
+ Berlin 13509\r
+ DE\r
+\r
+F4-54-20 (hex) TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO \r
+F45420 (base 16) TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO \r
+ Av. Buriti, 1900 – Setor B – Distrito Industrial\r
+ Manaus Amazonas 69075-000\r
+ BR\r
+\r
+4C-40-88 (hex) SANSHIN ELECTRONICS CO.,LTD.\r
+4C4088 (base 16) SANSHIN ELECTRONICS CO.,LTD.\r
+ 4-4-12, Shiba, Minato-ku,\r
+ Tokyo 108-8404\r
+ JP\r
+\r
+64-DD-E9 (hex) Xiaomi Communications Co Ltd\r
+64DDE9 (base 16) Xiaomi Communications Co Ltd\r
+ The Rainbow City of China Resources\r
+ NO.68, Qinghe Middle Street Haidian District, Beijing 100085\r
+ CN\r
+\r
+0C-81-7D (hex) EEP Elektro-Elektronik Pranjic GmbH\r
+0C817D (base 16) EEP Elektro-Elektronik Pranjic GmbH\r
+ Am Luftschacht 21\r
+ Gelsenkirchen 45886\r
+ DE\r
+\r
+04-F5-F4 (hex) Proxim Wireless\r
+04F5F4 (base 16) Proxim Wireless\r
+ 2114 Ringwood Ave\r
+ San Jose CA 95131\r
+ US\r
+\r
+C8-BC-E5 (hex) Sense Things Japan INC.\r
+C8BCE5 (base 16) Sense Things Japan INC.\r
+ 3-5-7 Kawaramachi,Chuo-ku\r
+ Osaka 541-0048\r
+ JP\r
+\r
+E8-B4-70 (hex) IEEE Registration Authority\r
+E8B470 (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
+\r
+00-1B-ED (hex) Brocade Communications Systems LLC\r
+001BED (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-0C-DB (hex) Brocade Communications Systems LLC\r
+000CDB (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-04-80 (hex) Brocade Communications Systems LLC\r
+000480 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+60-9C-9F (hex) Brocade Communications Systems LLC\r
+609C9F (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-00-88 (hex) Brocade Communications Systems LLC\r
+000088 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-60-DF (hex) Brocade Communications Systems LLC\r
+0060DF (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-60-69 (hex) Brocade Communications Systems LLC\r
+006069 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-05-33 (hex) Brocade Communications Systems LLC\r
+000533 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+B0-45-02 (hex) Huawei Device Co., Ltd.\r
+B04502 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+1C-1F-F1 (hex) Huawei Device Co., Ltd.\r
+1C1FF1 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+B4-EF-1C (hex) 360 AI Technology Co.Ltd\r
+B4EF1C (base 16) 360 AI Technology Co.Ltd\r
+ MTK Building B?No.6 Jiuxianqiao Road, Chaoyang District, Beijing, P.R.C. \r
+ Beijing Beijing 100015\r
+ CN\r
+\r
+14-DE-39 (hex) Huawei Device Co., Ltd.\r
+14DE39 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+B8-F0-09 (hex) Espressif Inc.\r
+B8F009 (base 16) Espressif Inc.\r
+ Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+ Shanghai Shanghai 201203\r
+ CN\r
+\r
+FC-71-FA (hex) Trane Technologies\r
+FC71FA (base 16) Trane Technologies\r
+ 6200 Troup Hwy.\r
+ Tyler TX 75707\r
+ US\r
+\r
+CC-BE-59 (hex) Calix Inc.\r
+CCBE59 (base 16) Calix Inc.\r
+ 2777 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+24-68-B0 (hex) Samsung Electronics Co.,Ltd\r
+2468B0 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+30-FC-EB (hex) LG Electronics (Mobile Communications)\r
+30FCEB (base 16) LG Electronics (Mobile Communications)\r
+ 60-39, Gasan-dong, Geumcheon-gu\r
+ Seoul 153-801\r
+ KR\r
+\r
+1C-13-38 (hex) Kimball Electronics Group, LLC\r
+1C1338 (base 16) Kimball Electronics Group, LLC\r
+ 1205 Kimball Blvd\r
+ Jasper IN 47546\r
+ US\r
+\r
+EC-63-ED (hex) Hyundai Autoever Corp.\r
+EC63ED (base 16) Hyundai Autoever Corp.\r
+ 38, Teheran-ro 114-gil\r
+ Gangnam-gu, Seoul 06176\r
+ KR\r
+\r
+84-A3-B5 (hex) Propulsion systems\r
+84A3B5 (base 16) Propulsion systems\r
+ Dooren 72\r
+ Merchtem Vlaams brabant 1785\r
+ BE\r
+\r
+9C-ED-FA (hex) EVUlution AG\r
+9CEDFA (base 16) EVUlution AG\r
+ Via da Clalt 12\r
+ Poschiavo GR 7742\r
+ CH\r
+\r
7C-8A-E1 (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD. \r
7C8AE1 (base 16) COMPAL INFORMATION (KUNSHAN) CO., LTD. \r
NO. 25, THE 3RD Street KUNSHAN EXPORT PROCESSING ZONE \r
Shenzhen Guangdong 518000\r
CN\r
\r
-48-77-46 (hex) Calix Inc.\r
-487746 (base 16) Calix Inc.\r
- 2777 Orchard Parkway\r
- San Jose CA 95134\r
- US\r
-\r
F8-AE-27 (hex) John Deere Electronic Solutions\r
F8AE27 (base 16) John Deere Electronic Solutions\r
1441 44th St N\r
New Taipei City, Taiwan 24159\r
TW\r
\r
-68-6D-BC (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-686DBC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
- No.555 Qianmo Road, Binjiang District\r
- Hangzhou Zhejiang 310052\r
- CN\r
-\r
10-DC-4A (hex) Fiberhome Telecommunication Technologies Co.,LTD\r
10DC4A (base 16) Fiberhome Telecommunication Technologies Co.,LTD\r
No.5 DongXin Road\r
Jinan Shandong 250101\r
CN\r
\r
-78-7D-53 (hex) Aerohive Networks Inc.\r
-787D53 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
E0-45-6D (hex) China Mobile Group Device Co.,Ltd.\r
E0456D (base 16) China Mobile Group Device Co.,Ltd.\r
32 Xuanwumen West Street,Xicheng District\r
Cambridge CB24 9ZR\r
GB\r
\r
-44-65-7F (hex) Calix Inc.\r
-44657F (base 16) Calix Inc.\r
- 2777 Orchard Parkway\r
- San Jose CA 95134\r
- US\r
-\r
40-62-EA (hex) China Mobile Group Device Co.,Ltd.\r
4062EA (base 16) China Mobile Group Device Co.,Ltd.\r
32 Xuanwumen West Street,Xicheng District\r
Rueil Malmaison Cedex hauts de seine 92848\r
FR\r
\r
-28-56-C1 (hex) Harman International\r
-2856C1 (base 16) Harman International\r
- 15th Fl, 400 Atlantic Street\r
- Stamford CT 06901\r
- US\r
-\r
B4-A3-82 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
B4A382 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
No.555 Qianmo Road\r
Matsumoto-shi Nagano-ken 399-8702\r
JP\r
\r
-24-E1-24 (hex) Xiamen Ursaconn Technology Co. , Ltd.\r
-24E124 (base 16) Xiamen Ursaconn Technology Co. , Ltd.\r
- 3/F, No. 46 Guanri Road, 2nd Software Park\r
- Xiamen Fujian 361008\r
- CN\r
-\r
8C-0F-83 (hex) Angie Hospitality LLC\r
8C0F83 (base 16) Angie Hospitality LLC\r
12465 S Fort St, Ste 300\r
Apeldoorn 7328JK\r
NL\r
\r
-78-A6-E1 (hex) Brocade Communications Systems, Inc.\r
-78A6E1 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose 95134\r
- US\r
-\r
E4-EC-10 (hex) Nokia Corporation\r
E4EC10 (base 16) Nokia Corporation\r
Elektroniikkatie 10\r
Taipei Neihu District 11491\r
TW\r
\r
-00-1B-17 (hex) Palo Alto Networks\r
-001B17 (base 16) Palo Alto Networks\r
- 2130 Gold Street Suite 200\r
- Alviso CA 95022\r
- US\r
-\r
-58-49-3B (hex) Palo Alto Networks\r
-58493B (base 16) Palo Alto Networks\r
- 4401 Great America Parkway\r
- Santa Clara CA 95054\r
- US\r
-\r
-78-6D-94 (hex) Palo Alto Networks\r
-786D94 (base 16) Palo Alto Networks\r
- 4401 Great America Pkwy\r
- Santa Clara CA 95054\r
- US\r
-\r
FC-5A-1D (hex) Hitron Technologies. Inc\r
FC5A1D (base 16) Hitron Technologies. Inc\r
No. 1-8, Lising 1st Rd. Hsinchu Science Park, Hsinchu, 300, Taiwan, R.O.C\r
San Jose CA 94568\r
US\r
\r
-EC-4F-82 (hex) Calix Inc.\r
-EC4F82 (base 16) Calix Inc.\r
- 2777 Orchard Parkway\r
- San Jose CA 95134\r
- US\r
-\r
D4-61-FE (hex) Hangzhou H3C Technologies Co., Limited\r
D461FE (base 16) Hangzhou H3C Technologies Co., Limited\r
466 Changhe Road, Binjiang District\r
Kulim Kedah 09000\r
MY\r
\r
-7C-95-B1 (hex) Aerohive Networks Inc.\r
-7C95B1 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
24-20-C7 (hex) Sagemcom Broadband SAS\r
2420C7 (base 16) Sagemcom Broadband SAS\r
250, route de l'Empereur\r
Hsinchu City Hsinchu 30071\r
TW\r
\r
-EC-68-81 (hex) Palo Alto Networks\r
-EC6881 (base 16) Palo Alto Networks\r
- 4401 Great America Parkway\r
- Santa Clara CA 95054\r
- US\r
-\r
E4-50-9A (hex) HW Communications Ltd\r
E4509A (base 16) HW Communications Ltd\r
Parkfield\r
San Diego CA 92101\r
US\r
\r
-6C-38-A1 (hex) Ubee Interactive Co., Limited\r
-6C38A1 (base 16) Ubee Interactive Co., Limited\r
- Room 1607 Dominion Centre, 43 Queen’s Road East\r
- Wanchai Hong Kong 302\r
- HK\r
-\r
00-17-42 (hex) FUJITSU LIMITED\r
001742 (base 16) FUJITSU LIMITED\r
403, Kosugi-cho 1-chome, Nakahara-ku\r
Shenzhen Guangdong 518000\r
CN\r
\r
-88-94-71 (hex) Brocade Communications Systems, Inc.\r
-889471 (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
-8C-7C-FF (hex) Brocade Communications Systems, Inc.\r
-8C7CFF (base 16) Brocade Communications Systems, Inc.\r
- 130 Holger Way\r
- San Jose CA 95134\r
- US\r
-\r
14-2D-27 (hex) Hon Hai Precision Ind. Co.,Ltd.\r
142D27 (base 16) Hon Hai Precision Ind. Co.,Ltd.\r
Building D21,No.1, East Zone 1st Road\r
Sunnyvale CA 94089\r
US\r
\r
-70-76-DD (hex) Oxyguard International A/S\r
-7076DD (base 16) Oxyguard International A/S\r
- Blokken 59\r
- Birkeroed DK-3460\r
- DK\r
-\r
54-61-EA (hex) Zaplox AB\r
5461EA (base 16) Zaplox AB\r
Scheelev\r
Santa Clara CA 95054\r
US\r
\r
-64-7C-34 (hex) Ubee Interactive Co., Limited\r
-647C34 (base 16) Ubee Interactive Co., Limited\r
- Room 1607 Dominion Centre, 43 Queen’s Road East\r
- Wanchai Hong Kong 302\r
- HK\r
-\r
C0-A3-64 (hex) 3D Systems Massachusetts\r
C0A364 (base 16) 3D Systems Massachusetts\r
19 Connector Road\r
Shindian Taipei 231\r
TW\r
\r
-00-1B-85 (hex) MAN Diesel SE\r
-001B85 (base 16) MAN Diesel SE\r
- Teglholmsgade 41\r
- Copenhagen 2450\r
- DK\r
-\r
00-1B-89 (hex) EMZA Visual Sense Ltd.\r
001B89 (base 16) EMZA Visual Sense Ltd.\r
20 Ha'ta'as St., Beith Hapamon\r
Seoul 135-010\r
KR\r
\r
-00-0E-F3 (hex) Smarthome\r
-000EF3 (base 16) Smarthome\r
- 16542 Millikan Ave.\r
- Irvine CA 92606\r
- US\r
-\r
00-0E-F2 (hex) Infinico Corporation\r
000EF2 (base 16) Infinico Corporation\r
4F, F-1 Bldg., 1-2-12,\r
LENEXA KS 66215-1239\r
US\r
\r
-00-10-7F (hex) CRESTRON ELECTRONICS, INC.\r
-00107F (base 16) CRESTRON ELECTRONICS, INC.\r
- 101 BROADWAY\r
- CRESSKILL NJ 07626\r
- US\r
-\r
00-10-E2 (hex) ArrayComm, Inc.\r
0010E2 (base 16) ArrayComm, Inc.\r
3141 ZANKER ROAD\r
Guangzhou Guangdong 511450\r
CN\r
\r
-58-59-C2 (hex) Aerohive Networks Inc.\r
-5859C2 (base 16) Aerohive Networks Inc.\r
- 1011 McCarthy Blvd\r
- Milpitas CA 95035\r
- US\r
-\r
14-59-C3 (hex) Creative Chips GmbH\r
1459C3 (base 16) Creative Chips GmbH\r
Im Bubenstück 1\r
Shenzhen Guangdong 518055\r
CN\r
\r
-F0-46-3B (hex) Comcast Cable Corporation\r
-F0463B (base 16) Comcast Cable Corporation\r
- 1800 Arch Street\r
- Philadelphia PA 19103\r
+1C-05-B7 (hex) Chongqing Trantor Technology Co., Ltd.\r
+1C05B7 (base 16) Chongqing Trantor Technology Co., Ltd.\r
+ No.69,Huoju Avenue,Jiulongpo District.\r
+ Chongqing Chongqing 400050\r
+ CN\r
+\r
+00-0E-F3 (hex) Smartlabs, Inc. \r
+000EF3 (base 16) Smartlabs, Inc. \r
+ 1621 Alton Parkway, Suite 100\r
+ Irvine CA 92606\r
+ US\r
+\r
+A0-43-B0 (hex) Hangzhou BroadLink Technology Co.,Ltd\r
+A043B0 (base 16) Hangzhou BroadLink Technology Co.,Ltd\r
+ Room 101,1/F,Unit C,Building 1,No.57 Jiang'er Road,Changhe Street,Binjiang District,Hangzhou,Zhejiang,P.R.China\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+74-AC-B9 (hex) Ubiquiti Networks Inc.\r
+74ACB9 (base 16) Ubiquiti Networks Inc.\r
+ 2580 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+F4-92-BF (hex) Ubiquiti Networks Inc.\r
+F492BF (base 16) Ubiquiti Networks Inc.\r
+ 2580 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+D8-C5-61 (hex) CommFront Communications Pte Ltd\r
+D8C561 (base 16) CommFront Communications Pte Ltd\r
+ No. 1 Yishun Industrial ST 1, #05-31 A'Posh BizHub\r
+ SG SG 768160\r
+ SG\r
+\r
+0C-29-EF (hex) Dell Inc.\r
+0C29EF (base 16) Dell Inc.\r
+ One Dell Way\r
+ Round Rock TX 78682\r
US\r
\r
+60-D8-9C (hex) HMD Global Oy\r
+60D89C (base 16) HMD Global Oy\r
+ Bertel Jungin aukio 9\r
+ Espoo 02600\r
+ FI\r
+\r
F8-2E-8E (hex) Nanjing Kechen Electric Co., Ltd.\r
F82E8E (base 16) Nanjing Kechen Electric Co., Ltd.\r
Room 202, Building 12, No. 50 Daguang road\r
Anzhou, Industrial Park\r
Mianyang Sichuan 622650\r
CN\r
+\r
+F0-46-3B (hex) Comcast Cable Corporation\r
+F0463B (base 16) Comcast Cable Corporation\r
+ 1800 Arch Street\r
+ Philadelphia PA 19103\r
+ US\r
+\r
+68-D7-9A (hex) Ubiquiti Networks Inc.\r
+68D79A (base 16) Ubiquiti Networks Inc.\r
+ 2580 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+1C-63-BF (hex) SHENZHEN BROADTEL TELECOM CO.,LTD\r
+1C63BF (base 16) SHENZHEN BROADTEL TELECOM CO.,LTD\r
+ No.14-1, Tongqing Road, Baolong street, Longgang District\r
+ ShenZhen GuangDong 518116\r
+ CN\r
+\r
+AC-36-51 (hex) Jiangsu Hengtong Terahertz Technology Co., Ltd.\r
+AC3651 (base 16) Jiangsu Hengtong Terahertz Technology Co., Ltd.\r
+ Room 1312, Beiyou Technology Building, Haidian District\r
+ Beijing Beijing 100876\r
+ CN\r
+\r
+68-4A-76 (hex) eero inc.\r
+684A76 (base 16) eero inc.\r
+ 660 3rd Street\r
+ San Francisco CA 94107\r
+ US\r
+\r
+68-8F-C9 (hex) Zhuolian (Shenzhen) Communication Co., Ltd\r
+688FC9 (base 16) Zhuolian (Shenzhen) Communication Co., Ltd\r
+ Shengli electromechanical Co., Ltd. 201, No.19, Xixiang section, Guangshen Road, Jingbei community, Xixiang street, Bao'an District\r
+ Shenzhen Shenzhen 518101\r
+ CN\r
+\r
+F0-81-75 (hex) Sagemcom Broadband SAS\r
+F08175 (base 16) Sagemcom Broadband SAS\r
+ 250, route de l'Empereur\r
+ Rueil Malmaison Cedex hauts de seine 92848\r
+ FR\r
+\r
+D8-47-32 (hex) TP-LINK TECHNOLOGIES CO.,LTD.\r
+D84732 (base 16) TP-LINK TECHNOLOGIES CO.,LTD.\r
+ Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+ Shenzhen Guangdong 518057\r
+ CN\r
+\r
+28-64-B0 (hex) Huawei Device Co., Ltd.\r
+2864B0 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+04-F1-69 (hex) Huawei Device Co., Ltd.\r
+04F169 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+50-21-EC (hex) Huawei Device Co., Ltd.\r
+5021EC (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+8C-68-3A (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+8C683A (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+B4-6E-08 (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+B46E08 (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+00-5E-0C (hex) HMD Global Oy\r
+005E0C (base 16) HMD Global Oy\r
+ Bertel Jungin aukio 9\r
+ Espoo 02600\r
+ FI\r
+\r
+B4-81-07 (hex) SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD\r
+B48107 (base 16) SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD\r
+ Unit East Block22-24/F,Skyworth semiconductor design Bldg., Gaoxin Ave.4.S.,Nanshan District,Shenzhen,China\r
+ SHENZHEN GUANGDONG 518057\r
+ CN\r
+\r
+70-66-55 (hex) AzureWave Technology Inc.\r
+706655 (base 16) AzureWave Technology Inc.\r
+ 8F., No. 94, Baozhong Rd.\r
+ New Taipei City Taiwan 231\r
+ TW\r
+\r
+C8-58-C0 (hex) Intel Corporate\r
+C858C0 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+64-7C-34 (hex) Ubee Interactive Co., Limited\r
+647C34 (base 16) Ubee Interactive Co., Limited\r
+ Flat/RM 1202, 12/F, AT Tower \r
+ North Point Hong Kong 180\r
+ HK\r
+\r
+6C-38-A1 (hex) Ubee Interactive Co., Limited\r
+6C38A1 (base 16) Ubee Interactive Co., Limited\r
+ Flat/RM 1202, 12/F, AT Tower \r
+ North Point Hong Kong 180\r
+ HK\r
+\r
+78-53-0D (hex) Shenzhen Skyworth Digital Technology CO., Ltd\r
+78530D (base 16) Shenzhen Skyworth Digital Technology CO., Ltd\r
+ 4F,Block A, Skyworth?Building,\r
+ Shenzhen Guangdong 518057\r
+ CN\r
+\r
+0C-48-C6 (hex) CELESTICA INC.\r
+0C48C6 (base 16) CELESTICA INC.\r
+ 1900-5140 Yonge Street PO Box 42 \r
+ Toronto Ontario M2N 6L7\r
+ CA\r
+\r
+A4-29-85 (hex) Sichuan AI-Link Technology Co., Ltd.\r
+A42985 (base 16) Sichuan AI-Link Technology Co., Ltd.\r
+ Anzhou, Industrial Park\r
+ Mianyang Sichuan 622650\r
+ CN\r
+\r
+78-AC-44 (hex) Dell Inc.\r
+78AC44 (base 16) Dell Inc.\r
+ One Dell Way\r
+ Round Rock TX 78682\r
+ US\r
+\r
+98-C8-B8 (hex) vivo Mobile Communication Co., Ltd.\r
+98C8B8 (base 16) vivo Mobile Communication Co., Ltd.\r
+ #283,BBK Road\r
+ Wusha,Chang'An DongGuan City,Guangdong, 523860\r
+ CN\r
+\r
+B8-D4-E7 (hex) Aruba, a Hewlett Packard Enterprise Company\r
+B8D4E7 (base 16) Aruba, a Hewlett Packard Enterprise Company\r
+ 3333 Scott Blvd\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+D8-4C-90 (hex) Apple, Inc.\r
+D84C90 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+24-D0-DF (hex) Apple, Inc.\r
+24D0DF (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+6C-4A-85 (hex) Apple, Inc.\r
+6C4A85 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+28-F0-33 (hex) Apple, Inc.\r
+28F033 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+20-9E-F7 (hex) Extreme Networks, Inc.\r
+209EF7 (base 16) Extreme Networks, Inc.\r
+ 6480 Via Del Oro\r
+ San Jose CA 95119\r
+ US\r
+\r
+BC-09-63 (hex) Apple, Inc.\r
+BC0963 (base 16) Apple, Inc.\r
+ 1 Infinite Loop\r
+ Cupertino CA 95014\r
+ US\r
+\r
+18-58-69 (hex) Sailer Electronic Co., Ltd\r
+185869 (base 16) Sailer Electronic Co., Ltd\r
+ No. 6, Sanxi Road, Ximagou Industrial Park, Jianxi District\r
+ Luoyang Henan 471000\r
+ CN\r
+\r
+BC-2D-EF (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+BC2DEF (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+ No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing.\r
+ Chongqing China 401120\r
+ CN\r
+\r
+78-81-CE (hex) China Mobile Iot Limited company\r
+7881CE (base 16) China Mobile Iot Limited company\r
+ No. 8 Yangliu North Road, Yubei District, Chongqing, China\r
+ Chong Qing Chong Qing 401120\r
+ CN\r
+\r
+BC-FF-21 (hex) Smart Code(shenzhen)Technology Co.,Ltd\r
+BCFF21 (base 16) Smart Code(shenzhen)Technology Co.,Ltd\r
+ Room 1206, Satellite Building,2002 Keyuan Road, Nanshan \r
+ Shenzhen Guangdong (Province) 518000\r
+ CN\r
+\r
+44-5C-E9 (hex) Samsung Electronics Co.,Ltd\r
+445CE9 (base 16) Samsung Electronics Co.,Ltd\r
+ 129, Samsung-ro, Youngtongl-Gu\r
+ Suwon Gyeonggi-Do 16677\r
+ KR\r
+\r
+C0-16-92 (hex) China Mobile Group Device Co.,Ltd.\r
+C01692 (base 16) China Mobile Group Device Co.,Ltd.\r
+ 32 Xuanwumen West Street,Xicheng District\r
+ Beijing 100053\r
+ CN\r
+\r
+38-17-30 (hex) Ulrich Lippert GmbH & Co KG\r
+381730 (base 16) Ulrich Lippert GmbH & Co KG\r
+ Christian-Henkel-Str. 12\r
+ Berlin 12349\r
+ DE\r
+\r
+40-2E-71 (hex) Texas Instruments\r
+402E71 (base 16) Texas Instruments\r
+ 12500 TI Blvd\r
+ Dallas TX 75243\r
+ US\r
+\r
+70-76-DD (hex) OxyGuard Internation A/S\r
+7076DD (base 16) OxyGuard Internation A/S\r
+ Farum Gydevej 64\r
+ Farum DK-3520\r
+ DK\r
+\r
+94-E9-EE (hex) Huawei Device Co., Ltd.\r
+94E9EE (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+28-E3-4E (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+28E34E (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+AC-12-03 (hex) Intel Corporate\r
+AC1203 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+64-BC-58 (hex) Intel Corporate\r
+64BC58 (base 16) Intel Corporate\r
+ Lot 8, Jalan Hi-Tech 2/3\r
+ Kulim Kedah 09000\r
+ MY\r
+\r
+D4-52-EE (hex) BSkyB Ltd\r
+D452EE (base 16) BSkyB Ltd\r
+ 130 Kings Road\r
+ Brentwood Essex 08854\r
+ GB\r
+\r
+E0-23-FF (hex) Fortinet, Inc.\r
+E023FF (base 16) Fortinet, Inc.\r
+ 899 Kifer Road\r
+ Sunnyvale 94086\r
+ US\r
+\r
+8C-59-DC (hex) ASR Microelectronics (Shanghai) Co., Ltd.\r
+8C59DC (base 16) ASR Microelectronics (Shanghai) Co., Ltd.\r
+ Building 2, NO.399 Keyuan Road,Pudong District\r
+ Shanghai Shanghai 201210\r
+ CN\r
+\r
+18-82-8C (hex) Arcadyan Corporation\r
+18828C (base 16) Arcadyan Corporation\r
+ No.8, Sec.2, Guangfu Rd.\r
+ Hsinchu City Hsinchu 30071\r
+ TW\r
+\r
+9C-F0-29 (hex) Integrated Device Technology (Malaysia) Sdn. Bhd.\r
+9CF029 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd.\r
+ Phase 3, Bayan Lepas FIZ\r
+ Bayan Lepas Penang 11900\r
+ MY\r
+\r
+28-56-C1 (hex) Harman/Becker Automotive Systems GmbH\r
+2856C1 (base 16) Harman/Becker Automotive Systems GmbH\r
+ 15th Fl, 400 Atlantic Street\r
+ Stamford CT 06901\r
+ US\r
+\r
+78-B8-D6 (hex) Zebra Technologies Inc.\r
+78B8D6 (base 16) Zebra Technologies Inc.\r
+ ONE ZEBRA PLAZA\r
+ HOLTSVILLE NY 11742\r
+ US\r
+\r
+BC-4A-56 (hex) Cisco Systems, Inc\r
+BC4A56 (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+6C-61-F4 (hex) SFR\r
+6C61F4 (base 16) SFR\r
+ 12 rue jean-philippe Rameau CS 80001\r
+ La plaine saint denis FRANCE 93634\r
+ FR\r
+\r
+F4-90-CB (hex) IEEE Registration Authority\r
+F490CB (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
+\r
+00-10-7F (hex) CRESTRON ELECTRONICS, INC.\r
+00107F (base 16) CRESTRON ELECTRONICS, INC.\r
+ 15 Volvo Drive\r
+ Rockleigh NJ 07647\r
+ US\r
+\r
+00-1B-85 (hex) MAN Energy Solutions\r
+001B85 (base 16) MAN Energy Solutions\r
+ Teglholmsgade 41\r
+ Copenhagen 2450\r
+ DK\r
+\r
+58-49-3B (hex) Palo Alto Networks\r
+58493B (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+00-1B-17 (hex) Palo Alto Networks\r
+001B17 (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+58-59-C2 (hex) Extreme Networks, Inc.\r
+5859C2 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+B8-2F-CB (hex) CMS Electracom\r
+B82FCB (base 16) CMS Electracom\r
+ 24 Binney Road\r
+ Kings Park NSW 2148\r
+ AU\r
+\r
+10-CE-45 (hex) Miromico AG\r
+10CE45 (base 16) Miromico AG\r
+ Gallusstrasse 4\r
+ Zurich Zurich CH-8006\r
+ CH\r
+\r
+78-7D-53 (hex) Extreme Networks, Inc.\r
+787D53 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+7C-95-B1 (hex) Extreme Networks, Inc.\r
+7C95B1 (base 16) Extreme Networks, Inc.\r
+ 1011 McCarthy Blvd\r
+ Milpitas CA 95035\r
+ US\r
+\r
+EC-68-81 (hex) Palo Alto Networks\r
+EC6881 (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+78-6D-94 (hex) Palo Alto Networks\r
+786D94 (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+F8-AF-05 (hex) Huawei Device Co., Ltd.\r
+F8AF05 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+4C-FC-AA (hex) Tesla,Inc.\r
+4CFCAA (base 16) Tesla,Inc.\r
+ 3500 Deer Creek Rd.\r
+ PALO ALTO CA 94304\r
+ US\r
+\r
+CC-AB-2C (hex) HUMAX Co., Ltd.\r
+CCAB2C (base 16) HUMAX Co., Ltd.\r
+ HUMAX Village, 216, Hwangsaeul-ro, Bu\r
+ Seongnam-si Gyeonggi-do 463-875\r
+ KR\r
+\r
+6C-6D-09 (hex) Kyowa Electronics Co.,Ltd.\r
+6C6D09 (base 16) Kyowa Electronics Co.,Ltd.\r
+ 4-3-31 Takatsukasa\r
+ Takarazuka Hyogo 665-0051\r
+ JP\r
+\r
+24-E1-24 (hex) Xiamen Ursalink Technology Co., Ltd.\r
+24E124 (base 16) Xiamen Ursalink Technology Co., Ltd.\r
+ 4/F, No. 63-2 Wanghai Road, 2nd Software Park\r
+ Xiamen Fujian 361008\r
+ CN\r
+\r
+24-43-E2 (hex) DASAN Network Solutions\r
+2443E2 (base 16) DASAN Network Solutions\r
+ DASAN Tower 8F, 49 Daewangpangyo-ro644beon-gil Bundang-gu\r
+ Seongnam-si Gyeonggi-do 13493\r
+ KR\r
+\r
+A8-6A-BB (hex) Sagemcom Broadband SAS\r
+A86ABB (base 16) Sagemcom Broadband SAS\r
+ 250, route de l'Empereur\r
+ Rueil Malmaison Cedex hauts de seine 92848\r
+ FR\r
+\r
+90-17-3F (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+90173F (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+54-0E-2D (hex) vivo Mobile Communication Co., Ltd.\r
+540E2D (base 16) vivo Mobile Communication Co., Ltd.\r
+ #283,BBK Road\r
+ Wusha,Chang'An DongGuan City,Guangdong, 523860\r
+ CN\r
+\r
+70-8F-47 (hex) vivo Mobile Communication Co., Ltd.\r
+708F47 (base 16) vivo Mobile Communication Co., Ltd.\r
+ #283,BBK Road\r
+ Wusha,Chang'An DongGuan City,Guangdong, 523860\r
+ CN\r
+\r
+A0-FF-70 (hex) Technicolor CH USA Inc.\r
+A0FF70 (base 16) Technicolor CH USA Inc.\r
+ 5030 Sugarloaf Parkway Bldg 6 \r
+ Lawrenceville GA 30044\r
+ US\r
+\r
+60-7E-CD (hex) HUAWEI TECHNOLOGIES CO.,LTD\r
+607ECD (base 16) HUAWEI TECHNOLOGIES CO.,LTD\r
+ No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+ Dongguan 523808\r
+ CN\r
+\r
+54-8A-BA (hex) Cisco Systems, Inc\r
+548ABA (base 16) Cisco Systems, Inc\r
+ 80 West Tasman Drive\r
+ San Jose CA 94568\r
+ US\r
+\r
+C8-07-39 (hex) NAKAYO Inc\r
+C80739 (base 16) NAKAYO Inc\r
+ 1-3-2, Soja-machi\r
+ Maebashi-shi Gunma 371-0853\r
+ JP\r
+\r
+8C-7C-FF (hex) Brocade Communications Systems LLC\r
+8C7CFF (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+AC-3C-8E (hex) Flextronics Computing(Suzhou)Co.,Ltd.\r
+AC3C8E (base 16) Flextronics Computing(Suzhou)Co.,Ltd.\r
+ No.1 GuanPu Road. Guoxiang street , WuZhong District,Suzhou City, Jiangsu Province. \r
+ Suzhou 215124 \r
+ CN\r
+\r
+40-62-34 (hex) Telink Semiconductor (Shanghai) Co., Ltd.\r
+406234 (base 16) Telink Semiconductor (Shanghai) Co., Ltd.\r
+ No. 1500 Zuchongzhi Rd, Building #3\r
+ Shanghai 201203\r
+ CN\r
+\r
+88-94-71 (hex) Brocade Communications Systems LLC\r
+889471 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+78-A6-E1 (hex) Brocade Communications Systems LLC\r
+78A6E1 (base 16) Brocade Communications Systems LLC\r
+ 1320 Ridder Park Dr\r
+ San Jose CA 95131\r
+ US\r
+\r
+00-94-EC (hex) Huawei Device Co., Ltd.\r
+0094EC (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+74-45-2D (hex) Huawei Device Co., Ltd.\r
+74452D (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+A4-50-06 (hex) SHENZHEN HUACHUANG SHIDAI TECHNOLOGYCO.,LTD\r
+A45006 (base 16) SHENZHEN HUACHUANG SHIDAI TECHNOLOGYCO.,LTD\r
+ longhua dalang huaronglu lianjiangongyeyuan 4-5\r
+ shenzhen guangdong 518000\r
+ CN\r
+\r
+C8-71-25 (hex) Johnson Outdoors Marine Electronics d/b/a Minnkota\r
+C87125 (base 16) Johnson Outdoors Marine Electronics d/b/a Minnkota\r
+ 1531 E Madison Ave\r
+ Mankato MN 56001\r
+ US\r
+\r
+68-6D-BC (hex) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+686DBC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ No.555 Qianmo Road\r
+ Hangzhou Zhejiang 310052\r
+ CN\r
+\r
+80-CF-A2 (hex) Huawei Device Co., Ltd.\r
+80CFA2 (base 16) Huawei Device Co., Ltd.\r
+ No.2 of Xincheng Road, Songshan Lake Zone\r
+ Dongguan Guangdong 523808\r
+ CN\r
+\r
+08-03-42 (hex) Palo Alto Networks\r
+080342 (base 16) Palo Alto Networks\r
+ 3000 Tannery Way\r
+ Santa Clara CA 95054\r
+ US\r
+\r
+EC-6C-B5 (hex) zte corporation\r
+EC6CB5 (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+C0-B1-01 (hex) zte corporation\r
+C0B101 (base 16) zte corporation\r
+ 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+ shenzhen guangdong 518057\r
+ CN\r
+\r
+EC-4F-82 (hex) Calix Inc.\r
+EC4F82 (base 16) Calix Inc.\r
+ 2777 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+44-65-7F (hex) Calix Inc.\r
+44657F (base 16) Calix Inc.\r
+ 2777 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+48-77-46 (hex) Calix Inc.\r
+487746 (base 16) Calix Inc.\r
+ 2777 Orchard Pkwy\r
+ San Jose CA 95131\r
+ US\r
+\r
+FC-3D-A5 (hex) Arcadyan Corporation\r
+FC3DA5 (base 16) Arcadyan Corporation\r
+ No.8, Sec.2, Guangfu Rd.\r
+ Hsinchu City Hsinchu 30071\r
+ TW\r
+\r
+BC-33-AC (hex) Silicon Laboratories\r
+BC33AC (base 16) Silicon Laboratories\r
+ 7000 W. William Cannon Dr.\r
+ Austin TX 78735\r
+ US\r
+\r
+14-01-52 (hex) Samsung Electronics Co.,Ltd\r
+140152 (base 16) Samsung Electronics Co.,Ltd\r
+ #94-1, Imsoo-Dong\r
+ Gumi Gyeongbuk 730-350\r
+ KR\r
+\r
+94-FB-A7 (hex) IEEE Registration Authority\r
+94FBA7 (base 16) IEEE Registration Authority\r
+ 445 Hoes Lane\r
+ Piscataway NJ 08554\r
+ US\r
Seoul 08501\r
KR\r
\r
-A4-DA-22 (hex) Abetechs GmbH\r
-A00000-AFFFFF (base 16) Abetechs GmbH\r
- Niermannsweg 11\r
- Erkrath North Rhine-Westphalia 40699\r
- DE\r
-\r
A4-DA-22 (hex) LORIOT AG\r
400000-4FFFFF (base 16) LORIOT AG\r
Zuercherstrasse 68\r
SHEN ZHEN GUANGDONG 518000\r
CN\r
\r
-9C-43-1E (hex) Midas Technology DBA Phoenix Audio Technologies\r
-E00000-EFFFFF (base 16) Midas Technology DBA Phoenix Audio Technologies\r
- 16 Goodyear #120\r
- Irvine CA 92618\r
- US\r
-\r
F8-B5-68 (hex) Dongwoo Engineering Co.,Ltd\r
300000-3FFFFF (base 16) Dongwoo Engineering Co.,Ltd\r
#311, dREC Techno 9-ro, Yuseong-gu\r
Nanjing Jiangsu 210019\r
CN\r
\r
-B0-B3-53 (hex) Blake UK\r
-000000-0FFFFF (base 16) Blake UK\r
- 177-187, Rutland Road\r
- Sheffield --select-- S3 9PT\r
- GB\r
-\r
B0-B3-53 (hex) Beijing Geekplus Technology Co.,Ltd.\r
C00000-CFFFFF (base 16) Beijing Geekplus Technology Co.,Ltd.\r
1st Floor, Building 1, Chaolai High-Tech industrial Part, Chaoyang District\r
Bay Shore NY 11706\r
US\r
\r
+B0-B3-53 (hex) Blake UK\r
+000000-0FFFFF (base 16) Blake UK\r
+ 177-187, Rutland Road\r
+ Sheffield --select-- S3 9PT\r
+ GB\r
+\r
B0-B3-53 (hex) Zoox\r
B00000-BFFFFF (base 16) Zoox\r
1149 Chess Drive\r
Foster City CA 94404\r
US\r
\r
+14-AE-85 (hex) Qingdao iTechene Technologies Co., Ltd.\r
+200000-2FFFFF (base 16) Qingdao iTechene Technologies Co., Ltd.\r
+ UnitA3-A4,Level8,Block A ,International Innovation Park,No.1Keyuanwei Rd.,Laoshan District\r
+ Qingdao 266100\r
+ CN\r
+\r
+14-AE-85 (hex) Henfred Technology Co., Ltd.\r
+100000-1FFFFF (base 16) Henfred Technology Co., Ltd.\r
+ 3F.-7, No.77, Sec. 1, Xintai 5th Rd\r
+ New Taipei City Xizhi Dist 221\r
+ TW\r
+\r
+14-AE-85 (hex) MTA Systems\r
+A00000-AFFFFF (base 16) MTA Systems\r
+ Pemstraße 2\r
+ Mauthausen 4310\r
+ AT\r
+\r
+64-62-66 (hex) MiiVii Dynamics Technology CO.,LTD\r
+000000-0FFFFF (base 16) MiiVii Dynamics Technology CO.,LTD\r
+ 1408-1415 Tower A BUGG Building,No.18 N. Taipingzhuang Rd,haidian District\r
+ Beijing Beijing 100000\r
+ CN\r
+\r
+64-62-66 (hex) Annapurna labs\r
+100000-1FFFFF (base 16) Annapurna labs\r
+ Matam Scientific Industries Center, Building 8.2\r
+ Mail box 15123 Haifa 3508409\r
+ IL\r
+\r
+64-62-66 (hex) Bühler AG\r
+500000-5FFFFF (base 16) Bühler AG\r
+ Gupfenstrasse 5\r
+ Uzwil 9240\r
+ CH\r
+\r
+64-62-66 (hex) Shenzhen Jie Shi Lian Industrial Co., LTD\r
+E00000-EFFFFF (base 16) Shenzhen Jie Shi Lian Industrial Co., LTD\r
+ 6F,C Building,Jinao Industrial Park,Juling Rd,Guanlan Town,Longhua\r
+ Shenzhen Guangdong 518000\r
+ CN\r
+\r
+64-62-66 (hex) Leontech Limited\r
+800000-8FFFFF (base 16) Leontech Limited\r
+ 1208 WorkingBerg Commercial Buildung, 41-47 Marble Road\r
+ Hong Kong Hong Kong 00000\r
+ HK\r
+\r
+94-CC-04 (hex) Sam Nazarko Trading Ltd\r
+600000-6FFFFF (base 16) Sam Nazarko Trading Ltd\r
+ 18 Watermill Way\r
+ London Surrey SW19 2RD\r
+ GB\r
+\r
+94-CC-04 (hex) Hanzhuo Information Technology(Shanghai) Ltd.\r
+D00000-DFFFFF (base 16) Hanzhuo Information Technology(Shanghai) Ltd.\r
+ Room 2085, building 2, 622 Yingyuan middle Road, Jiading Strict\r
+ Shanghai 201200\r
+ CN\r
+\r
+94-CC-04 (hex) hyBee Inc.\r
+A00000-AFFFFF (base 16) hyBee Inc.\r
+ #1003, Innovalley B, 253, Pangyo-ro, Bundang-gu\r
+ Seongnam-si Gyeonggi-do 13486\r
+ KR\r
+\r
+94-05-BB (hex) iungo\r
+800000-8FFFFF (base 16) iungo\r
+ Vrouwenlaan 62\r
+ Zwolle Overijssel 8017 HS\r
+ NL\r
+\r
+94-CC-04 (hex) Nanjing Yacer Communication Technology Co. Ltd.\r
+200000-2FFFFF (base 16) Nanjing Yacer Communication Technology Co. Ltd.\r
+ 333 Taiping South Road Jinling Yujingyuan 19nd floor Unit K Qin Huai District\r
+ nanjing jiangsu 210000\r
+ CN\r
+\r
+90-E2-FC (hex) Pars Ertebat Afzar Co.\r
+000000-0FFFFF (base 16) Pars Ertebat Afzar Co.\r
+ 1116 – Burlington Tower Business Bay\r
+ Dubai 90072\r
+ AE\r
+\r
+94-05-BB (hex) Dongguan CXWE Technology Co.,Ltd.\r
+200000-2FFFFF (base 16) Dongguan CXWE Technology Co.,Ltd.\r
+ Room 805, building 1, No. 16, Keji 4th Road, Songshanhu\r
+ Dongguan Guangdong 523000\r
+ CN\r
+\r
+94-05-BB (hex) Zimmer GmbH\r
+900000-9FFFFF (base 16) Zimmer GmbH\r
+ Im Salmenkopf 5\r
+ Rheinau Baden-Württemberg 77866\r
+ DE\r
+\r
+94-05-BB (hex) Qingdao Maotran Electronics co., ltd\r
+000000-0FFFFF (base 16) Qingdao Maotran Electronics co., ltd\r
+ Room2907, Building 2 of Minghui International, No.39 of Shiling Road, Laoshan District\r
+ Qingdao Shandong 266000\r
+ CN\r
+\r
+94-05-BB (hex) BAE Systems\r
+E00000-EFFFFF (base 16) BAE Systems\r
+ 21 continental boulevard\r
+ Merrimack NH 03054\r
+ US\r
+\r
+F4-90-CB (hex) Cheetah Medical\r
+C00000-CFFFFF (base 16) Cheetah Medical\r
+ 2A Hashlosha st.\r
+ Tel Aviv 6706055\r
+ IL\r
+\r
+F4-90-CB (hex) A-dec Inc.\r
+B00000-BFFFFF (base 16) A-dec Inc.\r
+ 2601 Crestview Drive\r
+ Newberg OR 97132\r
+ US\r
+\r
+C0-9B-F4 (hex) LTD Delovoy Office\r
+600000-6FFFFF (base 16) LTD Delovoy Office\r
+ Block “B”, floor 6, build 4/1, Stroiteley blvd\r
+ Krasnogorsk 143401\r
+ RU\r
+\r
+94-05-BB (hex) LTE-X, Inc\r
+700000-7FFFFF (base 16) LTE-X, Inc\r
+ 4F Ginza Showa Dori Building 8-14-14 Ginza\r
+ Chuo-ku Tokyo 104-0062\r
+ JP\r
+\r
+F4-90-CB (hex) TEQ SA\r
+700000-7FFFFF (base 16) TEQ SA\r
+ Via al Municipio 16\r
+ Barbengo Ticino 6917\r
+ CH\r
+\r
+9C-43-1E (hex) Midas Technology, Inc. dba Stem Audio / Phoenix Au\r
+E00000-EFFFFF (base 16) Midas Technology, Inc. dba Stem Audio / Phoenix Au\r
+ 2552 White Road, Suite A\r
+ Irvine CA 92614\r
+ US\r
+\r
+E8-B4-70 (hex) YAWATA ELECTRIC INDUSTRIAL CO.,LTD.\r
+400000-4FFFFF (base 16) YAWATA ELECTRIC INDUSTRIAL CO.,LTD.\r
+ 1-17-1 Ohmorihigashi\r
+ Ohta-ku Tokyo 143-0012\r
+ JP\r
+\r
+A4-DA-22 (hex) Grundig\r
+A00000-AFFFFF (base 16) Grundig\r
+ Steinhof 39\r
+ Erkrath North Rhine-Westphalia 40699\r
+ DE\r
+\r
+E8-B4-70 (hex) Tibit Communications\r
+700000-7FFFFF (base 16) Tibit Communications\r
+ 1 Willowbrook Court, Suite 150\r
+ Petaluma CA 94954\r
+ US\r
+\r
+94-FB-A7 (hex) Shanghai Hyco Genyong Technology Co., Ltd.\r
+900000-9FFFFF (base 16) Shanghai Hyco Genyong Technology Co., Ltd.\r
+ Room 105, 1/F, Building B, No.999 of Huaxu Road, Qingpu District, Shanghai, China\r
+ Shanghai 201702\r
+ CN\r
+\r
+94-FB-A7 (hex) Creotech Instruments S.A.\r
+D00000-DFFFFF (base 16) Creotech Instruments S.A.\r
+ ul. Gen. L. Okulickiego 7/9\r
+ Piaseczno Mazovia 05-500\r
+ PL\r
+\r
4C-4B-F9 (hex) Shenzhen dingsheng technology co., LTD\r
400000-4FFFFF (base 16) Shenzhen dingsheng technology co., LTD\r
Floor 3, building 5, kaijeda industrial zone, no.97, huaxing road, langkou community, dalang street, longhua district\r
Suwon-si Gyeonggi-do 16521\r
KR\r
\r
-D0-C8-57 (hex) IFLYTEK CO.,LTD.\r
-D00000-DFFFFF (base 16) IFLYTEK CO.,LTD.\r
- National Intelligent Speech High-tech Industrialization Base, No. 666, Wangjiang Road West,\r
- Heifei An hui 230088\r
- CN\r
-\r
60-95-CE (hex) Synamedia\r
C00000-CFFFFF (base 16) Synamedia\r
Luipaardstraat 12\r
Zhuhai Guangdong 519080\r
CN\r
\r
+90-E2-FC (hex) Huddly AS\r
+900000-9FFFFF (base 16) Huddly AS\r
+ Karenslyst Allé 51\r
+ Oslo 0279\r
+ NO\r
+\r
+90-E2-FC (hex) Shenzhen Dingsheng Intelligent Technology Co., Ltd\r
+B00000-BFFFFF (base 16) Shenzhen Dingsheng Intelligent Technology Co., Ltd\r
+ 10/F Block C, Skyworth Building, Gaoxin South 1st Rd., Hi-Tech Park, Nanshan District\r
+ Shenzhen Guangdong 518000\r
+ CN\r
+\r
+14-AE-85 (hex) Veo Technologies\r
+900000-9FFFFF (base 16) Veo Technologies\r
+ Aldersrogade 6c, 4. sal\r
+ København Denmark 2100\r
+ DK\r
+\r
+14-AE-85 (hex) SHENZHEN HONOR ELECTRONIC CO.,LTD\r
+700000-7FFFFF (base 16) SHENZHEN HONOR ELECTRONIC CO.,LTD\r
+ No. A Building, Xinghui Industrial Park, Gushu No. 2Rd,\r
+ SHEN ZHEN GUANG DONG 518000\r
+ CN\r
+\r
+94-CC-04 (hex) Shenzhen Link technology Co.,Ltd\r
+300000-3FFFFF (base 16) Shenzhen Link technology Co.,Ltd\r
+ 901,9/F,Dahong High TechIndusryPark?NO.6-18,Xinhe Road,Xinqiao Community,Baoan District\r
+ Shenzhen Guangdong 518000\r
+ CN\r
+\r
+94-CC-04 (hex) SHENZHEN SANRAY TECHNOLOGY CO.,LTD\r
+500000-5FFFFF (base 16) SHENZHEN SANRAY TECHNOLOGY CO.,LTD\r
+ 1B08 2/F Folk Culture Industrial Park,Qunli Second Road, Baoan District\r
+ Shenzhen GuangDong 518101\r
+ CN\r
+\r
+94-CC-04 (hex) GOCOAX, INC\r
+100000-1FFFFF (base 16) GOCOAX, INC\r
+ 15902A Halliburton Rd #662 \r
+ Hacienda Heights CA 91745\r
+ US\r
+\r
+94-05-BB (hex) Chengdu Zhongheng Network Co.,Ltd.\r
+500000-5FFFFF (base 16) Chengdu Zhongheng Network Co.,Ltd.\r
+ No.898 Baicao Road, Chengdu High-tech Zone (Western District)\r
+ Chengdu Sichuan 611731\r
+ CN\r
+\r
+D0-C8-57 (hex) IFLYTEK CO.,LTD.\r
+D00000-DFFFFF (base 16) IFLYTEK CO.,LTD.\r
+ National Intelligent Speech High-tech Industrialization Base, No. 666, Wangjiang Road West,\r
+ Hefei An hui 230088\r
+ CN\r
+\r
+94-CC-04 (hex) Shanxi Baixin Information Technology Co., Ltd.\r
+C00000-CFFFFF (base 16) Shanxi Baixin Information Technology Co., Ltd.\r
+ Room 210-213, Room 215-217, Room 219-220, No.2, Yari Street, Taiyuan University Park, Shanxi Comprehensive Reform Demonstration Zone\r
+ Taiyuan Shanxi 030032\r
+ CN\r
+\r
+94-05-BB (hex) ZIGPOS GmbH\r
+600000-6FFFFF (base 16) ZIGPOS GmbH\r
+ Räcknitzhöhe 35a\r
+ Dresden Saxony 01217\r
+ DE\r
+\r
+94-05-BB (hex) SolarEdge Technologies\r
+A00000-AFFFFF (base 16) SolarEdge Technologies\r
+ 1 Abba Eban St.\r
+ Herzelia 46725\r
+ IL\r
+\r
+F4-90-CB (hex) Airbeam Wireless Technologies Inc.\r
+600000-6FFFFF (base 16) Airbeam Wireless Technologies Inc.\r
+ #125, 21320 Gordon Way\r
+ Richmond British Columbia V6W 1J8\r
+ CA\r
+\r
+C0-9B-F4 (hex) Annapurna labs\r
+000000-0FFFFF (base 16) Annapurna labs\r
+ Matam Scientific Industries Center, Building 8.2\r
+ Mail box 15123 Haifa 3508409\r
+ IL\r
+\r
+C0-9B-F4 (hex) Alcatraz AI Inc.\r
+900000-9FFFFF (base 16) Alcatraz AI Inc.\r
+ 1808 El Camino Real\r
+ Redwood City CA 94063\r
+ US\r
+\r
+F4-90-CB (hex) RSAE Labs Inc\r
+E00000-EFFFFF (base 16) RSAE Labs Inc\r
+ 400 E 16th St\r
+ Panama City FL 32405\r
+ US\r
+\r
+C0-9B-F4 (hex) Big Dutchman International GmbH\r
+700000-7FFFFF (base 16) Big Dutchman International GmbH\r
+ Auf der Lage, 2\r
+ Vechta Niedersachsen 49377\r
+ DE\r
+\r
+E8-B4-70 (hex) Anduril Industries\r
+C00000-CFFFFF (base 16) Anduril Industries\r
+ 2272 Michelson Dr\r
+ Irvine CA 92612\r
+ US\r
+\r
+C0-9B-F4 (hex) Continental Automotive Component Malaysia Sdn.Bhd.\r
+E00000-EFFFFF (base 16) Continental Automotive Component Malaysia Sdn.Bhd.\r
+ 2455, MK.1, Tingkat Perusahaan 2A,\r
+ Prai Industrial Estate, Prai, Penang 13600\r
+ MY\r
+\r
+E8-B4-70 (hex) Webfleet Solutions B.V.\r
+300000-3FFFFF (base 16) Webfleet Solutions B.V.\r
+ De Ruijterkade 154\r
+ Amsterdam 1011 AC\r
+ NL\r
+\r
+E8-B4-70 (hex) Medica Corporation\r
+D00000-DFFFFF (base 16) Medica Corporation\r
+ 5 Oak Park Drive\r
+ Bedford MA 01730\r
+ US\r
+\r
20-85-93 (hex) UNILUMIN GROUP CO.,LTD\r
300000-3FFFFF (base 16) UNILUMIN GROUP CO.,LTD\r
No.112 Yongfu Rd.,BaoanDistrict,\r
Paris 75002\r
FR\r
\r
+14-AE-85 (hex) Trimble LEM\r
+800000-8FFFFF (base 16) Trimble LEM\r
+ 10368 Westmoor Dr\r
+ Westminster CO 80021\r
+ US\r
+\r
+14-AE-85 (hex) Kayamatics Limited\r
+000000-0FFFFF (base 16) Kayamatics Limited\r
+ Room 1209, Trend Centre, 29 Cheung Lee Street\r
+ Chaiwan NA NA\r
+ HK\r
+\r
+64-62-66 (hex) Shenzhen C & D Electronics Co., Ltd.\r
+700000-7FFFFF (base 16) Shenzhen C & D Electronics Co., Ltd.\r
+ 9th FIoor, Building 9, No.1 Qingxiang road, BaoNeng Science and TechnoIogy Industrial Park, Longhua New District\r
+ ShenZhen GuangDong 518000\r
+ CN\r
+\r
+64-62-66 (hex) Signal Hound\r
+B00000-BFFFFF (base 16) Signal Hound\r
+ 1502 SE Commerce Ave Suite 101\r
+ Battle Ground WA 98604\r
+ US\r
+\r
+64-62-66 (hex) Chunghwa System Integration Co., Ltd.\r
+900000-9FFFFF (base 16) Chunghwa System Integration Co., Ltd.\r
+ 2F., No. 35, Aiguo E. Rd.,\r
+ Taipei 106\r
+ TW\r
+\r
+64-62-66 (hex) Protectli\r
+200000-2FFFFF (base 16) Protectli\r
+ 1315 Hot Springs Way\r
+ Vista CA 92081\r
+ US\r
+\r
+64-62-66 (hex) Redstone Systems, Inc.\r
+400000-4FFFFF (base 16) Redstone Systems, Inc.\r
+ 24 School Street, 2nd floor\r
+ Boston 02108\r
+ US\r
+\r
+64-62-66 (hex) Kobol Innovations Pte. Ltd.\r
+D00000-DFFFFF (base 16) Kobol Innovations Pte. Ltd.\r
+ 101 Cecil Street, #26-01/07 Tong Eng Building\r
+ Singapore 069533\r
+ SG\r
+\r
+94-05-BB (hex) AUSTAR HEARING SCIENCE AND TECHNILIGY(XIAMEN)CO.,LTD\r
+B00000-BFFFFF (base 16) AUSTAR HEARING SCIENCE AND TECHNILIGY(XIAMEN)CO.,LTD\r
+ RM201,No.2Gaoqi South 12th Road,HuliDist\r
+ XIamen Fujian 361006\r
+ CN\r
+\r
+94-05-BB (hex) LAO INDUSTRIA LTDA\r
+C00000-CFFFFF (base 16) LAO INDUSTRIA LTDA\r
+ AV DR MAURO LINDENBERG MONTEIRO, 1003\r
+ OSASCO SÃO PAULO 06278010\r
+ BR\r
+\r
+F4-90-CB (hex) Avilution\r
+500000-5FFFFF (base 16) Avilution\r
+ 103 Shoreline Dr\r
+ Madison AL 35758\r
+ US\r
+\r
+F4-90-CB (hex) Fractyl Labs\r
+900000-9FFFFF (base 16) Fractyl Labs\r
+ 17 HARTWELL AVE\r
+ LEXINGTON MA 02421\r
+ US\r
+\r
+F4-90-CB (hex) OmniNet\r
+400000-4FFFFF (base 16) OmniNet\r
+ 6410 Del Rio Rd\r
+ Charlotte NC 28277\r
+ US\r
+\r
+C0-9B-F4 (hex) JSC NPK ATRONIK\r
+400000-4FFFFF (base 16) JSC NPK ATRONIK\r
+ VARSHAVSKOE SH, 118-1-P XLII K4 10\r
+ Moscow Moscow 117587\r
+ RU\r
+\r
+C0-9B-F4 (hex) Pinpark Inc.\r
+C00000-CFFFFF (base 16) Pinpark Inc.\r
+ 9F., No. 101, Sec. 2, Nanjing E. Rd.,, Zhongshan Dist.,\r
+ Taipei Taiwan 104\r
+ TW\r
+\r
+F4-90-CB (hex) Epitel, Inc.\r
+000000-0FFFFF (base 16) Epitel, Inc.\r
+ 630 S. Stringfellow Ct., Unit #B\r
+ Salt Lake City UT 84111\r
+ US\r
+\r
+C0-9B-F4 (hex) NUCTECH COMPANY LIMITED\r
+B00000-BFFFFF (base 16) NUCTECH COMPANY LIMITED\r
+ 2/F Block A,Tongfang Building,Shuangqinglu,Haidian District\r
+ Beijing Beijing 100084\r
+ CN\r
+\r
+C0-9B-F4 (hex) Osprey Video, Inc\r
+300000-3FFFFF (base 16) Osprey Video, Inc\r
+ 1628 Valwood Parkway Suite 200\r
+ Carrollton TX 75006\r
+ US\r
+\r
+C0-9B-F4 (hex) Infiot Inc.\r
+500000-5FFFFF (base 16) Infiot Inc.\r
+ 75 E. Santa Clara St., Suite 600\r
+ San Jose CA 95113\r
+ US\r
+\r
+E8-B4-70 (hex) plc2 Design GmbH\r
+A00000-AFFFFF (base 16) plc2 Design GmbH\r
+ Hugstmattweg 30\r
+ Freiburg i. Br. 79112\r
+ DE\r
+\r
+E8-B4-70 (hex) DEHN SE + Co KG\r
+800000-8FFFFF (base 16) DEHN SE + Co KG\r
+ Hans-Dehn-Straße 1\r
+ Neumarkt Bavaria 92318\r
+ DE\r
+\r
+E8-B4-70 (hex) internet domain name system beijing engineering research center ltd\r
+200000-2FFFFF (base 16) internet domain name system beijing engineering research center ltd\r
+ 4,4TH SOUTH STREET ZHONG GUAN CUN\r
+ hai dian qu ,beijing BEIJING 100190\r
+ CN\r
+\r
+E8-B4-70 (hex) DongGuan Ramaxel Memory Technology\r
+000000-0FFFFF (base 16) DongGuan Ramaxel Memory Technology\r
+ No.32, Industrial East Road,Innovation Park, High-tech Industrial Development Zone, Songshan Lake, Dongguan City, Guangdong Province,China\r
+ DongGuan Guangdong 523808\r
+ CN\r
+\r
+E8-B4-70 (hex) Miltek Industries Pte Ltd\r
+900000-9FFFFF (base 16) Miltek Industries Pte Ltd\r
+ 62 Ubi Road 1 #10-03, Oxley Bizhub 2. Singapore 408734\r
+ Singapore 408734\r
+ SG\r
+\r
+E8-B4-70 (hex) Elcoma\r
+600000-6FFFFF (base 16) Elcoma\r
+ Rua Barbosa Lima, 149\r
+ Recife Pernambuco 50030-330\r
+ BR\r
+\r
+E8-B4-70 (hex) Alperia Fiber srl \r
+500000-5FFFFF (base 16) Alperia Fiber srl \r
+ Via Dodiciville 8\r
+ Bolzano bz 39100\r
+ IT\r
+\r
4C-4B-F9 (hex) Shandong Linkotech Electronic Co., Ltd.\r
600000-6FFFFF (base 16) Shandong Linkotech Electronic Co., Ltd.\r
22nd Floor, Building 2, Aosheng Building, No.1166 Xinyi Street, High-tech Zone\r
Shenzhen Guangdong 518101\r
CN\r
\r
-7C-BC-84 (hex) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
-D00000-DFFFFF (base 16) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
- B-11 SECTOR-VII\r
- NOIDA UTTAR PRADESH 201301\r
- IN\r
-\r
7C-BC-84 (hex) Shanghai Yitu Technology Co. Ltd\r
300000-3FFFFF (base 16) Shanghai Yitu Technology Co. Ltd\r
23/F, Tower 1, No.523 Loushanguan Road, Changning District\r
Beijing Beijing 100192\r
CN\r
\r
+90-E2-FC (hex) ShenZhen Temwey Innovation Technology Co.,Ltd.\r
+200000-2FFFFF (base 16) ShenZhen Temwey Innovation Technology Co.,Ltd.\r
+ Room 1008, 10/F, Bld.B, Bantian International Centre, No. 5 South Huancheng Road, Bantian Street of Shenzhen Longgang District\r
+ SHENZHEN GUANGDONG 518129\r
+ CN\r
+\r
+90-E2-FC (hex) Dongguan Kangyong electronics technology Co. Ltd\r
+400000-4FFFFF (base 16) Dongguan Kangyong electronics technology Co. Ltd\r
+ No 9,Yincheng 1st Road, Xiabian Village, Chang’an Town\r
+ Dongguan GuangDong 523877\r
+ CN\r
+\r
+90-E2-FC (hex) bitsensing Inc.\r
+800000-8FFFFF (base 16) bitsensing Inc.\r
+ 165, Yeoksam-ro,\r
+ Gangnam-gu, Seoul, Republic of Korea 06247\r
+ KR\r
+\r
+90-E2-FC (hex) Stanley Security\r
+C00000-CFFFFF (base 16) Stanley Security\r
+ 8350 Sunlight Drive\r
+ Fishers IN 46037\r
+ US\r
+\r
+14-AE-85 (hex) iSolution Technologies Co.,Ltd.\r
+D00000-DFFFFF (base 16) iSolution Technologies Co.,Ltd.\r
+ 5F,Bldg #6, Zhongguan Honghualing Industrial South Park\r
+ Shenzhen Guangdong 518055\r
+ CN\r
+\r
+7C-BC-84 (hex) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+D00000-DFFFFF (base 16) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+ B3, Bredon House, 321, Tettenhall Road, Tettenhall\r
+ Wolverhampton West Midlands WV6 0JZ\r
+ GB\r
+\r
+64-62-66 (hex) FaceHeart Inc.\r
+300000-3FFFFF (base 16) FaceHeart Inc.\r
+ Rm. 8, 19F., No.118, Ciyun Rd., East Dist.\r
+ Hsinchu Taiwan 300\r
+ TW\r
+\r
+64-62-66 (hex) Sensoro Co., Ltd.\r
+A00000-AFFFFF (base 16) Sensoro Co., Ltd.\r
+ 7F D-Block, Lei Shing Hong Center, No. 8 Guangshun South Street, Chaoyang District,\r
+ Beijing Beijing 100102\r
+ CN\r
+\r
+64-62-66 (hex) Pass & Seymour, Inc d/b/a Legrand\r
+600000-6FFFFF (base 16) Pass & Seymour, Inc d/b/a Legrand\r
+ 50 Boyd Ave\r
+ Syracuse NY 13209\r
+ US\r
+\r
+94-CC-04 (hex) Shandong free optical technology co., ltd.\r
+B00000-BFFFFF (base 16) Shandong free optical technology co., ltd.\r
+ 195 East First Street, Industrial First Street, Economic Development Zone, Weifang, Weicheng District,\r
+ Weifeng Shandong 216000\r
+ CN\r
+\r
+94-CC-04 (hex) ENTEC Electric & Electronic Co., LTD.\r
+900000-9FFFFF (base 16) ENTEC Electric & Electronic Co., LTD.\r
+ 78-2 Buncheon-ri, Bongdam-eup\r
+ Hwaseong-city Gyungki-do 445-894\r
+ KR\r
+\r
+94-CC-04 (hex) SynchronicIT BV\r
+E00000-EFFFFF (base 16) SynchronicIT BV\r
+ Spoorstraat 155, room 413\r
+ Gennep Nederland 6591 GT\r
+ NL\r
+\r
+F4-90-CB (hex) Simavita (Aust) Pty Ltd\r
+D00000-DFFFFF (base 16) Simavita (Aust) Pty Ltd\r
+ Suite 2.02, L2, 54 Miller Street\r
+ North Sydney NSW 2060\r
+ AU\r
+\r
+F4-90-CB (hex) Ricker Lyman Robotic\r
+300000-3FFFFF (base 16) Ricker Lyman Robotic\r
+ 319 Main Street\r
+ Beacon NY 12508\r
+ US\r
+\r
+F4-90-CB (hex) Beijing Penslink Co., Ltd.\r
+800000-8FFFFF (base 16) Beijing Penslink Co., Ltd.\r
+ 502,12rd floor,no.2,Fangheng International Center Beijing, Chaoyang district 100102\r
+ Beijing Beijing 100102\r
+ CN\r
+\r
+F4-90-CB (hex) Private\r
+A00000-AFFFFF (base 16) Private\r
+\r
+C0-9B-F4 (hex) The Professional Monitor Company Ltd\r
+D00000-DFFFFF (base 16) The Professional Monitor Company Ltd\r
+ Holme Court A1\r
+ Biggleswade Bedfordshire SG189ST\r
+ GB\r
+\r
+C0-9B-F4 (hex) Connected Space Management\r
+100000-1FFFFF (base 16) Connected Space Management\r
+ 62 boulevard Diderot\r
+ Paris 75012\r
+ FR\r
+\r
+C0-9B-F4 (hex) Inveo\r
+A00000-AFFFFF (base 16) Inveo\r
+ Rzemieslnicza 21\r
+ Kozy 43-340\r
+ PL\r
+\r
20-85-93 (hex) Great Lite International\r
700000-7FFFFF (base 16) Great Lite International\r
11F., No.207-2, Sec. 3, Beixin Rd., Xindian Dist.,\r
Oldenburg Niedersachsen 26129\r
DE\r
\r
-DC-44-27 (hex) Tesla Motors, Inc\r
-100000-1FFFFF (base 16) Tesla Motors, Inc\r
- 3500 Deer Creek Road\r
- Palo Alto CA 94304\r
- US\r
-\r
78-C2-C0 (hex) ShenZhen TuLing Robot CO.,LTD\r
500000-5FFFFF (base 16) ShenZhen TuLing Robot CO.,LTD\r
BLK 9, No 28, Langshan Road, Northern District of High Tech. Industry Park, Nanshan Dist., SZ., PRC.\r
shenzhen guangdong 518000\r
CN\r
\r
+B0-B3-53 (hex) VOXISCOM\r
+800000-8FFFFF (base 16) VOXISCOM\r
+ Rue Jules Ferry\r
+ PORNIC 44210\r
+ FR\r
+\r
+B0-B3-53 (hex) Innotas Elektronik GmbH\r
+400000-4FFFFF (base 16) Innotas Elektronik GmbH\r
+ Rathenaustr. 18a\r
+ Zittau D-02763\r
+ DE\r
+\r
3C-FA-D3 (hex) Mirico\r
E00000-EFFFFF (base 16) Mirico\r
30 DongSan Rd 9th floor Mirico\r
Ansan Gyunggi 15434\r
KR\r
\r
-B0-B3-53 (hex) Innotas Elektronik GmbH\r
-400000-4FFFFF (base 16) Innotas Elektronik GmbH\r
- Rathenaustr. 18a\r
- Zittau D-02763\r
+90-E2-FC (hex) Power Engineering & Manufacturing, Inc.\r
+A00000-AFFFFF (base 16) Power Engineering & Manufacturing, Inc.\r
+ 1463 94th Lane NE\r
+ Blaine MN 55449\r
+ US\r
+\r
+90-E2-FC (hex) Sindoh Techno Co., Ltd.\r
+600000-6FFFFF (base 16) Sindoh Techno Co., Ltd.\r
+ Sindoh Bldg., 6, Hyoryeong-ro 61-gil, Seocho-gu\r
+ Seoul 06643\r
+ KR\r
+\r
+90-E2-FC (hex) Shenzhen Hisource Technology Development CO.,Ltd.\r
+300000-3FFFFF (base 16) Shenzhen Hisource Technology Development CO.,Ltd.\r
+ Dalang\r
+ Shenzhen Guangdong 518109\r
+ CN\r
+\r
+90-E2-FC (hex) TOTALONE TECHNOLOGY CO., LTD.\r
+500000-5FFFFF (base 16) TOTALONE TECHNOLOGY CO., LTD.\r
+ 3F.-1, NO.18, LN. 48, XingShan RD.,\r
+ Taipei Neihu dist 11469\r
+ TW\r
+\r
+90-E2-FC (hex) Yite technology\r
+100000-1FFFFF (base 16) Yite technology\r
+ No. 56, Xiaobei Rd., North Dist\r
+ tainan 70448 \r
+ TW\r
+\r
+90-E2-FC (hex) DevCom spol. s r.o.\r
+E00000-EFFFFF (base 16) DevCom spol. s r.o.\r
+ Božanovská 884\r
+ Praha Select a State 19300\r
+ CZ\r
+\r
+14-AE-85 (hex) IO Industries Inc.\r
+C00000-CFFFFF (base 16) IO Industries Inc.\r
+ 15940 Robin's Hill Rd\r
+ London Ontario N5V 0A4\r
+ CA\r
+\r
+14-AE-85 (hex) AZ-TECHNOLOGY SDN BHD\r
+500000-5FFFFF (base 16) AZ-TECHNOLOGY SDN BHD\r
+ A108 & A109 BLOCK A KELANA BUSINESS CENTRE NO: 97 JALAN SS7/2 KELANA JAYA\r
+ PETALING JAYA SELANGOR 47301\r
+ MY\r
+\r
+14-AE-85 (hex) CENTERVUE SPA\r
+400000-4FFFFF (base 16) CENTERVUE SPA\r
+ VIA SAN MARCO 9/H\r
+ PADOVA PADOVA 35129\r
+ IT\r
+\r
+90-E2-FC (hex) Fair Winds Digital srl\r
+700000-7FFFFF (base 16) Fair Winds Digital srl\r
+ Via Italo Svevo 85\r
+ Rome Italy 00137\r
+ IT\r
+\r
+14-AE-85 (hex) TMG TE GmbH\r
+600000-6FFFFF (base 16) TMG TE GmbH\r
+ Zur Gießerei 10\r
+ Karlsruhe 776227\r
DE\r
\r
-B0-B3-53 (hex) VOXISCOM\r
-800000-8FFFFF (base 16) VOXISCOM\r
- Rue Jules Ferry\r
- PORNIC 44210\r
+14-AE-85 (hex) NTC SOFT\r
+B00000-BFFFFF (base 16) NTC SOFT\r
+ B-805, Gwangmyeong SK Techno park, 60, Haan-ro,\r
+ Gwangmyeong-si Gyeonggi-do 14322\r
+ KR\r
+\r
+14-AE-85 (hex) Sercomm Corporation.\r
+E00000-EFFFFF (base 16) Sercomm Corporation.\r
+ 3F,No.81,Yu-Yih Rd.,Chu-Nan Chen\r
+ Miao-Lih Hsuan 115\r
+ TW\r
+\r
+64-62-66 (hex) Jiangsu Aisida Electronic Co.,Ltd\r
+C00000-CFFFFF (base 16) Jiangsu Aisida Electronic Co.,Ltd\r
+ Aisida Industrial Park,Lanling Road,Danyang Development Zone\r
+ DanYang JiangSu 212300\r
+ CN\r
+\r
+94-CC-04 (hex) Hangzhou Yongkong Technology Co., Ltd.\r
+000000-0FFFFF (base 16) Hangzhou Yongkong Technology Co., Ltd.\r
+ Room 503, Building 12, Lefu Zhihui Garden, 28 Xiangyuan Road, Gongshu Distric\r
+ Hangzhou Zhejiang 310000\r
+ CN\r
+\r
+94-CC-04 (hex) Gowing Business And Contracting Wenzhou Co., LTD\r
+700000-7FFFFF (base 16) Gowing Business And Contracting Wenzhou Co., LTD\r
+ Room 101, No.4 Liming Industrial District, Lucheng, Wenzhou, China\r
+ Wenzhou 325000\r
+ CN\r
+\r
+94-CC-04 (hex) CircuitWerkes, Inc.\r
+800000-8FFFFF (base 16) CircuitWerkes, Inc.\r
+ 2805 NW 6th St\r
+ Gainesville FL 32609\r
+ US\r
+\r
+14-AE-85 (hex) IFLYTEK CO.,LTD.\r
+300000-3FFFFF (base 16) IFLYTEK CO.,LTD.\r
+ National Intelligent Speech High-tech Industrialization Base, No. 666, Wangjiang Road West,\r
+ Hefei An hui 230088\r
+ CN\r
+\r
+94-05-BB (hex) Shenzhen Baolijie Technology Co., Ltd.\r
+400000-4FFFFF (base 16) Shenzhen Baolijie Technology Co., Ltd.\r
+ D2,No.47,Shasan Road,Sha jing Street,Baoan District\r
+ Shenzhen Kowloon 518104\r
+ CN\r
+\r
+94-CC-04 (hex) ProConnections, Inc.\r
+400000-4FFFFF (base 16) ProConnections, Inc.\r
+ 30 Massachusetts, Ave, Suite 301\r
+ North Andover MA 01845\r
+ US\r
+\r
+94-05-BB (hex) Neurik AG\r
+300000-3FFFFF (base 16) Neurik AG\r
+ Im alten Riet 143\r
+ Schaan SCHAAN 9494\r
+ LI\r
+\r
+94-05-BB (hex) Dongguan Kingtron Electronics Tech Co., Ltd\r
+100000-1FFFFF (base 16) Dongguan Kingtron Electronics Tech Co., Ltd\r
+ No.3 Fumin North Rd,Shu'an Industrial Park, Humen Town\r
+ Dongguan Guangdong China 523929\r
+ CN\r
+\r
+94-05-BB (hex) Sunthink S&T Development Co.,Ltd\r
+D00000-DFFFFF (base 16) Sunthink S&T Development Co.,Ltd\r
+ A3-f1, xinghezhong Technology Green Valley, No.14, luolei Industrial Avenue, Shiyan street, Bao'an District\r
+ Shenzhen 518100\r
+ CN\r
+\r
+DC-44-27 (hex) Tesla,Inc.\r
+100000-1FFFFF (base 16) Tesla,Inc.\r
+ 3500 Deer Creek Road\r
+ Palo Alto CA 94304\r
+ US\r
+\r
+F4-90-CB (hex) ICE Gateway GmbH\r
+200000-2FFFFF (base 16) ICE Gateway GmbH\r
+ Am Studio 2\r
+ Berlin Berlin 12489\r
+ DE\r
+\r
+F4-90-CB (hex) DELEM BV\r
+100000-1FFFFF (base 16) DELEM BV\r
+ LUCHTHAVEN WEG 42\r
+ 5657 EB EINDHOVEN \r
+ NL\r
+\r
+C0-9B-F4 (hex) SHENZHEN WINS ELECTRONIC TECHNOLOGY CO., LTD\r
+800000-8FFFFF (base 16) SHENZHEN WINS ELECTRONIC TECHNOLOGY CO., LTD\r
+ Baoan Xixiang Xinbaoji Industry Park,Building A1-2\r
+ Shenzhen Guangdong 518026\r
+ CN\r
+\r
+C0-9B-F4 (hex) Hitachi High-Tech Materials Corporation\r
+200000-2FFFFF (base 16) Hitachi High-Tech Materials Corporation\r
+ Toranomon Hills Business Tower, 1-17-1 Toranomon, Minato-ku\r
+ Tokyo 105-6413\r
+ JP\r
+\r
+E8-B4-70 (hex) Autocom Diagnostic Partner AB\r
+100000-1FFFFF (base 16) Autocom Diagnostic Partner AB\r
+ Grafitvägen 23B\r
+ TROLLHÄTTAN 46138\r
+ SE\r
+\r
+E8-B4-70 (hex) UNICACCES GROUPE\r
+E00000-EFFFFF (base 16) UNICACCES GROUPE\r
+ 24 Chemin des Vieilles Vignes\r
+ La tour-d'aigues Vaucluse 84240\r
FR\r
+\r
+E8-B4-70 (hex) Digifocus Technology Inc.\r
+B00000-BFFFFF (base 16) Digifocus Technology Inc.\r
+ 6F, No.89, Xinhu 1st Rd., Neihu Dist.\r
+ Taipei City 11494\r
+ TW\r
+\r
+94-FB-A7 (hex) Silver-I Co.,LTD.\r
+800000-8FFFFF (base 16) Silver-I Co.,LTD.\r
+ 2-14-4 Shinyokohama,kohoku-ku\r
+ Yokohama Kanagawa 222-0033\r
+ JP\r
MORNAGO VA 21020\r
IT\r
\r
-70-B3-D5 (hex) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
-6BE000-6BEFFF (base 16) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
- B-11 SECTOR-VII\r
- NOIDA UTTAR PRADESH 201301\r
- IN\r
-\r
70-B3-D5 (hex) Precitec Optronik GmbH\r
0C5000-0C5FFF (base 16) Precitec Optronik GmbH\r
Schleussnerstraße 54\r
Hong Kong Hong Kong 00000\r
HK\r
\r
+70-B3-D5 (hex) Technology Link Corporation\r
+B1B000-B1BFFF (base 16) Technology Link Corporation\r
+ Shin-Yokohama Kohoku-ku\r
+ yokohama kanagawa 222-0033\r
+ JP\r
+\r
+70-B3-D5 (hex) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+6BE000-6BEFFF (base 16) VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+ B3, Bredon House, 321, Tettenhall Road, Tettenhall\r
+ Wolverhampton West Midlands WV6 0JZ\r
+ GB\r
+\r
+70-B3-D5 (hex) Todd Digital Limited\r
+C9A000-C9AFFF (base 16) Todd Digital Limited\r
+ Level 15, 95 Customhouse Quay\r
+ Wellington 6011\r
+ NZ\r
+\r
+70-B3-D5 (hex) JENG IoT BV\r
+5AF000-5AFFFF (base 16) JENG IoT BV\r
+ Steenbokstraat 33\r
+ APELDOORN Gelderland 7324 AZ\r
+ NL\r
+\r
+70-B3-D5 (hex) TXMission Ltd.\r
+F47000-F47FFF (base 16) TXMission Ltd.\r
+ CP House, Otterspool Way\r
+ Watford Hertfordshire WD25 8HU\r
+ GB\r
+\r
+70-B3-D5 (hex) sensorway\r
+C52000-C52FFF (base 16) sensorway\r
+ A-339 samsong techno valley, 140 tongilro, deockyanggu\r
+ goyangsi gyeonggido 10594\r
+ KR\r
+\r
+70-B3-D5 (hex) Tucsen Photonics Co., Ltd. \r
+8A7000-8A7FFF (base 16) Tucsen Photonics Co., Ltd. \r
+ 6F NO.1 building Caimao Zone, 756# Qi an Road, Gaishan Town, Cangshan Area, Fuzhou, Fujian, PR, CHINA.\r
+ fuzhou 350000\r
+ CN\r
+\r
+70-B3-D5 (hex) Beijing Yourong Runda Rechnology Development Co.Ltd.\r
+980000-980FFF (base 16) Beijing Yourong Runda Rechnology Development Co.Ltd.\r
+ Changping District Science and Technology Park Advanced Road 37\r
+ Beijing 6219650\r
+ CN\r
+\r
+70-B3-D5 (hex) KDT Corp.\r
+E72000-E72FFF (base 16) KDT Corp.\r
+ no1705-1, BLDG 3#, Lantian shixin plaza, keqiao zone\r
+ shaoxing zhejiang 312030\r
+ CN\r
+\r
+70-B3-D5 (hex) AUTOMATICA Y REGULACION S.A.\r
+EBF000-EBFFFF (base 16) AUTOMATICA Y REGULACION S.A.\r
+ Condell 1735, Nunoa\r
+ Santiago RM 7770331\r
+ CL\r
+\r
+70-B3-D5 (hex) R.C. Systems Inc\r
+52F000-52FFFF (base 16) R.C. Systems Inc\r
+ 8621 hwy. 6\r
+ hitchcock TX 77563\r
+ US\r
+\r
+70-B3-D5 (hex) Dalcnet srl\r
+1CF000-1CFFFF (base 16) Dalcnet srl\r
+ Via Meucci 35\r
+ Brendola Vicenza 36040\r
+ IT\r
+\r
+70-B3-D5 (hex) Digital Solutions JSC\r
+D9F000-D9FFFF (base 16) Digital Solutions JSC\r
+ room 4, office 1, 3rd floor, building 7, house 9a, 2nd Sinichkina Str.\r
+ Moscow 111020\r
+ RU\r
+\r
+70-B3-D5 (hex) DOGA\r
+62A000-62AFFF (base 16) DOGA\r
+ 11 rue Lavoisier\r
+ MAUREPAS 78310\r
+ FR\r
+\r
+70-B3-D5 (hex) Oculii\r
+B96000-B96FFF (base 16) Oculii\r
+ 829 Space Dr\r
+ Beavercreek OH 45434\r
+ US\r
+\r
+70-B3-D5 (hex) XPS ELETRONICA LTDA\r
+4F3000-4F3FFF (base 16) XPS ELETRONICA LTDA\r
+ AVENIDA JAÇANÃ, 470/474 - VILA NELSON\r
+ SÃO PAULO SÃO PAULO 02273-001\r
+ BR\r
+\r
+70-B3-D5 (hex) Firecom, Inc.\r
+331000-331FFF (base 16) Firecom, Inc.\r
+ 3927 59th Street\r
+ Woodside NY 11377\r
+ US\r
+\r
+70-B3-D5 (hex) Remote Diagnostic Technologies Ltd\r
+C99000-C99FFF (base 16) Remote Diagnostic Technologies Ltd\r
+ Pavilion C2 Ashwood Park, Ashwood Way\r
+ Basingstoke Hampshire RG23 8BG\r
+ GB\r
+\r
+70-B3-D5 (hex) NEUROPHET, Inc.\r
+E31000-E31FFF (base 16) NEUROPHET, Inc.\r
+ 3rd Floor, 175, Yeoksam-ro, Gangnam-gu, seoul\r
+ Seoul Province 06247\r
+ KR\r
+\r
+70-B3-D5 (hex) Chromateq\r
+944000-944FFF (base 16) Chromateq\r
+ 191, allée de Lauzard, Bat. B, RDC 1 (Chromateq)\r
+ Saint Gély du Fesc 34980\r
+ FR\r
+\r
+70-B3-D5 (hex) Elk Solutions, LLC\r
+1A7000-1A7FFF (base 16) Elk Solutions, LLC\r
+ 12708 Misty Grove St\r
+ Moorpark CA 93021\r
+ US\r
+\r
+70-B3-D5 (hex) KAYA Instruments\r
+F3D000-F3DFFF (base 16) KAYA Instruments\r
+ 20 HaMesila St.\r
+ Nesher 3688520\r
+ IL\r
+\r
+70-B3-D5 (hex) Gogo Business Aviation\r
+3E0000-3E0FFF (base 16) Gogo Business Aviation\r
+ 105 Edgeview Dr., Suite 300\r
+ Broomfield CO 80021\r
+ US\r
+\r
+70-B3-D5 (hex) Asiga Pty Ltd\r
+53E000-53EFFF (base 16) Asiga Pty Ltd\r
+ Unit 2, 19-21 Bourke Road\r
+ Alexandria New South Wales 2015\r
+ AU\r
+\r
+70-B3-D5 (hex) ENABLER LTD.\r
+15A000-15AFFF (base 16) ENABLER LTD.\r
+ 29F Shiroyama Trust Tower 4-3-1 Toranomon \r
+ Minato-ku Tokyo 105-6029\r
+ JP\r
+\r
+70-B3-D5 (hex) LINEAGE POWER PVT LTD.,\r
+62E000-62EFFF (base 16) LINEAGE POWER PVT LTD.,\r
+ 30-A1, KIADB, 1ST PHASE INDUSTRIAL ESTATE,KUMBALGODU, BANGALORE-MYSORE ROAD\r
+ BANGALORE KARNATAKA 560074\r
+ IN\r
+\r
+70-B3-D5 (hex) Salupo Sas\r
+898000-898FFF (base 16) Salupo Sas\r
+ Via Laganeto n. 129\r
+ Rocca di Capri Leone Italia / ME / Sicilia 98070\r
+ IT\r
+\r
+70-B3-D5 (hex) Nippon Safety co,ltd\r
+872000-872FFF (base 16) Nippon Safety co,ltd\r
+ 1, suimeicho\r
+ Amagasaki Hyogo 660-0082\r
+ JP\r
+\r
+70-B3-D5 (hex) Grupo Epelsa S.L.\r
+40D000-40DFFF (base 16) Grupo Epelsa S.L.\r
+ C/ Punto Net,3\r
+ Alcala de Henares Madrid 28805\r
+ ES\r
+\r
70-B3-D5 (hex) EVCO SPA\r
A80000-A80FFF (base 16) EVCO SPA\r
VIA FELTRE N. 81\r
BRUGES 33520\r
FR\r
\r
+70-B3-D5 (hex) Newtec A/S\r
+18F000-18FFFF (base 16) Newtec A/S\r
+ Stærmosegårdsvej 18\r
+ Odense SV Region Syd 5230\r
+ DK\r
+\r
+70-B3-D5 (hex) AUDIO VISUAL DIGITAL SYSTEMS\r
+A7F000-A7FFFF (base 16) AUDIO VISUAL DIGITAL SYSTEMS\r
+ PLOT NO.180 PHASE V SECTOR56, HSIIDC I.E KUNDLI SONEPAT\r
+ SONEPAT HARYANA 131028\r
+ IN\r
+\r
+70-B3-D5 (hex) DEUTA-WERKE GmbH\r
+1BF000-1BFFFF (base 16) DEUTA-WERKE GmbH\r
+ Paffrather Str. 140\r
+ Bergisch Gladbach North Rhine-Westphalia 51465\r
+ DE\r
+\r
+70-B3-D5 (hex) Cetitec GmbH\r
+B36000-B36FFF (base 16) Cetitec GmbH\r
+ Mannheimer Strasse 17\r
+ Pforzheim 75179\r
+ DE\r
+\r
+70-B3-D5 (hex) DONG IL VISION Co., Ltd.\r
+038000-038FFF (base 16) DONG IL VISION Co., Ltd.\r
+ #9 Ftrek tower, 11-25, Simindaero 327 beongil,Dongan-gu\r
+ Anyangi-Si Gyeonggi-Do 14055\r
+ KR\r
+\r
+70-B3-D5 (hex) Kamacho Scale Co., Ltd.\r
+385000-385FFF (base 16) Kamacho Scale Co., Ltd.\r
+ 2246 Mure\r
+ Takamatsu-shi Kagawa-ken 761-0196\r
+ JP\r
+\r
+70-B3-D5 (hex) Visual Robotics\r
+0F4000-0F4FFF (base 16) Visual Robotics\r
+ 38 Irving Rd\r
+ Eugene OR 97404\r
+ US\r
+\r
+70-B3-D5 (hex) Vessel Technology Ltd\r
+44D000-44DFFF (base 16) Vessel Technology Ltd\r
+ Banchory Business Centre, Burn O'Bennie Road\r
+ Banchory Aberdeenshire AB31 5ZU\r
+ GB\r
+\r
+70-B3-D5 (hex) Munters\r
+FA8000-FA8FFF (base 16) Munters\r
+ Hasivim 18\r
+ Pethch Tikva Israel 4959376\r
+ IL\r
+\r
+70-B3-D5 (hex) TEX COMPUTER SRL \r
+6C2000-6C2FFF (base 16) TEX COMPUTER SRL \r
+ VIA MERCADANTE 35\r
+ CATTOLICA RIMINI 47841\r
+ IT\r
+\r
+70-B3-D5 (hex) TangRen C&S CO., Ltd\r
+3FC000-3FCFFF (base 16) TangRen C&S CO., Ltd\r
+ 3a-5d, Tingwei Daxia, Tingwei Industrial Park, No. 6, Liufang Road, Bao'an District\r
+ Shenzhen Guangdong 518052\r
+ CN\r
+\r
+70-B3-D5 (hex) LOTES TM OOO\r
+EA5000-EA5FFF (base 16) LOTES TM OOO\r
+ Barklaya 22, str.1\r
+ Moscow 121309\r
+ RU\r
+\r
+70-B3-D5 (hex) Yi An Electronics Co., Ltd\r
+F28000-F28FFF (base 16) Yi An Electronics Co., Ltd\r
+ 5F.-2, No. 81, Sec. 1, Xintai 5th Rd., Xizhi Dist\r
+ New Taipei City 22101\r
+ TW\r
+\r
+70-B3-D5 (hex) Ariston Thermo s.p.a.\r
+3D6000-3D6FFF (base 16) Ariston Thermo s.p.a.\r
+ Via Aristide Merloni 45\r
+ Fabriano Ancona 60044\r
+ IT\r
+\r
+70-B3-D5 (hex) MG s.r.l.\r
+130000-130FFF (base 16) MG s.r.l.\r
+ via Monte Bianco, 1\r
+ Solbiate Olona VA 21058\r
+ IT\r
+\r
+70-B3-D5 (hex) DORLET SAU\r
+639000-639FFF (base 16) DORLET SAU\r
+ Albert Eistein 34\r
+ Alava SPAIN 01510\r
+ ES\r
+\r
+70-B3-D5 (hex) OOO ORION-R\r
+047000-047FFF (base 16) OOO ORION-R\r
+ Novoselov str., 40A, room N200\r
+ Ryazan 390048\r
+ RU\r
+\r
+70-B3-D5 (hex) Glory Technology Service Inc.\r
+801000-801FFF (base 16) Glory Technology Service Inc.\r
+ 3F., No.43-1, Ln. 11, Sec. 6, Minquan E. Rd\r
+ Taipei City Neihu Dist 114\r
+ TW\r
+\r
+70-B3-D5 (hex) Toolplanet Co., Ltd.\r
+4B5000-4B5FFF (base 16) Toolplanet Co., Ltd.\r
+ 43-2 Himigaike-cho\r
+ Gifu-shi Gifu 500-8122\r
+ JP\r
+\r
+70-B3-D5 (hex) Postmark Incorporated \r
+CBB000-CBBFFF (base 16) Postmark Incorporated \r
+ 3197 Duncan Lane\r
+ San Luis Obispo CA 93401\r
+ US\r
+\r
+70-B3-D5 (hex) Abbott Diagnostics Technologies AS\r
+53F000-53FFFF (base 16) Abbott Diagnostics Technologies AS\r
+ P. O. Box 6863 Rodeløkka\r
+ Oslo Oslo 0504\r
+ NO\r
+\r
+70-B3-D5 (hex) Surion (Pty) Ltd\r
+7FC000-7FCFFF (base 16) Surion (Pty) Ltd\r
+ 205 Park Corner, 2 Bolton road, Rosebank\r
+ JOHANNESBURG NORTH Gauteng 2193\r
+ ZA\r
+\r
+70-B3-D5 (hex) REO AG\r
+8E7000-8E7FFF (base 16) REO AG\r
+ Brühlerstr. 100\r
+ Solingen 42657\r
+ DE\r
+\r
+70-B3-D5 (hex) GIORDANO CONTROLS SPA\r
+95D000-95DFFF (base 16) GIORDANO CONTROLS SPA\r
+ VIA PARALLELA 2/4\r
+ VILLA BARTOLOMEA IT 37049\r
+ IT\r
+\r
70-B3-D5 (hex) System West dba ICS Electronics\r
E06000-E06FFF (base 16) System West dba ICS Electronics\r
7034 Commerce Circle Suite A\r
Bingen WA 98605\r
US\r
\r
-70-B3-D5 (hex) Pano0ramic Power\r
-669000-669FFF (base 16) Pano0ramic Power\r
- 15 Atir Yeda\r
- Kfar Saba 4464312\r
- IL\r
-\r
00-1B-C5 (hex) Private\r
0B8000-0B8FFF (base 16) Private\r
\r
Cambridge Cambridgeshire CB21 5ET\r
GB\r
\r
-70-B3-D5 (hex) Taejin InforTech\r
-A75000-A75FFF (base 16) Taejin InforTech\r
- 40, Imi-ro, A-411\r
- Uiwang-si Gyeonggi-do 16006\r
- KR\r
-\r
70-B3-D5 (hex) AUTOMATIZACION Y CONECTIVIDAD SA DE CV\r
59B000-59BFFF (base 16) AUTOMATIZACION Y CONECTIVIDAD SA DE CV\r
LA GARITA ANDADOR 6 DUPLEX 1 CASA 2\r
Songpa-gu Seoul 05636\r
KR\r
\r
-70-B3-D5 (hex) Coheros Oy\r
-D2E000-D2EFFF (base 16) Coheros Oy\r
- Korkeakoulunkatu 1\r
- Tampere 33720\r
- FI\r
-\r
70-B3-D5 (hex) EA Elektroautomatik GmbH & Co. KG\r
26C000-26CFFF (base 16) EA Elektroautomatik GmbH & Co. KG\r
Helmholtzstraße 31-33\r
Bingen WA 98605\r
US\r
\r
+70-B3-D5 (hex) German Power GmbH\r
+C31000-C31FFF (base 16) German Power GmbH\r
+ Freiburger Strasse 7\r
+ Pforzheim 75179\r
+ DE\r
+\r
70-B3-D5 (hex) Lyse AS\r
F23000-F23FFF (base 16) Lyse AS\r
Breiflåtveien 18\r
Paris 75006\r
FR\r
\r
+70-B3-D5 (hex) Hefei STAROT Technology Co.,Ltd\r
+4D3000-4D3FFF (base 16) Hefei STAROT Technology Co.,Ltd\r
+ 406, 4th Floor, Quality Control Building, Saipu Science Park, No. 6 Yunfei Road, High-tech Zone\r
+ hefei anhui 230000\r
+ CN\r
+\r
+70-B3-D5 (hex) SysCom Automationstechnik GmbH\r
+117000-117FFF (base 16) SysCom Automationstechnik GmbH\r
+ An der Lehmkaute 13\r
+ Bad Marienberg Rheinland-Pfalz 56470\r
+ DE\r
+\r
+70-B3-D5 (hex) JFA Electronics Industry and Commerce EIRELI\r
+5F7000-5F7FFF (base 16) JFA Electronics Industry and Commerce EIRELI\r
+ Rua Flor das Pedras, 175\r
+ Belo Horizonte Minas Gerais 30810-000\r
+ BR\r
+\r
+70-B3-D5 (hex) Hubbell Power Systems\r
+858000-858FFF (base 16) Hubbell Power Systems\r
+ 353 Powerville Road\r
+ Boonton Township NJ 07005\r
+ US\r
+\r
+70-B3-D5 (hex) Walton Hi-Tech Industries Ltd.\r
+E5C000-E5CFFF (base 16) Walton Hi-Tech Industries Ltd.\r
+ HOLDING NO. I-65/2, WARD NO-07\r
+ CHANDRA, KALIAKOIR, GAZIPUR. 1750\r
+ BD\r
+\r
+70-B3-D5 (hex) aquila biolabs GmbH\r
+7DB000-7DBFFF (base 16) aquila biolabs GmbH\r
+ Arnold-Sommerfeld-Ring 2\r
+ Baesweiler NRW 52499\r
+ DE\r
+\r
+70-B3-D5 (hex) Sicon srl\r
+C82000-C82FFF (base 16) Sicon srl\r
+ Via Sila 1/3\r
+ Isola Vicentina Vicenza 36033\r
+ IT\r
+\r
+70-B3-D5 (hex) Flextronics International Kft\r
+699000-699FFF (base 16) Flextronics International Kft\r
+ 38. Zrinyi Str.\r
+ Zalaegerszeg Zala 8900\r
+ HU\r
+\r
+70-B3-D5 (hex) LGE\r
+DAE000-DAEFFF (base 16) LGE\r
+ 10, Magokjungang 10-ro, Gangseo-gu\r
+ Seoul 07796\r
+ KR\r
+\r
+70-B3-D5 (hex) Jonsa Australia Pty Ltd\r
+335000-335FFF (base 16) Jonsa Australia Pty Ltd\r
+ Unit D2 3-29 Birnie Ave\r
+ Lidcombe NSW 2141\r
+ AU\r
+\r
+70-B3-D5 (hex) GreenWake Technologies\r
+467000-467FFF (base 16) GreenWake Technologies\r
+ 56 boulevard Niels Bohr, CEI2\r
+ Villeurbanne 69100\r
+ FR\r
+\r
+70-B3-D5 (hex) shenzhen suofeixiang technology Co.,Ltd\r
+EEB000-EEBFFF (base 16) shenzhen suofeixiang technology Co.,Ltd\r
+ sales09@sfxhd.com\r
+ shenzhen 518000\r
+ CN\r
+\r
+70-B3-D5 (hex) RCH Vietnam Limited Liability Company\r
+97D000-97DFFF (base 16) RCH Vietnam Limited Liability Company\r
+ Workshop F.01B-2, Lot No. F.01B Long Hau\r
+ Ho Chi Minh City Ho Chi Minh 70000\r
+ VN\r
+\r
+70-B3-D5 (hex) SNK, Inc.\r
+E12000-E12FFF (base 16) SNK, Inc.\r
+ Rm 302 Inobiz park, 1646, Yuseong-daero, Yuseong-gu\r
+ Daejeon 34054\r
+ KR\r
+\r
+70-B3-D5 (hex) SYLink Technologie\r
+466000-466FFF (base 16) SYLink Technologie\r
+ 18 rue de la conche\r
+ Mirefleurs Auvergne 63730\r
+ FR\r
+\r
+70-B3-D5 (hex) silicom\r
+F64000-F64FFF (base 16) silicom\r
+ 14 Atir-Yeda St/\r
+ Kfar-Sava Israel 44000\r
+ IL\r
+\r
+70-B3-D5 (hex) NSP Europe Ltd\r
+18A000-18AFFF (base 16) NSP Europe Ltd\r
+ Unit 5, Devonshire Business Park\r
+ Borehamwood Hert WD6 1NA\r
+ GB\r
+\r
+70-B3-D5 (hex) Microchip Technology Germany II GmbH&Co.KG\r
+77F000-77FFFF (base 16) Microchip Technology Germany II GmbH&Co.KG\r
+ Emmy-Noether-Straße 14\r
+ Karlsruhe 76131\r
+ DE\r
+\r
+70-B3-D5 (hex) Trust Automation\r
+C98000-C98FFF (base 16) Trust Automation\r
+ 125 Venture Dr\r
+ San Luis Obispo CA 93401\r
+ US\r
+\r
+70-B3-D5 (hex) Kospel S.A.\r
+249000-249FFF (base 16) Kospel S.A.\r
+ Olchowa 1\r
+ Koszalin 75-136\r
+ PL\r
+\r
+70-B3-D5 (hex) Coheros Oy\r
+D2E000-D2EFFF (base 16) Coheros Oy\r
+ Tammukkakatu 6\r
+ Nokia 37130\r
+ FI\r
+\r
+70-B3-D5 (hex) Gogo Business Aviation\r
+E24000-E24FFF (base 16) Gogo Business Aviation\r
+ 105 Edgeview Dr., Suite 300\r
+ Broomfield CO 80021\r
+ US\r
+\r
+70-B3-D5 (hex) Taejin InfoTech\r
+A75000-A75FFF (base 16) Taejin InfoTech\r
+ 40, Imi-ro, A-411\r
+ Uiwang-si Gyeonggi-do 16006\r
+ KR\r
+\r
+70-B3-D5 (hex) ARCLAN'SYSTEM\r
+25C000-25CFFF (base 16) ARCLAN'SYSTEM\r
+ 1140 rue Ampère - Actimart II - Lot 9\r
+ AIX EN PROVENCE 13290\r
+ FR\r
+\r
+70-B3-D5 (hex) Smart Embedded Systems\r
+A09000-A09FFF (base 16) Smart Embedded Systems\r
+ 6701 Koll Center Parkway #250\r
+ Pleasonton CA 94566\r
+ US\r
+\r
+70-B3-D5 (hex) Guan Show Technologe Co., Ltd.\r
+F6A000-F6AFFF (base 16) Guan Show Technologe Co., Ltd.\r
+ No.127, Jianguo 1st Rd., Lingya Dist.\r
+ Kaohsiung City 802\r
+ TW\r
+\r
+70-B3-D5 (hex) LLC Sarov Innovative Technologies (WIZOLUTION)\r
+50F000-50FFFF (base 16) LLC Sarov Innovative Technologies (WIZOLUTION)\r
+ RUSSIAN FEDERATION, Nizhny Novgorod region, Varlamovskaya road, 7, build 2\r
+ Sarov Nizhny Novgorod 607188\r
+ RU\r
+\r
+70-B3-D5 (hex) SPX Radiodetection\r
+A77000-A77FFF (base 16) SPX Radiodetection\r
+ Western Drive\r
+ Bristol Avon BS14 0AF\r
+ GB\r
+\r
+70-B3-D5 (hex) LM-Instruments Oy\r
+5AC000-5ACFFF (base 16) LM-Instruments Oy\r
+ Norrbyn rantatie 8\r
+ Parainen 21600\r
+ FI\r
+\r
+70-B3-D5 (hex) Fuhr GmbH Filtertechnik\r
+DBB000-DBBFFF (base 16) Fuhr GmbH Filtertechnik\r
+ Am Weinkastell 14\r
+ Klein-Winternheim Rheinland-Pfalz 55270\r
+ DE\r
+\r
+70-B3-D5 (hex) Sanmina Israel\r
+C18000-C18FFF (base 16) Sanmina Israel\r
+ Koren Industrial Zone , POBox 102\r
+ Maalot Israel 2101002\r
+ IL\r
+\r
+70-B3-D5 (hex) INVISSYS\r
+AD4000-AD4FFF (base 16) INVISSYS\r
+ 25 rue marcel issartier\r
+ merignac 33700\r
+ FR\r
+\r
+70-B3-D5 (hex) Panoramic Power\r
+669000-669FFF (base 16) Panoramic Power\r
+ 15 Atir Yeda\r
+ Kfar Saba 4464312\r
+ IL\r
+\r
+70-B3-D5 (hex) Panoramic Power\r
+06D000-06DFFF (base 16) Panoramic Power\r
+ Atir Yeda 15\r
+ Kfar Saba 4464312\r
+ IL\r
+\r
+70-B3-D5 (hex) Avlinkpro\r
+2C1000-2C1FFF (base 16) Avlinkpro\r
+ 380 US Highway 46\r
+ Totowa NJ 07512\r
+ US\r
+\r
+70-B3-D5 (hex) DECYBEN\r
+683000-683FFF (base 16) DECYBEN\r
+ 170 Rue Raymond Losserand\r
+ Paris 75014\r
+ FR\r
+\r
+70-B3-D5 (hex) C4I Systems Ltd\r
+5C6000-5C6FFF (base 16) C4I Systems Ltd\r
+ Unit 1Twyford Court\r
+ Hereford Herefordshire HR2 6JR\r
+ GB\r
+\r
+70-B3-D5 (hex) Knowledge Resources GmbH\r
+C36000-C36FFF (base 16) Knowledge Resources GmbH\r
+ Ackerstrasse 30\r
+ Bsel BS 4057\r
+ CH\r
+\r
70-B3-D5 (hex) YUYAMA MFG Co.,Ltd\r
BBB000-BBBFFF (base 16) YUYAMA MFG Co.,Ltd\r
3-3-1\r
Lutz FL 33558\r
US\r
\r
-70-B3-D5 (hex) SFR\r
-B12000-B12FFF (base 16) SFR\r
- 12 rue jean-philippe Rameau CS 80001\r
- La plaine saint denis FRANCE 93634\r
- FR\r
-\r
70-B3-D5 (hex) S.E.I. CO.,LTD.\r
12D000-12DFFF (base 16) S.E.I. CO.,LTD.\r
59 Nirayama Tada\r
Herning Herning 7400\r
DK\r
\r
+70-B3-D5 (hex) Shanghai Tiancheng Communication Technology Corporation\r
+1C3000-1C3FFF (base 16) Shanghai Tiancheng Communication Technology Corporation\r
+ No.618,Guangxing Rd.,Songjiang \r
+ shanghai 200090\r
+ CN\r
+\r
+70-B3-D5 (hex) T&M Media Pty Ltd\r
+B41000-B41FFF (base 16) T&M Media Pty Ltd\r
+ 6, 476 Gardeners Road\r
+ Alexandria NSW 2015\r
+ AU\r
+\r
+70-B3-D5 (hex) SAMBO HITECH\r
+282000-282FFF (base 16) SAMBO HITECH\r
+ 469,Seokjung-ro,Namdong-Gu\r
+ Incheon 21501\r
+ KR\r
+\r
+70-B3-D5 (hex) M.A.C. Solutions (UK) Ltd\r
+F9F000-F9FFFF (base 16) M.A.C. Solutions (UK) Ltd\r
+ Units 6-7 Kingfisher Business Park, Arthur Street\r
+ Redditch Worcestershire B98 8LG\r
+ GB\r
+\r
+70-B3-D5 (hex) Shenzhen CAMERAY ELECTRONIC CO., LTD\r
+1E2000-1E2FFF (base 16) Shenzhen CAMERAY ELECTRONIC CO., LTD\r
+ 4-5FL, Building 1, Guanghui Science, and Technology Park; Minqing Road, Longhua Town\r
+ shenzhen GD 518109\r
+ CN\r
+\r
+70-B3-D5 (hex) Vulcan Wireless Inc.\r
+E4D000-E4DFFF (base 16) Vulcan Wireless Inc.\r
+ 2218 Faraday Ave Suite 110\r
+ Carlsbad CA 92008\r
+ US\r
+\r
+70-B3-D5 (hex) ERA TOYS LIMITED\r
+193000-193FFF (base 16) ERA TOYS LIMITED\r
+ Room 505, 5th Floor, Beverley Commercial Centre, 87-105 Chatham Road South\r
+ Tsim Sha Tsui Kowloon 0000\r
+ HK\r
+\r
+70-B3-D5 (hex) Scorpion Precision Industry (HK)CO. Ltd.\r
+02B000-02BFFF (base 16) Scorpion Precision Industry (HK)CO. Ltd.\r
+ 16th Floor, Excelsior Industrial Building,68-76 Sha Tsui Road,\r
+ Tsuen Wan New Territories 999077\r
+ HK\r
+\r
+70-B3-D5 (hex) Cryptotronix LLC\r
+0DB000-0DBFFF (base 16) Cryptotronix LLC\r
+ P.O. Box 273029\r
+ Fort Collins CO 80525\r
+ US\r
+\r
+70-B3-D5 (hex) MIVO Technology AB\r
+1D5000-1D5FFF (base 16) MIVO Technology AB\r
+ Hornsbergsvägen 28\r
+ Stockholm 11215\r
+ SE\r
+\r
+70-B3-D5 (hex) A&T Corporation\r
+32E000-32EFFF (base 16) A&T Corporation\r
+ 2023-1\r
+ Endo, Fujisawa, Kanagawa 252-0816\r
+ JP\r
+\r
+70-B3-D5 (hex) TOMEI TSUSHIN KOGYO CO,.LTD\r
+FB1000-FB1FFF (base 16) TOMEI TSUSHIN KOGYO CO,.LTD\r
+ 100-3, Amaike Kodacho\r
+ Inazawa Shi Aichi ken 4928274\r
+ JP\r
+\r
+70-B3-D5 (hex) DogWatch Inc\r
+1E7000-1E7FFF (base 16) DogWatch Inc\r
+ 10 Michigan Drive\r
+ Natick 01760\r
+ US\r
+\r
+70-B3-D5 (hex) RCH Vietnam Limited Liability Company\r
+C09000-C09FFF (base 16) RCH Vietnam Limited Liability Company\r
+ Workshop F.01B-2, Lot No. F.01B Long Hau\r
+ Ho Chi Minh City Ho Chi Minh 70000\r
+ VN\r
+\r
+70-B3-D5 (hex) Copper Labs, Inc.\r
+F69000-F69FFF (base 16) Copper Labs, Inc.\r
+ 3015 Sterling Circle #200\r
+ Boulder CO 80301\r
+ US\r
+\r
+70-B3-D5 (hex) SHENZHEN HUINENGYUAN Technology Co., Ltd\r
+A83000-A83FFF (base 16) SHENZHEN HUINENGYUAN Technology Co., Ltd\r
+ Room 206, 3 Building, Hongwanchuangke Center, Gushu, Xixiang, Baoan District\r
+ Shenzhen Guangdong 518126\r
+ CN\r
+\r
+70-B3-D5 (hex) Vars Technology \r
+C94000-C94FFF (base 16) Vars Technology \r
+ Squires gate industrial estate Unit 14\r
+ Blackpool lancashire FY4 3RN\r
+ GB\r
+\r
+70-B3-D5 (hex) Burk Technology\r
+641000-641FFF (base 16) Burk Technology\r
+ 7 Beaver Brook road\r
+ Littleton MA 01460\r
+ US\r
+\r
+70-B3-D5 (hex) Inventeq B.V.\r
+529000-529FFF (base 16) Inventeq B.V.\r
+ Ravenlaan 27\r
+ Blaricum 1261WT\r
+ NL\r
+\r
+70-B3-D5 (hex) Grossenbacher Systeme AG\r
+B75000-B75FFF (base 16) Grossenbacher Systeme AG\r
+ Spinnereistrasse 10\r
+ St. Gallen 9008\r
+ CH\r
+\r
+70-B3-D5 (hex) ITsynergy Ltd\r
+D2A000-D2AFFF (base 16) ITsynergy Ltd\r
+ 9 Bonhill Street\r
+ London EC2A 4DJ\r
+ GB\r
+\r
+70-B3-D5 (hex) Vaunix Technology Corporation\r
+EE6000-EE6FFF (base 16) Vaunix Technology Corporation\r
+ 7 New Pasture Rd\r
+ Newburyport MA 01950\r
+ US\r
+\r
+70-B3-D5 (hex) chargeBIG\r
+869000-869FFF (base 16) chargeBIG\r
+ Pragstraße 26-46\r
+ Stuttgart 70376\r
+ DE\r
+\r
+70-B3-D5 (hex) Portrait Displays, Inc.\r
+D77000-D77FFF (base 16) Portrait Displays, Inc.\r
+ 6663 OWENS DR\r
+ PLEASANTON CA 94588\r
+ US\r
+\r
+70-B3-D5 (hex) Sprintshield d.o.o.\r
+B03000-B03FFF (base 16) Sprintshield d.o.o.\r
+ Marina Getaldi?a 3\r
+ Velika Gorica 10410\r
+ HR\r
+\r
+70-B3-D5 (hex) Tricom Research Inc.\r
+601000-601FFF (base 16) Tricom Research Inc.\r
+ 17791 Sky Park Circle Suite GHJ\r
+ Irvine CA 92614\r
+ US\r
+\r
+70-B3-D5 (hex) Mictrotrac Retsch GmbH\r
+F09000-F09FFF (base 16) Mictrotrac Retsch GmbH\r
+ Retsch-Allee 1-5\r
+ Haan NRW 42781\r
+ DE\r
+\r
+70-B3-D5 (hex) KeyProd\r
+473000-473FFF (base 16) KeyProd\r
+ 66 avenue des Champs Elysées\r
+ Paris 77008\r
+ FR\r
+\r
+70-B3-D5 (hex) MB connect line GmbH Fernwartungssysteme\r
+6D7000-6D7FFF (base 16) MB connect line GmbH Fernwartungssysteme\r
+ Winnettener Straße 6\r
+ Dinkelsbuehl Bavaria 91550\r
+ DE\r
+\r
+70-B3-D5 (hex) EarTex\r
+E01000-E01FFF (base 16) EarTex\r
+ 41 Corsham Street\r
+ London England N1 6DR\r
+ GB\r
+\r
+70-B3-D5 (hex) AVL DiTEST GmbH\r
+78D000-78DFFF (base 16) AVL DiTEST GmbH\r
+ Alte Poststrasse 156\r
+ Graz 8020\r
+ AT\r
+\r
+70-B3-D5 (hex) Scharco Elektronik GmbH\r
+C72000-C72FFF (base 16) Scharco Elektronik GmbH\r
+ Tilsiter Strasse 8\r
+ Wuppertal NRW 42277\r
+ DE\r
+\r
+70-B3-D5 (hex) WARECUBE,INC\r
+AD3000-AD3FFF (base 16) WARECUBE,INC\r
+ #A-811, 142-10, Saneop-ro, 156beon-gil, Gwonseon-gu\r
+ Suwon-si 16648\r
+ KR\r
+\r
+70-B3-D5 (hex) myUpTech AB\r
+FC3000-FC3FFF (base 16) myUpTech AB\r
+ Box 14\r
+ Markaryd 28532\r
+ SE\r
+\r
70-B3-D5 (hex) DISMUNTEL SAL\r
92C000-92CFFF (base 16) DISMUNTEL SAL\r
Pol ind cotes\r
Eckental Bavaria 90542\r
DE\r
\r
-70-B3-D5 (hex) Pano0ramic Power\r
-53A000-53AFFF (base 16) Pano0ramic Power\r
- 15 Atir Yeda\r
- Kfar Saba 4464312\r
- IL\r
-\r
70-B3-D5 (hex) ND METER\r
68C000-68CFFF (base 16) ND METER\r
228 BOLTON ROAD\r
via Cupa Vicinale S.Aniello, 88\r
Naples 80146\r
IT\r
+\r
+70-B3-D5 (hex) ITK Dr. Kassen GmbH\r
+58A000-58AFFF (base 16) ITK Dr. Kassen GmbH\r
+ Beim Eberacker 3\r
+ D-35633 Lahnau \r
+ DE\r
+\r
+70-B3-D5 (hex) RCH Vietnam Limited Liability Company\r
+88E000-88EFFF (base 16) RCH Vietnam Limited Liability Company\r
+ Workshop F.01B-2, Lot No. F.01B Long Hau\r
+ Ho Chi Minh City Ho Chi Minh 70000\r
+ VN\r
+\r
+70-B3-D5 (hex) Privafy, Inc\r
+A6A000-A6AFFF (base 16) Privafy, Inc\r
+ 2 Burlington Woods Dr. Suite 200\r
+ Burlington MA 01803\r
+ US\r
+\r
+70-B3-D5 (hex) Contec Americas Inc.\r
+5D2000-5D2FFF (base 16) Contec Americas Inc.\r
+ 3991 Sarno Rd\r
+ Melbourne FL 32934\r
+ US\r
+\r
+70-B3-D5 (hex) elements\r
+62D000-62DFFF (base 16) elements\r
+ Townsgate Road Suite 200 \r
+ Westlake Village CA 91361\r
+ US\r
+\r
+70-B3-D5 (hex) DAT Informatics Pvt Ltd\r
+244000-244FFF (base 16) DAT Informatics Pvt Ltd\r
+ Plot No 109 HPSIDC Industria Estate Davni, Baddi\r
+ Baddi HIMACHAL PRADESH 173205\r
+ IN\r
+\r
+70-B3-D5 (hex) Abbott Diagnostics Technologies AS\r
+6C6000-6C6FFF (base 16) Abbott Diagnostics Technologies AS\r
+ P. O. Box 6863 Rodeløkka\r
+ Oslo Oslo 0504\r
+ NO\r
+\r
+70-B3-D5 (hex) Gamber Johnson-LLC\r
+E34000-E34FFF (base 16) Gamber Johnson-LLC\r
+ 3001 Borham Ave\r
+ Stevens Point WI 54481\r
+ US\r
+\r
+70-B3-D5 (hex) RCH Vietnam Limited Liability Company\r
+6BD000-6BDFFF (base 16) RCH Vietnam Limited Liability Company\r
+ Workshop F.01B-2, Lot No. F.01B Long Hau\r
+ Ho Chi Minh City Ho Chi Minh 70000\r
+ VN\r
+\r
+70-B3-D5 (hex) YUYAMA MFG Co.,Ltd\r
+1F2000-1F2FFF (base 16) YUYAMA MFG Co.,Ltd\r
+ 3-3-1\r
+ TOYONAKASHI OSAKA 561-0841\r
+ JP\r
+\r
+70-B3-D5 (hex) QUALITTEQ LLC\r
+614000-614FFF (base 16) QUALITTEQ LLC\r
+ 16th Parkovaya 26/1\r
+ Moscow 105484\r
+ RU\r
+\r
+70-B3-D5 (hex) YUYAMA MFG Co.,Ltd\r
+C2B000-C2BFFF (base 16) YUYAMA MFG Co.,Ltd\r
+ 3-3-1\r
+ TOYONAKASHI OSAKA 561-0841\r
+ JP\r
+\r
+70-B3-D5 (hex) Adcole Maryland Aerospace\r
+922000-922FFF (base 16) Adcole Maryland Aerospace\r
+ 669 Forest St\r
+ Marlborough MA 01752\r
+ US\r
+\r
+70-B3-D5 (hex) eSMART Technologies SA\r
+979000-979FFF (base 16) eSMART Technologies SA\r
+ Chemin de la Rueyre, 118\r
+ Renens VD 1020\r
+ CH\r
+\r
+70-B3-D5 (hex) Duplomatic MS spa\r
+DE1000-DE1FFF (base 16) Duplomatic MS spa\r
+ Via Re Depaolini 24\r
+ Parabiago Milan 20015\r
+ IT\r
+\r
+70-B3-D5 (hex) Axnes AS\r
+65F000-65FFFF (base 16) Axnes AS\r
+ Terje Løvåsvei 1\r
+ Grimstad 4879\r
+ NO\r
+\r
+70-B3-D5 (hex) Nanjing Pingguang Electronic Technology Co., Ltd\r
+541000-541FFF (base 16) Nanjing Pingguang Electronic Technology Co., Ltd\r
+ B30/B31 4th Floor, Building#11, Shengtai Road, JiangNing District\r
+ NanJing 211100\r
+ CN\r
+\r
+70-B3-D5 (hex) thingdust AG\r
+3C1000-3C1FFF (base 16) thingdust AG\r
+ Moosstrasse 7\r
+ Lucerne Lucerne 6003\r
+ CH\r
+\r
+70-B3-D5 (hex) ALVAT s.r.o.\r
+369000-369FFF (base 16) ALVAT s.r.o.\r
+ Chodovska 228/3\r
+ Praha 4 14100\r
+ CZ\r
+\r
+70-B3-D5 (hex) PHYZHON Health Inc\r
+744000-744FFF (base 16) PHYZHON Health Inc\r
+ 180 Blue Ravine Road, suite A\r
+ Folsom CA 95630\r
+ US\r
+\r
+70-B3-D5 (hex) PCB Piezotronics\r
+4CA000-4CAFFF (base 16) PCB Piezotronics\r
+ 3425 Walden Avenue\r
+ Depew NY 14043\r
+ US\r
+\r
+70-B3-D5 (hex) Panoramic Power\r
+53A000-53AFFF (base 16) Panoramic Power\r
+ 15 Atir Yeda\r
+ Kfar Saba 4464312\r
+ IL\r
+\r
+70-B3-D5 (hex) STEP sarl\r
+481000-481FFF (base 16) STEP sarl\r
+ 11, avenue Aristide Berges\r
+ LANCEY ISERE 38190\r
+ FR\r
20-net-ifname.hwdb
20-vmbus-class.hwdb
60-evdev.hwdb
+ 60-input-id.hwdb
60-keyboard.hwdb
60-sensor.hwdb
70-joystick.hwdb
('KEYBOARD_LED_CAPSLOCK', Literal('0')),
('ACCEL_MOUNT_MATRIX', mount_matrix),
('ACCEL_LOCATION', Or(('display', 'base'))),
+ ('PROXIMITY_NEAR_LEVEL', INTEGER),
)
fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE')
for name, val in props]
#
# List of PCI ID's
#
-# Version: 2019.11.26
-# Date: 2019-11-26 03:15:03
+# Version: 2020.03.05
+# Date: 2020-03-05 03:15:04
#
# Maintained by Albert Pool, Martin Mares, and other volunteers from
# the PCI ID Project at https://pci-ids.ucw.cz/.
# 018a is not LevelOne but there is a board misprogrammed
018a LevelOne
0106 FPC-0106TX misprogrammed [RTL81xx]
+01de Oxide Computer Company
# 021b is not Compaq but there is a board misprogrammed
021b Compaq Computer Corporation
8139 HNE-300 (RealTek RTL8139c) [iPaq Networking]
1028 1fd1 PERC H730P MX
17aa 1052 ThinkServer RAID 720i
17aa 1053 ThinkServer RAID 720ix
+ 1bd4 0014 12G SAS3108 2G
+ 1bd4 0015 12G SAS3108 4G
1d49 0600 ThinkSystem RAID 730-8i 1GB Cache PCIe 12Gb Adapter
1d49 0608 ThinkSystem RAID 730-8i 2GB Flash PCIe 12Gb Adapter
1d49 0609 ThinkSystem RAID 730-8i 4GB Flash PCIe 12Gb Adapter
1028 1f1f PERC H200 Modular
1028 1f20 PERC H200 Embedded
1028 1f22 PERC H200 Internal Tape Adapter
+# Fujitsu D2607 SAS2008 HBA controller
+ 1734 1177 HBA Ctrl SAS 6G 0/1 [D2607]
+ 1bd4 000d 6G SAS2008IT
+ 1bd4 000e 6G SAS2008IR
+ 1bd4 000f 6G SAS2008IT SA5248
+ 1bd4 0010 6G SAS2008IR SA5248
8086 350f RMS2LL040 RAID Controller
8086 3700 SSD 910 Series
0073 MegaRAID SAS 2008 [Falcon]
1590 0041 H220i
1590 0042 H221 / 9207-8e
1590 0044 H220i
+ 1bd4 0009 6G SAS2308IR
+ 1bd4 000a 6G SAS2308IT
8086 3000 RS25GB008 RAID Controller
8086 3060 RS25FB044 RAID Controller
8086 3516 RMS25JB080 RAID Controller
1028 1fd3 HBA330 MMZ
# Supermicro AOC-S3008L-L8e uses 0808 for their SAS3008 SAS controller
15d9 0808 AOC-S3008L-L8e
+ 1bd4 000b 12G SAS3008IR
+ 1bd4 000c 12G SAS3008IT
1bd4 0011 Inspur 12Gb 8i-3008 IT SAS HBA
+ 1bd4 0012 12Gb SAS3008IR UDM
+ 1bd4 0026 12G SAS3008IT RACK
+ 1bd4 0027 12G SAS3008IMR RACK
+ 1bd4 0028 12G SAS3008IR RACK
00ab SAS3516 Fusion-MPT Tri-Mode RAID On Chip (ROC)
# 8 Internal and 8 External port channel 9400 HBA
1000 3040 HBA 9400-8i8e
1306 Kaveri
1307 Kaveri
1308 Kaveri HDMI/DP Audio Controller
+ 17aa 3988 Z50-75
1309 Kaveri [Radeon R6/R7 Graphics]
+ 17aa 3830 Z50-75
130a Kaveri [Radeon R6 Graphics]
130b Kaveri [Radeon R4 Graphics]
130c Kaveri [Radeon R7 Graphics]
1561 Anubis
15d8 Picasso
103c 8615 Pavilion Laptop 15-cw1xxx
+ 17aa 5124 ThinkPad E595
15dd Raven Ridge [Radeon Vega Series / Radeon Vega Mobile Series]
103c 83c6 Radeon Vega 8 Mobile
1458 d000 Radeon RX Vega 11
15de Raven/Raven2/Fenghuang HDMI/DP Audio Controller
103c 8615 Pavilion Laptop 15-cw1xxx
+ 17aa 5124 ThinkPad E595
15df Raven/Raven2/Fenghuang/Renoir Cryptographic Coprocessor
103c 8615 Pavilion Laptop 15-cw1xxx
15ff Fenghuang [Zhongshan Subor Z+]
4382 SB600 AC97 Audio
4383 SBx00 Azalia (Intel HDA)
1019 2120 A785GM-M
- 103c 1611 Pavilion DM1Z-3000
+ 103c 1611 Pavilion dm1z-3000
103c 280a DC5750 Microtower
1043 8230 M3A78-EH Motherboard
1043 836c M4A785TD Motherboard
1458 b002 GA-MA770-DS3rev2.0 Motherboard
1849 4390 Motherboard (one of many)
4391 SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode]
+ 103c 1609 ProLiant MicroServer N36L
103c 1611 Pavilion DM1Z-3000
1043 82ef M3A78-EH Motherboard
1043 8443 M5A88-V EVO
4395 SB8x0/SB9x0 SATA Controller [Storage mode]
4396 SB7x0/SB8x0/SB9x0 USB EHCI Controller
1019 2120 A785GM-M
+ 103c 1609 ProLiant MicroServer N36L
103c 1611 Pavilion DM1Z-3000
1043 82ef M3A78-EH Motherboard
1043 8443 M5A88-V EVO
174b 1001 PURE Fusion Mini
4397 SB7x0/SB8x0/SB9x0 USB OHCI0 Controller
1019 2120 A785GM-M
+ 103c 1609 ProLiant MicroServer N36L
103c 1611 Pavilion DM1Z-3000
1043 82ef M3A78-EH Motherboard
1043 8443 M5A88-V EVO
439c SB7x0/SB8x0/SB9x0 IDE Controller
1002 4392 MSI MS-7713 motherboard
1019 2120 A785GM-M
+ 103c 1609 ProLiant MicroServer N36L
1043 82ef M3A78-EH Motherboard
105b 0e13 N15235/A74MX mainboard / AMD SB700
439d SB7x0/SB8x0/SB9x0 LPC host controller
1019 2120 A785GM-M
+ 103c 1609 ProLiant MicroServer N36L
103c 1611 Pavilion DM1Z-3000
1043 82ef M3A78-EH Motherboard
1043 8443 M5A88-V EVO
1642 3c81 Radeon HD 8670
1642 3c91 Radeon HD 8670
1642 3f09 Radeon R7 350
- 6611 Oland [Radeon HD 8570 / R7 240/340 OEM]
+ 6611 Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM]
1028 210b Radeon R5 240 OEM
+ 1642 1869 Radeon 520 OEM
174b 4248 Radeon R7 240 OEM
174b a240 Radeon R7 240 OEM
174b d340 Radeon R7 340 OEM
17aa 3805 Radeon HD 8570M
6664 Jet XT [Radeon R5 M240]
6665 Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile]
- 17aa 1309 Radeon R7 M260DX
+ 17aa 1309 Z50-75 Radeon R7 M260DX
17aa 368f Radeon R5 A230
6667 Jet ULT [Radeon R5 M230]
666f Sun LE [Radeon HD 8550M / R5 M230]
1462 3418 Radeon RX 580 Armor 4G OC
1462 341e Radeon RX 570 Armor 4G OC
1462 8a92 Radeon RX 580
- 148c 2372 Radeon RX 480
+ 148c 2372 Radeon RX 480 [Red Dragon]
148c 2373 Radeon RX 470
1682 9470 Radeon RX 470
1682 9480 Radeon RX 480
1787 a470 Radeon RX 470
1787 a480 Radeon RX 480
1849 5001 Phantom Gaming X RX 580 OC
+ 1849 5030 Phantom Gaming D Radeon RX580 8G OC
1da2 e353 Radeon RX 570 Pulse 4GB
1da2 e366 Nitro+ Radeon RX 570/580/590
67e0 Baffin [Radeon Pro WX 4170]
1043 04a0 Radeon R9 FURY X
174b e329 Radeon R9 FURY
7310 Navi 10
- 731f Navi 10 [Radeon RX 5700 / 5700 XT]
- 7340 Navi 14 [Radeon RX 5500 / 5500M]
+ 7312 Navi 10 [Radeon Pro W5700]
+ 731f Navi 10 [Radeon RX 5600 OEM/5600 XT / 5700/5700 XT]
+ 7340 Navi 14 [Radeon RX 5500/5500M / Pro 5500M]
+ 7341 Navi 14 [Radeon Pro W5500]
+ 7347 Navi 14 [Radeon Pro W5500M]
+ 734f Navi 14 [Radeon Pro W5300M]
7833 RS350 Host Bridge
7834 RS350 [Radeon 9100 PRO/XT IGP]
7835 RS350M [Mobility Radeon 9000 IGP]
1019 2120 A785GM-M
1043 83a2 M4A785TD Motherboard
9712 RS880M [Mobility Radeon HD 4225/4250]
+ 103c 1609 ProLiant MicroServer N36L
9713 RS880M [Mobility Radeon HD 4100]
9714 RS880 [Radeon HD 4290]
9715 RS880 [Radeon HD 4250]
9830 Kabini [Radeon HD 8400 / R3 Series]
9831 Kabini [Radeon HD 8400E]
9832 Kabini [Radeon HD 8330]
+ 1849 9832 QC5000-ITX/PH
9833 Kabini [Radeon HD 8330E]
9834 Kabini [Radeon HD 8210]
9835 Kabini [Radeon HD 8310E]
9839 Kabini [Radeon HD 8180]
983d Temash [Radeon HD 8250/8280G]
9840 Kabini HDMI/DP Audio
+ 1849 9840 QC5000-ITX/PH
9850 Mullins [Radeon R3 Graphics]
9851 Mullins [Radeon R4/R5 Graphics]
1179 f928 Beema [Radeon R5 Graphics]
1467 Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 7
1468 Zeppelin Cryptographic Coprocessor NTBCCP
1480 Starship/Matisse Root Complex
+ 1462 7c37 X570-A PRO motherboard
1481 Starship/Matisse IOMMU
1482 Starship/Matisse PCIe Dummy Host Bridge
1483 Starship/Matisse GPP Bridge
1485 Starship/Matisse Reserved SPP
1486 Starship/Matisse Cryptographic Coprocessor PSPCPP
1487 Starship/Matisse HD Audio Controller
+ 1462 9c37 X570-A PRO motherboard
1488 Starship Reserved SSP
1489 Starship Reserved SSP
148a Starship/Matisse PCIe Dummy Function
149a Starship PCIe GPP Bridge [1:0]
149b Starship Reserved SSP
149c Matisse USB 3.0 Host Controller
+ 1462 7c37 X570-A PRO motherboard
1510 Family 14h Processor Root Complex
174b 1001 PURE Fusion Mini
1512 Family 14h Processor Root Port
1534 Family 16h Processor Function 4
1535 Family 16h Processor Function 5
1536 Family 16h Processor Root Complex
+ 1849 1536 QC5000-ITX/PH
1537 Kabini/Mullins PSP-Platform Security Processor
1538 Family 16h Processor Function 0
1539 Kabini P2P Bridge for PCIe Ports[4:0]
15dc Raven/Raven2 Internal PCIe GPP Bridge 0 to Bus B
15de Raven/Raven2/FireFlight HD Audio Controller
15df Family 17h (Models 10h-1fh) Platform Security Processor
+ 17aa 5124 ThinkPad E595
15e0 Raven USB 3.1
103c 8615 Pavilion Laptop 15-cw1xxx
+ 17aa 5124 ThinkPad E595
15e1 Raven USB 3.1
103c 8615 Pavilion Laptop 15-cw1xxx
+ 17aa 5124 ThinkPad E595
15e2 Raven/Raven2/FireFlight/Renoir Audio Processor
+ 17aa 5124 ThinkPad E595
15e3 Family 17h (Models 10h-1fh) HD Audio Controller
103c 8615 Pavilion Laptop 15-cw1xxx
+ 17aa 5124 ThinkPad E595
15e4 Raven/Raven2/Renoir Sensor Fusion Hub
15e5 Raven2 USB 3.1
15e6 Raven/Raven2/Renoir Non-Sensor Fusion Hub KMDF driver
43c7 400 Series Chipset PCIe Port
43c8 400 Series Chipset SATA Controller
43d5 400 Series Chipset USB 3.1 XHCI Controller
+ 57a3 Matisse PCIe GPP Bridge
+ 57a4 Matisse PCIe GPP Bridge
+ 57ad Matisse Switch Upstream
7006 AMD-751 [Irongate] System Controller
7007 AMD-751 [Irongate] AGP Bridge
700a AMD-IGR4 AGP Host to PCI Bridge
7801 FCH SATA Controller [AHCI mode]
103c 168b ProBook 4535s Notebook
103c 194e ProBook 455 G1 Notebook
+ 17aa 3988 Z50-75
+ 1849 7801 QC5000-ITX/PH
7802 FCH SATA Controller [RAID mode]
7803 FCH SATA Controller [RAID mode]
7804 FCH SATA Controller [AHCI mode]
7807 FCH USB OHCI Controller
103c 194e ProBook 455 G1 Notebook
103c 1985 Pavilion 17-e163sg Notebook PC
+ 17aa 3988 Z50-75
+ 1849 7807 QC5000-ITX/PH
7808 FCH USB EHCI Controller
103c 194e ProBook 455 G1 Notebook
103c 1985 Pavilion 17-e163sg Notebook PC
+ 17aa 3988 Z50-75
+ 1849 7808 QC5000-ITX/PH
7809 FCH USB OHCI Controller
103c 194e ProBook 455 G1 Notebook
+ 17aa 3988 Z50-75
780a Kabini/Mullins SATA Raid/AHCI Mode (DotHill driver)
780b FCH SMBus Controller
103c 194e ProBook 455 G1 Notebook
103c 1985 Pavilion 17-e163sg Notebook PC
+ 17aa 3988 Z50-75
+ 1849 780b QC5000-ITX/PH
780c FCH IDE Controller
780d FCH Azalia Controller
103c 194e ProBook 455 G1 Notebook
103c 1985 Pavilion 17-e163sg Notebook PC
1043 8444 F2A85-M Series
+ 17aa 3988 Z50-75
+ 1849 8892 QC5000-ITX/PH
780e FCH LPC Bridge
103c 194e ProBook 455 G1 Notebook
103c 1985 Pavilion 17-e163sg Notebook PC
+ 17aa 3988 Z50-75
+ 1849 780e QC5000-ITX/PH
780f FCH PCI Bridge
7812 FCH USB XHCI Controller
7813 FCH SD Flash Controller
7814 FCH USB XHCI Controller
103c 194e ProBook 455 G1 Notebook
103c 1985 Pavilion 17-e163sg Notebook PC
+ 17aa 3988 Z50-75
+ 1849 7814 QC5000-ITX/PH
7900 FCH SATA Controller [IDE mode]
7901 FCH SATA Controller [AHCI mode]
103c 8615 Pavilion Laptop 15-cw1xxx
+ 1462 7c37 X570-A PRO motherboard
7902 FCH SATA Controller [RAID mode]
7903 FCH SATA Controller [RAID mode]
7904 FCH SATA Controller [AHCI mode]
7908 FCH USB EHCI Controller
790b FCH SMBus Controller
103c 8615 Pavilion Laptop 15-cw1xxx
+ 1462 7c37 X570-A PRO motherboard
+ 17aa 5124 ThinkPad E595
790e FCH LPC Bridge
103c 8615 Pavilion Laptop 15-cw1xxx
+ 1462 7c37 X570-A PRO motherboard
+ 17aa 5124 ThinkPad E595
790f FCH PCI Bridge
7914 FCH USB XHCI Controller
9600 RS780 Host Bridge
1043 82f1 M3A78-EH Motherboard
9601 RS880 Host Bridge
1019 2120 A785GM-M
+ 103c 1609 ProLiant MicroServer N36L
1043 83a2 M4A785-M Mainboard
1043 843e M5A88-V EVO
9602 RS780/RS880 PCI to PCI bridge (int gfx)
9603 RS780 PCI to PCI bridge (ext gfx port 0)
+ 103c 1609 ProLiant MicroServer N36L
9604 RS780/RS880 PCI to PCI bridge (PCIE port 0)
9605 RS780/RS880 PCI to PCI bridge (PCIE port 1)
9606 RS780 PCI to PCI bridge (PCIE port 2)
+ 103c 1609 ProLiant MicroServer N36L
9607 RS780/RS880 PCI to PCI bridge (PCIE port 3)
9608 RS780/RS880 PCI to PCI bridge (PCIE port 4)
9609 RS780/RS880 PCI to PCI bridge (PCIE port 5)
4031 zx2 I/O Controller
4037 PCIe Local Bus Adapter
9602 AMD RS780/RS880 PCI to PCI bridge (int gfx)
+ 103c 1609 ProLiant MicroServer N36L
103e Solliday Engineering
103f Synopsys/Logic Modeling Group
1040 Accelgraphics Inc.
4802 Falcon
4803 Hawk
4806 CPX8216
+# MPC7410 PowerPC microprocessor and PCI host bridge
+ 480b MPC7410
4d68 20268
5600 SM56 PCI Modem
1057 0300 SM56 PCI Speakerphone Modem
1077 02e4 QLE2772 Dual Port 32GFC PCIe Gen4 x8 Adapter
1077 02ee QLE2870 Single Port 64GFC PCIe Gen4 x8 Adapter
1077 02f0 QLE2770 Single Port 32GFC PCIe Gen4 x8 Adapter
+ 1077 02f2 QLogic 1x32Gb QLE2770 FC HBA
+ 1077 02f3 QLogic 2x32Gb QLE2772 FC HBA
1590 02d3 SN1610Q - 1P Enhanced 32GFC Single Port Fibre Channel Host Bus Adapter
1590 02d4 SN1610Q – 2P Enhanced 32GFC Dual Port Fibre Channel Host Bus Adapter
2300 QLA2300 64-bit Fibre Channel Adapter
1077 0055 QLogic 2x10GE QL41132HQCU NIC
1077 0056 2x10GE QL41132HxRJ NIC
1077 0057 2x25GE QL41232HxCU NIC
+ 1077 0065 QLogic 4x10GE QL41154HQRJ CNA
+ 1077 0066 QLogic 4x10GE QL41154HQCU CNA
+ 1077 0068 10GbE 2p SFP+ QL41132HLCU-HC Adapter
+ 1077 0069 10GbE 2p BASE-T QL41132HQRJ-HC OCP3 Adapter
+ 1077 0070 10GbE 2p BASE-T QL41132HLRJ-HC Adapter
+ 1077 0071 10GbE 2p SFP+ QL41132HQCU-HC OCP3 Adapter
+ 1077 0072 10GbE 4p SFP+ QL41134HLCU-HC Adapter
+ 1077 0073 10/25GbE 2p SFP28 QL41232HQCU-HC OCP3 Adapter
+ 1077 0074 10/25GbE 2p SFP28 QL41232HLCU-HC Adapter
1590 021a 10GbE 2P QL41162HLRJ-HP Adapter
1590 021b 10GbE 2P QL41162HLRJ-HP Adapter
1590 021d 10/25GbE 2P QL41222HLCU-HP Adapter
1077 000d FastLinQ QL41262H 25GbE iSCSI Adapter
1077 000e FastLinQ QL41162H 10GbE iSCSI Adapter
1077 000f 2x25GE QL41262HMKR CNA
+ 1077 0065 QLogic 4x10GE QL41154HQRJ CNA
+ 1077 0066 QLogic 4x10GE QL41154HQCU CNA
1590 021a 10GbE 2P QL41162HLRJ-HP Adapter
1590 021b 10GbE 2P QL41162HLRJ-HP Adapter
8090 FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF)
1077 0055 QLogic 2x10GE QL41132HQCU NIC
1077 0056 2x10GE QL41132HxRJ NIC
1077 0057 2x25GE QL41232HxCU NIC
+ 1077 0065 QLogic 4x10GE QL41154HQRJ CNA
+ 1077 0066 QLogic 4x10GE QL41154HQCU CNA
1590 021a 10GbE 2P QL41162HLRJ-HP Adapter
1590 021b 10GbE 2P QL41162HLRJ-HP Adapter
1590 021e 10/25GbE 2P QL41162HMRJ-HP Adapter
0fb9 GP107GL High Definition Audio Controller
0fba GM206 High Definition Audio Controller
0fbb GM204 High Definition Audio Controller
+ 0fbc GM107 High Definition Audio Controller [GeForce 940MX]
0fc0 GK107 [GeForce GT 640 OEM]
0fc1 GK107 [GeForce GT 640]
0fc2 GK107 [GeForce GT 630 OEM]
10f0 GP104 High Definition Audio Controller
10f1 GP106 High Definition Audio Controller
10f7 TU102 High Definition Audio Controller
+ 10f8 TU104 HD Audio Controller
10f9 TU106 High Definition Audio Controller
1043 8673 TURBO-RTX2070-8G
1140 GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M]
1406 GM206 [GeForce GTX 960 OEM]
1407 GM206 [GeForce GTX 750 v2]
1427 GM206M [GeForce GTX 965M]
+ 103c 825b OMEN-17-w001nv
1430 GM206GL [Quadro M2000]
1431 GM206GL [Tesla M4]
1436 GM206GLM [Quadro M2200 Mobile]
174e GM108M [GeForce MX110]
1789 GM107GL [GRID M3-3020]
179c GM107 [GeForce 940MX]
+ 1025 1094 Acer Aspire E5-575G
17c2 GM200 [GeForce GTX TITAN X]
17c8 GM200 [GeForce GTX 980 Ti]
17f0 GM200GL [Quadro M6000]
1adb TU106 USB Type-C UCSI Controller
1043 8673 TURBO-RTX2070-8G
1aeb TU116 High Definition Audio Controller
+ 1aed TU116 [GeForce GTX 1650 SUPER]
1b00 GP102 [TITAN X]
1b01 GP102 [GeForce GTX 1080 Ti 10GB]
1b02 GP102 [TITAN Xp]
1414 0020 GTX 1060 Mobile
1c2d GP106M
1c30 GP106GL [Quadro P2000]
+ 1c31 GP106GL [Quadro P2200]
1c35 GP106
1c60 GP106BM [GeForce GTX 1060 Mobile 6GB]
103c 8390 GeForce GTX 1060 Max-Q 6GB
1c70 GP106GL
1c81 GP107 [GeForce GTX 1050]
1c82 GP107 [GeForce GTX 1050 Ti]
+ 1043 8613 PH-GTX1050TI-4G
+ 1458 3763 GV-N105TOC-4GD
1c83 GP107 [GeForce GTX 1050 3GB]
1c8c GP107M [GeForce GTX 1050 Ti Mobile]
1c8d GP107M [GeForce GTX 1050 Mobile]
1c90 GP107M [GeForce MX150]
1c91 GP107M [GeForce GTX 1050 3 GB Max-Q]
1c92 GP107M [GeForce GTX 1050 Mobile]
+ 1c94 GP107M [GeForce MX350]
1ca7 GP107GL
1ca8 GP107GL
1caa GP107GL
103c 842f P1000 [Zbook 17 G5 mobile workstation]
103c 8451 P1000 [Zbook Studio x360 G5 mobile workstation]
1cbc GP107GLM [Quadro P600 Mobile]
+ 1cbd GP107GLM [Quadro P620]
1ccc GP107BM [GeForce GTX 1050 Ti Mobile]
1ccd GP107BM [GeForce GTX 1050 Mobile]
+ 1cfa GP107GL [Quadro P2000]
+ 1cfb GP107GL [Quadro P1000]
1d01 GP108 [GeForce GT 1030]
1d10 GP108M [GeForce MX150]
17aa 225e ThinkPad T480
1d12 GP108M [GeForce MX150]
1d72 1701 Mi Notebook Pro [GeForce MX150]
1d13 GP108M [GeForce MX250]
+ 1d16 GP108M [GeForce MX330]
1d33 GP108GLM [Quadro P500 Mobile]
+ 1d34 GP108GLM [Quadro P520]
1d52 GP108BM [GeForce MX250]
1d81 GV100 [TITAN V]
1db1 GV100GL [Tesla V100 SXM2 16GB]
- 1db2 GV100GL [Tesla V100-DGXS-16GB]
+ 1db2 GV100GL [Tesla V100 DGXS 16GB]
1db3 GV100GL [Tesla V100 FHHL 16GB]
1db4 GV100GL [Tesla V100 PCIe 16GB]
1db5 GV100GL [Tesla V100 SXM2 32GB]
1db6 GV100GL [Tesla V100 PCIe 32GB]
1db7 GV100GL [Tesla V100 DGXS 32GB]
+ 1db8 GV100GL [Tesla V100 SXM3 32GB]
+ 10de 131d Tesla V100-SXM3-32GB-H
1dba GV100GL [Quadro GV100]
10de 12eb TITAN V CEO Edition
+ 1df0 GV100GL [Tesla PG500-216]
+ 1df2 GV100GL [Tesla PG503-216]
+ 1df5 GV100GL [Tesla V100 SXM2 16GB]
+ 1df6 GV100GL [Tesla V100S PCIe 32GB]
1e02 TU102 [TITAN RTX]
1e04 TU102 [GeForce RTX 2080 Ti]
1e07 TU102 [GeForce RTX 2080 Ti Rev. A]
1e30 TU102GL [Quadro RTX 6000/8000]
10de 129e Quadro RTX 8000
10de 12ba Quadro RTX 6000
+ 1e37 TU102GL [GRID RTX T10-4/T10-8/T10-16]
+ 10de 1347 GRID RTX T10-8
+ 10de 1348 GRID RTX T10-4
+ 10de 1370 GRID RTX T10-16
1e38 TU102GL
1e3c TU102GL
1e3d TU102GL
1e3e TU102GL
+ 1e78 TU102GL [Quadro RTX 6000/8000]
+ 10de 13d8 Quadro RTX 8000
+ 10de 13d9 Quadro RTX 6000
1e81 TU104 [GeForce RTX 2080 SUPER]
1e82 TU104 [GeForce RTX 2080]
1e84 TU104 [GeForce RTX 2070 SUPER]
1e87 TU104 [GeForce RTX 2080 Rev. A]
+ 1e89 TU104 [GeForce RTX 2060]
1e90 TU104M [GeForce RTX 2080 Mobile]
1eab TU104M
1eae TU104M
1eb8 TU104GL [Tesla T4]
1eb9 TU104GL
1ebe TU104GL
+ 1ec2 TU104 [GeForce RTX 2070 SUPER]
+ 1ec7 TU104 [GeForce RTX 2070 SUPER]
1ed0 TU104BM [GeForce RTX 2080 Mobile]
1f02 TU106 [GeForce RTX 2070]
1043 8673 TURBO RTX 2070
1f11 TU106M [GeForce RTX 2060 Mobile]
1f2e TU106M
1f36 TU106GLM [Quadro RTX 3000 Mobile / Max-Q]
+ 1f42 TU106 [GeForce RTX 2060 SUPER]
+ 1f47 TU106 [GeForce RTX 2060 SUPER]
1f50 TU106BM [GeForce RTX 2070 Mobile]
1f51 TU106BM [GeForce RTX 2060 Mobile]
1f81 TU117
1f82 TU117 [GeForce GTX 1650]
+ 1f91 TU117M [GeForce GTX 1650 Mobile / Max-Q]
1f92 TU117M [GeForce GTX 1650 Mobile]
+ 1f96 TU117M [GeForce GTX 1650 Mobile / Max-Q]
1fae TU117GL
1fb8 TU117GLM [Quadro T2000 Mobile / Max-Q]
1fb9 TU117GLM [Quadro T1000 Mobile]
2182 TU116 [GeForce GTX 1660 Ti]
2183 TU116
2184 TU116 [GeForce GTX 1660]
+ 2187 TU116 [GeForce GTX 1650 SUPER]
2191 TU116M [GeForce GTX 1660 Ti Mobile]
21ae TU116GL
21bf TU116GL
+ 21c4 TU116 [GeForce GTX 1660 SUPER]
21d1 TU116BM [GeForce GTX 1660 Ti Mobile]
10df Emulex Corporation
0720 OneConnect NIC (Skyhawk)
17aa 3832 Yoga 520
522a RTS522A PCI Express Card Reader
103c 8079 EliteBook 840 G3
+ 103c 825b OMEN-17-w001nv
+ 17aa 5124 ThinkPad E595
5249 RTS5249 PCI Express Card Reader
103c 1909 ZBook 15
524a RTS524A PCI Express Card Reader
5260 RTS5260 PCI Express Card Reader
5286 RTS5286 PCI Express Card Reader
5287 RTL8411B PCI Express Card Reader
+ 1025 1094 Acer Aspire E5-575G
5288 RTS5288 PCI Express Card Reader
5289 RTL8411 PCI Express Card Reader
1043 1457 K55A Laptop
103c 006a NX9500
103c 2a20 Pavilion t3030.de Desktop PC
103c 30d9 Presario C700
- 1043 1045 L8400B or L3C/S notebook
+ 1043 1045 L8400B, L3C/S, X58LE notebook
1043 8109 P5P800-MX Mainboard
1071 8160 MIM2000
10bd 0320 EP-320X-R
1462 236c 945P Neo3-F motherboard
8168 RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller
1019 8168 RTL8111/8168 PCI Express Gigabit Ethernet controller
+ 1025 1094 Acer Aspire E5-575G
1028 0283 Vostro 220
1028 04b2 Vostro 3350
1028 04da Vostro 3750
103c 1611 Pavilion DM1Z-3000
103c 1950 ProBook 450/455
103c 2a6f Asus IPIBL-LB Motherboard
+ 103c 825b OMEN-17-w001nv
103c 8615 Pavilion Laptop 15-cw1xxx
1043 11f5 Notebook motherboard (one of many models)
1043 16d5 U6V/U31J laptop
1462 368c K9AG Neo2
1462 4180 Wind PC MS-7418
1462 7522 X58 Pro-E
+ 1462 7c37 X570-A PRO motherboard
1775 11cc CC11/CL11
+ 17aa 3814 Z50-75
+ 17aa 5124 ThinkPad E595
1849 8168 Motherboard (one of many)
7470 3468 TG-3468 Gigabit PCI Express Network Adapter
8086 2055 NUC Kit DN2820FYKH
8821 RTL8821AE 802.11ac PCIe Wireless Network Adapter
b723 RTL8723BE PCIe Wireless Network Adapter
10ec 8739 Dell Wireless 1801
+ 17aa b736 Z50-75
b822 RTL8822BE 802.11a/b/g/n/ac WiFi adapter
103c 831b Realtek RTL8822BE 802.11ac 2 × 2 Wi-Fi + Bluetooth 4.2 Combo Adapter (MU-MIMO supported)
+ 17aa 5124 ThinkPad E595
+ 17aa b023 ThinkPad E595
c821 RTL8821CE 802.11ac PCIe Wireless Network Adapter
+ c822 RTL8822CE 802.11ac PCIe Wireless Network Adapter
d723 RTL8723DE 802.11b/g/n PCIe Adapter
10ed Ascii Corporation
7310 V7310
1102 0021 X-Fi Platinum
1102 002c X-Fi XtremeGamer FATAL1TY PRO
1102 1003 X-Fi XtremeMusic
- 0006 EMU10k1X [SB Live! Value/OEM Series]
+# This chip is also known as CA0103 on Sound Blaster 5.1 SB0680 card.
+ 0006 EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1]
+ 1102 1001 SB0680 Sound Blaster 5.1
+ 1102 1003 SB0203 SB Live! 5.1 (Dell)
+ 1102 1004 TP0033 Ectiva Audio 5.1
0007 CA0106/CA0111 [SB Live!/Audigy/X-Fi Series]
1102 0007 SBLive! 24bit
1102 1001 SB0310 Audigy LS
1259 2975 AT-2970SX/2SC Gigabit Ethernet Adapter
1259 2976 AT-2970LX/2SC Gigabit Ethernet Adapter
1259 2977 AT-2970TX/2TX Gigabit Ethernet Adapter
- 4320 SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC
+ 4320 SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001]
1148 0121 Marvell RDK-8001 Adapter
1148 0221 Marvell RDK-8002 Adapter
1148 0321 Marvell RDK-8003 Adapter
1148 5061 SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter
1148 5071 SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter
1148 9521 SK-9521 10/100/1000Base-T Adapter
+ 1259 2916 AT-2916T
4400 SK-9Dxx Gigabit Ethernet Adapter
4500 SK-9Mxx Gigabit Ethernet Adapter
- 9000 SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45
+ 9000 SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022]
+ 1148 2100 SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45
+ 1148 2200 SK-9S22 10/100/1000Base-T Dual Port Server Adapter, PCI-X, 2 Copper RJ-45
+ 1148 2210 SK-9P22 10/100/1000 Base-T Dual Port PMC card
+ 1148 2220 TPMC-GBE-CO
+ 1148 8100 SK-9S81 1000Base-SX Server Adapter,PCI-X, Fiber SX/LC
+ 1148 8200 SK-9S82 1000Base-SX Dual Port Server Adapter, PCI-X, 2 Fiber SX/LC
+ 1148 8210 SK-9P82 1000 Base-SX Dual Port PMC card
+ 1148 8220 TPMC-GBE-FI
+ 1148 9100 SK-9S91 1000Base-LX Server Adapter,PCI-X, Fiber LX/LC
+ 1148 9200 SK-9S92 1000Base-LX Dual Port Server Adapter, PCI-X, 2 Fiber LX/LC
+ 1259 2973 AT-2971SX v2 Gigabit Adapter
+ 1259 2974 AT-2971T v2 Gigabit Adapter
+ 1259 2978 AT-2971LX Gigabit Adapter
9843 [Fujitsu] Gigabit Ethernet
9e00 SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45
1148 2100 SK-9E21 Server Adapter
1028 0188 Inspiron 6000 laptop
103c 30c0 Compaq 6710b
103c 30c1 Compaq 6910p
+ 1043 1017 X58LE
1043 1237 A6J-Q008
1043 1967 V6800V
1043 1987 A4K and Z81K notebooks, possibly others ( mid-2005 machines )
103c 30b7 Presario V6133CL
103c 30cc Pavilion dv6700
103c 30cf Pavilion dv95xx/96xx/97xx/98xx series
+ 1043 1017 X58LE
1043 1237 A6J-Q008
1043 1967 V6800V
104d 9035 VAIO VGN-FW11ZRU
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30cf Pavilion dv9668eg Laptop
+ 1043 1017 X58LE
1043 1237 A6J-Q008
1043 1967 V6800V
10f7 8338 Panasonic CF-Y5 laptop
103c 1521 HP EliteBook 8540w
103c 30b7 Presario V6133CL
103c 30cf Pavilion dv9500/9600/9700 series
+ 1043 1017 X58LE
1183 0843 Alienware Aurora m9700
0852 xD-Picture Card Controller
1025 0121 Aspire 5920G
4380 88E8057 PCI-E Gigabit Ethernet Controller
# AVB = "Audio Video Bridging"
4381 Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB]
+ 1259 2803 AT-2814FX
+ 1259 2804 AT-2874xx
4611 GT-64115 System Controller
4620 GT-64120/64120A/64121A System Controller
4801 GT-48001
000e PM/PPC
1224 Interactive Images
1225 Power I/O, Inc.
-1227 Tech-Source
+1227 EIZO Rugged Solutions
0006 Raptor GFX 8P
0023 Raptor GFX [1100T]
0045 Raptor 4000-L [Linux version]
1028 1ff8 Express Flash PM1725b 3.2TB AIC
1028 1ff9 Express Flash PM1725b 6.4TB AIC
1028 1ffa Express Flash PM1725b 12.8TB AIC
+ a824 NVMe SSD Controller PM173X
144e OLITEC
144f Askey Computer Corp.
1450 Octave Communications Ind.
10cf 1279 LifeBook E8010D
165f NetXtreme BCM5720 2-port Gigabit Ethernet PCIe
1028 04f7 PowerEdge R320 server
+ 1028 08fd PowerEdge R6515/R7515 LOM
1028 08ff PowerEdge Rx5xx LOM Board
1028 0900 PowerEdge C6525 LOM
103c 1786 NC332T Adapter
4430 BCM44xx CardBus iLine32 HomePNA 2.0
4432 BCM4432 CardBus 10/100BaseT
4464 BCM4364 802.11ac Wireless Network Adapter
+# brcmfmac reports it as BCM4377/4 but macOS drivers call it BCM4377b
+ 4488 BCM4377b Wireless Network Adapter
4610 BCM4610 Sentry5 PCI to SB Bridge
4611 BCM4610 Sentry5 iLine32 HomePNA 1.0
4612 BCM4610 Sentry5 V.90 56k Modem
0263 MT27710 [ConnectX-4 Lx Programmable Virtual Function] EN
0264 Innova-2 Flex Burn image
0281 NPS-600 Flash Recovery
+ 0538 MT2910 Family [ConnectX-7 Flash Recovery]
+ 0539 MT2910 Family [ConnectX-7 Secure Flash Recovery]
1002 MT25400 Family [ConnectX-2 Virtual Function]
1003 MT27500 Family [ConnectX-3]
1014 04b5 PCIe3 40GbE RoCE Converged Host Bus Adapter for Power
101e ConnectX Family mlx5Gen Virtual Function
101f MT2894 Family [ConnectX-6 Lx]
1020 MT28860
- 1021 MT28861
+ 1021 MT2910 Family [ConnectX-7]
1974 MT28800 Family [ConnectX-5 PCIe Bridge]
1975 MT416842 Family [BlueField SoC PCIe Bridge]
1976 MT28908 Family [ConnectX-6 PCIe Bridge]
1978 MT42822 Family [BlueField-2 SoC PCIe Bridge]
4117 MT27712A0-FDCF-AE
1bd4 0039 SN10XMP2P25
+ 1bd4 003a 25G SFP28 SP EO251FM9 Adapter
1bd4 004d SN10XMP2P25,YZPC-01191-101
5274 MT21108 InfiniBridge
5a44 MT23108 InfiniHost
0040 QCA9980/9990 802.11ac Wireless Network Adapter
0041 QCA6164 802.11ac Wireless Network Adapter
0042 QCA9377 802.11ac Wireless Network Adapter
+ 11ad 08a6 Qualcomm Atheros QCA9377 802.11ac Wireless Network Adapter
0046 QCA9984 802.11ac Wave 2 Wireless Network Adapter
0050 QCA9887 802.11ac Wireless Network Adapter
0207 AR5210 Wireless Network Adapter [AR5000 802.11a]
17d3 1882 ARC-1882 8/12/16/24 Port PCIe 3.0 to SAS/SATA 6Gb RAID Controller
17d3 1883 ARC-1883 8/12/16/24 Port PCIe 3.0 to SAS/SATA 12Gb RAID Controller
1884 ARC-1884 series PCIe 3.0 to SAS/SATA 12/6Gb RAID Controller
+ 188a ARC-1886 series PCIe 4.0 to NVMe/SAS/SATA 16/12/6Gb RAID Controller
# nee Neterion Inc., previously S2io Inc.
17d5 Exar Corp.
5731 Xframe 10-Gigabit Ethernet PCI-X
1026 AR8121/AR8113/AR8114 Gigabit or Fast Ethernet
1043 8304 P5KPL-CM Motherboard
1048 Attansic L1 Gigabit Ethernet
- 1043 8226 P5KPL-VM Motherboard
+ 1043 8226 P5B-MX/WiFi-AP, P5KPL-VM Motherboard
1062 AR8132 Fast Ethernet
1063 AR8131 Gigabit Ethernet
1458 e000 GA-G31M-ES2L Motherboard
1987 Phison Electronics Corporation
5007 E7 NVMe Controller
5012 E12 NVMe Controller
+ 5016 E16 PCIe4 NVMe Controller
1989 Montilio Inc.
0001 RapidFile Bridge
8001 RapidFile
19e5 3034 NVMe SSD ES3600C V3 1600GB HHHL AIC
19e5 3036 NVMe SSD ES3600C V3 3200GB HHHL AIC
0200 Hi1822 Family (2*100GE)
+ 19e5 d139 Hi1822 SP572 (2*100GE)
+ 19e5 d13d Hi1822 SC371 (2*100GE)
0202 Hi1822 Family (2*32G FC)
+ 19e5 d302 Hi1822 SP521 (2*32G FC)
+ 19e5 d304 Hi1822 SP526 (2*32G FC)
0203 Hi1822 Family (2*16G FC)
+ 19e5 d301 Hi1822 SP520 (2*16G FC)
+ 19e5 d305 Hi1822 SP525 (2*16G FC)
0205 Hi1822 Family (2*100GE)
+ 19e5 df27 Hi1822 MZ731 MEZZ (2*100GE)
0206 Hi1822 Family (2*25GE)
+ 19e5 d138 Hi1822 SP582 (2*25GE)
+ 19e5 d13a Hi1822 SC381 (2*25GE)
0210 Hi1822 Family (4*25GE)
+ 19e5 df2e Hi1822 MZ532 MEZZ (4*25GE)
0211 Hi1822 Family (4*25GE)
+ 19e5 d12f Hi1822 SP571 (4*25GE)
+ 19e5 d137 Hi1822 SP581 (4*25GE)
+ 19e5 d142 Hi1822 SP583 (4*25GE)
0212 Hi1822 Family (2*8G FC)
+ 19e5 d303 Hi1822 SP522 (2*8G FC)
+ 19e5 d306 Hi1822 SP523 (2*8G FC)
1710 iBMA Virtual Network Adapter
1711 Hi1710 [iBMC Intelligent Management system chip w/VGA support]
1822 Hi1822 Family (4*25GE)
+ 19e5 d129 Hi1822 SP570 (4*25GE)
+ 19e5 d136 Hi1822 SP580 (4*25GE)
+ 19e5 d141 Hi1822 SP583 (4*25GE)
371e Hi1822 Family Virtual Bridge
375e Hi1822 Family Virtual Function
379e Hi1822 Family Virtual Function
4005 Accelerated Virtual Video Adapter
4006 Memory Ballooning Controller
1ab9 Espia Srl
+1ac1 Global Unichip Corp.
+ 089a Coral Edge TPU
1ac8 Aeroflex Gaisler
1acc Point of View BV
1ad7 Spectracom Corporation
0310 Wil6200 802.11ad Wireless Network Adapter
1aea Alcor Micro
6601 AU6601 PCI-E Flash card reader controller
+ 6621 AU6621 PCI-E Flash card reader controller
+ 6625 AU6625 PCI-E Flash card reader controller
1aec Wolfson Microelectronics
# nee Fusion-io
1aed SanDisk
1b6f Etron Technology, Inc.
7023 EJ168 USB 3.0 Host Controller
7052 EJ188/EJ198 USB 3.0 Host Controller
+ 1849 7052 QC5000-ITX/PH
1b73 Fresco Logic
1000 FL1000G USB 3.0 Host Controller
1d5c 1000 Anker USB 3.0 Express Card
00a4 FBC4XGG3 Capture 4x10Gb [Livigno]
00a5 FBC2XLG Capture 2x40Gb [Livorno]
00a6 FBC1CG Capture 1x100Gb
- 00a9 FBC2XGHH Capture 2x10Gb
+ 00a9 FBC2XGHH Capture 2x10Gb [Latina]
00ad FBC2CGG3HL Capture 2x100Gb [Padua]
00af Capture slave device
00e0 PacketMover 2x100Gb [Savona]
1283 PC300 NVMe Solid State Drive 256GB
1284 PC300 NVMe Solid State Drive 512GB
1285 PC300 NVMe Solid State Drive 1TB
+ 1327 BC501 NVMe Solid State Drive 512GB
1504 SC300 512GB M.2 2280 SATA Solid State Drive
1c5f Beijing Memblaze Technology Co. Ltd.
+ 000d PBlaze5 520/526 AIC
+ 003d PBlaze5 920/926 AIC
+ 010d PBlaze5 520/526 U.2
+ 013d PBlaze5 920/926 U.2
0540 PBlaze4 NVMe SSD
+ 0550 PBlaze5 700/900
+ 0555 PBlaze5 510/516
+ 0557 PBlaze5 910/916
# http://www.nicevt.ru/ (in Russian)
1c63 Science and Research Centre of Computer Technology (JSC "NICEVT")
# http://www.radiotec.ru/catalog.php?cat=jr8&art=14109
1dbb NGD Systems, Inc.
1dbf Guizhou Huaxintong Semiconductor Technology Co., Ltd
0401 StarDragon4800 PCI Express Root Port
+1dc5 FADU Inc.
+1dcd Liqid Inc.
1dd8 Pensando Systems Inc
1000 DSC Capri Upstream Port
1dd8 4000 Naples 100Gb 2-port QSFP28 x16 8GB
1df3 0001 ENA2080F
1df3 0002 ENA2080FS
1df3 0003 ENA2100F
+ 1df3 0004 ENA2040F
0204 ACE-NIC-NID Programmable Network Accelerator
1df3 0001 ENA1020Z
1df3 0002 ENA1020ZS
# JungleCat VU35P Module
1635 JCM35
1e26 Fujitsu Client Computing Limited
-1e38 Thinci, Inc
+1e36 Shanghai Enflame Technology Co. Ltd
+ 0001 T10 [CloudBlazer]
+# nee Thinci, Inc
+1e38 Blaize, Inc
1e3d Burlywood, Inc
1e49 Yangtze Memory Technologies Co.,Ltd
1e4c GSI Technology
0010 Gemini [ Lida ]
1e4c 0120 SE120
1e57 Beijing Panyi Technology Co., Ltd
+ 0100 The device has already been deleted.
+ 0000 0100 PY8800 64GB Accelerator
1e6b Axiado Corp.
+1e89 ID Quantique SA
+ 0002 Quantis-PCIe-40M
+ 0003 Quantis-PCIe-240M
+# aka SED Systems
+1e94 Calian SED
# nee Tumsan Oy
1fc0 Ascom (Finland) Oy
0300 E2200 Dual E1/Rawpipe Card
1043 108d VivoBook X202EV
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
+ 10cf 16bf LIFEBOOK E752
0155 Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port
8086 2010 Server Board S1200BTS
0156 3rd Gen Core processor Graphics Controller
0166 3rd Gen Core processor Graphics Controller
1043 1517 Zenbook Prime UX31A
1043 2103 N56VZ
+ 10cf 16c1 LIFEBOOK E752
016a Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller
1043 844d P8B WS Motherboard
0172 Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller
1014 0549 Thinkpad
1179 0001 PRO/1000 MT Mobile Connection
8086 101e PRO/1000 MT Mobile Connection
+ 101f Ethernet Controller V710 for 5GBASE-T
1026 82545GM Gigabit Ethernet Controller
1028 0168 Precision Workstation 670 Mainboard
1028 0169 Precision 470
104e Ethernet Controller X710 for 10 Gigabit SFP+
104f Ethernet Controller X710 for 10 Gigabit backplane
1050 82562EZ 10/100 Ethernet Controller
+ 1014 0287 ThinkCentre S50
1028 019d Dimension 3000
1462 728c 865PE Neo2 (MS-6728)
1462 758c MS-6758 (875P Neo)
8086 357a Server Board S1200BTS
1503 82579V Gigabit Network Connection
1043 849c P8P67 Deluxe Motherboard
+ 10cf 161c LIFEBOOK E752
1507 Ethernet Express Module X520-P2
1508 82598EB Gigabit BX Network Connection
1509 82580 Gigabit Network Connection
108e 7b15 Sun Dual Port 10 GbE PCIe 2.0 Low Profile Adapter, Base-T
1137 00bf Ethernet Converged Network Adapter X540-T2
1170 0052 Ethernet Controller 10-Gigabit X540-AT2
+ 15d9 0734 AOC-STG-I2T
17aa 1073 ThinkServer X540-T2 AnyFabric
17aa 4006 Ethernet Controller 10-Gigabit X540-AT2
1bd4 001a 10G base-T DP ER102Ti3 Rack Adapter
18d4 0c08 X550 10Gb 2-port RJ45 OCP Mezz Card MOP81-I-10GT2
193d 1008 560T-B
193d 1009 560T-L
+ 193d 1011 UN-NIC-ETH563T-sL-2P
8086 0001 Ethernet Converged Network Adapter X550-T2
8086 001a Ethernet Converged Network Adapter X550-T2
8086 001b Ethernet Server Adapter X550-T2 for OCP
1137 0000 Ethernet Network Adapter XXV710
1137 0225 Ethernet Network Adapter XXV710
1137 02b4 Ethernet Network Adapter XXV710 OCP 2.0
+# UEFI PXE Disabled
+ 1374 0230 Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71)
+# With UEFI PXE Enabled
+ 1374 0231 Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71EU)
+# UEFI PXE Disabled
+ 1374 0234 Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71)
+# With UEFI PXE Enabled
+ 1374 0235 Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71EU)
+# PCIe x8 Bifurcated as x4x4, UEFI PXE Disabled, low profile
+ 1374 0238 Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71L)
+# PCIe x8 Bifurcated as x4x4, UEFI PXE Enabled, low profile
+ 1374 0239 Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71LEU)
+# PCIe x16 Bifurcated as x8x8, UEFI PXE Disabled, low profile
+ 1374 023a Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71L)
+# PCIe x16 Bifurcated as x8x8, UEFI PXE Enabled, low profile
+ 1374 023b Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71LEU)
1590 0000 Ethernet Network Adapter XXV710-2
1590 0253 Ethernet 10/25/Gb 2-port 661SFP28 Adapter
8086 0000 Ethernet Network Adapter XXV710
15ec JHL7540 Thunderbolt 3 USB Controller [Titan Ridge 4C 2018]
15ef JHL7540 Thunderbolt 3 Bridge [Titan Ridge DD 2018]
15f0 JHL7540 Thunderbolt 3 USB Controller [Titan Ridge DD 2018]
+ 15f4 Ethernet Connection (15) I219-LM
+ 15f5 Ethernet Connection (15) I219-V
15f6 I210 Gigabit Ethernet Connection
+ 15f9 Ethernet Connection (14) I219-LM
+ 15fa Ethernet Connection (14) I219-V
+ 15fb Ethernet Connection (13) I219-LM
+ 15fc Ethernet Connection (13) I219-V
15ff Ethernet Controller X710 for 10GBASE-T
1137 0000 X710TLG GbE RJ45 PCIe NIC
1137 02c1 X710T2LG 2x10 GbE RJ45 PCIe NIC
1903 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem
1028 06dc Latitude E7470
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
17aa 225d ThinkPad T480
1904 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
1028 06dc Latitude E7470
190f Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
1910 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
1911 Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
1919 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Imaging Unit
191b HD Graphics 530
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
191d HD Graphics P530
191e HD Graphics 515
191f Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
19df Atom Processor C3000 Series SMBus controller
19e0 Atom Processor C3000 Series SPI Controller
19e2 Atom Processor C3000 Series QuickAssist Technology
+ 1a1c Ethernet Connection (17) I219-LM
+ 1a1d Ethernet Connection (17) I219-V
+ 1a1e Ethernet Connection (16) I219-LM
+ 1a1f Ethernet Connection (16) I219-V
1a21 82840 840 [Carmel] Chipset Host Bridge (Hub A)
1a23 82840 840 [Carmel] Chipset AGP Bridge
1a24 82840 840 [Carmel] Chipset PCI Bridge (Hub B)
1043 108d VivoBook X202EV
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
+ 10cf 16e2 LIFEBOOK E752
144d c652 NP300E5C series laptop
1e04 7 Series/C210 Series Chipset Family SATA Controller [RAID mode]
1e05 7 Series Chipset SATA Controller [RAID mode]
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1043 84ca P8H77-I Motherboard
+ 10cf 16e9 LIFEBOOK E752
144d c652 NP300E5C series laptop
1849 1e10 Motherboard
1e12 7 Series/C210 Series Chipset Family PCI Express Root Port 2
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1e14 7 Series/C210 Series Chipset Family PCI Express Root Port 3
+ 10cf 16e9 LIFEBOOK E752
1e16 7 Series/C216 Chipset Family PCI Express Root Port 4
1043 108d VivoBook X202EV
1043 1477 N56VZ
1849 1e1a Motherboard
1e1c 7 Series/C210 Series Chipset Family PCI Express Root Port 7
1e1e 7 Series/C210 Series Chipset Family PCI Express Root Port 8
+ 10cf 16e9 LIFEBOOK E752
1849 1e1e Motherboard
1e20 7 Series/C216 Chipset Family High Definition Audio Controller
1028 054b XPS One 2710
1043 1517 Zenbook Prime UX31A
1043 8415 P8H77-I Motherboard
1043 8445 P8Z77-V LX Motherboard
+ 10cf 1757 LIFEBOOK E752
144d c652 NP300E5C series laptop
1849 1898 Z77 Extreme4 motherboard
1e22 7 Series/C216 Chipset Family SMBus Controller
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1043 84ca P8 series motherboard
+ 10cf 16e6 LIFEBOOK E752
144d c652 NP300E5C series laptop
1849 1e22 Motherboard
1e24 7 Series/C210 Series Chipset Family Thermal Management Controller
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1043 84ca P8 series motherboard
+ 10cf 16e8 LIFEBOOK E752
144d c652 NP300E5C series laptop
1849 1e26 Motherboard
1e2d 7 Series/C216 Chipset Family USB Enhanced Host Controller #2
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1043 84ca P8 series motherboard
+ 10cf 16e8 LIFEBOOK E752
144d c652 NP300E5C series laptop
1849 1e2d Motherboard
1e31 7 Series/C210 Series Chipset Family USB xHCI Host Controller
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1043 84ca P8 series motherboard
+ 10cf 16ee LIFEBOOK E752
17aa 21f3 ThinkPad T430
1849 1e31 Motherboard
1e33 7 Series/C210 Series Chipset Family LAN Controller
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
1043 84ca P8 series motherboard
+ 10cf 16ea LIFEBOOK E752
144d c652 NP300E5C series laptop
1849 1e3a Motherboard
1e3b 7 Series/C210 Series Chipset Family MEI Controller #2
1e59 HM76 Express Chipset LPC Controller
1043 1477 N56VZ
1043 1517 Zenbook Prime UX31A
+ 10cf 16e0 LIFEBOOK E752
1e5a 7 Series Chipset Family LPC Controller
1e5b UM77 Express Chipset LPC Controller
1e5c 7 Series Chipset Family LPC Controller
103c 309f Compaq nx9420 Notebook
103c 30a3 Compaq nw8440
103c 30c1 Compaq 6910p
+ 1043 1017 X58LE
104d 902d VAIO VGN-NR120E
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
8086 4c43 Desktop Board D865GLC
8086 524c D865PERL mainboard
24d2 82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1
+ 1014 0287 ThinkCentre S50
1014 02dd eServer xSeries server mainboard
1014 02ed eServer xSeries server mainboard
1028 0168 Precision Workstation 670 Mainboard
8086 4c43 Desktop Board D865GLC
8086 524c D865PERL mainboard
24d3 82801EB/ER (ICH5/ICH5R) SMBus Controller
+ 1014 0287 ThinkCentre S50
1014 02dd eServer xSeries server mainboard
1014 02ed eServer xSeries server mainboard
1028 0156 Precision 360
8086 4c43 Desktop Board D865GLC
8086 524c D865PERL mainboard
24d4 82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2
+ 1014 0287 ThinkCentre S50
1014 02dd eServer xSeries server mainboard
1014 02ed eServer xSeries server mainboard
1028 0168 Precision Workstation 670 Mainboard
8086 524c D865PERL mainboard
24d5 82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller
100a 147b Abit IS7-E motherboard
+ 1014 0287 ThinkCentre S50
1028 0168 Precision Workstation 670 Mainboard
1028 0169 Precision 470
103c 006a NX9500
24d6 82801EB/ER (ICH5/ICH5R) AC'97 Modem Controller
103c 006a NX9500
24d7 82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3
+ 1014 0287 ThinkCentre S50
1014 02ed xSeries server mainboard
1028 0168 Precision Workstation 670 Mainboard
1028 0169 Precision 470
8086 4c43 Desktop Board D865GLC
8086 524c D865PERL mainboard
24db 82801EB/ER (ICH5/ICH5R) IDE Controller
+ 1014 0287 ThinkCentre S50
1014 02dd eServer xSeries server mainboard
1014 02ed eServer xSeries server mainboard
1028 0168 Precision Workstation 670 Mainboard
8086 524c D865PERL mainboard
24dc 82801EB (ICH5) LPC Interface Bridge
24dd 82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller
+ 1014 0287 ThinkCentre S50
1014 02dd eServer xSeries server mainboard
1014 02ed eServer xSeries server mainboard
1028 0168 Precision Workstation 670 Mainboard
8086 4c43 Desktop Board D865GLC
8086 524c D865PERL mainboard
24de 82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4
+ 1014 0287 ThinkCentre S50
1014 02ed xSeries server mainboard
1028 0168 Precision Workstation 670 Mainboard
1028 0169 Precision 470
1458 2570 GA-8IPE1000 Pro2 motherboard (865PE)
2571 82865G/PE/P AGP Bridge
2572 82865G Integrated Graphics Controller
+ 1014 0287 ThinkCentre S50
1028 019d Dimension 3000
103c 12bc D530 sff(dc578av)
1043 80a5 P5P800-MX Mainboard
27b8 82801GB/GR (ICH7 Family) LPC Interface Bridge
1028 01e6 PowerEdge 860
103c 2a8c Compaq 500B Microtower
- 1043 8179 P5KPL-VM Motherboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM Motherboard
107b 5048 E4500
1462 7418 Wind PC MS-7418
1775 11cc CC11/CL11
1028 01df PowerEdge SC440
1028 01e6 PowerEdge 860
103c 2a8c Compaq 500B Microtower
- 1043 8179 P5KPL-VM Motherboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM Motherboard
107b 5048 E4500
1462 2310 MSI Hetis 945
1462 7236 945P Neo3-F Rev. 2.2 motherboard
103c 30a3 Compaq nw8440
103c 30d5 530 Laptop
1043 1237 A6J-Q008
- 1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
103c 30a1 NC2400
103c 30a3 Compaq nw8440
1043 1237 A6J-Q008
- 1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
103c 30a1 NC2400
103c 30a3 Compaq nw8440
1043 1237 A6J-Q008
- 1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
103c 30a1 NC2400
103c 30a3 Compaq nw8440
1043 1237 A6J-Q008
- 1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
103c 30a3 Compaq nw8440
103c 30d5 530 Laptop
1043 1237 A6J-Q008
- 1043 8179 P5KPL-VM,P5LD2-VM Mainboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
1043 83ad Eee PC 1015PX
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
1043 1123 A6J-Q008
1043 13c4 G2P
1043 817f P5LD2-VM Mainboard (Realtek ALC 882 codec)
+ 1043 8249 P5B-MX/WiFi-AP
1043 8290 P5KPL-VM Motherboard
1043 82ea P5KPL-CM Motherboard
1043 8437 Eee PC 1015PX
1028 01e6 PowerEdge 860
103c 2a3b Pavilion A1512X
103c 2a8c Compaq 500B Microtower
- 1043 8179 P5KPL-VM Motherboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM Motherboard
105b 0d7c D270S/D250S Motherboard
1071 8209 Medion MIM 2240 Notebook PC [MD98100]
10f7 8338 Panasonic CF-Y5 laptop
103c 30a3 Compaq nw8440
103c 30d5 530 Laptop
1043 1237 A6J-Q008
- 1043 8179 P5KPL-VM Motherboard
+ 1043 8179 P5B-MX/WiFi-AP, P5KPL-VM Motherboard
107b 5048 E4500
10f7 8338 Panasonic CF-Y5 laptop
1462 7418 Wind PC MS-7418
103c 30c0 Compaq 6710b
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
17aa 20a5 ThinkPad R61
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
17aa 20a7 ThinkPad T61/R61
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
103c 30c0 Compaq 6710b
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
103c 30c0 Compaq 6710b
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
103c 30c0 Compaq 6710b
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
1028 01f3 Inspiron 1420
1028 022f Inspiron 1525
103c 30d9 Presario C700
+ 1043 1017 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 9008 Vaio VGN-SZ79SN_C
283f 82801H (ICH8 Family) PCI Express Port 1
1028 01da OptiPlex 745
103c 30c1 Compaq 6910p
+ 1043 1017 X58LE
104d 902d VAIO VGN-NR120E
17aa 20ad ThinkPad T61/R61
17c0 4083 Medion WIM 2210 Notebook PC [MD96850]
2841 82801H (ICH8 Family) PCI Express Port 2
103c 30c1 Compaq 6910p
+ 1043 1017 X58LE
104d 902d VAIO VGN-NR120E
17aa 20ad ThinkPad T61/R61
17c0 4083 Medion WIM 2210 Notebook PC [MD96850]
2843 82801H (ICH8 Family) PCI Express Port 3
+ 1043 1017 X58LE
104d 902d VAIO VGN-NR120E
17aa 20ad ThinkPad T61/R61
17c0 4083 Medion WIM 2210 Notebook PC [MD96850]
2845 82801H (ICH8 Family) PCI Express Port 4
+ 1043 1017 X58LE
17aa 20ad ThinkPad T61/R61
17c0 4083 Medion WIM 2210 Notebook PC [MD96850]
2847 82801H (ICH8 Family) PCI Express Port 5
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
1043 1339 M51S series
+ 1043 17f3 X58LE
1043 81ec P5B
104d 9005 Vaio VGN-FZ260E
104d 9008 Vaio VGN-SZ79SN_C
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
17aa 20a6 ThinkPad T61/R61
17c0 4083 Medion WIM 2210 Notebook PC [MD96850]
e4bf cc47 CCG-RUMBA
+ 28c0 Volume Management Device NVMe RAID Controller
2912 82801IH (ICH9DH) LPC Interface Controller
2914 82801IO (ICH9DO) LPC Interface Controller
1028 0211 Optiplex 755
294c 82566DC-2 Gigabit Network Connection
17aa 302e 82566DM-2 Gigabit Network Connection
2970 82946GZ/PL/GL Memory Controller Hub
+ 1043 823b P5B-MX/WiFi-AP
2971 82946GZ/PL/GL PCI Express Root Port
2972 82946GZ/GL Integrated Graphics Controller
+ 1043 823b P5B-MX/WiFi-AP
2973 82946GZ/GL Integrated Graphics Controller
2974 82946GZ/GL HECI Controller
2975 82946GZ/GL HECI Controller
103c 30c1 Compaq 6910p
103c 30cc Pavilion dv6700
103c 30d9 Presario C700
+ 1043 1017 X58LE
104d 9005 Vaio VGN-FZ260E
104d 902d VAIO VGN-NR120E
17aa 20b1 ThinkPad T61
1028 022f Inspiron 1525
103c 30c0 Compaq 6710b
103c 30d9 Presario C700
+ 1043 14e2 X58LE
104d 902d VAIO VGN-NR120E
17aa 20b5 GM965 [X3100] on ThinkPad T61/R61
17c0 4082 GM965 on Medion WIM 2210 Notebook PC [MD96850]
1028 022f Inspiron 1525
103c 30c0 Compaq 6710b
103c 30d9 Presario C700
+ 1043 14e2 X58LE
104d 902d VAIO VGN-NR120E
17aa 20b5 GM965 [X3100] on ThinkPad T61/R61
17c0 4082 GM965 on Medion WIM 2210 Notebook PC [MD96850]
1028 02da OptiPlex 980
1028 040a Latitude E6410
1028 040b Latitude E6510
+ 103c 1521 EliteBook 8540p
144d c06a R730 Laptop
15d9 060d C7SIM-Q Motherboard
17c0 10d2 Medion Akoya E7214 Notebook PC [MD98410]
3e1f 8th Gen Core 4-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S]
1458 5000 Z370 AORUS Gaming K3-CF
3e30 8th Gen Core 8-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S]
+ 3e33 8th/9th Gen Core Processor Host Bridge/DRAM Registers [Coffee Lake]
3e34 Coffee Lake HOST and DRAM Controller
3e81 8th Gen Core Processor PCIe Controller (x16)
3e85 8th Gen Core Processor PCIe Controller (x8)
8086 1311 WiMAX/WiFi Link 5150 AGN
8086 1316 WiMAX/WiFi Link 5150 ABG
444e Turbo Memory Controller
+ 467f Volume Management Device NVMe RAID Controller
+ 4c3d Volume Management Device NVMe RAID Controller
5001 LE80578
5002 LE80578 Graphics Processor Unit
5009 LE80578 Video Display Controller
5901 Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x16)
5902 HD Graphics 610
5904 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+ 1025 115f Aspire E5-575G
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
5905 Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x8)
5914 Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
17aa 225d ThinkPad T480
5916 HD Graphics 620
+ 1025 1094 Aspire E5-575G
17aa 2248 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
5917 UHD Graphics 620
9622 Integrated RAID
9641 Integrated RAID
96a1 Integrated RAID
+ 9a0b Volume Management Device NVMe RAID Controller
9b41 UHD Graphics
9c00 8 Series SATA Controller 1 [IDE mode]
9c01 8 Series SATA Controller 1 [IDE mode]
9ce5 Wildcat Point-LP Serial IO GSPI Controller #0
9ce6 Wildcat Point-LP Serial IO GSPI Controller #1
9d03 Sunrise Point-LP SATA Controller [AHCI mode]
+ 1025 115f Acer Aspire E5-575G
1028 06dc Latitude E7470
1028 06f3 Latitude 3570
103c 8079 EliteBook 840 G3
9d19 Sunrise Point-LP PCI Express Root Port #10
9d1a Sunrise Point-LP PCI Express Root Port #11
9d21 Sunrise Point-LP PMC
+ 1025 115f Acer Aspire E5-575G
1028 06dc Latitude E7470
1028 06f3 Latitude 3570
103c 8079 EliteBook 840 G3
17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d23 Sunrise Point-LP SMBus
+ 1025 115f Acer Aspire E5-575G
1028 06dc Latitude E7470
1028 06f3 Latitude 3570
103c 8079 EliteBook 840 G3
9d2a Sunrise Point-LP Serial IO SPI Controller #1
9d2d Sunrise Point-LP Secure Digital IO Controller
9d2f Sunrise Point-LP USB 3.0 xHCI Controller
+ 1025 115f Acer Aspire E5-575G
1028 06dc Latitude E7470
1028 06f3 Latitude 3570
103c 8079 EliteBook 840 G3
17aa 225d ThinkPad T480
17aa 382a B51-80 Laptop
9d31 Sunrise Point-LP Thermal subsystem
+ 1025 115f Acer Aspire E5-575G
1028 06dc Latitude E7470
1028 06f3 Latitude 3570
103c 8079 EliteBook 840 G3
17aa 382a B51-80 Laptop
9d35 Sunrise Point-LP Integrated Sensor Hub
9d3a Sunrise Point-LP CSME HECI #1
+ 1025 115f Acer Aspire E5-575G
1028 06dc Latitude E7470
1028 06f3 Latitude 3570
103c 8079 EliteBook 840 G3
9d50 Sunrise Point LPC Controller
9d56 Sunrise Point-LP LPC Controller
9d58 Sunrise Point-LP LPC Controller
+ 1025 115f Acer Aspire E5-575G
17aa 2247 ThinkPad T570
17aa 224f ThinkPad X1 Carbon 5th Gen
9d60 Sunrise Point-LP Serial IO I2C Controller #0
+ 1025 115f Acer Aspire E5-575G
1028 06f3 Latitude 3570
103c 8079 EliteBook 840 G3
17aa 225d ThinkPad T480
103c 8079 EliteBook 840 G3
17aa 382a B51-80 Laptop
9d71 Sunrise Point-LP HD Audio
+ 1025 1094 Acer Aspire E5-575G
17aa 224f ThinkPad X1 Carbon 5th Gen
17aa 225d ThinkPad T480
9d84 Cannon Point-LP LPC Controller
9da4 Cannon Point-LP SPI Controller
9db0 Cannon Point-LP PCI Express Root Port #9
9db1 Cannon Point-LP PCI Express Root Port #10
+ 9db2 Cannon Point-LP PCI Express Root Port #1
9db4 Cannon Point-LP PCI Express Root Port #13
1028 089e Inspiron 5482
9db6 Cannon Point-LP PCI Express Root Port #15
1028 089e Inspiron 5482
9dd3 Cannon Point-LP SATA Controller [AHCI Mode]
9de0 Cannon Point-LP MEI Controller #1
+ 9de3 Cannon Point-LP Keyboard and Text (KT) Redirection
9de8 Cannon Point-LP Serial IO I2C Controller #0
1028 089e Inspiron 5482
9de9 Cannon Point-LP Serial IO I2C Controller #1
a102 Q170/Q150/B150/H170/H110/Z170/CM236 Chipset SATA Controller [AHCI Mode]
a103 HM170/QM170 Chipset SATA Controller [AHCI Mode]
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a105 Sunrise Point-H SATA Controller [RAID mode]
a106 Q170/H170/Z170/CM236 Chipset SATA Controller [RAID Mode]
a107 HM170/QM170 Chipset SATA Controller [RAID Mode]
a120 100 Series/C230 Series Chipset Family P2SB
a121 100 Series/C230 Series Chipset Family Power Management Controller
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a122 Sunrise Point-H cAVS
a123 100 Series/C230 Series Chipset Family SMBus
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a124 100 Series/C230 Series Chipset Family SPI Controller
a125 100 Series/C230 Series Chipset Family Gigabit Ethernet Controller
a126 100 Series/C230 Series Chipset Family Trace Hub
a12a 100 Series/C230 Series Chipset Family Serial IO GSPI #1
a12f 100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a130 100 Series/C230 Series Chipset Family USB Device Controller (OTG)
a131 100 Series/C230 Series Chipset Family Thermal Subsystem
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a133 Sunrise Point-H Northpeak ACPI Function
a135 100 Series/C230 Series Chipset Family Integrated Sensor Hub
a13a 100 Series/C230 Series Chipset Family MEI Controller #1
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a13b 100 Series/C230 Series Chipset Family MEI Controller #2
a13c 100 Series/C230 Series Chipset Family IDE Redirection
a13d 100 Series/C230 Series Chipset Family KT Redirection
a14d QM170 Chipset LPC/eSPI Controller
a14e HM170 Chipset LPC/eSPI Controller
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a14f Sunrise Point-H LPC Controller
a150 CM236 Chipset LPC/eSPI Controller
a151 Sunrise Point-H LPC Controller
a15f Sunrise Point-H LPC Controller
a160 100 Series/C230 Series Chipset Family Serial IO I2C Controller #0
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a161 100 Series/C230 Series Chipset Family Serial IO I2C Controller #1
1028 06e4 XPS 15 9550
a162 100 Series/C230 Series Chipset Family Serial IO I2C Controller #2
a16a 100 Series/C230 Series Chipset Family PCI Express Root Port #20
a170 100 Series/C230 Series Chipset Family HD Audio Controller
1028 06e4 XPS 15 9550
+ 103c 825b OMEN-17-w001nv
a171 CM238 HD Audio Controller
a182 C620 Series Chipset Family SATA Controller [AHCI mode]
a186 C620 Series Chipset Family SATA Controller [RAID mode]
a30c QM370 Chipset LPC/eSPI Controller
a323 Cannon Lake PCH SMBus Controller
a324 Cannon Lake PCH SPI Controller
+ a328 Cannon Lake PCH Serial IO UART Host Controller
a32c Cannon Lake PCH PCI Express Root Port #21
a32d Cannon Lake PCH PCI Express Root Port #22
a32e Cannon Lake PCH PCI Express Root Port #23
a353 Cannon Lake Mobile PCH SATA AHCI Controller
a360 Cannon Lake PCH HECI Controller
a363 Cannon Lake PCH Active Management Technology - SOL
+ a364 Cannon Lake PCH HECI Controller #2
a368 Cannon Lake PCH Serial IO I2C Controller #0
a369 Cannon Lake PCH Serial IO I2C Controller #1
a36a Cannon Lake PCH Serial IO I2C Controller #2
d158 Core Processor Miscellaneous Registers
f1a5 SSD 600P Series
f1a6 SSD Pro 7600p/760p/E 6100p Series
+ 8086 390b Intel Corporation SSD Pro 7600p/760p/E 6100p Series [NVM Express]
f1a8 SSD 660P Series
8088 Beijing Wangxun Technology Co., Ltd.
+ 0101 WX1860A2 Gigabit Ethernet Controller
+ 8088 0201 Dual-Port Ethernet Network Adaptor SF200T
+ 0102 WX1860A2S Gigabit Ethernet Controller
+ 8088 0210 Dual-Port Ethernet Network Adaptor SF200T-S
+ 0103 WX1860A4 Gigabit Ethernet Controller
+ 8088 0401 Qual-Port Ethernet Network Adaptor SF400T
+ 8088 0440 Qual-Port Ethernet Network Adaptor SF400-OCP
+ 0104 WX1860A4S Gigabit Ethernet Controller
+ 8088 0410 Qual-Port Ethernet Network Adaptor SF400T-S
+ 0105 WX1860AL2 Gigabit Ethernet Controller
+ 8088 0202 Dual-Port Ethernet Network Adaptor SF200HT
+ 0106 WX1860AL2S Gigabit Ethernet Controller
+ 8088 0220 Dual-Port Ethernet Network Adaptor SF200HT-S
+ 0107 WX1860AL4 Gigabit Ethernet Controller
+ 8088 0402 Qual-Port Ethernet Network Adaptor SF400HT
+ 0108 WX1860AL4S Gigabit Ethernet Controller
+ 8088 0420 Qual-Port Ethernet Network Adaptor SF400HT-S
1001 Ethernet Controller RP1000 for 10GbE SFP+
8088 0000 Ethernet Network Adaptor RP1000 for 10GbE SFP+
2001 Ethernet Controller RP2000 for 10GbE SFP+
8010 Wildcard A4B 4-port analog card (PCI-Express)
8013 Wildcard TE236/TE436 quad-span T1/E1/J1 card
b410 Wildcard B410 quad-BRI card
+d209 Ultimarc
+ 1500 PAC Drive
+ 15a2 SpinTrak
+ 1601 AimTrak
d4d4 Dy4 Systems Inc
0601 PCI Mezzanine Card
d531 I+ME ACTIA GmbH
c0ff Kona/Xena 2
cafe Kona SD
cfee Xena LS/SD-22-DA/SD-DA
+ dafe Corvid 1
daff KONA LHi
+ db00 IoExpress
db01 Corvid22
+ db02 Kona 3G
+ db03 Corvid 3G
+ db04 Kona 3G QUAD
+ db05 Kona LHe+
+ db06 IoXT
+ db07 Kona 3G P2P
+ db08 Kona 3G QUAD P2P
db09 Corvid 24
+ db11 T-Tap
dcaf Kona HD
dfee Xena HD-DA
+ eb07 Io4K
+ eb0a Io4K UFC
+ eb0b Kona 4
+ eb0c Kona 4 UFC
eb0d Corvid 88
eb0e Corvid 44
- eb1d Kona 5
+ eb16 Corvid HEVC
+ 10cf 1049 Corvid HEVC M31
+ eb18 Corvid HB-R
+ eb1a Kona IP 1SFP
+ eb1c Kona IP 2SFP
+ eb1d Io4KPlus
+ eb1e IoIP
+ eb1f Kona 5
+ eb23 Kona 1
+ eb24 Kona HDMI
+ eb25 Corvid 44 12g
efac Xena SD-MM/SD-22-MM
facd Xena HD-MM
f5f5 F5 Networks, Inc.
# The latest version can be obtained from
# http://www.linux-usb.org/usb.ids
#
-# Version: 2019.11.05
-# Date: 2019-11-05 20:34:06
+# Version: 2020.02.28
+# Date: 2020-02-28 20:34:06
#
# Vendors, devices and interfaces. Please keep sorted.
a001 Digitus DA-71114 SATA
0085 Boeye Technology Co., Ltd.
0600 eBook Reader
+0102 miniSTREAK
0105 Trust International B.V.
145f NW-3100 802.11b/g 54Mbps Wireless Network Adapter [zd1211]
0127 IBP
7617 AT76C505AS Wireless Adapter
7800 Mini Album
800c Airspy HF+
+ ff01 WootingOne
ff02 WootingTwo
ff07 Tux Droid fish dongle
03ec Iwatsu America, Inc.
c102 PhotoSmart 8000 series
c111 Deskjet 1510
c202 PhotoSmart 8200 series
+ c211 Deskjet 2540 series
c302 DeskJet D2300
c402 PhotoSmart D5100 series
c502 PhotoSmart D6100 series
8370 7 Port Hub
8371 PS/2 Keyboard And Mouse
8372 FT8U100AX Serial Port
+ 8508 Selectronic SP PRO
87d0 Cressi Dive Computer Interface
8a28 Rainforest Automation ZigBee Controller
8a98 TIAO Multi-Protocol Adapter
9135 Rotary Pub alarm
9136 Pulsecounter
9e90 Marvell OpenRD Base/Client
+ 9f08 CIB-1894 Conclusion SmartLink Box:
9f80 Ewert Energy Systems CANdapter
a6d0 Texas Instruments XDS100v2 JTAG / BeagleBone A3
a951 HCP HIT GSM/GPRS modem [Cinterion MC55i]
602a i900
040b Weltrend Semiconductor
0a68 Func MS-3 gaming mouse [WT6573F MCU]
+ 2000 wired Keyboard [Dynex DX-WRK1401]
2367 Human Interface Device [HP CalcPad 200 Calculator and Numeric Keypad]
6510 Weltrend Bar Code Reader
6520 Xploder Xbox Memory Unit (8MB)
b700 Tacticalboard
0450 DFI, Inc.
0451 Texas Instruments, Inc.
+ 0422 TUSB422 Port Controller with Power Delivery
1234 Bluetooth Device
1428 Hub
1446 TUSB2040/2070 Hub
+ 16a2 CC Debugger
16a6 BM-USBD1 BlueRobin RF heart rate sensor receiver
+ 16a8 CC2531 ZigBee
+ 16ae CC2531 Dongle
2036 TUSB2036 Hub
2046 TUSB2046 Hub
2077 TUSB2077 Hub
e012 TI-Nspire Calculator
e013 Network Bridge
e01c Data Collection Sled [Nspire Lab Cradle, Nspire Datatracker Cradle]
+ e01e Nspire\99 CX Navigator\99 Access Point
e01f Python Adapter (firmware install mode)
e020 Python Adapter
e022 Nspire CX II
0002 Genius NetMouse Pro
0003 Genius NetScroll+
0006 Easy Mouse+
+ 0007 Trackbar Emotion
000b NetMouse Wheel(P+U)
000c TACOMA Fingerprint V1.06.01
000e Genius NetScroll Optical
00cb Basic Optical Mouse v2.0
00ce Generic PPC Flash device
00d1 Optical Mouse with Tilt Wheel
+ 00d2 Notebook Optical Mouse with Tilt Wheel
00da eHome Infrared Receiver
00db Natural Ergonomic Keyboard 4000 V1.0
00dd Comfort Curve Keyboard 2000 V1.0
07cd Surface Keyboard
07f8 Wired Keyboard 600 (model 1576)
07fd Nano Transceiver 1.1
+ 0810 LifeCam HD-3000
0900 Surface Dock Hub
0901 Surface Dock Hub
0902 Surface Dock Hub
4d62 HP Laser Mobile Mini Mouse
4d75 Rocketfish RF-FLBTAD Bluetooth Adapter
4d81 Dell N889 Optical Mouse
+ 4d8a HP Multimedia Keyboard
4d91 Laser mouse M-D16DL
4d92 Optical mouse M-D17DR
4db1 Dell Laptop Integrated Webcam 2Mpix
4de3 HP 5-Button Optical Comfort Mouse
4de7 webcam
4e04 Lenovo Keyboard KB1021
+ 4e6f Acer Wired Keyboard Model KBAY211
0463 MGE UPS Systems
0001 UPS
ffff UPS
00a1 SmartCard Reader Keyboard KC 1000 SC
0106 R-300 Wireless Mouse Receiver
010d MX-Board 3.0 Keyboard
+ 0180 Strait 3.0
b090 Keyboard
b091 Mouse
046b American Megatrends, Inc.
0a45 960 Headset
0a4d G430 Surround Sound Gaming Headset
0a5b G933 Wireless Headset Dongle
+ 0a5d G933 Headset Battery Charger
0a66 [G533 Wireless Headset Dongle]
0b02 C-UV35 [Bluetooth Mini-Receiver] (HID proxy mode)
8801 Video Camera
c231 G13 Virtual Mouse
c245 G400 Optical Mouse
c246 Gaming Mouse G300
+ c247 G100S Optical Gaming Mouse
c248 G105 Gaming Keyboard
c24a G600 Gaming Mouse
c24c G400s Optical Mouse
c31f Comfort Keyboard K290
c326 Washable Keyboard K310
c328 Corded Keyboard K280e
+ c32b G910 Orion Spark Mechanical Keyboard
c332 G502 Proteus Spectrum Optical Mouse
c335 G910 Orion Spectrum Mechanical Keyboard
c33a G413 Gaming Keyboard
c531 C-U0007 [Unifying Receiver]
c532 Unifying Receiver
c534 Unifying Receiver
+ c537 Cordless Mouse Receiver
+ c53a PowerPlay Wireless Charging System
c603 3Dconnexion Spacemouse Plus XT
c605 3Dconnexion CADman
c606 3Dconnexion Spacemouse Classic
f101 Atlas Modem
047f Plantronics, Inc.
0101 Bulk Driver
+ 02ee BT600
0301 Bulk Driver
0411 Savi Office Base Station
0ca1 USB DSP v4 Audio Interface
af01 DA80
c008 Audio 655 DSP
c00e Blackwire C310 headset
+ c03b HD1
0480 Toshiba America Inc
0001 InTouch Module
0004 InTouch Module
0200 External Disk
0820 Canvio Advance Disk
0821 Canvio Advance 2TB model DTC920
+ 0900 MQ04UBF100
a006 External Disk 1.5TB
a007 External Disk USB 3.0
a009 Stor.E Basics
8259 Probe
91d1 Sensor Hub
a171 ThermaData WiFi
+ a2e0 BMeasure instrument
df11 STM Device in DFU Mode
ff10 Swann ST56 Modem
0484 Specialix
048d Integrated Technology Express, Inc.
1165 IT1165 Flash Controller
1172 Flash Drive
+ 1234 Mass storage
1336 SD/MMC Cardreader
1345 Multi Cardreader
9006 IT9135 BDA Afatech DVB-T HDTV Dongle
10c9 PIXMA iP4600 Printer
10ca PIXMA iP3600 Printer
10e3 PIXMA iX6850 Printer
+ 12fe Printer in service mode
1404 W6400PG
1405 W8400PG
150f BIJ2350 PCL
178a PIXMA MG3600 Series
178d PIXMA MG6853
180b PIXMA MG3000 series
+ 1856 PIXMA TS6250
1900 CanoScan LiDE 90
1901 CanoScan 8800F
1904 CanoScan LiDE 100
2633 LASERCLASS 500
2634 PC-D300/FAX-L400/ICD300
2635 MPC190
+ 2636 LBP3200
2637 iR C6800
2638 iR C3100
263c PIXMA MP360
264f MF5650 (FAX)
2650 iR 6800C EUR
2651 iR 3100C EUR
+ 2654 LBP3600
2655 FP-L170/MF350/L380/L398
2656 iR1510-1670 CAPT Printer
+ 2657 LBP3210
2659 MF8100
265b CAPT Printer
265c iR C3220
2666 iR C5800
2667 iR85PLUS
2669 iR105PLUS
- 266a CAPT Device
+ 266a LBP3000
266b iR8070
266c iR9070
266d iR 5800C EUR
2676 LBP2900
2677 iR C2570
2678 iR 2570C EUR
- 2679 CAPT Device
+ 2679 LBP5000
267a iR2016
267b iR2020
267d MF7100 series
+ 267e LBP3300
2684 MF3200 series
2686 MF6500 series
2687 iR4530
2688 LBP3460
2689 FAX-L180/L380S/L398S
268a LC310/L390/L408S
+ 268b LBP3500
268c iR C6870
268d iR 6870C EUR
268e iR C5870
268f iR 5870C EUR
2691 iR7105
+ 26a1 LBP5300
26a3 MF4100 series
+ 26a4 LBP5100
26b0 MF4600 series
26b4 MF4010 series
26b5 MF4200 series
26b6 FAX-L140/L130
- 26da LBP3010B printer
+ 26b9 LBP3310
+ 26ba LBP5050
+ 26da LBP3010/LBP3018/LBP3050
+ 26db LBP3100/LBP3108/LBP3150
26e6 iR1024
+ 26ea LBP9100C
+ 26ee MF4320-4350
+ 26f1 LBP7200C
+ 26ff LBP6300
271a LBP6000
+ 271b LBP6200
+ 271c LBP7010C/7018C
2736 I-SENSYS MF4550d
2737 MF4410
+ 2771 LBP6020
+ 2796 LBP6230/6240
3041 PowerShot S10
3042 CanoScan FS4000US Film Scanner
3043 PowerShot S20
0429 D5100
042a D800 (ptp)
0430 D7100
+ 0436 D810
043f D5600
0f03 PD-10 Wireless Printer Adapter
4000 Coolscan LS 40 ED
4611 Storage Adapter FX2 (CY)
4616 Flash Disk (TPP)
4624 DS-Xtreme Flash Card
+ 4717 West Bridge
5201 Combi Keyboard-Hub (Hub)
5202 Combi Keyboard-Hub (Keyboard)
5500 HID->COM RS232 Adapter
09c1 Arris Interactive LLC
1337 TOUCHSTONE DEVICE
09c2 Nisca Corp.
-09c3 ActivCard, Inc.
+09c3 HID Global
0007 Reader V2
0008 ZFG-9800-AC SmartCard Reader
0014 ActivIdentity ActivKey SIM USB Token
+ 0028 Crescendo Key
+ 0029 Crescendo Key
+ 002a Crescendo Key
+ 002b Crescendo Key
+ 002c Crescendo Key
+ 002e Crescendo Key
09c4 ACTiSYS Corp.
0011 ACT-IR2000U IrDA Dongle
09c5 Memory Corp.
6323 USB Electronic Scale
2237 Kobo Inc.
4161 eReader White
+224f APDM
+ 0001 Access Point
+ 0002 Docking Station
+ 0004 V2 Opal ACM
+ 0005 V2 Opal
+ 0006 V2 Docking Station
+ 0007 V2 Access Point ACM
+ 0008 V2 Access Point
225d Morpho
0001 FINGER VP Multimodal Biometric Sensor
0008 CBM-E3 Fingerprint Sensor
2548 Pulse-Eight
1001 CEC Adapter
1002 CEC Adapter
+25b5 FlatFrog
+ 0002 Multitouch 3200
2632 TwinMOS
3209 7-in-1 Card Reader
2639 Xsens
so no filesystems can be mounted before the check is complete.
When the root device becomes available,
- <filename>initd-root-device.target</filename> is reached.
+ <filename>initrd-root-device.target</filename> is reached.
If the root device can be mounted at
<filename>/sysroot</filename>, the
<filename>sysroot.mount</filename> unit becomes active and
<refsect1>
<title>Description</title>
- <para>The <filename>environment.d</filename> directories contain a list of "global" environment
- variable assignments for the user environment.
+ <para>The <filename>environment.d</filename> directories contain a list of environment variable
+ assignments for services started by the systemd user instance.
<citerefentry><refentrytitle>systemd-environment-d-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- parses them and updates the environment exported by the systemd user instance to the services it
- starts.</para>
+ parses them and updates the environment exported by the systemd user instance. See below for an
+ discussion of which processes inherit those variables.</para>
<para>It is recommended to use numerical prefixes for file names to simplify ordering.</para>
</refsect2>
</refsect1>
+ <refsect1>
+ <title>Applicability</title>
+
+ <para>Environment variables exported by the user manager (<command>systemd --user</command> instance
+ started in the <filename>user@<replaceable>uid</replaceable>.service</filename> system service) apply to
+ any services started by that manager. In particular, this may include services which run user shells. For
+ example in the Gnome environment, the graphical terminal emulator runs as the
+ <filename>gnome-terminal-server.service</filename> user unit, which in turn runs the user shell, so that
+ shell will inherit environment variables exported by the user manager. For other instances of the shell,
+ not launched by the user manager, the environment they inherit is defined by the program that starts
+ them. Hint: in general,
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ units contain programs launched by systemd, and
+ <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ units contain programs launched by something else.</para>
+
+ <para>Specifically, for ssh logins, the
+ <citerefentry project='die-net'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ service builds an environment that is a combination of variables forwarded from the remote system and
+ defined by <command>sshd</command>, see the discussion in
+ <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ A graphical display session will have an analogous mechanism to define the environment. Note that some
+ managers query the systemd user instance for the exported environment and inject this configuration into
+ programs they start, using <command>systemctl show-environment</command> or the underlying D-Bus call.
+ </para>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para>
<refsect1>
<title>Description</title>
- <para><command>halt</command>, <command>poweroff</command>,
- <command>reboot</command> may be used to halt, power-off or reboot
- the machine.</para>
+ <para><command>halt</command>, <command>poweroff</command>, <command>reboot</command> may be used to
+ halt, power-off, or reboot the machine. All three commands take the same options.</para>
</refsect1>
<refsect1>
<title>Notes</title>
- <para>These commands are implemented in a way that preserves compatibility with
- the original SysV commands.
- <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- verbs <command>halt</command>, <command>poweroff</command>,
- <command>reboot</command> provide the same functionality with some additional
- features.</para>
+ <para>These commands are implemented in a way that preserves basic compatibility with the original SysV
+ commands. <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ verbs <command>halt</command>, <command>poweroff</command>, <command>reboot</command> provide the same
+ functionality with some additional features.</para>
+
+ <para>Note that on many SysV systems <command>halt</command> used to be synonymous to
+ <command>poweroff</command>, i.e. both commands would equally result in powering the machine off. systemd
+ is more accurate here, and <command>halt</command> results in halting the machine only (leaving power
+ on), and <command>poweroff</command> is required to actually power it off.</para>
</refsect1>
<refsect1>
--- /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="homectl" conditional='ENABLE_HOMED'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>homectl</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>homectl</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>homectl</refname>
+ <refpurpose>Create, remove, change or inspect home directories</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>homectl</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="req">COMMAND</arg>
+ <arg choice="opt" rep="repeat">NAME</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>homectl</command> may be used to create, remove, change or inspect a user's home
+ directory. It's primarily a command interfacing with
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ which manages home directories of users.</para>
+
+ <para>Home directories managed by <filename>systemd-homed.service</filename> are self-contained, and thus
+ include the user's full metadata record in the home's data storage itself, making them easy to migrate
+ between machines. In particular, a home directory describes a matching user record, and every user record
+ managed by <filename>systemd-homed.service</filename> also implies existence and encapsulation of a home
+ directory. The user account and home directory become the same concept.</para>
+
+ <para>The following backing storage mechanisms are supported:</para>
+
+ <itemizedlist>
+ <listitem><para>An individual LUKS2 encrypted loopback file for a user, stored in
+ <filename>/home/*.home</filename>. At login the file system contained in this files is mounted, after
+ the LUKS2 encrypted volume has been attached. The user's password is identical to the encryption
+ passphrase of the LUKS2 volume. Access to data without preceeding user authentication is thus not
+ possible, even for the system administrator. This storage mechanism provides the strongest data
+ security and is thus recommended.</para></listitem>
+
+ <listitem><para>Similar, but the LUKS2 encrypted file system is located on regular block device, such
+ as an USB storage stick. In this mode home directories and all data they include are nicely migratable
+ between machines, simply by plugging the USB stick into different systems at different
+ times.</para></listitem>
+
+ <listitem><para>An encrypted directory using <literal>fscrypt</literal> on file systems that support it
+ (at the moment this is primarily <literal>ext4</literal>), located in
+ <filename>/home/*.homedir</filename>. This mechanism also provides encryption, but substantially
+ weaker than LUKS2, as most file system metadata is unprotected. Moreover
+ it currently does not support changing user passwords once the home directory has been
+ created.</para></listitem>
+
+ <listitem><para>A <literal>btrfs</literal> subvolume for each user, also located in
+ <filename>/home/*.homedir</filename>. This provides no encryption, but good quota
+ support.</para></listitem>
+
+ <listitem><para>A regular directory for each user, also located in
+ <filename>/home/*.homedir</filename>. This provides no encryption, but is a suitable fallback
+ available on all machines, even where LUKS2, <literal>fscrypt</literal> or <literal>btrfs</literal>
+ support is not available.</para></listitem>
+
+ <listitem><para>An individual Windows file share (CIFS) for each user.</para></listitem>
+ </itemizedlist>
+
+ <para>Note that <filename>systemd-homed.service</filename> and <command>homectl</command> will not manage
+ "classic" UNIX user accounts as created with <citerefentry
+ project='man-pages'><refentrytitle>useradd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
+ similar tools. In particular, this functionality is not suitable for managing system users (i.e. users
+ with a UID below 1000) but is exclusive to regular ("human") users.</para>
+
+ <para>Note that users/home directories managed via <command>systemd-homed.service</command> do not show
+ up in <filename>/etc/passwd</filename> and similar files, they are synthesized via glibc NSS during
+ runtime. They are thus resolvable and may be enumerated via the <citerefentry
+ project='man-pages'><refentrytitle>getent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ tool.</para>
+
+ <para>This tool interfaces directly with <filename>systemd-homed.service</filename>, and may execute
+ specific commands on the home directories it manages. Since every home directory managed that way also
+ defines a JSON user and group record these home directories may also be inspected and enumerated via
+ <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+ <para>Home directories managed by <filename>systemd-homed.service</filename> are usually in one of two
+ states, or in a transition state between them: when <literal>active</literal> they are unlocked and
+ mounted, and thus accessible to the system and its programs; when <literal>inactive</literal> they are
+ not mounted and thus not accessible. Activation happens automatically at login of the user and usually
+ can only complete after a password (or other authentication token) has been supplied. Deactivation
+ happens after the user fully logged out. A home directory remains active as long as the user is logged in
+ at least once, i.e. has at least one login session. When the user logs in a second time simultaneously
+ the home directory remains active. It is deactivated only after the last of the user's sessions
+ ends.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following general options are understood (further options that control the various properties
+ of user records managed by <filename>systemd-homed.service</filename> are documented further
+ down):</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><option>--identity=</option><replaceable>FILE</replaceable></term>
+
+ <listitem><para>Read the user's JSON record from the specified file. If passed as
+ <literal>-</literal> reads the user record from standard input. The supplied JSON object must follow
+ the structure documented on <ulink url="https://systemd.io/USER_RECORDS">JSON User
+ Records</ulink>. This option may be used in conjunction with the <command>create</command> and
+ <command>update</command> commands (see below), where it allows configuring the user record in JSON
+ as-is, instead of setting the individual user record properties (see below).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--json=</option><replaceable>FORMAT</replaceable></term>
+ <term><option>-J</option></term>
+
+ <listitem><para>Controls whether to output the user record in JSON format, if the
+ <command>inspect</command> command (see below) is used. Takes one of <literal>pretty</literal>,
+ <literal>short</literal> or <literal>off</literal>. If <literal>pretty</literal> human-friendly
+ whitespace and newlines are inserted in the output to make the JSON data more readable. If
+ <literal>short</literal> all superfluous whitespace is suppressed. If <literal>off</literal> (the
+ default) the user information is not shown in JSON format but in a friendly human readable formatting
+ instead. The <option>-J</option> option picks <literal>pretty</literal> when run interactively and
+ <literal>short</literal> otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--export-format=</option><replaceable>FORMAT</replaceable></term>
+ <term><option>-E</option></term>
+ <term><option>-EE</option></term>
+
+ <listitem><para>When used with the <command>inspect</command> verb in JSON mode (see above) may be
+ used to suppress certain aspects of the JSON user record on output. Specifically, if
+ <literal>stripped</literal> format is used the binding and runtime fields of the record are
+ removed. If <literal>minimal</literal> format is used the cryptographic signature is removed too. If
+ <literal>full</literal> format is used the full JSON record is shown (this is the default). This
+ option is useful for copying an existing user record to a different system in order to create a
+ similar user there with the same settings. Specifically: <command>homectl inspect -EE | ssh
+ root@othersystem homectl create -i-</command> may be used as simple command line for replicating a
+ user on another host. <option>-E</option> is equivalent to <option>-j --export-format=stripped</option>,
+ <option>-EE</option> to <option>-j --export-format=minimal</option>. Note that when replicating user
+ accounts user records acquired in <literal>stripped</literal> mode will retain the original
+ cryptographic signatures and thus may only be modified when the private key to update them is available
+ on the destination machine. When replicating users in <literal>minimal</literal> mode, the signature
+ is removed during the replication and thus the record will be implicitly signed with the key of the destination
+ machine and may be updated there without any private key replication.</para></listitem>
+ </varlistentry>
+
+ <xi:include href="user-system-options.xml" xpointer="host" />
+ <xi:include href="user-system-options.xml" xpointer="machine" />
+
+ <xi:include href="standard-options.xml" xpointer="no-pager" />
+ <xi:include href="standard-options.xml" xpointer="no-legend" />
+ <xi:include href="standard-options.xml" xpointer="no-ask-password" />
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>User Record Properties</title>
+
+ <para>The following options control various properties of the user records/home directories that
+ <filename>systemd-homed.service</filename> manages. These switches may be used in conjunction with the
+ <command>create</command> and <command>update</command> commands for configuring various aspects of the
+ home directory and the user account:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><option>--real-name=</option><replaceable>NAME</replaceable></term>
+ <term><option>-c</option> <replaceable>NAME</replaceable></term>
+
+ <listitem><para>The real name for the user. This corresponds with the GECOS field on classic UNIX NSS
+ records.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--realm=</option><replaceable>REALM</replaceable></term>
+
+ <listitem><para>The realm for the user. The realm associates a user with a specific organization or
+ installation, and allows distuingishing users of the same name defined in different contexts. The
+ realm can be any string that also qualifies as valid DNS domain name, and it is recommended to use
+ the organization's or installation's domain name for this purpose, but this is not enforced nor
+ required. On each system only a single user of the same name may exist, and if a user with the same
+ name and realm is seen it is assumed to refer to the same user while a user with the same name but
+ different realm is considered a different user. Note that this means that two users sharing the same
+ name but with distinct realms are not allowed on the same system. Assigning a realm to a user is
+ optional.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--email-address=</option><replaceable>EMAIL</replaceable></term>
+
+ <listitem><para>Takes an electronic mail address to associate with the user. On log-in the
+ <varname>$EMAIL</varname> environment variable is initialized from this value.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--location=</option><replaceable>TEXT</replaceable></term>
+
+ <listitem><para>Takes location specification for this user. This is free-form text, which might or
+ might not be usable by geo-location applications. Example: <option>--location="Berlin,
+ Germany"</option> or <option>--location="Basement, Room 3a"</option></para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--icon-name=</option><replaceable>ICON</replaceable></term>
+
+ <listitem><para>Takes an icon name to associate with the user, following the scheme defined by the <ulink
+ url="https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html">Icon Naming
+ Specification</ulink>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--home-dir=</option><replaceable>PATH</replaceable></term>
+ <term><option>-d</option><replaceable>PATH</replaceable></term>
+
+ <listitem><para>Takes a path to use as home directory for the user. Note that this is the directory
+ the user's home directory is mounted to while the user is logged in. This is not where the user's
+ data is actually stored, see <option>--image-path=</option> for that. If not specified defaults to
+ <filename>/home/$USER</filename>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--uid=</option><replaceable>UID</replaceable></term>
+
+ <listitem><para>Takes a preferred numeric UNIX UID to assign this user. If a user is to be created
+ with the specified UID and it is already taken by a different user on the local system then creation
+ of the home directory is refused. Note though, if after creating the home directory it is used on a
+ different system and the configured UID is taken by another user there, then
+ <command>systemd-homed</command> may assign the user a different UID on that system. The specified
+ UID must be outside of the system user range. It is recommended to use the 60001…60513 UID range for
+ this purpose. If not specified the UID is automatically picked. When logging in and the home
+ directory is found to be owned by a UID not matching the user's assigned one the home directory and
+ all files and directories inside it will have their ownership changed automatically before login
+ completes.</para>
+
+ <para>Note that users managed by <command>systemd-homed</command> always have a matching group
+ associated with the same name as well as a GID matching the UID of the user. Thus, configuring the
+ GID separately is not permitted.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--member-of=</option><replaceable>GROUP</replaceable></term>
+ <term><option>-G</option> <replaceable>GROUP</replaceable></term>
+
+ <listitem><para>Takes a comma-separated list of auxiliary UNIX groups this user shall belong
+ to. Example: <option>--member-of=wheel</option> to provide the user with administrator
+ privileges. Note that <command>systemd-homed</command> does not manage any groups besides a group
+ matching the user in name and numeric UID/GID. Thus any groups listed here must be registered
+ independently, for example with <citerefentry
+ project='man-pages'><refentrytitle>groupadd</refentrytitle><manvolnum>8</manvolnum></citerefentry>. If
+ non-existant groups that are listed there are ignored. This option may be used more than once, in
+ which case all specified group lists are combined.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--skel=</option><replaceable>PATH</replaceable></term>
+
+ <listitem><para>Takes a file system path to a directory. Specifies the skeleton directory to
+ initialize the home directory with. All files and directories in the specified are copied into any
+ newly create home directory. If not specified defaults to
+ <filename>/etc/skel/</filename>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--shell=</option><replaceable>SHELL</replaceable></term>
+
+ <listitem><para>Takes a file system path. Specifies the shell binary to execute on terminal
+ logins. If not specified defaults to <filename>/bin/bash</filename>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>=<replaceable>VALUE</replaceable></term>
+
+ <listitem><para>Takes an environment variable assignment to set for all user processes. Note that a
+ number of other settings also result in environment variables to be set for the user, including
+ <option>--email=</option>, <option>--timezone=</option> and <option>--language=</option>. May be used
+ multiple times to set multiple environment variables.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--timezone=</option><replaceable>TIMEZONE</replaceable></term>
+
+ <listitem><para>Takes a timezone specification as string that sets the timezone for the specified
+ user. Expects a `tzdata` location string. When the user logs in the <varname>$TZ</varname>
+ environment variable is initialized from this setting. Example:
+ <option>--timezone=Europe/Amsterdam</option> will result in the environment variable
+ <literal>TZ=:Europe/Amsterdam</literal>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--language=</option><replaceable>LANG</replaceable></term>
+
+ <listitem><para>Takes a specifier indicating the preferred language of the user. The
+ <varname>$LANG</varname> environment variable is initialized from this value on login, and thus a
+ value suitable for this environment variable is accepted here, for example
+ <option>--language=de_DE.UTF8</option></para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--ssh-authorized-keys=</option><replaceable>KEYS</replaceable></term>
+ <listitem><para>Either takes a SSH authorized key line to associate with the user record or a
+ <literal>@</literal> character followed by a path to a file to read one or more such lines from. SSH
+ keys configured this way are made available to SSH to permit access to this home directory and user
+ record. This option may be used more than once to configure multiple SSH keys.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
+ <listitem><para>Takes an RFC 7512 PKCS#11 URI referencing a security token (e.g. YubiKey or PIV
+ smartcard) that shall be able to unlock the user account. The security token URI should reference a
+ security token with exactly one pair of X.509 certificate and private key. A random secret key is
+ then generated, encrypted with the public key of the X.509 certificate, and stored as part of the
+ user record. At login time it is decrypted with the PKCS#11 module and then used to unlock the
+ account and associated resources. See below for an example how to set up authentication with security
+ token.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--locked=</option><replaceable>BOOLEAN</replaceable></term>
+
+ <listitem><para>Takes a boolean argument. Specifies whether this user account shall be locked. If
+ true logins into this account are prohibited, if false (the default) they are permitted (of course,
+ only if authorization otherwise succeeds).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--not-before=</option><replaceable>TIMESTAMP</replaceable></term>
+ <term><option>--not-after=</option><replaceable>TIMESTAMP</replaceable></term>
+
+ <listitem><para>These options take a timestamp string, in the format documented in
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
+ configures points in time before and after logins into this account are not
+ permitted.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--rate-limit-interval=</option><replaceable>SECS</replaceable></term>
+ <term><option>--rate-limit-burst=</option><replaceable>NUMBER</replaceable></term>
+
+ <listitem><para>Configures a rate limit on authentication attempts for this user. If the user
+ attempts to authenticate more often than the specified number, on a specific system, within the
+ specified time interval authentication is refused until the time interval passes. Defaults to 10
+ times per 1min.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--password-hint=</option><replaceable>TEXT</replaceable></term>
+
+ <listitem><para>Takes a password hint to store alongside the user record. This string is stored
+ accessible only to privileged users and the user itself and may not be queried by other users.
+ Example: <option>--password-hint="My first pet's name"</option></para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--enforce-password-policy=</option><replaceable>BOOL</replaceable></term>
+ <term><option>-P</option></term>
+
+ <listitem><para>Takes a boolean argument. Configures whether to enforce the system's password policy
+ for this user, regarding quality and strength of selected passwords. Defaults to
+ on. <option>-P</option> is short for
+ <option>---enforce-password-policy=no</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--password-change-now=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Takes a boolean argument. If true the user is asked to change their password on next
+ login.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--password-change-min=</option><replaceable>TIME</replaceable></term>
+ <term><option>--password-change-max=</option><replaceable>TIME</replaceable></term>
+ <term><option>--password-change-warn=</option><replaceable>TIME</replaceable></term>
+ <term><option>--password-change-inactive=</option><replaceable>TIME</replaceable></term>
+
+ <listitem><para>Each of these options takes a time span specification as argument (in the syntax
+ documented in
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and
+ configure various aspects of the user's password expiration policy. Specifically,
+ <option>--password-change-min=</option> configures how much time has to pass after changing the
+ password of the user until the password may be changed again. If the user tries to change their
+ password before this time passes the attempt is refused. <option>--password-change-max=</option>
+ configures how much time has to pass after the the password is changed until the password expires and
+ needs to be changed again. After this time passes any attempts to log in may only proceed after the
+ password is changed. <option>--password-change-warn=</option> specifies how much earlier than then
+ the time configured with <option>--password-change-max=</option> the user is warned at login to
+ change their password as it will expire soon. Finally <option>--password-change-inactive=</option>
+ configures the time which has to pass after the password as expired until the user is not permitted
+ to log in or change the password anymore. Note that these options only apply to password
+ authentication, and do not apply to other forms of authentication, for example PKCS#11-based security
+ token authentication.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--disk-size=</option><replaceable>BYTES</replaceable></term>
+ <listitem><para>Either takes a size in bytes as argument (possibly using the usual K, M, G, …
+ suffixes for 1024 base values), or a percentage value and configures the disk space to assign to the
+ user. If a percentage value is specified (i.e. the argument suffixed with <literal>%</literal>) it is
+ taken relative to the available disk space of the backing file system. If the LUKS2 backend is used
+ this configures the size of the loopback file and file system contained therein. For the other
+ storage backends configures disk quota using the filesystem's native quota logic, if available. If
+ not specified, defaults to 85% of the available disk space for the LUKS2 backend and to no quota for
+ the others.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--access-mode=</option><replaceable>MODE</replaceable></term>
+
+ <listitem><para>Takes a UNIX file access mode written in octal. Configures the access mode of the
+ home directory itself. Note that this is only used when the directory is first created, and the user
+ may change this any time afterwards. Example:
+ <option>--access-mode=0700</option></para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--umask=</option><replaceable>MASK</replaceable></term>
+
+ <listitem><para>Takes the access mode mask (in octal syntax) to apply to newly created files and
+ directories of the user ("umask"). If set this controls the initial umask set for all login sessions of
+ the user, possibly overriding the system's defaults.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--nice=</option><replaceable>NICE</replaceable></term>
+
+ <listitem><para>Takes the numeric scheduling priority ("nice level") to apply to the processes of the user at login
+ time. Takes a numeric value in the range -20 (highest priority) to 19 (lowest priority).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--rlimit=</option><replaceable>LIMIT</replaceable>=<replaceable>VALUE</replaceable><optional>:<replaceable>VALUE</replaceable></optional></term>
+
+ <listitem><para>Allows configuration of resource limits for processes of this user, see <citerefentry
+ project='man-pages'><refentrytitle>getrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ for details. Takes a resource limit name (e.g. <literal>LIMIT_NOFILE</literal>) followed by an equal
+ sign, followed by a numeric limit. Optionally, separated by colon a second numeric limit may be
+ specified. If two are specified this refers to the soft and hard limits, respectively. If only one
+ limit is specified the setting sets both limits in one.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--tasks-max=</option><replaceable>TASKS</replaceable></term>
+
+ <listitem><para>Takes a non-zero unsigned integer as argument. Configures the maximum numer of tasks
+ (i.e. processes and threads) the user may have at any given time. This limit applies to all tasks
+ forked off the user's sessions, even if they change user identity via <citerefentry
+ project='man-pages'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry> or a
+ similar tool. Use <option>--rlimit=LIMIT_NPROC=</option> to place a limit on the tasks actually
+ running under the UID of the user, thus excluding any child processes that might have changed user
+ identity. This controls the <varname>TasksMax=</varname> settting of the per-user systemd slice unit
+ <filename>user-$UID.slice</filename>. See
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for further details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--memory-high=</option><replaceable>BYTES</replaceable></term>
+ <term><option>--memory-max=</option><replaceable>BYTES</replaceable></term>
+
+ <listitem><para>Set a limit on the memory a user may take up on a system at any given time in bytes
+ (the usual K, M, G, … suffixes are supported, to the base of 1024). This includes all memory used by
+ the user itself and all processes they forked off that changed user credentials. This controls the
+ <varname>MemoryHigh=</varname> and <varname>MemoryMax=</varname> settings of the per-user systemd
+ slice unit <filename>user-$UID.slice</filename>. See
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for further details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--cpu-weight=</option><replaceable>WEIGHT</replaceable></term>
+ <term><option>--io-weight=</option><replaceable>WEIGHT</replaceable></term>
+
+ <listitem><para>Set a CPU and IO scheduling weights of the processes of the user, including those of
+ processes forked off by the user that changed user credentials. Takes a numeric value in the range
+ 1…10000. This controls the <varname>CPUWeight=</varname> and <varname>IOWeight=</varname> settings of
+ the per-user systemd slice unit <filename>user-$UID.slice</filename>. See
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for further details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--storage=</option><replaceable>STORAGE</replaceable></term>
+
+ <listitem><para>Selects the storage mechanism to use for this home directory. Takes one of
+ <literal>luks</literal>, <literal>fscrypt</literal>, <literal>directory</literal>,
+ <literal>subvolume</literal>, <literal>cifs</literal>. For details about these mechanisms, see
+ above. If a new home directory is created and the storage type is not specifically specified defaults
+ to <literal>luks</literal> if supported, <literal>subvolume</literal> as first fallback if supported,
+ and <literal>directory</literal> if not.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--image-path=</option><replaceable>PATH</replaceable></term>
+
+ <listitem><para>Takes a file system path. Configures where to place the user's home directory. When
+ LUKS2 storage is used refers to the path to the loopback file, otherwise to the path to the home
+ directory. When unspecified defaults to <filename>/home/$USER.home</filename> when LUKS storage is
+ used and <filename>/home/$USER.homedir</filename> for the other storage mechanisms. Not defined for
+ the <literal>cifs</literal> storage mechanism. To use LUKS2 storage on a regular block device (for
+ example a USB stick) pass the path to the block device here.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>
+
+ <listitem><para>When LUKS2 storage is used configures the file system type to use inside the home
+ directory LUKS2 container. One of <literal>ext4</literal>, <literal>xfs</literal>,
+ <literal>btrfs</literal>. If not specified defaults to <literal>ext4</literal>. Note that
+ <literal>xfs</literal> is not recommended as its support for file system resizing is too
+ limited.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--luks-discard=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>When LUKS2 storage is used configures whether to enable the
+ <literal>discard</literal> feature of the file system. If enabled the file system on top of the LUKS2
+ volume will report empty block information to LUKS2 and the loopback file below, ensuring that empty
+ space in the home directory is returned to the backing file system below the LUKS2 volume, resulting
+ in a "sparse" loopback file. This option mostly defaults to off, since this permits over-committing
+ home directories which results in I/O errors if the underlying file system runs full while the upper
+ file system wants to allocate a block. Such I/O errors are generally not handled well by file systems
+ nor applications. When LUKS2 storage is used on top of regular block devices (instead of on top a
+ loopback file) the discard logic defaults to on.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
+ <term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
+ <term><option>--luks-volume-key-size=</option><replaceable>BITS</replaceable></term>
+ <term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term>
+ <term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term>
+ <term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
+ <term><option>--luks-pbkdf-memory-cost=</option><replaceable>BYTES</replaceable></term>
+ <term><option>--luks-pbkdf-parallel-threads=</option><replaceable>THREADS</replaceable></term>
+
+ <listitem><para>Configures various cryptographic parameters for the LUKS2 storage mechanism. See
+ <citerefentry
+ project='man-pages'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for details on the specific attributes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--nosuid=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--nodev=</option><replaceable>BOOL</replaceable></term>
+ <term><option>--noexec=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Configures the <literal>nosuid</literal>, <literal>nodev</literal> and
+ <literal>noexec</literal> mount options for the home directories. By default <literal>nodev</literal>
+ and <literal>nosuid</literal> are on, while <literal>noexec</literal> is off. For details about these
+ mount options see <citerefentry
+ project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--cifs-domain=</option><replaceable>DOMAIN</replaceable></term>
+ <term><option>--cifs-user-name=</option><replaceable>USER</replaceable></term>
+ <term><option>--cifs-service=</option><replaceable>SERVICE</replaceable></term>
+
+ <listitem><para>Configures the Windows File Sharing (CIFS) domain and user to associate with the home
+ directory/user account, as well as the file share ("service") to mount as directory. The latter is used when
+ <literal>cifs</literal> storage is selected.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--stop-delay=</option><replaceable>SECS</replaceable></term>
+
+ <listitem><para>Configures the time the per-user service manager shall continue to run after the all
+ sessions of the user ended. The default is configured in
+ <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> (for
+ home directories of LUKS2 storage located on removable media this defaults to 0 though). A longer
+ time makes sure quick, repetitive logins are more efficient as the user's service manager doesn't
+ have to be started every time.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--kill-processes=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Configures whether to kill all processes of the user on logout. The default is
+ configured in
+ <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--auto-login=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Takes a boolean argument. Configures whether the graphical UI of the system should
+ automatically log this user in if possible. Defaults to off. If less or more than one user is marked
+ this way automatic login is disabled.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Commands</title>
+
+ <para>The following commands are understood:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><command>list</command></term>
+
+ <listitem><para>List all home directories (along with brief details) currently managed by
+ <filename>systemd-homed.service</filename>. This command is also executed if none is specified on the
+ command line. (Note that the list of users shown by this command does not include users managed by
+ other subsystems, such as system users or any traditional users listed in
+ <filename>/etc/passwd</filename>.)</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>activate</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+ <listitem><para>Activate one or more home directories. The home directories of each listed user will
+ be activated and made available under their mount points (typically in
+ <filename>/home/$USER</filename>). Note that any home activated this way stays active indefinitely,
+ until it is explicitly deactivated again (with <command>deactivate</command>, see below), or the user
+ logs in and out again and it thus is deactivated due to the automatic deactivation-on-logout
+ logic.</para>
+
+ <para>Activation of a home directory involves various operations that depend on the selected storage
+ mechanism. If the LUKS2 mechanism is used, this generally involves: inquiring the user for a
+ password, setting up a loopback device, validating and activating the LUKS2 volume, checking the file
+ system, mounting the file system, and potentiatlly changing the ownership of all included files to
+ the correct UID/GID.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>deactivate</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+ <listitem><para>Deactivate one or more home directories. This undoes the effect of
+ <command>activate</command>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>inspect</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+ <listitem><para>Show various details about the specified home directories. This shows various
+ information about the home directory and its user account, including runtime data such as current
+ state, disk use and similar. Combine with <option>--json=</option> to show the detailed JSON user
+ record instead, possibly combined with <option>--export-format=</option> to suppress certain aspects
+ of the output.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>authenticate</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+ <listitem><para>Validate authentication credentials of a home directory. This queries the caller for
+ a password (or similar) and checks that it correctly unlocks the home directory. This leaves the home
+ directory in the state it is in, i.e. it leaves the home directory in inactive state if it was
+ inactive before, and in active state if it was active before.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>create</command> <replaceable>USER</replaceable></term>
+ <term><command>create</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+
+ <listitem><para>Create a new home directory/user account of the specified name. Use the various
+ user record property options (as documented above) to control various aspects of the home directory
+ and its user accounts.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>remove</command> <replaceable>USER</replaceable></term>
+
+ <listitem><para>Remove a home directory/user account. This will remove both the home directory's user
+ record and the home directory itself, and thus delete all files and directories owned by the
+ user.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>update</command> <replaceable>USER</replaceable></term>
+ <term><command>update</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+
+ <listitem><para>Update a home directory/user account. Use the various user record property options
+ (as documented above) to make changes to the account, or alternatively provide a full, updated JSON
+ user record via the <option>--identity=</option> option.</para>
+
+ <para>Note that changes to user records not signed by a cryptographic private key available locally
+ are not permitted, unless <option>--identity=</option> is used with a user record that is already
+ correctly signed by a recognized private key.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>passwd</command> <replaceable>USER</replaceable></term>
+
+ <listitem><para>Change the password of the specified home direcory/user account.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>resize</command> <replaceable>USER</replaceable> <replaceable>BYTES</replaceable></term>
+
+ <listitem><para>Change the disk space assigned to the specified home directory. If the LUKS2 storage
+ mechanism is used this will automatically resize the loopback file and the file system contained
+ within. Note that if <literal>ext4</literal> is used inside of the LUKS2 volume, it is necessary to
+ deactivate the home directory before shrinking it (i.e the user has to log out). Growing can be done
+ while the home directory is active. If <literal>xfs</literal> is used inside of the LUKS2 volume the
+ home directory may not be shrunk whatsoever. On all three of <literal>ext4</literal>,
+ <literal>xfs</literal> and <literal>btrfs</literal> the home directory may be grown while the user is
+ logged in, and on the latter also shrunk while the user is logged in. If the
+ <literal>subvolume</literal>, <literal>directory</literal>, <literal>fscrypt</literal> storage
+ mechanisms are used, resizing will change file system quota.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>lock</command> <replaceable>USER</replaceable></term>
+
+ <listitem><para>Temporarily suspend access to the user's home directory and remove any associated
+ cryptographic keys from memory. Any attempts to access the user's home directory will stall until the
+ home directory is unlocked again (i.e. re-authenticated). This functionality is primarily intended to
+ be used during system suspend to make sure the user's data cannot be accessed until the user
+ re-authenticates on resume. This operation is only defined for home directories that use the LUKS2
+ storage mechanism.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>unlock</command> <replaceable>USER</replaceable></term>
+
+ <listitem><para>Resume access to the user's home directory again, undoing the effect of
+ <command>lock</command> above. This requires authentication of the user, as the cryptographic keys
+ required for access to the home directory need to be reacquired.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>lock-all</command></term>
+
+ <listitem><para>Execute the <command>lock</command> command on all suitable home directories at
+ once. This operation is generally executed on system suspend (i.e. by <command>systemctl
+ suspend</command> and related commands), to ensure all active user's cryptographic keys for accessing
+ their home directories are removed from memory.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>with</command> <replaceable>USER</replaceable> <replaceable>COMMAND…</replaceable></term>
+
+ <listitem><para>Activate the specified user's home directory, run the specified command (under the
+ caller's identity, not the specified user's) and deactivate the home directory afterwards again
+ (unless the user is logged in otherwise). This command is useful for running privileged backup
+ scripts and such, but requires authentication with the user's credentials in order to be able to
+ unlock the user's home directory.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+ </refsect1>
+
+ <xi:include href="less-variables.xml" />
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Create a user <literal>waldo</literal> in the administrator group <literal>wheel</literal>, and
+ assign 500 MiB disk space to them.</title>
+
+ <programlisting>homectl create waldo --real-name="Waldo McWaldo" -G wheel --disk-size=500M</programlisting>
+ </example>
+
+ <example>
+ <title>Create a user <literal>wally</literal> on a USB stick, and assign a maximum of 500 concurrent
+ tasks to them.</title>
+
+ <programlisting>homectl create wally --real-name="Wally McWally" --image-path=/dev/disk/by-id/usb-SanDisk_Ultra_Fit_476fff954b2b5c44-0:0 --tasks-max=500</programlisting>
+ </example>
+
+ <example>
+ <title>Change nice level of user <literal>odlaw</literal> to +5 and make sure the environment variable
+ <varname>$SOME</varname> is set to the string <literal>THING</literal> for them on login.</title>
+
+ <programlisting>homectl update odlaw --nice=5 --setenv=SOME=THING</programlisting>
+ </example>
+
+ <example>
+ <title>Set up authentication with a YubiKey security token:</title>
+
+ <programlisting># Clear the Yubikey from any old keys (careful!)
+ykman piv reset
+
+# Generate a new private/public key pair on the device, store the public key in 'pubkey.pem'.
+ykman piv generate-key -a RSA2048 9d pubkey.pem
+
+# Create a self-signed certificate from this public key, and store it on the device.
+ykman piv generate-certificate --subject "Knobelei" 9d pubkey.pem
+
+# We don't need the publibc key on disk anymore
+rm pubkey.pem
+
+# Check if the newly create key on the Yubikey shows up as token in PKCS#11. Have a look at the output, and
+# copy the resulting token URI to the clipboard.
+p11tool --list-tokens
+
+# Allow the security token referenced by the determined PKCS#11 URI to unlock the account of user
+# 'lafcadio'. (Replace the '…' by the URI from the clipboard.)
+homectl update lafcadio --pkcs11-token-uri=…</programlisting>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>useradd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
priorities.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--facility=</option></term>
+
+ <listitem><para>Filter output by syslog facility. Takes a comma-separated list of numbers or facility
+ names. The names are the usual syslog facilities as documented in
+ <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ <option>--facility=help</option> may be used to display a list of known facility names and exit.
+ </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-g</option></term>
<term><option>--grep=</option></term>
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>
+
+ <listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
+ collected by the default namespace is shown. If specified shows the log data of the specified
+ namespace instead. If the namespace is specified as <literal>*</literal> data from all namespaces is
+ shown, interleaved. If the namespace identifier is prefixed with <literal>+</literal> data from the
+ specified namespace and the default namespace is shown, interleaved, but no other. For details about
+ journal namespaces see
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--header</option></term>
<refnamediv>
<refname>journald.conf</refname>
<refname>journald.conf.d</refname>
+ <refname>journald@.conf</refname>
<refpurpose>Journal service configuration files</refpurpose>
</refnamediv>
<para><filename>/etc/systemd/journald.conf.d/*.conf</filename></para>
<para><filename>/run/systemd/journald.conf.d/*.conf</filename></para>
<para><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></para>
+ <para><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename></para>
</refsynopsisdiv>
<refsect1>
<citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for a general description of the syntax.</para>
+ <para>The <command>systemd-journald</command> instance managing the default namespace is configured by
+ <filename>/etc/systemd/journald.conf</filename> and associated drop-ins. Instances managing other
+ namespaces read <filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename> with
+ the namespace identifier filled in. This allows each namespace to carry a distinct configuration. See
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for details about journal namespaces.</para>
</refsect1>
<xi:include href="standard-conf.xml" xpointer="main-conf" />
<varlistentry>
<term><varname>Storage=</varname></term>
- <listitem><para>Controls where to store journal data. One of
- <literal>volatile</literal>,
- <literal>persistent</literal>,
- <literal>auto</literal> and
- <literal>none</literal>. If
- <literal>volatile</literal>, journal
- log data will be stored only in memory, i.e. below the
- <filename>/run/log/journal</filename> hierarchy (which is
- created if needed). If <literal>persistent</literal>, data
- will be stored preferably on disk, i.e. below the
- <filename>/var/log/journal</filename> hierarchy (which is
- created if needed), with a fallback to
- <filename>/run/log/journal</filename> (which is created if
- needed), during early boot and if the disk is not writable.
- <literal>auto</literal> is similar to
- <literal>persistent</literal> but the directory
- <filename>/var/log/journal</filename> is not created if
- needed, so that its existence controls where log data goes.
- <literal>none</literal> turns off all storage, all log data
- received will be dropped. Forwarding to other targets, such as
- the console, the kernel log buffer, or a syslog socket will
- still work however. Defaults to
- <literal>auto</literal>.</para></listitem>
+ <listitem><para>Controls where to store journal data. One of <literal>volatile</literal>,
+ <literal>persistent</literal>, <literal>auto</literal> and <literal>none</literal>. If
+ <literal>volatile</literal>, journal log data will be stored only in memory, i.e. below the
+ <filename>/run/log/journal</filename> hierarchy (which is created if needed). If
+ <literal>persistent</literal>, data will be stored preferably on disk, i.e. below the
+ <filename>/var/log/journal</filename> hierarchy (which is created if needed), with a fallback to
+ <filename>/run/log/journal</filename> (which is created if needed), during early boot and if the disk
+ is not writable. <literal>auto</literal> is similar to <literal>persistent</literal> but the
+ directory <filename>/var/log/journal</filename> is not created if needed, so that its existence
+ controls where log data goes. <literal>none</literal> turns off all storage, all log data received
+ will be dropped. Forwarding to other targets, such as the console, the kernel log buffer, or a syslog
+ socket will still work however. Defaults to <literal>auto</literal> in the default journal namespace,
+ and <literal>persistent</literal> in all others.</para></listitem>
</varlistentry>
<varlistentry>
<varname>TTYPath=</varname>, described below.</para>
<para>When forwarding to the kernel log buffer (kmsg), make sure to select a suitably large size for
- the log buffer, and ensure the kernel's rate-limiting applied to userspace processes is turned
- off. Specifically, add <literal>log_buf_len=8M</literal> and <literal>printk.devkmsg=on</literal> (or
- similar) to the kernel command line.</para></listitem>
+ the log buffer, for example by adding <literal>log_buf_len=8M</literal> to the kernel command line.
+ <command>systemd</command> will automatically disable kernel's rate-limiting applied to userspace
+ processes (equivalent to setting <literal>printk.devkmsg=on</literal>).</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>MaxLevelWall=</varname></term>
<listitem><para>Controls the maximum log level of messages
- that are stored on disk, forwarded to syslog, kmsg, the
+ that are stored in the journal, forwarded to syslog, kmsg, the
console or wall (if that is enabled, see above). As argument,
takes one of
<literal>emerg</literal>,
are stored/forwarded, messages above are dropped. Defaults to
<literal>debug</literal> for <varname>MaxLevelStore=</varname>
and <varname>MaxLevelSyslog=</varname>, to ensure that the all
- messages are written to disk and forwarded to syslog. Defaults
- to
+ messages are stored in the journal and forwarded to syslog.
+ Defaults to
<literal>notice</literal> for <varname>MaxLevelKMsg=</varname>,
<literal>info</literal> for <varname>MaxLevelConsole=</varname>,
and <literal>emerg</literal> for
<varlistentry>
<term><varname>ReadKMsg=</varname></term>
- <listitem><para>Takes a boolean value. If enabled (the
- default), journal reads <filename>/dev/kmsg</filename>
- messages generated by the kernel.</para></listitem>
+ <listitem><para>Takes a boolean value. If enabled <command>systemd-journal</command> processes
+ <filename>/dev/kmsg</filename> messages generated by the kernel. In the default journal namespace
+ this option is enabled by default, it is disabled in all others.</para></listitem>
</varlistentry>
<varlistentry>
<para>The operational status is one of the following:
<variablelist>
+ <varlistentry>
+ <term>missing</term>
+ <listitem>
+ <para>the device is missing</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term>off</term>
<listitem>
Takes interface name or index number.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <command>forcerenew</command>
+ </term>
+ <listitem><para>Send a FORCERENEW message to all connected clients, triggering DHCP reconfiguration.
+ Takes interface name or index number.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term>
<command>reconfigure</command>
<listitem><para>Specifies the time interval to calculate the traffic speed of each interface.
If <varname>SpeedMeter=no</varname>, the value is ignored. Defaults to 10sec.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>ManageForeignRoutes=</varname></term>
+ <listitem><para>A boolean. When true, <command>systemd-networkd</command> will store any routes
+ configured by other tools in its memory. When false, <command>systemd-networkd</command> will
+ not manage the foreign routes, thus they are kept even if <varname>KeepConfiguration=</varname>
+ is false. Defaults to yes.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
<refnamediv>
<refname>nss-systemd</refname>
<refname>libnss_systemd.so.2</refname>
- <refpurpose>Provide UNIX user and group name resolution for dynamic users and groups.</refpurpose>
+ <refpurpose>Provide UNIX user and group name resolution for user/group lookup via Varlink</refpurpose>
</refnamediv>
<refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><command>nss-systemd</command> is a plug-in module for the GNU Name Service Switch (NSS) functionality of the
- GNU C Library (<command>glibc</command>), providing UNIX user and group name resolution for dynamic users and
- groups allocated through the <varname>DynamicUser=</varname> option in systemd unit files. See
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details on
- this option.</para>
+ <para><command>nss-systemd</command> is a plug-in module for the GNU Name Service Switch (NSS)
+ functionality of the GNU C Library (<command>glibc</command>), providing UNIX user and group name
+ resolution for services implementing the <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record
+ Lookup API via Varlink</ulink>, such as the system and service manager
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> (for its
+ <varname>DynamicUser=</varname> feature, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details) or
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>This module also ensures that the root and nobody users and groups (i.e. the users/groups with the UIDs/GIDs
0 and 65534) remain resolvable at all times, even if they aren't listed in <filename>/etc/passwd</filename> or
<filename>/etc/group</filename>, or if these files are missing.</para>
+ <para>This module preferably utilizes
+ <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for resolving users and groups, but also works without the service running.</para>
+
<para>To activate the NSS module, add <literal>systemd</literal> to the lines starting with
<literal>passwd:</literal> and <literal>group:</literal> in <filename>/etc/nsswitch.conf</filename>.</para>
<!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
<programlisting>passwd: compat mymachines <command>systemd</command>
-group: compat mymachines <command>systemd</command>
+group: compat [SUCCESS=merge] mymachines [SUCCESS=merge] <command>systemd</command>
shadow: compat
hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
<citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
and hence the systemd control group hierarchy.</para>
+ <para>The module also applies various resource management and runtime parameters to the new session, as
+ configured in the <ulink url="https://systemd.io/USER_RECORD">JSON User Record</ulink> of the user, when
+ one is defined.</para>
+
<para>On login, this module — in conjunction with <filename>systemd-logind.service</filename> — ensures the
following:</para>
<listitem><para>A new systemd scope unit is created for the session. If this is the first concurrent session of
the user, an implicit per-user slice unit below <filename>user.slice</filename> is automatically created and the
scope placed into it. An instance of the system service <filename>user@.service</filename>, which runs the
- systemd user manager instance, is started. </para></listitem>
+ systemd user manager instance, is started.</para></listitem>
+
+ <listitem><para>The <literal>$TZ</literal>, <literal>$EMAIL</literal> and <literal>$LANG</literal>
+ environment variables are configured for the user, based on the respective data from the user's JSON
+ record (if it is defined). Moreover, any environment variables explicitly configured in the user record
+ are imported, and the umask, nice level, and resource limits initialized.</para></listitem>
</orderedlist>
<para>On logout, this module ensures the following:</para>
is not set if the current user is not the original user of the session.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>$TZ</varname></term>
+ <term><varname>$EMAIL</varname></term>
+ <term><varname>$LANG</varname></term>
+
+ <listitem><para>If a JSON user record is known for the user logging in these variables are
+ initialized from the respective data in the record.</para></listitem>
+ </varlistentry>
+
</variablelist>
<para>The following environment variables are read by the module and may be used by the PAM service to pass
<refsect1>
<title>Example</title>
+ <para>Here's an example PAM configuration fragment that allows users sessions to be managed by
+ <filename>systemd-logind.service</filename>:</para>
+
<programlisting>#%PAM-1.0
-auth required pam_unix.so
-auth required pam_nologin.so
-account required pam_unix.so
-password required pam_unix.so
-session required pam_unix.so
-session required pam_loginuid.so
-session required pam_systemd.so</programlisting>
+auth sufficient pam_unix.so
+auth required pam_deny.so
+
+account required pam_nologin.so
+account sufficient pam_unix.so
+account required pam_permit.so
+
+password sufficient pam_unix.so sha512 shadow try_first_pass try_authtok
+password required pam_deny.so
+
+-session optional pam_loginuid.so
+-session optional pam_systemd.so
+session required pam_unix.so</programlisting>
</refsect1>
<refsect1>
<citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>pam_systemd_home</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!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="pam_systemd_home" conditional='ENABLE_PAM_HOME'>
+
+ <refentryinfo>
+ <title>pam_systemd_home</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>pam_systemd_home</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>pam_systemd_home</refname>
+ <refpurpose>Automatically mount home directories managed by <filename>systemd-homed.service</filename> on
+ login, and unmount them on logout</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>pam_systemd_home.so</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>pam_systemd_home</command> ensures that home directories managed by
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ are automatically activated (mounted) on user login, and are deactivated (unmounted) when the last
+ session of the user ends.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist class='pam-directives'>
+
+ <varlistentry>
+ <term><varname>suspend=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If true, the home directory of the user will be suspended
+ automatically during system suspend; if false it will remain active. Automatic suspending of the home
+ directory improves security substantially as secret key material is automatically removed from memory
+ before the system is put to sleep and must be re-acquired (through user re-authentication) when
+ coming back from suspend. It is recommended to set this parameter for all PAM applications that have
+ support for automatically re-authenticating via PAM on system resume. If multiple sessions of the
+ same user are open in parallel the user's home directory will be left unsuspended on system suspend
+ as long as at least one of the sessions does not set this parameter. Defaults to
+ off.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>debug</varname><optional>=</optional></term>
+
+ <listitem><para>Takes an optional boolean argument. If yes or without the argument, the module will log
+ debugging information as it operates.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Module Types Provided</title>
+
+ <para>The module provides all four management operations: <option>auth</option>, <option>account</option>,
+ <option>session</option>, <option>password</option>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Environment</title>
+
+ <para>The following environment variables are initialized by the module and available to the processes of the
+ user's session:</para>
+
+ <variablelist class='environment-variables'>
+ <varlistentry>
+ <term><varname>$SYSTEMD_HOME=1</varname></term>
+
+ <listitem><para>Indicates that the user's home directory is managed by <filename>systemd-homed.service</filename>.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>Here's an example PAM configuration fragment that permits users managed by
+ <filename>systemd-homed.service</filename> to log in:</para>
+
+ <programlisting>#%PAM-1.0
+auth sufficient pam_unix.so
+-auth sufficient pam_systemd_home.so
+auth required pam_deny.so
+
+account required pam_nologin.so
+-account sufficient pam_systemd_home.so
+account sufficient pam_unix.so
+account required pam_permit.so
+
+-password sufficient pam_systemd_home.so
+password sufficient pam_unix.so sha512 shadow try_first_pass try_authtok
+password required pam_deny.so
+
+-session optional pam_keyinit.so revoke
+-session optional pam_loginuid.so
+-session optional pam_systemd_home.so
+-session optional pam_systemd.so
+session required pam_unix.so</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>homed.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<para>By default, after the unit files are attached the service manager's configuration is reloaded, except
when <option>--no-reload</option> is specified (see above). This ensures that the new units made available to
the service manager are seen by it.</para>
+
+ <para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
+ immediately started (blocking operation unless <option>--no-block</option> is passed) and/or enabled after
+ attaching the image.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><command>detach</command> <replaceable>IMAGE</replaceable></term>
+ <term><command>detach</command> <replaceable>IMAGE</replaceable> [<replaceable>PREFIX…</replaceable>]</term>
<listitem><para>Detaches a portable service image from the host. This undoes the operations executed by the
<command>attach</command> command above, and removes the unit file copies, drop-ins and image symlink
component of it (i.e. the file or directory name itself, not the path to it) is used for finding matching unit
files. This is a convencience feature to allow all arguments passed as <command>attach</command> also to
<command>detach</command>.</para></listitem>
+
+ <para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
+ immediately stopped (blocking operation) and/or disabled before detaching the image. Prefix(es) are also accepted,
+ to be used in case the unit names do not match the image name as described in the <command>attach</command>.</para>
</varlistentry>
<varlistentry>
contents of the image.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--enable</option></term>
+
+ <listitem><para>Immediately enable/disable the portable service after attaching/detaching.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--now</option></term>
+
+ <listitem><para>Immediately start/stop the portable service after attaching/before detaching.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--no-block</option></term>
+
+ <listitem><para>Don't block waiting for attach --now to complete.</para></listitem>
+ </varlistentry>
+
<xi:include href="user-system-options.xml" xpointer="host" />
<xi:include href="user-system-options.xml" xpointer="machine" />
--- /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">
+<refentry id="repart.d" conditional='ENABLE_REPART'>
+
+ <refentryinfo>
+ <title>repart.d</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>repart.d</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>repart.d</refname>
+ <refpurpose>Partition Definition Files for Automatic Boot-Time Repartitioning</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><literallayout><filename>/etc/repart.d/*.conf</filename>
+<filename>/run/repart.d/*.conf</filename>
+<filename>/usr/lib/repart.d/*.conf</filename>
+ </literallayout></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>repart.d/*.conf</filename> files describe basic properties of partitions of block
+ devices of the local system. They may be used to declare types, names and sizes of partitions that shall
+ exist. The
+ <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ service reads these files and attempts to add new partitions currently missing and enlarge existing
+ partitions according to these definitions. Operation is generally incremental, i.e. when applied, what
+ exists already is left intact, and partitions are never shrunk, moved or deleted.</para>
+
+ <para>These definition files are useful for implementing operating system images that are prepared and
+ delivered with minimally sized images (for example lacking any state or swap partitions), and which on
+ first boot automatically take possession of any remaining disk space following a few basic rules.</para>
+
+ <para>Currently, support for partition definition files is only implemented for GPT partitition
+ tables.</para>
+
+ <para>Partition files are generally matched against any partitions already existing on disk in a simple
+ algorithm: the partition files are sorted by their filename (ignoring the directory prefix), and then
+ compared in order against existing partitions matching the same partition type UUID. Specifically, the
+ first existing partition with a specific partition type UUID is assigned the first definition file with
+ the same partition type UUID, and the second existing partition with a specific type UUID the second
+ partition file with the same type UUID, and so on. Any left-over partition files that have no matching
+ existing partition are assumed to define new partition that shall be created. Such partitions are
+ appended to the end of the partition table, in the order defined by their names utilizing the first
+ partition slot greater than the highest slot number currently in use. Any existing partitions that have
+ no matching partition file are left as they are.</para>
+
+ <para>Note that these partition definition files do not describe the contents of the partitions, such as
+ the file system used. Separate mechanisms, such as
+ <citerefentry><refentrytitle>systemd-growfs</refentrytitle><manvolnum>8</manvolnum></citerefentry> and
+ <command>systemd-makefs</command> maybe be used to initialize or grow the file systems inside of these
+ partitions.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>[Partition] Section Options</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>Type=</varname></term>
+
+ <listitem><para>The GPT partition type UUID to match. This may be a GPT partition type UUID such as
+ <constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or one of the following special
+ identifiers:</para>
+
+ <table>
+ <title>GPT partition type identifiers</title>
+
+ <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+ <colspec colname="name" />
+ <colspec colname="explanation" />
+
+ <thead>
+ <row>
+ <entry>Identifier</entry>
+ <entry>Explanation</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><constant>esp</constant></entry>
+ <entry>EFI System Partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>xbootldr</constant></entry>
+ <entry>Extended Boot Loader Partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>swap</constant></entry>
+ <entry>Swap partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>home</constant></entry>
+ <entry>Home (<filename>/home/</filename>) partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>srv</constant></entry>
+ <entry>Server data (<filename>/srv/</filename>) partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>var</constant></entry>
+ <entry>Variable data (<filename>/var/</filename>) partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>tmp</constant></entry>
+ <entry>Temporary data (<filename>/var/tmp/</filename>) partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>linux-generic</constant></entry>
+ <entry>Generic Linux file system partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>root</constant></entry>
+ <entry>Root file system partition type appropriate for the local architecture (an alias for an architecture root file system partition type listed below, e.g. <constant>root-x86-64</constant>)</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-verity</constant></entry>
+ <entry>Verity data for the root file system partition for the local architecture</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-secondary</constant></entry>
+ <entry>Root file system partition of the secondary architecture of the local architecture; usually the matching 32bit architecture for the local 64bit architecture)</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-secondary-verity</constant></entry>
+ <entry>Verity data for the root file system partition of the secondary architecture</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-x86</constant></entry>
+ <entry>Root file system partition for the x86 (32bit, aka i386) architecture</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-x86-verity</constant></entry>
+ <entry>Verity data for the x86 (32bit) root file system partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-x86-64</constant></entry>
+ <entry>Root file system partition for the x86_64 (64bit, aka amd64) architecture</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-x86-64-verity</constant></entry>
+ <entry>Verity data for the x86_64 (64bit) root file system partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-arm</constant></entry>
+ <entry>Root file system partition for the ARM (32bit) architecture</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-arm-verity</constant></entry>
+ <entry>Verity data for the ARM (32bit) root file system partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-arm64</constant></entry>
+ <entry>Root file system partition for the ARM (64bit, aka aarch64) architecture</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-arm64-verity</constant></entry>
+ <entry>Verity data for the ARM (64bit, aka aarch64) root file system partition</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-ia64</constant></entry>
+ <entry>Root file system partition for the ia64 architecture</entry>
+ </row>
+
+ <row>
+ <entry><constant>root-ia64-verity</constant></entry>
+ <entry>Verity data for the ia64 root file system partition</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>This setting defaults to <constant>linux-generic</constant>.</para>
+
+ <para>Most of the partition type UUIDs listed above are defined in the <ulink
+ url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+ Specification</ulink>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Label=</varname></term>
+
+ <listitem><para>The textual label to assign to the partition if none is assigned yet. Note that this
+ setting is not used for matching. It is also not used when a label is already set for an existing
+ partition. It is thus only used when a partition is newly created or when an existing one had a no
+ label set (that is: an empty label). If not specified a label derived from the partition type is
+ automatically used.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Priority=</varname></term>
+
+ <listitem><para>A numeric priority to assign to this partition, in the range -2147483648…2147483647,
+ with smaller values indicating higher priority, and higher values indicating smaller priority. This
+ priority is used in case the configured size constraints on the defined partitions do not permit
+ fitting all partitions onto the available disk space. If the partitions do not fit, the highest
+ numeric partition priority of all defined partitions is determined, and all defined partitions with
+ this priority are removed from the list of new partitions to create (which may be multiple, if the
+ same priority is used for multiple partitions). The fitting algorithm is then tried again. If the
+ partitions still do not fit, the now highest numeric partition priority is determined, and the
+ matching partitions removed too, and so on. Partitions of a priority of 0 or lower are never
+ removed. If all partitions with a priority above 0 are removed and the partitions still do not fit on
+ the device the operation fails. Note that this priority has no effect on ordering partitions, for
+ that use the alphabetical order of the filenames of the partition definition files. Defaults to
+ 0.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Weight=</varname></term>
+
+ <listitem><para>A numeric weight to assign to this partition in the range 0…1000000. Available disk
+ space is assigned the defined partitions according to their relative weights (subject to the size
+ constraints configured with <varname>SizeMinBytes=</varname>, <varname>SizeMaxBytes=</varname>), so
+ that a partition with weight 2000 gets double the space as one with weight 1000, and a partition with
+ weight 333 a third of that. Defaults to 1000.</para>
+
+ <para>The <varname>Weight=</varname> setting is used to distribute available disk space in an
+ "elastic" fashion, based on the disk size and existing partitions. If a partition shall have a fixed
+ size use both <varname>SizeMinBytes=</varname> and <varname>SizeMaxBytes=</varname> with the same
+ value in order to fixate the size to one value, in which case the weight has no
+ effect.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PaddingWeight=</varname></term>
+
+ <listitem><para>Similar to <varname>Weight=</varname> but sets a weight for the free space after the
+ partition (the "padding"). When distributing available space the weights of all partitions and all
+ defined padding is summed, and then each partition and padding gets the fraction defined by its
+ weight. Defaults to 0, i.e. by default no padding is applied.</para>
+
+ <para>Padding is useful if empty space shall be left for later additions or a safety margin at the
+ end of the device or between partitions.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SizeMinBytes=</varname></term>
+ <term><varname>SizeMaxBytes=</varname></term>
+
+ <listitem><para>Specifies minimum and maximum size constraints in bytes. Takes the usual K, M, G, T,
+ … suffixes (to the base of 1024). If <varname>SizeMinBytes=</varname> is specified the partition is
+ created at or grown to at least the specified size. If <varname>SizeMaxBytes=</varname> is specified
+ the partition is created at or grown to at most the specified size. The precise size is determined
+ through the weight value value configured with <varname>Weight=</varname>, see above. When
+ <varname>SizeMinBytes=</varname> is set equal to <varname>SizeMaxBytes=</varname> the configured
+ weight has no effect as the partition is explicitly sized to the specified fixed value. Note that
+ partitions are never created smaller than 4096 bytes, and since partitions are never shrunk the
+ previous size of the partition (in case the partition already exists) is also enforced as lower bound
+ for the new size. The values should be specified as multiples of 4096 bytes, and are rounded upwards
+ (in case of <varname>SizeMinBytes=</varname>) or downwards (in case of
+ <varname>SizeMaxBytes=</varname>) otherwise. If the backing device does not provide enough space to
+ fulfill the constraints placing the partition will fail. For partitions that shall be created,
+ depending on the setting of <varname>Priority=</varname> (see above) the partition might be dropped
+ and the placing algorithm restarted. By default no size constraints are set.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PaddingMinBytes=</varname></term>
+ <term><varname>PaddingMaxBytes=</varname></term>
+
+ <listitem><para>Specifies minimum and maximum size constrains in bytes for the free space after the
+ partition (the "padding"). Semantics are similar to <varname>SizeMinBytes=</varname> and
+ <varname>SizeMaxBytes=</varname>, except that unlike partition sizes free space can be shrunk and can
+ be as small as zero. By default no size constraints on padding are set, so that only
+ <varname>PaddingWeight=</varname> determines the size of the padding applied.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>FactoryReset=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If specified the partition is marked for removal during a
+ factory reset operation. This functionality is useful to implement schemes where images can be reset
+ into their original state by removing partitions and creating them anew. Defaults to off.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Grow the root partition to the full disk size at first boot</title>
+
+ <para>With the following file the root partition is automatically grown to the full disk if possible during boot.</para>
+
+ <para><programlisting># /usr/lib/repart.d/50-root.conf
+[Partition]
+Type=root
+</programlisting></para>
+ </example>
+
+ <example>
+ <title>Create a swap and home partition automatically on boot, if missing</title>
+
+ <para>The home partition gets all available disk space while the swap partition gets 1G at most and 64M
+ at least. We set a priority > 0 on the swap partition to ensure the swap partition is not used if not
+ enough space is available. For every three bytes assigned to the home partition the swap partition gets
+ assigned one.</para>
+
+ <para><programlisting># /usr/lib/repart.d/60-home.conf
+[Partition]
+Type=home
+</programlisting></para>
+
+ <para><programlisting># /usr/lib/repart.d/70-swap.conf
+[Partition]
+Type=swap
+SizeMinBytes=64M
+SizeMaxBytes=1G
+Priority=1
+Weight=333
+</programlisting></para>
+ </example>
+
+ <example>
+ <title>Create B partitions in an A/B Verity setup, if missing</title>
+
+ <para>Let's say the vendor intends to update OS images in an A/B setup, i.e. with two root partitions
+ (and two matching Verity partitions) that shall be used alternatingly during upgrades. To minimize
+ image sizes the original image is shipped only with one root and one Verity partition (the "A" set),
+ and the second root and Verity partitions (the "B" set) shall be created on first boot on the free
+ space on the medium.</para>
+
+ <para><programlisting># /usr/lib/repart.d/50-root.conf
+[Partition]
+Type=root
+SizeMinBytes=512M
+SizeMaxBytes=512M
+</programlisting></para>
+
+ <para><programlisting># /usr/lib/repart.d/60-root-verity.conf
+[Partition]
+Type=root-verity
+SizeMinBytes=64M
+SizeMaxBytes=64M
+</programlisting></para>
+
+ <para>The definitions above cover the "A" set of root partition (of a fixed 512M size) and Verity
+ partition for the root partition (of a fixed 64M size). Let's use symlinks to create the "B" set of
+ partitions, since after all they shall have the same properties and sizes as the "A" set.</para>
+
+<para><programlisting># ln -s 50-root.conf /usr/lib/repart.d/70-root-b.conf
+# ln -s 60-root-verity.conf /usr/lib/repart.d/80-root-verity-b.conf
+</programlisting></para>
+ </example>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>sfdisk</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<varlistentry>
<term><varname>DNSOverTLS=</varname></term>
<listitem>
- <para>Takes a boolean argument or <literal>opportunistic</literal>.
- If true all connections to the server will be encrypted. Note that
- this mode requires a DNS server that supports DNS-over-TLS and has
- a valid certificate for it's IP. If the DNS server does not support
- DNS-over-TLS all DNS requests will fail. When set to <literal>opportunistic</literal>
+ <para>Takes a boolean argument or <literal>opportunistic</literal>. If
+ true all connections to the server will be encrypted. Note that this
+ mode requires a DNS server that supports DNS-over-TLS and has a valid
+ certificate. If the hostname was specified in <varname>DNS=</varname>
+ by using the format format <literal>address#server_name</literal> it
+ is used to validate its certificate and also to enable Server Name
+ Indication (SNI) when opening a TLS connection. Otherwise
+ the certificate is checked against the server's IP.
+ If the DNS server does not support DNS-over-TLS all DNS requests will fail.</para>
+
+ <para>When set to <literal>opportunistic</literal>
DNS request are attempted to send encrypted with DNS-over-TLS.
If the DNS server does not support TLS, DNS-over-TLS is disabled.
Note that this mode makes DNS-over-TLS vulnerable to "downgrade"
resolver is not capable of authenticating the server, so it is
vulnerable to "man-in-the-middle" attacks.</para>
- <para>Server Name Indication (SNI) can be used when opening a TLS connection.
- Entries in <varname>DNS=</varname> should be in format <literal>address#server_name</literal>.</para>
-
<para>In addition to this global DNSOverTLS setting
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
also maintains per-link DNSOverTLS settings. For system DNS
['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'],
['file-hierarchy', '7', [], ''],
['halt', '8', ['poweroff', 'reboot'], ''],
+ ['homectl', '1', [], 'ENABLE_HOMED'],
['hostname', '5', [], ''],
['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
['hwdb', '7', [], 'ENABLE_HWDB'],
['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'],
['journal-upload.conf', '5', ['journal-upload.conf.d'], 'HAVE_MICROHTTPD'],
['journalctl', '1', [], ''],
- ['journald.conf', '5', ['journald.conf.d'], ''],
+ ['journald.conf', '5', ['journald.conf.d', 'journald@.conf'], ''],
['kernel-command-line', '7', [], ''],
['kernel-install', '8', [], ''],
['libudev', '3', [], ''],
['nss-systemd', '8', ['libnss_systemd.so.2'], 'ENABLE_NSS_SYSTEMD'],
['os-release', '5', [], ''],
['pam_systemd', '8', [], 'HAVE_PAM'],
+ ['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
['portablectl', '1', [], 'ENABLE_PORTABLED'],
['pstore.conf', '5', ['pstore.conf.d'], 'ENABLE_PSTORE'],
+ ['repart.d', '5', [], 'ENABLE_REPART'],
['resolvectl', '1', ['resolvconf'], 'ENABLE_RESOLVE'],
['resolved.conf', '5', ['resolved.conf.d'], 'ENABLE_RESOLVE'],
['runlevel', '8', [], ''],
'sd_bus_open_user_with_description',
'sd_bus_open_with_description'],
''],
+ ['sd_bus_enqueue_for_read', '3', [], ''],
['sd_bus_error',
'3',
['SD_BUS_ERROR_MAKE_CONST',
''],
['sd_bus_message_append_strv', '3', [], ''],
['sd_bus_message_copy', '3', [], ''],
+ ['sd_bus_message_dump', '3', [], ''],
['sd_bus_message_get_cookie', '3', ['sd_bus_message_get_reply_cookie'], ''],
['sd_bus_message_get_monotonic_usec',
'3',
''],
['sd_journal_open',
'3',
- ['SD_JOURNAL_CURRENT_USER',
+ ['SD_JOURNAL_ALL_NAMESPACES',
+ 'SD_JOURNAL_CURRENT_USER',
+ 'SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE',
'SD_JOURNAL_LOCAL_ONLY',
'SD_JOURNAL_OS_ROOT',
'SD_JOURNAL_RUNTIME_ONLY',
['systemd-backlight@.service', '8', ['systemd-backlight'], 'ENABLE_BACKLIGHT'],
['systemd-binfmt.service', '8', ['systemd-binfmt'], 'ENABLE_BINFMT'],
['systemd-bless-boot-generator', '8', [], 'ENABLE_EFI'],
- ['systemd-bless-boot.service', '8', [], 'ENABLE_EFI'],
- ['systemd-boot-check-no-failures.service', '8', [], ''],
+ ['systemd-bless-boot.service', '8', ['systemd-bless-boot'], 'ENABLE_EFI'],
+ ['systemd-boot-check-no-failures.service',
+ '8',
+ ['systemd-boot-check-no-failures'],
+ ''],
['systemd-boot-system-token.service', '8', [], 'ENABLE_EFI'],
['systemd-boot', '7', ['sd-boot'], 'ENABLE_EFI'],
['systemd-cat', '1', [], ''],
'8',
['systemd-hibernate-resume'],
'ENABLE_HIBERNATE'],
+ ['systemd-homed.service', '8', ['systemd-homed'], 'ENABLE_HOMED'],
['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'],
['systemd-hwdb', '8', [], 'ENABLE_HWDB'],
['systemd-id128', '1', [], ''],
['systemd-journald',
'systemd-journald-audit.socket',
'systemd-journald-dev-log.socket',
- 'systemd-journald.socket'],
+ 'systemd-journald-varlink@.socket',
+ 'systemd-journald.socket',
+ 'systemd-journald@.service',
+ 'systemd-journald@.socket'],
''],
['systemd-localed.service', '8', ['systemd-localed'], 'ENABLE_LOCALED'],
['systemd-logind.service', '8', ['systemd-logind'], 'ENABLE_LOGIND'],
''],
['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
['systemd-mount', '1', ['systemd-umount'], ''],
+ ['systemd-network-generator.service',
+ '8',
+ ['systemd-network-generator'],
+ 'ENABLE_NETWORKD'],
['systemd-networkd-wait-online.service',
'8',
['systemd-networkd-wait-online'],
['systemd-nspawn', '1', [], ''],
['systemd-path', '1', [], ''],
['systemd-portabled.service', '8', ['systemd-portabled'], 'ENABLE_PORTABLED'],
- ['systemd-pstore', '8', ['systemd-pstore.service'], 'ENABLE_PSTORE'],
+ ['systemd-pstore.service', '8', ['systemd-pstore'], 'ENABLE_PSTORE'],
['systemd-quotacheck.service',
'8',
['systemd-quotacheck'],
'ENABLE_RANDOMSEED'],
['systemd-rc-local-generator', '8', [], ''],
['systemd-remount-fs.service', '8', ['systemd-remount-fs'], ''],
+ ['systemd-repart', '8', ['systemd-repart.service'], 'ENABLE_REPART'],
['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'],
['systemd-rfkill.service',
'8',
['systemd-update-utmp', 'systemd-update-utmp-runlevel.service'],
'ENABLE_UTMP'],
['systemd-user-sessions.service', '8', ['systemd-user-sessions'], 'HAVE_PAM'],
+ ['systemd-userdbd.service', '8', ['systemd-userdbd'], 'ENABLE_USERDB'],
['systemd-vconsole-setup.service',
'8',
['systemd-vconsole-setup'],
['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
['udevadm', '8', [], ''],
['user@.service', '5', ['user-runtime-dir@.service'], ''],
+ ['userdbctl', '1', [], 'ENABLE_USERDB'],
['vconsole.conf', '5', [], 'ENABLE_VCONSOLE']
]
# Really, do not edit.
<citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_copy</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_dump</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_bus_message_get_signature</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
--- /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="sd_bus_enqueue_for_read"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_enqueue_for_read</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_enqueue_for_read</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_enqueue_for_read</refname>
+
+ <refpurpose>Re-enqueue a bus message on a bus connection, for reading.</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_bus_enqueue_for_read</function></funcdef>
+ <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+ <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_bus_enqueue_for_read()</function> may be used to re-enqueue an incoming bus message on
+ the local read queue, so that it is processed and dispatched locally again, similar to how an incoming
+ message from the peer is processed. Takes a bus connection object and the message to enqueue. A reference
+ is taken of the message and the caller's reference thus remains in possession of the caller. The message
+ is enqueued at the end of the queue, thus will be dispatched after all other already queued messages are
+ dispatched.</para>
+
+ <para>This call is primarily useful for dealing with incoming method calls that may be processed only
+ after an additional asynchronous operation completes. One example are PolicyKit authorization requests
+ that are determined to be necessary to authorize a newly incoming method call: when the PolicyKit response
+ is received the original method call may be re-enqueued to process it again, this time with the
+ authorization result known.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, this function return 0 or a positive integer. On failure, it returns a negative errno-style
+ error code.</para>
+
+ <refsect2>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-ECHILD</constant></term>
+
+ <listitem><para>The bus connection has been created in a different process.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ </para>
+ </refsect1>
+
+</refentry>
--- /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="sd_bus_message_dump"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_bus_message_dump</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_bus_message_dump</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_bus_message_dump</refname>
+
+ <refpurpose>Produce a string representation of a message for debugging purposes</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include <systemd/sd-bus.h></funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int sd_bus_message_dump</funcdef>
+ <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+ <paramdef>FILE *<parameter>f</parameter></paramdef>
+ <paramdef>uint64_t <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <para>
+ <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>,
+ <constant>SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY</constant>
+ </para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>The <function>sd_bus_message_dump()</function> function writes a textual representation of the
+ message <parameter>m</parameter> to the stream <parameter>f</parameter>. This function is intended to be
+ used for debugging purposes, and the output is neither stable nor designed to be machine readable.
+ </para>
+
+ <para>The <parameter>flags</parameter> parameter may be used to modify the output. With
+ <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>, a header that specifies the message type and flags
+ and some additional metadata is printed. When <constant>SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY</constant> is
+ not passed, the contents of the whole message are printed. When it <emphasis>is</emphasis> passed,
+ only the current container in printed.</para>
+
+ <para>Note that this function moves the read pointer of the message. It may be necessary to reset the
+ position afterwards, for example with
+ <citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>Output for a signal message (with <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>):
+ <programlisting>
+‣ Type=signal Endian=l Flags=1 Version=1 Priority=0 Cookie=22
+ Path=/value/a Interface=org.freedesktop.DBus.Properties Member=PropertiesChanged
+ MESSAGE "sa{sv}as" {
+ STRING "org.freedesktop.systemd.ValueTest";
+ ARRAY "{sv}" {
+ DICT_ENTRY "sv" {
+ STRING "Value";
+ VARIANT "s" {
+ STRING "object 0x1e, path /value/a";
+ };
+ };
+ };
+ ARRAY "s" {
+ STRING "Value2";
+ STRING "AnExplicitProperty";
+ };
+ };
+ </programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, this function returns 0 or a positive integer. On failure, it returns a negative
+ errno-style error code. No error codes are currently defined.</para>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<para>For each type specified in the type string, one or more arguments need to be specified
after the <parameter>types</parameter> parameter, in the same order. The arguments must be
- pointers to appropriate types (a pointer to <code>int8_t</code> for a <literal>y</literal> in
- the type string, a pointer to <code>int32_t</code> for an <literal>i</literal>, a pointer to
- <code>const char*</code> for an <literal>s</literal>, ...) which are set based on the values in
+ pointers to appropriate types (a pointer to <type>int8_t</type> for a <literal>y</literal> in
+ the type string, a pointer to <type>int32_t</type> for an <literal>i</literal>, a pointer to
+ <type>const char*</type> for an <literal>s</literal>, ...) which are set based on the values in
the message. As an exception, in case or array and variant types, the first argument is an
"input" argument that further specifies how the message should be read. See the table below for
a complete list of allowed arguments and their types. Note that, if the basic type is a pointer
- (e.g., <code>const char *</code> in the case of a string), the argument is a pointer to a
+ (e.g., <type>const char *</type> in the case of a string), the argument is a pointer to a
pointer, and also the pointer value that is written is only borrowed and the contents must be
copied if they are to be used after the end of the messages lifetime.</para>
<entry><literal>a</literal></entry>
<entry><constant>SD_BUS_TYPE_ARRAY</constant></entry>
<entry>array</entry>
- <entry>int, which specifies the expected length <parameter>n</parameter> of the array</entry>
+ <entry><type>int</type>, which specifies the expected length <parameter>n</parameter> of the array</entry>
<entry><parameter>n</parameter> sets of arguments appropriate for the array element type</entry>
</row>
sd_bus_message_read(m, "x", &x);</programlisting>
+ <para>Read a boolean value:</para>
+
+ <programlisting>sd_bus_message *m;
+int x; /* Do not use C99 'bool' type here, it's typically smaller
+ in memory and would cause memory corruption */
+
+sd_bus_message_read(m, "b", &x);</programlisting>
+
<para>Read all types of integers:</para>
<programlisting>uint8_t y;
appropriate for the data type. The data is part of the message — it may not be modified and is
valid only as long as the message is referenced. After this function returns, the "read pointer"
points at the next element after the array.</para>
+
+ <para>Note that this function only supports arrays of trivial types, i.e. arrays of booleans, the various
+ integer types, as well as floating point numbers. In particular it may not be used for arrays of strings,
+ structures or similar.</para>
</refsect1>
<refsect1>
<varlistentry>
<term><constant>-EINVAL</constant></term>
- <listitem><para>Specified type is invalid or the message parameter or one of the output
- parameters are <constant>NULL</constant>.</para></listitem>
+ <listitem><para>Specified type is invalid or not a trivial type (see above), or the message
+ parameter or one of the output parameters are <constant>NULL</constant>.</para></listitem>
</varlistentry>
<varlistentry>
If <parameter>p</parameter> is not <constant>NULL</constant>, it should contain
a pointer to an appropriate object. For example, if <parameter>type</parameter>
is <constant>'y'</constant>, the object passed in <parameter>p</parameter>
- should have type <code>uint8_t *</code>. If <parameter>type</parameter> is
+ should have type <type>uint8_t *</type>. If <parameter>type</parameter> is
<constant>'s'</constant>, the object passed in <parameter>p</parameter> should
- have type <code>const char **</code>. Note that, if the basic type is a pointer
- (e.g., <code>const char *</code> in the case of a string), the pointer is only
+ have type <type>const char **</type>. Note that, if the basic type is a pointer
+ (e.g., <type>const char *</type> in the case of a string), the pointer is only
borrowed and the contents must be copied if they are to be used after the end
of the messages lifetime. Similarly, during the lifetime of such a pointer, the
message must not be modified. See the table below for a complete list of allowed
<row>
<entry><literal>y</literal></entry>
<entry><constant>SD_BUS_TYPE_BYTE</constant></entry>
- <entry>unsigned integer</entry>
- <entry>uint8_t *</entry>
+ <entry>8bit unsigned integer</entry>
+ <entry><type>uint8_t *</type></entry>
</row>
<row>
<entry><literal>b</literal></entry>
<entry><constant>SD_BUS_TYPE_BOOLEAN</constant></entry>
<entry>boolean</entry>
- <entry>int *</entry>
+ <entry><type>int *</type> (NB: not <type>bool *</type>)</entry>
</row>
<row>
<entry><literal>n</literal></entry>
<entry><constant>SD_BUS_TYPE_INT16</constant></entry>
- <entry>signed integer</entry>
- <entry>int16_t *</entry>
+ <entry>16bit signed integer</entry>
+ <entry><type>int16_t *</type></entry>
</row>
<row>
<entry><literal>q</literal></entry>
<entry><constant>SD_BUS_TYPE_UINT16</constant></entry>
- <entry>unsigned integer</entry>
- <entry>uint16_t *</entry>
+ <entry>16bit unsigned integer</entry>
+ <entry><type>uint16_t *</type></entry>
</row>
<row>
<entry><literal>i</literal></entry>
<entry><constant>SD_BUS_TYPE_INT32</constant></entry>
- <entry>signed integer</entry>
- <entry>int32_t *</entry>
+ <entry>32bit signed integer</entry>
+ <entry><type>int32_t *</type></entry>
</row>
<row>
<entry><literal>u</literal></entry>
<entry><constant>SD_BUS_TYPE_UINT32</constant></entry>
- <entry>unsigned integer</entry>
- <entry>uint32_t *</entry>
+ <entry>32bit unsigned integer</entry>
+ <entry><type>uint32_t *</type></entry>
</row>
<row>
<entry><literal>x</literal></entry>
<entry><constant>SD_BUS_TYPE_INT64</constant></entry>
- <entry>signed integer</entry>
- <entry>int64_t *</entry>
+ <entry>64bit signed integer</entry>
+ <entry><type>int64_t *</type></entry>
</row>
<row>
<entry><literal>t</literal></entry>
<entry><constant>SD_BUS_TYPE_UINT64</constant></entry>
- <entry>unsigned integer</entry>
- <entry>uint64_t *</entry>
+ <entry>64bit unsigned integer</entry>
+ <entry><type>uint64_t *</type></entry>
</row>
<row>
<entry><literal>d</literal></entry>
<entry><constant>SD_BUS_TYPE_DOUBLE</constant></entry>
- <entry>floating-point</entry>
- <entry>double *</entry>
+ <entry>IEEE 754 double precision floating-point</entry>
+ <entry><type>double *</type></entry>
</row>
<row>
<entry><literal>s</literal></entry>
<entry><constant>SD_BUS_TYPE_STRING</constant></entry>
- <entry>Unicode string</entry>
- <entry>const char **</entry>
+ <entry>UTF-8 string</entry>
+ <entry><type>const char **</type></entry>
</row>
<row>
<entry><literal>o</literal></entry>
<entry><constant>SD_BUS_TYPE_OBJECT_PATH</constant></entry>
- <entry>object path</entry>
- <entry>const char **</entry>
+ <entry>D-Bus object path string</entry>
+ <entry><type>const char **</type></entry>
</row>
<row>
<entry><literal>g</literal></entry>
<entry><constant>SD_BUS_TYPE_SIGNATURE</constant></entry>
- <entry>signature</entry>
- <entry>const char **</entry>
+ <entry>D-Bus signature string</entry>
+ <entry><type>const char **</type></entry>
</row>
<row>
<entry><literal>h</literal></entry>
<entry><constant>SD_BUS_TYPE_UNIX_FD</constant></entry>
<entry>UNIX file descriptor</entry>
- <entry>int *</entry>
+ <entry><type>int *</type></entry>
</row>
</tbody>
</tgroup>
<refname>SD_JOURNAL_SYSTEM</refname>
<refname>SD_JOURNAL_CURRENT_USER</refname>
<refname>SD_JOURNAL_OS_ROOT</refname>
+ <refname>SD_JOURNAL_ALL_NAMESPACES</refname>
+ <refname>SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE</refname>
<refpurpose>Open the system journal for reading</refpurpose>
</refnamediv>
<paramdef>int <parameter>flags</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>int <function>sd_journal_open_namespace</function></funcdef>
+ <paramdef>sd_journal **<parameter>ret</parameter></paramdef>
+ <paramdef>const char *<parameter>namespace</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+
<funcprototype>
<funcdef>int <function>sd_journal_open_directory</function></funcdef>
<paramdef>sd_journal **<parameter>ret</parameter></paramdef>
<constant>SD_JOURNAL_CURRENT_USER</constant> are specified, all
journal file types will be opened.</para>
+ <para><function>sd_journal_open_namespace()</function> is similar to
+ <function>sd_journal_open()</function> but takes an additional <parameter>namespace</parameter> parameter
+ that specifies which journal namespace to operate on. If specified as <constant>NULL</constant> the call
+ is identical to <function>sd_journal_open()</function>. If non-<constant>NULL</constant> only data from
+ the namespace identified by the specified parameter is accessed. This call understands two additional
+ flags: if <constant>SD_JOURNAL_ALL_NAMESPACES</constant> is specified the
+ <parameter>namespace</parameter> parameter is ignored and all defined namespaces are accessed
+ simultaneously; if <constant>SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE</constant> the specified namespace and
+ the default namespace are accessed but no others (this flag has no effect when
+ <parameter>namespace</parameter> is passed as <constant>NULL</constant>). For details about journal
+ namespaces see
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
<para><function>sd_journal_open_directory()</function> is similar to <function>sd_journal_open()</function> but
takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved
automatically. This call also takes a flags argument. The flags parameters accepted by this call are
<refsection id='confd'>
<title>Configuration Directories and Precedence</title>
- <para>Configuration files are read from directories in <filename>/etc/</filename>, <filename>/run/</filename>,
- <filename>/usr/local/lib/</filename>, and <filename>/usr/lib/</filename>, in order of precedence. Each
- configuration file in these configuration directories shall be named in the style of
- <filename><replaceable>filename</replaceable>.conf</filename>. Files in <filename>/etc/</filename> override files
- with the same name in <filename>/run/</filename>, <filename>/usr/local/lib/</filename>, and
- <filename>/usr/lib/</filename>. Files in <filename>/run/</filename> override files with the same name under
- <filename>/usr/</filename>.</para>
+ <para>Configuration files are read from directories in <filename>/etc/</filename>,
+ <filename>/run/</filename>, <filename>/usr/local/lib/</filename>, and <filename>/usr/lib/</filename>, in
+ order of precedence, as listed in the SYNOPSIS section above. Files must have the the
+ <literal>.conf</literal> extension. Files in <filename>/etc/</filename> override files with the same name
+ in <filename>/run/</filename>, <filename>/usr/local/lib/</filename>, and
+ <filename>/usr/lib/</filename>. Files in <filename>/run/</filename> override files with the same name
+ under <filename>/usr/</filename>.</para>
- <para>Packages should install their configuration files in <filename>/usr/lib/</filename> (distribution packages)
- or <filename>/usr/local/lib/</filename> (local installs). Files in <filename>/etc/</filename> are
- reserved for the local administrator, who may use this logic to override the
- configuration files installed by vendor packages. All configuration files
- are sorted by their filename in lexicographic order, regardless of which of
- the directories they reside in. If multiple files specify the same option,
- the entry in the file with the lexicographically latest name will take
- precedence. It is recommended to prefix all filenames with a two-digit number
- and a dash, to simplify the ordering of the files.</para>
+ <para>All configuration files are sorted by their filename in lexicographic order, regardless of which of
+ the directories they reside in. If multiple files specify the same option, the entry in the file with the
+ lexicographically latest name will take precedence. Thus, the configuration in a certain file may either
+ be replaced completely (by placing a file with the same name in a directory with higher priority), or
+ individual settings might be changed (by specifying additional settings in a file with a different name
+ that is ordered later).</para>
- <para>If the administrator wants to disable a configuration file supplied by
- the vendor, the recommended way is to place a symlink to
- <filename>/dev/null</filename> in the configuration directory in
- <filename>/etc/</filename>, with the same filename as the vendor
- configuration file. If the vendor configuration file is included in
- the initrd image, the image has to be regenerated.</para>
+ <para>Packages should install their configuration files in <filename>/usr/lib/</filename> (distribution
+ packages) or <filename>/usr/local/lib/</filename> (local installs). Files in <filename>/etc/</filename>
+ are reserved for the local administrator, who may use this logic to override the configuration files
+ installed by vendor packages. It is recommended to prefix all filenames with a two-digit number and a
+ dash, to simplify the ordering of the files.</para>
+
+ <para>If the administrator wants to disable a configuration file supplied by the vendor, the recommended
+ way is to place a symlink to <filename>/dev/null</filename> in the configuration directory in
+ <filename>/etc/</filename>, with the same filename as the vendor configuration file. If the vendor
+ configuration file is included in the initrd image, the image has to be regenerated.</para>
</refsection>
<refsection id='main-conf'>
can be edited to create local overrides.
</para>
- <para>When packages need to customize the configuration, they can
- install configuration snippets in
- <filename>/usr/lib/systemd/*.conf.d/</filename> or
- <filename>/usr/local/lib/systemd/*.conf.d/</filename>. Files in
- <filename>/etc/</filename> are reserved for the local
- administrator, who may use this logic to override the
- configuration files installed by vendor packages. The main
- configuration file is read before any of the configuration
- directories, and has the lowest precedence; entries in a file in
- any configuration directory override entries in the single
- configuration file. Files in the <filename>*.conf.d/</filename>
- configuration subdirectories are sorted by their filename in lexicographic
- order, regardless of which of the subdirectories they reside in. When
- multiple files specify the same option, for options which accept just a
- single value, the entry in the file with the lexicographically latest name
- takes precedence. For options which accept a list of values, entries are
- collected as they occur in files sorted lexicographically. It is recommended
- to prefix all filenames in those subdirectories with a two-digit number and
- a dash, to simplify the ordering of the files.</para>
+ <para>When packages need to customize the configuration, they can install configuration snippets in
+ <filename>/usr/lib/systemd/*.conf.d/</filename> or <filename>/usr/local/lib/systemd/*.conf.d/</filename>.
+ The main configuration file is read before any of the configuration directories, and has the lowest
+ precedence; entries in a file in any configuration directory override entries in the single configuration
+ file. Files in the <filename>*.conf.d/</filename> configuration subdirectories are sorted by their
+ filename in lexicographic order, regardless of in which of the subdirectories they reside. When multiple
+ files specify the same option, for options which accept just a single value, the entry in the file with
+ the lexicographically latest name takes precedence. For options which accept a list of values, entries
+ are collected as they occur in files sorted lexicographically.</para>
+
+ <para>Files in <filename>/etc/</filename> are reserved for the local administrator, who may use this
+ logic to override the configuration files installed by vendor packages. It is recommended to prefix all
+ filenames in those subdirectories with a two-digit number and a dash, to simplify the ordering of the
+ files.</para>
<para>To disable a configuration file supplied by the vendor, the
recommended way is to place a symlink to
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refsect1>
+
+<para id="controllers-text">The following controller names may be specified: <option>cpu</option>, <option>cpuacct</option>,
+<option>cpuset</option>, <option>io</option>, <option>blkio</option>, <option>memory</option>, <option>devices</option>,
+<option>pids</option>, <option>bpf-firewall</option>, and <option>bpf-devices</option>.</para>
+
+</refsect1>
<para><filename>/etc/sysctl.d/*.conf</filename></para>
<para><filename>/run/sysctl.d/*.conf</filename></para>
<para><filename>/usr/lib/sysctl.d/*.conf</filename></para>
+
+ <programlisting>key.name.under.proc.sys = some value
+key/name/under/proc/sys = some value
+key/middle.part.with.dots/foo = 123
+key.middle/part/with/dots.foo = 123
+-key.that.will.not.fail = value
+key.pattern.*.with.glob = whatever
+-key.pattern.excluded.with.glob
+key.pattern.overriden.with.glob = custom
+</programlisting>
</refsynopsisdiv>
<refsect1>
first non-whitespace character is <literal>#</literal> or
<literal>;</literal> are ignored.</para>
- <para>Note that either <literal>/</literal> or
- <literal>.</literal> may be used as separators within sysctl
- variable names. If the first separator is a slash, remaining
- slashes and dots are left intact. If the first separator is a dot,
- dots and slashes are interchanged.
- <literal>kernel.domainname=foo</literal> and
- <literal>kernel/domainname=foo</literal> are equivalent and will
- cause <literal>foo</literal> to be written to
+ <para>Note that either <literal>/</literal> or <literal>.</literal> may be used as separators within
+ sysctl variable names. If the first separator is a slash, remaining slashes and dots are left intact. If
+ the first separator is a dot, dots and slashes are interchanged.
+ <literal>kernel.domainname=foo</literal> and <literal>kernel/domainname=foo</literal> are equivalent and
+ will cause <literal>foo</literal> to be written to
<filename>/proc/sys/kernel/domainname</filename>. Either
<literal>net.ipv4.conf.enp3s0/200.forwarding</literal> or
- <literal>net/ipv4/conf/enp3s0.200/forwarding</literal> may be used
- to refer to
- <filename>/proc/sys/net/ipv4/conf/enp3s0.200/forwarding</filename>.
- </para>
-
- <para>Any access permission errors and attempts to write variables not defined on the local system are
- logged, but do not cause the service to fail. Moreover, if a variable assignment is prefixed with a
- single <literal>-</literal> character, failure to set the variable will be logged, but will not cause the
- service to fail. All other errors when setting variables cause the service to return failure at the end
- (other variables are still processed).</para>
-
- <para>The settings configured with <filename>sysctl.d</filename>
- files will be applied early on boot. The network
- interface-specific options will also be applied individually for
- each network interface as it shows up in the system. (More
- specifically, <filename>net.ipv4.conf.*</filename>,
- <filename>net.ipv6.conf.*</filename>,
- <filename>net.ipv4.neigh.*</filename> and
+ <literal>net/ipv4/conf/enp3s0.200/forwarding</literal> may be used to refer to
+ <filename>/proc/sys/net/ipv4/conf/enp3s0.200/forwarding</filename>. A glob
+ <citerefentry><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry> pattern may be
+ used to write the same value to all matching keys. Keys for which an explicit pattern exists will be
+ excluded from any glob matching. In addition, a key may be explicitly excluded from being set by any
+ matching glob patterns by specifying the key name prefixed with a <literal>-</literal> character and not
+ followed by <literal>=</literal>, see SYNOPSIS.</para>
+
+ <para>Any access permission errors and attempts to write variables not present on the local system are
+ logged, but do not cause the service to fail. Debug log level is used, which means that the message will
+ not show up at all by default. Moreover, if a variable assignment is prefixed with a single
+ <literal>-</literal> character, any failure to set the variable will be logged at debug level, but will
+ not cause the service to fail. All other errors when setting variables are logged with higher priority
+ and cause the service to return failure at the end (other variables are still processed).</para>
+
+ <para>The settings configured with <filename>sysctl.d</filename> files will be applied early on boot. The
+ network interface-specific options will also be applied individually for each network interface as it
+ shows up in the system. (More specifically, <filename>net.ipv4.conf.*</filename>,
+ <filename>net.ipv6.conf.*</filename>, <filename>net.ipv4.neigh.*</filename> and
<filename>net.ipv6.neigh.*</filename>).</para>
<para>Many sysctl parameters only become available when certain
(starting with kernel 3.18), so simply not loading the module is
sufficient to avoid filtering.</para>
</example>
+
+ <example>
+ <title>Set network routing properties for all interfaces</title>
+ <para><filename>/etc/systemd/20-rp_filter.conf</filename>:</para>
+
+ <programlisting>net.ipv4.conf.default.rp_filter = 2
+net.ipv4.conf.*.rp_filter = 2
+-net.ipv4.conf.all.rp_filter
+net.ipv4.conf.hub0.rp_filter = 1
+</programlisting>
+
+ <para>The <option>rp_filter</option> key will be set to "2" for all interfaces, except "hub0". We set
+ <filename>net.ipv4.conf.default.rp_filter</filename> first, so any interfaces which are added
+ <emphasis>later</emphasis> will get this value (this also covers any interfaces detected while we're
+ running). The glob matches any interfaces which were detected <emphasis>earlier</emphasis>. The glob
+ will also match <filename>net.ipv4.conf.all.rp_filter</filename>, which we don't want to set at all, so
+ it is explicitly excluded. And "hub0" is excluded from the glob because it has an explicit setting.
+ </para>
+ </example>
+
</refsect1>
<refsect1>
<varlistentry>
<term>
<command>list-dependencies</command>
- <optional><replaceable>UNIT</replaceable></optional>
+ <optional><replaceable>UNIT</replaceable>...</optional>
</term>
<listitem>
<para>Shows units required and wanted by the specified
- unit. This recursively lists units following the
+ units. This recursively lists units following the
<varname>Requires=</varname>,
<varname>Requisite=</varname>,
<varname>ConsistsOf=</varname>,
<varname>Wants=</varname>, <varname>BindsTo=</varname>
- dependencies. If no unit is specified,
+ dependencies. If no units are specified,
<filename>default.target</filename> is implied.</para>
<para>By default, only target units are recursively
<refnamediv>
<refname>systemd-bless-boot.service</refname>
+ <refname>systemd-bless-boot</refname>
<refpurpose>Mark current boot process as successful</refpurpose>
</refnamediv>
<refnamediv>
<refname>systemd-boot-check-no-failures.service</refname>
+ <refname>systemd-boot-check-no-failures</refname>
<refpurpose>verify that the system booted up cleanly</refpurpose>
</refnamediv>
<varlistentry>
<term><varname>rootflags=</varname></term>
- <listitem><para>Takes the root filesystem mount options to
- use. <varname>rootflags=</varname> is honored by the
- initrd.</para></listitem>
+ <listitem><para>Takes the root filesystem mount options to use. <varname>rootflags=</varname> is
+ honored by the initrd.</para>
+
+ <para>Note that unlike most kernel command line options this setting does not override settings made
+ in configuration files (specifically: the mount option string in
+ <filename>/etc/fstab</filename>). See
+ <citerefentry><refentrytitle>systemd-remount-fs.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
<refnamediv>
<refname>systemd-gpt-auto-generator</refname>
- <refpurpose>Generator for automatically discovering
- and mounting root, <filename>/home</filename> and
- <filename>/srv</filename> partitions, as well as
- discovering and enabling swap partitions, based on GPT
- partition type GUIDs.</refpurpose>
+ <refpurpose>Generator for automatically discovering and mounting root, <filename>/home/</filename>,
+ <filename>/srv/</filename>, <filename>/var/</filename> and <filename>/var/tmp/</filename> partitions, as
+ well as discovering and enabling swap partitions, based on GPT partition type GUIDs.</refpurpose>
</refnamediv>
<refsynopsisdiv>
<title>Description</title>
<para><filename>systemd-gpt-auto-generator</filename> is a unit generator that automatically discovers
- root, <filename>/home/</filename>, <filename>/srv/</filename>, the EFI System Partition, the Extended
- Boot Loader Partition and swap partitions and creates mount and swap units for them, based on the
- partition type GUIDs of GUID partition tables (GPT), see <ulink
- url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5. It implements the <ulink
- url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable Partitions
+ root, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>,
+ <filename>/var/tmp/</filename>, the EFI System Partition, the Extended Boot Loader Partition and swap
+ partitions and creates mount and swap units for them, based on the partition type GUIDs of GUID partition
+ tables (GPT), see <ulink url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5. It
+ implements the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
Specification</ulink>. Note that this generator has no effect on non-GPT systems, and on specific mount
points that are directories already containing files. Also, on systems where the units are explicitly
configured (for example, listed in <citerefentry
created.</para>
<para>This generator will only look for the root partition on the same physical disk the EFI System
- Partition (ESP) is located on. Note that support from the boot loader is required: EFI variable
- <varname>LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</varname> is used to determine from
- which partition, and hence the disk from which the system was booted. If the boot loader does not set
- this variable, this generator will not be able to autodetect the root partition.</para>
+ Partition (ESP) is located on. Note that support from the boot loader is required: the EFI variable
+ <varname>LoaderDevicePartUUID</varname> of the <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</constant>
+ vendor UUID is used to determine from which partition, and hence the disk from which the system was
+ booted. If the boot loader does not set this variable, this generator will not be able to autodetect the
+ root partition. See the <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader
+ Interface</ulink> for details.</para>
<para>Similarly, this generator will only look for the other partitions on the same physical disk as the
root partition. In this case, boot loader support is not required. These partitions will not be searched
<entry>933ac7e1-2eb4-4f13-b844-0e14e2aef915</entry>
<entry>Home Partition</entry>
<entry><filename>/home/</filename></entry>
- <entry>The first home partition on the disk the root partition is located on is mounted to <filename>/home</filename>.</entry>
+ <entry>The first home partition on the disk the root partition is located on is mounted to <filename>/home/</filename>.</entry>
</row>
<row>
<entry>3b8f8425-20e0-4f3b-907f-1a25a76f98e8</entry>
<entry>Server Data Partition</entry>
<entry><filename>/srv/</filename></entry>
- <entry>The first server data partition on the disk the root partition is located on is mounted to <filename>/srv</filename>.</entry>
+ <entry>The first server data partition on the disk the root partition is located on is mounted to <filename>/srv/</filename>.</entry>
+ </row>
+ <row>
+ <entry>4d21b016-b534-45c2-a9fb-5c16e091fd2d</entry>
+ <entry>Variable Data Partition</entry>
+ <entry><filename>/var/</filename></entry>
+ <entry>The first variable data partition on the disk the root partition is located on is mounted to <filename>/var/</filename> — under the condition its partition UUID matches the first 128 bit of the HMAC-SHA256 of the GPT type uuid of this partition keyed by the machine ID of the installation stored in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
+ </row>
+ <row>
+ <entry>7ec6f557-3bc5-4aca-b293-16ef5df639d1</entry>
+ <entry>Temporary Data Partition</entry>
+ <entry><filename>/var/tmp/</filename></entry>
+ <entry>The first temporary data partition on the disk the root partition is located on is mounted to <filename>/var/tmp/</filename>.</entry>
</row>
<row>
<entry>0657fd6d-a4ab-43c4-84e5-0933c84b4f4f</entry>
<entry>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</entry>
<entry>EFI System Partition (ESP)</entry>
<entry><filename>/efi/</filename> or <filename>/boot/</filename></entry>
- <entry>The first ESP located on the disk the root partition is located on is mounted to <filename>/boot</filename> or <filename>/efi</filename>, see below.</entry>
+ <entry>The first ESP located on the disk the root partition is located on is mounted to <filename>/boot/</filename> or <filename>/efi/</filename>, see below.</entry>
</row>
<row>
<entry>bc13c2ff-59e6-4262-a352-b275fd6f7172</entry>
<entry>Extended Boot Loader Partition</entry>
<entry><filename>/boot/</filename></entry>
- <entry>The first Extended Boot Loader Partition is mounted to <filename>/boot</filename>, see below.</entry>
+ <entry>The first Extended Boot Loader Partition is mounted to <filename>/boot/</filename>, see below.</entry>
</row>
</tbody>
</tgroup>
<row>
<entry><constant>GPT_FLAG_READ_ONLY</constant></entry>
<entry>0x1000000000000000</entry>
- <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, Extended Boot Loader Partition</entry>
+ <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
<entry>Partition is mounted read-only</entry>
</row>
<row>
<entry><constant>GPT_FLAG_NO_AUTO</constant></entry>
<entry>0x8000000000000000</entry>
- <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, Extended Boot Loader Partition</entry>
+ <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
<entry>Partition is not mounted automatically</entry>
</row>
</tgroup>
</table>
- <para>The <filename>/home/</filename> and <filename>/srv/</filename> partitions may be encrypted in LUKS
- format. In this case, a device mapper device is set up under the names
- <filename>/dev/mapper/home</filename> and <filename>/dev/mapper/srv</filename>. Note that this might
- create conflicts if the same partition is listed in <filename>/etc/crypttab</filename> with a different
- device mapper device name.</para>
+ <para>The <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename> and
+ <filename>/var/tmp/</filename> partitions may be encrypted in LUKS format. In this case, a device mapper
+ device is set up under the names <filename>/dev/mapper/home</filename>,
+ <filename>/dev/mapper/srv</filename>, <filename>/dev/mapper/var</filename> and
+ <filename>/dev/mapper/tmp</filename>. Note that this might create conflicts if the same partition is
+ listed in <filename>/etc/crypttab</filename> with a different device mapper device name.</para>
<para>When systemd is running in the initrd the <filename>/</filename> partition may be encrypted in LUKS
format as well. In this case, a device mapper device is set up under the name <filename>/dev/mapper/root</filename>,
<para>If the disk contains an Extended Boot Loader partition, as defined in the <ulink
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, it is made
- available at <filename>/boot</filename> (by means of an automount point, similar to the ESP, see
+ available at <filename>/boot/</filename> (by means of an automount point, similar to the ESP, see
above). If both an EFI System Partition and an Extended Boot Loader partition exist the latter is
preferably mounted to <filename>/boot/</filename>. Make sure to create both <filename>/efi/</filename>
and <filename>/boot/</filename> to ensure both partitions are mounted.</para>
<citerefentry><refentrytitle>systemd.swap</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!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-homed.service" conditional='ENABLE_HOMED'>
+
+ <refentryinfo>
+ <title>systemd-homed.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-homed.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-homed.service</refname>
+ <refname>systemd-homed</refname>
+ <refpurpose>Home Directory/User Account Manager</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-homed.service</filename></para>
+ <para><filename>/usr/lib/systemd/systemd-homed</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-homed</command> is a system service that may be used to create, remove, change or
+ inspect home directories.</para>
+
+ <para>Most of <command>systemd-homed</command>'s functionality is accessible through the
+ <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> command.</para>
+
+ <para>See the <ulink url="https://systemd.io/HOME_DIRECTORY">Home Directories</ulink> documentation for
+ details about the format and design of home directories managed by
+ <filename>systemd-homed.service</filename>.</para>
+
+ <para>Each home directory managed by <filename>systemd-homed.service</filename> synthesizes a local user
+ and group. These are made available to the system using the <ulink
+ url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and thus may be
+ browsed with
+ <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
will be printed. This is available in systemd services. See
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
+
+ <para>With <command>show</command>, well-known UUIDs are printed. When no arguments are specified, all
+ known UUIDs are shown. When arguments are specified, they must be the names or values of one or more
+ known UUIDs, which are then printed.</para>
</refsect1>
<refsect1>
<refname>systemd-journald.socket</refname>
<refname>systemd-journald-dev-log.socket</refname>
<refname>systemd-journald-audit.socket</refname>
+ <refname>systemd-journald@.service</refname>
+ <refname>systemd-journald@.socket</refname>
+ <refname>systemd-journald-varlink@.socket</refname>
<refname>systemd-journald</refname>
<refpurpose>Journal service</refpurpose>
</refnamediv>
<para><filename>systemd-journald.socket</filename></para>
<para><filename>systemd-journald-dev-log.socket</filename></para>
<para><filename>systemd-journald-audit.socket</filename></para>
+ <para><filename>systemd-journald@.service</filename></para>
+ <para><filename>systemd-journald@.socket</filename></para>
+ <para><filename>systemd-journald-varlink@.socket</filename></para>
<para><filename>/usr/lib/systemd/systemd-journald</filename></para>
</refsynopsisdiv>
<constant>EPIPE</constant> right from the beginning.</para>
</refsect1>
+ <refsect1>
+ <title>Journal Namespaces</title>
+
+ <para>Journal 'namespaces' are both a mechanism for logically isolating the log stream of projects
+ consisting of one or more services from the rest of the system and a mechanism for improving
+ performance. Multiple journal namespaces may exist simultaneously, each defining its own, independent log
+ stream managed by its own instance of <command>systemd-journald</command>. Namespaces are independent of
+ each other, both in the data store and in the IPC interface. By default only a single 'default' namespace
+ exists, managed by <filename>systemd-journald.service</filename> (and its associated socket
+ units). Additional namespaces are created by starting an instance of the
+ <filename>systemd-journald@.service</filename> service template. The instance name is the namespace
+ identifier, which is a short string used for referencing the journal namespace. Service units may be
+ assigned to a specific journal namespace through the <varname>LogNamespace=</varname> unit file setting,
+ see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details. The <option>--namespace=</option> switch of
+ <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> may be
+ used to view the log stream of a specific namespace. If the switch is not used the log stream of the
+ default namespace is shown, i.e. log data from other namespaces is not visible.</para>
+
+ <para>Services associated with a specific log namespace may log via syslog, the native logging protocol
+ of the journal and via stdout/stderr; the logging from all three transports is associated with the
+ namespace.</para>
+
+ <para>By default only the default namespace will collect kernel and audit log messages.</para>
+
+ <para>The <command>systemd-journald</command> instance of the default namespace is configured through
+ <filename>/etc/systemd/journald.conf</filename> (see below), while the other instances are configured
+ through <filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename>. The journal
+ log data for the default namespace is placed in
+ <filename>/var/log/journal/<replaceable>MACHINE_ID</replaceable></filename> (see below) while the data
+ for the other namespaces is located in
+ <filename>/var/log/journal/<replaceable>MACHINE_ID</replaceable>.<replaceable>NAMESPACE</replaceable></filename>.</para>
+ </refsect1>
+
<refsect1>
<title>Signals</title>
</varlistentry>
</variablelist>
+
+ <para>Note that these kernel command line options are only honoured by the default namespace, see
+ above.</para>
</refsect1>
<refsect1>
<term><filename>/run/systemd/journal/socket</filename></term>
<term><filename>/run/systemd/journal/stdout</filename></term>
- <listitem><para>Sockets and other paths that
- <command>systemd-journald</command> will listen on that are
- visible in the file system. In addition to these, journald can
- listen for audit events using netlink.</para></listitem>
+ <listitem><para>Sockets and other file node paths that <command>systemd-journald</command> will
+ listen on and are visible in the file system. In addition to these,
+ <command>systemd-journald</command> can listen for audit events using <citerefentry
+ project='man-pages'><refentrytitle>netlink</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
</variablelist>
+
+ <para>If journal namespacing is used these paths are slightly altered to include a namespace identifier, see above.</para>
</refsect1>
<refsect1>
<citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-journal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-coredump</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
- <citerefentry project='die-net'><refentrytitle>setfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>setfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<command>pydoc systemd.journal</command>
</para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>mkfs.btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>mkfs.cramfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>mkfs.ext4</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!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-network-generator.service" conditional='ENABLE_NETWORKD'>
+
+ <refentryinfo>
+ <title>systemd-network-generator.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-network-generator.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-network-generator.service</refname>
+ <refname>systemd-network-generator</refname>
+ <refpurpose>Generate network configuration from the kernel command line</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-network-generator.service</filename></para>
+ <para><filename>/usr/lib/systemd/systemd-network-generator</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-network-generator.service</filename> is a system service that translates
+ <varname>ip=</varname> and the related settings on the kernel command line (see below) into
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>, and
+ <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ configuration files understood by
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and
+ <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para>
+
+ <para>Files are generated in <filename>/run/systemd/network/</filename>.</para>
+ </refsect1>
+
+ <refsect1><title>Kernel command line options</title>
+ <para>This tool understands the following options:</para>
+
+ <variablelist class='kernel-commandline-options'>
+ <varlistentry>
+ <term><varname>ip=</varname></term>
+ <term><varname>rd.route=</varname></term>
+ <term><varname>rd.peerdns=</varname></term>
+ <listitem>
+ <para>— translated into
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> files.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ifname=</varname></term>
+ <listitem>
+ <para>— translated into
+ <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry> files.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>vlan=</varname></term>
+ <term><varname>bond=</varname></term>
+ <term><varname>bridge=</varname></term>
+ <term><varname>bootdev=</varname></term>
+ <listitem>
+ <para>— translated into
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry> files.</para>
+ </listitem>
+ </varlistentry>
+
+ <!-- unsupported:
+ team=<teammaster>:<teamslaves>
+ bootdev=
+ BOOTIF=
+ bootdev=
+ bootdev=
+ bootdev=
+ -->
+ </variablelist>
+
+ <para>See
+ <citerefentry project='man-pages'><refentrytitle>dracut.kernel</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for option syntax and details.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>dracut</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<variablelist>
<varlistentry>
- <term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
- <term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
+ <term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
+ <term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
<listitem><para>Network interface to wait for before deciding if the system is online. This
is useful when a system has several interfaces which will be configured, but a particular
one is necessary to access some network resources. When used, all other interfaces are ignored.
This option may be used more than once to wait for multiple network interfaces. When this
option is specified multiple times, then <command>systemd-networkd-wait-online</command> waits
- for all specified interfaces to be online. Optionally, required minimum operational state can be
- specified after a colon <literal>:</literal>. Please see
+ for all specified interfaces to be online. Optionally, required minimum and maximum operational
+ states can be specified after a colon <literal>:</literal>. Please see
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible operational states. If the operational state is not specified here, then
the value from <varname>RequiredForOnline=</varname> in the corresponding
</varlistentry>
<varlistentry>
- <term><option>-o</option> <replaceable>OPERSTATE</replaceable></term>
- <term><option>--operational-state=</option><replaceable>OPERSTATE</replaceable></term>
+ <term><option>-o</option> <replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
+ <term><option>--operational-state=</option><replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
- <listitem><para>Takes an operational state. Please see
- <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <listitem><para>Takes a minimum operational state and an optional maximum operational state.
+ Please see <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible operational states. If set, the specified value overrides
<varname>RequiredForOnline=</varname> settings in <filename>.network</filename> files.
But this does not override operational states specified in <option>--interface=</option> option.
<filename>systemd-networkd</filename> is restarted, netdev interfaces for
which configuration was removed will not be dropped, and may need to be
cleaned up manually.</para>
+
+ <para><command>systemd-networkd</command> may be introspected and controlled at runtime using
+ <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ </para>
</refsect1>
<refsect1><title>Configuration Files</title>
<refsect1>
<title>See Also</title>
<para>
+ <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-networkd-wait-online.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd-networkd-wait-online.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-networkd-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
<?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 "30">
-<!ENTITY fedora_cloud_release "1.2">
+<!ENTITY fedora_latest_version "31">
+<!ENTITY fedora_cloud_release "1.9">
]>
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
a server data partition which are mounted to the appropriate
places in the container. All these partitions must be
identified by the partition types defined by the <ulink
- url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable
+ url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable
Partitions Specification</ulink>.</para></listitem>
<listitem><para>No partition table, and a single file system spanning the whole image.</para></listitem>
<programlisting># dnf -y --releasever=&fedora_latest_version; --installroot=/var/lib/machines/f&fedora_latest_version; \
--disablerepo='*' --enablerepo=fedora --enablerepo=updates install \
- systemd passwd dnf fedora-release vim-minimal
+ systemd passwd dnf fedora-release vim-minimal glibc-minimal-langpack
# systemd-nspawn -bD /var/lib/machines/f&fedora_latest_version;</programlisting>
<para>This installs a minimal Fedora distribution into the
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
- <title>systemd-pstore</title>
+ <title>systemd-pstore.service</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
- <refentrytitle>systemd-pstore</refentrytitle>
+ <refentrytitle>systemd-pstore.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
- <refname>systemd-pstore</refname>
<refname>systemd-pstore.service</refname>
- <refpurpose>Tool to archive contents of the persistent storage filesystem</refpurpose>
+ <refname>systemd-pstore</refname>
+ <refpurpose>A service to archive contents of pstore</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para>The pstore service is independent of the kdump service. In cloud environments
specifically, host and guest filesystems are on remote filesystems (eg. iSCSI
- or NFS), thus kdump relies [implicitly and/or explicitly] upon proper operation
- of networking software *and* hardware *and* infrastructure. Thus it may not be
+ or NFS), thus kdump relies (implicitly and/or explicitly) upon proper operation
+ of networking software *and* hardware *and* infrastructure. Thus it may not be
possible to capture a kernel coredump to a file since writes over the network
may not be possible.</para>
debugging.</para>
<para>The <command>systemd-pstore</command> executable does the actual work. Upon starting,
- the <filename>pstore.conf</filename> is read to obtain options, then the /sys/fs/pstore
+ the <filename>pstore.conf</filename> file is read and the <filename>/sys/fs/pstore</filename>
directory contents are processed according to the options. Pstore files are written to the
- journal, and optionally saved into /var/lib/systemd/pstore.</para>
+ journal, and optionally saved into <filename>/var/lib/systemd/pstore</filename>.</para>
</refsect1>
<refsect1>
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!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-repart" conditional='ENABLE_REPART'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-repart</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-repart</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-repart</refname>
+ <refname>systemd-repart.service</refname>
+ <refpurpose>Automatically grow and add partitions</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>systemd-repart</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="opt" rep="repeat"><replaceable><optional>BLOCKDEVICE</optional></replaceable></arg>
+ </cmdsynopsis>
+
+ <para><filename>systemd-repart.service</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-repart</command> grows and adds partitions to a partition table, based on the
+ configuration files described in
+ <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
+
+ <para>If invoked with no arguments, it operates on the block device backing the root file system partition
+ of the OS, thus growing and adding partitions of the booted OS image itself. When called in the initial
+ RAM disk it operates on the block device backing <filename>/sysroot/</filename> instead, i.e. on the
+ block device the system will soon transition into. The <filename>systemd-repart.service</filename>
+ service is generally run at boot in the initial RAM disk, in order to augment the partition table of the
+ OS before its partitions are mounted. <command>systemd-repart</command> (mostly) operates in a purely
+ incremental mode: it only grows existing and adds new partitions; it does not shrink, delete or move
+ existing partitions. The service is intended to be run on every boot, but when it detects that the
+ partition table already matches the installed <filename>repart.d/*.conf</filename> configuration
+ files, it executes no operation.</para>
+
+ <para><command>systemd-repart</command> is intended to be used when deploying OS images, to automatically
+ adjust them to the system they are running on, during first boot. This way the deployed image can be
+ minimal in size and may be augmented automatically at boot when needed, taking possession of disk space
+ available but not yet used. Specifically the following use cases are among those covered:</para>
+
+ <itemizedlist>
+ <listitem><para>The root partition may be grown to cover the whole available disk space</para></listitem>
+ <listitem><para>A <filename>/home/</filename>, swap or <filename>/srv</filename> partition can be added in</para></listitem>
+ <listitem><para>A second (or third, …) root partition may be added in, to cover A/B style setups
+ where a second version of the root file system is alternatingly used for implementing update
+ schemes. The deployed image would carry only a single partition ("A") but on first boot a second
+ partition ("B") for this purpose is automatically created.</para></listitem>
+ </itemizedlist>
+
+ <para>The algorithm executed by <command>systemd-repart</command> is roughly as follows:</para>
+
+ <orderedlist>
+ <listitem><para>The <filename>repart.d/*.conf</filename> configuration files are loaded and parsed,
+ and ordered by filename (without the directory suffix). </para></listitem>
+
+ <listitem><para>The partition table already existing on the block device is loaded and
+ parsed.</para></listitem>
+
+ <listitem><para>The existing partitions in the partition table are matched up with the
+ <filename>repart.d/*.conf</filename> files by GPT partition type UUID. The first existing partition
+ of a specific type is assigned the first configuration file declaring the same type. The second
+ existing partition of a specific type is then assigned the second configuration file declaring the same
+ type, and so on. After this iterative assigning is complete any left-over existing partitions that have
+ no matching configuration file are considered "foreign" and left as they are. And any configuration
+ files for which no partition currently exists are understood as a request to create such a
+ partition.</para></listitem>
+
+ <listitem><para>Taking the size constraints and weights declared in the configuration files into
+ account, all partitions that shall be created are now allocated to the disk, taking up all free space,
+ always respecting the size and padding requests. Similar, existing partitions that are determined to
+ grow are grown. New partitions are always appended to the end of the existing partition table, taking
+ the first partition table slot whose index is greater than the indexes of all existing
+ partitions. Partition table slots are never reordered and thus partition numbers are ensured to remain
+ stable. Note that this allocation happens in RAM only, the partition table on disk is not updated
+ yet.</para></listitem>
+
+ <listitem><para>All existing partitions for which configuration files exist and which currently have no
+ GPT partition label set will be assigned a label, either explicitly configured in the configuration or
+ (if that's missing) derived automatically from the partition type. The same is done for all partitions
+ that are newly created. These assignments are done in RAM only, too, the disk is not updated
+ yet.</para></listitem>
+
+ <listitem><para>Similarly, all existing partitions for which configuration files exist and which
+ currently have an all-zero identifying UUID will be assigned a new UUID. This UUID is cryptographically
+ hashed from a common seed value together with the partition type UUID (and a counter in case multiple
+ partitions of the same type are defined), see below. The same is done for all partitions that are
+ created anew. These assignments are done in RAM only, too, the disk is not updated
+ yet.</para></listitem>
+
+ <listitem><para>Similarly, if the disk's volume UUID is all zeroes it is also initialized, also
+ cryptographically hashed from the same common seed value. Also, in RAM only, too.</para></listitem>
+
+ <listitem><para>The disk space assigned to new partitions (i.e. what was previously considered free
+ space but is no longer) is now erased. Specifically, all file system signatures are removed, and if the
+ device supports it the <constant>BLKDISCARD</constant> I/O control command is issued to inform the
+ hardware that the space is empty now. In addition any "padding" between partitions and at the end of
+ the device is similarly erased.</para></listitem>
+
+ <listitem><para>The new partition table is finally written to disk. The kernel is asked to reread the
+ partition table.</para></listitem>
+ </orderedlist>
+
+ <para>As exception to the normally strictly incremental operation, when called in a special "factory
+ reset" mode <command>systemd-repart</command> may also be used to erase select existing partitions to
+ reset an installation back to vendor defaults. This mode of operation is used when either the
+ <option>--factory-reset=yes</option> switch is passed on the tool's command line, or the
+ <option>systemd.factory_reset=yes</option> option specified on the kernel command line, or the
+ <varname>FactoryReset</varname> EFI variable (vendor UUID
+ <constant>8cf2644b-4b0b-428f-9387-6d876050dc67</constant>) is set to "yes". It alters the algorithm above
+ slightly: between the 3rd and the 4th step above the any partition marked explicitly via the
+ <varname>FactoryReset=</varname> boolean is deleted, and the algorithm restarted, thus immediately
+ re-creating these partitions anew empty.</para>
+
+ <para>Note that <command>systemd-repart</command> only changes partition tables, it does not create or
+ resize any file systems within these partitions. A separate mechanism should be used for that, for
+ example
+ <citerefentry><refentrytitle>systemd-growfs</refentrytitle><manvolnum>8</manvolnum></citerefentry> and
+ <command>systemd-makefs</command>.</para>
+
+ <para>The UUIDs identifying the new partitions created (or assigned to existing partitions that have no
+ UUID yet), as well as the disk as a whole are hashed cryptographically from a common seed value. This
+ seed value is usually the
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the
+ system, so that the machine ID reproducibly determines the UUIDs assigned to all partitions. If the
+ machine ID cannot be read (or the user passes <option>--seed=random</option>, see below) the seed is
+ generated randomly instead, so that the partition UUIDs are also effectively random. The seed value may
+ also be set explicitly, formatted as UUID via the <option>--seed=</option> option. By hashing these UUIDs
+ from a common seed images prepared with this tool become reproducible and the result of the algorithm
+ above deterministic.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--dry-run=</option></term>
+ <listitem><para>Takes a boolean. If this switch is not specified <option>--dry-run=yes</option> is
+ the implied default. Controls whether <filename>systemd-repart</filename> executes the requested
+ re-partition operations or whether it should only show what it would do. Unless
+ <option>--dry-run=no</option> is specified <filename>systemd-repart</filename> will not actually
+ touch the device's partition table.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--empty=</option></term>
+ <listitem><para>Takes one of <literal>refuse</literal>, <literal>allow</literal>,
+ <literal>require</literal> or <literal>force</literal>. Controls how to operate on block devices that
+ are entirely empty, i.e. carry no partition table/disk label yet. If this switch is not specified the
+ implied default is <literal>refuse</literal>.</para>
+
+ <para>If <literal>refuse</literal> <command>systemd-repart</command> requires that the block device
+ it shall operate on already carries a partition table and refuses operation if none is found. If
+ <literal>allow</literal> the command will extend an existing partition table or create a new one if
+ none exists. If <literal>require</literal> the command will create a new partition table if none
+ exists so far, and refuse operation if one already exists. If <literal>force</literal> it will create
+ a fresh partition table unconditionally, erasing the disk fully in effect. If
+ <literal>force</literal> no existing partitions will be taken into account or survive the
+ operation. Hence: use with care, this is a great way to lose all your data.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--discard=</option></term>
+
+ <listitem><para>Takes a boolean. If this switch is not specified <option>--discard=yes</option> is
+ the implied default. Controls whether to issue the <constant>BLKDISCARD</constant> I/O control
+ command on the space taken up by any added partitions or on the space in between them. Usually, it's
+ a good idea to issue this request since it tells the underlying hardware that the covered blocks
+ shall be considered empty, improving performance.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--factory-reset=</option></term>
+
+ <listitem><para>Takes boolean. If this switch is not specified <option>--factory=reset=no</option> is
+ the implied default. Controls whether to operate in "factory reset" mode, see above. If set to true
+ this will remove all existing partitions marked with <varname>FactoryReset=</varname> set to yes
+ early while executing the re-partitioning algorithm. Use with care, this is a great way to lose all
+ your data. Note that partition files need to explicitly turn <varname>FactoryReset=</varname> on, as
+ the option defaults to off. If no partitions are marked for factory reset this switch has no
+ effect. Note that there are two other methods to request factory reset operation: via the kernel
+ command line and via an EFI variable, see above.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--can-factory-reset</option></term>
+
+ <listitem><para>If this switch is specified the disk is not re-partitioned. Instead it is determined
+ if any existing partitions are marked with <varname>FactoryReset=</varname>. If there are the tool
+ will exit with exit status zero, otherwise non-zero. This switch may be used to quickly determine
+ whether the running system supports a factory reset mechanism built on
+ <command>systemd-repart</command>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--root=</option></term>
+
+ <listitem><para>Takes a path to a directory to use as root file system when searching for
+ <filename>repart.d/*.conf</filename> files and for the machine ID file to use as seed. By default
+ when invoked on the regular system this defaults to the host's root file system
+ <filename>/</filename>. If invoked from the initial RAM disk this defaults to
+ <filename>/sysroot/</filename>, so that the tool operates on the configuration and machine ID stored
+ in the root file system later transitioned into itself.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--seed=</option></term>
+
+ <listitem><para>Takes a UUID as argument or the special value <constant>random</constant>. If a UUID
+ is specified the UUIDs to assign to partitions and the partition table itself are derived via
+ cryptographic hashing from it. If not specified it is attempted to read the machine ID from the host
+ (or more precisely, the root directory configured via <option>--root=</option>) and use it as seed
+ instead, falling back to a randomized seed otherwise. Use <option>--seed=random</option> to force a
+ randomized seed. Explicitly specifying the seed may be used to generated strictly reproducible
+ partition tables.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--pretty=</option></term>
+
+ <listitem><para>Takes a boolean argument. If this switch is not specified, it defaults to on when
+ called from an interactive terminal and off otherwise. Controls whether to show a user friendly table
+ and graphic illustrating the changes applied.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--definitions=</option></term>
+
+ <listitem><para>Takes a file system path. If specified the <filename>*.conf</filename> are directly
+ read from the specified directory instead of searching in
+ <filename>/usr/lib/repart.d/*.conf</filename>, <filename>/etc/repart.d/*.conf</filename>,
+ <filename>/run/repart.d/*.conf</filename>.</para></listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<term><varname>DefaultLimitRTPRIO=</varname></term>
<term><varname>DefaultLimitRTTIME=</varname></term>
- <listitem><para>These settings control various default
- resource limits for units. See
- <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
- for details. The resource limit is possible to specify in two formats,
- <option>value</option> to set soft and hard limits to the same value,
- or <option>soft:hard</option> to set both limits individually (e.g. DefaultLimitAS=4G:16G).
- Use the string <varname>infinity</varname> to
- configure no limit on a specific resource. The multiplicative
- suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
- may be used for resource limits measured in bytes
- (e.g. DefaultLimitAS=16G). For the limits referring to time values,
- the usual time units ms, s, min, h and so on may be used (see
- <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for details). Note that if no time unit is specified for
- <varname>DefaultLimitCPU=</varname> the default unit of seconds is
- implied, while for <varname>DefaultLimitRTTIME=</varname> the default
- unit of microseconds is implied. Also, note that the effective
- granularity of the limits might influence their
- enforcement. For example, time limits specified for
- <varname>DefaultLimitCPU=</varname> will be rounded up implicitly to
- multiples of 1s. These settings may be overridden in individual units
- using the corresponding LimitXXX= directives. Note that these resource
- limits are only defaults for units, they are not applied to PID 1
- itself.</para></listitem>
+ <listitem><para>These settings control various default resource limits for processes executed by
+ units. See
+ <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+ details. These settings may be overridden in individual units using the corresponding
+ <varname>LimitXXX=</varname> directives, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>, for
+ details, and they accept the same parameter syntax. Note that these resource limits are only defaults
+ for units, they are not applied to the service manager process (i.e. PID 1) itself.</para></listitem>
</varlistentry>
<varlistentry>
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!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-userdbd.service" conditional='ENABLE_USERDB'>
+
+ <refentryinfo>
+ <title>systemd-userdbd.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-userdbd.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-userdbd.service</refname>
+ <refname>systemd-userdbd</refname>
+ <refpurpose>JSON User/Group Record Query Multiplexer/NSS Compatibility</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-userdbd.service</filename></para>
+ <para><filename>/usr/lib/systemd/systemd-userdbd</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-userdbd</command> is a system service that multiplexes user/group lookups to all
+ local services that provide JSON user/group record definitions to the system. In addition it synthesizes
+ JSON user/group records from classic UNIX/glibc NSS user/group records in order to provide full backwards
+ compatibility.</para>
+
+ <para>Most of <command>systemd-userdbd</command>'s functionality is accessible through the
+ <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ command.</para>
+
+ <para>The user and group records this service provides access to follow the <ulink
+ url="https://systemd.io/USER_RECORD">JSON User Record</ulink> and <ulink
+ url="https://systemd.io/GROUP_RECORD">JSON Group Record</ulink> definitions. This service implements the
+ <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and
+ multiplexes access other services implementing this API, too. It is thus both server and client of this
+ API.</para>
+
+ <para>This service provides two distinct <ulink url="https://varlink.org/">Varlink</ulink> services:
+ <constant>io.systemd.Multiplexer</constant> provides a single, unified API for querying JSON user and
+ group records. Internally it talks to all other user/group record services running on the system in
+ parallel and forwards any information discovered. This simplifies clients substantially since they need
+ to talk to a single service only instead of all of them in
+ parallel. <constant>io.systemd.NameSeviceSwitch</constant> provides compatibility with classic UNIX/glibc
+ NSS user records, i.e. converts <type>struct passwd</type> and <type>struct group</type> records as
+ acquired with APIs such as <citerefentry
+ project='man-pages'><refentrytitle>getpwnam</refentrytitle><manvolnum>1</manvolnum></citerefentry> to JSON
+ user/group records, thus hiding the differences between the services as much as possible.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>nss-systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
<option>syslog</option> or <option>kmsg</option> (or their combinations with console output, see below)
automatically acquire dependencies of type <varname>After=</varname> on
<filename>systemd-journald.socket</filename>.</para></listitem>
+
+ <listitem><para>Units using <varname>LogNamespace=</varname> will automatically gain ordering and
+ requirement dependencies on the two socket units associated with
+ <filename>systemd-journald@.service</filename> instances.</para></listitem>
</itemizedlist>
</refsect1>
<varlistentry>
<term><varname>RootImage=</varname></term>
- <listitem><para>Takes a path to a block device node or regular file as argument. This call is similar to
- <varname>RootDirectory=</varname> however mounts a file system hierarchy from a block device node or loopback
- file instead of a directory. The device node or file system image file needs to contain a file system without a
- partition table, or a file system within an MBR/MS-DOS or GPT partition table with only a single
- Linux-compatible partition, or a set of file systems within a GPT partition table that follows the <ulink
- url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable Partitions
+ <listitem><para>Takes a path to a block device node or regular file as argument. This call is similar
+ to <varname>RootDirectory=</varname> however mounts a file system hierarchy from a block device node
+ or loopback file instead of a directory. The device node or file system image file needs to contain a
+ file system without a partition table, or a file system within an MBR/MS-DOS or GPT partition table
+ with only a single Linux-compatible partition, or a set of file systems within a GPT partition table
+ that follows the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
Specification</ulink>.</para>
<para>When <varname>DevicePolicy=</varname> is set to <literal>closed</literal> or
Linux systems.</para>
<para>When used in conjunction with <varname>DynamicUser=</varname> the user/group name specified is
- dynamically allocated at the time the service is started, and released at the time the service is stopped —
- unless it is already allocated statically (see below). If <varname>DynamicUser=</varname> is not used the
- specified user and group must have been created statically in the user database no later than the moment the
- service is started, for example using the
- <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> facility, which
- is applied at boot or package install time.</para>
+ dynamically allocated at the time the service is started, and released at the time the service is
+ stopped — unless it is already allocated statically (see below). If <varname>DynamicUser=</varname>
+ is not used the specified user and group must have been created statically in the user database no
+ later than the moment the service is started, for example using the
+ <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ facility, which is applied at boot or package install time. If the user does not exist by then
+ program invocation will fail.</para>
<para>If the <varname>User=</varname> setting is used the supplementary group list is initialized
from the specified user's default group list, as defined in the system's user and group
<varname>RestrictAddressFamilies=</varname>, <varname>RestrictNamespaces=</varname>,
<varname>PrivateDevices=</varname>, <varname>ProtectKernelTunables=</varname>,
<varname>ProtectKernelModules=</varname>, <varname>ProtectKernelLogs=</varname>,
- <varname>MemoryDenyWriteExecute=</varname>, <varname>RestrictRealtime=</varname>,
- <varname>RestrictSUIDSGID=</varname>, <varname>DynamicUser=</varname> or <varname>LockPersonality=</varname>
- are specified. Note that even if this setting is overridden by them, <command>systemctl show</command> shows the
- original value of this setting. Also see <ulink
- url="https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html">No New Privileges
+ <varname>ProtectClock=</varname>, <varname>MemoryDenyWriteExecute=</varname>,
+ <varname>RestrictRealtime=</varname>, <varname>RestrictSUIDSGID=</varname>, <varname>DynamicUser=</varname>
+ or <varname>LockPersonality=</varname> are specified. Note that even if this setting is overridden by them,
+ <command>systemctl show</command> shows the original value of this setting.
+ Also see <ulink url="https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html">No New Privileges
Flag</ulink>.</para></listitem>
</varlistentry>
<term><varname>LimitRTTIME=</varname></term>
<listitem><para>Set soft and hard limits on various resources for executed processes. See
- <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details on
- the resource limit concept. Resource limits may be specified in two formats: either as single value to set a
- specific soft and hard limit to the same value, or as colon-separated pair <option>soft:hard</option> to set
- both limits individually (e.g. <literal>LimitAS=4G:16G</literal>). Use the string <option>infinity</option> to
- configure no limit on a specific resource. The multiplicative suffixes K, M, G, T, P and E (to the base 1024)
- may be used for resource limits measured in bytes (e.g. LimitAS=16G). For the limits referring to time values,
- the usual time units ms, s, min, h and so on may be used (see
+ <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+ details on the resource limit concept. Resource limits may be specified in two formats: either as
+ single value to set a specific soft and hard limit to the same value, or as colon-separated pair
+ <option>soft:hard</option> to set both limits individually (e.g. <literal>LimitAS=4G:16G</literal>).
+ Use the string <option>infinity</option> to configure no limit on a specific resource. The
+ multiplicative suffixes K, M, G, T, P and E (to the base 1024) may be used for resource limits
+ measured in bytes (e.g. <literal>LimitAS=16G</literal>). For the limits referring to time values, the
+ usual time units ms, s, min, h and so on may be used (see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
- details). Note that if no time unit is specified for <varname>LimitCPU=</varname> the default unit of seconds
- is implied, while for <varname>LimitRTTIME=</varname> the default unit of microseconds is implied. Also, note
- that the effective granularity of the limits might influence their enforcement. For example, time limits
- specified for <varname>LimitCPU=</varname> will be rounded up implicitly to multiples of 1s. For
- <varname>LimitNICE=</varname> the value may be specified in two syntaxes: if prefixed with <literal>+</literal>
- or <literal>-</literal>, the value is understood as regular Linux nice value in the range -20..19. If not
- prefixed like this the value is understood as raw resource limit parameter in the range 0..40 (with 0 being
- equivalent to 1).</para>
-
- <para>Note that most process resource limits configured with these options are per-process, and processes may
- fork in order to acquire a new set of resources that are accounted independently of the original process, and
- may thus escape limits set. Also note that <varname>LimitRSS=</varname> is not implemented on Linux, and
- setting it has no effect. Often it is advisable to prefer the resource controls listed in
+ details). Note that if no time unit is specified for <varname>LimitCPU=</varname> the default unit of
+ seconds is implied, while for <varname>LimitRTTIME=</varname> the default unit of microseconds is
+ implied. Also, note that the effective granularity of the limits might influence their
+ enforcement. For example, time limits specified for <varname>LimitCPU=</varname> will be rounded up
+ implicitly to multiples of 1s. For <varname>LimitNICE=</varname> the value may be specified in two
+ syntaxes: if prefixed with <literal>+</literal> or <literal>-</literal>, the value is understood as
+ regular Linux nice value in the range -20..19. If not prefixed like this the value is understood as
+ raw resource limit parameter in the range 0..40 (with 0 being equivalent to 1).</para>
+
+ <para>Note that most process resource limits configured with these options are per-process, and
+ processes may fork in order to acquire a new set of resources that are accounted independently of the
+ original process, and may thus escape limits set. Also note that <varname>LimitRSS=</varname> is not
+ implemented on Linux, and setting it has no effect. Often it is advisable to prefer the resource
+ controls listed in
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- over these per-process limits, as they apply to services as a whole, may be altered dynamically at runtime, and
- are generally more expressive. For example, <varname>MemoryLimit=</varname> is a more powerful (and working)
- replacement for <varname>LimitRSS=</varname>.</para>
-
- <para>For system units these resource limits may be chosen freely. For user units however (i.e. units run by a
- per-user instance of
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>), these limits are
- bound by (possibly more restrictive) per-user limits enforced by the OS.</para>
+ over these per-process limits, as they apply to services as a whole, may be altered dynamically at
+ runtime, and are generally more expressive. For example, <varname>MemoryMax=</varname> is a more
+ powerful (and working) replacement for <varname>LimitRSS=</varname>.</para>
<para>Resource limits not configured explicitly for a unit default to the value configured in the various
<varname>DefaultLimitCPU=</varname>, <varname>DefaultLimitFSIZE=</varname>, … options available in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, and –
if not configured there – the kernel or per-user defaults, as defined by the OS (the latter only for user
- services, see above).</para>
+ services, see below).</para>
+
+ <para>For system units these resource limits may be chosen freely. When these settings are configured
+ in a user service (i.e. a service run by the per-user instance of the service manager) they cannot be
+ used to raise the limits above those set for the user manager itself when it was first invoked, as
+ the user's service manager generally lacks the privileges to do so. In user context these
+ configuration options are hence only useful to lower the limits passed in or to raise the soft limit
+ to the maximum of the hard limit as configured for the user. To raise the user's limits further, the
+ available configuration mechanisms differ between operating systems, but typically require
+ privileges. In most cases it is possible to configure higher per-user resource limits via PAM or by
+ setting limits on the system service encapsulating the user's service manager, i.e. the user's
+ instance of <filename>user@.service</filename>. After making such changes, make sure to restart the
+ user's service manager.</para>
<table>
<title>Resource limit directives, their equivalent <command>ulimit</command> shell commands and the unit used</title>
<xi:include href="system-only.xml" xpointer="singular"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ProtectClock=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If set, writes to the hardware clock or system clock will be denied.
+ It is recommended to turn this on for most services that do not need modify the clock. Defaults to off. Enabling
+ this option removes <constant>CAP_SYS_TIME</constant> and <constant>CAP_WAKE_ALARM</constant> from the
+ capability bounding set for this unit, installs a system call filter to block calls that can set the
+ clock, and <varname>DeviceAllow=char-rtc r</varname> is implied. This ensures <filename>/dev/rtc0</filename>,
+ <filename>/dev/rtc1</filename>, etc are made read only to the service. See
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for the details about <varname>DeviceAllow=</varname>.</para>
+
+ <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>ProtectKernelTunables=</varname></term>
mappings. Specifically these are the options <varname>PrivateTmp=</varname>,
<varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>, <varname>ProtectHome=</varname>,
<varname>ProtectKernelTunables=</varname>, <varname>ProtectControlGroups=</varname>,
- <varname>ProtectKernelLogs=</varname>, <varname>ReadOnlyPaths=</varname>,
+ <varname>ProtectKernelLogs=</varname>, <varname>ProtectClock=</varname>, <varname>ReadOnlyPaths=</varname>,
<varname>InaccessiblePaths=</varname> and <varname>ReadWritePaths=</varname>.</para></listitem>
</varlistentry>
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>LogNamespace=</varname></term>
+
+ <listitem><para>Run the unit's processes in the specified journal namespace. Expects a short
+ user-defined string identifying the namespace. If not used the processes of the service are run in
+ the default journal namespace, i.e. their log stream is collected and processed by
+ <filename>systemd-journald.service</filename>. If this option is used any log data generated by
+ processes of this unit (regardless if via the <function>syslog()</function>, journal native logging
+ or stdout/stderr logging) is collected and processed by an instance of the
+ <filename>systemd-journald@.service</filename> template unit, which manages the specified
+ namespace. The log data is stored in a data store independent from the default log namespace's data
+ store. See
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for details about journal namespaces.</para>
+
+ <para>Internally, journal namespaces are implemented through Linux mount namespacing and
+ over-mounting the directory that contains the relevant <constant>AF_UNIX</constant> sockets used for
+ logging in the unit's mount namespace. Since mount namespaces are used this setting disconnects
+ propagation of mounts from the unit's processes to the host, similar to how
+ <varname>ReadOnlyPaths=</varname> and similar settings (see above) work. Journal namespaces may hence
+ not be used for services that need to establish mount points on the host.</para>
+
+ <para>When this option is used the unit will automatically gain ordering and requirement dependencies
+ on the two socket units associated with the <filename>systemd-journald@.service</filename> instance
+ so that they are automatically established prior to the unit starting up. Note that when this option
+ is used log output of this service does not appear in the regular
+ <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ output, unless the <option>--namespace=</option> option is used.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>SyslogIdentifier=</varname></term>
marking the log line end.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>_NAMESPACE=</varname></term>
+
+ <listitem><para>If this file was written by a <command>systemd-journald</command> instance managing a
+ journal namespace that is not the default, this field contains the namespace identifier. See
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for details about journal namespaces.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Description</title>
- <para>Network link configuration is performed by the
- <command>net_setup_link</command> udev builtin.</para>
+ <para>A plain ini-style text file that encodes configuration for matching network devices, used by
+ <citerefentry><refentrytitle>systemd-udev</refentrytitle><manvolnum>8</manvolnum></citerefentry> and in
+ particular its <command>net_setup_link</command> builtin. See
+ <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry> for a
+ general description of the syntax.</para>
<para>The link files are read from the files located in the system
network directory <filename>/usr/lib/systemd/network</filename>,
<varlistentry id='type'>
<term><varname>Type=</varname></term>
<listitem>
- <para>A whitespace-separated list of shell-style globs matching the device type, as exposed by
- the udev property <literal>DEVTYPE</literal>. If the list is prefixed with a "!", the test is
- inverted.</para>
+ <para>A whitespace-separated list of shell-style globs matching the device type, as exposed by
+ <command>networkctl status</command>. If the list is prefixed with a "!", the test is inverted.
+ </para>
</listitem>
</varlistentry>
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ReceiveChecksumOffload=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If set to true, the hardware offload for checksumming of ingress
+ network packets is enabled. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>TransmitChecksumOffload=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If set to true, the hardware offload for checksumming of egress
+ network packets is enabled. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>TCPSegmentationOffload=</varname></term>
<listitem>
When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry>
<term><varname>GenericReceiveOffload=</varname></term>
<listitem>
<para>Takes a boolean. If set to true, the Generic Receive Offload (GRO) is enabled.
<para>Takes a integer. Specifies the NIC transmit ring buffer size. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>RxFlowControl=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
+ receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
+ default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>TxFlowControl=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
+ transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
+ default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>AutoNegotiationFlowControl=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
+ advertisements with the connected peer so that the two devices can agree on the ethernet
+ PAUSE configuration. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<term><option>x-systemd.before=</option></term>
<term><option>x-systemd.after=</option></term>
- <listitem><para>Configures a <varname>Before=</varname>
- dependency or <varname>After=</varname> between the created
- mount unit and another systemd unit, such as a mount unit.
+ <listitem><para>In the created mount unit, configures a
+ <varname>Before=</varname> or <varname>After=</varname>
+ dependency on another systemd unit, such as a mount unit.
The argument should be a unit name or an absolute path
to a mount point. This option may be specified more than once.
This option is particularly useful for mount point declarations
for details.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>x-systemd.wanted-by=</option></term>
+ <term><option>x-systemd.required-by=</option></term>
+
+ <listitem><para>In the created mount unit, configures a
+ <varname>WantedBy=</varname> or <varname>RequiredBy=</varname>
+ dependency on another unit. This option may be
+ specified more than once. If this is specified, the normal
+ automatic dependencies on the created mount unit, e.g.,
+ <filename>local-fs.target</filename>, are not automatically
+ created. See <varname>WantedBy=</varname> and <varname>RequiredBy=</varname> in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>x-systemd.requires-mounts-for=</option></term>
</row>
<row>
<entry><constant>sl</constant></entry>
- <entry>serial line IP (slip)</entry>
+ <entry>Serial line IP (slip)</entry>
</row>
<row>
<entry><constant>wl</constant></entry>
<refsect1>
<title>Description</title>
- <para>Network setup is performed by
+ <para>A plain ini-style text file that encodes configuration about a virtual network device, used by
<citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
- </para>
+ See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for a general description of the syntax.</para>
<para>The main Virtual Network Device file must have the extension <filename>.netdev</filename>;
other extensions are ignored. Virtual network devices are created as soon as networkd is
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>DestinationPort=</varname></term>
+ <term><varname>UDPDestinationPort=</varname></term>
<listitem>
<para>Specifies destination port. When UDP encapsulation is selected it's mandotory. Ignored when ip
encapsulation is selected.</para>
<example>
<title>/etc/systemd/network/27-xfrm.netdev</title>
- <programlisting>[Xfrm]
+ <programlisting>[NetDev]
Name=xfrm0
Kind=xfrm
<refsect1>
<title>Description</title>
- <para>Network setup is performed by
+ <para>A plain ini-style text file that encodes network configuration for matching network interfaces,
+ used by
<citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
- </para>
+ See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for a general description of the syntax.</para>
<para>The main network file must have the extension <filename>.network</filename>; other
extensions are ignored. Networks are applied to links whenever the links appear.</para>
<varlistentry>
<term><varname>RequiredForOnline=</varname></term>
<listitem>
- <para>Takes a boolean or operational state. Please see
- <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <para>Takes a boolean or a minimum operational state and an optional maximum operational state.
+ Please see <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for possible operational states. When <literal>yes</literal>, the network is deemed required when
determining whether the system is online when running
<command>systemd-networkd-wait-online</command>. When <literal>no</literal>, the network is ignored
- when checking for online state. When an operational state is set, <literal>yes</literal> is implied,
- and this controls the operational state required for the network interface to be considered online.
+ when checking for online state. When a minimum operational state and an optional maximum operational
+ state are set, <literal>yes</literal> is implied, and this controls the minimum and maximum
+ operational state required for the network interface to be considered online.
Defaults to <literal>yes</literal>.</para>
<para>The network will be brought up normally in all cases, but in
<varlistentry>
<term><varname>IPv6Token=</varname></term>
<listitem>
- <para>An IPv6 address with the top 64 bits unset. When set, indicates the
- 64-bit interface part of SLAAC IPv6 addresses for this link. Note that
- the token is only ever used for SLAAC, and not for DHCPv6 addresses, even
- in the case DHCP is requested by router advertisement. By default, the
- token is autogenerated.</para>
+ <para>Specifies an optional address generation mode and a required IPv6 address. If
+ the mode is present, the two parts must be separated with a colon
+ <literal><replaceable>mode</replaceable>:<replaceable>address</replaceable></literal>. The
+ address generation mode may be either <constant>prefixstable</constant> or
+ <constant>static</constant>. If not specified, <constant>static</constant> is assumed.
+ </para>
+ <para>When the mode is set to <constant>static</constant>, or unspecified, the lower bits of
+ the supplied address are combined with the upper bits of a prefix received in a Router Advertisement
+ message to form a complete address. Note that if multiple prefixes are received in an RA message, or in
+ multiple RA messages, addresses will be formed from each of them using the supplied address. This
+ mode implements SLAAC but uses a static interface identifier instead of an identifier generated
+ using the EUI-64 algorithm. Because the interface identifier is static, if Duplicate Address Detection
+ detects that the computed address is a duplicate (in use by another node on the link), then this
+ mode will fail to provide an address for that prefix.
+ </para>
+ <para>When the mode is set to <literal>prefixstable</literal> the RFC 7217 algorithm for generating
+ interface identifiers will be used, but only when a prefix received in an RA message matches the supplied address.
+ See <ulink url="https://tools.ietf.org/html/rfc7217">RFC 7217</ulink>. Prefix matching will be attempted
+ against each <constant>prefixstable</constant> IPv6Token variable provided in the configuration; if a received
+ prefix does not match any of the provided addresses, then the EUI-64 algorithm will be used to form
+ an interface identifier for that prefix. This mode is also SLAAC, but with a potentially stable interface
+ identifier which does not directly map to the interface's hardware address.
+
+ Note that the <constant>prefixstable</constant> algorithm includes both the interface's name and
+ MAC address in the hash used to compute the interface identifier, so if either of those are changed the resulting
+ interface identifier (and address) will change, even if the prefix received in the RA message has not changed.
+
+ Note that if multiple <constant>prefixstable</constant> IPv6Token variables are supplied with addresses that
+ match a prefix received in an RA message, only the first one will be used to generate addresses.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>Domains=</varname></term>
<listitem>
- <para>A list of domains which should be resolved using the DNS servers on this link. Each item in the list
- should be a domain name, optionally prefixed with a tilde (<literal>~</literal>). The domains with the
- prefix are called "routing-only domains". The domains without the prefix are called "search domains" and
- are first used as search suffixes for extending single-label host names (host names containing no dots) to
- become fully qualified domain names (FQDNs). If a single-label host name is resolved on this interface,
- each of the specified search domains are appended to it in turn, converting it into a fully qualified
- domain name, until one of them may be successfully resolved.</para>
+ <para>A whitespace-separated list of domains which should be resolved using the DNS servers on
+ this link. Each item in the list should be a domain name, optionally prefixed with a tilde
+ (<literal>~</literal>). The domains with the prefix are called "routing-only domains". The
+ domains without the prefix are called "search domains" and are first used as search suffixes for
+ extending single-label host names (host names containing no dots) to become fully qualified
+ domain names (FQDNs). If a single-label host name is resolved on this interface, each of the
+ specified search domains are appended to it in turn, converting it into a fully qualified domain
+ name, until one of them may be successfully resolved.</para>
<para>Both "search" and "routing-only" domains are used for routing of DNS queries: look-ups for host names
ending in those domains (hence also single label names, if any "search domains" are listed), are routed to
</varlistentry>
<varlistentry>
<term><varname>IPv6AcceptRA=</varname></term>
- <listitem><para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support
- for the interface. If true, RAs are accepted; if false, RAs are ignored, independently of the
- local forwarding state. When RAs are accepted, they may trigger the start of the DHCPv6
- client if the relevant flags are set in the RA data, or if no routers are found on the link.</para>
+ <listitem><para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the
+ interface. If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they may
+ trigger the start of the DHCPv6 client if the relevant flags are set in the RA data, or if no
+ routers are found on the link. The default is to disable RA reception for bridge devices or when IP
+ forwarding is enabled, and to enable it otherwise. Cannot be enabled on bond devices and when link
+ local adressing is disabled.</para>
<para>Further settings for the IPv6 RA support may be configured in the
<literal>[IPv6AcceptRA]</literal> section, see below.</para>
<varlistentry>
<term><varname>InvertRule=</varname></term>
<listitem>
- <para>A boolean. Specifies whether the rule to be inverted. Defaults to false.</para>
+ <para>A boolean. Specifies whether the rule is to be inverted. Defaults to false.</para>
</listitem>
</varlistentry>
<varlistentry>
<literal>ipv4</literal>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>User=</varname></term>
+ <listitem>
+ <para>Takes a username, a user ID, or a range of user IDs separated by a dash. Defaults to
+ unset.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>SuppressPrefixLength=</varname></term>
+ <listitem>
+ <para>Takes a number <replaceable>N</replaceable> in the range 0-128 and rejects routing
+ decisions that have a prefix length of <replaceable>N</replaceable> or less. Defaults to
+ unset.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<varlistentry>
<term><varname>Gateway=</varname></term>
<listitem>
- <para>Takes the gateway address or special value <literal>dhcp</literal>. If
- <literal>dhcp</literal>, then the gateway address provided by DHCP (or in the IPv6 case,
+ <para>Takes the gateway address or special value <literal>_dhcp</literal>. If
+ <literal>_dhcp</literal>, then the gateway address provided by DHCP (or in the IPv6 case,
provided by IPv6 RA) is used.</para>
</listitem>
</varlistentry>
<para>Note that this configuration will overwrite others.
In concrete, the following variables will be ignored:
<varname>SendHostname=</varname>, <varname>ClientIdentifier=</varname>,
- <varname>UseRoutes=</varname>, <varname>SendHostname=</varname>,
- <varname>UseMTU=</varname>, <varname>VendorClassIdentifier=</varname>,
- <varname>UseTimezone=</varname>.</para>
+ <varname>UseRoutes=</varname>, <varname>UseMTU=</varname>,
+ <varname>VendorClassIdentifier=</varname>, <varname>UseTimezone=</varname>.</para>
<para>With this option enabled DHCP requests will mimic those generated by Microsoft Windows, in
order to reduce the ability to fingerprint and recognize installations. This means DHCP request
a prefix-hint in the DHCPv6 solicitation. Prefix ranges 1-128. Defaults to unset.</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>WithoutRA=</varname></term>
+ <listitem>
+ <para>When true, DHCPv6 client starts without router advertisements's managed or other address configuration flag.
+ Defaults to false.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>DHCPv6Client=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When true (the default), the DHCPv6 client will be started when the
+ RA has the managed or other information flag.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
<term><varname>EmitDNS=</varname></term>
<term><varname>DNS=</varname></term>
- <listitem><para><varname>DNS=</varname> specifies a list of recursive
- DNS server IPv6 addresses that distributed via Router Advertisement
- messages when <varname>EmitDNS=</varname> is true. If <varname>DNS=
- </varname> is empty, DNS servers are read from the
- <literal>[Network]</literal> section. If the
- <literal>[Network]</literal> section does not contain any DNS servers
- either, DNS servers from the uplink with the highest priority default
- route are used. When <varname>EmitDNS=</varname> is false, no DNS server
- information is sent in Router Advertisement messages.
+ <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses
+ that are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
+ true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that
+ case the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS
+ servers are read from the <literal>[Network]</literal> section. If the
+ <literal>[Network]</literal> section does not contain any DNS servers either, DNS servers from
+ the uplink with the highest priority default route are used. When <varname>EmitDNS=</varname>
+ is false, no DNS server information is sent in Router Advertisement messages.
<varname>EmitDNS=</varname> defaults to true.
</para></listitem>
</varlistentry>
to 2592000 seconds (30 days).</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Assign=</varname></term>
+ <listitem><para>Takes a boolean. When true, adds an address from the prefix. Default to false.
+ </para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
automatic restart off. By default automatic restart is disabled.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Termination=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, the termination resistor will be selected for
+ the bias network. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>TripleSampling=</varname></term>
<listitem>
</refsect1>
<refsect1>
- <title>[TrafficControlQueueingDiscipline] Section Options</title>
- <para>The <literal>[TrafficControlQueueingDiscipline]</literal> section manages the Traffic control. It can be used
- to configure the kernel packet scheduler and simulate packet delay and loss for UDP or TCP applications,
- or limit the bandwidth usage of a particular service to simulate internet connections.</para>
+ <title>[QDisc] Section Options</title>
+ <para>The <literal>[QDisc]</literal> section manages the traffic control queueing discipline (qdisc).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>clsact</literal>
+ or <literal>ingress</literal>. This is mandatory.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[NetworkEmulator] Section Options</title>
+ <para>The <literal>[NetworkEmulator]</literal> section manages the queueing discipline (qdisc) of
+ the network emulator. It can be used to configure the kernel packet scheduler and simulate packet
+ delay and loss for UDP or TCP applications, or limit the bandwidth usage of a particular service to
+ simulate internet connections.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
- <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>NetworkEmulatorDelaySec=</varname></term>
+ <term><varname>DelaySec=</varname></term>
<listitem>
<para>Specifies the fixed amount of delay to be added to all packets going out of the
interface. Defaults to unset.</para>
</varlistentry>
<varlistentry>
- <term><varname>NetworkEmulatorDelayJitterSec=</varname></term>
+ <term><varname>DelayJitterSec=</varname></term>
<listitem>
<para>Specifies the chosen delay to be added to the packets outgoing to the network
interface. Defaults to unset.</para>
</varlistentry>
<varlistentry>
- <term><varname>NetworkEmulatorPacketLimit=</varname></term>
+ <term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the maximum number of packets the qdisc may hold queued at a time.
An unsigned integer ranges 0 to 4294967294. Defaults to 1000.</para>
</varlistentry>
<varlistentry>
- <term><varname>NetworkEmulatorLossRate=</varname></term>
+ <term><varname>LossRate=</varname></term>
<listitem>
<para>Specifies an independent loss probability to be added to the packets outgoing from the
network interface. Takes a percentage value, suffixed with "%". Defaults to unset.</para>
</varlistentry>
<varlistentry>
- <term><varname>NetworkEmulatorDuplicateRate=</varname></term>
+ <term><varname>DuplicateRate=</varname></term>
<listitem>
<para>Specifies that the chosen percent of packets is duplicated before queuing them.
Takes a percentage value, suffixed with "%". Defaults to unset.</para>
</listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[TokenBucketFilter] Section Options</title>
+ <para>The <literal>[TokenBucketFilter]</literal> section manages the queueing discipline (qdisc) of
+ token bucket filter (tbf).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
- <term><varname>TokenBufferFilterLatencySec=</varname></term>
+ <term><varname>LatencySec=</varname></term>
<listitem>
<para>Specifies the latency parameter, which specifies the maximum amount of time a
- packet can sit in the Token Buffer Filter (TBF). Defaults to unset.</para>
+ packet can sit in the Token Bucket Filter (TBF). Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>TokenBufferFilterLimitSize=</varname></term>
+ <term><varname>LimitSize=</varname></term>
<listitem>
<para>Takes the number of bytes that can be queued waiting for tokens to become available.
When the size is suffixed with K, M, or G, it is parsed as Kilobytes, Megabytes, or Gigabytes,
</varlistentry>
<varlistentry>
- <term><varname>TokenBufferFilterBurst=</varname></term>
+ <term><varname>Burst=</varname></term>
<listitem>
<para>Specifies the size of the bucket. This is the maximum amount of bytes that tokens
can be available for instantaneous transfer. When the size is suffixed with K, M, or G, it is
</varlistentry>
<varlistentry>
- <term><varname>TokenBufferFilterRate=</varname></term>
+ <term><varname>Rate=</varname></term>
<listitem>
<para>Specifies the device specific bandwidth. When suffixed with K, M, or G, the specified
bandwidth is parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000.
</varlistentry>
<varlistentry>
- <term><varname>TokenBufferFilterMPUBytes=</varname></term>
+ <term><varname>MPUBytes=</varname></term>
<listitem>
<para>The Minimum Packet Unit (MPU) determines the minimal token usage (specified in bytes)
for a packet. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
</varlistentry>
<varlistentry>
- <term><varname>TokenBufferFilterPeakRate=</varname></term>
+ <term><varname>PeakRate=</varname></term>
<listitem>
<para>Takes the maximum depletion rate of the bucket. When suffixed with K, M, or G, the
specified size is parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of
</varlistentry>
<varlistentry>
- <term><varname>TokenBufferFilterMTUBytes=</varname></term>
+ <term><varname>MTUBytes=</varname></term>
<listitem>
<para>Specifies the size of the peakrate bucket. When suffixed with K, M, or G, the specified
size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000.
Defaults to unset.</para>
</listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[StochasticFairBlue] Section Options</title>
+ <para>The <literal>[StochasticFairBlue]</literal> section manages the queueing discipline
+ (qdisc) of stochastic fair blue (sfb).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
- <term><varname>StochasticFairnessQueueingPerturbPeriodSec=</varname></term>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PacketLimit=</varname></term>
+ <listitem>
+ <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
+ dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[StochasticFairnessQueueing] Section Options</title>
+ <para>The <literal>[StochasticFairnessQueueing]</literal> section manages the queueing discipline
+ (qdisc) of stochastic fairness queueing (sfq).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PerturbPeriodSec=</varname></term>
<listitem>
<para>Specifies the interval in seconds for queue algorithm perturbation. Defaults to unset.</para>
</listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[PFIFO] Section Options</title>
+ <para>The <literal>[PFIFO]</literal> section manages the queueing discipline (qdisc) of
+ Packet First In First Out (pfifo).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PacketLimit=</varname></term>
+ <listitem>
+ <para>Specifies the hard limit on the FIFO size in number of packets. The size limit (a buffer size) to prevent it
+ from overflowing in case it is unable to dequeue packets as quickly as it receives them. When this limit is reached,
+ incoming packets are dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[CAKE] Section Options</title>
+ <para>The <literal>[CAKE]</literal> section manages the queueing discipline (qdisc) of
+ Common Applications Kept Enhanced (CAKE).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Overhead=</varname></term>
+ <listitem>
+ <para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative.
+ Takes an integer ranges -64 to 256. Defaults to unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Bandwidth=</varname></term>
+ <listitem>
+ <para>Specifies the shaper bandwidth. When suffixed with K, M, or G, the specified size is
+ parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000. Defaults to
+ unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[ControlledDelay] Section Options</title>
+ <para>The <literal>[ControlledDelay]</literal> section manages the queueing discipline (qdisc) of
+ controlled delay (CoDel).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
- <term><varname>ControlledDelayPacketLimit=</varname></term>
+ <term><varname>Handle=</varname></term>
<listitem>
- <para>Specifies the hard lmit on the queue size in number of packets. When this limit is reached, incoming packets are
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PacketLimit=</varname></term>
+ <listitem>
+ <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>ControlledDelayTargetSec=</varname></term>
+ <term><varname>TargetSec=</varname></term>
<listitem>
<para>Takes a timespan. Specifies the acceptable minimum standing/persistent queue delay.
Defaults to unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>ControlledDelayIntervalSec=</varname></term>
+ <term><varname>IntervalSec=</varname></term>
<listitem>
<para>Takes a timespan. This is used to ensure that the measured minimum delay does not
become too stale. Defaults to unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>ControlledDelayECN=</varname></term>
+ <term><varname>ECN=</varname></term>
<listitem>
<para>Takes a boolean. This can be used to mark packets instead of dropping them. Defaults to
unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>ControlledDelayCEThresholdSec=</varname></term>
+ <term><varname>CEThresholdSec=</varname></term>
<listitem>
<para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1>
+ <title>[GenericRandomEarlyDetection] Section Options</title>
+ <para>The <literal>[GenericRandomEarlyDetection]</literal> section manages the queueing discipline
+ (qdisc) of Generic Random Early Detection (GRED).</para>
+
+ <variablelist class='network-directives'>
<varlistentry>
- <term><varname>FairQueuingControlledDelayPacketLimit=</varname></term>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>VirtualQueues=</varname></term>
+ <listitem>
+ <para>Specifies the number of virtual queues. Takes a integer in the range 1-16. Defaults to unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>DefaultVirtualQueue=</varname></term>
+ <listitem>
+ <para>Specifies the number of default virtual queue. This must be less than <varname>VirtualQueue=</varname>.
+ Defaults to unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>GenericRIO=</varname></term>
+ <listitem>
+ <para>Takes a boolean. It turns on the RIO-like buffering scheme. Defaults to
+ unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[FairQueueingControlledDelay] Section Options</title>
+ <para>The <literal>[FairQueueingControlledDelay]</literal> section manages the queueing discipline
+ (qdisc) of fair queuing controlled delay (FQ-CoDel).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the real queue size. When this limit is reached, incoming packets are
dropped. Defaults to unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueuingControlledDelayMemoryLimit=</varname></term>
+ <term><varname>MemoryLimit=</varname></term>
<listitem>
<para>Specifies the limit on the total number of bytes that can be queued in this FQ-CoDel instance.
When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
</varlistentry>
<varlistentry>
- <term><varname>FairQueuingControlledDelayFlows=</varname></term>
+ <term><varname>Flows=</varname></term>
<listitem>
<para>Specifies the number of flows into which the incoming packets are classified.
Defaults to unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueuingControlledDelayTargetSec=</varname></term>
+ <term><varname>TargetSec=</varname></term>
<listitem>
<para>Takes a timespan. Specifies the acceptable minimum standing/persistent queue delay.
Defaults to unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueuingControlledDelayIntervalSec=</varname></term>
+ <term><varname>IntervalSec=</varname></term>
<listitem>
<para>Takes a timespan. This is used to ensure that the measured minimum delay does not
become too stale. Defaults to unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueuingControlledDelayQuantum=</varname></term>
+ <term><varname>Quantum=</varname></term>
<listitem>
<para>Specifies the number of bytes used as 'deficit' in the fair queuing algorithmtimespan.
When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
</varlistentry>
<varlistentry>
- <term><varname>FairQueuingControlledDelayECN=</varname></term>
+ <term><varname>ECN=</varname></term>
<listitem>
<para>Takes a boolean. This can be used to mark packets instead of dropping them. Defaults to
unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueuingControlledDelayCEThresholdSec=</varname></term>
+ <term><varname>CEThresholdSec=</varname></term>
<listitem>
<para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[FairQueueing] Section Options</title>
+ <para>The <literal>[FairQueueing]</literal> section manages the queueing discipline
+ (qdisc) of fair queue traffic policing (FQ).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingPacketLimit=</varname></term>
+ <term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the real queue size. When this limit is reached, incoming packets are
dropped. Defaults to unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingFlowLimit=</varname></term>
+ <term><varname>FlowLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the maximum number of packets queued per flow. Defaults to
unset and kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingQuantum=</varname></term>
+ <term><varname>Quantum=</varname></term>
<listitem>
<para>Specifies the credit per dequeue RR round, i.e. the amount of bytes a flow is allowed
to dequeue at once. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingInitialQuantum=</varname></term>
+ <term><varname>InitialQuantum=</varname></term>
<listitem>
<para>Specifies the initial sending rate credit, i.e. the amount of bytes a new flow is
allowed to dequeue initially. When suffixed with K, M, or G, the specified size is parsed as
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingMaximumRate=</varname></term>
+ <term><varname>MaximumRate=</varname></term>
<listitem>
<para>Specifies the maximum sending rate of a flow. When suffixed with K, M, or G, the
specified size is parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingBuckets=</varname></term>
+ <term><varname>Buckets=</varname></term>
<listitem>
<para>Specifies the size of the hash table used for flow lookups. Defaults to unset and
kernel's default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingOrphanMask=</varname></term>
+ <term><varname>OrphanMask=</varname></term>
<listitem>
<para>Takes an unsigned integer. For packets not owned by a socket, fq is able to mask a part
of hash and reduce number of buckets associated with the traffic. Defaults to unset and
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingPacing=</varname></term>
+ <term><varname>Pacing=</varname></term>
<listitem>
<para>Takes a boolean, and enables or disables flow pacing. Defaults to unset and kernel's
default is used.</para>
</varlistentry>
<varlistentry>
- <term><varname>FairQueueTrafficPolicingCEThresholdSec=</varname></term>
+ <term><varname>CEThresholdSec=</varname></term>
<listitem>
<para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[TrivialLinkEqualizer] Section Options</title>
+ <para>The <literal>[TrivialLinkEqualizer]</literal> section manages the queueing discipline (qdisc) of
+ trivial link equalizer (teql).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Id=</varname></term>
+ <listitem>
+ <para>Specifies the interface ID <literal>N</literal> of teql. Defaults to <literal>0</literal>.
+ Note that when teql is used, currently, the module <constant>sch_teql</constant> with
+ <constant>max_equalizers=N+1</constant> option must be loaded before
+ <command>systemd-networkd</command> is started.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[HierarchyTokenBucket] Section Options</title>
+ <para>The <literal>[HierarchyTokenBucket]</literal> section manages the queueing discipline (qdisc) of
+ hierarchy token bucket (htb).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+ major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+ (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Handle=</varname></term>
+ <listitem>
+ <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
+ Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>DefaultClass=</varname></term>
+ <listitem>
+ <para>Takes the minor id in hexadecimal of the default class. Unclassified traffic gets sent
+ to the class. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[HierarchyTokenBucketClass] Section Options</title>
+ <para>The <literal>[HierarchyTokenBucketClass]</literal> section manages the traffic control class of
+ hierarchy token bucket (htb).</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Parent=</varname></term>
+ <listitem>
+ <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+ or a qdisc id. The qdisc id takes the major and minor number in hexadecimal ranges 1 to ffff
+ separated with a colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ClassId=</varname></term>
+ <listitem>
+ <para>Specifies the major and minur number of unique identifier of the class, known as the
+ class ID. Each number is in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Priority=</varname></term>
+ <listitem>
+ <para>Specifies the priority of the class. In the round-robin process, classes with the lowest
+ priority field are tried for packets first. This setting is mandatory.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Rate=</varname></term>
+ <listitem>
+ <para>Specifies the maximum rate this class and all its children are guaranteed. When suffixed
+ with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits, respectively,
+ to the base of 1000. This setting is mandatory.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>CeilRate=</varname></term>
+ <listitem>
+ <para>Specifies the maximum rate at which a class can send, if its parent has bandwidth to spare.
+ When suffixed with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits,
+ respectively, to the base of 1000. When unset, the value specified with <varname>Rate=</varname>
+ is used.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
limitations as inotify, and for example cannot be used to monitor
files or directories changed by other machines on remote NFS file
systems.</para>
+
+ <para>When a service unit triggered by a path unit terminates (regardless whether it exited successfully
+ or failed), monitored paths are checked immediately again, and the service accordingly restarted
+ instantly. As protection against busy looping in this trigger/start cycle, a start rate limit is enforced
+ on the service unit, see <varname>StartLimitIntervalSec=</varname> and
+ <varname>StartLimitBurst=</varname> in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Unlike
+ other service failures, the error condition that the start rate limit is hit is propagated from the
+ service unit to the path unit and causes the path unit to fail as well, thus ending the loop.</para>
</refsect1>
<refsect1>
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
-<refentry id="systemd.resource-control">
+<refentry id="systemd.resource-control" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd.resource-control</title>
<productname>systemd</productname>
<refsect1>
<title>Unified and Legacy Control Group Hierarchies</title>
- <para>The unified control group hierarchy is the new version of kernel control group interface, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>. Depending on the resource type,
- there are differences in resource control capabilities. Also, because of interface changes, some resource types
- have separate set of options on the unified hierarchy.</para>
+ <para>The unified control group hierarchy is the new version of kernel control group interface, see
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink>.
+ Depending on the resource type, there are differences in resource control capabilities. Also, because of
+ interface changes, some resource types have separate set of options on the unified hierarchy.</para>
<para>
<variablelist>
application.</para>
<para>Legacy control group hierarchy (see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt">cgroups.txt</ulink>), also called cgroup-v1,
- doesn't allow safe delegation of controllers to unprivileged processes. If the system uses the legacy control group
- hierarchy, resource control is disabled for systemd user instance, see
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
- </para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/">Control Groups version 1</ulink>),
+ also called cgroup-v1, doesn't allow safe delegation of controllers to unprivileged processes. If the
+ system uses the legacy control group hierarchy, resource control is disabled for the systemd user
+ instance, see
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
is used on the system. These options take an integer value and control the <literal>cpu.weight</literal>
control group attribute. The allowed range is 1 to 10000. Defaults to 100. For details about this control
group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink> and <ulink
- url="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink> and <ulink
+ url="https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html">CFS Scheduler</ulink>.
The available CPU time is split up among all units within one slice relative to their CPU time weight.</para>
<para>While <varname>StartupCPUWeight=</varname> only applies to the startup phase of the system,
available on one CPU. Use values > 100% for allotting CPU time on more than one CPU. This controls the
<literal>cpu.max</literal> attribute on the unified control group hierarchy and
<literal>cpu.cfs_quota_us</literal> on legacy. For details about these control group attributes, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink> and <ulink
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink> and <ulink
url="https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt">sched-bwc.txt</ulink>.</para>
<para>Example: <varname>CPUQuota=20%</varname> ensures that the executed processes will never get more than
<para>This controls the second field of <literal>cpu.max</literal> attribute on the unified control group hierarchy
and <literal>cpu.cfs_period_us</literal> on legacy. For details about these control group attributes, see
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink> and
- <ulink url="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.</para>
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink> and
+ <ulink url="https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html">CFS Scheduler</ulink>.</para>
<para>Example: <varname>CPUQuotaPeriodSec=10ms</varname> to request that the CPU quota is measured in periods of 10ms.</para>
</listitem>
useful in order to always inherit all of the protection afforded by ancestors.
This controls the <literal>memory.min</literal> control group attribute. For details about this
control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
useful in order to always inherit all of the protection afforded by ancestors.
This controls the <literal>memory.low</literal> control group attribute. For details about this
control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
system. If assigned the
special value <literal>infinity</literal>, no memory throttling is applied. This controls the
<literal>memory.high</literal> control group attribute. For details about this control group attribute, see
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
percentage value may be specified, which is taken relative to the installed physical memory on the system. If
assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
<literal>memory.max</literal> control group attribute. For details about this control group attribute, see
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>This setting replaces <varname>MemoryLimit=</varname>.</para>
</listitem>
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
special value <literal>infinity</literal>, no swap limit is applied. This controls the
<literal>memory.swap.max</literal> control group attribute. For details about this control group attribute,
- see <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ see <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
<para>This setting is supported only if the unified control group hierarchy is used and disables
<varname>MemoryLimit=</varname>.</para>
of tasks or a percentage value that is taken relative to the configured maximum number of tasks on the
system. If assigned the special value <literal>infinity</literal>, no tasks limit is applied. This controls
the <literal>pids.max</literal> control group attribute. For details about this control group attribute, see
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt">pids.txt</ulink>.</para>
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/pids.html">Process Number Controller</ulink>.
+ </para>
- <para>The
- system default for this setting may be controlled with
+ <para>The system default for this setting may be controlled with
<varname>DefaultTasksMax=</varname> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
hierarchy is used on the system. Takes a single weight value (between 1 and 10000) to set the default block
I/O weight. This controls the <literal>io.weight</literal> control group attribute, which defaults to
100. For details about this control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>. The available I/O
- bandwidth is split up among all units within one slice relative to their block I/O weight.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
+ The available I/O bandwidth is split up among all units within one slice relative to their block
+ I/O weight.</para>
<para>While <varname>StartupIOWeight=</varname> only applies
to the startup phase of the system,
device of the file system of the file is determined. This controls the <literal>io.weight</literal> control
group attribute, which defaults to 100. Use this option multiple times to set weights for multiple devices.
For details about this control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.</para>
<para>This setting replaces <varname>BlockIODeviceWeight=</varname> and disables settings prefixed with
<varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
+
+ <para>The specified device node should reference a block device that has an I/O scheduler
+ associated, i.e. should not refer to partition or loopback block devices, but to the originating,
+ physical device. When a path to a regular file or directory is specified it is attempted to
+ discover the correct originating device backing the file system of the specified path. This works
+ correctly only for simpler cases, where the file system is directly placed on a partition or
+ physical block device, or where simple 1:1 encryption using dm-crypt/LUKS is used. This discovery
+ does not cover complex storage and in particular RAID and volume management storage devices.</para>
</listitem>
</varlistentry>
"/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 5M"). This controls the <literal>io.max</literal> control
group attributes. Use this option multiple times to set bandwidth limits for multiple devices. For details
about this control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
</para>
<para>These settings replace <varname>BlockIOReadBandwidth=</varname> and
<varname>BlockIOWriteBandwidth=</varname> and disable settings prefixed with <varname>BlockIO</varname> or
<varname>StartupBlockIO</varname>.</para>
+
+ <para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
</listitem>
</varlistentry>
"/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 1K"). This controls the <literal>io.max</literal> control
group attributes. Use this option multiple times to set IOPS limits for multiple devices. For details about
this control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
</para>
<para>These settings are supported only if the unified control group hierarchy is used and disable settings
prefixed with <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
+
+ <para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
</listitem>
</varlistentry>
system of the file is determined. This controls the <literal>io.latency</literal> control group
attribute. Use this option multiple times to set latency target for multiple devices. For details about this
control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.</para>
<para>Implies <literal>IOAccounting=yes</literal>.</para>
<para>These settings are supported only if the unified control group hierarchy is used.</para>
+
+ <para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
</listitem>
</varlistentry>
(<emphasis>m</emphasis>knod), respectively. On cgroup-v1 this controls the
<literal>devices.allow</literal> control group attribute. For details about this control group
attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v1/devices.txt">devices.txt</ulink>. On
- cgroup-v2 this functionality is implemented using eBPF filtering.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/devices.html">Device Whitelist Controller</ulink>.
+ In the unified cgroup hierarchy this functionality is implemented using eBPF filtering.</para>
<para>The device node specifier is either a path to a device node in the file system, starting with
<filename>/dev/</filename>, or a string starting with either <literal>char-</literal> or
hierarchy. Accordingly, access to the specified controllers will not be granted to unprivileged services on
the legacy hierarchy, even when requested.</para>
- <para>The following controller names may be specified: <option>cpu</option>, <option>cpuacct</option>,
- <option>io</option>, <option>blkio</option>, <option>memory</option>, <option>devices</option>,
- <option>pids</option>. Not all of these controllers are available on all kernels however, and some are
+ <xi:include href="supported-controllers.xml" xpointer="controllers-text" />
+
+ <para>Not all of these controllers are available on all kernels however, and some are
specific to the unified hierarchy while others are specific to the legacy hierarchy. Also note that the
kernel might support further controllers, which aren't covered here yet as delegation is either not supported
at all for them or not defined cleanly.</para>
to disable. Passing <varname>DisableControllers=</varname> by itself with no controller name present resets
the disabled controller list.</para>
- <para>Valid controllers are <option>cpu</option>, <option>cpuacct</option>, <option>io</option>,
- <option>blkio</option>, <option>memory</option>, <option>devices</option>, and <option>pids</option>.</para>
+ <xi:include href="supported-controllers.xml" xpointer="controllers-text" />
</listitem>
</varlistentry>
</variablelist>
<para>Assign the specified CPU time share weight to the processes executed. These options take an integer
value and control the <literal>cpu.shares</literal> control group attribute. The allowed range is 2 to
262144. Defaults to 1024. For details about this control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.
+ url="https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html">CFS Scheduler</ulink>.
The available CPU time is split up among all units within one slice relative to their CPU time share
weight.</para>
<literal>infinity</literal>, no memory limit is applied. This controls the
<literal>memory.limit_in_bytes</literal> control group attribute. For details about this control group
attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/memory.html">Memory Resource Controller</ulink>.</para>
<para>Implies <literal>MemoryAccounting=yes</literal>.</para>
group hierarchy is used on the system. Takes a single weight value (between 10 and 1000) to set the default
block I/O weight. This controls the <literal>blkio.weight</literal> control group attribute, which defaults to
500. For details about this control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.
The available I/O bandwidth is split up among all units within one slice relative to their block I/O
weight.</para>
file system of the file is determined. This controls the <literal>blkio.weight_device</literal> control group
attribute, which defaults to 1000. Use this option multiple times to set weights for multiple devices. For
details about this control group attribute, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.</para>
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.</para>
<para>Implies
<literal>BlockIOAccounting=yes</literal>.</para>
<literal>blkio.throttle.read_bps_device</literal> and <literal>blkio.throttle.write_bps_device</literal>
control group attributes. Use this option multiple times to set bandwidth limits for multiple devices. For
details about these control group attributes, see <ulink
- url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.
+ url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.
</para>
<para>Implies
<citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
The documentation for control groups and specific controllers in the Linux kernel:
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt">cgroups.txt</ulink>,
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt">cpuacct.txt</ulink>,
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>,
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.
- <ulink url="https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt">sched-bwc.txt</ulink>.
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink>.
</para>
</refsect1>
</refentry>
url="https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/">New
Control Group Interfaces</ulink> for an introduction on how to make
use of scope units from programs.</para>
+
+ <para>Note that, unlike service units, scope units have no "main" process: all processes in the scope are
+ equivalent. The lifecycle of the scope unit is thus not bound to the lifetime of one specific process,
+ but to the existence of at least one process in the scope. This also means that the exit statuses of
+ these processes are not relevant for the scope unit failure state. Scope units may still enter a failure
+ state, for example due to resource exhaustion or stop timeouts being reached, but not due to programs
+ inside of them terminating uncleanly. Since processes managed as scope units generally remain children of
+ the original process that forked them off, it is also the job of that process to collect their exit
+ statuses and act on them as needed.</para>
</refsect1>
<refsect1>
<filename>cryptsetup-pre.target</filename>,
<filename>cryptsetup.target</filename>,
<filename>ctrl-alt-del.target</filename>,
+ <filename>blockdev@.target</filename>,
<filename>boot-complete.target</filename>,
<filename>default.target</filename>,
<filename>emergency.target</filename>,
not useful as only unit within a transaction.</para>
<variablelist>
+ <varlistentry>
+ <term><filename>blockdev@.target</filename></term>
+ <listitem><para>This template unit is used to order mount units and other consumers of block
+ devices after services that synthesize these block devices. In particular, this is intended to be
+ used with storage services (such as
+ <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
+ that allocate and manage a virtual block device. Storage services are ordered before an instance of
+ <filename>blockdev@.target</filename>, and the consumer units after it. The ordering is
+ particularly relevant during shutdown, as it ensures that the mount is deactivated first and the
+ service backing the mount later. The <filename>blockdev@.target</filename> instance should be
+ pulled in via a <option>Wants=</option> dependency of the storage daemon and thus generally not be
+ part of any transaction unless a storage daemon is used. The instance name for instances of this
+ template unit must be a properly escaped block device node path, e.g.
+ <filename>blockdev@dev-mapper-foobar.target</filename> for the storage device
+ <filename>/dev/mapper/foobar</filename>.</para></listitem>
+ </varlistentry>
<varlistentry>
<term><filename>cryptsetup-pre.target</filename></term>
<listitem>
<citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry>
</para></listitem>
+ <listitem><para>link files, see
+ <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ </para></listitem>
+
+ <listitem><para>netdev and network files, see
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ </para></listitem>
+
<listitem><para>daemon config files, see
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-user.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
timers defined in the other directives.</para>
<para>These are monotonic timers, independent of wall-clock time and timezones. If the computer is
- temporarily suspended, the monotonic clock pauses, too.</para>
+ temporarily suspended, the monotonic clock generally pauses, too. Note that if
+ <varname>WakeSystem=</varname> is used, a different monotonic clock is selected that continues to
+ advance while the system is suspended and thus can be used as the trigger to resume the
+ system.</para>
<para>If the empty string is assigned to any of these options, the list of timers is reset (both
monotonic timers and <varname>OnCalendar=</varname> timers, see below), and all prior assignments
<citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for details. To optimize power consumption, make sure to set
this value as high as possible and as low as
- necessary.</para></listitem>
+ necessary.</para>
+
+ <para>Note that this setting is primarily a power saving option that allows coalescing CPU
+ wake-ups. It should not be confused with <varname>RandomizedDelaySec=</varname> (see below) which
+ adds a random value to the time the timer shall elapse next and whose purpose is the opposite: to
+ stretch elapsing of timer events over a longer period to reduce workload spikes. For further details
+ and explanations and how both settings play together, see below.</para></listitem>
</varlistentry>
<varlistentry>
<varname>false</varname>.</para>
<para>Note that this functionality requires privileges and is thus generally only available in the
- system service manager.</para></listitem>
+ system service manager.</para>
+
+ <para>Note that behaviour of monotonic clock timers (as configured with
+ <varname>OnActiveSec=</varname>, <varname>OnBootSec=</varname>, <varname>OnStartupSec=</varname>,
+ <varname>OnUnitActiveSec=</varname>, <varname>OnUnitInactiveSec=</varname>, see above) is altered
+ depending on this option. If false, a monotonic clock is used that is paused during system suspend
+ (<constant>CLOCK_MONOTONIC</constant>), if true a different monotonic clock is used that continues
+ advancing during system suspend (<constant>CLOCK_BOOTTIME</constant>), see
+ <citerefentry><refentrytitle>clock_getres</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+ details.</para></listitem>
</varlistentry>
<varlistentry>
<para>Units listed in this option will be started if the configuring unit is. However, if the listed
units fail to start or cannot be added to the transaction, this has no impact on the validity of the
transaction as a whole, and this unit will still be started. This is the recommended way to hook
- start-up of one unit to the start-up of another unit.</para>
+ the start-up of one unit to the start-up of another unit.</para>
<para>Note that requirement dependencies do not influence the order in which services are started or
stopped. This has to be configured independently with the <varname>After=</varname> or
<listitem><para>These two settings expect a space-separated list of unit names. They may be specified
more than once, in which case dependencies for all listed names are created.</para>
- <para>Those two setttings configure ordering dependencies between units. If unit
+ <para>Those two settings configure ordering dependencies between units. If unit
<filename>foo.service</filename> contains the setting <option>Before=bar.service</option> and both
units are being started, <filename>bar.service</filename>'s start-up is delayed until
<filename>foo.service</filename> has finished starting up. <varname>After=</varname> is the inverse
configured by <varname>Requires=</varname>, <varname>Wants=</varname>, <varname>Requisite=</varname>,
or <varname>BindsTo=</varname>. It is a common pattern to include a unit name in both the
<varname>After=</varname> and <varname>Wants=</varname> options, in which case the unit listed will
- be started before the unit that is configured with these options.</para></listitem>
+ be started before the unit that is configured with these options.</para>
+
+ <para>Note that <varname>Before=</varname> dependencies on device units have no effect and are not
+ supported. Devices generally become available as a result of an external hotplug event, and systemd
+ creates the corresponding device unit without delay.</para></listitem>
</varlistentry>
<varlistentry>
<orderedlist>
<listitem><para>It is in an active, activating, deactivating or failed state (i.e. in any unit state except for <literal>inactive</literal>)</para></listitem>
<listitem><para>It has a job queued for it</para></listitem>
- <listitem><para>It is a dependency of some sort of at least one other unit that is loaded into memory</para></listitem>
+ <listitem><para>It is a dependency of at least one other unit that is loaded into memory</para></listitem>
<listitem><para>It has some form of resource still allocated (e.g. a service unit that is inactive but for which
a process is still lingering that ignored the request to be terminated)</para></listitem>
<listitem><para>It has been pinned into memory programmatically by a D-Bus call</para></listitem>
<varlistentry>
<term><varname>$SYSTEMD_UNIT_PATH</varname></term>
-
- <listitem><para>Controls where systemd looks for unit
- files.</para></listitem>
+ <term><varname>$SYSTEMD_GENERATOR_PATH</varname></term>
+ <term><varname>$SYSTEMD_ENVIRONMENT_GENERATOR_PATH</varname></term>
+
+ <listitem><para>Controls where systemd looks for unit files and
+ generators.</para>
+ <para>These variables may contain a list of paths, separated by colons
+ (<literal>:</literal>). When set, if the list ends with an empty
+ component (<literal>...:</literal>), this list is prepended to the
+ usual set of of paths. Otherwise, the specified list replaces the usual
+ set of paths.
+ </para></listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>systemd.show_status</varname></term>
- <listitem><para>Takes a boolean argument or the constant
- <constant>auto</constant>. Can be also specified without an argument, with
- the same effect as a positive boolean. If enabled, the systemd manager (PID
- 1) shows terse service status updates on the console during bootup.
- <constant>auto</constant> behaves like <option>false</option> until a unit
- fails or there is a significant delay in boot. Defaults to enabled, unless
- <option>quiet</option> is passed as kernel command line option, in which case
- it defaults to <constant>auto</constant>. If specified overrides the system
- manager configuration file option <option>ShowStatus=</option>, see
+ <listitem><para>Takes a boolean argument or the constants <constant>error</constant> and
+ <constant>auto</constant>. Can be also specified without an argument, with the same effect as a
+ positive boolean. If enabled, the systemd manager (PID 1) shows terse service status updates on the
+ console during bootup. With <constant>error</constant>, only messages about failures are shown, but
+ boot is otherwise quiet. <constant>auto</constant> behaves like <option>false</option> until there is
+ a significant delay in boot. Defaults to enabled, unless <option>quiet</option> is passed as kernel
+ command line option, in which case it defaults to <constant>error</constant>. If specified overrides
+ the system manager configuration file option <option>ShowStatus=</option>, see
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
<listitem><para>When specified without an argument or with a true argument,
enables the usage of
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">unified cgroup hierarchy</ulink>
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">unified cgroup hierarchy</ulink>
(a.k.a. cgroups-v2). When specified with a false argument, fall back to
hybrid or full legacy cgroup hierarchy.</para>
<para><filename>/etc/sysusers.d/*.conf</filename></para>
<para><filename>/run/sysusers.d/*.conf</filename></para>
<para><filename>/usr/lib/sysusers.d/*.conf</filename></para>
+
+ <programlisting>
+#Type Name ID GECOS Home directory Shell
+u user_name uid "User Description" /path/to/shell
+u user_name uid:gid - -
+u user_name /file/owned/by/user - -
+g group_name gid "Group Description"
+g group_name /file/owned/by/group -
+m user_name group_name
+r - lowest-highest</programlisting>
</refsynopsisdiv>
<refsect1>
<programlisting>#Type Name ID GECOS Home directory Shell
u httpd 404 "HTTP User"
-u authd /usr/bin/authd "Authorization user"
+u _authd /usr/bin/authd "Authorization user"
u postgres - "Postgresql Database" /var/lib/pgsql /usr/libexec/postgresdb
g input - -
-m authd input
-u root 0 "Superuser" /root /bin/zsh</programlisting>
+m _authd input
+u root 0 "Superuser" /root /bin/zsh
+r - 500-900
+</programlisting>
<para>Empty lines and lines beginning with the <literal>#</literal> character are ignored, and may be used for
commenting.</para>
<term><varname>u</varname></term>
<listitem><para>Create a system user and group of the specified name should
they not exist yet. The user's primary group will be set to the group
- bearing the same name. The account will be created disabled, so that logins
- are not allowed.</para></listitem>
+ bearing the same name unless the ID field specifies it. The account will be
+ created disabled, so that logins are not allowed.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>g</varname></term>
<listitem><para>Create a system group of the specified name
should it not exist yet. Note that <varname>u</varname>
- implicitly create a matching group. The group will be
+ implicitly creates a matching group. The group will be
created with no password set.</para></listitem>
</varlistentry>
path's owner/group. This is useful to create users whose UID/GID
match the owners of pre-existing files (such as SUID or SGID
binaries).
- The syntax <literal><replaceable>uid</replaceable>:<replaceable>gid</replaceable></literal> is also supported to
- allow creating user and group pairs with different numeric UID and GID values. The group with the indicated GID must get created explicitly before or it must already exist. Specifying <literal>-</literal> for the UID in this syntax
- is also supported.
+ The syntaxes <literal><replaceable>uid</replaceable>:<replaceable>gid</replaceable></literal> and
+ <literal><replaceable>uid</replaceable>:<replaceable>groupname</replaceable></literal> are supported to
+ allow creating users with specific primary groups. The given group must be created explicitly, or it
+ must already exist. Specifying <literal>-</literal> for the UID in these syntaxes is also supported.
</para>
<para>For <varname>m</varname> lines, this field should contain
D /directory/to/create-and-remove mode user group cleanup-age -
e /directory/to/cleanup mode user group cleanup-age -
v /subvolume-or-directory/to/create mode user group - -
-Q /subvolume/to/create mode user group - -
+q /subvolume-or-directory/to/create mode user group - -
+Q /subvolume-or-directory/to/create mode user group - -
p /fifo/to/create mode user group - -
p+ /fifo/to/[re]create mode user group - -
L /symlink/to/create - - - - symlink/target/path
X /path-or-glob/to/ignore/recursively - - - - -
r /empty/dir/to/remove - - - - -
R /dir/to/remove/recursively - - - - -
-z /path-or-glob/to/adjust/mode mode user group - MAC context
-Z /path-or-glob/to/adjust/mode/recursively mode user group - MAC context
+z /path-or-glob/to/adjust/mode mode user group - -
+Z /path-or-glob/to/adjust/mode/recursively mode user group - -
t /path-or-glob/to/set/xattrs - - - - xattrs
T /path-or-glob/to/set/xattrs/recursively - - - - xattrs
h /path-or-glob/to/set/attrs - - - - file attrs
<filename>systemd-tmpfiles-cleanup.service</filename>, and associated units.</para>
<para>System daemons frequently require private runtime directories below <filename>/run</filename> to
- store communication sockets and similar. For these, is is better to use
+ store communication sockets and similar. For these, it is better to use
<varname>RuntimeDirectory=</varname> in their unit files (see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details), if the flexibility provided by <filename>tmpfiles.d</filename> is not required. The advantages
that is enforced on <filename>systemd-udevd.service</filename>.</para>
<para>Please also note that <literal>:=</literal> and <literal>=</literal> are clearing
both, program and builtin commands.</para>
+ <para>In order to activate long-running processes from udev rules, provide a service unit, and
+ pull it in from a udev device using the <varname>SYSTEMD_WANTS</varname> device property. See
+ <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details.</para>
</listitem>
</varlistentry>
<para>User processes may be started by the <filename>user@.service</filename> instance, in which
case they will be part of that unit in the system hierarchy. They may also be started elsewhere,
for example by
- <citerefentry><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or a
+ <citerefentry project='die-net'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or a
display manager like <command>gdm</command>, in which case they form a .scope unit (see
<citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
Both <filename>user@<replaceable>UID</replaceable>.service</filename> and the scope units are
…</programlisting>
<para>User with UID 1000 is logged in using <command>gdm</command> (<filename
index="false">session-4.scope</filename>) and
- <citerefentry><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
(<filename index="false">session-19.scope</filename>), and also has a user manager instance
running (<filename index="false">user@1000.service</filename>). User with UID 1001 is logged
in using <command>ssh</command> (<filename index="false">session-20.scope</filename>) and
--- /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="userdbctl" conditional='ENABLE_USERDB'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>userdbctl</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>userdbctl</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>userdbctl</refname>
+ <refpurpose>Inspect users, groups and group memberships</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>userdbctl</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="req">COMMAND</arg>
+ <arg choice="opt" rep="repeat">NAME</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>userdbctl</command> may be used to inspect user and groups (as well as group memberships)
+ of the system. This client utility inquires user/group information provided by various system services,
+ both operating on JSON user/group records (as defined by the <ulink
+ url="https://systemd.io/USER_RECORD">JSON User Record</ulink> and <ulink
+ url="https://systemd.io/GROUP_RECORD">JSON Group Record</ulink> definitions), and classic UNIX NSS/glibc
+ user and group records. This tool is primarily a client to the <ulink
+ url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><option>--output=</option><replaceable>MODE</replaceable></term>
+
+ <listitem><para>Choose the output mode, takes one of <literal>classic</literal>,
+ <literal>friendly</literal>, <literal>table</literal>, <literal>json</literal>. If
+ <literal>classic</literal>, an output very close to the format of <filename>/etc/passwd</filename> or
+ <filename>/etc/group</filename> is generated. If <literal>friendly</literal> a more comprehensive and
+ user friendly, human readable output is generated; if <literal>table</literal> a minimal, tabular
+ output is generated; if <literal>json</literal> a JSON formatted output is generated. Defaults to
+ <literal>friendly</literal> if a user/group is specified on the command line,
+ <literal>table</literal> otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--service=</option><replaceable>SERVICE</replaceable><optional>:<replaceable>SERVICE…</replaceable></optional></term>
+ <term><option>-s</option> <replaceable>SERVICE</replaceable>:<replaceable>SERVICE…</replaceable></term>
+
+ <listitem><para>Controls which services to query for users/groups. Takes a list of one or more
+ service names, separated by <literal>:</literal>. See below for a list of well-known service
+ names. If not specified all available services are queried at once.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--with-nss=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Controls whether to include classic glibc/NSS user/group lookups in the output. If
+ <option>--with-nss=no</option> is used any attempts to resolve or enumerate users/groups provided
+ only via glibc NSS is suppressed. If <option>--with-nss=yes</option> is specified such users/groups
+ are included in the output (which is the default).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--synthesize=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Controls whether to synthesize records for the root and nobody users/groups if they
+ aren't defined otherwise. By default (or <literal>yes</literal>) such records are implicitly
+ synthesized if otherwise missing since they have special significance to the OS. When
+ <literal>no</literal> this synthesizing is turned off.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-N</option></term>
+
+ <listitem><para>This option is short for <option>--with-nss=no</option>
+ <option>--synthesize=no</option>. Use this option to show only records that are natively defined as
+ JSON user or group records, with all NSS/glibc compatibility and all implicit synthesis turned
+ off.</para></listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="no-pager" />
+ <xi:include href="standard-options.xml" xpointer="no-legend" />
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Commands</title>
+
+ <para>The following commands are understood:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><command>user</command> <optional><replaceable>USER</replaceable>…</optional></term>
+
+ <listitem><para>List all known users records or show details of one or more specified user
+ records. Use <option>--output=</option> to tweak output mode.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>group</command> <optional><replaceable>GROUP</replaceable>…</optional></term>
+
+ <listitem><para>List all known group records or show details of one or more specified group
+ records. Use <option>--output=</option> to tweak output mode.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>users-in-group</command> <optional><replaceable>GROUP</replaceable>…</optional></term>
+
+ <listitem><para>List users that are members of the specified groups. If no groups are specified list
+ all user/group memberships defined. Use <option>--output=</option> to tweak output
+ mode.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>groups-of-user</command> <optional><replaceable>USER</replaceable>…</optional></term>
+
+ <listitem><para>List groups that the specified users are members of. If no users are specified list
+ all user/group memberships defined (in this case <command>groups-of-user</command> and
+ <command>users-in-group</command> are equivalent). Use <option>--output=</option> to tweak output
+ mode.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>services</command></term>
+
+ <listitem><para>List all services currently providing user/group definitions to the system. See below
+ for a list of well-known services providing user information.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>ssh-authorized-keys</command></term>
+
+ <listitem><para>This operation is not a public, user-facing interface. It is used to allow the SSH daemon to pick
+ up authorized keys from user records, see below.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Well-Known Services</title>
+
+ <para>The <command>userdbctl services</command> command will list all currently running services that
+ provide user or group definitions to the system. The following are well-known services are shown among
+ this list.</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>io.systemd.DynamicUser</constant></term>
+
+ <listitem><para>This service is provided by the system service manager itself (i.e. PID 1) and
+ makes all users (and their groups) synthesized through the <varname>DynamicUser=</varname> setting in
+ service unit files available to the system (see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details about this setting).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>io.systemd.Home</constant></term>
+
+ <listitem><para>This service is provided by
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and makes all users (and their groups) belonging to home directories managed by that service
+ available to the system.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>io.systemd.Multiplexer</constant></term>
+
+ <listitem><para>This service is provided by
+ <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and multiplexes user/group look-ups to all other running lookup services. This is the primary entry point
+ for user/group record clients, as it simplifies client side implementation substantially since they
+ can ask a single service for lookups instead of asking all running services in parallel.
+ <command>userdbctl</command> uses this service preferably, too, unless <option>--with-nss=</option>
+ or <option>--service=</option> are used, in which case finer control over the services to talk to is
+ required.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>io.systemd.NameSeviceSwitch</constant></term>
+
+ <listitem><para>This service is (also) provided by
+ <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and converts classic NSS/glibc user and group records to JSON user/group records, providing full
+ backwards compatibility. Use <option>--with-nss=no</option> to disable this compatibility, see
+ above. Note that compatibility is actually provided in both directions:
+ <citerefentry><refentrytitle>nss-systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry> will
+ automatically synthesize classic NSS/glibc user/group records from all JSON user/group records
+ provided to the system, thus using both APIs is mostly equivalent and provides access to the same
+ data, however the NSS/glibc APIs necessarily expose a more reduced set of fields
+ only.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Note that <command>userdbctl</command> has internal support for NSS-based lookups too. This means
+ that if neither <constant>io.systemd.Multiplexer</constant> nor
+ <constant>io.systemd.NameSeviceSwitch</constant> are running look-ups into the the basic user/group
+ databases will still work.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Integration with SSH</title>
+
+ <para>The <command>userdbctl</command> tool may be used to make the list of SSH authorized keys possibly
+ contained in a user record available to the SSH daemon for authentication. For that configure the
+ following in <citerefentry
+ project='die-net'><refentrytitle>sshd_config</refentrytitle><manvolnum>5</manvolnum></citerefentry>:</para>
+
+ <programlisting>…
+AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
+AuthorizedKeysCommandUser root
+…</programlisting>
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+ </refsect1>
+
+ <xi:include href="less-variables.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>nss-systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>getent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
# SPDX-License-Identifier: LGPL-2.1+
project('systemd', 'c',
- version : '244',
+ version : '245',
license : 'LGPLv2+',
default_options: [
'c_std=gnu99',
meson_version : '>= 0.46',
)
-libsystemd_version = '0.27.1'
-libudev_version = '1.6.16'
+libsystemd_version = '0.28.0'
+libudev_version = '1.6.17'
# We need the same data in two different formats, ugh!
# Also, for hysterical reasons, we use different variable
# names, sometimes. Not all variables are included in every
# set. Ugh, ugh, ugh!
conf = configuration_data()
-conf.set('PROJECT_VERSION', meson.project_version())
+conf.set('PROJECT_VERSION', meson.project_version(),
+ description : 'Numerical project version (used where a simple number is expected)')
substs = configuration_data()
substs.set('PROJECT_URL', 'https://www.freedesktop.org/wiki/Software/systemd')
-substs.set('PROJECT_VERSION', meson.project_version())
+substs.set('PROJECT_VERSION', meson.project_version(),
+ description : 'Numerical project version (used where a simple number is expected)')
# This is to be used instead of meson.source_root(), as the latter will return
# the wrong result when systemd is being built as a meson subproject
conf.set_quoted('SYSTEMD_MAKEFS_PATH', join_paths(rootlibexecdir, 'systemd-makefs'))
conf.set_quoted('SYSTEMD_GROWFS_PATH', join_paths(rootlibexecdir, 'systemd-growfs'))
conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-shutdown'))
-conf.set_quoted('SYSTEMD_SLEEP_BINARY_PATH', join_paths(rootlibexecdir, 'systemd-sleep'))
conf.set_quoted('SYSTEMCTL_BINARY_PATH', join_paths(rootbindir, 'systemctl'))
conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', join_paths(rootbindir, 'systemd-tty-ask-password-agent'))
conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH', join_paths(bindir, 'systemd-stdio-bridge'))
conf.set_quoted('VENDOR_KEYRING_PATH', join_paths(rootlibexecdir, 'import-pubring.gpg'))
conf.set_quoted('USER_KEYRING_PATH', join_paths(pkgsysconfdir, 'import-pubring.gpg'))
conf.set_quoted('DOCUMENT_ROOT', join_paths(pkgdatadir, 'gatewayd'))
+conf.set_quoted('SYSTEMD_HOMEWORK_PATH', join_paths(rootlibexecdir, 'systemd-homework'))
+conf.set_quoted('SYSTEMD_USERWORK_PATH', join_paths(rootlibexecdir, 'systemd-userwork'))
conf.set10('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default)
conf.set_quoted('MEMORY_ACCOUNTING_DEFAULT_YES_NO', memory_accounting_default ? 'yes' : 'no')
conf.set('STATUS_UNIT_FORMAT_DEFAULT', 'STATUS_UNIT_FORMAT_' + status_unit_format_default.to_upper())
substs.set('systemshutdowndir', systemshutdowndir)
substs.set('systemsleepdir', systemsleepdir)
substs.set('CERTIFICATEROOT', get_option('certificate-root'))
-substs.set('SYSTEMCTL', join_paths(rootbindir, 'systemctl'))
substs.set('RANDOM_SEED', join_paths(randomseeddir, 'random-seed'))
substs.set('SYSTEM_SYSVINIT_PATH', sysvinit_path)
substs.set('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
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>'))
-conf.set('SIZEOF_GID_T', cc.sizeof('gid_t', prefix : '#include <sys/types.h>'))
conf.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix : '#include <sys/types.h>'))
conf.set('SIZEOF_INO_T', cc.sizeof('ino_t', prefix : '#include <sys/types.h>'))
conf.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix : '#include <sys/time.h>'))
libmount = dependency('mount',
version : fuzzer_build ? '>= 0' : '>= 2.30')
+want_libfdisk = get_option('fdisk')
+if want_libfdisk != 'false' and not skip_deps
+ libfdisk = dependency('fdisk',
+ required : want_libfdisk == 'true')
+ have = libfdisk.found()
+else
+ have = false
+ libfdisk = []
+endif
+conf.set10('HAVE_LIBFDISK', have)
+
+want_pwquality = get_option('pwquality')
+if want_pwquality != 'false' and not skip_deps
+ libpwquality = dependency('pwquality', required : want_pwquality == 'true')
+ have = libpwquality.found()
+else
+ have = false
+ libpwquality = []
+endif
+conf.set10('HAVE_PWQUALITY', have)
+
want_seccomp = get_option('seccomp')
if want_seccomp != 'false' and not skip_deps
libseccomp = dependency('libseccomp',
version : '>= 2.0.1',
required : want_libcryptsetup == 'true')
have = libcryptsetup.found()
+
+ conf.set10('HAVE_CRYPT_SET_METADATA_SIZE',
+ have and cc.has_function('crypt_set_metadata_size', dependencies : libcryptsetup))
else
have = false
libcryptsetup = []
'DNS_OVER_TLS_' + default_dns_over_tls.underscorify().to_upper())
substs.set('DEFAULT_DNS_OVER_TLS_MODE', default_dns_over_tls)
+want_repart = get_option('repart')
+if want_repart != 'false'
+ have = (conf.get('HAVE_OPENSSL') == 1 and
+ conf.get('HAVE_LIBFDISK') == 1)
+ if want_repart == 'true' and not have
+ error('repart support was requested, but dependencies are not available')
+ endif
+else
+ have = false
+endif
+conf.set10('ENABLE_REPART', have)
+
want_importd = get_option('importd')
if want_importd != 'false'
have = (conf.get('HAVE_LIBCURL') == 1 and
endif
conf.set10('ENABLE_IMPORTD', have)
+want_homed = get_option('homed')
+if want_homed != 'false'
+ have = (conf.get('HAVE_OPENSSL') == 1 and
+ conf.get('HAVE_LIBFDISK') == 1 and
+ conf.get('HAVE_LIBCRYPTSETUP') == 1)
+ if want_homed == 'true' and not have
+ error('homed support was requested, but dependencies are not available')
+ endif
+else
+ have = false
+endif
+conf.set10('ENABLE_HOMED', have)
+
+have = have and conf.get('HAVE_PAM') == 1
+conf.set10('ENABLE_PAM_HOME', have)
+
want_remote = get_option('remote')
if want_remote != 'false'
have_deps = [conf.get('HAVE_MICROHTTPD') == 1,
'localed',
'machined',
'portabled',
+ 'userdb',
'networkd',
'timedated',
'timesyncd',
subdir('src/pstore')
subdir('src/hostname')
subdir('src/import')
+subdir('src/partition')
subdir('src/kernel-install')
subdir('src/locale')
subdir('src/machine')
subdir('src/portable')
+subdir('src/userdb')
+subdir('src/home')
subdir('src/nspawn')
subdir('src/resolve')
subdir('src/timedate')
build_by_default : want_tests != 'false')
foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
- ['systemd', 'ENABLE_NSS_SYSTEMD'],
+ ['systemd', 'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h'],
['mymachines', 'ENABLE_NSS_MYMACHINES'],
['resolve', 'ENABLE_NSS_RESOLVE']]
sym = 'src/nss-@0@/nss-@0@.sym'.format(module)
version_script_arg = join_paths(project_source_root, sym)
+ sources = ['src/nss-@0@/nss-@0@.c'.format(module)]
+ if tuple.length() > 2
+ sources += tuple[2].split()
+ endif
+
nss = shared_library(
'nss_' + module,
- 'src/nss-@0@/nss-@0@.c'.format(module),
+ sources,
disable_mempool_c,
version : '2',
include_directories : includes,
public_programs += exe
endif
+if conf.get('ENABLE_USERDB') == 1
+ executable('systemd-userwork',
+ systemd_userwork_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [threads],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
+ executable('systemd-userdbd',
+ systemd_userdbd_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [threads],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
+ executable('userdbctl',
+ userdbctl_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [threads],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootbindir)
+endif
+
+if conf.get('ENABLE_HOMED') == 1
+ executable('systemd-homework',
+ systemd_homework_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [threads,
+ libcryptsetup,
+ libblkid,
+ libcrypt,
+ libopenssl,
+ libfdisk,
+ libp11kit],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
+ executable('systemd-homed',
+ systemd_homed_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [threads,
+ libcrypt,
+ libopenssl,
+ libpwquality],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootlibexecdir)
+
+ executable('homectl',
+ homectl_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [threads,
+ libcrypt,
+ libopenssl,
+ libp11kit,
+ libpwquality],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootbindir)
+
+ if conf.get('HAVE_PAM') == 1
+ version_script_arg = join_paths(project_source_root, pam_systemd_home_sym)
+ pam_systemd = shared_library(
+ 'pam_systemd_home',
+ pam_systemd_home_c,
+ name_prefix : '',
+ include_directories : includes,
+ link_args : ['-shared',
+ '-Wl,--version-script=' + version_script_arg],
+ link_with : [libsystemd_static,
+ libshared_static],
+ dependencies : [threads,
+ libpam,
+ libpam_misc,
+ libcrypt],
+ link_depends : pam_systemd_home_sym,
+ install : true,
+ install_dir : pamlibdir)
+ endif
+endif
+
foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit']
meson.add_install_script(meson_make_symlink,
join_paths(rootbindir, 'systemctl'),
endif
if conf.get('ENABLE_TIMESYNCD') == 1
+ if get_option('link-timesyncd-shared')
+ timesyncd_link_with = [libshared]
+ else
+ timesyncd_link_with = [libsystemd_static,
+ libshared_static,
+ libjournal_client,
+ libbasic_gcrypt]
+ endif
+
executable('systemd-timesyncd',
systemd_timesyncd_sources,
include_directories : includes,
- link_with : [libshared],
+ link_with : [timesyncd_link_with],
dependencies : [threads,
libm],
install_rpath : rootlibexecdir,
executable('systemd-time-wait-sync',
'src/time-wait-sync/time-wait-sync.c',
include_directories : includes,
- link_with : [libshared],
+ link_with : [timesyncd_link_with],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
mkdir_p.format(join_paths(sysconfdir, 'binfmt.d')))
endif
+if conf.get('ENABLE_REPART') == 1
+ executable('systemd-repart',
+ systemd_repart_sources,
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [threads,
+ libcryptsetup,
+ libblkid,
+ libfdisk,
+ libopenssl],
+ install_rpath : rootlibexecdir,
+ install : true,
+ install_dir : rootbindir)
+endif
+
if conf.get('ENABLE_VCONSOLE') == 1
executable('systemd-vconsole-setup',
'src/vconsole/vconsole-setup.c',
status += [
'EFI machine type: @0@'.format(EFI_MACHINE_TYPE_NAME),
'EFI CC @0@'.format(' '.join(efi_cc)),
- 'EFI lib directory: @0@'.format(efi_libdir),
- 'EFI lds directory: @0@'.format(efi_ldsdir),
+ 'EFI lds: @0@'.format(efi_lds),
+ 'EFI crt0: @0@'.format(efi_crt0),
'EFI include directory: @0@'.format(efi_incdir)]
endif
endif
foreach tuple : [
['libcryptsetup'],
['PAM'],
+ ['pwquality'],
+ ['libfdisk'],
['p11kit'],
['AUDIT'],
['IMA'],
['libiptc'],
['elfutils'],
['binfmt'],
+ ['repart'],
['vconsole'],
['quotacheck'],
['tmpfiles'],
['logind'],
['machined'],
['portabled'],
+ ['userdb'],
+ ['homed'],
['importd'],
['hostnamed'],
['timedated'],
['link-udev-shared', get_option('link-udev-shared')],
['link-systemctl-shared', get_option('link-systemctl-shared')],
['link-networkd-shared', get_option('link-networkd-shared')],
+ ['link-timesyncd-shared', get_option('link-timesyncd-shared')],
]
if tuple.length() >= 2
description : 'link systemctl against libsystemd-shared.so')
option('link-networkd-shared', type: 'boolean',
description : 'link systemd-networkd and its helpers to libsystemd-shared.so')
+option('link-timesyncd-shared', type: 'boolean',
+ description : 'link systemd-timesyncd and its helpers to libsystemd-shared.so')
option('static-libsystemd', type : 'combo',
choices : ['false', 'true', 'pic', 'no-pic'],
description : '''install a static library for libsystemd''')
description : 'support for environment.d')
option('binfmt', type : 'boolean',
description : 'support for custom binary formats')
+option('repart', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'install the systemd-repart tool')
option('coredump', type : 'boolean',
description : 'install the coredump handler')
option('pstore', type : 'boolean',
description : 'install the systemd-machined stack')
option('portabled', type : 'boolean',
description : 'install the systemd-portabled stack')
+option('userdb', type : 'boolean',
+ description : 'install the systemd-userdbd stack')
+option('homed', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'install the systemd-homed stack')
option('networkd', type : 'boolean',
description : 'install the systemd-networkd stack')
option('timedated', type : 'boolean',
description : 'libaudit support')
option('blkid', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'libblkid support')
+option('fdisk', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'libfdisk support')
option('kmod', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'support for loadable modules')
option('pam', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'PAM support')
+option('pwquality', type : 'combo', choices : ['auto', 'true', 'false'],
+ description : 'libpwquality support')
option('microhttpd', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'libµhttpd support')
option('libcryptsetup', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'the linker to use for EFI modules')
option('efi-libdir', type : 'string',
description : 'path to the EFI lib directory')
-option('efi-ldsdir', type : 'string',
- description : 'path to the EFI lds directory')
option('efi-includedir', type : 'string', value : '/usr/include/efi',
description : 'path to the EFI header directory')
option('tpm-pcrindex', type : 'integer', value : 8,
src/core/org.freedesktop.systemd1.policy.in
+src/home/org.freedesktop.home1.policy
src/hostname/org.freedesktop.hostname1.policy
src/import/org.freedesktop.import1.policy
src/locale/org.freedesktop.locale1.policy
msgstr "Дазволіць далучаць прылады да працоўных месцаў"
#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Неабходна аўтэнтыфікацыя для далучэння прылад да працоўных месцаў."
#: ../src/login/org.freedesktop.login1.policy.in.h:25
#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr "Неабходна аўтэнтыфікацыя для адключэння прылад ад працоўных месцаў."
#: ../src/login/org.freedesktop.login1.policy.in.h:27
msgstr "Выключыць сістэму"
#: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Неабходна аўтэнтыфікацыя для выключэння сістэмы."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Неабходна аўтэнтыфікацыя для выключэння сістэмы пры прысутнасці іншых "
"карыстальнікаў."
#: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Выключыць сістэму, калі праграмы перашкаджаюць гэтаму"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Неабходна аўтэнтыфікацыя для выключэння сістэмы, калі праграмы перашкаджаюць "
"гэтаму."
msgstr "Перазагрузіць сістэму"
#: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Неабходна аўтэнтыфікацыя для перазагрузкі сістэмы."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Неабходна аўтэнтыфікацыя для перазагрузкі сістэмы пры прысутнасці іншых "
"карыстальнікаў."
#: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Перазагрузіць сістэму, калі праграмы перашкаджаюць гэтаму"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Неабходна аўтэнтыфікацыя для перазагрузкі сістэмы, калі праграмы "
"перашкаджаюць гэтаму."
msgstr "Прыпыніць сістэму"
#: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Неабходна аўтэнтыфікацыя для прыпынення сістэмы."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Неабходна аўтэнтыфікацыя для прыпынення сістэмы пры прысутнасці іншых "
"карыстальнікаў."
#: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Прыпыніць сістэму, калі праграмы перашкаджаюць гэтаму"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Неабходна аўтэнтыфікацыя для прыпынення сістэмы, калі праграмы перашкаджаюць "
"гэтаму."
msgstr "Гібернаваць сістэму"
#: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Неабходна аўтэнтыфікацыя для гібернацыі сістэмы."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Неабходна аўтэнтыфікацыя для гібернацыі сістэмы пры прысутнасці іншых "
"карыстальнікаў."
#: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Гібернаваць сістэму, калі праграмы перашкаджаюць гэтаму"
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Неабходна аўтэнтыфікацыя для гібернацыі сістэмы, калі праграмы перашкаджаюць "
"гэтаму."
#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Неабходна аўтэнтыфікацыя для кіравання актыўнымі сесіямі, карыстальнікамі і "
"месцамі."
msgstr "Dazvolić dalučać prylady da pracoŭnych miescaŭ"
#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia dalučennia prylad da pracoŭnych miescaŭ."
#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia adkliučennia prylad ad pracoŭnych miescaŭ."
msgstr "Vykliučyć sistemu"
#: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Nieabchodna aŭtentyfikacyja dlia vykliučennia sistemy."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia vykliučennia sistemy pry prysutnasci inšych "
"karystaĺnikaŭ."
#: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Vykliučyć sistemu, kali prahramy pieraškadžajuć hetamu"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia vykliučennia sistemy, kali prahramy "
"pieraškadžajuć hetamu."
msgstr "Pierazahruzić sistemu"
#: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Nieabchodna aŭtentyfikacyja dlia pierazahruzki sistemy."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia pierazahruzki sistemy pry prysutnasci "
"inšych karystaĺnikaŭ."
#: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Pierazahruzić sistemu, kali prahramy pieraškadžajuć hetamu"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia pierazahruzki sistemy, kali prahramy "
"pieraškadžajuć hetamu."
msgstr "Prypynić sistemu"
#: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Nieabchodna aŭtentyfikacyja dlia prypyniennia sistemy."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia prypyniennia sistemy pry prysutnasci inšych "
"karystaĺnikaŭ."
#: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Prypynić sistemu, kali prahramy pieraškadžajuć hetamu"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia prypyniennia sistemy, kali prahramy "
"pieraškadžajuć hetamu."
msgstr "Hibiernavać sistemu"
#: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Nieabchodna aŭtentyfikacyja dlia hibiernacyi sistemy."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia hibiernacyi sistemy pry prysutnasci inšych "
"karystaĺnikaŭ."
#: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Hibiernavać sistemu, kali prahramy pieraškadžajuć hetamu"
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia hibiernacyi sistemy, kali prahramy "
"pieraškadžajuć hetamu."
#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Nieabchodna aŭtentyfikacyja dlia kiravannia aktyŭnymi siesijami, "
"karystaĺnikami i miescami."
msgstr "Позволяване на закачане на устройства към работните места"
#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"За позволяване на закачане на устройства към работните места е необходима "
"идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"За изчистване на връзките между устройствата и работните места е необходима "
"идентификация."
msgstr "Изключване на системата"
#: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "За изключване на системата е необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"За изключване на системата, дори когато има други вписани потребители, е "
"необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Изключване на системата, дори когато програма иска да предотврати това"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"За изключване на системата, дори когато програма иска да предотврати това, е "
"необходима идентификация."
msgstr "Рестартиране на системата"
#: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "За рестартиране на системата е необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"За рестартиране на системата, дори когато има други вписани потребители, е "
"необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
"Рестартиране на системата, дори когато програма иска да предотврати това"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"За рестартиране на системата, дори когато програма иска да предотврати това, "
"е необходима идентификация."
msgstr "Приспиване на системата"
#: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "За приспиване на системата е необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"За приспиване на системата, дори когато има други вписани потребители, е "
"необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Приспиване на системата, дори когато програма иска да предотврати това"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"За приспиване на системата, дори когато програма иска да предотврати това, е "
"необходима идентификация."
msgstr "Дълбоко приспиване на системата"
#: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "За дълбоко приспиване на системата е необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"За дълбоко приспиване на системата, дори когато има други вписани "
"потребители, е необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
"Дълбоко приспиване на системата, дори когато програма иска да предотврати "
"това"
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"За дълбоко приспиване на системата, дори когато програма иска да предотврати "
"това, е необходима идентификация."
#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"За управление на работещите сесии, потребители и работни места е необходима "
"идентификация."
msgstr "Permet l'annexió de dispositius als llocs de treball"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Es requereix autenticació per annexar un dispositiu a un lloc de treball."
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Es requereix autenticació per restablir com s'annexionen els dispositius als "
"llocs de treball."
msgstr "Apaga el sistema"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Es requereix autenticació per apagar el sistema."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Es requereix autenticació per apagar el sistema mentre hi ha altres usuaris "
"amb la sessió iniciada."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Apaga el sistema mentre hi ha una aplicació que ha demanat inhibir-ho"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Es requereix autenticació per apagar el sistema mentre hi ha una aplicació "
"que ha demanat inhibir-ho."
msgstr "Reinicia el sistema"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Es requereix autenticació per reiniciar el sistema."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Es requereix autenticació per reiniciar el sistema mentre hi ha usuaris amb "
"la sessió iniciada."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
"Reinicia el sistema mentre hi ha una aplicació que ha demanat inhibir-ho"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Es requereix autenticació per reiniciar el sistema mentre hi ha una "
"aplicació que ha demanat inhibir-ho."
msgstr "Atura el sistema"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Es requereix autenticació per aturar el sistema."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Es requereix autenticació per aturar el sistema mentre hi ha altres usuaris "
"amb la sessió iniciada."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr ""
"Atura el sistema mentre hi ha una aplicació que ha demanat d'inhibir-ho"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Es requereix autenticació per aturar el sistema mentre hi ha una aplicació "
msgstr "Suspèn el sistema"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Es requereix autenticació per suspendre el sistema."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Es requereix autenticació per reiniciar el sistema mentre hi ha altres "
"usuaris amb la sessió iniciada."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
"Suspèn el sistema mentre hi ha una aplicació que ha demanat d'inhibir-ho"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Es requereix autenticació per suspendre el sistema mentre hi ha una "
"aplicació que ha demanat d'inhibir-ho."
msgstr "Hiberna el sistema"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Es requereix autenticació per hibernar el sistema."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Es requereix autenticació per hibernar el sistema mentre hi ha altres "
"usuaris amb la sessió iniciada."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
"Hiberna el sistema mentre hi ha una aplicació que ha demanat d'inhibir-ho"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Es requereix autenticació per hibernar el sistema mentre hi ha una aplicació "
"que ha demanat d'inhibir-ho."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Es requereix autenticació per gestionar les sessions, usuaris i llocs de "
"treball actius."
msgstr ""
"Project-Id-Version: systemd master\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-09-17 14:31+0000\n"
-"PO-Revision-Date: 2019-09-19 15:48+0200\n"
+"POT-Creation-Date: 2020-02-29 15:12+0000\n"
+"PO-Revision-Date: 2020-03-01 13:58+0100\n"
"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
"Language-Team: Czech\n"
"Language: cs\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 2.2.3\n"
+"X-Generator: Poedit 2.3\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/core/org.freedesktop.systemd1.policy.in:65
msgid "Authentication is required to reload the systemd state."
-msgstr "Pro znovu načtení stavu systemd je vyžadováno ověření."
+msgstr "Pro opětovné načtení stavu systemd je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Vytvořit domovský adresář"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Pro vytvoření domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Odebrat domovský adresář"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Pro odebrání domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Zkontrolovat pověření domovského adresáře"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Pro kontrolu pověření proti domovskému adresáři uživatele je vyžadováno "
+"ověření."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Aktualizovat domovský adresář"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "Pro aktualizaci domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Změnit velikost domovského adresáře"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Pro změnu velikosti domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Změnit heslo domovského adresáře"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr "Pro změnu hesla domovského adresáře uživatele je vyžadováno ověření."
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Povolit připojování zařízení ke stanovištím"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Pro připojování zařízení ke stanovišti je vyžadováno ověření."
#: src/login/org.freedesktop.login1.policy:148
msgstr "Odstranit přiřazení zařízení ke stanovištím"
#: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
msgstr ""
-"Pro reset způsobu jak jsou zařízení přiřazována ke stanovištím je vyžadováno "
-"ověření."
+"Pro resetování způsobu jak jsou zařízení přiřazována ke stanovištím je "
+"vyžadováno ověření."
#: src/login/org.freedesktop.login1.policy:158
msgid "Power off the system"
msgstr "Vypnout systém"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Pro vypnutí systému je vyžadováno ověření."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Pro vypnutí systému, když jsou přihlášeni další uživatelé je vyžadováno "
"ověření."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Vypnout systém, i když aplikace požádala o zákaz vypnutí"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
msgstr ""
"Pro vypnutí systému, když aplikace požádala o zákaz vypnutí je vyžadováno "
"ověření."
msgstr "Restartovat systém"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Pro restartování systému je vyžadováno ověření."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
msgstr ""
"Pro restartování systému, když jsou přihlášeni další uživatelé je vyžadováno "
"ověření."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Restartovat systém, i když aplikace požádala o zákaz restartu"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
msgstr ""
"Pro restartování systému, když aplikace požádala o zákaz restartu je "
"vyžadováno ověření."
msgstr "Zastavit systém"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Pro zastavení systému je vyžadováno ověření."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
msgstr ""
"Pro zastavení systému, když jsou přihlášeni další uživatelé je vyžadováno "
"ověření."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Zastavit systém, i když aplikace požádala o zákaz zastavení"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
msgstr ""
"Pro zastavení systému, když aplikace požádala o zákaz zastavení je "
"vyžadováno ověření."
msgstr "Uspat systém"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Pro uspání systému je vyžadováno ověření."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Pro uspání systému, když jsou přihlášeni další uživatelé je vyžadováno "
"ověření."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Uspat systém, i když aplikace požádala o zákaz uspání"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
msgstr ""
"Pro uspání systému, když aplikace požádala o zákaz uspání je vyžadováno "
"ověření."
msgstr "Hibernovat systém"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Pro hibernaci systému je vyžadováno ověření."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Pro hibernaci systému, když jsou přihlášeni další uživatelé je vyžadováno "
"ověření."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Hibernovat systém, i když aplikace požádala o zákaz hibernace"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
msgstr ""
"Pro hibernaci systému, když aplikace požádala o zákaz hibernace je "
"vyžadováno ověření."
msgstr "Spravovat aktivní sezení, uživatele a stanoviště"
#: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
msgstr ""
"Pro správu aktivních sezení, uživatelů a stanovišť je vyžadováno ověření."
msgid "Authentication is required to set a wall message"
msgstr "K nastavení zprávy všem uživatelům je vyžadováno ověření"
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Změnit sezení"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Pro změnu virtuálního terminálu je vyžadováno ověření."
+
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Přihlásit se do lokálního kontejneru"
msgid "Authentication is required to reset DNS settings."
msgstr "Pro resetování nastavení DNS je vyžadováno ověření."
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Obnovit dynamické adresy"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Pro obnovení dynamických adres je vyžadováno ověření."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Znovu načíst nastavení sítě"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "Pro opětovné načtení nastavení sítě je vyžadováno ověření."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Přenastavit síťové rozhraní"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Pro přenastavení síťového rozhraní je vyžadováno ověření."
+
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgstr "Prohlédnout obraz přenosné služby"
"shall be enabled."
msgstr "Pro kontrolu synchronizace času ze sítě je vyžadováno ověření."
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to start '$(unit)'."
msgstr "Pro spuštění „$(unit)” je vyžadováno ověření."
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to stop '$(unit)'."
msgstr "Pro vypnutí „$(unit)” je vyžadováno ověření."
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:358
msgid "Authentication is required to reload '$(unit)'."
-msgstr "Pro znovu načtení „$(unit)” je vyžadováno ověření."
+msgstr "Pro opětovné načtení „$(unit)” je vyžadováno ověření."
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
msgid "Authentication is required to restart '$(unit)'."
msgstr "Pro restart „$(unit)” je vyžadováno ověření."
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:532
msgid ""
"Authentication is required to send a UNIX signal to the processes of "
"'$(unit)'."
msgstr "Pro odeslání UNIX signálu procesům „$(unit)” je vyžadováno ověření."
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:563
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr "Pro resetování chybného stavu „$(unit)” je vyžadováno ověření."
-#: src/core/dbus-unit.c:594
+#: src/core/dbus-unit.c:596
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Pro nastavení vlastností na „$(unit)” je vyžadováno ověření."
-#: src/core/dbus-unit.c:703
+#: src/core/dbus-unit.c:705
msgid ""
"Authentication is required to delete files and directories associated with "
"'$(unit)'."
msgstr ""
"Pro odstranění souborů nebo adresářů souvisejících s „$(unit)” je vyžadováno "
"ověření."
-
-#~ msgid "Authentication is required to kill '$(unit)'."
-#~ msgstr "Pro ukončení „$(unit)” je vyžadováno ověření."
# www.freedesktop.org/wiki/Software/systemd/multiseat/
#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Autentificering er nødvendig for at montere en enhed til en "
"arbejdsstation."
# www.freedesktop.org/wiki/Software/systemd/multiseat/
#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Autentificering er nødvendig for at nulstille måden enheder er monteret "
"arbejdsstationer."
msgstr "Sluk for systemet"
#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Autentificering er nødvendig for at slukke systemet"
#: ../src/login/org.freedesktop.login1.policy.in.h:27
#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Autentificering er nødvendig for at slukke systemet mens andre brugere "
"er logget på."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr ""
"Sluk for systemet mens en applikation har forespurgt at hæmme det"
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificering er nødvendig for at slukke systemet mens en applikation har "
"forespurgt at hæmme det."
msgstr "Genstart systemet"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Autentificering er nødvendig for at genstarte systemet."
#: ../src/login/org.freedesktop.login1.policy.in.h:33
#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Autentificering er nødvendig for at genstarte systemet mens andre brugere "
"er logget ind."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
"Genstart systemet mens en applikation har forespurgt at hæmme det"
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificering er nødvendig for at genstarte systemet mens en applikation "
"har forespurgt at hæmme det."
msgstr "Sæt systemet på standby"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Autentificering er nødvendig for at sætte systemet på standby"
#: ../src/login/org.freedesktop.login1.policy.in.h:39
#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Autentificering er nødvendig for at sætte systemet på standby, mens andre "
"brugere er logget på."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
"Sæt systemet på standby mens en applikation har forespurgt at hæmme"
"det"
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificering er nødvendig for at sætte systemet på standby, mens en "
"applikation har forespurgt at hæmme det."
msgstr "Sæt systemet i dvale"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr ""
"Autentificering er nødvendig for at sætte systemet i dvale-tilstand."
#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Autentificering er nødvendig for at sætte systemet i dvale-tilstand, mens "
"andre brugere er logget på."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Sæt systemet i dvale-tilstand mens en applikation har forespurgt at "
"hæmme det"
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificering er nødvendig for at sætte systemet i dvale tilstand, mens "
"en applikation har forespurgt at hæmme det."
# www.freedesktop.org/wiki/Software/systemd/multiseat/
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Autentificering er nødvendig for at håndtere aktive sessioner, brugere "
"og arbejdsstationer."
# www.freedesktop.org/wiki/Software/systemd/multiseat/
#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Legitimierung ist zum Anschließen eines Geräts an eine Arbeitsstation "
"notwendig."
# www.freedesktop.org/wiki/Software/systemd/multiseat/
#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Legitimierung ist zum Zurücksetzen notwendig, wie Geräte an eine "
"Arbeitsstation angeschlossen werden."
msgstr "Das System ausschalten"
#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Legitimierung ist zum Ausschalten des Systems notwendig."
#: ../src/login/org.freedesktop.login1.policy.in.h:27
#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Legitimierung ist zum Herunterfahren des Systems notwendig, während andere "
"Benutzer angemeldet sind."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr ""
"Das System ausschalten, während eine Anwendung anfordert es zu unterbinden"
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Legitimierung ist zum Ausschalten des Systems notwendig, während eine "
"Anwendung anfordert es zu unterbinden."
msgstr "Das System neu starten"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Legitimierung ist zum Neustart des Systems notwendig."
#: ../src/login/org.freedesktop.login1.policy.in.h:33
#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Legitimierung ist zum Neustart des Systems notwendig, während andere "
"Benutzer angemeldet sind."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
"Das System neu starten, während eine Anwendung anfordert es zu unterbinden"
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Legitimierung ist zum Neustart des Systems notwendig, während eine Anwendung "
"anforderte es zu unterbinden."
msgstr "Das System in Bereitschaft versetzen"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Legitimierung ist zum Versetzen des Systems in Bereitschaft notwendig."
#: ../src/login/org.freedesktop.login1.policy.in.h:39
#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Legitimierung ist zum Versetzen des Systems in Bereitschaft notwendig, "
"während andere Benutzer angemeldet sind."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
"Das System in Bereitschaft versetzen, während eine Anwendung anfordert dies "
"zu unterbinden"
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Legitimierung ist zum Versetzen des Systems in Bereitschaft notwendig, "
"während eine Anwendung anfordert dies zu unterbinden."
msgstr "Den Ruhezustand des Systems aktivieren"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr ""
"Legitimierung ist zum Aktivieren des Ruhezustands des Systems notwendig."
#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Legitimierung ist zum Aktivieren des Ruhezustands des Systems notwendig, "
"während andere Benutzer angemeldet sind."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
"Das System in den Ruhezustand versetzen, während eine Anwendung wünscht dies "
"zu verhindern"
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Legitimierung ist zum Versetzen des System in den Ruhezustand notwendig, "
"während eine Anwendung wünscht dies zu verhindern."
# www.freedesktop.org/wiki/Software/systemd/multiseat/
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Legitimierung ist zur Verwaltung aktiver Sitzungen, Benutzern und "
"Arbeitsstationen notwendig."
msgstr "Να επιτρέπεται η προσάρτηση συσκευών στους σταθμούς εργασίας"
#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Απαιτείται πιστοποίηση για προσάρτηση μιας συσκευής σε έναν σταθμό εργασίας."
#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Απαιτείται πιστοποίηση για επαναφορά του τρόπου που οι συσκευές προσαρτώνται "
"στους σταθμούς εργασίας."
msgstr "Σβήσιμο του συστήματος"
#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Απαιτείται πιστοποίηση για την σβήσιμο του συστήματος."
#: ../src/login/org.freedesktop.login1.policy.in.h:27
#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Απαιτείται πιστοποίηση για σβήσιμο του συστήματος ενώ άλλοι χρήστες είναι "
"συνδεμένοι."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Απενεργοποίηση του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί."
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Απαιτείται πιστοποίηση για απενεργοποίηση του συστήματος ενώ μια εφαρμογή "
"ζήτησε να αποτραπεί."
msgstr "Επανεκκίνηση του συστήματος"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Απαιτείται πιστοποίηση για επανεκκίνηση του συστήματος."
#: ../src/login/org.freedesktop.login1.policy.in.h:33
#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Απαιτείται πιστοποίηση για επανεκκίνηση του συστήματος ενώ άλλοι χρήστες "
"είναι συνδεμένοι."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Επανεκκίνηση του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί"
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Απαιτείται πιστοποίηση για επανεκκίνηση του συστήματος ενώ μια εφαρμογή "
"ζήτησε να αποτραπεί."
msgstr "Αναστολή του συστήματος"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Απαιτείται πιστοποίηση για την αναστολή του συστήματος."
#: ../src/login/org.freedesktop.login1.policy.in.h:39
#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Απαιτείται πιστοποίηση για αναστολή του συστήματος ενώ άλλοι χρήστες είναι "
"συνδεμένοι."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Αναστολή του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί"
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Απαιτείται πιστοποίηση για αναστολή του συστήματος ενώ μια εφαρμογή ζήτησε "
"να αποτραπεί."
msgstr "Αδρανοποίηση του συτήματος"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Απαιτείται πιστοποίηση για αδρανοποίηση του συστήματος."
#: ../src/login/org.freedesktop.login1.policy.in.h:45
#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Απαιτείται πιστοποίηση για αδρανοποίηση του συστήματος ενώ άλλοι χρήστες "
"είναι συνδεμένοι."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Αδρανοποίηση του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί"
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Απαιτείται πιστοποίηση για αδρανοποίηση του συστήματος ενώ μια εφαρμογή "
"ζήτησε να αποτραπεί."
#: ../src/login/org.freedesktop.login1.policy.in.h:50
#, fuzzy
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Απαιτείται πιστοποίηση για προσάρτηση μιας συσκευής σε έναν σταθμό εργασίας."
msgstr "Permitir la conexión de dispositivos a los puestos de trabajo"
#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Se requiere autenticación para conectar un dispositivo a un puesto de "
"trabajo."
#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Se requiere autenticación para reconfigurar los dispositivos asociados a "
"cada puesto de trabajo."
msgstr "Apagar el sistema"
#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Se requiere autenticación para apagar el sistema."
#: ../src/login/org.freedesktop.login1.policy.in.h:27
#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Se requiere autenticación para apagar el sistema mientras todavía hay "
"usuarios conectados."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Apagar el sistema a pesar de que una aplicación lo impide"
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Se requiere autenticación para apagar el sistema a pesar de que una "
"aplicación lo impide."
msgstr "Reiniciar el sistema"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Se requiere autenticación para reiniciar el sistema."
#: ../src/login/org.freedesktop.login1.policy.in.h:33
#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Se requiere autenticación para reiniciar el sistema mientras todavía hay "
"usuarios conectados."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Reiniciar el sistema a pesar de que una aplicación lo impide"
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Se requiere autenticación para reiniciar el sistema a pesar de que una "
"aplicación lo impide."
msgstr "Suspender el sistema"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Se requiere autenticación para suspender el sistema."
#: ../src/login/org.freedesktop.login1.policy.in.h:39
#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Se requiere autenticación para suspender el sistema mientras todavía hay "
"usuarios conectados."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Suspender el sistema a pesar de que una aplicación lo impide"
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Se requiere autenticación para suspender el sistema a pesar de que una "
"aplicación lo impide."
msgstr "Hibernar el sistema"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Se requiere autenticación para hibernar el sistema."
#: ../src/login/org.freedesktop.login1.policy.in.h:45
#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Se requiere autenticación para hibernar el sistema mientras todavía hay "
"usuarios conectados."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Hibernar el sistema a pesar de que una aplicación lo impide"
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Se requiere autenticación para hibernar el sistema a pesar de que una "
"aplicación lo impide."
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Se requiere autenticación para administrar las sesiones activas, usuarios y "
"puestos de trabajo."
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-03-07 22:43+0100\n"
+"POT-Creation-Date: 2020-02-03 01:21+0100\n"
"PO-Revision-Date: 2019-03-07 23:09+0100\n"
"Last-Translator: Sylvain Plantefève <sylvain.plantefeve@gmail.com>\n"
"Language-Team: French\n"
msgid "Authentication is required to reload the systemd state."
msgstr "Authentification requise pour recharger l'état de systemd"
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Créer un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr ""
+"Authentification requise pour créer l'espace personnel d'un utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Retirer un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr ""
+"Authentification requise pour retirer l'espace personnel d'un utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Vérifier les identifiants d'un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Authentification requise pour vérifier les identifiants de l'espace "
+"personnel d'un utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Mettre à jour un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+"Authentification requise pour mettre à jour l'espace personnel d'un "
+"utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Retailler un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr "Authentification requise pour retailler un espace personnel."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Changer le mot de passe d'un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid "Authentication is required to change the password of a user's home area."
+msgstr ""
+"Authentification requise pour changer le mot de passe de l'espace personnel "
+"d'un utilisateur."
+
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Définir le nom d'hôte"
msgstr "Permet d'associer des périphériques à des postes (seats)"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Authentification requise pour associer un périphérique à un poste (seat)."
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Authentification requise pour révoquer les associations de périphériques aux "
"postes (seats)."
msgstr "Éteindre le système"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Authentification requise pour éteindre le système."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Authentification requise pour éteindre le système alors que d'autres "
"utilisateurs sont connectés."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Éteindre le système alors qu'une application a demandé de l'empêcher"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Authentification requise pour éteindre le système alors qu'une application a "
"demandé de l'empêcher."
msgstr "Redémarrer le système"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Authentification requise pour redémarrer le système."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Authentification requise pour redémarrer le système alors que d'autres "
"utilisateurs sont connectés."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Redémarrer le système alors qu'une application a demandé de l'empêcher"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Authentification requise pour redémarrer le système alors qu'une application "
"a demandé de l'empêcher."
msgstr "Arrêter le système"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Authentification requise pour arrêter le système."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Authentification requise pour arrêter le système alors que d'autres "
"utilisateurs sont connectés."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Arrêter le système alors qu'une application a demandé de l'empêcher"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Authentification requise pour arrêter le système alors qu'une application a "
msgstr "Mettre le système en veille"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Authentification requise pour mettre le système en veille."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Authentification requise pour mettre le système en veille alors que d'autres "
"utilisateurs sont connectés."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
"Mettre le système en veille alors qu'une application a demandé de l'empêcher"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Authentification requise pour mettre le système en veille alors qu'une "
"application a demandé de l'empêcher."
msgstr "Mettre le système en hibernation"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Authentification requise pour mettre le système en hibernation."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Authentification requise pour mettre le système en hibernation alors que "
"d'autres utilisateurs sont connectés."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
"Mettre le système en hibernation alors qu'une application a demandé de "
"l'empêcher"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Authentification requise pour mettre le système en hibernation alors qu'une "
"application a demandé de l'empêcher."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Authentification requise pour gérer les sessions actives, les utilisateurs "
"et les postes (seats)."
"actives."
#: src/login/org.freedesktop.login1.policy:341
-msgid "Indicate to the firmware to boot to setup interface"
-msgstr ""
-"Indiquer au micrologiciel de démarrer sur l'interface de configuration"
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "Définir la « raison » du redémarrage dans le noyau"
#: src/login/org.freedesktop.login1.policy:342
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr ""
+"Authentification requise pour définir la « raison » du redémarrage dans le "
+"noyau."
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Indicate to the firmware to boot to setup interface"
+msgstr "Indiquer au micrologiciel de démarrer sur l'interface de configuration"
+
+#: src/login/org.freedesktop.login1.policy:353
msgid ""
"Authentication is required to indicate to the firmware to boot to setup "
"interface."
"Authentification requise pour indiquer au micrologiciel de démarrer sur "
"l'interface de configuration."
-#: 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 "Indiquer au programme d'amorçage d'afficher le menu au démarrage"
-#: src/login/org.freedesktop.login1.policy:353
+#: 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 ""
-"Authentification requise pour indiquer au programme d'amorçage d'afficher "
-"le menu au démarrage."
+"Authentification requise pour indiquer au programme d'amorçage d'afficher le "
+"menu au démarrage."
-#: src/login/org.freedesktop.login1.policy:363
+#: src/login/org.freedesktop.login1.policy:374
msgid "Indicate to the boot loader to boot a specific entry"
msgstr "Indiquer au programme d'amorçage de démarrer une entrée spécifique"
-#: src/login/org.freedesktop.login1.policy:364
+#: 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."
"Authentification requise pour indiquer au programme d'amorçage de démarrer "
"une entrée spécifique."
-#: src/login/org.freedesktop.login1.policy:374
+#: src/login/org.freedesktop.login1.policy:385
msgid "Set a wall message"
msgstr "Définir un message wall"
-#: src/login/org.freedesktop.login1.policy:375
+#: src/login/org.freedesktop.login1.policy:386
msgid "Authentication is required to set a wall message"
msgstr "Authentification requise pour définir un message wall."
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Changer de Session"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Authentification requise pour changer de terminal virtuel."
+
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Connexion dans un conteneur local"
"Authentification requise pour gérer les images locales de machines "
"virtuelles (VM) et de conteneurs."
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Définir les serveurs NTP"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Authentification requise pour définir les serveurs NTP."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Définir les serveurs DNS"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Authentification requise pour définir les serveurs DNS."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Définir les domaines"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Authentification requise pour définir les domaines."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Définir la route par défaut"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr "Authentification requise pour définir la route par défaut."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Activer/désactiver LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Authentification requise pour activer ou désactiver LLMNR."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Activer/désactiver la multidiffusion DNS"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+"Authentification requise pour activer ou désactiver la multidiffusion DNS."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Activer/désactiver DNS sur TLS"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr "Authentification requise pour activer ou désactiver DNS sur TLS."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Activer/désactiver DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Authentification requise pour activer ou désactiver DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Définir les Negative Trust Anchors DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Authentification requise pour définir les Negative Trust Anchors DNSSEC."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Réinitialiser les paramètres NTP"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr "Authentification requise pour réinitialiser les paramètres NTP."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Réinitialiser les paramètres DNS"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr "Authentification requise pour réinitialiser les paramètres DNS."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Renouveler les adresses dynamiques"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Authentification requise pour renouveler les adresses dynamiques."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Recharger les paramètres réseau"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "Authentification requise pour recharger les paramètres réseau."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Reconfigurer une interface réseau"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Authentification requise pour reconfigurer une interface réseau."
+
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgstr "Inspecter une image de service portable"
msgid "Authentication is required to unregister a DNS-SD service"
msgstr "Authentification requise pour retirer un service DNS-SD"
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Réinitialiser les paramètres de résolution de noms"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+"Authentification requise pour réinitialiser les paramètres de résolution de "
+"noms."
+
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "Définir l'heure du système"
"Authentification requise pour activer ou désactiver la synchronisation de "
"l'heure avec le réseau."
-#: src/core/dbus-unit.c:326
+#: src/core/dbus-unit.c:355
msgid "Authentication is required to start '$(unit)'."
msgstr "Authentification requise pour démarrer « $(unit) »."
-#: src/core/dbus-unit.c:327
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to stop '$(unit)'."
msgstr "Authentification requise pour arrêter « $(unit) »."
-#: src/core/dbus-unit.c:328
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to reload '$(unit)'."
msgstr "Authentification requise pour recharger « $(unit) »."
-#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
+#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359
msgid "Authentication is required to restart '$(unit)'."
msgstr "Authentification requise pour redémarrer « $(unit) »."
-#: src/core/dbus-unit.c:437
+#: src/core/dbus-unit.c:531
msgid ""
"Authentication is required to send a UNIX signal to the processes of "
"'$(unit)'."
"Authentification requise pour envoyer un signal UNIX aux processus de "
"« $(unit) »."
-#: src/core/dbus-unit.c:468
+#: src/core/dbus-unit.c:562
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Authentification requise pour réinitialiser l'état d'« échec » de "
"« $(unit) »."
-#: src/core/dbus-unit.c:501
+#: src/core/dbus-unit.c:595
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Authentification requise pour définir des propriétés de « $(unit) »."
+#: src/core/dbus-unit.c:704
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Authentification requise pour supprimer les fichiers et les dossiers "
+"associés à « $(unit) »."
+
#~ msgid "Authentication is required to kill '$(unit)'."
#~ msgstr "Authentification requise pour tuer « $(unit) »."
msgstr "Permitir conectar anexar a asentos"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Requírese autenticación para anexar un dispositivo a un asento."
#: src/login/org.freedesktop.login1.policy:148
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Requírese autenticación para reiniciar como os dispositivos están anexados "
"aos asentos."
msgstr "Apagar o sistema"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Requírese autenticación para apagar o sistema."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Requírese autenticación para apagar o sistema mentres hai usuarios con unha "
"sesión iniciada."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Apagar o sistema cando unha aplicación solicitou a súa inhibición"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Requírese autenticación para apagar o sistema mentres unha aplicación "
"solicitou a súa inhibición."
msgstr "Reiniciar o sistema"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Requírese autenticación para reiniciar o sistema."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Requírese autenticación para reiniciar o sistema mentres outros usuarios "
"teñen unha sesión iniciada."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Reiniciar o sistema cando unha aplicación solicitou a súa inhibición"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Requírese autenticación para reiniciar o sistema mentres unha aplicación "
"solicitou a súa inhibición."
msgstr "Deter o sistema"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Requírese autenticación para deter o sistema."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Requírese autenticación para deter o sistema mentres outros usuarios teñen "
"unha sesión iniciada."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Deter o sistema cando unha aplicación solicitou a súa inhibición"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Requírese autenticación para deter o sistema mentres unha aplicación "
msgstr "Suspender o sistema"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Requírese autenticación para suspender o sistema."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Requírese autenticación para suspender o sistema mentres outros usuarios "
"teñen unha sesión iniciada."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Suspender o sistema cando unha aplicación solicitou a súa inhibición"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Requírese autenticación para suspender o sistema mentres unha aplicación "
"solicitou a súa inhibición."
msgstr "Hibernar o sistema"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Requírese autenticación para hibernar o sistema."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Requírese autenticación para hibernar o sistema mentres outros usuarios "
"teñen unha sesión iniciada."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Hibernar o sistema cando unha aplicación solicitou a súa inhibición"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Requírese autenticación para hibernar o sistema mentres unha aplicación "
"solicitou a súa inhibición."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Requírese autenticación para xestionar as sesións, usuarios e asentos "
"activos."
msgid ""
msgstr ""
"Project-Id-Version: systemd master\n"
-"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2016-04-27 11:57+0100\n"
-"PO-Revision-Date: 2016-04-27 12:11+0200\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-03-08 12:55+0100\n"
+"PO-Revision-Date: 2020-03-08 12:57+0100\n"
+"Last-Translator: gogo <linux.hr@protonmail.com>\n"
"Language-Team: \n"
+"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.8.7.1\n"
-"Last-Translator: gogo <trebelnik2@gmail.com>com>\n"
+"X-Generator: Poedit 2.0.6\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"Language: hr\n"
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
+#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
msgstr "Pošalji lozinku natrag u sustav"
-#: ../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 "Potrebna je ovjera za slanje upisane lozinke natrag u sustav."
-#: ../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 "Upravljajte uslugama sustava ili drugim jedinicama"
-#: ../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 "Potrebna je ovjera za upravljanje uslugama sustava ili jedinicama."
-#: ../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 "Upravljajte uslugama sustava ili datotekama jedinica"
-#: ../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 ""
"Potrebna je ovjera za upravljanje uslugama sustava ili datotekama jedinica."
-#: ../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 "Postavite ili uklonite varijable okruženja sustava i usluga"
-#: ../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."
"Potrebna je ovjera za postavljanje ili uklanjanje varijabla okruženja "
"sustava i usluga."
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
+#: src/core/org.freedesktop.systemd1.policy.in:64
msgid "Reload the systemd state"
msgstr "Ponovno učitaj systemd stanje"
-#: ../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 "Potrebna je ovjera za ponovno učitavanje systemd stanja."
+msgstr "Potrebna je ovjera za ponovno učitavanja systemd stanja."
+
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Stvori osobni prostor"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Potrebna je ovjera za stvaranje osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Ukloni osobni prostor"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Potrebna je ovjera za uklanjanje osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Provjeri vjerodajnice osobnog prostora"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Potrebna je ovjera za provjeru vjerodajnica osobnog prostora korisnika."
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Nadopuni osobni prostor"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "Potrebna je ovjera za nadopunu osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Promjeni veličinu osobnog prostora korisnika"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr "Potrebna je ovjera za promjenu veličine osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Promijeni lozinku osobnog prostora"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr "Potrebna je ovjera za promjenu lozinke osobnog prostora korisnika."
+
+#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Postavi naziv računala"
-#: ../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 "Potrebna je ovjera za postavljanje naziva lokalnog računala."
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
+#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static host name"
msgstr "Postavi nepromjenjivi naziv račumala"
-#: ../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."
"Potrebna je ovjera za postavljenje nepromjenjivog naziva lokalnog računala, "
"kao i prijatnog naziva računala."
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
+#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
msgstr "Postavi informacije računala"
-#: ../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 "Potrebna je ovjera za postavljanje informacije lokalnog računala."
-#: ../src/import/org.freedesktop.import1.policy.in.h:1
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Prikaži UUID proizvoda"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Potrebna je ovjera za prikaz UUID-a proizvoda."
+
+#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
msgstr "Uvezi VM ili spremnik slike"
-#: ../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 "Potrebna je ovjera za uvoz WM ili spremnika slike"
-#: ../src/import/org.freedesktop.import1.policy.in.h:3
+#: src/import/org.freedesktop.import1.policy:32
msgid "Export a VM or container image"
msgstr "Izvezi VM ili spremnik slike"
-#: ../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 "Potrebna je ovjera za izvoz WM ili spremnika slike"
-#: ../src/import/org.freedesktop.import1.policy.in.h:5
+#: src/import/org.freedesktop.import1.policy:42
msgid "Download a VM or container image"
msgstr "Preuzmi VM ili spremnik slike"
-#: ../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 "Potrebna je ovjera za preuzimanje VM ili spremnika slike."
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:1
+#: src/locale/org.freedesktop.locale1.policy:22
msgid "Set system locale"
msgstr "Postavi sustav lokalizacije"
-#: ../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 "Potrebna je ovjera za postavljanje sustava lokalizacije."
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:3
+#: src/locale/org.freedesktop.locale1.policy:33
msgid "Set system keyboard settings"
msgstr "Postavi postavke tipkovnice sustava"
-#: ../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 "Potrebna je ovjera za postavljanje postavki tipkovnice sustava."
-#: ../src/login/org.freedesktop.login1.policy.in.h:1
+#: src/login/org.freedesktop.login1.policy:22
msgid "Allow applications to inhibit system shutdown"
-msgstr "Dopusti aplikacijama zaustavljanje isključivanja sustava"
+msgstr "Dopusti aplikacijama spriječavanje isključivanja sustava"
-#: ../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 ""
-"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje isključivanja "
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju isključivanja "
"sustava."
-#: ../src/login/org.freedesktop.login1.policy.in.h:3
+#: src/login/org.freedesktop.login1.policy:33
msgid "Allow applications to delay system shutdown"
msgstr "Dopusti aplikacijama odgodu isključivanja sustava"
-#: ../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 ""
-"Potrebna je ovjera za dopuštanje aplikacijama odgode isključivanja sustava."
+"Potrebna je ovjera za dopuštanje aplikacijama u odgodi isključivanja sustava."
-#: ../src/login/org.freedesktop.login1.policy.in.h:5
+#: src/login/org.freedesktop.login1.policy:44
msgid "Allow applications to inhibit system sleep"
-msgstr "Dopusti aplikacijama zaustavljanje spavanja sustava"
+msgstr "Dopusti aplikacijama spriječavanje spavanja sustava"
-#: ../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 ""
-"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanja spavanja sustava."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju spavanja "
+"sustava."
-#: ../src/login/org.freedesktop.login1.policy.in.h:7
+#: src/login/org.freedesktop.login1.policy:55
msgid "Allow applications to delay system sleep"
msgstr "Dopusti aplikacijama odgodu spavanja sustava"
-#: ../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 "Potrebna je ovjera za odgodu spavanja sustava."
-#: ../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 "Dopusti aplikacijama zaustavljanje automatskog suspendiranja sustava"
+msgstr "Dopusti aplikacijama spriječavanje automatskog suspendiranja sustava"
-#: ../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 ""
-"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje automatskog "
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju automatskog "
"suspendiranja sustava."
-#: ../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 ""
-"Dopusti aplikacijama sprječavanje rukovanja sustava tipkom isključivanja"
+"Dopusti aplikacijama spriječavanje rukovanja sustava tipkom isključivanja"
-#: ../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 ""
-"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
-"tipkom isključivanja."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju rukovanja "
+"sustava tipkom isključivanja."
-#: ../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 "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom suspenzije"
+msgstr "Dopusti aplikacijama spriječavanje rukovanja sustava tipkom suspenzije"
-#: ../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 ""
-"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
-"tipkom suspenzije."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju rukovanja "
+"sustava tipkom suspenzije."
-#: ../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 "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom hibernacije"
+msgstr ""
+"Dopusti aplikacijama spriječavanje rukovanja sustava tipkom hibernacije"
-#: ../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 ""
-"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
-"tipkom hibernacije."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju rukovanja "
+"sustava tipkom hibernacije."
-#: ../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 "Dopusti aplikacijama sprječavanje rukovanja sustava preklopnicama"
+msgstr "Dopusti aplikacijama spriječavanje rukovanja sustava preklopnicama"
-#: ../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 ""
-"Potrebna je ovjera za dopuštenje sprječavanja rukovanja sustava "
+"Potrebna je ovjera za dopuštanje spriječavanja rukovanja sustava "
"preklopnicama."
-#: ../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 "Dopusti neprijavljenim korisnicima pokretanje programa"
+
+#: src/login/org.freedesktop.login1.policy:118
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+"Potrebna je ovjera za dopuštanje neprijavljenim korisnicima pokretanje "
+"programa."
+
+#: src/login/org.freedesktop.login1.policy:127
msgid "Allow non-logged-in users to run programs"
msgstr "Dopusti neprijavljenim korisnicima pokretanje programa"
-#: ../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 ""
-"Potrebna je ovjera za dopuštenje neprijavljenim korisnicima pokretanje "
+"Potrebna je ovjera za dopuštanje neprijavljenim korisnicima pokretanje "
"programa."
-#: ../src/login/org.freedesktop.login1.policy.in.h:21
+#: src/login/org.freedesktop.login1.policy:137
msgid "Allow attaching devices to seats"
msgstr "Dopusti povezivanje uređaja skupu sesija i hardvera"
-#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+#: src/login/org.freedesktop.login1.policy:138
+msgid "Authentication is required to attach a device to a seat."
msgstr "Potrebna je ovjera za povezivanje uređaja sa skupom sesija i hardvera."
-#: ../src/login/org.freedesktop.login1.policy.in.h:23
+#: src/login/org.freedesktop.login1.policy:148
msgid "Flush device to seat attachments"
msgstr "Ukloni povezani uređaj sa skupa sesija i hardvera"
-#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+#: src/login/org.freedesktop.login1.policy:149
+msgid "Authentication is required to reset how devices are attached to seats."
msgstr ""
"Potrebna je ovjera za obnovu povezivanja uređaja sa skupom sesija i hardvera."
-#: ../src/login/org.freedesktop.login1.policy.in.h:25
+#: src/login/org.freedesktop.login1.policy:158
msgid "Power off the system"
msgstr "Isključi sustav"
-#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+#: src/login/org.freedesktop.login1.policy:159
+msgid "Authentication is required to power off the system."
msgstr "Potrebna je ovjera za isključivanje sustava."
-#: ../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 "Isključi sustav kada su ostali korisnici prijavljeni"
-#: ../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 "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Potrebna je ovjera za isključivanje sustava kada su ostali korisnici "
"prijavljeni."
-#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:180
+msgid "Power off the system while an application is inhibiting this"
msgstr ""
-"Isključi sustav kada je aplikacija zatražila zaustavljanje isključivanja"
+"Isključi sustav kada je aplikacija zatražila spriječavanje isključivanja"
-#: ../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."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
msgstr ""
"Potrebna je ovjera za isključivanje sustava kada je aplikacija zatražila "
-"zaustavljanje isključivanja."
+"spriječavanje isključivanja."
-#: ../src/login/org.freedesktop.login1.policy.in.h:31
+#: src/login/org.freedesktop.login1.policy:191
msgid "Reboot the system"
msgstr "Ponovno pokreni sustav"
-#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+#: src/login/org.freedesktop.login1.policy:192
+msgid "Authentication is required to reboot the system."
msgstr "Potrebna je ovjera za ponovno pokretanje sustava."
-#: ../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 "Ponovno pokreni sustav kada su ostali korisnici prijavljeni"
-#: ../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."
+"Authentication is required to reboot the system while other users are logged "
+"in."
msgstr ""
"Potrebna je ovjera za ponovno pokretanje sustava kada su ostali korisnici "
"prijavljeni."
-#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:213
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
-"Ponovno pokreni sustav kada je aplikacija zatražila zaustavljanje ponovnog "
+"Ponovno pokreni sustav kada je aplikacija zatražila spriječavanje ponovnog "
"pokretanja"
-#: ../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."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
msgstr ""
"Potrebna je ovjera za ponovno pokretanje sustava kada je aplikacija "
-"zatražila zaustavljanje ponovnog pokretanja."
+"zatražila spriječavanje ponovnog pokretanja."
+
+#: src/login/org.freedesktop.login1.policy:224
+msgid "Halt the system"
+msgstr "Zaustavi sustav"
+
+#: src/login/org.freedesktop.login1.policy:225
+msgid "Authentication is required to halt the system."
+msgstr "Potrebna je ovjera za zaustavljanje sustava."
+
+#: src/login/org.freedesktop.login1.policy:235
+msgid "Halt the system while other users are logged in"
+msgstr "Zaustavi sustav kada su ostali korisnici prijavljeni"
+
+#: src/login/org.freedesktop.login1.policy:236
+msgid ""
+"Authentication is required to halt the system while other users are logged "
+"in."
+msgstr ""
+"Potrebna je ovjera za zaustavljanje sustava kada su drugi korisnici "
+"prijavljeni."
+
+#: src/login/org.freedesktop.login1.policy:246
+msgid "Halt the system while an application is inhibiting this"
+msgstr ""
+"Zaustavi sustav kada je aplikacija zatražila spriječavanje zaustavljanja"
-#: ../src/login/org.freedesktop.login1.policy.in.h:37
+#: src/login/org.freedesktop.login1.policy:247
+msgid ""
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
+msgstr ""
+"Potrebna je ovjera za zaustavljanje sustava kada je aplikacija zatražila "
+"spriječavanje hibernacije."
+
+#: src/login/org.freedesktop.login1.policy:257
msgid "Suspend the system"
msgstr "Suspendiraj sustav"
-#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+#: src/login/org.freedesktop.login1.policy:258
+msgid "Authentication is required to suspend the system."
msgstr "Potrebna je ovjera za suspendiranje sustava."
-#: ../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 "Suspendiraj sustav kada su drugi korisnici prijavljeni"
-#: ../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 "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Potrebna je ovjera za suspendiranje sustava kada su drugi korisnici "
"prijavljeni."
-#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:278
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
-"Suspendiraj sustav kada je aplikacija zatražila zaustavljanje suspendiranja"
+"Suspendiraj sustav kada je aplikacija zatražila spriječavanje suspendiranja"
-#: ../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."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
msgstr ""
"Potrebna je ovjera za suspendiranje sustava kada je aplikacija zatražila "
-"zaustavljanje suspendiranja."
+"spriječavanje suspendiranja."
-#: ../src/login/org.freedesktop.login1.policy.in.h:43
+#: src/login/org.freedesktop.login1.policy:289
msgid "Hibernate the system"
msgstr "Hiberniraj sustav"
-#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+#: src/login/org.freedesktop.login1.policy:290
+msgid "Authentication is required to hibernate the system."
msgstr "Potrebna je ovjera za hibernaciju sustava."
-#: ../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 "Hiberniraj sustav kada su ostali korisnici prijavljeni."
+msgstr "Hiberniraj sustav kada su ostali korisnici prijavljeni"
-#: ../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 "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Potrebna je ovjera za hibernaciju sustava kada su drugi korisnici "
"prijavljeni."
-#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:310
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
-"Hiberniraj sustav kada je aplikacija zatražila zaustavljanje hibernacije"
+"Hiberniraj sustav kada je aplikacija zatražila spriječavanje hibernacije"
-#: ../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."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
msgstr ""
"Potrebna je ovjera za hibernaciju sustava kada je aplikacija zatražila "
-"zaustavljanje hibernacije."
+"spriječavanje hibernacije."
-#: ../src/login/org.freedesktop.login1.policy.in.h:49
+#: src/login/org.freedesktop.login1.policy:321
msgid "Manage active sessions, users and seats"
msgstr ""
"Upravljanje aktivnim sesijama, korisnicima i skupovima sesija i hardvera"
-#: ../src/login/org.freedesktop.login1.policy.in.h:50
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+#: src/login/org.freedesktop.login1.policy:322
+msgid "Authentication is required to manage active sessions, users and seats."
msgstr ""
"Potrebna je ovjera za upravljanje aktivnim sesijama, korisnicima i skupovima "
"sesija i hardvera."
-#: ../src/login/org.freedesktop.login1.policy.in.h:51
+#: src/login/org.freedesktop.login1.policy:331
msgid "Lock or unlock active sessions"
msgstr "Zaključavanje ili otključavanje aktivne sesije"
-#: ../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 "Potrebna je ovjera za zaključavanje ili otključavanje aktivne sesije."
-#: ../src/login/org.freedesktop.login1.policy.in.h:53
-msgid "Allow indication to the firmware to boot to setup interface"
-msgstr "Dopusti najavu frimveru za pokretanje sučelja postavljanja"
+#: src/login/org.freedesktop.login1.policy:341
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "Postavi \"reason\" ponovnog pokretanja u kernel"
+
+#: src/login/org.freedesktop.login1.policy:342
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr ""
+"Potrebna je ovjera za postaviti \"reason\" ponovnog pokretanja u kernelu."
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Indicate to the firmware to boot to setup interface"
+msgstr "Najavi firmveru da pokrene sučelje postavljanja"
-#: ../src/login/org.freedesktop.login1.policy.in.h:54
+#: src/login/org.freedesktop.login1.policy:353
msgid ""
"Authentication is required to indicate to the firmware to boot to setup "
"interface."
-msgstr "Potrebna je ovjera najave frimvera za pokretanje sučelja postavljanja."
+msgstr "Potrebna je ovjera za najavu firmveru da pokrene sučelje postavljanja."
+
+#: src/login/org.freedesktop.login1.policy:363
+msgid "Indicate to the boot loader to boot to the boot loader menu"
+msgstr "Najavi učitaču pokretanja da pokrene izbornik učitača pokretanja"
-#: ../src/login/org.freedesktop.login1.policy.in.h:55
+#: 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 ""
+"Potrebna je ovjera za najavu učitaču pokretanja da pokrene izbornik učitača "
+"pokretanja."
+
+#: src/login/org.freedesktop.login1.policy:374
+msgid "Indicate to the boot loader to boot a specific entry"
+msgstr "Najavi učitaču pokretanja da pokrene određeni unos"
+
+#: 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 ""
+"Potrebna je ovjera za najavu učitaču pokretanja da pokrene određeni unos."
+
+#: src/login/org.freedesktop.login1.policy:385
msgid "Set a wall message"
msgstr "Postavljanje zaslonske pruke"
-#: ../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 "Potrebna je ovjera za postavljanje zaslonske pruke."
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:1
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Promijeni sesiju"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Potrebna je ovjera za promjenu virtualnog terminala."
+
+#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Prijavi se u lokalni spremnik"
-#: ../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 "Potrebna je ovjera za prijavu u lokalni spremnik."
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#: src/machine/org.freedesktop.machine1.policy:32
msgid "Log into the local host"
msgstr "Prijava na lokalno računalo"
-#: ../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 "Potrebna je ovjera za prijavu na lokalno račuanlo."
-#: ../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 "Pokretanje ljuske u lokalnom spremniku"
-#: ../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 "Potrebna je ovjera za pokretanje ljuske u lokalnom spremniku."
-#: ../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 "Pokretanje ljuske na lokalnom računalu"
-#: ../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 "Potrebna je ovjera za pokretanje ljuske na lokalnom računalu."
-#: ../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 "Pokretanje pseudo TTY na lokalnom spremniku"
-#: ../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 "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom spremniku."
-#: ../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 "Pokretanje pseudo TTY na lokalnom računalu"
-#: ../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 "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom računalu."
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
+#: src/machine/org.freedesktop.machine1.policy:84
msgid "Manage local virtual machines and containers"
msgstr "Upravljanje lokalnim vurtualnim strojevima i spremnicima"
-#: ../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 ""
"Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i "
"spremnicima."
-#: ../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 "Upravljanje lokalnim vurtualnim strojevima i spremnicima slika"
-#: ../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."
"Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i "
"spremnicima slika."
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Postavi NTP poslužitelje"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Potrebna je ovjera za postavljanje NTP poslužitelja."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Postavi DNS poslužitelje"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Potrebna je ovjera za postavljanje DNS poslužitelja."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Postavi domene"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Potrebna je ovjera za postavljanje domena."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Postavi uobičajenu rutu"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr "Potrebna je ovjera za postavljanje uobičajene rute."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Omogući/Onemogući LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Potrebna je ovjera za LLMNR omogućavanje ili onemogućavanje."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Omogući/Onemogući glatko osvježavanje"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+"Potrebna je ovjera za omogućavanje ili onemogućavanje DNS grupnog emitiranja."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Omogući/Onemogući DNS putem TLS-a"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr "Potrebna je ovjera za omogućavanje/onemogućavanje DNS-a putem TLS-a."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Omogući/Onemogući DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Potrebna je ovjera za DNSSEC omogućavanje ili onemogućavanje."
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Postavi DNSSEC negativna pouzdana sidrišta"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Potrebna je ovjera za postavljanje DNSSEC negativnih pouzdanih sidrišta."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Vrati izvorne NTP postavke"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr "Potrebna je ovjera za vraćanje izvornih NTP postavki."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Vrati izvorne DNS postavke"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr "Potrebna je ovjera za vraćanje izvornih DNS postavki."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr "DHCP poslužitelj šalje poruku prisilne ponovne uspostave"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr "Potrebna je ovjera za slanje poruke prisilne ponovne uspostave."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Renew dynamic addresses"
+msgstr "Ponovno uspostavi promjenjive adrese"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Potrebna je ovjera za ponovno uspostavljanje promjenjivih adresa."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reload network settings"
+msgstr "Ponovno učitaj postavke mreže"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reload network settings."
+msgstr "Potrebna je ovjera za ponovno učitavanje postavka mreže."
+
+#: src/network/org.freedesktop.network1.policy:176
+msgid "Reconfigure network interface"
+msgstr "Ponovno podesi mrežno sučelje"
+
+#: src/network/org.freedesktop.network1.policy:177
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Potrebna je ovjera za ponovno podešavanje mrežnog sučelja."
+
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr "Provjeri prijenosnu sliku usluge"
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr "Potrebna je ovjera za provjeru prijenosne slike usluge."
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr "Dodaj ili ukloni prijenosnu sliku usluge"
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+"Potrebna je ovjera za dodavanje ili uklanjanje prijenosne slike usluge."
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr "Obriši ili promijeni prijenosnu sliku usluge"
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr "Potrebna je ovjera za brisnje ili promijenu prijenosne slike usluge."
+
+#: src/resolve/org.freedesktop.resolve1.policy:22
+msgid "Register a DNS-SD service"
+msgstr "Registriraj DNS-SD uslugu"
+
+#: src/resolve/org.freedesktop.resolve1.policy:23
+msgid "Authentication is required to register a DNS-SD service"
+msgstr "Potrebna je ovjera za registriranje DNS-SD usluge."
+
+#: src/resolve/org.freedesktop.resolve1.policy:33
+msgid "Unregister a DNS-SD service"
+msgstr "Ukloni registriraciju DNS-SD usluge"
+
+#: src/resolve/org.freedesktop.resolve1.policy:34
+msgid "Authentication is required to unregister a DNS-SD service"
+msgstr "Potrebna je ovjera za uklanjanje registracije DNS-SD usluge."
+
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Vrati izvorni naziv postavki razlučivosti"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr "Potrebna je ovjera za vraćanje izvornog naziva postavki razlučivosti."
+
+#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "Postavi vrijeme sustava"
-#: ../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 "Potrebna je ovjera za postavljanje vremena sustava."
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3
+#: src/timedate/org.freedesktop.timedate1.policy:33
msgid "Set system timezone"
msgstr "Postavi vremensku zonu sustava"
-#: ../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 "Potrebna je ovjera za postavljanje vremenske zone sustava."
-#: ../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 "Postavi RTC u lokalnu vremensku zonu ili 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 ""
"Potrebna je ovjera za postavljanje RTC-a u lokalnu vremensku zonu ili 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 "Uključi ili isključi mrežno uklađivanje vremena"
-#: ../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."
"Potrebna je ovjera za uključivanje ili isključivanje mrežnog usklađivanja "
"vremena."
-#: ../src/core/dbus-unit.c:428
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to start '$(unit)'."
msgstr "Potrebna je ovjera za pokretanje '$(unit)'."
-#: ../src/core/dbus-unit.c:429
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to stop '$(unit)'."
msgstr "Potrebna je ovjera za zaustavljanje '$(unit)'."
-#: ../src/core/dbus-unit.c:430
+#: src/core/dbus-unit.c:358
msgid "Authentication is required to reload '$(unit)'."
msgstr "Potrebna je ovjera za ponovno učitavnje '$(unit)'."
-#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
msgid "Authentication is required to restart '$(unit)'."
msgstr "Potrebna je ovjera za ponovno pokretanje'$(unit)'."
-#: ../src/core/dbus-unit.c:535
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "Potrebna je ovjera za ubijanje '$(unit)'."
+#: src/core/dbus-unit.c:532
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr "Potrebna je ovjera za slanje UNIX signala u procese '$(unit)'."
-#: ../src/core/dbus-unit.c:565
+#: src/core/dbus-unit.c:563
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr "Potrebna je ovjera za vraćanje \"neuspjelog\" stanja '$(unit)'."
-#: ../src/core/dbus-unit.c:597
+#: src/core/dbus-unit.c:596
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Potrebna je ovjera za postavljanje svojstava na '$(unit)'."
+
+#: src/core/dbus-unit.c:705
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Potrebna je ovjera za za brisanje datoteka i direktorija pridruženih sa "
+"'$(unit)'."
msgstr "Eszközök csatolásának engedélyezése munkaállomásokhoz"
#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
"munkaállomáshoz"
#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Hitelesítés szükséges az eszközök munkaállomásokhoz csatolásainak "
"alaphelyzetbe állításához."
msgstr "A rendszer kikapcsolása"
#: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Hitelesítés szükséges a rendszer kikapcsolásához."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Hitelesítés szükséges a rendszer kikapcsolásához miközben be vannak "
"jelentkezve más felhasználók."
#: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr ""
"A rendszer kikapcsolása miközben egy alkalmazás ennek meggátlását kérte"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Hitelesítés szükséges a rendszer kikapcsolásához miközben egy alkalmazás "
"ennek meggátlását kérte."
msgstr "A rendszer újraindítása"
#: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Hitelesítés szükséges a rendszer újraindításához."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Hitelesítés szükséges a rendszer újraindításához miközben be vannak "
"jelentkezve más felhasználók."
#: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
"A rendszer újraindítása miközben egy alkalmazás ennek meggátlását kérte"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Hitelesítés szükséges a rendszer újraindításához miközben egy alkalmazás "
"ennek meggátlását kérte."
msgstr "A rendszer felfüggesztése"
#: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Hitelesítés szükséges a rendszer felfüggesztéséhez."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Hitelesítés szükséges a rendszer felfüggesztéséhez miközben be vannak "
"jelentkezve más felhasználók."
#: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
"A rendszer felfüggesztése miközben egy alkalmazás ennek meggátlását kérte"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Hitelesítés szükséges a rendszer felfüggesztéséhez miközben egy alkalmazás "
"ennek meggátlását kérte."
msgstr "A rendszer hibernálása"
#: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Hitelesítés szükséges a rendszer hibernálásához."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Hitelesítés szükséges a rendszer hibernálásához miközben be vannak "
"jelentkezve más felhasználók."
#: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "A rendszer hibernálása miközben egy alkalmazás ennek meggátlását kérte"
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Hitelesítés szükséges a rendszer hibernálásához miközben egy alkalmazás "
"ennek meggátlását kérte."
#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
"kezeléséhez."
msgstr "Ijinkan mencantolkan perangkat ke seat"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Otentikasi diperlukan untuk mencantol suatu perangkat ke sebuah seat."
#: src/login/org.freedesktop.login1.policy:148
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Otentikasi diperlukan untuk me-reset bagaimana perangkat dicantolkan ke seat."
msgstr "Matikan daya sistem"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Otentikasi diperlukan untuk mematikan daya sistem."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Otentikasi diperlukan untuk mematikan daya sistem ketika pengguna lain "
"sedang log masuk."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Matikan daya sistem ketika sebuah aplikasi meminta untuk mencegahnya"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Otentikasi diperlukan untuk mematikan daya sistem ketika sebuah aplikasi "
"meminta untuk mencegahnya."
msgstr "Boot ulang sistem"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Otentikasi diperlukan untuk mem-boot ulang sistem."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Otentikasi diperlukan untuk mem-boot ulang sistem ketika pengguna lain "
"sedang log masuk."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Boot ulang sistem ketika sebuah aplikasi meminta untuk mencegahnya"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Otentikasi diperlukan untuk mem-boot ulang sistem ketika sebuah aplikasi "
"meminta untuk mencegahnya."
msgstr "Halt sistem"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Otentikasi diperlukan untuk meng-halt sistem."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Otentikasi diperlukan untuk meng-halt sistem ketika pengguna lain sedang log "
"masuk."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Halt sistem ketika sebuah aplikasi meminta untuk mencegahnya"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Otentikasi diperlukan untuk meng-halt sistem ketika sebuah aplikasi meminta "
msgstr "Suspensikan sistem"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Otentikasi diperlukan untuk mensuspensi sistem."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Otentikasi diperlukan untuk mensuspensi sistem ketika pengguna lain sedang "
"log masuk."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Suspensikan sistem ketika sebuah aplikasi meminta untuk mencegahnya"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Otentikasi diperlukan untuk mensuspensi sistem ketika suatu aplikasi meminta "
"untuk mencegahnya."
msgstr "Hibernasikan sistem"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Otentikasi diperlukan untuk menghibernasi sistem."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Otentikasi diperlukan untuk menghibernasi sistem ketika pengguna lain sedang "
"log masuk."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Hibernasikan sistem ketika sebuah aplikasi meminta untuk mencegahnya."
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Otentikasi diperlukan untuk menghibernasi sistem ketika sebuah aplikasi "
"meminta mencegahnya."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr "Otentikasi diperlukan untuk mengelola seat, pengguna, dan sesi aktif."
#: src/login/org.freedesktop.login1.policy:331
#
# Italian translation for systemd package
# Traduzione in italiano per il pacchetto systemd
-# Daniele Medri <dmedri@gmail.com>, 2013-2019.
+# Daniele Medri <dmedri@gmail.com>, 2013-2020.
#
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
-"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-05-05 17:02+0200\n"
-"PO-Revision-Date: 2019-05-05 17:13+0200\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-27 17:10+0100\n"
+"PO-Revision-Date: 2020-02-27 17:22+0100\n"
"Last-Translator: Daniele Medri <dmedri@gmail.com>\n"
"Language-Team: Italian\n"
"Language: it\n"
msgid "Authentication is required to reload the systemd state."
msgstr "Autenticazione richiesta per riavviare lo stato di sistemd."
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Crea un'area home"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to creat a user's home area."
+msgstr "Autenticazione richiesta per creare un'area home per l'utente."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Rimuovi un'area home"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remov a user's home area."
+msgstr "Autenticazione richiesta per rimuovere un'area home per l'utente."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Controlla credenziali di un'area home"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Autenticazione richiesta per controllare le credenziali di un'area home per "
+"l'utente."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Aggiorna un'area home"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to updat a user's home area."
+msgstr "Autenticazione richiesta per aggiornare un'area home per l'utente."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Ridimensiona un'area home"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resiz a user's home area."
+msgstr "Autenticazione richiesta per ridimensionare l'area home dell'utente."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Cambia password di un'area home"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid "Authentication is required to chang the password of a user's home area."
+msgstr ""
+"Autenticazione richiesta per cambiare le password per l'area home "
+"dell'utente."
+
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Configura il nome host"
#: src/login/org.freedesktop.login1.policy:34
msgid "Authentication is required for an application to delay system shutdown."
msgstr ""
-"Autenticazione richiesta ad un'applicazione per ritardare lo spegnimento "
-"del sistema."
+"Autenticazione richiesta ad un'applicazione per ritardare lo spegnimento del "
+"sistema."
#: src/login/org.freedesktop.login1.policy:44
msgid "Allow applications to inhibit system sleep"
#: 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"
-"accensione"
+"Consenti alle applicazioni di inibire la gestione di sistema del "
+"tastoaccensione"
#: src/login/org.freedesktop.login1.policy:76
msgid ""
msgstr "Consenti di collegare dispositivi alle postazioni"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Autenticazione richiesta per collegare un dispositivo ad una postazione."
msgstr "Scollega i dispositivi dalla postazione"
#: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
msgstr ""
"Autenticazione richiesta per ripristinare come i dispositivi sono collegati "
"alle postazioni."
msgstr "Spegni il sistema"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Autenticazione richiesta per spegnere il sistema."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Autenticazione richiesta per spegnere il sistema mentre altri utenti sono "
"connessi."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Spegni il sistema mentre un'applicazione chiede di inibirne l'azione"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
msgstr ""
"Autenticazione richiesta per spegnere il sistema mentre un'applicazione "
"chiede di inibirne l'azione."
msgstr "Riavvia il sistema"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Autenticazione richiesta per riavviare il sistema."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
msgstr ""
"Autenticazione richiesta per riavviare il sistema mentre altri utenti sono "
"connessi."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Riavvia il sistema mentre un'applicazione chiede di inibirne l'azione"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
msgstr ""
"Autenticazione richiesta per riavviare il sistema mentre un'applicazione "
"chiede di inibirne l'azione."
msgstr "Ferma il sistema"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Autenticazione richiesta per fermare il sistema."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
msgstr ""
"Autenticazione richiesta per fermare il sistema mentre altri utenti sono "
"connessi."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Ferma il sistema mentre un'applicazione chiede di inibirne l'azione"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
msgstr ""
-"Autenticazione richiesta per fermare il sistema mentre un'applicazione "
-"chiede di inibirne l'azione."
+"Autenticazione richiesta per ibernare il sistema mentre un'applicazione ne "
+"inibisce l'azione."
#: src/login/org.freedesktop.login1.policy:257
msgid "Suspend the system"
msgstr "Sospendi il sistema"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Autenticazione richiesta per sospendere il sistema."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Autenticazione richiesta per sospendere il sistema mentre altri utenti sono "
"connessi."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Sospendi il sistema mentre un'applicazione chiede di inibirne l'azione"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
msgstr ""
"Autenticazione richiesta per sospendere il sistema mentre un'applicazione "
"chiede di inibirne l'azione."
msgstr "Iberna il sistema"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Autenticazione richiesta per ibernare il sistema."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Autenticazione richiesta per ibernare il sistema mentre altri utenti sono "
"connessi."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Iberna il sistema mentre un'applicazione chiede di inibirne l'azione"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
msgstr ""
"Autenticazione richiesta per ibernare il sistema mentre un'applicazione "
"chiede di inibirne l'azione."
msgstr "Gestione delle sessioni attive, utenti e postazioni"
#: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
msgstr ""
"Autenticazione richiesta per gestire le sessioni attive, gli utenti e le "
"postazioni."
msgid "Authentication is required to set a wall message"
msgstr "Autenticazione richiesta per configurare un messaggio per gli utenti"
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Cambia sessione"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Autenticazione richiesta per cambiare il terminale virtuale."
+
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Accedi ad un container locale"
"Autenticazione richiesta per gestire le immagini delle virtual machine e dei "
"container locali."
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Configura server NTP"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Autenticazione richiesta per configurare i server NTP."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Configura i server DNS"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Autenticazione richiesta per configurare i server DNS."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Configura domini"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Autenticazione richiesta per configurare i domini."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Configura la tabella di instradamento"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr ""
+"Autenticazione richiesta per configurare la tabella di instradamento "
+"predefinita."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Abilita/disabilita LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Autenticazione richiesta per attivare/disattivare LLMNR."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Abilita/disabilita DNS multicast"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr "Autenticazione richiesta per abilitare/disabilitare DNS multicast."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Abilita/disabilita DNS su TLS"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr "Autenticazione richiesta per abilitare o disabilitare DNS su TLS."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Abilita/disabilita DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Autenticazione richiesta per abilitare o disabilitare DNSSEC."
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Configura DNSSEC Negative Trust Anchors"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Autenticazione richiesta per configurare DNSSEC Negative Trust Anchors."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Ripristina configurazioni NTP"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr "Autenticazione richiesta per ripristinare le configurazioni NTP."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Ripristina configurazioni DNS"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr "Autenticazione richiesta per ripristinare le configurazioni DNS."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Rinnova indirizzi dinamici"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Autenticazione richiesta per rinnovare gli indirizzi dinamici."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Ricarica configurazioni di rete"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "Autenticazione richiesta per ricaricare le configurazioni di rete."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Riconfigura interfaccia di rete"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Autenticazione richiesta per riconfigurare l'interfaccia di rete."
+
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgstr "Ispeziona un'immagine di servizio portabile"
msgstr ""
"Autenticazione richiesta per annullare la registrazione di un servizio DNS-SD"
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Ripristina le configurazioni per la risoluzione dei nomi"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+"Autenticazione richiesta per ripristinare le configurazioni per la "
+"risoluzione dei nomi."
+
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "Configura l'orario di sistema"
"Autenticazione richiesta per verificare se la sincronizzazione dell'orario "
"in rete deve essere attivata."
-#: src/core/dbus-unit.c:317
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to start '$(unit)'."
msgstr "Autenticazione richiesta per avviare '$(unit)'."
-#: src/core/dbus-unit.c:318
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to stop '$(unit)'."
msgstr "Autenticazione richiesta per fermare '$(unit)'."
-#: src/core/dbus-unit.c:319
+#: src/core/dbus-unit.c:358
msgid "Authentication is required to reload '$(unit)'."
msgstr "Autenticazione richiesta per ricaricare '$(unit)'."
-#: src/core/dbus-unit.c:320 src/core/dbus-unit.c:321
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
msgid "Authentication is required to restart '$(unit)'."
msgstr "Autenticazione richiesta per riavviare '$(unit)'."
-#: src/core/dbus-unit.c:493
+#: src/core/dbus-unit.c:532
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:524
+#: src/core/dbus-unit.c:563
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:557
+#: src/core/dbus-unit.c:596
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Autenticazione richiesta per configurare le proprietà di '$(unit)'."
+
+#: src/core/dbus-unit.c:705
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Autenticazione richiesta per eliminare i file e le directory associate a "
+"'$(unit)'."
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-09-21 20:13+0900\n"
+"POT-Creation-Date: 2020-02-02 23:20+0900\n"
"PO-Revision-Date: 2018-10-27 07:41+0900\n"
"Last-Translator: Yu Watanabe <watanabe.yu+github@gmail.com>\n"
"Language-Team: \n"
msgid "Authentication is required to reload the systemd state."
msgstr "systemdの状態を再読込するには認証が必要です。"
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "ホーム領域の作成"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "ユーザのホーム領域を作成するには認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "ホーム領域の削除"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "ユーザのホーム領域の削除には認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "ホーム領域の認証情報の確認"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr "ユーザのホーム領域に対する認証情報の確認には認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "ホーム領域の更新"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "ユーザのホーム領域の更新には認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "ホーム領域のサイズ変更"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr "ユーザのホーム領域のサイズ変更には認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "ホーム領域のパスワード変更"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid "Authentication is required to change the password of a user's home area."
+msgstr "ユーザのホーム領域のパスワードを変更するには認証が必要です。"
+
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "ホスト名の設定"
msgstr "シートにデバイスを接続することを許可"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "シートにデバイスを接続するには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:148
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr "デバイスのシートへの接続をリセットするには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:158
msgstr "システムの電源を切る"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "システムの電源を切るには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"他のユーザがログインしている状態でシステムの電源を切るには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "アプリケーションが使用されている状態でシステムの電源を切る"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"アプリケーションが使用されている状態でシステムの電源を切るには認証が必要で"
"す。"
msgstr "システムの再起動"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "システムの再起動には認証が必要です。"
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot 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"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "アプリケーションが使用されている状態でシステムを再起動する"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"アプリケーションが使用されている状態でシステムを再起動するには認証が必要で"
"す。"
msgstr "システムの停止"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "システムを停止するには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt 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"
+msgid "Halt the system while an application is inhibiting this"
msgstr "アプリケーションが使用されている状態でシステムを停止する"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"アプリケーションが使用されている状態でシステムを停止するには認証が必要です。"
msgstr "システムのサスペンド"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "システムのサスペンドには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend 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"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "アプリケーションが使用されている状態でシステムをサスペンドする"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"アプリケーションが使用されている状態でシステムをサスペンドするには認証が必要"
"です。"
msgstr "システムのハイバネート"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "システムのハイバネートには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"他のユーザがログインしている状態でシステムをハイバネートするには認証が必要で"
"す。"
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "アプリケーションが使用されている状態でシステムをハイバネートする"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"アプリケーションが使用されている状態でシステムをハイバネートするには認証が必"
"要です。"
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr "アクティブなセッションやユーザ,シートを管理するには認証が必要です。"
#: src/login/org.freedesktop.login1.policy:331
msgid "Authentication is required to set a wall message"
msgstr "全ユーザへのメッセージを設定するには認証が必要です。"
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "セッションの変更"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "仮想ターミナルを変更するには認証が必要です。"
+
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "ローカルなコンテナへログイン"
msgid "Authentication is required to reset DNS settings."
msgstr "DNSの設定を破棄するには認証が必要です。"
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "動的アドレスの更新"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "動的アドレスの更新には認証が必要です。"
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "ネットワークの設定の再読み込み"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "ネットワークの設定を再読み込みするには認証が必要です。"
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "ネットワークインターフェイスの再設定"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "ネットワークインターフェイスの再設定には認証が必要です。"
+
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgstr "ポータブルサービスイメージの読み込み"
"shall be enabled."
msgstr "ネットワーク経由の時刻同期を有効もしくは無効にするには認証が必要です。"
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:355
msgid "Authentication is required to start '$(unit)'."
msgstr "'$(unit)'を開始するには認証が必要です。"
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to stop '$(unit)'."
msgstr "'$(unit)'を停止するには認証が必要です。"
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to reload '$(unit)'."
msgstr "'$(unit)'を再読込するには認証が必要です。"
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359
msgid "Authentication is required to restart '$(unit)'."
msgstr "'$(unit)'を再起動するには認証が必要です。"
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:531
msgid ""
"Authentication is required to send a UNIX signal to the processes of "
"'$(unit)'."
msgstr "'$(unit)'のプロセスにUNIXシグナルを送るには認証が必要です。"
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:562
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr "'$(unit)'の「失敗」状態をリセットするには認証が必要です。"
-#: src/core/dbus-unit.c:594
+#: src/core/dbus-unit.c:595
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "'$(unit)'のプロパティを設定するには認証が必要です。"
-#: src/core/dbus-unit.c:703
+#: src/core/dbus-unit.c:704
msgid ""
"Authentication is required to delete files and directories associated with "
"'$(unit)'."
msgstr "시트에 장치 부착 허용"
#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "시트에 장치 부착을 허용하려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:23
#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr "시트에 붙인 장치 상태를 초기화하려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:25
msgstr "시스템 끄기"
#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "시스템을 끄려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:27
#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr "다른 사용자가 로그인 했을 때 시스템 전원을 끄려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "프로그램이 시스템을 끄지 못하게 요청할 때 시스템 전원 끄기"
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"프로그램이 시스템을 끄지 못하게 요청할 때 시스템 전원을 끄려면 인증이 필요합"
"니다."
msgstr "시스템 다시 시작"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "시스템을 다시 시작하려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:33
#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"다른 사용자가 로그인 했을 때 시스템을 다시 시작하려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "프로그램이 시스템을 다시 시작하지 못하게 요청할 때 시스템 다시 시작"
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"프로그램이 시스템을 다시 시작하지 못하게 요청할 때 시스템을 다시 시작하려면 "
"인증이 필요합니다."
msgstr "시스템 절전 상태 진입"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "시스템을 절전 상태로 놓으려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:39
#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"다른 사용자가 로그인 했을 때 시스템을 절전 상태로 놓으려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "프로그램이 절전 상태 진입을 못하게 요청할 때 시스템 절전 상태 진입"
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"프로그램이 절전 상태 진입을 못하게 요청할 때 시스템을 절전 상태로 놓으려면 인"
"증이 필요합니다."
msgstr "시스템 최대 절전 상태 진입"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "시스템을 최대 절전 상태로 놓으려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:45
#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"다른 사용자가 로그인 했을 때 시스템을 최대 절전 상태로 놓으려면 인증이 필요합"
"니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
"프로그램이 최대 절전 상태 진입을 못하게 요청할 때 시스템 최대 절전 상태 진입"
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"프로그램이 최대 절전 상태 진입을 못하게 요청할 때 시스템을 최대 절전 상태로 "
"놓으려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr "활성 세션, 사용자 시트를 관리하려면 인증이 필요합니다."
#: ../src/login/org.freedesktop.login1.policy.in.h:51
msgstr "Leisti prijungti įrenginius prie darbo vietų"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Norint prijungti įrenginį prie darbo vietos, reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Norint atstatyti tai, kaip įrenginiai yra prijungti prie darbo vietų, reikia "
"nustatyti tapatybę."
msgstr "Išjungti sistemos maitinimą"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Norint išjungti sistemos maitinimą, reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Norint išjungti sistemos maitinimą nepaisant kitų prisijungusių naudotojų, "
"reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Išjungti sistemos maitinimą, nors programa paprašė tai sulaikyti"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Norint išjungti sistemos maitinimą, nepaisant to, kad programa paprašė tai "
"sulaikyti, reikia nustatyti tapatybę."
msgstr "Paleisti sistemą iš naujo"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Norint paleisti sistemą iš naujo, reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Norint paleisti sistemą iš naujo nepaisant kitų prisijungusių naudotojų, "
"reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Paleisti sistemą iš naujo, nors programa paprašė tai sulaikyti"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Norint paleisti sistemą iš naujo, nepaisant to, kad programa paprašė tai "
"sulaikyti, reikia nustatyti tapatybę."
msgstr "Stabdyti sistemą"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Norint stabdyti sistemą, reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Norint stabdyti sistemą nepaisant kitų prisijungusių naudotojų, reikia "
"nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Stabdyti sistemą, nors programa paprašė tai sulaikyti"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Norint stabdyti sistemą, nepaisant to, kad programa paprašė tai sulaikyti, "
msgstr "Pristabdyti sistemą"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Norint pristabdyti sistemą, reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Norint pristabdyti sistemą nepaisant kitų prisijungusių naudotojų, reikia "
"nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Pristabdyti sistemą, nors programa paprašė tai sulaikyti"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Norint pristabdyti sistemą, nepaisant to, kad programa paprašė tai "
"sulaikyti, reikia nustatyti tapatybę."
msgstr "Užmigdyti sistemą"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Norint užmigdyti sistemą, reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Norint užmigdyti sistemą nepaisant kitų prisijungusių naudotojų, reikia "
"nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Užmigdyti sistemą, nors programa paprašė tai sulaikyti"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Norint užmigdyti sistemą, nepaisant to, kad programa paprašė tai sulaikyti, "
"reikia nustatyti tapatybę."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Norint tvarkyti aktyvius seansus, naudotojus ir darbo vietas, reikia "
"nustatyti tapatybę."
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-10-26 14:02+0000\n"
-"PO-Revision-Date: 2019-10-26 16:05+0200\n"
+"POT-Creation-Date: 2020-02-29 15:12+0000\n"
+"PO-Revision-Date: 2020-03-01 14:45+0100\n"
"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
"Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n"
"Language: pl\n"
msgid "Authentication is required to reload the systemd state."
msgstr "Wymagane jest uwierzytelnienie, aby ponownie wczytać stan systemd."
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Utworzenie przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby utworzyć przestrzeń domową użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Usunięcie przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby usunąć przestrzeń domową użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Sprawdzenie danych uwierzytelniających przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby sprawdzić dane uwierzytelniające "
+"przestrzeni domowej użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Aktualizacja przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby zaktualizować przestrzeń domową "
+"użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Zmiana rozmiaru przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby zmienić rozmiar przestrzeni domowej "
+"użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Zmiana hasła przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby zmienić hasło przestrzeni domowej "
+"użytkownika."
+
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Ustawienie nazwy komputera"
msgstr "Zezwolenie na podłączanie urządzeń do stanowisk"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Wymagane jest uwierzytelnienie, aby podłączyć urządzenie do stanowiska."
msgstr "Usunięcie podłączenia urządzeń do stanowisk"
#: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie ustawić sposób podłączenia "
"urządzeń do stanowisk."
msgstr "Wyłączenie systemu"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Wymagane jest uwierzytelnienie, aby wyłączyć system."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy są zalogowani "
"inni użytkownicy."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
-msgstr "Wyłączenie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Power off the system while an application is inhibiting this"
+msgstr "Wyłączenie systemu, kiedy program je wstrzymuje"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
msgstr ""
-"Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy program zażądał "
-"jego wstrzymania."
+"Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy program to "
+"wstrzymuje."
#: src/login/org.freedesktop.login1.policy:191
msgid "Reboot the system"
msgstr "Ponowne uruchomienie systemu"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Wymagane jest uwierzytelnienie, aby ponownie uruchomić system."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie uruchomić system, kiedy są "
"zalogowani inni użytkownicy."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
-msgstr "Ponowne uruchomienie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Reboot the system while an application is inhibiting this"
+msgstr "Ponowne uruchomienie systemu, kiedy program je wstrzymuje"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie uruchomić system, kiedy program "
-"zażądał jego wstrzymania."
+"to wstrzymuje."
#: src/login/org.freedesktop.login1.policy:224
msgid "Halt the system"
msgstr "Zatrzymanie systemu"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać system."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
msgstr ""
"Wymagane jest uwierzytelnienie, aby zatrzymać system, kiedy są zalogowani "
"inni użytkownicy."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
-msgstr "Zatrzymanie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Halt the system while an application is inhibiting this"
+msgstr "Zatrzymanie systemu, kiedy program je wstrzymuje"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
msgstr ""
-"Wymagane jest uwierzytelnienie, aby zatrzymać system, kiedy program zażądał "
-"jego wstrzymania."
+"Wymagane jest uwierzytelnienie, aby zatrzymać system, kiedy program to "
+"wstrzymuje."
#: src/login/org.freedesktop.login1.policy:257
msgid "Suspend the system"
msgstr "Uśpienie systemu"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Wymagane jest uwierzytelnienie, aby uśpić system."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Wymagane jest uwierzytelnienie, aby uśpić system, kiedy są zalogowani inni "
"użytkownicy."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
-msgstr "Uśpienie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Suspend the system while an application is inhibiting this"
+msgstr "Uśpienie systemu, kiedy program je wstrzymuje"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
msgstr ""
-"Wymagane jest uwierzytelnienie, aby uśpić system, kiedy program zażądał jego "
-"wstrzymania."
+"Wymagane jest uwierzytelnienie, aby uśpić system, kiedy program to "
+"wstrzymuje."
#: src/login/org.freedesktop.login1.policy:289
msgid "Hibernate the system"
msgstr "Hibernacja systemu"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Wymagane jest uwierzytelnienie, aby zahibernować system."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy są zalogowani "
"inni użytkownicy."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
-msgstr "Hibernacja systemu, kiedy program zażądał jej wstrzymania"
+msgid "Hibernate the system while an application is inhibiting this"
+msgstr "Hibernacja systemu, kiedy program ją wstrzymuje"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
msgstr ""
-"Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy program "
-"zażądał jej wstrzymania."
+"Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy program to "
+"wstrzymuje."
#: src/login/org.freedesktop.login1.policy:321
msgid "Manage active sessions, users and seats"
msgstr "Zarządzanie aktywnymi sesjami, użytkownikami i stanowiskami"
#: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
msgstr ""
"Wymagane jest uwierzytelnienie, aby zarządzać aktywnymi sesjami, "
"użytkownikami i stanowiskami."
msgid "Authentication is required to set a wall message"
msgstr "Wymagane jest uwierzytelnienie, aby ustawić komunikat wall"
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Zmiana sesji"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Wymagane jest uwierzytelnienie, aby zmienić terminal wirtualny."
+
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Logowanie do lokalnego kontenera"
"Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację "
"czasu przez sieć."
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to start '$(unit)'."
msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”."
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to stop '$(unit)'."
msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”."
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:358
msgid "Authentication is required to reload '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”."
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
msgid "Authentication is required to restart '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”."
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:532
msgid ""
"Authentication is required to send a UNIX signal to the processes of "
"'$(unit)'."
"Wymagane jest uwierzytelnienie, aby wysłać sygnał uniksowy do procesów "
"jednostki „$(unit)”."
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:563
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby przywrócić stan „failed” (niepowodzenia) "
"jednostki „$(unit)”."
-#: src/core/dbus-unit.c:594
+#: src/core/dbus-unit.c:596
msgid "Authentication is required to set properties on '$(unit)'."
msgstr ""
"Wymagane jest uwierzytelnienie, aby ustawić właściwości jednostki „$(unit)”."
-#: src/core/dbus-unit.c:703
+#: src/core/dbus-unit.c:705
msgid ""
"Authentication is required to delete files and directories associated with "
"'$(unit)'."
msgstr "Permitir conectar dispositivos em estações"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "É necessária autenticação para conectar um dispositivo em uma estação."
#: src/login/org.freedesktop.login1.policy:148
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"É necessária autenticação para redefinir a quantidade de dispositivos "
"conectados na estação."
msgstr "Desligar o sistema"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "É necessária autenticação para desligar o sistema."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"É necessária autenticação para desligar o sistema enquanto outros usuários "
"estão conectados."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Desligar o sistema enquanto um aplicativo solicitou inibição"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"É necessária autenticação para desligar o sistema enquanto um aplicativo "
"solicitou inibição."
msgstr "Reiniciar o sistema"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "É necessária autenticação para reiniciar o sistema."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"É necessária autenticação para reiniciar o sistema enquanto outros usuários "
"estiverem conectados."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Reiniciar o sistema enquanto um aplicativo solicitou inibição"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"É necessária autenticação para reiniciar o sistema enquanto um aplicativo "
"solicitou inibição."
msgstr "Parar o sistema"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "É necessária autenticação para parar o sistema."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"É necessária autenticação para parar o sistema enquanto outros usuários "
"estejam logados."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Parar o sistema enquanto um aplicativo solicitou inibição"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"É necessária autenticação para parar o sistema enquanto um aplicativo "
msgstr "Suspender o sistema"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "É necessária autenticação para suspender o sistema."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"É necessária autenticação para suspender o sistema enquanto outros usuários "
"estiverem conectados."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Suspender o sistema enquanto um aplicativo solicitou inibição"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"É necessária autenticação para suspender o sistema enquanto um aplicativo "
"solicitou inibição."
msgstr "Hibernar o sistema"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "É necessária autenticação para hibernar o sistema."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"É necessária autenticação para hibernar o sistema enquanto outros usuários "
"estiverem conectados."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Hibernar o sistema enquanto um aplicativo solicitou inibição"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"É necessária autenticação para hibernar o sistema enquanto um aplicativo "
"solicitou inibição."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"É necessária autenticação para gerenciar estações, usuários e sessões ativas."
msgstr "Permite atașarea dispozitivelor la locuri"
#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Autentificarea este necesară pentru a atașa un dispozitiv la un loc."
#: ../src/login/org.freedesktop.login1.policy.in.h:25
#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Autentificarea este necesară pentru a restabili cum dispozitivele sunt "
"atașate la locuri."
msgstr "Oprește sistemul"
#: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Este necesară autentificarea pentru oprirea sistemului."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Autentificarea este necesară pentru oprirea sistemului în timp ce alți "
"utilizatori sunt autentificați."
#: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Oprește sistemul în timp ce o aplicație a cerut să împiedice asta"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificarea este necesară pentru oprirea sistemului în timp ce o "
"aplicație a cerut să împiedice asta."
msgstr "Repornește sistemul"
#: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Autentificarea este necesară pentru repornirea sistemului."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Autentificarea este necesară pentru repornirea sistemului în timp ce alți "
"utilizatori autentificați."
#: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Repornește sistemul în timp ce o aplicație a cerut să împiedice asta"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificarea este necesară pentru repornirea sistemului în timp ce o "
"aplicație a cerut să împiedice asta."
msgstr "Suspendă sistemul"
#: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Este necesară autentificarea pentru suspendarea sistemului."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Autentificarea este necesară pentru suspendarea sistemului timp ce alți "
"utilizatori autentificați."
#: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Suspendă sistemul în timp ce o aplicație a cerut să împiedice asta"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificarea este necesară pentru suspendarea sistemului în timp ce o "
"aplicație a cerut să împiedice asta."
msgstr "Hiberneaza sistemul"
#: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Este necesară autentificarea pentru hibernarea sistemului."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Autentificarea este necesară pentru hibernare a sistemului în timp ce alți "
"utilizatori sunt autentificați."
#: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Hibernează sistemul în timp ce o aplicație a cerut să împiedice asta"
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Autentificarea este necesară pentru hibernarea sistemului în timp ce o "
"aplicație a cerut să împiedice asta."
#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Autentificarea este necesară pentru gestionarea sesiunilor active, "
"utilizatorilor și locurilor."
# SPDX-License-Identifier: LGPL-2.1+
#
# translation of ru.po to Rissian
+#
# Julia Dronova <juliette.tux@gmail.com>, 2013.
# Sergey Ptashnick <0comffdiz@inbox.ru>, 2013-2018.
-#
+# Vladimir Yerilov <openmindead@gmail.com>, 2020.
msgid ""
msgstr ""
"Project-Id-Version: systemd\n"
-"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2015-11-22 16:37+0100\n"
-"PO-Revision-Date: 2018-09-01 18:46+0300\n"
-"Last-Translator: Sergey Ptashnick <0comffdiz@inbox.ru>\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-03-03 00:56+1000\n"
+"PO-Revision-Date: 2020-03-03 16:05+1000\n"
+"Last-Translator: Vladimir Yerilov <openmindead@gmail.com>\n"
"Language: ru\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"
+"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.12.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
"Чтобы заставить systemd перечитать конфигурацию, необходимо пройти "
"аутентификацию."
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Создать домашнее пространство"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr ""
+"Чтобы создать домашнее пространство пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Удалить домашнее пространство"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr ""
+"Чтобы удалить домашнее пространство пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Проверить учётные данные домашнего пространства"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Чтобы проверить учётные данные для домашнего пространства пользователя,"
+" необходимо пройти аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Обновить домашнее пространство"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+"Чтобы обновить домашнее пространство пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Изменить размер домашнего пространства"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Чтобы изменить размер домашнего пространства пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Изменить пароль для домашнего пространства"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr ""
+"Чтобы изменить пароль для домашнего пространства пользователя, необходимо"
+" пройти аутентификацию."
+
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Настроить имя компьютера"
#: src/login/org.freedesktop.login1.policy:117
msgid "Allow non-logged-in user to run programs"
-msgstr ""
-"Разрешить работу программ в фоновом режиме после завершения сеанса"
+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:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Чтобы разрешить подключение устройств к рабочим местам, необходимо пройти "
"аутентификацию."
msgstr "Сбросить привязки устройств к рабочим местам"
#: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
msgstr ""
"Чтобы сбросить привязки устройств к рабочим местам, необходимо пройти "
"аутентификацию."
msgstr "Выключить систему"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Чтобы выключить систему, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Чтобы выключить систему, несмотря на то, что в ней работают другие "
"пользователи, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr ""
"Выключить систему, несмотря на то, что приложение запросило блокировку "
"выключения"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
msgstr ""
"Чтобы выключить систему, несмотря на то, что приложение запросило блокировку "
"выключения, необходимо пройти аутентификацию."
msgstr "Перезагрузить систему"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Чтобы перезагрузить систему, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot 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"
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
"Перезагрузить систему, несмотря на то, что приложение запросило блокировку "
"выключения"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
msgstr ""
"Чтобы перезагрузить систему, несмотря на то, что приложение запросило "
"блокировку выключения, необходимо пройти аутентификацию."
msgstr "Остановить систему"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Чтобы остановить систему, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt 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"
+msgid "Halt the system while an application is inhibiting this"
msgstr ""
-"Остановить систему, несмотря на то, что приложение запросило блокировку "
+"Остановить систему несмотря на то, что приложение запросило блокировку "
"выключения"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
msgstr ""
-"Чтобы остановить систему, несмотря на то, что приложение запросило "
-"блокировку выключения, необходимо пройти аутентификацию."
+"Чтобы остановить систему несмотря на то, что приложение запросило блокировку"
+" выключения, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:257
msgid "Suspend the system"
msgstr "Перевести систему в ждущий режим"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr ""
"Чтобы перевести систему в ждущий режим, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend 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"
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
"Перевести систему в ждущий режим, несмотря на то, что приложение запросило "
"блокировку"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
msgstr ""
"Чтобы перевести систему в ждущий режим, несмотря на то, что приложение "
"запросило блокировку, необходимо пройти аутентификацию."
msgstr "Перевести систему в спящий режим"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr ""
"Чтобы перевести систему в спящий режим, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Чтобы перевести систему в спящий режим, несмотря на то, что в ней работают "
"другие пользователи, необходимо пройти аутентификацию."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
"Перевести систему в спящий режим, несмотря на то, что приложение запросило "
"блокировку"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
msgstr ""
"Чтобы перевести систему в спящий режим, несмотря на то, что приложение "
"запросило блокировку, необходимо пройти аутентификацию."
msgstr "Управление текущими сеансами, пользователями и рабочими местами"
#: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
msgstr ""
"Для управления текущими сеансами, пользователями и рабочими местами, "
"необходимо пройти аутентификацию."
"аутентификацию."
#: src/login/org.freedesktop.login1.policy:341
-msgid "Allow indication to the firmware to boot to setup interface"
-msgstr "РазÑ\80еÑ\88иÑ\82Ñ\8c загÑ\80Ñ\83зкÑ\83 в Ñ\80ежиме наÑ\81Ñ\82Ñ\80ойки пÑ\80оÑ\88ивки маÑ\82еÑ\80инÑ\81кой плаÑ\82Ñ\8b"
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "УÑ\81Ñ\82ановиÑ\82Ñ\8c \"пÑ\80иÑ\87инÑ\83\" пеÑ\80езагÑ\80Ñ\83зки"
#: 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."
msgstr ""
-"Чтобы разрешить загрузку в режиме настройки прошивки материнской платы, "
+"Чтобы запустить режим настройки прошивки материнской платы, "
+"необходимо пройти аутентификацию."
+
+#: 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:351
+#: 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/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Сменить сессию"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Чтобы сменить виртуальный терминал, необходимо пройти аутентификацию."
+
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Зайти в локальный контейнер"
"Для управления образами виртуальных машин и контейнеров, необходимо пройти "
"аутентификацию."
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Задать NTP-серверы"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Чтобы задать NTP-серверы, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Задать DNS-серверы"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Чтобы задать DNS-серверы, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Задать домены"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Чтобы задать домены, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Задать маршрут по умолчанию"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr "Чтобы задать маршрут по умолчанию, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Включить/отключить LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Чтобы включить или отключить LLMNR, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Включить/отключить multicast DNS"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+"Чтобы включить или отключить multicast DNS, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Включить/отключить DNS поверх TLS"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr ""
+"Чтобы включить или отключить DNS поверх TLS, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Включить/отключить DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Чтобы включить или отключить DNSSEC, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Задать DNSSEC Negative Trust Anchors"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Чтобы задать DNSSEC Negative Trust Anchors, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Восстановить настройки NTP по умолчанию"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr ""
+"Чтобы сбросить локальные настройки NTP, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Восстановить настройки DNS по умолчанию"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr ""
+"Чтобы сбросить локальные настройки DNS, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Обновить динамические адреса"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Чтобы обновить динамические адреса, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Перечитать настройки сети"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr ""
+"Чтобы заставить systemd перечитать настройки сети, необходимо пройти "
+"аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Изменить конфигурацию сетевого интерфейса"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+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 "Чтобы прочитать образ переносимой службы, необходимо пройти "
-"аутентификацию."
+msgstr ""
+"ЧÑ\82обÑ\8b пÑ\80оÑ\87иÑ\82аÑ\82Ñ\8c обÑ\80аз пеÑ\80еноÑ\81имой Ñ\81лÑ\83жбÑ\8b, необÑ\85одимо пÑ\80ойÑ\82и аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86иÑ\8e."
#: src/portable/org.freedesktop.portable1.policy:23
msgid "Attach or detach a portable service image"
#: src/portable/org.freedesktop.portable1.policy:24
msgid ""
"Authentication is required to attach or detach a portable service image."
-msgstr "Чтобы подключить или отключить образ переносимой службы, необходимо "
-"пройти аутентификацию."
+msgstr ""
+"Чтобы подключить или отключить образ переносимой службы, необходимо пройти "
+"аутентификацию."
#: src/portable/org.freedesktop.portable1.policy:34
msgid "Delete or modify portable service image"
#: src/resolve/org.freedesktop.resolve1.policy:23
msgid "Authentication is required to register a DNS-SD service"
-msgstr "Чтобы зарегистрировать службу в DNS-SD, необходимо пройти "
-"аутентификацию."
+msgstr ""
+"ЧÑ\82обÑ\8b заÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80оваÑ\82Ñ\8c Ñ\81лÑ\83жбÑ\83 в DNS-SD, необÑ\85одимо пÑ\80ойÑ\82и аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86иÑ\8e."
#: src/resolve/org.freedesktop.resolve1.policy:33
msgid "Unregister a DNS-SD service"
msgid "Authentication is required to unregister a DNS-SD service"
msgstr "Чтобы удалить службу из DNS-SD, необходимо пройти аутентификацию."
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Вернуть настройки разрешения имён по умолчанию"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+"Чтобы сбросить настройки разрешения имён, необходимо пройти аутентификацию."
+
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "Настроить системное время"
"Чтобы включить или выключить синхронизацию времени по сети, необходимо "
"пройти аутентификацию."
-#: src/core/dbus-unit.c:326
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to start '$(unit)'."
msgstr "Чтобы запустить «$(unit)», необходимо пройти аутентификацию."
-#: src/core/dbus-unit.c:327
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to stop '$(unit)'."
msgstr "Чтобы остановить «$(unit)», необходимо пройти аутентификацию."
-#: src/core/dbus-unit.c:328
+#: src/core/dbus-unit.c:358
msgid "Authentication is required to reload '$(unit)'."
msgstr ""
"Чтобы заставить «$(unit)» перечитать конфигурацию, необходимо пройти "
"аутентификацию."
-#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
msgid "Authentication is required to restart '$(unit)'."
msgstr "Чтобы перезапустить «$(unit)», необходимо пройти аутентификацию."
-#: src/core/dbus-unit.c:437
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "Чтобы убить юнит «$(unit)», необходимо пройти аутентификацию."
+#: src/core/dbus-unit.c:532
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr ""
+"Чтобы отправить сигнал UNIX процессам юнита «$(unit)», необходимо пройти"
+" аутентификацию."
-#: src/core/dbus-unit.c:468
+#: src/core/dbus-unit.c:563
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr ""
"Чтобы сбросить состояние «failed» у юнита «$(unit)», необходимо пройти "
"аутентификацию."
-#: src/core/dbus-unit.c:501
+#: src/core/dbus-unit.c:596
msgid "Authentication is required to set properties on '$(unit)'."
-msgstr "Чтобы изменить параметры юнита «$(unit)», необходимо пройти "
-"аутентификацию."
+msgstr ""
+"ЧÑ\82обÑ\8b измениÑ\82Ñ\8c паÑ\80амеÑ\82Ñ\80Ñ\8b Ñ\8eниÑ\82а «$(unit)», необÑ\85одимо пÑ\80ойÑ\82и аÑ\83Ñ\82енÑ\82иÑ\84икаÑ\86иÑ\8e."
-#~ msgid "Press Ctrl+C to cancel all filesystem checks in progress"
-#~ msgstr ""
-#~ "Чтобы прервать все запущенные проверки файловых систем, нажмите Ctrl+C"
-
-# There is no difference between "на 2 дисках" (plural==1) and "на 5 дисках" (plural==2)
-#~ msgid "Checking in progress on %d disk (%3.1f%% complete)"
-#~ msgid_plural "Checking in progress on %d disks (%3.1f%% complete)"
-#~ msgstr[0] ""
-#~ "Проверяется целостность файловой системы на %d диске (выполнено %3.1f%%)"
-#~ msgstr[1] ""
-#~ "Проверяется целостность файловых систем на %d дисках (выполнено %3.1f%%)"
-#~ msgstr[2] ""
-#~ "Проверяется целостность файловых систем на %d дисках (выполнено %3.1f%%)"
+#: src/core/dbus-unit.c:705
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Чтобы удалить файлы и директории, относящиеся к юниту «$(unit)», необходимо"
+" пройти аутентификацию."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:25
#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:27
msgstr "Vypnutie systému"
#: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Vyžaduje sa overenie totožnosti na vypnutie systému."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Vyžaduje sa overenie totožnosti na vypnutie systému, pokiaľ sú prihlásení "
"iní používatelia."
#: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:33
msgstr "Reštart systému"
#: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Vyžaduje sa overenie totožnosti na reštartovanie systému."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Vyžaduje sa overenie totožnosti na reštartovanie systému, pokiaľ sú "
"prihlásení iní používatelia."
#: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:39
msgstr "Uspanie systému"
#: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Vyžaduje sa overenie totožnosti na uspanie systému."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Vyžaduje sa overenie totožnosti na uspanie systému, pokiaľ sú prihlásení iní "
"používatelia."
#: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:45
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:47
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:51
#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
#: ../src/login/org.freedesktop.login1.policy.in.h:53
msgstr "Дозволи качење уређаја на седишта"
#: src/login/org.freedesktop.login1.policy.in:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Потребно је да се идентификујете да бисте закачили уређај на седиште."
#: src/login/org.freedesktop.login1.policy.in:148
#: src/login/org.freedesktop.login1.policy.in:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Потребно је да се идентификујете да бисте поново подесили како се уређаји "
"каче на седишта."
msgstr "Искључи систем"
#: src/login/org.freedesktop.login1.policy.in:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Потребно је да се идентификујете да бисте искључили систем."
#: src/login/org.freedesktop.login1.policy.in:169
#: src/login/org.freedesktop.login1.policy.in:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Потребно је да се идентификујете да бисте искључили систем док су други "
"корисници пријављени."
#: src/login/org.freedesktop.login1.policy.in:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Искључи систем иако је програм затражио да се спречи гашење"
#: src/login/org.freedesktop.login1.policy.in:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Потребно је да се идентификујете да бисте искључили систем иако је програм "
"затражио да се спречи гашење система."
msgstr "Поново покрени систем"
#: src/login/org.freedesktop.login1.policy.in:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Потребно је да се идентификујете да бисте поново покренули систем."
#: src/login/org.freedesktop.login1.policy.in:202
#: src/login/org.freedesktop.login1.policy.in:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Потребно је да се идентификујете да бисте поново покренули систем док су "
"други корисници пријављени."
#: src/login/org.freedesktop.login1.policy.in:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Поново покрени систем иако је програм затражио да се спречи гашење"
#: src/login/org.freedesktop.login1.policy.in:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Потребно је да се идентификујете да бисте поново покренули систем иако је "
"програм затражио да се спречи гашење система."
msgstr "Заустави систем"
#: src/login/org.freedesktop.login1.policy.in:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Потребно је да се идентификујете да бисте зауставили систем."
#: src/login/org.freedesktop.login1.policy.in:235
#: src/login/org.freedesktop.login1.policy.in:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Потребно је да се идентификујете да бисте зауставили систем док су други "
"корисници пријављени."
#: src/login/org.freedesktop.login1.policy.in:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Заустави систем иако програм тражи да се спречи заустављање"
#: src/login/org.freedesktop.login1.policy.in:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Потребно је да се идентификујете да бисте зауставили систем иако програм "
msgstr "Обустави систем"
#: src/login/org.freedesktop.login1.policy.in:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Потребно је да се идентификујете да бисте обуставили систем."
#: src/login/org.freedesktop.login1.policy.in:267
#: src/login/org.freedesktop.login1.policy.in:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Потребно је да се идентификујете да бисте обуставили систем док су други "
"корисници пријављени."
#: src/login/org.freedesktop.login1.policy.in:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Обуставите систем иако програм тражи да се спречи обустава"
#: src/login/org.freedesktop.login1.policy.in:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Потребно је да се идентификујете да бисте обуставили систем иако је програм "
"затражио да се спречи обустава система."
msgstr "Успавај систем"
#: src/login/org.freedesktop.login1.policy.in:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Потребно је да се идентификујете да бисте успавали систем."
#: src/login/org.freedesktop.login1.policy.in:299
#: src/login/org.freedesktop.login1.policy.in:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Потребно је да се идентификујете да бисте успавали систем док су други "
"корисници пријављени."
#: src/login/org.freedesktop.login1.policy.in:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Успавај систем иако је програм затражио да се спречи спавање"
#: src/login/org.freedesktop.login1.policy.in:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Потребно је да се идентификујете да бисте успавали систем иако је програм "
"затражио да се спречи успављивање система."
#: src/login/org.freedesktop.login1.policy.in:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Потребно је да се идентификујете да бисте управљали покренутим сесијама, "
"корисницима и седиштима."
msgstr "Tillåt att binda enheter till platser"
#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Autentisering krävs för att binda en enhet till en plats."
#: ../src/login/org.freedesktop.login1.policy.in.h:25
#: ../src/login/org.freedesktop.login1.policy.in.h:26
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Autentisering krävs för att återställa hur enheter är bundna till platser."
msgstr "Stäng av systemet"
#: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Autentisering krävs för att stänga av systemet."
#: ../src/login/org.freedesktop.login1.policy.in.h:29
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Autentisering krävs för att stänga av systemet medan andra användare är "
"inloggade."
#: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Stäng av systemet även då ett program hindrar det"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Autentisering krävs för att stänga av systemet även då ett program hindrar "
"det."
msgstr "Starta om systemet"
#: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Autentisering krävs för att starta om systemet."
#: ../src/login/org.freedesktop.login1.policy.in.h:35
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Autentisering krävs för att starta om systemet medan andra användare är "
"inloggade."
#: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Starta om systemet även då ett program hindrar det"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Autentisering krävs för att starta om systemet även då ett program hindrar "
"det."
msgstr "Stoppa systemet"
#: src/login/org.freedesktop.login1.policy.in:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Autentisering krävs för att stoppa systemet."
#: src/login/org.freedesktop.login1.policy.in:235
#: src/login/org.freedesktop.login1.policy.in:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Autentisering krävs för att stoppa systemet medan andra användare är "
"inloggade."
#: src/login/org.freedesktop.login1.policy.in:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Stoppa systemet även då ett program hindrar det"
#: src/login/org.freedesktop.login1.policy.in:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Autentisering krävs för att stoppa systemet även då ett program hindrar det."
msgstr "Försätt system i vänteläge"
#: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Autentisering krävs för att försätta system i vänteläge."
#: ../src/login/org.freedesktop.login1.policy.in.h:41
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Autentisering krävs för att försätta systemet i vänteläge medan andra "
"användare är inloggade."
#: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Försätt systemet i vänteläge även då ett program hindrar det"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Autentisering krävs för att försätta ett program i vänteläge även då ett "
"program hindrar det."
msgstr "Försätt systemet i viloläge"
#: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Autentisering krävs för att försätta systemet i viloläge."
#: ../src/login/org.freedesktop.login1.policy.in.h:47
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Autentisering krävs för att försätta systemet i viloläge medan andra "
"användare är inloggade."
#: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Försätt systemet i viloläge även då ett program hindrar det"
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Autentisering krävs för att försätta ett program i viloläge även då ett "
"program hindrar det."
#: ../src/login/org.freedesktop.login1.policy.in.h:52
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Autentisering krävs för att hantera aktiva sessioner, användare och platser."
msgstr "Aygıtların yuvaya takılmasına izin ver"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr ""
"Bir aygıtın yuvaya takılmasına izin vermek kimlik doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Aygıtların yuvalara nasıl takıldığını sıfırlamak kimlik doğrulama "
"gerektiriyor."
msgstr "Sistemi kapat"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Sistemi kapatmak için kimlik doğrulaması gerekiyor."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Diğer kullanıcılar oturum açmışken sistemi kapatmak kimlik doğrulaması "
"gerektiriyor."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Bir uygulama engellenmesini isterken sistemi kapat"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Bir uygulama engellenmesini isterken sistemi kapatmak kimlik doğrulaması "
"gerektiriyor."
msgstr "Sistemi yeniden başlat"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Sistemi yeniden başlatmak kimlik doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr ""
"Diğer kullanıcılar oturum açmışken sistemi yeniden başlatmak kimlik "
"doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Bir uygulama engellenmesini isterken sistemi yeniden başlat"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Bir uygulama engellenmesini isterken sistemi yeniden başlatmak kimlik "
"doğrulaması gerektiriyor."
msgstr "Sistemi durdur"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Sistemi durdurmak kimlik doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
"logged in."
msgstr ""
"Diğer kullanıcılar oturum açmışken sistemi durdurmak kimlik doğrulaması "
"gerektiriyor."
#: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Bir uygulama engellenmesini isterken sistemi durdur"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Bir uygulama engellenmesini isterken sistemi durdurmak kimlik doğrulaması "
msgstr "Sistemi askıya al"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Sistemi askıya almak kimlik doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr ""
"Diğer kullanıcılar oturum açmışken sistemi askıya almak kimlik doğrulaması "
"gerektiriyor."
#: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Bir uygulama engellenmesini isterken sistemi askıya al"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Bir uygulama engellenmesini isterken sistemi askıya almak kimlik doğrulaması "
"gerektiriyor."
msgstr "Sistemi hazırda beklet"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Sistemi hazırda bekletmek kimlik doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Diğer kullanıcılar oturum açmışken sistemi hazırda bekletmek kimlik "
"doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Bir uygulama engellenmesini isterken sistemi hazırda beklet"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Bir uygulama engellenmesini isterken sistemi hazırda bekletmek kimlik "
"doğrulaması gerektiriyor."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Aktif oturumları, kullanıcıları ve yuvaları yönetmek için kimlik doğrulaması "
"gereklidir."
# 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.
+# Yuri Chornoivan <yurchor@ukr.net>, 2019, 2020.
msgid ""
msgstr ""
"Project-Id-Version: systemd master\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-07-29 15:34+0000\n"
-"PO-Revision-Date: 2019-08-16 09:11+0300\n"
+"POT-Creation-Date: 2020-01-30 15:31+0000\n"
+"PO-Revision-Date: 2020-02-07 12:37+0200\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
"Language: uk\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: Lokalize 19.11.70\n"
+"X-Generator: Lokalize 20.03.70\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
msgid "Authentication is required to reload the systemd state."
msgstr "Потрібна автентифікація, щоб перезапустити стан системи."
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Створення домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Для створення домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Вилучення домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Для вилучення домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Перевірка реєстраційних даних для доступу до домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Для перевірки реєстраційних даних для доступу до домашньої теки користувача"
+" слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Оновлення домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "Для оновлення домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Зміна розмірів домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Для зміни розмірів домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Зміна пароля до домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid "Authentication is required to change the password of a user's home area."
+msgstr ""
+"Для зміни пароля для доступу до домашньої теки користувача слід пройти"
+" розпізнавання."
+
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set host name"
msgstr "Встановити назву вузла"
msgstr "Дозволити під'єднання пристроїв до місць"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "Потрібна автентифікація, щоб під'єднувати пристрої до місць."
#: src/login/org.freedesktop.login1.policy:148
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr ""
"Потрібна автентифікація, щоб перезапустити спосіб під'єднання до місць."
msgstr "Вимкнути систему"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "Потрібна автентифікація, щоб вимкнути систему."
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr ""
"Потрібна автентифікація, щоб вимкнути систему, коли інші користувачі в ній."
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "Вимкнути систему, коли програми намагаються перешкодити цьому"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr ""
"Потрібна автентифікація, щоб вимкнути систему, коли програми намагаються "
"перешкодити цьому."
msgstr "Перезавантажити систему"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "Для перезавантаження системи необхідна ідентифікація."
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot 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"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "Перезапустити систему, коли програми намагаються перешкодити цьому"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr ""
"Потрібна автентифікація, щоб перезапустити систему, коли програми "
"намагаються перешкодити цьому."
msgstr "Зупинити систему"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "Потрібна автентифікація, щоб зупинити систему."
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt 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"
+msgid "Halt the system while an application is inhibiting this"
msgstr "Зупинити систему, коли програми намагаються перешкодити цьому"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr ""
"Потрібна автентифікація, щоб зупинити систему, коли програми намагаються "
msgstr "Призупинити систему"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "Потрібна автентифікація, щоб призупинити систему."
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend 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"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "Призупинити систему, коли програми намагаються перешкодити цьому"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr ""
"Потрібна автентифікація, щоб призупинити систему, коли програми намагаються "
"перешкодити цьому."
msgstr "Приспати систему"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "Потрібна автентифікація, щоб приспати систему."
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr ""
"Потрібна автентифікація, щоб присипання систему, коли інші користувачі в ній."
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "Приспати систему, коли програми намагаються перешкодити цьому"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr ""
"Потрібна автентифікація, щоб приспати систему, коли програми намагаються "
"перешкодити цьому."
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr ""
"Потрібна автентифікація, щоб керувати сеансами, користувачами і робочими "
"місцями."
msgid "Authentication is required to set a wall message"
msgstr "Потрібна автентифікація, щоб вказати повідомлення на стіні"
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Зміна сеансу"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Для зміни віртуального термінала слід пройти розпізнавання."
+
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
msgstr "Увійти в локальний контейнер"
msgstr "Потрібна автентифікація, щоб встановити сервери NTP."
#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
msgid "Set DNS servers"
msgstr "Встановлення серверів DNS"
#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
msgid "Authentication is required to set DNS servers."
msgstr "Потрібна автентифікація, щоб встановити сервери DNS."
#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
msgid "Set domains"
msgstr "Встановлення доменів"
#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
msgid "Authentication is required to set domains."
msgstr "Потрібна автентифікація, щоб встановити домени."
#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
msgid "Set default route"
msgstr "Встановлення типового маршруту"
#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
msgid "Authentication is required to set default route."
msgstr "Потрібна автентифікація, щоб встановити типовий маршрут."
#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
msgid "Enable/disable LLMNR"
msgstr "Вмикання або вимикання LLMNR"
#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
msgid "Authentication is required to enable or disable LLMNR."
msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути LLMNR."
#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
msgid "Enable/disable multicast DNS"
msgstr "Вмикання або вимикання трансляційного DNS"
#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
msgid "Authentication is required to enable or disable multicast DNS."
msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути трансляційну DNS."
#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
msgid "Enable/disable DNS over TLS"
msgstr "Вмикання і вимикання DNS через TLS"
#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
msgid "Authentication is required to enable or disable DNS over TLS."
msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути DNS через TLS."
#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
msgid "Enable/disable DNSSEC"
msgstr "Вмикання або вимикання DNSSEC"
#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
msgid "Authentication is required to enable or disable DNSSEC."
msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути DNSSEC."
#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
msgid "Set DNSSEC Negative Trust Anchors"
msgstr "Встановлення прив'язок від'ємної довіри DNSSEC"
#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
msgstr ""
"Потрібна автентифікація, щоб встановити прив'язки від'ємної довіри DNSSEC."
msgstr "Повернення до початкових параметрів NTP"
#: src/network/org.freedesktop.network1.policy:122
-msgid "Authentication is required to revert NTP settings."
-msgstr "Ð\9fоÑ\82Ñ\80Ñ\96бна авÑ\82енÑ\82иÑ\84Ñ\96каÑ\86Ñ\96Ñ\8f, Ñ\89об повеÑ\80нÑ\83Ñ\82иÑ\81Ñ\8f до поÑ\87аÑ\82ковиÑ\85 паÑ\80амеÑ\82Ñ\80Ñ\96в NTP."
+msgid "Authentication is required to reset NTP settings."
+msgstr "Ð\94лÑ\8f Ñ\81киданнÑ\8f паÑ\80амеÑ\82Ñ\80Ñ\96в NTP до Ñ\82иповиÑ\85 Ñ\81лÑ\96д пÑ\80ойÑ\82и Ñ\80озпÑ\96знаваннÑ\8f."
#: src/network/org.freedesktop.network1.policy:132
msgid "Revert DNS settings"
msgstr "Повернення до початкових параметрів DNS"
#: src/network/org.freedesktop.network1.policy:133
-msgid "Authentication is required to revert DNS settings."
-msgstr "Потрібна автентифікація, щоб повернутися до початкових параметрів DNS."
+msgid "Authentication is required to reset DNS settings."
+msgstr "Для скидання параметрів DNS до типових слід пройти розпізнавання."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Оновлення динамічних адрес"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Для оновлення динамічних адрес слід пройти розпізнавання."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Перезавантаження параметрів мережі"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "Для перезавантаження параметрів мережі слід пройти розпізнавання."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Переналаштування інтерфейсу мережі"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Для зміни налаштувань інтерфейсу мережі слід пройти розпізнавання."
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgid "Authentication is required to unregister a DNS-SD service"
msgstr "Потрібна автентифікація, щоб зняти з реєстрації службу DNS-SD"
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Повернення до початкових параметрів визначення назв вузлів"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+"Для повернення до початкових параметрів визначення назв вузлів за адресами"
+" слід пройти розпізнавання."
+
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "Вказати системний час"
"Потрібна автентифікація, щоб контролювати, чи синхронізування часу через "
"мережу запущено."
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:355
msgid "Authentication is required to start '$(unit)'."
msgstr "Потрібна автентифікація, щоб запустити «$(unit)»."
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:356
msgid "Authentication is required to stop '$(unit)'."
msgstr "Потрібна автентифікація, щоб зупинити «$(unit)»."
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:357
msgid "Authentication is required to reload '$(unit)'."
msgstr "Потрібна автентифікація, щоб перезавантажити «$(unit)»."
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359
msgid "Authentication is required to restart '$(unit)'."
msgstr "Потрібна автентифікація, щоб перезапустити «$(unit)»."
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:531
msgid ""
"Authentication is required to send a UNIX signal to the processes of "
"'$(unit)'."
msgstr ""
"Потрібна автентифікація, щоб надіслати сигнал UNIX до процесів «$(unit)»."
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:562
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgstr "Потрібна автентифікація, щоб скинути «пошкоджений» стан з «$(unit)»."
-#: src/core/dbus-unit.c:594
+#: src/core/dbus-unit.c:595
msgid "Authentication is required to set properties on '$(unit)'."
msgstr "Потрібна автентифікація, щоб вказати властивості на «$(unit)»."
-#: src/core/dbus-unit.c:703
+#: src/core/dbus-unit.c:704
msgid ""
"Authentication is required to delete files and directories associated with "
"'$(unit)'."
msgstr ""
-"Потрібна автентифікація, щоб вилучити файли і каталоги, які пов'язано із"
-" «$(unit)»."
+"Потрібна автентифікація, щоб вилучити файли і каталоги, які пов'язано із "
+"«$(unit)»."
#
# To fully understand the meaning, please refer to session management in old ConsoleKit and new systemd-logind.
#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "允许将设备附加至某个会话座位需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:23
#: ../src/login/org.freedesktop.login1.policy.in.h:24
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr "重新设定设备的会话座位接入方式时需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:25
msgstr "关闭系统"
#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "关闭系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:27
#: ../src/login/org.freedesktop.login1.policy.in.h:28
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr "存在其他已登录用户时关闭系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "有其它应用程序阻止时仍然关机"
#: ../src/login/org.freedesktop.login1.policy.in.h:30
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr "在其它应用程序阻止关机时关闭系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:31
msgstr "重启系统"
#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "重启系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:33
#: ../src/login/org.freedesktop.login1.policy.in.h:34
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
"logged in."
msgstr "存在其他已登录用户时重启系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "有其它应用程序阻止时仍然重启"
#: ../src/login/org.freedesktop.login1.policy.in.h:36
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr "在其它应用程序阻止重启时重启系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:37
msgstr "挂起系统"
#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "挂起系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:39
#: ../src/login/org.freedesktop.login1.policy.in.h:40
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
"logged in."
msgstr "存在其他已登录用户时挂起系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "有其它应用程序阻止时仍然挂起系统"
#: ../src/login/org.freedesktop.login1.policy.in.h:42
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr "在其它应用程序阻止挂起时挂起系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:43
msgstr "休眠系统"
#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "休眠系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:45
#: ../src/login/org.freedesktop.login1.policy.in.h:46
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr "存在其他已登录用户时进行休眠系统需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "有其它应用程序阻止时仍然休眠"
#: ../src/login/org.freedesktop.login1.policy.in.h:48
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr "在其它应用程序阻止休眠时进行休眠需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:49
#: ../src/login/org.freedesktop.login1.policy.in.h:50
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr "管理活动会话、用户与会话座位需要认证。"
#: ../src/login/org.freedesktop.login1.policy.in.h:51
msgstr "允許將設備連接到座位"
#: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
msgstr "將設備連接到座位需要驗證。"
#: src/login/org.freedesktop.login1.policy:148
#: src/login/org.freedesktop.login1.policy:149
msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
msgstr "要重置裝置如何連接到座位需要驗證。"
#: src/login/org.freedesktop.login1.policy:158
msgstr "關閉系統電源"
#: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
msgstr "關閉系統電源需要身份驗證。"
#: src/login/org.freedesktop.login1.policy:169
#: src/login/org.freedesktop.login1.policy:170
msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
"logged in."
msgstr "在有其他使用者登入時關閉系統電源需要驗證。"
#: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
msgstr "當應用程式阻止系統電源關閉時將其關閉"
#: src/login/org.freedesktop.login1.policy:181
msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
msgstr "當應用程式阻止系統電源關閉時將系統電源關閉需要驗證。"
#: src/login/org.freedesktop.login1.policy:191
msgstr "重新啟動系統"
#: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
msgstr "重新啟動系統需要驗證。"
#: src/login/org.freedesktop.login1.policy:202
#: src/login/org.freedesktop.login1.policy:203
msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot 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"
+msgid "Reboot the system while an application is inhibiting this"
msgstr "當應用程式阻止重新啟動系統時將系統重新啟動"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
msgstr "當應用程式阻止系統重新啟動時將系統重新啟動需要驗證。"
#: src/login/org.freedesktop.login1.policy:224
msgstr "停止系統"
#: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
msgstr "停止系統需要身份驗證。"
#: src/login/org.freedesktop.login1.policy:235
#: src/login/org.freedesktop.login1.policy:236
msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt 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"
+msgid "Halt the system while an application is inhibiting this"
msgstr "在應用程式阻止時停止系統"
#: src/login/org.freedesktop.login1.policy:247
msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
"to inhibit it."
msgstr "在應用程式阻止時停止系統需要身份驗證。"
msgstr "暫停系統"
#: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
msgstr "暫停系統需要驗證。"
#: src/login/org.freedesktop.login1.policy:267
#: src/login/org.freedesktop.login1.policy:268
msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend 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"
+msgid "Suspend the system while an application is inhibiting this"
msgstr "當應用程式阻止暫停系統時將系統暫停"
#: src/login/org.freedesktop.login1.policy:279
msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
msgstr "當應用程式阻止系統暫停時將系統暫停需要驗證。"
#: src/login/org.freedesktop.login1.policy:289
msgstr "系統冬眠"
#: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
msgstr "系統冬眠需要驗證。"
#: src/login/org.freedesktop.login1.policy:299
#: src/login/org.freedesktop.login1.policy:300
msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
"logged in."
msgstr "在有其他使用者登入時冬眠系統需要驗證。"
#: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
msgstr "當應用程式阻止冬眠系統時將系統冬眠"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
msgstr "當應用程式阻止系統冬眠時將系統冬眠需要驗證。"
#: src/login/org.freedesktop.login1.policy:321
#: src/login/org.freedesktop.login1.policy:322
msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
msgstr "管理活躍的工作階段、使用者與座位需要驗證。"
#: src/login/org.freedesktop.login1.policy:331
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
-# These ones should be enabled by default, even if distributions
-# generally follow a default-off policy.
+# Settings for systemd units distributed with systemd itself. Most of these
+# should be enabled by default, even if the distribution follows a general
+# default-off policy.
enable remote-fs.target
enable remote-cryptsetup.target
disable systemd-journal-gatewayd.*
disable systemd-journal-remote.*
disable systemd-journal-upload.*
+
+enable systemd-pstore.service
+
+# Passive targets: always off by default, since they should only be pulled in
+# by dependent units.
+
+disable cryptsetup-pre.target
+disable getty-pre.target
+disable local-fs-pre.target
+disable network.target
+disable network-pre.target
+disable nss-lookup.target
+disable nss-user-lookup.target
+disable remote-fs-pre.target
+disable rpcbind.target
+disable time-set.target
+disable time-sync.target
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
-# These ones should be enabled by default, even if distributions
-# generally follow a default-off policy.
+# Settings for systemd units distributed with systemd itself. These should be
+# enabled by default, even if the distribution follows a general default-off
+# policy.
enable systemd-tmpfiles-setup.service
enable systemd-tmpfiles-clean.timer
+
+# Passive targets: always off by default, since they should only be pulled in
+# by dependent units.
+
+disable graphical-session-pre.target
+disable graphical-session.target
# skip later rules when we find something for this input device
IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \
- RUN{builtin}+="keyboard", GOTO="evdev_end"
+ IMPORT{builtin}="keyboard", GOTO="evdev_end"
# AT keyboard matching by the machine's DMI data
DRIVERS=="atkbd", \
IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \
- RUN{builtin}+="keyboard", GOTO="evdev_end"
+ IMPORT{builtin}="keyboard", GOTO="evdev_end"
# device matching the input device name + properties + the machine's DMI data
-KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
- RUN{builtin}+="keyboard", GOTO="evdev_end"
+KERNELS=="input*", \
+ IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
+ IMPORT{builtin}="keyboard", GOTO="evdev_end"
# device matching the input device name and the machine's DMI data
-KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
- RUN{builtin}+="keyboard", GOTO="evdev_end"
+KERNELS=="input*", \
+ IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
+ IMPORT{builtin}="keyboard", GOTO="evdev_end"
LABEL="evdev_end"
apt-get -q --allow-releaseinfo-change update
apt-get -y dist-upgrade
apt-get install -y eatmydata
+# The following four are needed as long as these deps are not covered by Debian's own packaging
+apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
apt-get purge --auto-remove -y unattended-upgrades
systemctl unmask systemd-networkd
systemctl enable systemd-networkd
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
local -A OPTS=(
[STANDALONE]='-q --quiet --runtime --no-reload --cat --no-pager --no-legend
- --no-ask-password -h --help --version'
+ --no-ask-password --enable --now -h --help --version'
[ARG]='-p --profile --copy -H --host -M --machine'
)
bool protect_kernel_modules;
bool protect_kernel_tunables;
bool protect_kernel_logs;
+ bool protect_clock;
char *protect_home;
char *protect_system;
{
.id = "ProtectControlGroups=",
.description_good = "Service cannot modify the control group file system",
- .description_bad = "Service may modify to the control group file system",
+ .description_bad = "Service may modify the control group file system",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
.weight = 1000,
.range = 1,
.assess = assess_bool,
.offset = offsetof(struct security_info, protect_kernel_logs),
},
+ {
+ .id = "ProtectClock=",
+ .description_good = "Service cannot write to the hardware clock or system clock",
+ .description_bad = "Service may write to the hardware clock or system clock",
+ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
+ .weight = 1000,
+ .range = 1,
+ .assess = assess_bool,
+ .offset = offsetof(struct security_info, protect_clock),
+ },
{
.id = "ProtectHome=",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
if (!details_table)
return log_oom();
- (void) table_set_sort(details_table, 3, 1, (size_t) -1);
+ (void) table_set_sort(details_table, (size_t) 3, (size_t) 1, (size_t) -1);
(void) table_set_reverse(details_table, 3, true);
if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
- (void) table_set_display(details_table, 0, 1, 2, 6, (size_t) -1);
+ (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 6, (size_t) -1);
}
for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
{ "ProtectKernelModules", "b", NULL, offsetof(struct security_info, protect_kernel_modules) },
{ "ProtectKernelTunables", "b", NULL, offsetof(struct security_info, protect_kernel_tunables) },
{ "ProtectKernelLogs", "b", NULL, offsetof(struct security_info, protect_kernel_logs) },
+ { "ProtectClock", "b", NULL, offsetof(struct security_info, protect_clock) },
{ "ProtectSystem", "s", NULL, offsetof(struct security_info, protect_system) },
{ "RemoveIPC", "b", NULL, offsetof(struct security_info, remove_ipc) },
{ "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 },
if (info->protect_kernel_logs)
info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
+ if (info->protect_clock)
+ info->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) |
+ (UINT64_C(1) << CAP_WAKE_ALARM));
+
if (info->private_devices)
info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) |
(UINT64_C(1) << CAP_SYS_RAWIO));
if (r < 0)
return r;
- r = table_set_sort(table, 0, SIZE_MAX);
+ r = table_set_sort(table, (size_t) 0, (size_t) SIZE_MAX);
if (r < 0)
return r;
continue;
r = table_add_many(table,
- TABLE_TIMESPAN_MSEC, &u->time,
+ TABLE_TIMESPAN_MSEC, u->time,
TABLE_STRING, u->name);
if (r < 0)
return table_log_add_error(r);
assert(prop);
assert(color);
- match_patterns = strv_fnmatch(patterns, u->id, 0);
+ match_patterns = strv_fnmatch(patterns, u->id);
- if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id, 0))
+ if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id))
return 0;
r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
STRV_FOREACH(unit, units) {
bool match_patterns2;
- match_patterns2 = strv_fnmatch(patterns, *unit, 0);
+ match_patterns2 = strv_fnmatch(patterns, *unit);
- if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit, 0))
+ if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit))
continue;
if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
return table_log_add_error(r);
r = table_add_many(table,
- TABLE_UINT64, &output_usecs,
+ TABLE_UINT64, output_usecs,
TABLE_STRING, "Human:",
- TABLE_TIMESPAN, &output_usecs,
+ TABLE_TIMESPAN, output_usecs,
TABLE_SET_COLOR, ansi_highlight());
if (r < 0)
return table_log_add_error(r);
TABLE_STRING, "Original form:",
TABLE_STRING, p,
TABLE_STRING, "Normalized form:",
- TABLE_TIMESTAMP, &usec,
+ TABLE_TIMESTAMP, usec,
TABLE_SET_COLOR, ansi_highlight_blue());
if (r < 0)
return table_log_add_error(r);
if (!in_utc_timezone()) {
r = table_add_many(table,
TABLE_STRING, "(in UTC):",
- TABLE_TIMESTAMP_UTC, &usec);
+ TABLE_TIMESTAMP_UTC, usec);
if (r < 0)
return table_log_add_error(r);
}
r = table_add_many(table,
TABLE_STRING, "From now:",
- TABLE_TIMESTAMP_RELATIVE, &usec);
+ TABLE_TIMESTAMP_RELATIVE, usec);
if (r < 0)
return table_log_add_error(r);
if (i == 0) {
r = table_add_many(table,
TABLE_STRING, "Next elapse:",
- TABLE_TIMESTAMP, &next,
+ TABLE_TIMESTAMP, next,
TABLE_SET_COLOR, ansi_highlight_blue());
if (r < 0)
return table_log_add_error(r);
return table_log_add_error(r);
r = table_add_many(table,
- TABLE_TIMESTAMP, &next,
+ TABLE_TIMESTAMP, next,
TABLE_SET_COLOR, ansi_highlight_blue());
if (r < 0)
return table_log_add_error(r);
if (!in_utc_timezone()) {
r = table_add_many(table,
TABLE_STRING, "(in UTC):",
- TABLE_TIMESTAMP_UTC, &next);
+ TABLE_TIMESTAMP_UTC, next);
if (r < 0)
return table_log_add_error(r);
}
r = table_add_many(table,
TABLE_STRING, "From now:",
- TABLE_TIMESTAMP_RELATIVE, &next);
+ TABLE_TIMESTAMP_RELATIVE, next);
if (r < 0)
return table_log_add_error(r);
#include "strv.h"
#include "time-util.h"
#include "utf8.h"
+#include "virt.h"
#if ENABLE_EFI
return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
}
+bool is_efi_boot(void) {
+ if (detect_container() > 0)
+ return false;
+
+ return access("/sys/firmware/efi/", F_OK) >= 0;
+}
+
+static int read_flag(const char *varname) {
+ _cleanup_free_ void *v = NULL;
+ uint8_t b;
+ size_t s;
+ int r;
+
+ if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
+ return 0;
+
+ r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
+ if (r < 0)
+ return r;
+
+ if (s != 1)
+ return -EINVAL;
+
+ b = *(uint8_t *)v;
+ return !!b;
+}
+
+bool is_efi_secure_boot(void) {
+ return read_flag("SecureBoot") > 0;
+}
+
+bool is_efi_secure_boot_setup_mode(void) {
+ return read_flag("SetupMode") > 0;
+}
+
int systemd_efi_options_variable(char **line) {
const char *e;
int r;
int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p);
+bool is_efi_boot(void);
+bool is_efi_secure_boot(void);
+bool is_efi_secure_boot_setup_mode(void);
+
int systemd_efi_options_variable(char **line);
#else
return -EOPNOTSUPP;
}
+static inline bool is_efi_boot(void) {
+ return false;
+}
+
+static inline bool is_efi_secure_boot(void) {
+ return false;
+}
+
+static inline bool is_efi_secure_boot_setup_mode(void) {
+ return false;
+}
+
static inline int systemd_efi_options_variable(char **line) {
return -ENODATA;
}
return cescape_length(s, strlen(s));
}
-int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) {
+int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) {
int r = 1;
assert(p);
return -EINVAL;
/* Don't allow NUL bytes */
- if (a == 0 && b == 0)
+ if (a == 0 && b == 0 && !accept_nul)
return -EINVAL;
*ret = (a << 4U) | b;
c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
/* Don't allow 0 chars */
- if (c == 0)
+ if (c == 0 && !accept_nul)
return -EINVAL;
*ret = c;
((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
/* Don't allow 0 chars */
- if (c == 0)
+ if (c == 0 && !accept_nul)
return -EINVAL;
/* Don't allow invalid code points */
return -EINVAL;
/* don't allow NUL bytes */
- if (a == 0 && b == 0 && c == 0)
+ if (a == 0 && b == 0 && c == 0 && !accept_nul)
return -EINVAL;
/* Don't allow bytes above 255 */
return -EINVAL;
}
- k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
+ k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
if (k < 0) {
if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
return t - r;
}
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
- return cunescape_length_with_prefix(s, length, NULL, flags, ret);
-}
-
-int cunescape(const char *s, UnescapeFlags flags, char **ret) {
- return cunescape_length(s, strlen(s), flags, ret);
-}
-
char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
char *ans, *t, *prev, *prev2;
const char *f;
#define SHELL_NEED_ESCAPE_POSIX "\\\'"
typedef enum UnescapeFlags {
- UNESCAPE_RELAX = 1,
+ UNESCAPE_RELAX = 1 << 0,
+ UNESCAPE_ACCEPT_NUL = 1 << 1,
} UnescapeFlags;
typedef enum EscapeStyle {
ESCAPE_BACKSLASH = 1,
- ESCAPE_POSIX = 2,
+ ESCAPE_POSIX = 2,
} EscapeStyle;
char *cescape(const char *s);
char *cescape_length(const char *s, size_t n);
int cescape_char(char c, char *buf);
-int cunescape(const char *s, UnescapeFlags flags, char **ret);
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
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);
+static inline int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+ return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
+static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+ return cunescape_length(s, strlen(s), flags, ret);
+}
+int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
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) {
bool eight_bit = false;
char32_t u;
- r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
+ r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false);
if (r < 0) {
if (flags & EXTRACT_CUNESCAPE_RELAX) {
s[sz++] = '\\';
#include <net/if.h>
#include <stdbool.h>
-#if SIZEOF_PID_T == 4
-# define PID_PRI PRIi32
-#elif SIZEOF_PID_T == 2
-# define PID_PRI PRIi16
-#else
-# error Unknown pid_t size
-#endif
+#include "cgroup-util.h"
+#include "macro.h"
+
+assert_cc(sizeof(pid_t) == sizeof(int32_t));
+#define PID_PRI PRIi32
#define PID_FMT "%" PID_PRI
-#if SIZEOF_UID_T == 4
-# define UID_FMT "%" PRIu32
-#elif SIZEOF_UID_T == 2
-# define UID_FMT "%" PRIu16
-#else
-# error Unknown uid_t size
-#endif
+assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+#define UID_FMT "%" PRIu32
-#if SIZEOF_GID_T == 4
-# define GID_FMT "%" PRIu32
-#elif SIZEOF_GID_T == 2
-# define GID_FMT "%" PRIu16
-#else
-# error Unknown gid_t size
-#endif
+assert_cc(sizeof(gid_t) == sizeof(uint32_t));
+#define GID_FMT "%" PRIu32
#if SIZEOF_TIME_T == 8
# define PRI_TIME PRIi64
FORMAT_BYTES_TRAILING_B = 1 << 2,
} FormatBytesFlag;
-#define FORMAT_BYTES_MAX 8
+#define FORMAT_BYTES_MAX 16
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag);
static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
}
+static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) {
+ if (t == CGROUP_LIMIT_MAX) {
+ (void) snprintf(buf, l, "%s", "infinity");
+ return buf;
+ }
+ return format_bytes(buf, l, t);
+}
return do_chown || do_chmod;
}
+int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ bool do_chown, do_chmod;
+ struct stat st;
+
+ assert(path);
+
+ /* Change ownership and access mode of the specified path, see description of fchmod_and_chown().
+ * Should only be used on trusted paths. */
+
+ if (lstat(path, &st) < 0)
+ return -errno;
+
+ do_chown =
+ (uid != UID_INVALID && st.st_uid != uid) ||
+ (gid != GID_INVALID && st.st_gid != gid);
+
+ do_chmod =
+ !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
+ ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
+ do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown()
+ * modifies the access mode too */
+
+ if (mode == MODE_INVALID)
+ mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
+ else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0)
+ return -EINVAL; /* insist on the right file type if it was specified */
+
+ if (do_chown && do_chmod) {
+ mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
+
+ if (((minimal ^ st.st_mode) & 07777) != 0)
+ if (chmod(path, minimal & 07777) < 0)
+ return -errno;
+ }
+
+ if (do_chown)
+ if (lchown(path, uid, gid) < 0)
+ return -errno;
+
+ if (do_chmod)
+ if (chmod(path, mode & 07777) < 0)
+ return -errno;
+
+ return do_chown || do_chmod;
+}
+
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
if (r < 0)
return r;
+ /* Simplify the root directory, so that it has no duplicate slashes and nothing at the
+ * end. While we won't resolve the root path we still simplify it. Note that dropping the
+ * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY
+ * anyway. Moreover at the end of this function after processing everything we'll always turn
+ * the empty string back to "/". */
+ delete_trailing_chars(root, "/");
+ path_simplify(root, true);
+
if (flags & CHASE_PREFIX_ROOT) {
/* We don't support relative paths in combination with a root directory */
if (!path_is_absolute(path))
if (r < 0)
return r;
- fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
return -errno;
}
+ if (root) {
+ _cleanup_free_ char *absolute = NULL;
+ const char *e;
+
+ /* If we are operating on a root directory, let's take the root directory as it is. */
+
+ e = path_startswith(buffer, root);
+ if (!e)
+ return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG,
+ SYNTHETIC_ERRNO(ECHRNG),
+ "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
+ path, root);
+
+ done = strdup(root);
+ if (!done)
+ return -ENOMEM;
+
+ /* Make sure "todo" starts with a slash */
+ absolute = strjoin("/", e);
+ if (!absolute)
+ return -ENOMEM;
+
+ free_and_replace(buffer, absolute);
+ }
+
todo = buffer;
for (;;) {
_cleanup_free_ char *first = NULL;
/* Determine length of first component in the path */
n = strspn(todo, "/"); /* The slashes */
+
+ if (n > 1) {
+ /* If we are looking at more than a single slash then skip all but one, so that when
+ * we are done with everything we have a normalized path with only single slashes
+ * separating the path components. */
+ todo += n - 1;
+ n = 1;
+ }
+
m = n + strcspn(todo + n, "/"); /* The entire length of the component */
/* Extract the first component. */
if (fstat(child, &st) < 0)
return -errno;
if ((flags & CHASE_SAFE) &&
- (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) &&
unsafe_transition(&previous_stat, &st))
return log_unsafe_transition(fd, child, path, flags);
* directory as base. */
safe_close(fd);
- fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid);
+int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_umask(int fd, mode_t mode);
int fchmod_opath(int fd, mode_t m);
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * linux/can/netlink.h
+ *
+ * Definitions for the CAN netlink interface
+ *
+ * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_CAN_NETLINK_H
+#define _UAPI_CAN_NETLINK_H
+
+#include <linux/types.h>
+
+/*
+ * CAN bit-timing parameters
+ *
+ * For further information, please read chapter "8 BIT TIMING
+ * REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
+ * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
+ */
+struct can_bittiming {
+ __u32 bitrate; /* Bit-rate in bits/second */
+ __u32 sample_point; /* Sample point in one-tenth of a percent */
+ __u32 tq; /* Time quanta (TQ) in nanoseconds */
+ __u32 prop_seg; /* Propagation segment in TQs */
+ __u32 phase_seg1; /* Phase buffer segment 1 in TQs */
+ __u32 phase_seg2; /* Phase buffer segment 2 in TQs */
+ __u32 sjw; /* Synchronisation jump width in TQs */
+ __u32 brp; /* Bit-rate prescaler */
+};
+
+/*
+ * CAN hardware-dependent bit-timing constant
+ *
+ * Used for calculating and checking bit-timing parameters
+ */
+struct can_bittiming_const {
+ char name[16]; /* Name of the CAN controller hardware */
+ __u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
+ __u32 tseg1_max;
+ __u32 tseg2_min; /* Time segment 2 = phase_seg2 */
+ __u32 tseg2_max;
+ __u32 sjw_max; /* Synchronisation jump width */
+ __u32 brp_min; /* Bit-rate prescaler */
+ __u32 brp_max;
+ __u32 brp_inc;
+};
+
+/*
+ * CAN clock parameters
+ */
+struct can_clock {
+ __u32 freq; /* CAN system clock frequency in Hz */
+};
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */
+ CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */
+ CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */
+ CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */
+ CAN_STATE_STOPPED, /* Device is stopped */
+ CAN_STATE_SLEEPING, /* Device is sleeping */
+ CAN_STATE_MAX
+};
+
+/*
+ * CAN bus error counters
+ */
+struct can_berr_counter {
+ __u16 txerr;
+ __u16 rxerr;
+};
+
+/*
+ * CAN controller mode
+ */
+struct can_ctrlmode {
+ __u32 mask;
+ __u32 flags;
+};
+
+#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */
+#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */
+#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */
+#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */
+#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */
+#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */
+#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
+#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ __u32 bus_error; /* Bus errors */
+ __u32 error_warning; /* Changes to error warning state */
+ __u32 error_passive; /* Changes to error passive state */
+ __u32 bus_off; /* Changes to bus off state */
+ __u32 arbitration_lost; /* Arbitration lost errors */
+ __u32 restarts; /* CAN controller re-starts */
+};
+
+/*
+ * CAN netlink interface
+ */
+enum {
+ IFLA_CAN_UNSPEC,
+ IFLA_CAN_BITTIMING,
+ IFLA_CAN_BITTIMING_CONST,
+ IFLA_CAN_CLOCK,
+ IFLA_CAN_STATE,
+ IFLA_CAN_CTRLMODE,
+ IFLA_CAN_RESTART_MS,
+ IFLA_CAN_RESTART,
+ IFLA_CAN_BERR_COUNTER,
+ IFLA_CAN_DATA_BITTIMING,
+ IFLA_CAN_DATA_BITTIMING_CONST,
+ IFLA_CAN_TERMINATION,
+ IFLA_CAN_TERMINATION_CONST,
+ IFLA_CAN_BITRATE_CONST,
+ IFLA_CAN_DATA_BITRATE_CONST,
+ IFLA_CAN_BITRATE_MAX,
+ __IFLA_CAN_MAX
+};
+
+#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1)
+
+/* u16 termination range: 1..65535 Ohms */
+#define CAN_TERMINATION_DISABLED 0
+
+#endif /* !_UAPI_CAN_NETLINK_H */
-#!/bin/bash
+#!/usr/bin/env bash
set -eu
[SPECIAL_GLYPH_MU] = "u",
[SPECIAL_GLYPH_CHECK_MARK] = "+",
[SPECIAL_GLYPH_CROSS_MARK] = "-",
+ [SPECIAL_GLYPH_LIGHT_SHADE] = "-",
+ [SPECIAL_GLYPH_DARK_SHADE] = "X",
+ [SPECIAL_GLYPH_SIGMA] = "S",
[SPECIAL_GLYPH_ARROW] = "->",
[SPECIAL_GLYPH_ELLIPSIS] = "...",
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
[SPECIAL_GLYPH_MU] = "\316\274", /* μ (actually called: GREEK SMALL LETTER MU) */
[SPECIAL_GLYPH_CHECK_MARK] = "\342\234\223", /* ✓ */
[SPECIAL_GLYPH_CROSS_MARK] = "\342\234\227", /* ✗ (actually called: BALLOT X) */
+ [SPECIAL_GLYPH_LIGHT_SHADE] = "\342\226\221", /* ░ */
+ [SPECIAL_GLYPH_DARK_SHADE] = "\342\226\223", /* ▒ */
+ [SPECIAL_GLYPH_SIGMA] = "\316\243", /* Σ */
/* Single glyph in Unicode, two in ASCII */
[SPECIAL_GLYPH_ARROW] = "\342\206\222", /* → (actually called: RIGHTWARDS ARROW) */
SPECIAL_GLYPH_CROSS_MARK,
SPECIAL_GLYPH_ARROW,
SPECIAL_GLYPH_ELLIPSIS,
+ SPECIAL_GLYPH_LIGHT_SHADE,
+ SPECIAL_GLYPH_DARK_SHADE,
+ SPECIAL_GLYPH_SIGMA,
_SPECIAL_GLYPH_FIRST_SMILEY,
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_SMILEY,
SPECIAL_GLYPH_HAPPY_SMILEY,
char *buffer, size_t buflen, \
int *errnop) _public_
+#define NSS_PWENT_PROTOTYPES(module) \
+enum nss_status _nss_##module##_endpwent( \
+ void) _public_; \
+enum nss_status _nss_##module##_setpwent( \
+ int stayopen) _public_; \
+enum nss_status _nss_##module##_getpwent_r( \
+ struct passwd *result, \
+ char *buffer, \
+ size_t buflen, \
+ int *errnop) _public_;
+
+#define NSS_GRENT_PROTOTYPES(module) \
+enum nss_status _nss_##module##_endgrent( \
+ void) _public_; \
+enum nss_status _nss_##module##_setgrent( \
+ int stayopen) _public_; \
+enum nss_status _nss_##module##_getgrent_r( \
+ struct group *result, \
+ char *buffer, \
+ size_t buflen, \
+ int *errnop) _public_;
+
+#define NSS_INITGROUPS_PROTOTYPE(module) \
+enum nss_status _nss_##module##_initgroups_dyn( \
+ const char *user, \
+ gid_t group, \
+ long int *start, \
+ long int *size, \
+ gid_t **groupsp, \
+ long int limit, \
+ int *errnop) _public_;
+
typedef enum nss_status (*_nss_gethostbyname4_r_t)(
const char *name,
struct gaih_addrtuple **pat,
return 0;
}
+int parse_ip_prefix_length(const char *s, int *ret) {
+ unsigned l;
+ int r;
+
+ r = safe_atou(s, &l);
+ if (r < 0)
+ return r;
+
+ if (l > 128)
+ return -ERANGE;
+
+ *ret = (int) l;
+
+ return 0;
+}
+
int parse_dev(const char *s, dev_t *ret) {
const char *major;
unsigned x, y;
int safe_atoi16(const char *s, int16_t *ret);
-static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+static inline int safe_atou32_full(const char *s, unsigned base, uint32_t *ret_u) {
assert_cc(sizeof(uint32_t) == sizeof(unsigned));
- return safe_atou(s, (unsigned*) ret_u);
+ return safe_atou_full(s, base, (unsigned*) ret_u);
+}
+
+static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+ return safe_atou32_full(s, 0, (unsigned*) ret_u);
}
static inline int safe_atoi32(const char *s, int32_t *ret_i) {
int parse_ip_port(const char *s, uint16_t *ret);
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
+int parse_ip_prefix_length(const char *s, int *ret);
+
int parse_oom_score_adjust(const char *s, int *ret);
return false;
}
+
+bool prefixed_path_strv_contains(char **l, const char *path) {
+ char **i, *j;
+
+ STRV_FOREACH(i, l) {
+ j = *i;
+ if (*j == '-')
+ j++;
+ if (*j == '+')
+ j++;
+ if (path_equal(j, path))
+ return true;
+ }
+
+ return false;
+}
}
bool path_strv_contains(char **l, const char *path);
+bool prefixed_path_strv_contains(char **l, const char *path);
return read_one_line_file("/proc/cmdline", ret);
}
+/* In SecureBoot mode this is probably not what you want. As your cmdline is
+ * cryptographically signed like when using Type #2 EFI Unified Kernel Images
+ * (https://systemd.io/BOOT_LOADER_SPECIFICATION/) The user's intention is then
+ * that the cmdline should not be modified. You want to make sure that the
+ * system starts up as exactly specified in the signed artifact. */
+static int systemd_options_variable(char **line) {
+ if (is_efi_secure_boot())
+ return -ENODATA;
+
+ return systemd_efi_options_variable(line);
+}
+
static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdlineFlags flags) {
const char *q = *p;
int r;
/* We parse the EFI variable first, because later settings have higher priority. */
- r = systemd_efi_options_variable(&line);
+ r = systemd_options_variable(&line);
if (r < 0 && r != -ENODATA)
log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
return r;
line = mfree(line);
- r = systemd_efi_options_variable(&line);
+ r = systemd_options_variable(&line);
if (r == -ENODATA)
return false; /* Not found */
if (r < 0)
int set_oom_score_adjust(int value);
-#if SIZEOF_PID_T == 4
/* The highest possibly (theoretic) pid_t value on this architecture. */
#define PID_T_MAX ((pid_t) INT32_MAX)
/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value
* these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at
* least to define them here too. */
#define TASKS_MAX 4194303U
-#elif SIZEOF_PID_T == 2
-#define PID_T_MAX ((pid_t) INT16_MAX)
-#define TASKS_MAX 32767U
-#else
-#error "Unknown pid_t size"
-#endif
assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
#include <syslog.h>
#if HAVE_SELINUX
+#include <selinux/avc.h>
#include <selinux/context.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
#include "time-util.h"
#if HAVE_SELINUX
-DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
-
-#define _cleanup_freecon_ _cleanup_(freeconp)
#define _cleanup_context_free_ _cleanup_(context_freep)
+static int mac_selinux_reload(int seqno);
+
static int cached_use = -1;
+static int cached_enforcing = -1;
static struct selabel_handle *label_hnd = NULL;
-#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
-#define log_enforcing_errno(r, ...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, r, __VA_ARGS__)
+#define log_enforcing(...) log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
+#define log_enforcing_errno(r, ...) log_full_errno(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, r, __VA_ARGS__)
#endif
bool mac_selinux_use(void) {
#endif
}
+bool mac_selinux_enforcing(void) {
+#if HAVE_SELINUX
+ if (cached_enforcing < 0) {
+ cached_enforcing = security_getenforce();
+ if (cached_enforcing == -1) {
+ log_error_errno(errno, "Failed to get SELinux enforced status: %m");
+ }
+ }
+
+ /* treat failure as enforced mode */
+ return (cached_enforcing != 0);
+#else
+ return false;
+#endif
+}
+
void mac_selinux_retest(void) {
#if HAVE_SELINUX
cached_use = -1;
+ cached_enforcing = -1;
#endif
}
+#if HAVE_SELINUX
+static int setenforce_callback(int enforcing) {
+ cached_enforcing = enforcing;
+
+ return 0;
+}
+#endif
+
int mac_selinux_init(void) {
int r = 0;
usec_t before_timestamp, after_timestamp;
struct mallinfo before_mallinfo, after_mallinfo;
+ selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload);
+ selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) setenforce_callback);
+
if (label_hnd)
return 0;
label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
if (!label_hnd) {
log_enforcing_errno(errno, "Failed to initialize SELinux context: %m");
- r = security_getenforce() == 1 ? -errno : 0;
+ r = mac_selinux_enforcing() ? -errno : 0;
} else {
char timespan[FORMAT_TIMESPAN_MAX];
int l;
#endif
}
-void mac_selinux_reload(void) {
-
#if HAVE_SELINUX
+static int mac_selinux_reload(int seqno) {
struct selabel_handle *backup_label_hnd;
if (!label_hnd)
- return;
+ return 0;
backup_label_hnd = TAKE_PTR(label_hnd);
selabel_close(backup_label_hnd);
else
label_hnd = backup_label_hnd;
-#endif
+
+ return 0;
}
+#endif
int mac_selinux_fix(const char *path, LabelFixFlags flags) {
if (fstat(fd, &st) < 0)
return -errno;
+ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+ (void) avc_netlink_check_nb();
+
if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) {
r = -errno;
fail:
log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", path);
- if (security_getenforce() == 1)
+ if (mac_selinux_enforcing())
return r;
#endif
if (setfilecon(path, label) < 0) {
log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
- if (security_getenforce() > 0)
+ if (mac_selinux_enforcing())
return -errno;
}
#endif
return -errno;
sclass = string_to_security_class("process");
+ if (sclass == 0)
+ return -ENOSYS;
+
r = security_compute_create_raw(mycon, fcon, sclass, label);
if (r < 0)
return -errno;
return -ENOMEM;
sclass = string_to_security_class("process");
+ if (sclass == 0)
+ return -ENOSYS;
+
r = security_compute_create_raw(mycon, fcon, sclass, label);
if (r < 0)
return -errno;
assert(abspath);
assert(path_is_absolute(abspath));
+ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+ (void) avc_netlink_check_nb();
+
r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
if (r < 0) {
/* No context specified by the policy? Proceed without setting it. */
log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
}
- if (security_getenforce() > 0)
+ if (mac_selinux_enforcing())
return -errno;
return 0;
if (setsockcreatecon(label) < 0) {
log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
- if (security_getenforce() == 1)
+ if (mac_selinux_enforcing())
return -errno;
}
#endif
path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
+ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+ (void) avc_netlink_check_nb();
+
if (path_is_absolute(path))
r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
else {
goto skipped;
log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
- if (security_getenforce() > 0)
+ if (mac_selinux_enforcing())
return -errno;
} else {
if (setfscreatecon_raw(fcon) < 0) {
log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
- if (security_getenforce() > 0)
+ if (mac_selinux_enforcing())
return -errno;
} else
context_changed = true;
#include "macro.h"
#include "label.h"
+#if HAVE_SELINUX
+#include <selinux/selinux.h>
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
+#define _cleanup_freecon_ _cleanup_(freeconp)
+#endif
+
bool mac_selinux_use(void);
+bool mac_selinux_enforcing(void);
void mac_selinux_retest(void);
int mac_selinux_init(void);
void mac_selinux_finish(void);
-void mac_selinux_reload(void);
int mac_selinux_fix(const char *path, LabelFixFlags flags);
int mac_selinux_apply(const char *path, const char *label);
char *s; \
if (i < 0 || i > max) \
return -ERANGE; \
- if (i < (type) ELEMENTSOF(name##_table)) { \
+ if (i < (type) ELEMENTSOF(name##_table) && name##_table[i]) { \
s = strdup(name##_table[i]); \
if (!s) \
return -ENOMEM; \
if (!p)
return false;
+ /* Checks if the specified string contains no quotes or control characters */
+
for (t = p; *t; t++) {
if (*t > 0 && *t < ' ') /* no control characters */
return false;
return NULL;
}
-void strv_clear(char **l) {
+char **strv_free(char **l) {
char **k;
if (!l)
- return;
+ return NULL;
for (k = l; *k; k++)
free(*k);
- *l = NULL;
-}
-
-char **strv_free(char **l) {
- strv_clear(l);
return mfree(l);
}
return l;
}
-bool strv_equal(char * const *a, char * const *b) {
+int strv_compare(char * const *a, char * const *b) {
+ int r;
- if (strv_isempty(a))
- return strv_isempty(b);
+ if (strv_isempty(a)) {
+ if (strv_isempty(b))
+ return 0;
+ else
+ return -1;
+ }
if (strv_isempty(b))
- return false;
+ return 1;
- for ( ; *a || *b; ++a, ++b)
- if (!streq_ptr(*a, *b))
- return false;
+ for ( ; *a || *b; ++a, ++b) {
+ r = strcmp_ptr(*a, *b);
+ if (r != 0)
+ return r;
+ }
- return true;
+ return 0;
}
void strv_print(char * const *l) {
return l;
}
-bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
- char* const* p;
-
- STRV_FOREACH(p, patterns)
- if (fnmatch(*p, s, flags) == 0)
+bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) {
+ for (size_t i = 0; patterns && patterns[i]; i++)
+ if (fnmatch(patterns[i], s, flags) == 0) {
+ if (matched_pos)
+ *matched_pos = i;
return true;
+ }
return false;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
-void strv_clear(char **l);
-
char **strv_copy(char * const *l);
size_t strv_length(char * const *l) _pure_;
char **strv_uniq(char **l);
bool strv_is_uniq(char * const *l);
-bool strv_equal(char * const *a, char * const *b);
+int strv_compare(char * const *a, char * const *b);
+static inline bool strv_equal(char * const *a, char * const *b) {
+ return strv_compare(a, b) == 0;
+}
#define strv_contains(l, s) (!!strv_find((l), (s)))
char **strv_reverse(char **l);
char **strv_shell_escape(char **l, const char *bad);
-bool strv_fnmatch(char* const* patterns, const char *s, int flags);
+bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos);
+static inline bool strv_fnmatch(char* const* patterns, const char *s) {
+ return strv_fnmatch_full(patterns, s, 0, NULL);
+}
static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) {
assert(s);
return strv_isempty(patterns) ||
- strv_fnmatch(patterns, s, flags);
+ strv_fnmatch_full(patterns, s, flags, NULL);
}
char ***strv_free_free(char ***l);
#include <syslog.h>
+#include "sd-id128.h"
+
+#include "glob-util.h"
#include "hexdecoct.h"
#include "macro.h"
+#include "path-util.h"
#include "string-table.h"
#include "syslog-util.h"
+#include "unit-name.h"
int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
int a = 0, b = 0, c = 0;
bool log_level_is_valid(int level) {
return level >= 0 && level <= LOG_DEBUG;
}
+
+/* The maximum size for a log namespace length. This is the file name size limit 255 minus the size of a
+ * formatted machine ID minus a separator char */
+#define LOG_NAMESPACE_MAX (NAME_MAX - (SD_ID128_STRING_MAX - 1) - 1)
+
+bool log_namespace_name_valid(const char *s) {
+ /* Let's make sure the namespace fits in a filename that is prefixed with the machine ID and a dot
+ * (so that /var/log/journal/<machine-id>.<namespace> can be created based on it). Also make sure it
+ * is suitable as unit instance name, and does not contain fishy characters. */
+
+ if (!filename_is_valid(s))
+ return false;
+
+ if (strlen(s) > LOG_NAMESPACE_MAX)
+ return false;
+
+ if (!unit_instance_is_valid(s))
+ return false;
+
+ if (!string_is_safe(s))
+ return false;
+
+ /* Let's avoid globbing for now */
+ if (string_is_glob(s))
+ return false;
+
+ return true;
+}
bool log_level_is_valid(int level);
int syslog_parse_priority(const char **p, int *priority, bool with_facility);
+
+bool log_namespace_name_valid(const char *s);
return 0;
}
+int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper) {
+ uint32_t u, l;
+ int r;
+
+ assert(s);
+ assert(ret_lower);
+ assert(ret_upper);
+
+ r = parse_range(s, &l, &u);
+ if (r < 0)
+ return r;
+
+ if (l > u)
+ return -EINVAL;
+
+ if (!uid_is_valid(l) || !uid_is_valid(u))
+ return -ENXIO;
+
+ *ret_lower = l;
+ *ret_upper = u;
+ return 0;
+}
+
char* getlogname_malloc(void) {
uid_t uid;
struct stat st;
free(allocated);
- allocated = new(gid_t, ngroups);
+ p = allocated = new(gid_t, ngroups);
if (!allocated)
return -ENOMEM;
-
- p = allocated;
}
*gids = TAKE_PTR(p);
return !!s;
}
#endif
-
-int make_salt(char **ret) {
- static const char table[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "./";
-
- uint8_t raw[16];
- char *salt, *j;
- size_t i;
- int r;
-
- /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
- * SHA512, i.e. is legacy-free and minimizes our deps. */
-
- assert_cc(sizeof(table) == 64U + 1U);
-
- /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
- r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
- if (r < 0)
- return r;
-
- salt = new(char, 3+sizeof(raw)+1+1);
- if (!salt)
- return -ENOMEM;
-
- /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
- j = stpcpy(salt, "$6$");
- for (i = 0; i < sizeof(raw); i++)
- j[i] = table[raw[i] & 63];
- j[i++] = '$';
- j[i] = 0;
-
- *ret = salt;
- return 0;
-}
}
int parse_uid(const char *s, uid_t* ret_uid);
+int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper);
static inline int parse_gid(const char *s, gid_t *ret_gid) {
return parse_uid(s, (uid_t*) ret_gid);
int putsgent_sane(const struct sgrp *sg, FILE *stream);
#endif
-int make_salt(char **ret);
-
bool is_nologin_shell(const char *shell);
#include "string-util.h"
#include "virt.h"
+#if defined(__i386__) || defined(__x86_64__)
static const char *const vm_table[_VIRTUALIZATION_MAX] = {
[VIRTUALIZATION_XEN] = "XenVMMXenVMM",
[VIRTUALIZATION_KVM] = "KVMKVMKVM",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(vm, int);
+#endif
static int detect_vm_cpuid(void) {
Print(L"random-seed-mode: off\n");
break;
case RANDOM_SEED_WITH_SYSTEM_TOKEN:
- Print(L"random-seed-node: with-system-token\n");
+ Print(L"random-seed-mode: with-system-token\n");
break;
case RANDOM_SEED_ALWAYS:
- Print(L"random-seed-node: always\n");
+ Print(L"random-seed-mode: always\n");
break;
default:
;
UINTN bufsize = sizeof buf;
EFI_FILE_INFO *f;
CHAR8 *sections[] = {
- (UINT8 *)".osrel",
- (UINT8 *)".cmdline",
+ (CHAR8 *)".osrel",
+ (CHAR8 *)".cmdline",
NULL
};
UINTN offs[ELEMENTSOF(sections)-1] = {};
efi_libdir = get_option('efi-libdir')
if efi_libdir == ''
- ret = run_command(efi_cc + ['-print-multi-os-directory'])
- if ret.returncode() == 0
- path = join_paths('/usr/lib', ret.stdout().strip())
- ret = run_command('realpath', '-e', path)
- if ret.returncode() == 0
- efi_libdir = ret.stdout().strip()
+ # New location first introduced with gnu-efi 3.0.11
+ efi_libdir = join_paths('/usr/lib/gnuefi', EFI_MACHINE_TYPE_NAME)
+ cmd = run_command('test', '-e', efi_libdir)
+
+ if cmd.returncode() != 0
+ # Fall back to the old approach
+ cmd = run_command(efi_cc + ['-print-multi-os-directory'])
+ if cmd.returncode() == 0
+ path = join_paths('/usr/lib', cmd.stdout().strip())
+ cmd = run_command('realpath', '-e', path)
+ if cmd.returncode() == 0
+ efi_libdir = cmd.stdout().strip()
+ endif
endif
endif
endif
objcopy = find_program('objcopy')
- efi_ldsdir = get_option('efi-ldsdir')
- arch_lds = 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)
- if efi_ldsdir == ''
- efi_ldsdir = join_paths(efi_libdir, 'gnuefi')
- cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds))
- if cmd.returncode() != 0
- efi_ldsdir = efi_libdir
- cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds))
- if cmd.returncode() != 0
- error('Cannot find @0@'.format(arch_lds))
+ efi_location_map = [
+ # New locations first introduced with gnu-efi 3.0.11
+ [join_paths(efi_libdir, 'efi.lds'),
+ join_paths(efi_libdir, 'crt0.o')],
+ # Older locations...
+ [join_paths(efi_libdir, 'gnuefi', 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
+ join_paths(efi_libdir, 'gnuefi', 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))],
+ [join_paths(efi_libdir, 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
+ join_paths(efi_libdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))]]
+ efi_lds = ''
+ foreach location : efi_location_map
+ if efi_lds == ''
+ cmd = run_command('test', '-f', location[0])
+ if cmd.returncode() == 0
+ efi_lds = location[0]
+ efi_crt0 = location[1]
endif
endif
+ endforeach
+ if efi_lds == ''
+ if get_option('gnu-efi') == 'true'
+ error('gnu-efi support requested, but cannot find efi.lds')
+ else
+ have_gnu_efi = false
+ endif
endif
+endif
+if have_gnu_efi
compile_args = ['-Wall',
'-Wextra',
'-std=gnu90',
elif efi_arch == 'ia32'
compile_args += ['-mno-sse',
'-mno-mmx']
+ elif efi_arch == 'arm'
+ compile_args += ['-mgeneral-regs-only']
endif
if get_option('werror') == true
compile_args += ['-Werror']
compile_args += ['-O2']
endif
- efi_ldflags = ['-T',
- join_paths(efi_ldsdir, arch_lds),
+ efi_ldflags = ['-T', efi_lds,
'-shared',
'-Bsymbolic',
'-nostdlib',
'-znocombreloc',
'-L', efi_libdir,
- join_paths(efi_ldsdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))]
+ efi_crt0]
if efi_arch == 'aarch64' or efi_arch == 'arm'
# Aarch64 and ARM32 don't have an EFI capable objcopy. Use 'binary'
# instead, and add required symbols manually.
set_variable(tuple[0].underscorify(), so)
set_variable(tuple[0].underscorify() + '_stub', stub)
endforeach
-endif
-############################################################
+ ############################################################
-if have_gnu_efi
test_efi_disk_img = custom_target(
'test-efi-disk.img',
input : [systemd_boot_so, stub_so_stub],
UINTN size;
BOOLEAN secure = FALSE;
CHAR8 *sections[] = {
- (UINT8 *)".cmdline",
- (UINT8 *)".linux",
- (UINT8 *)".initrd",
- (UINT8 *)".splash",
+ (CHAR8 *)".cmdline",
+ (CHAR8 *)".linux",
+ (CHAR8 *)".initrd",
+ (CHAR8 *)".splash",
NULL
};
UINTN addrs[ELEMENTSOF(sections)-1] = {};
UINTN len;
UINTN i;
- if (stra[0] < 0x80)
+ if (!(stra[0] & 0x80))
len = 1;
else if ((stra[0] & 0xe0) == 0xc0)
len = 2;
uefi_call_wrapper((*handle)->Close, 1, *handle);
}
-const EFI_GUID loader_guid;
+extern const EFI_GUID loader_guid;
#define UINTN_MAX (~(UINTN)0)
#define INTN_MAX ((INTN)(UINTN_MAX>>1))
if (r < 0)
return log_error_errno(r, "Failed to set empty string: %m");
- r = table_set_sort(table, COLUMN_NAME, (size_t) -1);
+ r = table_set_sort(table, (size_t) COLUMN_NAME, (size_t) -1);
if (r < 0)
return log_error_errno(r, "Failed to set sort column: %m");
if (arg_show_machine)
- r = table_set_display(table, COLUMN_NAME, COLUMN_PID, COLUMN_PROCESS, COLUMN_USER, COLUMN_CONNECTION, COLUMN_UNIT, COLUMN_SESSION, COLUMN_DESCRIPTION, COLUMN_MACHINE, (size_t) -1);
+ r = table_set_display(table, (size_t) COLUMN_NAME,
+ (size_t) COLUMN_PID,
+ (size_t) COLUMN_PROCESS,
+ (size_t) COLUMN_USER,
+ (size_t) COLUMN_CONNECTION,
+ (size_t) COLUMN_UNIT,
+ (size_t) COLUMN_SESSION,
+ (size_t) COLUMN_DESCRIPTION,
+ (size_t) COLUMN_MACHINE,
+ (size_t) -1);
else
- r = table_set_display(table, COLUMN_NAME, COLUMN_PID, COLUMN_PROCESS, COLUMN_USER, COLUMN_CONNECTION, COLUMN_UNIT, COLUMN_SESSION, COLUMN_DESCRIPTION, (size_t) -1);
+ r = table_set_display(table, (size_t) COLUMN_NAME,
+ (size_t) COLUMN_PID,
+ (size_t) COLUMN_PROCESS,
+ (size_t) COLUMN_USER,
+ (size_t) COLUMN_CONNECTION,
+ (size_t) COLUMN_UNIT,
+ (size_t) COLUMN_SESSION,
+ (size_t) COLUMN_DESCRIPTION,
+ (size_t) -1);
+
if (r < 0)
return log_error_errno(r, "Failed to set columns to display: %m");
}
static int message_dump(sd_bus_message *m, FILE *f) {
- return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
+ return sd_bus_message_dump(m, f, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
}
static int message_pcap(sd_bus_message *m, FILE *f) {
} else if (arg_verbose) {
(void) pager_open(arg_pager_flags);
- r = bus_message_dump(reply, stdout, 0);
+ r = sd_bus_message_dump(reply, stdout, 0);
if (r < 0)
return r;
} else {
} else if (arg_verbose) {
(void) pager_open(arg_pager_flags);
- r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
+ r = sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY);
if (r < 0)
return r;
} else {
"Automount\0"
"Install\0",
+ .can_transient = true,
+ .can_fail = true,
+ .can_trigger = true,
+
.init = automount_init,
.load = automount_load,
.done = automount_done,
.bus_vtable = bus_automount_vtable,
.bus_set_property = bus_automount_set_property,
- .can_transient = true,
-
.shutdown = automount_shutdown,
.supported = automount_supported,
r = device_path_parse_major_minor(p, &mode, &rdev);
if (r == -ENODEV) { /* not a parsable device node, need to go to disk */
struct stat st;
+
if (stat(p, &st) < 0)
return log_warning_errno(errno, "Couldn't stat device '%s': %m", p);
- rdev = (dev_t)st.st_rdev;
- dev = (dev_t)st.st_dev;
+
mode = st.st_mode;
+ rdev = st.st_rdev;
+ dev = st.st_dev;
} else if (r < 0)
return log_warning_errno(r, "Failed to parse major/minor from path '%s': %m", p);
- if (S_ISCHR(mode)) {
- log_warning("Device node '%s' is a character device, but block device needed.", p);
- return -ENOTBLK;
- } else if (S_ISBLK(mode))
+ if (S_ISCHR(mode))
+ return log_warning_errno(SYNTHETIC_ERRNO(ENOTBLK),
+ "Device node '%s' is a character device, but block device needed.", p);
+ if (S_ISBLK(mode))
*ret = rdev;
else if (major(dev) != 0)
*ret = dev; /* If this is not a device node then use the block device this file is stored on */
else {
/* If this is btrfs, getting the backing block device is a bit harder */
r = btrfs_get_block_device(p, ret);
- if (r < 0 && r != -ENOTTY)
+ if (r == -ENOTTY)
+ return log_warning_errno(SYNTHETIC_ERRNO(ENODEV),
+ "'%s' is not a block device node, and file system block device cannot be determined or is not local.", p);
+ if (r < 0)
return log_warning_errno(r, "Failed to determine block device backing btrfs file system '%s': %m", p);
- if (r == -ENOTTY) {
- log_warning("'%s' is not a block device node, and file system block device cannot be determined or is not local.", p);
- return -ENODEV;
- }
}
- /* If this is a LUKS device, try to get the originating block device */
- (void) block_get_originating(*ret, ret);
+ /* If this is a LUKS/DM device, recursively try to get the originating block device */
+ while (block_get_originating(*ret, ret) > 0);
/* If this is a partition, try to get the originating block device */
(void) block_get_whole_disk(*ret, ret);
Unit *member;
Iterator i;
- HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
+ HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i)
if (UNIT_DEREF(member->slice) == u)
u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
- }
}
u->cgroup_members_mask_valid = true;
Unit *slice;
/* This adds the siblings of the specified unit and the siblings of all parent units to the cgroup
- * queue. (But neither the specified unit itself nor the parents.) */
+ * queue. (But neither the specified unit itself nor the parents.)
+ *
+ * Propagation of realization "side-ways" (i.e. towards siblings) is relevant on cgroup-v1 where
+ * scheduling becomes very weird if two units that own processes reside in the same slice, but one is
+ * realized in the "cpu" hierarchy and one is not (for example because one has CPUWeight= set and the
+ * other does not), because that means individual processes need to be scheduled against whole
+ * cgroups. Let's avoid this asymmetry by always ensuring that units below a slice that are realized
+ * at all are always realized in *all* their hierarchies, and it is sufficient for a unit's sibling
+ * to be realized for the unit itself to be realized too. */
while ((slice = UNIT_DEREF(u->slice))) {
Iterator i;
void *v;
HASHMAP_FOREACH_KEY(v, m, slice->dependencies[UNIT_BEFORE], i) {
+
/* Skip units that have a dependency on the slice but aren't actually in it. */
if (UNIT_DEREF(m->slice) != slice)
continue;
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m)))
continue;
+ /* We only enqueue siblings if they were realized once at least, in the main
+ * hierarchy. */
+ if (!m->cgroup_realized)
+ continue;
+
/* If the unit doesn't need any new controllers and has current ones realized, it
* doesn't need any changes. */
if (unit_has_mask_realized(m,
/* Let's verify that the cgroup is really empty */
if (!u->cgroup_path)
return;
+
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
if (r < 0) {
log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", u->cgroup_path);
Iterator i;
void *v;
- HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
+ HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i)
if (UNIT_DEREF(member->slice) == u)
unit_invalidate_cgroup_bpf(member);
- }
}
}
return r;
if (r == 0)
return -ENODATA;
- if (r > 0)
- r = cg_get_attribute("cpuset", u->cgroup_path, name, &v);
+
+ r = cg_get_attribute("cpuset", u->cgroup_path, name, &v);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "core-varlink.h"
+#include "mkdir.h"
+#include "user-util.h"
+#include "varlink.h"
+
+typedef struct LookupParameters {
+ const char *user_name;
+ const char *group_name;
+ union {
+ uid_t uid;
+ gid_t gid;
+ };
+ const char *service;
+} LookupParameters;
+
+static int build_user_json(const char *user_name, uid_t uid, JsonVariant **ret) {
+ assert(user_name);
+ assert(uid_is_valid(uid));
+ assert(ret);
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(uid)),
+ JSON_BUILD_PAIR("realName", JSON_BUILD_STRING("Dynamic User")),
+ JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/")),
+ JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
+ JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
+ JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.DynamicUser")),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
+}
+
+static bool user_match_lookup_parameters(LookupParameters *p, const char *name, uid_t uid) {
+ assert(p);
+
+ if (p->user_name && !streq(name, p->user_name))
+ return false;
+
+ if (uid_is_valid(p->uid) && uid != p->uid)
+ return false;
+
+ return true;
+}
+
+static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .uid = UID_INVALID,
+ };
+ _cleanup_free_ char *found_name = NULL;
+ uid_t found_uid = UID_INVALID, uid;
+ Manager *m = userdata;
+ const char *un;
+ int r;
+
+ assert(parameters);
+ assert(m);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.DynamicUser"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (uid_is_valid(p.uid))
+ r = dynamic_user_lookup_uid(m, p.uid, &found_name);
+ else if (p.user_name)
+ r = dynamic_user_lookup_name(m, p.user_name, &found_uid);
+ else {
+ Iterator i;
+ DynamicUser *d;
+
+ HASHMAP_FOREACH(d, m->dynamic_users, i) {
+ r = dynamic_user_current(d, &uid);
+ if (r == -EAGAIN) /* not realized yet? */
+ continue;
+ if (r < 0)
+ return r;
+
+ if (!user_match_lookup_parameters(&p, d->name, uid))
+ continue;
+
+ if (v) {
+ r = varlink_notify(link, v);
+ if (r < 0)
+ return r;
+
+ v = json_variant_unref(v);
+ }
+
+ r = build_user_json(d->name, uid, &v);
+ if (r < 0)
+ return r;
+ }
+
+ if (!v)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, v);
+ }
+ if (r == -ESRCH)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ if (r < 0)
+ return r;
+
+ uid = uid_is_valid(found_uid) ? found_uid : p.uid;
+ un = found_name ?: p.user_name;
+
+ if (!user_match_lookup_parameters(&p, un, uid))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ r = build_user_json(un, uid, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret) {
+ assert(group_name);
+ assert(gid_is_valid(gid));
+ assert(ret);
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)),
+ JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.DynamicUser")),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
+ }
+
+static bool group_match_lookup_parameters(LookupParameters *p, const char *name, gid_t gid) {
+ assert(p);
+
+ if (p->group_name && !streq(name, p->group_name))
+ return false;
+
+ if (gid_is_valid(p->gid) && gid != p->gid)
+ return false;
+
+ return true;
+}
+
+static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .gid = GID_INVALID,
+ };
+ _cleanup_free_ char *found_name = NULL;
+ uid_t found_gid = GID_INVALID, gid;
+ Manager *m = userdata;
+ const char *gn;
+ int r;
+
+ assert(parameters);
+ assert(m);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.DynamicUser"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (gid_is_valid(p.gid))
+ r = dynamic_user_lookup_uid(m, (uid_t) p.gid, &found_name);
+ else if (p.group_name)
+ r = dynamic_user_lookup_name(m, p.group_name, (uid_t*) &found_gid);
+ else {
+ DynamicUser *d;
+ Iterator i;
+
+ HASHMAP_FOREACH(d, m->dynamic_users, i) {
+ uid_t uid;
+
+ r = dynamic_user_current(d, &uid);
+ if (r == -EAGAIN)
+ continue;
+ if (r < 0)
+ return r;
+
+ if (!group_match_lookup_parameters(&p, d->name, (gid_t) uid))
+ continue;
+
+ if (v) {
+ r = varlink_notify(link, v);
+ if (r < 0)
+ return r;
+
+ v = json_variant_unref(v);
+ }
+
+ r = build_group_json(d->name, (gid_t) uid, &v);
+ if (r < 0)
+ return r;
+ }
+
+ if (!v)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, v);
+ }
+ if (r == -ESRCH)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ if (r < 0)
+ return r;
+
+ gid = gid_is_valid(found_gid) ? found_gid : p.gid;
+ gn = found_name ?: p.group_name;
+
+ if (!group_match_lookup_parameters(&p, gn, gid))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ r = build_group_json(gn, gid, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ LookupParameters p = {};
+ int r;
+
+ assert(parameters);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.DynamicUser"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ /* We don't support auxiliary groups with dynamic users. */
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+int manager_varlink_init(Manager *m) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+ int r;
+
+ assert(m);
+
+ if (m->varlink_server)
+ return 0;
+
+ if (!MANAGER_IS_SYSTEM(m))
+ return 0;
+
+ r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate varlink server object: %m");
+
+ varlink_server_set_userdata(s, m);
+
+ r = varlink_server_bind_method_many(
+ s,
+ "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
+ "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
+ "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register varlink methods: %m");
+
+ (void) mkdir_p("/run/systemd/userdb", 0755);
+
+ r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.DynamicUser", 0666);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to varlink socket: %m");
+
+ r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+ m->varlink_server = TAKE_PTR(s);
+ return 0;
+}
+
+void manager_varlink_done(Manager *m) {
+ assert(m);
+
+ m->varlink_server = varlink_server_unref(m->varlink_server);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "manager.h"
+
+int manager_varlink_init(Manager *m);
+void manager_varlink_done(Manager *m);
SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_ratelimit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_ratelimit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LogNamespace", "s", NULL, offsetof(ExecContext, log_namespace), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AmbientCapabilities", "t", NULL, offsetof(ExecContext, capability_ambient_set), SD_BUS_VTABLE_PROPERTY_CONST),
if (streq(name, "ProtectKernelLogs"))
return bus_set_transient_bool(u, name, &c->protect_kernel_logs, message, flags, error);
+ if (streq(name, "ProtectClock"))
+ return bus_set_transient_bool(u, name, &c->protect_clock, message, flags, error);
+
if (streq(name, "ProtectControlGroups"))
return bus_set_transient_bool(u, name, &c->protect_control_groups, message, flags, error);
return 1;
+ } else if (streq(name, "LogNamespace")) {
+ const char *n;
+
+ r = sd_bus_message_read(message, "s", &n);
+ if (r < 0)
+ return r;
+
+ if (!isempty(n) && !log_namespace_name_valid(n))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+
+ if (isempty(n)) {
+ c->log_namespace = mfree(c->log_namespace);
+ unit_write_settingf(u, flags, name, "%s=", name);
+ } else {
+ r = free_and_strdup(&c->log_namespace, n);
+ if (r < 0)
+ return r;
+
+ unit_write_settingf(u, flags, name, "%s=%s", name, n);
+ }
+ }
+
+ return 1;
+
} else if (streq(name, "LogExtraFields")) {
size_t n = 0;
r = seccomp_parse_syscall_filter("@default",
-1,
c->syscall_filter,
+ SECCOMP_PARSE_PERMISSIVE |
SECCOMP_PARSE_WHITELIST | invert_flag,
u->id,
NULL, 0);
r = seccomp_parse_syscall_filter(n,
e,
c->syscall_filter,
- (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0) | invert_flag,
+ SECCOMP_PARSE_LOG | SECCOMP_PARSE_PERMISSIVE |
+ invert_flag |
+ (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0),
u->id,
NULL, 0);
if (r < 0)
#include "sd-bus.h"
#include "alloc-util.h"
+#include "bus-util.h"
#include "dbus-job.h"
#include "dbus-unit.h"
#include "dbus.h"
#include "architecture.h"
#include "build.h"
#include "bus-common-errors.h"
+#include "bus-util.h"
#include "dbus-cgroup.h"
#include "dbus-execute.h"
#include "dbus-job.h"
assert(message);
assert(m);
- assert_cc(sizeof(uid) == sizeof(uint32_t));
+ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
r = sd_bus_message_read_basic(message, 'u', &uid);
if (r < 0)
return r;
if (r == -EAGAIN) /* not realized yet? */
continue;
if (r < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to lookup a dynamic user.");
+ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to look up a dynamic user.");
r = sd_bus_message_append(reply, "(us)", uid, d->name);
if (r < 0)
#include "alloc-util.h"
#include "bpf-firewall.h"
#include "bus-common-errors.h"
+#include "bus-polkit.h"
+#include "bus-util.h"
#include "cgroup-util.h"
#include "condition.h"
#include "dbus-job.h"
if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
*p = (cast_type) v; \
unit_write_settingf(u, flags, name, \
- "%s=%s", name, s); \
+ "%s=%s", \
+ name, strempty(s)); \
} \
\
return 1; \
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-internal.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "dbus-automount.h"
#include "dbus-cgroup.h"
return 0;
}
-static int manager_dispatch_sync_bus_names(sd_event_source *es, void *userdata) {
- _cleanup_strv_free_ char **names = NULL;
- Manager *m = userdata;
- const char *name;
- Iterator i;
- Unit *u;
- int r;
-
- assert(es);
- assert(m);
- assert(m->sync_bus_names_event_source == es);
-
- /* First things first, destroy the defer event so that we aren't triggered again */
- m->sync_bus_names_event_source = sd_event_source_unref(m->sync_bus_names_event_source);
-
- /* Let's see if there's anything to do still? */
- if (!m->api_bus)
- return 0;
- if (hashmap_isempty(m->watch_bus))
- return 0;
-
- /* OK, let's sync up the names. Let's see which names are currently on the bus. */
- r = sd_bus_list_names(m->api_bus, &names, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to get initial list of names: %m");
-
- /* We have to synchronize the current bus names with the
- * list of active services. To do this, walk the list of
- * all units with bus names. */
- HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- if (!streq_ptr(s->bus_name, name)) {
- log_unit_warning(u, "Bus name has changed from %s → %s, ignoring.", s->bus_name, name);
- continue;
- }
-
- /* Check if a service's bus name is in the list of currently
- * active names */
- if (strv_contains(names, name)) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *unique;
-
- /* If it is, determine its current owner */
- r = sd_bus_get_name_creds(m->api_bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
- if (r < 0) {
- log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get bus name owner %s: %m", name);
- continue;
- }
-
- r = sd_bus_creds_get_unique_name(creds, &unique);
- if (r < 0) {
- log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get unique name for %s: %m", name);
- continue;
- }
-
- /* Now, let's compare that to the previous bus owner, and
- * if it's still the same, all is fine, so just don't
- * bother the service. Otherwise, the name has apparently
- * changed, so synthesize a name owner changed signal. */
-
- if (!streq_ptr(unique, s->bus_name_owner))
- UNIT_VTABLE(u)->bus_name_owner_change(u, s->bus_name_owner, unique);
- } else {
- /* So, the name we're watching is not on the bus.
- * This either means it simply hasn't appeared yet,
- * or it was lost during the daemon reload.
- * Check if the service has a stored name owner,
- * and synthesize a name loss signal in this case. */
-
- if (s->bus_name_owner)
- UNIT_VTABLE(u)->bus_name_owner_change(u, s->bus_name_owner, NULL);
- }
- }
-
- return 0;
-}
-
-int manager_enqueue_sync_bus_names(Manager *m) {
- int r;
-
- assert(m);
-
- /* Enqueues a request to synchronize the bus names in a later event loop iteration. The callers generally don't
- * want us to invoke ->bus_name_owner_change() unit calls from their stack frames as this might result in event
- * dispatching on its own creating loops, hence we simply create a defer event for the event loop and exit. */
-
- if (m->sync_bus_names_event_source)
- return 0;
-
- r = sd_event_add_defer(m->event, &m->sync_bus_names_event_source, manager_dispatch_sync_bus_names, m);
- if (r < 0)
- return log_error_errno(r, "Failed to create bus name synchronization event: %m");
-
- r = sd_event_source_set_priority(m->sync_bus_names_event_source, SD_EVENT_PRIORITY_IDLE);
- if (r < 0)
- return log_error_errno(r, "Failed to set event priority: %m");
-
- r = sd_event_source_set_enabled(m->sync_bus_names_event_source, SD_EVENT_ONESHOT);
- if (r < 0)
- return log_error_errno(r, "Failed to set even to oneshot: %m");
-
- (void) sd_event_source_set_description(m->sync_bus_names_event_source, "manager-sync-bus-names");
- return 0;
-}
-
static int bus_setup_api(Manager *m, sd_bus *bus) {
Iterator i;
char *name;
m->api_bus = TAKE_PTR(bus);
- r = manager_enqueue_sync_bus_names(m);
- if (r < 0)
- return r;
-
return 0;
}
int bus_init_private(Manager *m) {
_cleanup_close_ int fd = -1;
- union sockaddr_union sa = {};
+ union sockaddr_union sa;
+ socklen_t sa_len;
sd_event_source *s;
- int r, salen;
+ int r;
assert(m);
if (getpid_cached() != 1)
return 0;
- salen = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
+ r = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
} else {
const char *e, *joined;
"XDG_RUNTIME_DIR is not set, refusing.");
joined = strjoina(e, "/systemd/private");
- salen = sockaddr_un_set_path(&sa.un, joined);
+ r = sockaddr_un_set_path(&sa.un, joined);
}
- if (salen < 0)
- return log_error_errno(salen, "Can't set path for AF_UNIX socket to bind to: %m");
+ if (r < 0)
+ return log_error_errno(r, "Can't set path for AF_UNIX socket to bind to: %m");
+ sa_len = r;
(void) mkdir_parents_label(sa.un.sun_path, 0755);
(void) sockaddr_un_unlink(&sa.un);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate private socket: %m");
- r = bind(fd, &sa.sa, salen);
+ r = bind(fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "Failed to bind private socket: %m");
/* Make sure all bus slots watching names are released. */
HASHMAP_FOREACH(u, m->watch_bus, i) {
- if (!u->match_bus_slot)
- continue;
-
- if (sd_bus_slot_get_bus(u->match_bus_slot) != *bus)
- continue;
-
- u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+ if (u->match_bus_slot && sd_bus_slot_get_bus(u->match_bus_slot) == *bus)
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+ if (u->get_name_owner_slot && sd_bus_slot_get_bus(u->get_name_owner_slot) == *bus)
+ u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
}
/* Get rid of tracked clients on this bus */
void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
-int manager_enqueue_sync_bus_names(Manager *m);
-
int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
"Device\0"
"Install\0",
+ .refuse_after = true,
.gc_jobs = true,
.init = device_init,
int r;
assert(d);
- assert(ret);
/* Get the currently assigned UID for the user, if there's any. This simply pops the data from the storage socket, and pushes it back in right-away. */
if (r < 0)
return r;
- *ret = uid;
+ if (ret)
+ *ret = uid;
+
return 0;
}
assert(m);
assert(name);
- assert(ret);
/* A friendly call for translating a dynamic user's name into its UID */
if (creds->user && (!group || streq_ptr(user, group)))
creds->group = dynamic_user_ref(creds->user);
- else {
+ else if (group) {
r = dynamic_user_acquire(m, group, &creds->group);
if (r < 0) {
if (acquired)
return move_fd(fd, nfd, false);
}
-static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
- static const union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/journal/stdout",
- };
+static int connect_journal_socket(
+ int fd,
+ const char *log_namespace,
+ uid_t uid,
+ gid_t gid) {
+
+ union sockaddr_union sa;
+ socklen_t sa_len;
uid_t olduid = UID_INVALID;
gid_t oldgid = GID_INVALID;
+ const char *j;
int r;
+ j = log_namespace ?
+ strjoina("/run/systemd/journal.", log_namespace, "/stdout") :
+ "/run/systemd/journal/stdout";
+ r = sockaddr_un_set_path(&sa.un, j);
+ if (r < 0)
+ return r;
+ sa_len = r;
+
if (gid_is_valid(gid)) {
oldgid = getgid();
}
}
- r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0 ? -errno : 0;
+ r = connect(fd, &sa.sa, sa_len) < 0 ? -errno : 0;
/* If we fail to restore the uid or gid, things will likely
fail later on. This should only happen if an LSM interferes. */
if (fd < 0)
return -errno;
- r = connect_journal_socket(fd, uid, gid);
+ r = connect_journal_socket(fd, context->log_namespace, uid, gid);
if (r < 0)
return r;
}
static int acquire_path(const char *path, int flags, mode_t mode) {
- union sockaddr_union sa = {};
+ union sockaddr_union sa;
+ socklen_t sa_len;
_cleanup_close_ int fd = -1;
- int r, salen;
+ int r;
assert(path);
if (errno != ENXIO) /* ENXIO is returned when we try to open() an AF_UNIX file system socket on Linux */
return -errno;
- if (strlen(path) >= sizeof(sa.un.sun_path)) /* Too long, can't be a UNIX socket */
- return -ENXIO;
/* So, it appears the specified path could be an AF_UNIX socket. Let's see if we can connect to it. */
+ r = sockaddr_un_set_path(&sa.un, path);
+ if (r < 0)
+ return r == -EINVAL ? -ENXIO : r;
+ sa_len = r;
+
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
return -errno;
- salen = sockaddr_un_set_path(&sa.un, path);
- if (salen < 0)
- return salen;
-
- if (connect(fd, &sa.sa, salen) < 0)
+ if (connect(fd, &sa.sa, sa_len) < 0)
return errno == EINVAL ? -ENXIO : -errno; /* Propagate initial error if we get EINVAL, i.e. we have
* indication that his wasn't an AF_UNIX socket after all */
else if ((flags & O_ACCMODE) == O_WRONLY)
r = shutdown(fd, SHUT_RD);
else
- return TAKE_FD(fd);
+ r = 0;
if (r < 0)
return -errno;
gid_t gid,
const char *tty,
char ***env,
- int fds[], size_t n_fds) {
+ const int fds[], size_t n_fds) {
#if HAVE_PAM
pam_code = pam_setcred(handle, PAM_ESTABLISH_CRED | flags);
if (pam_code != PAM_SUCCESS)
- goto fail;
+ log_debug("pam_setcred() failed, ignoring: %s", pam_strerror(handle, pam_code));
pam_code = pam_open_session(handle, flags);
if (pam_code != PAM_SUCCESS)
c->restrict_realtime ||
c->restrict_suid_sgid ||
exec_context_restrict_namespaces_set(c) ||
+ c->protect_clock ||
c->protect_kernel_tunables ||
c->protect_kernel_modules ||
c->protect_kernel_logs ||
return seccomp_protect_syslog();
}
+static int apply_protect_clock(const Unit *u, const ExecContext *c) {
+ assert(u);
+ assert(c);
+
+ if (!c->protect_clock)
+ return 0;
+
+ if (skip_seccomp_unavailable(u, "ProtectClock="))
+ return 0;
+
+ return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_CLOCK, SCMP_ACT_ERRNO(EPERM), false);
+}
+
static int apply_private_devices(const Unit *u, const ExecContext *c) {
assert(u);
assert(c);
assert(p);
assert(ret);
- our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
+ our_env = new0(char*, 15 + _EXEC_DIRECTORY_TYPE_MAX);
if (!our_env)
return -ENOMEM;
our_env[n_env++] = x;
}
+ if (c->log_namespace) {
+ x = strjoin("LOG_NAMESPACE=", c->log_namespace);
+ if (!x)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
_cleanup_free_ char *pre = NULL, *joined = NULL;
const char *n;
!strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths)))
return true;
+ if (context->log_namespace)
+ return true;
+
return false;
}
if (type != EXEC_DIRECTORY_CONFIGURATION &&
readlink_and_make_absolute(p, &target) >= 0) {
- _cleanup_free_ char *q = NULL;
+ _cleanup_free_ char *q = NULL, *q_resolved = NULL, *target_resolved = NULL;
/* This already exists and is a symlink? Interesting. Maybe it's one created
* by DynamicUser=1 (see above)?
* since they all support the private/ symlink logic at least in some
* configurations, see above. */
+ r = chase_symlinks(target, NULL, 0, &target_resolved, NULL);
+ if (r < 0)
+ goto fail;
+
q = path_join(params->prefix[type], "private", *rt);
if (!q) {
r = -ENOMEM;
goto fail;
}
- if (path_equal(q, target)) {
+ /* /var/lib or friends may be symlinks. So, let's chase them also. */
+ r = chase_symlinks(q, NULL, CHASE_NONEXISTENT, &q_resolved, NULL);
+ if (r < 0)
+ goto fail;
+
+ if (path_equal(q_resolved, target_resolved)) {
/* Hmm, apparently DynamicUser= was once turned on for this service,
* but is no longer. Let's move the directory back up. */
if (!path_equal(bind_mounts[i].source, bind_mounts[i].destination))
return true;
+ if (context->log_namespace)
+ return true;
+
return false;
}
assert(context);
- /* The runtime struct only contains the parent of the private /tmp,
- * which is non-accessible to world users. Inside of it there's a /tmp
- * that is sticky, and that's the one we want to use here. */
-
- if (context->private_tmp && runtime) {
- if (runtime->tmp_dir)
- tmp = strjoina(runtime->tmp_dir, "/tmp");
- if (runtime->var_tmp_dir)
- var = strjoina(runtime->var_tmp_dir, "/tmp");
- }
-
if (params->flags & EXEC_APPLY_CHROOT) {
root_image = context->root_image;
return r;
needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED);
- if (needs_sandboxing)
+ if (needs_sandboxing) {
+ /* The runtime struct only contains the parent of the private /tmp,
+ * which is non-accessible to world users. Inside of it there's a /tmp
+ * that is sticky, and that's the one we want to use here. */
+
+ if (context->private_tmp && runtime) {
+ if (runtime->tmp_dir)
+ tmp = strjoina(runtime->tmp_dir, "/tmp");
+ if (runtime->var_tmp_dir)
+ var = strjoina(runtime->var_tmp_dir, "/tmp");
+ }
+
ns_info = (NamespaceInfo) {
.ignore_protect_paths = false,
.private_dev = context->private_devices,
.mount_apivfs = context->mount_apivfs,
.private_mounts = context->private_mounts,
};
- else if (!context->dynamic_user && root_dir)
+ } else if (!context->dynamic_user && root_dir)
/*
* If DynamicUser=no and RootDirectory= is set then lets pass a relaxed
* sandbox info, otherwise enforce it, don't ignore protected paths and
context->n_temporary_filesystems,
tmp,
var,
+ context->log_namespace,
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|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
error_path);
/* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports
int user_lookup_fd,
int socket_fd,
int exec_fd,
- int *fds, size_t n_fds) {
+ const int *fds, size_t n_fds) {
size_t n_dont_close = 0;
int dont_close[n_fds + 12];
our_env,
pass_env,
context->environment,
- files_env,
- NULL);
+ files_env);
if (!accum_env) {
*exit_status = EXIT_MEMORY;
return log_oom();
if (ns_type_supported(NAMESPACE_NET)) {
r = setup_netns(runtime->netns_storage_socket);
- if (r < 0) {
+ if (r == -EPERM)
+ log_unit_warning_errno(unit, r,
+ "PrivateNetwork=yes is configured, but network namespace setup failed, ignoring: %m");
+ else if (r < 0) {
*exit_status = EXIT_NETWORK;
return log_unit_error_errno(unit, r, "Failed to set up network namespacing: %m");
}
} else if (context->network_namespace_path) {
*exit_status = EXIT_NETWORK;
- return log_unit_error_errno(unit, SYNTHETIC_ERRNO(EOPNOTSUPP), "NetworkNamespacePath= is not supported, refusing.");
+ return log_unit_error_errno(unit, SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "NetworkNamespacePath= is not supported, refusing.");
} else
log_unit_warning(unit, "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring.");
}
return log_unit_error_errno(unit, r, "Failed to apply kernel log restrictions: %m");
}
+ r = apply_protect_clock(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return log_unit_error_errno(unit, r, "Failed to apply clock restrictions: %m");
+ }
+
r = apply_private_devices(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
c->stdin_data_size = 0;
c->network_namespace_path = mfree(c->network_namespace_path);
+
+ c->log_namespace = mfree(c->log_namespace);
}
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
"%sProtectKernelTunables: %s\n"
"%sProtectKernelModules: %s\n"
"%sProtectKernelLogs: %s\n"
+ "%sProtectClock: %s\n"
"%sProtectControlGroups: %s\n"
"%sPrivateNetwork: %s\n"
"%sPrivateUsers: %s\n"
prefix, yes_no(c->protect_kernel_tunables),
prefix, yes_no(c->protect_kernel_modules),
prefix, yes_no(c->protect_kernel_logs),
+ prefix, yes_no(c->protect_clock),
prefix, yes_no(c->protect_control_groups),
prefix, yes_no(c->private_network),
prefix, yes_no(c->private_users),
}
}
+ if (c->log_namespace)
+ fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
+
if (c->secure_bits) {
_cleanup_free_ char *str = NULL;
r = namespace_flags_to_string(c->restrict_namespaces, &s);
if (r >= 0)
fprintf(f, "%sRestrictNamespaces: %s\n",
- prefix, s);
+ prefix, strna(s));
}
if (c->network_namespace_path)
if (!c->private_network && !c->private_tmp && !c->network_namespace_path)
return 0;
- if (c->private_tmp) {
+ if (c->private_tmp &&
+ !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
+ (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
+ prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
if (r < 0)
return r;
int log_level_max;
+ char *log_namespace;
+
bool private_tmp;
bool private_network;
bool private_devices;
bool protect_kernel_tunables;
bool protect_kernel_modules;
bool protect_kernel_logs;
+ bool protect_clock;
bool protect_control_groups;
ProtectSystem protect_system;
ProtectHome protect_home;
format = job_get_begin_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
- unit_status_printf(u, "", format);
+ unit_status_printf(u, STATUS_TYPE_NORMAL, "", format);
REENABLE_WARNING;
}
assert(t < _JOB_TYPE_MAX);
if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
+ const UnitStatusMessageFormats *formats = &UNIT_VTABLE(u)->status_message_formats;
+ if (formats->finished_job) {
+ format = formats->finished_job(u, t, result);
+ if (format)
+ return format;
+ }
format = t == JOB_START ?
- UNIT_VTABLE(u)->status_message_formats.finished_start_job[result] :
- UNIT_VTABLE(u)->status_message_formats.finished_stop_job[result];
+ formats->finished_start_job[result] :
+ formats->finished_stop_job[result];
if (format)
return format;
}
else
status = job_print_done_status_messages[result].word;
- if (result != JOB_DONE)
- manager_flip_auto_status(u->manager, true);
-
DISABLE_WARNING_FORMAT_NONLITERAL;
- unit_status_printf(u, status, format);
+ unit_status_printf(u,
+ result == JOB_DONE ? STATUS_TYPE_NORMAL : STATUS_TYPE_NOTICE,
+ status, format);
REENABLE_WARNING;
if (t == JOB_START && result == JOB_FAILED) {
$1.ProtectKernelTunables, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_tunables)
$1.ProtectKernelModules, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_modules)
$1.ProtectKernelLogs, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_logs)
+$1.ProtectClock, config_parse_bool, 0, offsetof($1, exec_context.protect_clock)
$1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups)
$1.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.network_namespace_path)
+$1.LogNamespace, config_parse_log_namespace, 0, offsetof($1, exec_context)
$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
$1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users)
$1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts)
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
+#include "syslog-util.h"
+#include "time-util.h"
#include "unit-name.h"
#include "unit-printf.h"
#include "user-util.h"
-#include "time-util.h"
#include "web-util.h"
static int parse_socket_protocol(const char *s) {
}
}
+int config_parse_log_namespace(
+ 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_free_ char *k = NULL;
+ ExecContext *c = data;
+ const Unit *u = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(c);
+
+ if (isempty(rvalue)) {
+ c->log_namespace = mfree(c->log_namespace);
+ return 0;
+ }
+
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (!log_namespace_name_valid(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Specified log namespace name is not valid: %s", k);
+ return 0;
+ }
+
+ free_and_replace(c->log_namespace, k);
+ return 0;
+}
+
int config_parse_unit_condition_path(
const char *unit,
const char *filename,
return r;
if (null_or_empty(&st)) {
- u->load_state = UNIT_MASKED;
+ /* Unit file is masked */
+
+ u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
u->fragment_mtime = 0;
} else {
u->load_state = UNIT_LOADED;
{ config_parse_unsigned, "UNSIGNED" },
{ config_parse_iec_size, "SIZE" },
{ config_parse_iec_uint64, "SIZE" },
- { config_parse_si_size, "SIZE" },
+ { config_parse_si_uint64, "SIZE" },
{ config_parse_bool, "BOOLEAN" },
{ config_parse_string, "STRING" },
{ config_parse_path, "PATH" },
CONFIG_PARSER_PROTOTYPE(config_parse_job_timeout_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_job_running_timeout_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields);
+CONFIG_PARSER_PROTOTYPE(config_parse_log_namespace);
CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_pid_file);
CONFIG_PARSER_PROTOTYPE(config_parse_exit_status);
} else if (streq(key, "quiet") && !value) {
if (arg_show_status == _SHOW_STATUS_INVALID)
- arg_show_status = SHOW_STATUS_AUTO;
+ arg_show_status = SHOW_STATUS_ERROR;
} else if (streq(key, "debug") && !value) {
m->kexec_watchdog = arg_kexec_watchdog;
m->cad_burst_action = arg_cad_burst_action;
- manager_set_show_status(m, arg_show_status);
+ manager_set_show_status(m, arg_show_status, "commandline");
m->status_unit_format = arg_status_unit_format;
}
_cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
int r;
- if (IN_SET(arg_show_status, SHOW_STATUS_NO, SHOW_STATUS_AUTO))
+ if (!show_status_on(arg_show_status))
return 0;
r = parse_os_release(NULL,
saved_log_level = m->log_level_overridden ? log_get_max_level() : -1;
saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID;
- mac_selinux_reload();
-
(void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
set_manager_defaults(m);
status_welcome();
hostname_setup();
machine_id_setup(NULL, arg_machine_id, NULL);
- loopback_setup();
+ (void) loopback_setup();
bump_unix_max_dgram_qlen();
bump_file_max_and_nr_open();
test_usr();
#include "bus-util.h"
#include "clean-ipc.h"
#include "clock-util.h"
+#include "core-varlink.h"
#include "dbus-job.h"
#include "dbus-manager.h"
#include "dbus-unit.h"
#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
-#include "io-util.h"
#include "install.h"
+#include "io-util.h"
#include "label.h"
#include "locale-setup.h"
#include "log.h"
#define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024)
/* Initial delay and the interval for printing status messages about running jobs */
-#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
+#define JOBS_IN_PROGRESS_WAIT_USEC (2*USEC_PER_SEC)
+#define JOBS_IN_PROGRESS_QUIET_WAIT_USEC (25*USEC_PER_SEC)
#define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3)
#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
static int manager_run_environment_generators(Manager *m);
static int manager_run_generators(Manager *m);
+static usec_t manager_watch_jobs_next_time(Manager *m) {
+ return usec_add(now(CLOCK_MONOTONIC),
+ show_status_on(m->show_status) ? JOBS_IN_PROGRESS_WAIT_USEC :
+ JOBS_IN_PROGRESS_QUIET_WAIT_USEC);
+}
+
static void manager_watch_jobs_in_progress(Manager *m) {
usec_t next;
int r;
if (m->jobs_in_progress_event_source)
return;
- next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC;
+ next = manager_watch_jobs_next_time(m);
r = sd_event_add_time(
m->event,
&m->jobs_in_progress_event_source,
}
}
-void manager_flip_auto_status(Manager *m, bool enable) {
+void manager_flip_auto_status(Manager *m, bool enable, const char *reason) {
assert(m);
if (enable) {
if (m->show_status == SHOW_STATUS_AUTO)
- manager_set_show_status(m, SHOW_STATUS_TEMPORARY);
+ manager_set_show_status(m, SHOW_STATUS_TEMPORARY, reason);
} else {
if (m->show_status == SHOW_STATUS_TEMPORARY)
- manager_set_show_status(m, SHOW_STATUS_AUTO);
+ manager_set_show_status(m, SHOW_STATUS_AUTO, reason);
}
}
assert(m);
assert(m->n_running_jobs > 0);
- manager_flip_auto_status(m, true);
+ manager_flip_auto_status(m, true, "delay");
print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
if (m->notify_fd < 0) {
_cleanup_close_ int fd = -1;
- union sockaddr_union sa = {};
- int salen;
+ union sockaddr_union sa;
+ socklen_t sa_len;
/* First free all secondary fields */
m->notify_socket = mfree(m->notify_socket);
if (!m->notify_socket)
return log_oom();
- salen = sockaddr_un_set_path(&sa.un, m->notify_socket);
- if (salen < 0)
- return log_error_errno(salen, "Notify socket '%s' not valid for AF_UNIX socket address, refusing.", m->notify_socket);
+ r = sockaddr_un_set_path(&sa.un, m->notify_socket);
+ if (r < 0)
+ return log_error_errno(r, "Notify socket '%s' not valid for AF_UNIX socket address, refusing.",
+ m->notify_socket);
+ sa_len = r;
(void) mkdir_parents_label(m->notify_socket, 0755);
(void) sockaddr_un_unlink(&sa.un);
- r = bind(fd, &sa.sa, salen);
+ r = bind(fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", m->notify_socket);
lookup_paths_flush_generator(&m->lookup_paths);
bus_done(m);
+ manager_varlink_done(m);
exec_runtime_vacuum(m);
hashmap_free(m->exec_runtime_by_id);
sd_event_source_unref(m->jobs_in_progress_event_source);
sd_event_source_unref(m->run_queue_event_source);
sd_event_source_unref(m->user_lookup_event_source);
- sd_event_source_unref(m->sync_bus_names_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
manager_recheck_journal(m);
manager_recheck_dbus(m);
- /* Sync current state of bus names with our set of listening units */
- (void) manager_enqueue_sync_bus_names(m);
-
/* Let's finally catch up with any changes that took place while we were reloading/reexecing */
manager_catchup(m);
log_warning_errno(r, "Failed to deserialized tracked clients, ignoring: %m");
m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
+ r = manager_varlink_init(m);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set up Varlink server, ignoring: %m");
+
/* Third, fire things up! */
manager_coldplug(m);
switch (sfsi.ssi_signo - SIGRTMIN) {
case 20:
- manager_set_show_status(m, SHOW_STATUS_YES);
+ manager_set_show_status(m, SHOW_STATUS_YES, "signal");
break;
case 21:
- manager_set_show_status(m, SHOW_STATUS_NO);
+ manager_set_show_status(m, SHOW_STATUS_NO, "signal");
break;
case 22:
if (s < 0)
log_notice("Failed to parse show-status flag '%s', ignoring.", val);
else
- manager_set_show_status(m, s);
+ manager_set_show_status(m, s, "deserialization");
} else if ((val = startswith(l, "log-level-override="))) {
int level;
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
- (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
-
+ (void) sd_event_source_set_time(m->jobs_in_progress_event_source,
+ manager_watch_jobs_next_time(m));
return;
}
- manager_flip_auto_status(m, false);
+ manager_flip_auto_status(m, false, "boot finished");
/* Notify Type=idle units that we are done now */
manager_close_idle_pipe(m);
return found;
}
-static const char *const system_env_generator_binary_paths[] = {
- "/run/systemd/system-environment-generators",
- "/etc/systemd/system-environment-generators",
- "/usr/local/lib/systemd/system-environment-generators",
- SYSTEM_ENV_GENERATOR_PATH,
- NULL
-};
-
-static const char *const user_env_generator_binary_paths[] = {
- "/run/systemd/user-environment-generators",
- "/etc/systemd/user-environment-generators",
- "/usr/local/lib/systemd/user-environment-generators",
- USER_ENV_GENERATOR_PATH,
- NULL
-};
-
static int manager_run_environment_generators(Manager *m) {
char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
- const char *const *paths;
+ _cleanup_strv_free_ char **paths = NULL;
void* args[] = {
[STDOUT_GENERATE] = &tmp,
[STDOUT_COLLECT] = &tmp,
if (MANAGER_IS_TEST_RUN(m) && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS))
return 0;
- paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths;
+ paths = env_generator_binary_paths(MANAGER_IS_SYSTEM(m));
+ if (!paths)
+ return log_oom();
- if (!generator_path_any(paths))
+ if (!generator_path_any((const char* const*) paths))
return 0;
RUN_WITH_UMASK(0022)
- r = execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment,
+ r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment,
args, NULL, m->transient_environment, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
return r;
}
log_open();
}
-void manager_set_show_status(Manager *m, ShowStatus mode) {
+void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason) {
assert(m);
- assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
+ assert(mode >= 0 && mode < _SHOW_STATUS_MAX);
if (!MANAGER_IS_SYSTEM(m))
return;
- if (m->show_status != mode)
- log_debug("%s showing of status.",
- mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
+ if (mode == m->show_status)
+ return;
+
+ bool enabled = IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
+ log_debug("%s (%s) showing of status (%s).",
+ enabled ? "Enabling" : "Disabling",
+ strna(show_status_to_string(mode)),
+ reason);
m->show_status = mode;
- if (IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES))
+ if (enabled)
(void) touch("/run/systemd/show-status");
else
(void) unlink("/run/systemd/show-status");
if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
return false;
- return IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
+ if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO)
+ return true;
+
+ return show_status_on(m->show_status);
}
const char *manager_get_confirm_spawn(Manager *m) {
r = parse_uid(value, &uid);
if (r < 0 || uid == 0) {
- log_debug("Unable to parse UID reference serialization");
+ log_debug("Unable to parse UID reference serialization: " UID_FMT, uid);
return;
}
#include "list.h"
#include "prioq.h"
#include "ratelimit.h"
+#include "varlink.h"
struct libmnt_monitor;
typedef struct Unit Unit;
typedef enum StatusType {
STATUS_TYPE_EPHEMERAL,
STATUS_TYPE_NORMAL,
+ STATUS_TYPE_NOTICE,
STATUS_TYPE_EMERGENCY,
} StatusType;
int user_lookup_fds[2];
sd_event_source *user_lookup_event_source;
- sd_event_source *sync_bus_names_event_source;
-
UnitFileScope unit_file_scope;
LookupPaths lookup_paths;
Hashmap *unit_id_map;
unsigned notifygen;
bool honor_device_enumeration;
+
+ VarlinkServer *varlink_server;
};
static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
void manager_recheck_dbus(Manager *m);
void manager_recheck_journal(Manager *m);
-void manager_set_show_status(Manager *m, ShowStatus mode);
+void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason);
void manager_set_first_boot(Manager *m, bool b);
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5);
-void manager_flip_auto_status(Manager *m, bool enable);
+void manager_flip_auto_status(Manager *m, bool enable, const char *reason);
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
bpf-firewall.h
cgroup.c
cgroup.h
+ core-varlink.c
+ core-varlink.h
dbus-automount.c
dbus-automount.h
dbus-cgroup.c
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
- { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME,
+ { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
}
-_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
+static MountParameters* get_mount_parameters_fragment(Mount *m) {
assert(m);
if (m->from_fragment)
return NULL;
}
-_pure_ static MountParameters* get_mount_parameters(Mount *m) {
+static MountParameters* get_mount_parameters(Mount *m) {
assert(m);
if (m->from_proc_self_mountinfo)
if (!is_device_path(p->what))
return 0;
- /* /dev/root is a really weird thing, it's not a real device,
- * but just a path the kernel exports for the root file system
- * specified on the kernel command line. Ignore it here. */
- if (path_equal(p->what, "/dev/root"))
+ /* /dev/root is a really weird thing, it's not a real device, but just a path the kernel exports for
+ * the root file system specified on the kernel command line. Ignore it here. */
+ if (PATH_IN_SET(p->what, "/dev/root", "/dev/nfs"))
return 0;
if (path_equal(m->where, "/"))
return 0;
- /* Mount units from /proc/self/mountinfo are not bound to devices
- * by default since they're subject to races when devices are
- * unplugged. But the user can still force this dep with an
- * appropriate option (or udev property) so the mount units are
- * automatically stopped when the device disappears suddenly. */
+ /* Mount units from /proc/self/mountinfo are not bound to devices by default since they're subject to
+ * races when devices are unplugged. But the user can still force this dep with an appropriate option
+ * (or udev property) so the mount units are automatically stopped when the device disappears
+ * suddenly. */
dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
/* We always use 'what' from /proc/self/mountinfo if mounted */
if (r < 0)
return r;
- return 0;
+ return unit_add_blockdev_dependency(UNIT(m), p->what, mask);
}
static int mount_add_quota_dependencies(Mount *m) {
}
p = get_mount_parameters_fragment(m);
- if (p && !p->what) {
- log_unit_error(UNIT(m), "What= setting is missing. Refusing.");
- return -ENOEXEC;
- }
+ if (p && !p->what && !UNIT(m)->perpetual)
+ return log_unit_error_errno(UNIT(m), SYNTHETIC_ERRNO(ENOEXEC),
+ "What= setting is missing. Refusing.");
if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) {
log_unit_error(UNIT(m), "Unit has PAM enabled. Kill mode must be set to control-group'. Refusing.");
const char *fstype,
MountProcFlags *ret_flags) {
- MountProcFlags flags = MOUNT_PROC_IS_MOUNTED;
int r;
assert(u);
- assert(flags);
+ assert(ret_flags);
if (!MOUNT(u)->where) {
MOUNT(u)->where = strdup(where);
return -ENOMEM;
}
+ /* In case we have multiple mounts established on the same mount point, let's merge flags set already
+ * for the current unit. Note that the flags field is reset on each iteration of reading
+ * /proc/self/mountinfo, hence we know for sure anything already set here is from the current
+ * iteration and thus worthy of taking into account. */
+ MountProcFlags flags =
+ MOUNT(u)->proc_flags | MOUNT_PROC_IS_MOUNTED;
+
r = update_parameters_proc_self_mountinfo(MOUNT(u), what, options, fstype);
if (r < 0)
return r;
if (r > 0)
flags |= MOUNT_PROC_JUST_CHANGED;
- if (!MOUNT(u)->from_proc_self_mountinfo || FLAGS_SET(MOUNT(u)->proc_flags, MOUNT_PROC_JUST_MOUNTED))
+ /* There are two conditions when we consider a mount point just mounted: when we haven't seen it in
+ * /proc/self/mountinfo before or when MOUNT_MOUNTING is our current state. Why bother with the
+ * latter? Shouldn't that be covered by the former? No, during reload it is not because we might then
+ * encounter a new /proc/self/mountinfo in combination with an old mount unit state (since it stems
+ * from the serialized state), and need to catch up. Since we know that the MOUNT_MOUNTING state is
+ * reached when we wait for the mount to appear we hence can assume that if we are in it, we are
+ * actually seeing it established for the first time. */
+ if (!MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->state == MOUNT_MOUNTING)
flags |= MOUNT_PROC_JUST_MOUNTED;
MOUNT(u)->from_proc_self_mountinfo = true;
"Install\0",
.private_section = "Mount",
+ .can_transient = true,
+ .can_fail = true,
+
.init = mount_init,
.load = mount_load,
.done = mount_done,
.get_timeout = mount_get_timeout,
- .can_transient = true,
-
.enumerate_perpetual = mount_enumerate_perpetual,
.enumerate = mount_enumerate,
.shutdown = mount_shutdown,
}
/* We're about to fallback to bind-mounting the device
- * node. So create a dummy bind-mount target. */
- mac_selinux_create_file_prepare(d, 0);
+ * node. So create a dummy bind-mount target.
+ * Do not prepare device-node SELinux label (see issue 13762) */
r = mknod(dn, S_IFREG, 0);
- mac_selinux_create_file_clear();
if (r < 0 && errno != EEXIST)
return log_debug_errno(errno, "mknod() fallback failed for '%s': %m", d);
r = dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
if (r < 0)
- log_debug_errno(r, "Failed to setup basic device tree at '%s', ignoring: %m", temporary_mount);
+ log_debug_errno(r, "Failed to set up basic device tree at '%s', ignoring: %m", temporary_mount);
/* Create the /dev directory if missing. It is more likely to be
* missing when the service is started with RootDirectory. This is
size_t n_temporary_filesystems,
const char* tmp_dir,
const char* var_tmp_dir,
+ const char* log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system) {
(ns_info->protect_control_groups ? 1 : 0) +
protect_home_cnt + protect_system_cnt +
(ns_info->protect_hostname ? 2 : 0) +
- (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
+ (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) +
+ !!log_namespace;
}
static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
if (protect_system == PROTECT_SYSTEM_STRICT)
return true;
- if (path_strv_contains(read_only_paths, "/"))
+ if (prefixed_path_strv_contains(read_only_paths, "/"))
return true;
return false;
if (protect_home != PROTECT_HOME_NO)
return true;
- if (path_strv_contains(read_only_paths, "/home") ||
- path_strv_contains(inaccessible_paths, "/home") ||
- path_strv_contains(empty_directories, "/home"))
+ if (prefixed_path_strv_contains(read_only_paths, "/home") ||
+ prefixed_path_strv_contains(inaccessible_paths, "/home") ||
+ prefixed_path_strv_contains(empty_directories, "/home"))
return true;
for (i = 0; i < n_temporary_filesystems; i++)
size_t n_temporary_filesystems,
const char* tmp_dir,
const char* var_tmp_dir,
+ const char *log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,
n_bind_mounts,
n_temporary_filesystems,
tmp_dir, var_tmp_dir,
+ log_namespace,
protect_home, protect_system);
if (n_mounts > 0) {
};
}
+ if (log_namespace) {
+ _cleanup_free_ char *q;
+
+ q = strjoin("/run/systemd/journal.", log_namespace);
+ if (!q) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ *(m++) = (MountEntry) {
+ .path_const = "/run/systemd/journal",
+ .mode = BIND_MOUNT_RECURSIVE,
+ .read_only = true,
+ .source_malloc = TAKE_PTR(q),
+ };
+ }
+
assert(mounts + n_mounts == m);
/* Prepend the root directory where that's necessary */
size_t n_temporary_filesystems,
const char *tmp_dir,
const char *var_tmp_dir,
+ const char *log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,
.private_section = "Path",
.can_transient = true,
+ .can_fail = true,
+ .can_trigger = true,
.init = path_init,
.done = path_done,
.can_transient = true,
.can_delegate = true,
+ .can_fail = true,
.once_only = true,
.init = scope_init,
return 1;
if (avc_open(NULL, 0) != 0) {
- int enforce, saved_errno = errno;
+ int saved_errno = errno;
+ const bool enforce = mac_selinux_enforcing();
- enforce = security_getenforce();
- log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
+ log_full_errno(enforce ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
/* If enforcement isn't on, then let's suppress this
* error, and just don't do any AVC checks. The
* warning we printed is hence all the admin will
* see. */
- if (enforce == 0)
+ if (!enforce)
return 0;
/* Return an access denied error, if we couldn't load
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *tclass = NULL, *scon = NULL;
- struct audit_info audit_info = {};
+ const char *tclass, *scon;
_cleanup_free_ char *cl = NULL;
- char *fcon = NULL;
+ _cleanup_freecon_ char *fcon = NULL;
char **cmdline = NULL;
+ const bool enforce = mac_selinux_enforcing();
int r = 0;
assert(message);
SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
&creds);
if (r < 0)
- goto finish;
+ return r;
/* The SELinux context is something we really should have
* gotten directly from the message or sender, and not be an
r = sd_bus_creds_get_selinux_context(creds, &scon);
if (r < 0)
- goto finish;
+ return r;
if (path) {
/* Get the file context of the unit file */
- r = getfilecon_raw(path, &fcon);
- if (r < 0) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
- goto finish;
+ if (getfilecon_raw(path, &fcon) < 0) {
+ r = -errno;
+
+ log_warning_errno(r, "SELinux getfilecon_raw on '%s' failed%s (perm=%s): %m",
+ path,
+ enforce ? "" : ", ignoring",
+ permission);
+ if (!enforce)
+ return 0;
+
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
}
tclass = "service";
+
} else {
- r = getcon_raw(&fcon);
- if (r < 0) {
- r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
- goto finish;
+ if (getcon_raw(&fcon) < 0) {
+ r = -errno;
+
+ log_warning_errno(r, "SELinux getcon_raw failed%s (perm=%s): %m",
+ enforce ? "" : ", ignoring",
+ permission);
+ if (!enforce)
+ return 0;
+
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
}
tclass = "system";
sd_bus_creds_get_cmdline(creds, &cmdline);
cl = strv_join(cmdline, " ");
- audit_info.creds = creds;
- audit_info.path = path;
- audit_info.cmdline = cl;
+ struct audit_info audit_info = {
+ .creds = creds,
+ .path = path,
+ .cmdline = cl,
+ };
r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
- if (r < 0)
- r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
-
- log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r);
-
-finish:
- freecon(fcon);
+ if (r < 0) {
+ r = errno_or_else(EPERM);
- if (r < 0 && security_getenforce() != 1) {
- sd_bus_error_free(error);
- r = 0;
+ if (enforce)
+ sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
}
- return r;
+ log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %m",
+ scon, fcon, tclass, permission, path, cl);
+ return enforce ? r : 0;
}
#else
#include "sd-bus.h"
-#include "bus-util.h"
#include "manager.h"
int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
}
-static void service_fix_output(Service *s) {
+static void service_fix_stdio(Service *s) {
assert(s);
- /* If nothing has been explicitly configured, patch default output in. If input is socket/tty we avoid this
- * however, since in that case we want output to default to the same place as we read input from. */
+ /* Note that EXEC_INPUT_NULL and EXEC_OUTPUT_INHERIT play a special role here: they are both the
+ * default value that is subject to automatic overriding triggered by other settings and an explicit
+ * choice the user can make. We don't distuingish between these cases currently. */
+
+ if (s->exec_context.std_input == EXEC_INPUT_NULL &&
+ s->exec_context.stdin_data_size > 0)
+ s->exec_context.std_input = EXEC_INPUT_DATA;
+
+ if (IN_SET(s->exec_context.std_input,
+ EXEC_INPUT_TTY,
+ EXEC_INPUT_TTY_FORCE,
+ EXEC_INPUT_TTY_FAIL,
+ EXEC_INPUT_SOCKET,
+ EXEC_INPUT_NAMED_FD))
+ return;
+
+ /* We assume these listed inputs refer to bidirectional streams, and hence duplicating them from
+ * stdin to stdout/stderr makes sense and hence leaving EXEC_OUTPUT_INHERIT in place makes sense,
+ * too. Outputs such as regular files or sealed data memfds otoh don't really make sense to be
+ * duplicated for both input and output at the same time (since they then would cause a feedback
+ * loop), hence override EXEC_OUTPUT_INHERIT with the default stderr/stdout setting. */
if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT &&
- s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
- s->exec_context.std_input == EXEC_INPUT_NULL)
+ s->exec_context.std_output == EXEC_OUTPUT_INHERIT)
s->exec_context.std_error = UNIT(s)->manager->default_std_error;
- if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
- s->exec_context.std_input == EXEC_INPUT_NULL)
+ if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT)
s->exec_context.std_output = UNIT(s)->manager->default_std_output;
-
- if (s->exec_context.std_input == EXEC_INPUT_NULL &&
- s->exec_context.stdin_data_size > 0)
- s->exec_context.std_input = EXEC_INPUT_DATA;
}
static int service_setup_bus_name(Service *s) {
if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
s->timeout_start_usec = USEC_INFINITY;
- service_fix_output(s);
+ service_fix_stdio(s);
r = unit_patch_contexts(UNIT(s));
if (r < 0)
break;
case SERVICE_STOP_POST:
+
+ if (control_pid_good(s) <= 0)
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+
+ break;
+
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
break;
case SERVICE_STOP_POST:
+ if (main_pid_good(s) <= 0)
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ break;
+
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
if (main_pid_good(s) <= 0)
return 1;
}
-static void service_bus_name_owner_change(
- Unit *u,
- const char *old_owner,
- const char *new_owner) {
+static void service_bus_name_owner_change(Unit *u, const char *new_owner) {
Service *s = SERVICE(u);
int r;
assert(s);
- assert(old_owner || new_owner);
-
- if (old_owner && new_owner)
- log_unit_debug(u, "D-Bus name %s changed owner from %s to %s", s->bus_name, old_owner, new_owner);
- else if (old_owner)
- log_unit_debug(u, "D-Bus name %s no longer registered by %s", s->bus_name, old_owner);
+ if (new_owner)
+ log_unit_debug(u, "D-Bus name %s now owned by %s", s->bus_name, new_owner);
else
- log_unit_debug(u, "D-Bus name %s now registered by %s", s->bus_name, new_owner);
+ log_unit_debug(u, "D-Bus name %s now not owned by anyone.", s->bus_name);
s->bus_name_good = !!new_owner;
return exec_context_get_clean_mask(&s->exec_context, ret);
}
+static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
+ if (t == JOB_START && result == JOB_DONE) {
+ Service *s = SERVICE(u);
+
+ if (s->type == SERVICE_ONESHOT)
+ return "Finished %s.";
+ }
+
+ /* Fall back to generic */
+ return NULL;
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
.can_transient = true,
.can_delegate = true,
+ .can_fail = true,
.init = service_init,
.done = service_done,
[1] = "Stopping %s...",
},
.finished_start_job = {
- [JOB_DONE] = "Started %s.",
[JOB_FAILED] = "Failed to start %s.",
[JOB_SKIPPED] = "Skipped %s.",
},
[JOB_DONE] = "Stopped %s.",
[JOB_FAILED] = "Stopped (with error) %s.",
},
+ .finished_job = service_finished_job,
},
};
static const char* const show_status_table[_SHOW_STATUS_MAX] = {
[SHOW_STATUS_NO] = "no",
+ [SHOW_STATUS_ERROR] = "error",
[SHOW_STATUS_AUTO] = "auto",
[SHOW_STATUS_TEMPORARY] = "temporary",
[SHOW_STATUS_YES] = "yes",
/* Manager status */
typedef enum ShowStatus {
- SHOW_STATUS_NO,
- SHOW_STATUS_AUTO,
- SHOW_STATUS_TEMPORARY,
- SHOW_STATUS_YES,
+ SHOW_STATUS_NO, /* printing of status is disabled */
+ SHOW_STATUS_ERROR, /* only print errors */
+ SHOW_STATUS_AUTO, /* disabled but may flip to _TEMPORARY */
+ SHOW_STATUS_TEMPORARY, /* enabled temporarily, may flip back to _AUTO */
+ SHOW_STATUS_YES, /* printing of status is enabled */
_SHOW_STATUS_MAX,
_SHOW_STATUS_INVALID = -1,
} ShowStatus;
_STATUS_UNIT_FORMAT_INVALID = -1,
} StatusUnitFormat;
+static inline bool show_status_on(ShowStatus s) {
+ return IN_SET(s, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
+}
ShowStatus show_status_from_string(const char *v) _const_;
const char* show_status_to_string(ShowStatus s) _pure_;
int parse_show_status(const char *v, ShowStatus *ret);
.private_section = "Socket",
.can_transient = true,
+ .can_trigger = true,
+ .can_fail = true,
.init = socket_init,
.done = socket_done,
return 0;
}
+static SwapParameters* swap_get_parameters(Swap *s) {
+ assert(s);
+
+ if (s->from_proc_swaps)
+ return &s->parameters_proc_swaps;
+
+ if (s->from_fragment)
+ return &s->parameters_fragment;
+
+ return NULL;
+}
+
static int swap_add_device_dependencies(Swap *s) {
+ UnitDependencyMask mask;
+ SwapParameters *p;
+ int r;
+
assert(s);
if (!s->what)
return 0;
- if (!s->from_fragment)
+ p = swap_get_parameters(s);
+ if (!p)
return 0;
- if (is_device_path(s->what))
- return unit_add_node_dependency(UNIT(s), s->what, UNIT_BINDS_TO, UNIT_DEPENDENCY_FILE);
+ mask = s->from_proc_swaps ? UNIT_DEPENDENCY_PROC_SWAP : UNIT_DEPENDENCY_FILE;
- /* File based swap devices need to be ordered after systemd-remount-fs.service,
- * since they might need a writable file system. */
- return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, UNIT_DEPENDENCY_FILE);
+ if (is_device_path(p->what)) {
+ r = unit_add_node_dependency(UNIT(s), p->what, UNIT_REQUIRES, mask);
+ if (r < 0)
+ return r;
+
+ return unit_add_blockdev_dependency(UNIT(s), p->what, mask);
+ }
+
+ /* File based swap devices need to be ordered after systemd-remount-fs.service, since they might need
+ * a writable file system. */
+ return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, mask);
}
static int swap_add_default_dependencies(Swap *s) {
static void swap_enter_dead_or_active(Swap *s, SwapResult f) {
assert(s);
- if (s->from_proc_swaps)
+ if (s->from_proc_swaps) {
+ Swap *other;
+
swap_enter_active(s, f);
- else
+
+ LIST_FOREACH_OTHERS(same_devnode, other, s)
+ if (UNIT(other)->job)
+ swap_enter_dead_or_active(other, f);
+ } else
swap_enter_dead(s, f);
}
"Install\0",
.private_section = "Swap",
+ .can_fail = true,
+
.init = swap_init,
.load = swap_load,
.done = swap_done,
systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user
systemdsystemgeneratordir=${rootprefix}/lib/systemd/system-generators
systemdusergeneratordir=${prefix}/lib/systemd/user-generators
+systemdsystemgeneratorpath=/run/systemd/system-generators:/etc/systemd/system-generators:/usr/local/lib/systemd/system-generators:${systemdsystemgeneratordir}
+systemdusergeneratorpath=/run/systemd/user-generators:/etc/systemd/user-generators:/usr/local/lib/systemd/user-generators:${systemdusergeneratordir}
systemdsleepdir=${rootprefix}/lib/systemd/system-sleep
systemdshutdowndir=${rootprefix}/lib/systemd/system-shutdown
tmpfilesdir=${prefix}/lib/tmpfiles.d
"Install\0",
.private_section = "Timer",
+ .can_transient = true,
+ .can_fail = true,
+ .can_trigger = true,
+
.init = timer_init,
.done = timer_done,
.load = timer_load,
.bus_vtable = bus_timer_vtable,
.bus_set_property = bus_timer_set_property,
-
- .can_transient = true,
};
else
status = " SKIP ";
- unit_status_printf(delete->unit, status,
+ unit_status_printf(delete->unit,
+ STATUS_TYPE_NOTICE,
+ status,
"Ordering cycle found, skipping %s");
transaction_delete_unit(tr, delete->unit);
return -EAGAIN;
!IN_SET(c->std_error,
EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
- EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE))
+ EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
+ !c->log_namespace)
return 0;
- /* If syslog or kernel logging is requested, make sure our own
- * logging daemon is run first. */
+ /* If syslog or kernel logging is requested (or log namespacing is), make sure our own logging daemon
+ * is run first. */
+
+ if (c->log_namespace) {
+ _cleanup_free_ char *socket_unit = NULL, *varlink_socket_unit = NULL;
+
+ r = unit_name_build_from_type("systemd-journald", c->log_namespace, UNIT_SOCKET, &socket_unit);
+ if (r < 0)
+ return r;
+
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, socket_unit, true, UNIT_DEPENDENCY_FILE);
+ if (r < 0)
+ return r;
+
+ r = unit_name_build_from_type("systemd-journald-varlink", c->log_namespace, UNIT_SOCKET, &varlink_socket_unit);
+ if (r < 0)
+ return r;
- r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, varlink_socket_unit, true, UNIT_DEPENDENCY_FILE);
+ if (r < 0)
+ return r;
+ } else
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
return u->assert_result;
}
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+void unit_status_printf(Unit *u, StatusType status_type, const char *status, const char *unit_status_msg_format) {
const char *d;
d = unit_status_string(u);
d = strjoina(ANSI_HIGHLIGHT, d, ANSI_NORMAL);
DISABLE_WARNING_FORMAT_NONLITERAL;
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, d);
+ manager_status_printf(u->manager, status_type, status, unit_status_msg_format, d);
REENABLE_WARNING;
}
return 0;
}
- if ((d == UNIT_BEFORE && other->type == UNIT_DEVICE) ||
- (d == UNIT_AFTER && u->type == UNIT_DEVICE)) {
- log_unit_warning(u, "Dependency Before=%s ignored (.device units cannot be delayed)", other->id);
+ if (d == UNIT_AFTER && UNIT_VTABLE(u)->refuse_after) {
+ log_unit_warning(u, "Requested dependency After=%s ignored (%s units cannot be delayed).", other->id, unit_type_to_string(u->type));
+ return 0;
+ }
+
+ if (d == UNIT_BEFORE && UNIT_VTABLE(other)->refuse_after) {
+ log_unit_warning(u, "Requested dependency Before=%s ignored (%s units cannot be delayed).", other->id, unit_type_to_string(other->type));
return 0;
}
+ if (d == UNIT_ON_FAILURE && !UNIT_VTABLE(u)->can_fail) {
+ log_unit_warning(u, "Requested dependency OnFailure=%s ignored (%s units cannot fail).", other->id, unit_type_to_string(u->type));
+ return 0;
+ }
+
+ if (d == UNIT_TRIGGERS && !UNIT_VTABLE(u)->can_trigger)
+ return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
+ "Requested dependency Triggers=%s refused (%s units cannot trigger other units).", other->id, unit_type_to_string(u->type));
+ if (d == UNIT_TRIGGERED_BY && !UNIT_VTABLE(other)->can_trigger)
+ return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
+ "Requested dependency TriggeredBy=%s refused (%s units cannot trigger other units).", other->id, unit_type_to_string(other->type));
+
r = unit_add_dependency_hashmap(u->dependencies + d, other, mask, 0);
if (r < 0)
return r;
}
static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- const char *name, *old_owner, *new_owner;
+ const char *new_owner;
Unit *u = userdata;
int r;
assert(message);
assert(u);
- r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
+ r = sd_bus_message_read(message, "sss", NULL, NULL, &new_owner);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
- old_owner = empty_to_null(old_owner);
- new_owner = empty_to_null(new_owner);
-
if (UNIT_VTABLE(u)->bus_name_owner_change)
- UNIT_VTABLE(u)->bus_name_owner_change(u, old_owner, new_owner);
+ UNIT_VTABLE(u)->bus_name_owner_change(u, empty_to_null(new_owner));
return 0;
}
u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
- if (sd_bus_error_is_set(error)) {
- log_error("Failed to get name owner from bus: %s", error->message);
- return 0;
- }
-
e = sd_bus_message_get_error(message);
- if (sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner"))
- return 0;
-
if (e) {
- log_error("Unexpected error response from GetNameOwner: %s", e->message);
- return 0;
- }
+ if (!sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner"))
+ log_unit_error(u, "Unexpected error response from GetNameOwner(): %s", e->message);
- r = sd_bus_message_read(message, "s", &new_owner);
- if (r < 0) {
- bus_log_parse_error(r);
- return 0;
- }
+ new_owner = NULL;
+ } else {
+ r = sd_bus_message_read(message, "s", &new_owner);
+ if (r < 0)
+ return bus_log_parse_error(r);
- new_owner = empty_to_null(new_owner);
+ assert(!isempty(new_owner));
+ }
if (UNIT_VTABLE(u)->bus_name_owner_change)
- UNIT_VTABLE(u)->bus_name_owner_change(u, NULL, new_owner);
+ UNIT_VTABLE(u)->bus_name_owner_change(u, new_owner);
return 0;
}
int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
const char *match;
+ int r;
assert(u);
assert(bus);
assert(name);
- if (u->match_bus_slot)
+ if (u->match_bus_slot || u->get_name_owner_slot)
return -EBUSY;
match = strjoina("type='signal',"
"member='NameOwnerChanged',"
"arg0='", name, "'");
- int r = sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
+ r = sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
if (r < 0)
return r;
- return sd_bus_call_method_async(bus,
- &u->get_name_owner_slot,
- "org.freedesktop.DBus",
- "/org/freedesktop/DBus",
- "org.freedesktop.DBus",
- "GetNameOwner",
- get_name_owner_handler,
- u,
- "s", name);
+ r = sd_bus_call_method_async(
+ bus,
+ &u->get_name_owner_slot,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetNameOwner",
+ get_name_owner_handler,
+ u,
+ "s", name);
+ if (r < 0) {
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+ return r;
+ }
+
+ log_unit_debug(u, "Watching D-Bus name '%s'.", name);
+ return 0;
}
int unit_watch_bus_name(Unit *u, const char *name) {
r = hashmap_put(u->manager->watch_bus, name, u);
if (r < 0) {
u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+ u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
return log_warning_errno(r, "Failed to put bus name to hashmap: %m");
}
}
int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, UnitDependencyMask mask) {
- Unit *device;
_cleanup_free_ char *e = NULL;
+ Unit *device;
int r;
assert(u);
if (!is_device_path(what))
return 0;
- /* When device units aren't supported (such as in a
- * container), don't create dependencies on them. */
+ /* When device units aren't supported (such as in a container), don't create dependencies on them. */
if (!unit_type_supported(UNIT_DEVICE))
return 0;
device, true, mask);
}
+int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask mask) {
+ _cleanup_free_ char *escaped = NULL, *target = NULL;
+ int r;
+
+ assert(u);
+
+ if (isempty(what))
+ return 0;
+
+ if (!path_startswith(what, "/dev/"))
+ return 0;
+
+ /* If we don't support devices, then also don't bother with blockdev@.target */
+ if (!unit_type_supported(UNIT_DEVICE))
+ return 0;
+
+ r = unit_name_path_escape(what, &escaped);
+ if (r < 0)
+ return r;
+
+ r = unit_name_build("blockdev", escaped, ".target", &target);
+ if (r < 0)
+ return r;
+
+ return unit_add_dependency_by_name(u, UNIT_AFTER, target, true, mask);
+}
+
int unit_coldplug(Unit *u) {
int r = 0, q;
char **i;
if (ec->protect_kernel_logs)
ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
+ if (ec->protect_clock)
+ ec->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) | (UINT64_C(1) << CAP_WAKE_ALARM));
+
if (ec->dynamic_user) {
if (!ec->user) {
r = user_from_unit_name(u, &ec->user);
if (r < 0)
return r;
}
+
+ if (ec->protect_clock) {
+ r = cgroup_add_device_allow(cc, "char-rtc", "r");
+ if (r < 0)
+ return r;
+ }
}
return 0;
*ref_uid = UID_INVALID;
}
-void unit_unref_uid(Unit *u, bool destroy_now) {
+static void unit_unref_uid(Unit *u, bool destroy_now) {
unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid);
}
-void unit_unref_gid(Unit *u, bool destroy_now) {
+static void unit_unref_gid(Unit *u, bool destroy_now) {
unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid);
}
+void unit_unref_uid_gid(Unit *u, bool destroy_now) {
+ assert(u);
+
+ unit_unref_uid(u, destroy_now);
+ unit_unref_gid(u, destroy_now);
+}
+
static int unit_ref_uid_internal(
Unit *u,
uid_t *ref_uid,
return 1;
}
-int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
+static int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
return unit_ref_uid_internal(u, &u->ref_uid, uid, clean_ipc, manager_ref_uid);
}
-int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
+static int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
return unit_ref_uid_internal(u, (uid_t*) &u->ref_gid, (uid_t) gid, clean_ipc, manager_ref_gid);
}
return r;
}
-void unit_unref_uid_gid(Unit *u, bool destroy_now) {
- assert(u);
-
- unit_unref_uid(u, destroy_now);
- unit_unref_gid(u, destroy_now);
-}
-
void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
int r;
if (di.origin_mask == 0 && di.destination_mask == 0) {
/* No bit set anymore, let's drop the whole entry */
assert_se(hashmap_remove(u->dependencies[d], other));
- log_unit_debug(u, "%s lost dependency %s=%s", u->id, unit_dependency_to_string(d), other->id);
+ log_unit_debug(u, "lost dependency %s=%s", unit_dependency_to_string(d), other->id);
} else
/* Mask was reduced, let's update the entry */
assert_se(hashmap_update(u->dependencies[d], other, di.data) == 0);
return exec_context_may_touch_console(ec);
}
-const char *unit_label_path(Unit *u) {
+const char *unit_label_path(const Unit *u) {
const char *p;
+ assert(u);
+
/* Returns the file system path to use for MAC access decisions, i.e. the file to read the SELinux label off
* when validating access checks. */
#include "condition.h"
#include "emergency-action.h"
#include "list.h"
+#include "show-status.h"
#include "set.h"
#include "unit-file.h"
#include "cgroup.h"
const char *starting_stopping[2];
const char *finished_start_job[_JOB_RESULT_MAX];
const char *finished_stop_job[_JOB_RESULT_MAX];
+ /* If this entry is present, it'll be called to provide a context-dependent format string,
+ * or NULL to fall back to finished_{start,stop}_job; if those are NULL too, fall back to generic. */
+ const char *(*finished_job)(Unit *u, JobType t, JobResult result);
} UnitStatusMessageFormats;
/* Flags used when writing drop-in files or transient unit files */
void (*notify_message)(Unit *u, const struct ucred *ucred, char **tags, FDSet *fds);
/* Called whenever a name this Unit registered for comes or goes away. */
- void (*bus_name_owner_change)(Unit *u, const char *old_owner, const char *new_owner);
+ void (*bus_name_owner_change)(Unit *u, const char *new_owner);
/* Called for each property that is being set */
int (*bus_set_property)(Unit *u, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
/* True if cgroup delegation is permissible */
bool can_delegate:1;
+ /* True if the unit type triggers other units, i.e. can have a UNIT_TRIGGERS dependency */
+ bool can_trigger:1;
+
+ /* True if the unit type knows a failure state, and thus can be source of an OnFailure= dependency */
+ bool can_fail:1;
+
+ /* True if After= dependencies should be refused */
+ bool refuse_after:1;
+
/* True if units of this type shall be startable only once and then never again */
bool once_only:1;
int unit_deserialize_skip(FILE *f);
int unit_add_node_dependency(Unit *u, const char *what, UnitDependency d, UnitDependencyMask mask);
+int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask mask);
int unit_coldplug(Unit *u);
void unit_catchup(Unit *u);
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0);
+void unit_status_printf(Unit *u, StatusType status_type, const char *status, const char *unit_status_msg_format) _printf_(4, 0);
bool unit_need_daemon_reload(Unit *u);
int unit_test_start_limit(Unit *u);
-void unit_unref_uid(Unit *u, bool destroy_now);
-int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc);
-
-void unit_unref_gid(Unit *u, bool destroy_now);
-int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc);
-
int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid);
void unit_unref_uid_gid(Unit *u, bool destroy_now);
bool unit_needs_console(Unit *u);
-const char *unit_label_path(Unit *u);
+const char *unit_label_path(const Unit *u);
int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
return 0;
}
-static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) {
+static int generate_keydev_mount(
+ const char *name,
+ const char *keydev,
+ const char *keydev_timeout,
+ bool canfail,
+ char **unit,
+ char **mount) {
+
_cleanup_free_ char *u = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
const char *options) {
_cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
- *keydev_mount = NULL, *keyfile_timeout_value = NULL, *password_escaped = NULL,
- *filtered = NULL, *u_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *header_path = NULL;
+ *keydev_mount = NULL, *keyfile_timeout_value = NULL,
+ *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *dmname;
bool noauto, nofail, tmp, swap, netdev, attach_in_initrd;
if (r < 0)
return r;
- fprintf(f,
- "[Unit]\n"
- "Description=Cryptography Setup for %%I\n"
- "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
- "SourcePath=%s\n"
- "DefaultDependencies=no\n"
- "IgnoreOnIsolate=true\n"
- "After=%s\n",
- arg_crypttab,
- netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
+ r = generator_write_cryptsetup_unit_section(f, arg_crypttab);
+ if (r < 0)
+ return r;
+
+ if (netdev)
+ fprintf(f, "After=remote-fs-pre.target\n");
/* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
if (!attach_in_initrd)
fprintf(f, "Conflicts=umount.target\n");
- if (password) {
- password_escaped = specifier_escape(password);
- if (!password_escaped)
- return log_oom();
- }
-
if (keydev) {
- _cleanup_free_ char *unit = NULL, *p = NULL;
+ _cleanup_free_ char *unit = NULL;
r = generate_keydev_mount(name, keydev, keyfile_timeout_value, keyfile_can_timeout > 0, &unit, &keydev_mount);
if (r < 0)
return log_error_errno(r, "Failed to generate keydev mount unit: %m");
- p = path_join(keydev_mount, password_escaped);
- if (!p)
+ password_buffer = path_join(keydev_mount, password);
+ if (!password_buffer)
return log_oom();
- free_and_replace(password_escaped, p);
+ password = password_buffer;
fprintf(f, "After=%s\n", unit);
if (keyfile_can_timeout > 0)
return r;
}
- if (path_startswith(u, "/dev/")) {
+ if (path_startswith(u, "/dev/"))
fprintf(f,
"BindsTo=%s\n"
"After=%s\n"
"Before=umount.target\n",
d, d);
-
- if (swap)
- fputs("Before=dev-mapper-%i.swap\n",
- f);
- } else
+ else
/* For loopback devices, add systemd-tmpfiles-setup-dev.service
dependency to ensure that loopback support is available in
the kernel (/dev/loop-control needs to exist) */
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
- if (filtered) {
- filtered_escaped = specifier_escape(filtered);
- if (!filtered_escaped)
- return log_oom();
- }
-
- fprintf(f,
- "\n[Service]\n"
- "Type=oneshot\n"
- "RemainAfterExit=yes\n"
- "TimeoutSec=0\n" /* the binary handles timeouts anyway */
- "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
- "OOMScoreAdjust=500\n" /* unlocking can allocate a lot of memory if Argon2 is used */
- "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
- "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
- name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped),
- name_escaped);
+ r = generator_write_cryptsetup_service_section(f, name, u, password, filtered);
+ if (r < 0)
+ return r;
if (tmp)
fprintf(f,
#include "log.h"
#include "loop-util.h"
#include "main-func.h"
+#include "parse-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
} arg_action = ACTION_DISSECT;
static const char *arg_image = NULL;
static const char *arg_path = NULL;
-static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP;
+static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
static void *arg_root_hash = NULL;
static size_t arg_root_hash_size = 0;
" --version Show package version\n"
" -m --mount Mount the image to the specified directory\n"
" -r --read-only Mount read-only\n"
+ " --fsck=BOOL Run fsck before mounting\n"
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
" --root-hash=HASH Specify root hash for verity\n",
program_invocation_short_name,
ARG_VERSION = 0x100,
ARG_DISCARD,
ARG_ROOT_HASH,
+ ARG_FSCK,
};
static const struct option options[] = {
{ "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
+ { "fsck", required_argument, NULL, ARG_FSCK },
{}
};
break;
}
+ case ARG_FSCK:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --fsck= parameter: %s", optarg);
+
+ SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r);
+ break;
+
case '?':
return -EINVAL;
return r;
r = dissected_image_mount(m, arg_path, UID_INVALID, arg_flags);
+ if (r == -EUCLEAN)
+ return log_error_errno(r, "File system check on image failed: %m");
if (r < 0)
return log_error_errno(r, "Failed to mount image: %m");
#include <getopt.h>
#include <unistd.h>
-#if HAVE_CRYPT_H
-/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be
- * removed from glibc at some point. As part of the removal, defines for
- * crypt(3) are dropped from unistd.h, and we must include crypt.h instead.
- *
- * Newer versions of glibc (v2.0+) already ship crypt.h with a definition
- * of crypt(3) as well, so we simply include it if it is present. MariaDB,
- * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the
- * same way since ages without any problems.
- */
-# include <crypt.h>
-#endif
-
#include "sd-id128.h"
#include "alloc-util.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "kbd-util.h"
+#include "libcrypt-util.h"
#include "locale-util.h"
#include "main-func.h"
#include "memory-util.h"
r = ask_password_tty(-1, msg1, NULL, 0, 0, NULL, &a);
if (r < 0)
return log_error_errno(r, "Failed to query root password: %m");
- if (strv_length(a) != 1) {
- log_warning("Received multiple passwords, where we expected one.");
- return -EINVAL;
- }
+ if (strv_length(a) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Received multiple passwords, where we expected one.");
if (isempty(*a)) {
log_warning("No password entered, skipping.");
r = ask_password_tty(-1, msg2, NULL, 0, 0, NULL, &b);
if (r < 0)
return log_error_errno(r, "Failed to query root password: %m");
+ if (strv_length(b) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Received multiple passwords, where we expected one.");
if (!streq(*a, *b)) {
log_error("Entered passwords did not match, please try again.");
fprintf(f,
"[Unit]\n"
- "SourcePath=%s\n"
- "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
- "[Swap]\n",
+ "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
+ "SourcePath=%s\n",
fstab_path());
+ r = generator_write_blockdev_dependency(f, what);
+ if (r < 0)
+ return r;
+
+ fprintf(f,
+ "\n"
+ "[Swap]\n");
+
r = write_what(f, what);
if (r < 0)
return r;
streq(me->mnt_dir, "/usr");
}
-static int write_timeout(FILE *f, const char *where, const char *opts,
- const char *filter, const char *variable) {
+static int write_timeout(
+ FILE *f,
+ const char *where,
+ const char *opts,
+ const char *filter,
+ const char *variable) {
+
_cleanup_free_ char *timeout = NULL;
char timespan[FORMAT_TIMESPAN_MAX];
usec_t u;
"x-systemd.mount-timeout\0", "TimeoutSec");
}
-static int write_dependency(FILE *f, const char *opts,
- const char *filter, const char *format) {
+static int write_dependency(
+ FILE *f,
+ const char *opts,
+ const char *filter,
+ const char *format) {
+
_cleanup_strv_free_ char **names = NULL, **units = NULL;
_cleanup_free_ char *res = NULL;
char **s;
r = unit_name_mangle_with_suffix(*s, "as dependency", 0, ".mount", &x);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
+
r = strv_consume(&units, x);
if (r < 0)
return log_oom();
}
static int write_after(FILE *f, const char *opts) {
- return write_dependency(f, opts, "x-systemd.after", "After=%1$s\n");
+ return write_dependency(f, opts,
+ "x-systemd.after", "After=%1$s\n");
}
static int write_requires_after(FILE *f, const char *opts) {
*automount_name = NULL,
*filtered = NULL,
*where_escaped = NULL;
+ _cleanup_strv_free_ char **wanted_by = NULL, **required_by = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
mount_point_ignore(where))
return 0;
+ r = fstab_extract_values(opts, "x-systemd.wanted-by", &wanted_by);
+ if (r < 0)
+ return r;
+
+ r = fstab_extract_values(opts, "x-systemd.required-by", &required_by);
+ if (r < 0)
+ return r;
+
if (path_equal(where, "/")) {
if (flags & NOAUTO)
log_warning("Ignoring \"noauto\" for root device");
log_warning("Ignoring \"nofail\" for root device");
if (flags & AUTOMOUNT)
log_warning("Ignoring automount option for root device");
+ if (!strv_isempty(wanted_by))
+ log_warning("Ignoring \"x-systemd.wanted-by=\" for root device");
+ if (!strv_isempty(required_by))
+ log_warning("Ignoring \"x-systemd.required-by=\" for root device");
+ required_by = strv_free(required_by);
+ wanted_by = strv_free(wanted_by);
SET_FLAG(flags, NOAUTO | NOFAIL | AUTOMOUNT, false);
}
fprintf(f,
"[Unit]\n"
- "SourcePath=%s\n"
- "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
+ "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
+ "SourcePath=%s\n",
source);
/* All mounts under /sysroot need to happen later, at initrd-fs.target time. IOW, it's not
return r;
}
- fprintf(f, "\n[Mount]\n");
+ r = generator_write_blockdev_dependency(f, what);
+ if (r < 0)
+ return r;
+
+ fprintf(f,
+ "\n"
+ "[Mount]\n");
+
if (original_where)
fprintf(f, "# Canonicalized from %s\n", original_where);
return r;
}
- if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) {
- r = generator_add_symlink(dest, post,
- (flags & NOFAIL) ? "wants" : "requires", name);
- if (r < 0)
- return r;
- }
-
- if (flags & AUTOMOUNT) {
+ if (!FLAGS_SET(flags, AUTOMOUNT)) {
+ if (!FLAGS_SET(flags, NOAUTO) && strv_isempty(wanted_by) && strv_isempty(required_by)) {
+ r = generator_add_symlink(dest, post,
+ (flags & NOFAIL) ? "wants" : "requires", name);
+ if (r < 0)
+ return r;
+ } else {
+ char **s;
+
+ STRV_FOREACH(s, wanted_by) {
+ r = generator_add_symlink(dest, *s, "wants", name);
+ if (r < 0)
+ return r;
+ }
+
+ STRV_FOREACH(s, required_by) {
+ r = generator_add_symlink(dest, *s, "requires", name);
+ if (r < 0)
+ return r;
+ }
+ }
+ } else {
r = unit_name_from_path(where, ".automount", &automount_name);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0)
assert_se(g = open_memstream_unlocked(&out, &out_size));
- bus_message_dump(m, g ?: stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(m, g ?: stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
r = sd_bus_message_rewind(m, true);
assert_se(r >= 0);
}
static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
- _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *id_escaped = NULL, *what_escaped = NULL;
+ _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
_cleanup_fclose_ FILE *f = NULL;
- const char *p;
int r;
assert(id);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- id_escaped = specifier_escape(id);
- if (!id_escaped)
- return log_oom();
-
- what_escaped = specifier_escape(what);
- if (!what_escaped)
- return log_oom();
+ r = generator_open_unit_file(arg_dest, NULL, n, &f);
+ if (r < 0)
+ return r;
- p = prefix_roota(arg_dest, n);
- f = fopen(p, "wxe");
- if (!f)
- return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+ r = generator_write_cryptsetup_unit_section(f, NULL);
+ if (r < 0)
+ return r;
fprintf(f,
- "# Automatically generated by systemd-gpt-auto-generator\n\n"
- "[Unit]\n"
- "Description=Cryptography Setup for %%I\n"
- "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
- "DefaultDependencies=no\n"
- "Conflicts=umount.target\n"
- "BindsTo=dev-mapper-%%i.device %s\n"
"Before=umount.target cryptsetup.target\n"
- "After=%s\n"
- "IgnoreOnIsolate=true\n"
- "[Service]\n"
- "Type=oneshot\n"
- "RemainAfterExit=yes\n"
- "TimeoutSec=0\n" /* the binary handles timeouts anyway */
- "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
- "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
- "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
- d, d,
- id_escaped, what_escaped, rw ? "" : "read-only",
- id_escaped);
+ "Conflicts=umount.target\n"
+ "BindsTo=%s\n"
+ "After=%s\n",
+ d, d);
+
+ r = generator_write_cryptsetup_service_section(f, id, what, NULL, rw ? NULL : "read-only");
+ if (r < 0)
+ return r;
r = fflush_and_check(f);
if (r < 0)
- return log_error_errno(r, "Failed to write file %s: %m", p);
+ return log_error_errno(r, "Failed to write file %s: %m", n);
r = generator_add_symlink(arg_dest, d, "wants", n);
if (r < 0)
log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
if (streq_ptr(fstype, "crypto_LUKS")) {
-
r = add_cryptsetup(id, what, rw, true, &crypto_what);
if (r < 0)
return r;
if (r < 0)
return r;
+ r = generator_write_blockdev_dependency(f, what);
+ if (r < 0)
+ return r;
+
fprintf(f,
"\n"
"[Mount]\n"
"# Automatically generated by systemd-gpt-auto-generator\n\n"
"[Unit]\n"
"Description=Swap Partition\n"
- "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
+ "Documentation=man:systemd-gpt-auto-generator(8)\n");
+
+ r = generator_write_blockdev_dependency(f, path);
+ if (r < 0)
+ return r;
+
+ fprintf(f,
+ "\n"
"[Swap]\n"
"What=%s\n",
path);
r = k;
}
+ if (m->partitions[PARTITION_VAR].found) {
+ k = add_partition_mount(m->partitions + PARTITION_VAR, "var", "/var", "Variable Data Partition");
+ if (k < 0)
+ r = k;
+ }
+
+ if (m->partitions[PARTITION_TMP].found) {
+ k = add_partition_mount(m->partitions + PARTITION_TMP, "var-tmp", "/var/tmp", "Temporary Data Partition");
+ if (k < 0)
+ r = k;
+ }
+
if (m->partitions[PARTITION_ROOT].found) {
k = add_root_rw(m->partitions + PARTITION_ROOT);
if (k < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
- "# Automatically generated by systemd-cryptsetup-generator\n\n"
+ "# Automatically generated by systemd-hibernate-resume-generator\n\n"
"[Unit]\nJobTimeoutSec=0");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in: %m");
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "dns-domain.h"
+#include "errno-util.h"
+#include "home-util.h"
+#include "libcrypt-util.h"
+#include "memory-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+
+bool suitable_user_name(const char *name) {
+
+ /* Checks whether the specified name is suitable for management via homed. Note that client-side
+ * we usually validate with the simple valid_user_group_name(), while server-side we are a bit more
+ * restrictive, so that we can change the rules server-side without having to update things
+ * client-side too. */
+
+ if (!valid_user_group_name(name))
+ return false;
+
+ /* We generally rely on NSS to tell us which users not to care for, but let's filter out some
+ * particularly well-known users. */
+ if (STR_IN_SET(name,
+ "root",
+ "nobody",
+ NOBODY_USER_NAME, NOBODY_GROUP_NAME))
+ return false;
+
+ /* Let's also defend our own namespace, as well as Debian's (unwritten?) logic of prefixing system
+ * users with underscores. */
+ if (STARTSWITH_SET(name, "systemd-", "_"))
+ return false;
+
+ return true;
+}
+
+int suitable_realm(const char *realm) {
+ _cleanup_free_ char *normalized = NULL;
+ int r;
+
+ /* Similar to the above: let's validate the realm a bit stricter server-side than client side */
+
+ r = dns_name_normalize(realm, 0, &normalized); /* this also checks general validity */
+ if (r == -EINVAL)
+ return 0;
+ if (r < 0)
+ return r;
+
+ if (!streq(realm, normalized)) /* is this normalized? */
+ return false;
+
+ if (dns_name_is_root(realm)) /* Don't allow top level domain */
+ return false;
+
+ return true;
+}
+
+int suitable_image_path(const char *path) {
+
+ return !empty_or_root(path) &&
+ path_is_valid(path) &&
+ path_is_absolute(path);
+}
+
+int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm) {
+ _cleanup_free_ char *user_name = NULL, *realm = NULL;
+ const char *c;
+ int r;
+
+ assert(t);
+ assert(ret_user_name);
+ assert(ret_realm);
+
+ c = strchr(t, '@');
+ if (!c) {
+ user_name = strdup(t);
+ if (!user_name)
+ return -ENOMEM;
+ } else {
+ user_name = strndup(t, c - t);
+ if (!user_name)
+ return -ENOMEM;
+
+ realm = strdup(c + 1);
+ if (!realm)
+ return -ENOMEM;
+ }
+
+ if (!suitable_user_name(user_name))
+ return -EINVAL;
+
+ if (realm) {
+ r = suitable_realm(realm);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+ }
+
+ *ret_user_name = TAKE_PTR(user_name);
+ *ret_realm = TAKE_PTR(realm);
+
+ return 0;
+}
+
+int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) {
+ _cleanup_(erase_and_freep) char *formatted = NULL;
+ JsonVariant *v;
+ int r;
+
+ assert(m);
+ assert(secret);
+
+ if (!FLAGS_SET(secret->mask, USER_RECORD_SECRET))
+ return sd_bus_message_append(m, "s", "{}");
+
+ v = json_variant_by_key(secret->json, "secret");
+ if (!v)
+ return -EINVAL;
+
+ r = json_variant_format(v, 0, &formatted);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_append(m, "s", formatted);
+}
+
+int test_password_one(const char *hashed_password, const char *password) {
+ struct crypt_data cc = {};
+ const char *k;
+ bool b;
+
+ errno = 0;
+ k = crypt_r(password, hashed_password, &cc);
+ if (!k) {
+ explicit_bzero_safe(&cc, sizeof(cc));
+ return errno_or_else(EINVAL);
+ }
+
+ b = streq(k, hashed_password);
+ explicit_bzero_safe(&cc, sizeof(cc));
+ return b;
+}
+
+int test_password_many(char **hashed_password, const char *password) {
+ char **hpw;
+ int r;
+
+ STRV_FOREACH(hpw, hashed_password) {
+ r = test_password_one(*hpw, password);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return true;
+ }
+
+ return false;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+#include "time-util.h"
+#include "user-record.h"
+
+bool suitable_user_name(const char *name);
+int suitable_realm(const char *realm);
+int suitable_image_path(const char *path);
+
+int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm);
+
+int bus_message_append_secret(sd_bus_message *m, UserRecord *secret);
+
+/* Many of our operations might be slow due to crypto, fsck, recursive chown() and so on. For these
+ * operations permit a *very* long time-out */
+#define HOME_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
+
+int test_password_one(const char *hashed_password, const char *password);
+int test_password_many(char **hashed_password, const char *password);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+
+#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "ask-password-api.h"
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "dns-domain.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-table.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "home-util.h"
+#include "libcrypt-util.h"
+#include "locale-util.h"
+#include "main-func.h"
+#include "memory-util.h"
+#include "openssl-util.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "pkcs11-util.h"
+#include "pretty-print.h"
+#include "process-util.h"
+#include "pwquality-util.h"
+#include "random-util.h"
+#include "rlimit-util.h"
+#include "spawn-polkit-agent.h"
+#include "terminal-util.h"
+#include "user-record-show.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "verbs.h"
+
+static PagerFlags arg_pager_flags = 0;
+static bool arg_legend = true;
+static bool arg_ask_password = true;
+static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
+static const char *arg_host = NULL;
+static const char *arg_identity = NULL;
+static JsonVariant *arg_identity_extra = NULL;
+static JsonVariant *arg_identity_extra_privileged = NULL;
+static JsonVariant *arg_identity_extra_this_machine = NULL;
+static JsonVariant *arg_identity_extra_rlimits = NULL;
+static char **arg_identity_filter = NULL; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
+static char **arg_identity_filter_rlimits = NULL;
+static uint64_t arg_disk_size = UINT64_MAX;
+static uint64_t arg_disk_size_relative = UINT64_MAX;
+static char **arg_pkcs11_token_uri = NULL;
+static bool arg_json = false;
+static JsonFormatFlags arg_json_format_flags = 0;
+static bool arg_and_resize = false;
+static bool arg_and_change_password = false;
+static enum {
+ EXPORT_FORMAT_FULL, /* export the full record */
+ EXPORT_FORMAT_STRIPPED, /* strip "state" + "binding", but leave signature in place */
+ EXPORT_FORMAT_MINIMAL, /* also strip signature */
+} arg_export_format = EXPORT_FORMAT_FULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp);
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp);
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged, json_variant_unrefp);
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, json_variant_unrefp);
+STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep);
+
+static bool identity_properties_specified(void) {
+ return
+ arg_identity ||
+ !json_variant_is_blank_object(arg_identity_extra) ||
+ !json_variant_is_blank_object(arg_identity_extra_privileged) ||
+ !json_variant_is_blank_object(arg_identity_extra_this_machine) ||
+ !json_variant_is_blank_object(arg_identity_extra_rlimits) ||
+ !strv_isempty(arg_identity_filter) ||
+ !strv_isempty(arg_identity_filter_rlimits) ||
+ !strv_isempty(arg_pkcs11_token_uri);
+}
+
+static int acquire_bus(sd_bus **bus) {
+ int r;
+
+ assert(bus);
+
+ if (*bus)
+ return 0;
+
+ r = bus_connect_transport(arg_transport, arg_host, false, bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to bus: %m");
+
+ (void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
+
+ return 0;
+}
+
+static int list_homes(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
+ int r;
+
+ (void) pager_open(arg_pager_flags);
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ListHomes",
+ &error,
+ &reply,
+ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to list homes: %s", bus_error_message(&error, r));
+
+ table = table_new("name", "uid", "gid", "state", "realname", "home", "shell");
+ if (!table)
+ return log_oom();
+
+ r = sd_bus_message_enter_container(reply, 'a', "(susussso)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ for (;;) {
+ const char *name, *state, *realname, *home, *shell, *color;
+ TableCell *cell;
+ uint32_t uid, gid;
+
+ r = sd_bus_message_read(reply, "(susussso)", &name, &uid, &state, &gid, &realname, &home, &shell, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0)
+ break;
+
+ r = table_add_many(table,
+ TABLE_STRING, name,
+ TABLE_UID, uid,
+ TABLE_GID, gid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add row to table: %m");
+
+
+ r = table_add_cell(table, &cell, TABLE_STRING, state);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add field to table: %m");
+
+ color = user_record_state_color(state);
+ if (color)
+ (void) table_set_color(table, cell, color);
+
+ r = table_add_many(table,
+ TABLE_STRING, strna(empty_to_null(realname)),
+ TABLE_STRING, home,
+ TABLE_STRING, strna(empty_to_null(shell)));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add row to table: %m");
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (table_get_rows(table) > 1 || arg_json) {
+ r = table_set_sort(table, (size_t) 0, (size_t) -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to sort table: %m");
+
+ table_set_header(table, arg_legend);
+
+ if (arg_json)
+ r = table_print_json(table, stdout, arg_json_format_flags);
+ else
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to show table: %m");
+ }
+
+ if (arg_legend && !arg_json) {
+ if (table_get_rows(table) > 1)
+ printf("\n%zu homes listed.\n", table_get_rows(table) - 1);
+ else
+ printf("No homes.\n");
+ }
+
+ return 0;
+}
+
+static int acquire_existing_password(const char *user_name, UserRecord *hr, bool emphasize_current) {
+ _cleanup_(strv_free_erasep) char **password = NULL;
+ _cleanup_free_ char *question = NULL;
+ char *e;
+ int r;
+
+ assert(user_name);
+ assert(hr);
+
+ e = getenv("PASSWORD");
+ if (e) {
+ /* People really shouldn't use environment variables for passing passwords. We support this
+ * only for testing purposes, and do not document the behaviour, so that people won't
+ * actually use this outside of testing. */
+
+ r = user_record_set_password(hr, STRV_MAKE(e), true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store password: %m");
+
+ string_erase(e);
+
+ if (unsetenv("PASSWORD") < 0)
+ return log_error_errno(errno, "Failed to unset $PASSWORD: %m");
+
+ return 0;
+ }
+
+ if (asprintf(&question, emphasize_current ?
+ "Please enter current password for user %s:" :
+ "Please enter password for user %s:",
+ user_name) < 0)
+ return log_oom();
+
+ r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &password);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire password: %m");
+
+ r = user_record_set_password(hr, password, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store password: %m");
+
+ return 0;
+}
+
+static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
+ _cleanup_(strv_free_erasep) char **pin = NULL;
+ _cleanup_free_ char *question = NULL;
+ char *e;
+ int r;
+
+ assert(user_name);
+ assert(hr);
+
+ e = getenv("PIN");
+ if (e) {
+ r = user_record_set_pkcs11_pin(hr, STRV_MAKE(e), false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store PKCS#11 PIN: %m");
+
+ string_erase(e);
+
+ if (unsetenv("PIN") < 0)
+ return log_error_errno(errno, "Failed to unset $PIN: %m");
+
+ return 0;
+ }
+
+ if (asprintf(&question, "Please enter security token PIN for user %s:", user_name) < 0)
+ return log_oom();
+
+ /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */
+ r = ask_password_auto(question, "user-home", NULL, "pkcs11-pin", USEC_INFINITY, 0, &pin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire security token PIN: %m");
+
+ r = user_record_set_pkcs11_pin(hr, pin, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store security token PIN: %m");
+
+ return 0;
+}
+
+static int handle_generic_user_record_error(
+ const char *user_name,
+ UserRecord *hr,
+ const sd_bus_error *error,
+ int ret,
+ bool emphasize_current_password) {
+ int r;
+
+ assert(user_name);
+ assert(hr);
+
+ if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT))
+ return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
+ "Home of user %s is currently absent, please plug in the necessary stroage device or backing file system.", user_name);
+
+ else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT))
+ return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
+ "Too frequent unsuccessful login attempts for user %s, try again later.", user_name);
+
+ else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
+
+ if (!strv_isempty(hr->password))
+ log_notice("Password incorrect or not sufficient, please try again.");
+
+ r = acquire_existing_password(user_name, hr, emphasize_current_password);
+ if (r < 0)
+ return r;
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
+
+ if (strv_isempty(hr->password))
+ log_notice("Security token not inserted, please enter password.");
+ else
+ log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
+
+ r = acquire_existing_password(user_name, hr, emphasize_current_password);
+ if (r < 0)
+ return r;
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
+
+ r = acquire_pkcs11_pin(user_name, hr);
+ if (r < 0)
+ return r;
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
+
+ log_notice("Please authenticate physically on security token.");
+
+ r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock security token PIN first.");
+
+ else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
+
+ log_notice("Security token PIN incorrect, please try again.");
+
+ r = acquire_pkcs11_pin(user_name, hr);
+ if (r < 0)
+ return r;
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
+
+ log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
+
+ r = acquire_pkcs11_pin(user_name, hr);
+ if (r < 0)
+ return r;
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
+
+ log_notice("Security token PIN incorrect, please try again (only one try left!).");
+
+ r = acquire_pkcs11_pin(user_name, hr);
+ if (r < 0)
+ return r;
+ } else
+ return log_error_errno(ret, "Operation on home %s failed: %s", user_name, bus_error_message(error, ret));
+
+ return 0;
+}
+
+static int activate_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r, ret = 0;
+ char **i;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, strv_skip(argv, 1)) {
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+
+ secret = user_record_new();
+ if (!secret)
+ return log_oom();
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ActivateHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *i);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, secret);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ r = handle_generic_user_record_error(*i, secret, &error, r, false);
+ if (r < 0) {
+ if (ret == 0)
+ ret = r;
+
+ break;
+ }
+ } else
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int deactivate_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r, ret = 0;
+ char **i;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, strv_skip(argv, 1)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "DeactivateHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *i);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to deactivate user home: %s", bus_error_message(&error, r));
+ if (ret == 0)
+ ret = r;
+ }
+ }
+
+ return ret;
+}
+
+static void dump_home_record(UserRecord *hr) {
+ int r;
+
+ assert(hr);
+
+ if (hr->incomplete) {
+ fflush(stdout);
+ log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr->user_name);
+ }
+
+ if (arg_json) {
+ _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
+
+ if (arg_export_format == EXPORT_FORMAT_STRIPPED)
+ r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED, &stripped);
+ else if (arg_export_format == EXPORT_FORMAT_MINIMAL)
+ r = user_record_clone(hr, USER_RECORD_EXTRACT_SIGNABLE, &stripped);
+ else
+ r = 0;
+ if (r < 0)
+ log_warning_errno(r, "Failed to strip user record, ignoring: %m");
+ if (stripped)
+ hr = stripped;
+
+ json_variant_dump(hr->json, arg_json_format_flags, stdout, NULL);
+ } else
+ user_record_show(hr, true);
+}
+
+static char **mangle_user_list(char **list, char ***ret_allocated) {
+ _cleanup_free_ char *myself = NULL;
+ char **l;
+
+ if (!strv_isempty(list)) {
+ *ret_allocated = NULL;
+ return list;
+ }
+
+ myself = getusername_malloc();
+ if (!myself)
+ return NULL;
+
+ l = new(char*, 2);
+ if (!l)
+ return NULL;
+
+ l[0] = TAKE_PTR(myself);
+ l[1] = NULL;
+
+ *ret_allocated = l;
+ return l;
+}
+
+static int inspect_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(strv_freep) char **mangled_list = NULL;
+ int r, ret = 0;
+ char **items, **i;
+
+ (void) pager_open(arg_pager_flags);
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
+ if (!items)
+ return log_oom();
+
+ STRV_FOREACH(i, items) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ const char *json;
+ int incomplete;
+ uid_t uid;
+
+ r = parse_uid(*i, &uid);
+ if (r < 0) {
+ if (!valid_user_group_name(*i)) {
+ log_error("Invalid user name '%s'.", *i);
+ if (ret == 0)
+ ret = -EINVAL;
+
+ continue;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "GetUserRecordByName",
+ &error,
+ &reply,
+ "s",
+ *i);
+ } else {
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "GetUserRecordByUID",
+ &error,
+ &reply,
+ "u",
+ (uint32_t) uid);
+ }
+
+ if (r < 0) {
+ log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
+ if (ret == 0)
+ ret = r;
+
+ continue;
+ }
+
+ r = sd_bus_message_read(reply, "sbo", &json, &incomplete, NULL);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ if (ret == 0)
+ ret = r;
+
+ continue;
+ }
+
+ r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse JSON identity: %m");
+ if (ret == 0)
+ ret = r;
+
+ continue;
+ }
+
+ hr = user_record_new();
+ if (!hr)
+ return log_oom();
+
+ r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG);
+ if (r < 0) {
+ if (ret == 0)
+ ret = r;
+
+ continue;
+ }
+
+ hr->incomplete = incomplete;
+ dump_home_record(hr);
+ }
+
+ return ret;
+}
+
+static int authenticate_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(strv_freep) char **mangled_list = NULL;
+ int r, ret = 0;
+ char **i, **items;
+
+ items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
+ if (!items)
+ return log_oom();
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ STRV_FOREACH(i, items) {
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+
+ secret = user_record_new();
+ if (!secret)
+ return log_oom();
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "AuthenticateHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *i);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, secret);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ r = handle_generic_user_record_error(*i, secret, &error, r, false);
+ if (r < 0) {
+ if (ret == 0)
+ ret = r;
+
+ break;
+ }
+ } else
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int update_last_change(JsonVariant **v, bool with_password, bool override) {
+ JsonVariant *c;
+ usec_t n;
+ int r;
+
+ assert(v);
+
+ n = now(CLOCK_REALTIME);
+
+ c = json_variant_by_key(*v, "lastChangeUSec");
+ if (c) {
+ uintmax_t u;
+
+ if (!override)
+ goto update_password;
+
+ if (!json_variant_is_unsigned(c))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec field is not an unsigned integer, refusing.");
+
+ u = json_variant_unsigned(c);
+ if (u >= n)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec is from the future, can't update.");
+ }
+
+ r = json_variant_set_field_unsigned(v, "lastChangeUSec", n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update lastChangeUSec: %m");
+
+update_password:
+ if (!with_password)
+ return 0;
+
+ c = json_variant_by_key(*v, "lastPasswordChangeUSec");
+ if (c) {
+ uintmax_t u;
+
+ if (!override)
+ return 0;
+
+ if (!json_variant_is_unsigned(c))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
+
+ u = json_variant_unsigned(c);
+ if (u >= n)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec is from the future, can't update.");
+ }
+
+ r = json_variant_set_field_unsigned(v, "lastPasswordChangeUSec", n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update lastPasswordChangeUSec: %m");
+
+ return 1;
+}
+
+static int apply_identity_changes(JsonVariant **_v) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ int r;
+
+ assert(_v);
+
+ v = json_variant_ref(*_v);
+
+ r = json_variant_filter(&v, arg_identity_filter);
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter identity: %m");
+
+ r = json_variant_merge(&v, arg_identity_extra);
+ if (r < 0)
+ return log_error_errno(r, "Failed to merge identities: %m");
+
+ if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
+ _cleanup_(json_variant_unrefp) JsonVariant *per_machine = NULL, *mmid = NULL;
+ char mids[SD_ID128_STRING_MAX];
+ sd_id128_t mid;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire machine ID: %m");
+
+ r = json_variant_new_string(&mmid, sd_id128_to_string(mid, mids));
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
+
+ per_machine = json_variant_ref(json_variant_by_key(v, "perMachine"));
+ if (per_machine) {
+ _cleanup_(json_variant_unrefp) JsonVariant *npm = NULL, *add = NULL;
+ _cleanup_free_ JsonVariant **array = NULL;
+ JsonVariant *z;
+ size_t i = 0;
+
+ if (!json_variant_is_array(per_machine))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine field is not an array, refusing.");
+
+ array = new(JsonVariant*, json_variant_elements(per_machine) + 1);
+ if (!array)
+ return log_oom();
+
+ JSON_VARIANT_ARRAY_FOREACH(z, per_machine) {
+ JsonVariant *u;
+
+ if (!json_variant_is_object(z))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine entry is not an object, refusing.");
+
+ array[i++] = z;
+
+ u = json_variant_by_key(z, "matchMachineId");
+ if (!u)
+ continue;
+
+ if (!json_variant_equal(u, mmid))
+ continue;
+
+ r = json_variant_merge(&add, z);
+ if (r < 0)
+ return log_error_errno(r, "Failed to merge perMachine entry: %m");
+
+ i--;
+ }
+
+ r = json_variant_filter(&add, arg_identity_filter);
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter perMachine: %m");
+
+ r = json_variant_merge(&add, arg_identity_extra_this_machine);
+ if (r < 0)
+ return log_error_errno(r, "Failed to merge in perMachine fields: %m");
+
+ if (arg_identity_filter_rlimits || arg_identity_extra_rlimits) {
+ _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
+
+ rlv = json_variant_ref(json_variant_by_key(add, "resourceLimits"));
+
+ r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter resource limits: %m");
+
+ r = json_variant_merge(&rlv, arg_identity_extra_rlimits);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set resource limits: %m");
+
+ if (json_variant_is_blank_object(rlv)) {
+ r = json_variant_filter(&add, STRV_MAKE("resourceLimits"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
+ } else {
+ r = json_variant_set_field(&add, "resourceLimits", rlv);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update resource limits of identity: %m");
+ }
+ }
+
+ if (!json_variant_is_blank_object(add)) {
+ r = json_variant_set_field(&add, "matchMachineId", mmid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set matchMachineId field: %m");
+
+ array[i++] = add;
+ }
+
+ r = json_variant_new_array(&npm, array, i);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate new perMachine array: %m");
+
+ json_variant_unref(per_machine);
+ per_machine = TAKE_PTR(npm);
+ } else {
+ _cleanup_(json_variant_unrefp) JsonVariant *item = json_variant_ref(arg_identity_extra_this_machine);
+
+ if (arg_identity_extra_rlimits) {
+ r = json_variant_set_field(&item, "resourceLimits", arg_identity_extra_rlimits);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update resource limits of identity: %m");
+ }
+
+ r = json_variant_set_field(&item, "matchMachineId", mmid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set matchMachineId field: %m");
+
+ r = json_variant_append_array(&per_machine, item);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append to perMachine array: %m");
+ }
+
+ r = json_variant_set_field(&v, "perMachine", per_machine);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update per machine record: %m");
+ }
+
+ if (arg_identity_extra_privileged || arg_identity_filter) {
+ _cleanup_(json_variant_unrefp) JsonVariant *privileged = NULL;
+
+ privileged = json_variant_ref(json_variant_by_key(v, "privileged"));
+
+ r = json_variant_filter(&privileged, arg_identity_filter);
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter identity (privileged part): %m");
+
+ r = json_variant_merge(&privileged, arg_identity_extra_privileged);
+ if (r < 0)
+ return log_error_errno(r, "Failed to merge identities (privileged part): %m");
+
+ if (json_variant_is_blank_object(privileged)) {
+ r = json_variant_filter(&v, STRV_MAKE("privileged"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop privileged part from identity: %m");
+ } else {
+ r = json_variant_set_field(&v, "privileged", privileged);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update privileged part of identity: %m");
+ }
+ }
+
+ if (arg_identity_filter_rlimits) {
+ _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
+
+ rlv = json_variant_ref(json_variant_by_key(v, "resourceLimits"));
+
+ r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter resource limits: %m");
+
+ /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
+
+ if (json_variant_is_blank_object(rlv)) {
+ r = json_variant_filter(&v, STRV_MAKE("resourceLimits"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
+ } else {
+ r = json_variant_set_field(&v, "resourceLimits", rlv);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update resource limits of identity: %m");
+ }
+ }
+
+ json_variant_unref(*_v);
+ *_v = TAKE_PTR(v);
+
+ return 0;
+}
+
+static int add_disposition(JsonVariant **v) {
+ int r;
+
+ assert(v);
+
+ if (json_variant_by_key(*v, "disposition"))
+ return 0;
+
+ /* Set the disposition to regular, if not configured explicitly */
+ r = json_variant_set_field_string(v, "disposition", "regular");
+ if (r < 0)
+ return log_error_errno(r, "Failed to set disposition field: %m");
+
+ return 1;
+}
+
+struct pkcs11_callback_data {
+ char *pin_used;
+ X509 *cert;
+};
+
+static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
+ erase_and_free(data->pin_used);
+ X509_free(data->cert);
+}
+
+#if HAVE_P11KIT
+static int pkcs11_callback(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_SLOT_ID slot_id,
+ const CK_SLOT_INFO *slot_info,
+ const CK_TOKEN_INFO *token_info,
+ P11KitUri *uri,
+ void *userdata) {
+
+ _cleanup_(erase_and_freep) char *pin_used = NULL;
+ struct pkcs11_callback_data *data = userdata;
+ CK_OBJECT_HANDLE object;
+ int r;
+
+ assert(m);
+ assert(slot_info);
+ assert(token_info);
+ assert(uri);
+ assert(data);
+
+ /* Called for every token matching our URI */
+
+ r = pkcs11_token_login(m, session, slot_id, token_info, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX, &pin_used);
+ if (r < 0)
+ return r;
+
+ r = pkcs11_token_find_x509_certificate(m, session, uri, &object);
+ if (r < 0)
+ return r;
+
+ r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert);
+ if (r < 0)
+ return r;
+
+ /* Let's read some random data off the token and write it to the kernel pool before we generate our
+ * random key from it. This way we can claim the quality of the RNG is at least as good as the
+ * kernel's and the token's pool */
+ (void) pkcs11_token_acquire_rng(m, session);
+
+ data->pin_used = TAKE_PTR(pin_used);
+ return 1;
+}
+#endif
+
+static int acquire_pkcs11_certificate(
+ const char *uri,
+ X509 **ret_cert,
+ char **ret_pin_used) {
+
+#if HAVE_P11KIT
+ _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {};
+ int r;
+
+ r = pkcs11_find_token(uri, pkcs11_callback, &data);
+ if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */
+ return log_error_errno(ENXIO, "Specified PKCS#11 token with URI '%s' not found.", uri);
+ if (r < 0)
+ return r;
+
+ *ret_cert = TAKE_PTR(data.cert);
+ *ret_pin_used = TAKE_PTR(data.pin_used);
+
+ return 0;
+#else
+ return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
+#endif
+}
+
+static int encrypt_bytes(
+ EVP_PKEY *pkey,
+ const void *decrypted_key,
+ size_t decrypted_key_size,
+ void **ret_encrypt_key,
+ size_t *ret_encrypt_key_size) {
+
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
+ _cleanup_free_ void *b = NULL;
+ size_t l;
+
+ ctx = EVP_PKEY_CTX_new(pkey, NULL);
+ if (!ctx)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate public key context");
+
+ if (EVP_PKEY_encrypt_init(ctx) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize public key context");
+
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure PKCS#1 padding");
+
+ if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
+
+ b = malloc(l);
+ if (!b)
+ return log_oom();
+
+ if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
+
+ *ret_encrypt_key = TAKE_PTR(b);
+ *ret_encrypt_key_size = l;
+
+ return 0;
+}
+
+static int add_pkcs11_pin(JsonVariant **v, const char *pin) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL;
+ _cleanup_(strv_free_erasep) char **pins = NULL;
+ int r;
+
+ assert(v);
+
+ if (isempty(pin))
+ return 0;
+
+ w = json_variant_ref(json_variant_by_key(*v, "secret"));
+ l = json_variant_ref(json_variant_by_key(w, "pkcs11Pin"));
+
+ r = json_variant_strv(l, &pins);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert PIN array: %m");
+
+ if (strv_find(pins, pin))
+ return 0;
+
+ r = strv_extend(&pins, pin);
+ if (r < 0)
+ return log_oom();
+
+ strv_uniq(pins);
+
+ l = json_variant_unref(l);
+
+ r = json_variant_new_array_strv(&l, pins);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate new PIN array JSON: %m");
+
+ json_variant_sensitive(l);
+
+ r = json_variant_set_field(&w, "pkcs11Pin", l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update PIN field: %m");
+
+ r = json_variant_set_field(v, "secret", w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update secret object: %m");
+
+ return 1;
+}
+
+static int add_pkcs11_encrypted_key(
+ JsonVariant **v,
+ const char *uri,
+ const void *encrypted_key, size_t encrypted_key_size,
+ const void *decrypted_key, size_t decrypted_key_size) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
+ _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+ _cleanup_free_ char *salt = NULL;
+ struct crypt_data cd = {};
+ char *k;
+ int r;
+
+ assert(v);
+ assert(uri);
+ assert(encrypted_key);
+ assert(encrypted_key_size > 0);
+ assert(decrypted_key);
+ assert(decrypted_key_size > 0);
+
+ r = make_salt(&salt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate salt: %m");
+
+ /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
+ * expect a NUL terminated string, and we use a binary key */
+ r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (r < 0)
+ return log_error_errno(r, "Failed to base64 encode secret key: %m");
+
+ errno = 0;
+ k = crypt_r(base64_encoded, salt, &cd);
+ if (!k)
+ return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m");
+
+ r = json_build(&e, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)),
+ JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)),
+ JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build encrypted JSON key object: %m");
+
+ w = json_variant_ref(json_variant_by_key(*v, "privileged"));
+ l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey"));
+
+ r = json_variant_append_array(&l, e);
+ if (r < 0)
+ return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m");
+
+ r = json_variant_set_field(&w, "pkcs11EncryptedKey", l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m");
+
+ r = json_variant_set_field(v, "privileged", w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update privileged field: %m");
+
+ return 0;
+}
+
+static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ int r;
+
+ assert(v);
+ assert(uri);
+
+ w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri"));
+ if (w) {
+ r = json_variant_strv(w, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse PKCS#11 token list: %m");
+
+ if (strv_contains(l, uri))
+ return 0;
+ }
+
+ r = strv_extend(&l, uri);
+ if (r < 0)
+ return log_oom();
+
+ w = json_variant_unref(w);
+ r = json_variant_new_array_strv(&w, l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m");
+
+ r = json_variant_set_field(v, "pkcs11TokenUri", w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m");
+
+ return 0;
+}
+
+static int add_pkcs11_key_data(JsonVariant **v, const char *uri) {
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
+ _cleanup_(erase_and_freep) char *pin = NULL;
+ size_t decrypted_key_size, encrypted_key_size;
+ _cleanup_(X509_freep) X509 *cert = NULL;
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ int bits;
+ int r;
+
+ assert(v);
+
+ r = acquire_pkcs11_certificate(uri, &cert, &pin);
+ if (r < 0)
+ return r;
+
+ pkey = X509_get0_pubkey(cert);
+ if (!pkey)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to exract public key from X.509 certificate.");
+
+ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key.");
+
+ rsa = EVP_PKEY_get0_RSA(pkey);
+ if (!rsa)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire RSA public key from X.509 certificate.");
+
+ bits = RSA_bits(rsa);
+ log_debug("Bits in RSA key: %i", bits);
+
+ /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
+ * generate a random key half the size of the RSA length */
+ decrypted_key_size = bits / 8 / 2;
+
+ if (decrypted_key_size < 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?");
+
+ log_debug("Generating %zu bytes random key.", decrypted_key_size);
+
+ decrypted_key = malloc(decrypted_key_size);
+ if (!decrypted_key)
+ return log_oom();
+
+ r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate random key: %m");
+
+ r = encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to encrypt key: %m");
+
+ /* Add the token URI to the public part of the record. */
+ r = add_pkcs11_token_uri(v, uri);
+ if (r < 0)
+ return r;
+
+ /* Include the encrypted version of the random key we just generated in the privileged part of the record */
+ r = add_pkcs11_encrypted_key(
+ v,
+ uri,
+ encrypted_key, encrypted_key_size,
+ decrypted_key, decrypted_key_size);
+ if (r < 0)
+ return r;
+
+ /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
+ * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
+ * fscrypt. */
+ r = add_pkcs11_pin(v, pin);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int acquire_new_home_record(UserRecord **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ char **i;
+ int r;
+
+ assert(ret);
+
+ if (arg_identity) {
+ unsigned line, column;
+
+ r = json_parse_file(
+ streq(arg_identity, "-") ? stdin : NULL,
+ streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
+ }
+
+ r = apply_identity_changes(&v);
+ if (r < 0)
+ return r;
+
+ r = add_disposition(&v);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, arg_pkcs11_token_uri) {
+ r = add_pkcs11_key_data(&v, *i);
+ if (r < 0)
+ return r;
+ }
+
+ r = update_last_change(&v, true, false);
+ if (r < 0)
+ return r;
+
+ if (DEBUG_LOGGING)
+ json_variant_dump(v, JSON_FORMAT_PRETTY, NULL, NULL);
+
+ hr = user_record_new();
+ if (!hr)
+ return log_oom();
+
+ r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(hr);
+ return 0;
+}
+
+static int acquire_new_password(
+ const char *user_name,
+ UserRecord *hr,
+ bool suggest) {
+
+ unsigned i = 5;
+ char *e;
+ int r;
+
+ assert(user_name);
+ assert(hr);
+
+ e = getenv("NEWPASSWORD");
+ if (e) {
+ /* As above, this is not for use, just for testing */
+
+ r = user_record_set_password(hr, STRV_MAKE(e), /* prepend = */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store password: %m");
+
+ string_erase(e);
+
+ if (unsetenv("NEWPASSWORD") < 0)
+ return log_error_errno(errno, "Failed to unse $NEWPASSWORD: %m");
+
+ return 0;
+ }
+
+ if (suggest)
+ (void) suggest_passwords();
+
+ for (;;) {
+ _cleanup_(strv_free_erasep) char **first = NULL, **second = NULL;
+ _cleanup_free_ char *question = NULL;
+
+ if (--i == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up:");
+
+ if (asprintf(&question, "Please enter new password for user %s:", user_name) < 0)
+ return log_oom();
+
+ r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &first);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire password: %m");
+
+ question = mfree(question);
+ if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
+ return log_oom();
+
+ r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &second);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire password: %m");
+
+ if (strv_equal(first, second)) {
+ r = user_record_set_password(hr, first, /* prepend = */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store password: %m");
+
+ return 0;
+ }
+
+ log_error("Password didn't mach, try again.");
+ }
+}
+
+static int create_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ _cleanup_strv_free_ char **original_hashed_passwords = NULL;
+ int r;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ if (argc >= 2) {
+ /* If a username was specified, use it */
+
+ if (valid_user_group_name(argv[1]))
+ r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
+ else {
+ _cleanup_free_ char *un = NULL, *rr = NULL;
+
+ /* Before we consider the user name invalid, let's check if we can split it? */
+ r = split_user_name_realm(argv[1], &un, &rr);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
+
+ if (rr) {
+ r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set realm field: %m");
+ }
+
+ r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set userName field: %m");
+ } else {
+ /* If neither a username nor an identity have been specified we cannot operate. */
+ if (!arg_identity)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
+ }
+
+ r = acquire_new_home_record(&hr);
+ if (r < 0)
+ return r;
+
+ /* Remember the original hashed paswords before we add our own, so that we can return to them later,
+ * should the entered password turn out not to be acceptable. */
+ original_hashed_passwords = strv_copy(hr->hashed_password);
+ if (!original_hashed_passwords)
+ return log_oom();
+
+ /* If the JSON record carries no plain text password, then let's query it manually. */
+ if (!hr->password) {
+
+ if (strv_isempty(hr->hashed_password)) {
+ /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
+ r = acquire_new_password(hr->user_name, hr, /* suggest = */ true);
+ if (r < 0)
+ return r;
+
+ r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to hash password: %m");
+ } else {
+ /* There's a hash password set in the record, acquire the unhashed version of it. */
+ r = acquire_existing_password(hr->user_name, hr, false);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (hr->enforce_password_policy == 0) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ /* If password quality enforcement is disabled, let's at least warn client side */
+
+ r = quality_check_password(hr, hr, &error);
+ if (r < 0)
+ log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error, r));
+ }
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(erase_and_freep) char *formatted = NULL;
+
+ r = json_variant_format(hr->json, 0, &formatted);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "CreateHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", formatted);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (!sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY))
+ return log_error_errno(r, "Failed to create user home: %s", bus_error_message(&error, r));
+
+ log_error_errno(r, "%s", bus_error_message(&error, r));
+ log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
+ } else
+ break; /* done */
+
+ r = user_record_set_hashed_password(hr, original_hashed_passwords);
+ if (r < 0)
+ return r;
+
+ r = acquire_new_password(hr->user_name, hr, /* suggest = */ false);
+ if (r < 0)
+ return r;
+
+ r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to hash passwords: %m");
+ }
+
+ return 0;
+}
+
+static int remove_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r, ret = 0;
+ char **i;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ STRV_FOREACH(i, strv_skip(argv, 1)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "RemoveHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *i);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to remove home: %s", bus_error_message(&error, r));
+ if (ret == 0)
+ ret = r;
+ }
+ }
+
+ return ret;
+}
+
+static int acquire_updated_home_record(
+ sd_bus *bus,
+ const char *username,
+ UserRecord **ret) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ char **i;
+ int r;
+
+ assert(ret);
+
+ if (arg_identity) {
+ unsigned line, column;
+ JsonVariant *un;
+
+ r = json_parse_file(
+ streq(arg_identity, "-") ? stdin : NULL,
+ streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &json, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
+
+ un = json_variant_by_key(json, "userName");
+ if (un) {
+ if (!json_variant_is_string(un) || (username && !streq(json_variant_string(un), username)))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name specified on command line and in JSON record do not match.");
+ } else {
+ if (!username)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No username specified.");
+
+ r = json_variant_set_field_string(&arg_identity_extra, "userName", username);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set userName field: %m");
+ }
+
+ } else {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int incomplete;
+ const char *text;
+
+ if (!identity_properties_specified())
+ return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "No field to change specified.");
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "GetUserRecordByName",
+ &error,
+ &reply,
+ "s",
+ username);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire user home record: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "sbo", &text, &incomplete, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (incomplete)
+ return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Lacking rights to acquire user record including privileged metadata, can't update record.");
+
+ r = json_parse(text, JSON_PARSE_SENSITIVE, &json, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse JSON identity: %m");
+
+ reply = sd_bus_message_unref(reply);
+
+ r = json_variant_filter(&json, STRV_MAKE("binding", "status", "signature"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to strip binding and status from record to update: %m");
+ }
+
+ r = apply_identity_changes(&json);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, arg_pkcs11_token_uri) {
+ r = add_pkcs11_key_data(&json, *i);
+ if (r < 0)
+ return r;
+ }
+
+ /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
+ * override. */
+ r = update_last_change(&json, !!arg_pkcs11_token_uri, !arg_identity);
+ if (r < 0)
+ return r;
+
+ if (DEBUG_LOGGING)
+ json_variant_dump(json, JSON_FORMAT_PRETTY, NULL, NULL);
+
+ hr = user_record_new();
+ if (!hr)
+ return log_oom();
+
+ r = user_record_load(hr, json, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(hr);
+ return 0;
+}
+
+static int update_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ _cleanup_free_ char *buffer = NULL;
+ const char *username;
+ int r;
+
+ if (argc >= 2)
+ username = argv[1];
+ else if (!arg_identity) {
+ buffer = getusername_malloc();
+ if (!buffer)
+ return log_oom();
+
+ username = buffer;
+ } else
+ username = NULL;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ r = acquire_updated_home_record(bus, username, &hr);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_free_ char *formatted = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "UpdateHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = json_variant_format(hr->json, 0, &formatted);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "s", formatted);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (arg_and_change_password &&
+ sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
+ /* In the generic handler we'd ask for a password in this case, but when
+ * changing passwords that's not sufficient, as we need to acquire all keys
+ * first. */
+ return log_error_errno(r, "Security token not inserted, refusing.");
+
+ r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+ if (r < 0)
+ return r;
+ } else
+ break;
+ }
+
+ /* Also sync down disk size to underlying LUKS/fscrypt/quota */
+ while (arg_and_resize) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ log_debug("Resizing");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ResizeHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
+ r = sd_bus_message_append(m, "st", hr->user_name, UINT64_MAX);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, hr);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (arg_and_change_password &&
+ sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
+ return log_error_errno(r, "Security token not inserted, refusing.");
+
+ r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+ if (r < 0)
+ return r;
+ } else
+ break;
+ }
+
+ /* Also sync down passwords to underlying LUKS/fscrypt */
+ while (arg_and_change_password) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ log_debug("Propagating password");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ChangePasswordHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
+ r = sd_bus_message_append(m, "ss", hr->user_name, "{}");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, hr);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
+ return log_error_errno(r, "Security token not inserted, refusing.");
+
+ r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+ if (r < 0)
+ return r;
+ } else
+ break;
+ }
+
+ return 0;
+}
+
+static int passwd_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(user_record_unrefp) UserRecord *old_secret = NULL, *new_secret = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_free_ char *buffer = NULL;
+ const char *username;
+ int r;
+
+ if (arg_pkcs11_token_uri)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'.");
+ if (identity_properties_specified())
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time.");
+
+ if (argc >= 2)
+ username = argv[1];
+ else {
+ buffer = getusername_malloc();
+ if (!buffer)
+ return log_oom();
+
+ username = buffer;
+ }
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ old_secret = user_record_new();
+ if (!old_secret)
+ return log_oom();
+
+ new_secret = user_record_new();
+ if (!new_secret)
+ return log_oom();
+
+ r = acquire_new_password(username, new_secret, /* suggest = */ true);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ChangePasswordHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", username);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, new_secret);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, old_secret);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
+
+ log_error_errno(r, "%s", bus_error_message(&error, r));
+
+ r = acquire_new_password(username, new_secret, /* suggest = */ false);
+
+ } else if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
+
+ /* In the generic handler we'd ask for a password in this case, but when
+ * changing passwords that's not sufficeint, as we need to acquire all keys
+ * first. */
+ return log_error_errno(r, "Security token not inserted, refusing.");
+ else
+ r = handle_generic_user_record_error(username, old_secret, &error, r, true);
+ if (r < 0)
+ return r;
+ } else
+ break;
+ }
+
+ return 0;
+}
+
+static int resize_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ uint64_t ds = UINT64_MAX;
+ int r;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ if (arg_disk_size_relative != UINT64_MAX ||
+ (argc > 2 && parse_percent(argv[2]) >= 0))
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Relative disk size specification currently not supported when resizing.");
+
+ if (argc > 2) {
+ r = parse_size(argv[2], 1024, &ds);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse disk size parameter: %s", argv[2]);
+ }
+
+ if (arg_disk_size != UINT64_MAX) {
+ if (ds != UINT64_MAX && ds != arg_disk_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size specified twice and doesn't match, refusing.");
+
+ ds = arg_disk_size;
+ }
+
+ secret = user_record_new();
+ if (!secret)
+ return log_oom();
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ResizeHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "st", argv[1], ds);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, secret);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
+ if (r < 0)
+ return r;
+ } else
+ break;
+ }
+
+ return 0;
+}
+
+static int lock_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r, ret = 0;
+ char **i;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, strv_skip(argv, 1)) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "LockHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *i);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to lock home: %s", bus_error_message(&error, r));
+ if (ret == 0)
+ ret = r;
+ }
+ }
+
+ return ret;
+}
+
+static int unlock_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r, ret = 0;
+ char **i;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, strv_skip(argv, 1)) {
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+
+ secret = user_record_new();
+ if (!secret)
+ return log_oom();
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "UnlockHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *i);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, secret);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
+ if (r < 0) {
+ if (ret == 0)
+ ret = r;
+
+ break;
+ }
+ } else
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int with_home(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ _cleanup_close_ int acquired_fd = -1;
+ _cleanup_strv_free_ char **cmdline = NULL;
+ const char *home;
+ int r, ret;
+ pid_t pid;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ if (argc < 3) {
+ _cleanup_free_ char *shell = NULL;
+
+ /* If no command is specified, spawn a shell */
+ r = get_shell(&shell);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire shell: %m");
+
+ cmdline = strv_new(shell);
+ } else
+ cmdline = strv_copy(argv + 2);
+ if (!cmdline)
+ return log_oom();
+
+ secret = user_record_new();
+ if (!secret)
+ return log_oom();
+
+ for (;;) {
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "AcquireHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", argv[1]);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = bus_message_append_secret(m, secret);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+ m = sd_bus_message_unref(m);
+ if (r < 0) {
+ r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
+ if (r < 0)
+ return r;
+
+ sd_bus_error_free(&error);
+ } else {
+ int fd;
+
+ r = sd_bus_message_read(reply, "h", &fd);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (acquired_fd < 0)
+ return log_error_errno(errno, "Failed to duplicate acquired fd: %m");
+
+ reply = sd_bus_message_unref(reply);
+ break;
+ }
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "GetHomeByName",
+ &error,
+ &reply,
+ "s",
+ argv[1]);
+ if (r < 0)
+ return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "usussso", NULL, NULL, NULL, NULL, &home, NULL, NULL);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = safe_fork("(with)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REOPEN_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (chdir(home) < 0) {
+ log_error_errno(errno, "Failed to change to directory %s: %m", home);
+ _exit(255);
+ }
+
+ execvp(cmdline[0], cmdline);
+ log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]);
+ _exit(255);
+ }
+
+ ret = wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL);
+
+ /* Close the fd that pings the home now. */
+ acquired_fd = safe_close(acquired_fd);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ReleaseHome");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", argv[1]);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
+ log_notice("Not deactivating home directory of %s, as it is still used.", argv[1]);
+ else
+ return log_error_errno(r, "Failed to release user home: %s", bus_error_message(&error, r));
+ }
+
+ return ret;
+}
+
+static int lock_all_homes(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "LockAllHomes");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to lock home: %s", bus_error_message(&error, r));
+
+ return 0;
+}
+
+static int drop_from_identity(const char *field) {
+ int r;
+
+ assert(field);
+
+ /* If we are called to update an identity record and drop some field, let's keep track of what to
+ * remove from the old record */
+ r = strv_extend(&arg_identity_filter, field);
+ if (r < 0)
+ return log_oom();
+
+ /* Let's also drop the field if it was previously set to a new value on the same command line */
+ r = json_variant_filter(&arg_identity_extra, STRV_MAKE(field));
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+ r = json_variant_filter(&arg_identity_extra_this_machine, STRV_MAKE(field));
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+ r = json_variant_filter(&arg_identity_extra_privileged, STRV_MAKE(field));
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+ return 0;
+}
+
+static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("homectl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%1$s [OPTIONS...] COMMAND ...\n\n"
+ "%2$sCreate, manipulate or inspect home directories.%3$s\n"
+ "\n%4$sCommands:%5$s\n"
+ " list List homes\n"
+ " activate USER… Activate home\n"
+ " deactivate USER… Deactivate home\n"
+ " inspect USER… Inspect home\n"
+ " authenticate USER… Authenticate home\n"
+ " create USER Create a home area\n"
+ " remove USER… Remove a home area\n"
+ " update USER Update a home area\n"
+ " passwd USER Change password of a home area\n"
+ " resize USER SIZE Resize a home area\n"
+ " lock USER… Temporarily lock an active home\n"
+ " unlock USER… Unlock a temporarily locked home\n"
+ " lock-all Lock all suitable homes\n"
+ " with USER [COMMAND…] Run shell or command with access to home\n"
+ "\n%4$sOptions:%5$s\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n"
+ " --no-ask-password Do not ask for system passwords\n"
+ " -H --host=[USER@]HOST Operate on remote host\n"
+ " -M --machine=CONTAINER Operate on local container\n"
+ " --identity=PATH Read JSON identity from file\n"
+ " --json=FORMAT Output inspection data in JSON (takes one of\n"
+ " pretty, short, off)\n"
+ " -j Equivalent to --json=pretty (on TTY) or\n"
+ " --json=short (otherwise)\n"
+ " --export-format= Strip JSON inspection data (full, stripped,\n"
+ " minimal)\n"
+ " -E When specified once equals -j --export-format=\n"
+ " stripped, when specified twice equals\n"
+ " -j --export-format=minimal\n"
+ "\n%4$sGeneral User Record Properties:%5$s\n"
+ " -c --real-name=REALNAME Real name for user\n"
+ " --realm=REALM Realm to create user in\n"
+ " --email-address=EMAIL Email address for user\n"
+ " --location=LOCATION Set location of user on earth\n"
+ " --icon-name=NAME Icon name for user\n"
+ " -d --home-dir=PATH Home directory\n"
+ " --uid=UID Numeric UID for user\n"
+ " -G --member-of=GROUP Add user to group\n"
+ " --skel=PATH Skeleton directory to use\n"
+ " --shell=PATH Shell for account\n"
+ " --setenv=VARIABLE=VALUE Set an environment variable at log-in\n"
+ " --timezone=TIMEZONE Set a time-zone\n"
+ " --language=LOCALE Set preferred language\n"
+ " --ssh-authorized-keys=KEYS\n"
+ " Specify SSH public keys\n"
+ " --pkcs11-token-uri=URI URI to PKCS#11 security token containing\n"
+ " private key and matching X.509 certificate\n"
+ "\n%4$sAccount Management User Record Properties:%5$s\n"
+ " --locked=BOOL Set locked account state\n"
+ " --not-before=TIMESTAMP Do not allow logins before\n"
+ " --not-after=TIMESTAMP Do not allow logins after\n"
+ " --rate-limit-interval=SECS\n"
+ " Login rate-limit interval in seconds\n"
+ " --rate-limit-burst=NUMBER\n"
+ " Login rate-limit attempts per interval\n"
+ "\n%4$sPassword Policy User Record Properties:%5$s\n"
+ " --password-hint=HINT Set Password hint\n"
+ " --enforce-password-policy=BOOL\n"
+ " Control whether to enforce system's password\n"
+ " policy for this user\n"
+ " -P Equivalent to --enforce-password-password=no\n"
+ " --password-change-now=BOOL\n"
+ " Require the password to be changed on next login\n"
+ " --password-change-min=TIME\n"
+ " Require minimum time between password changes\n"
+ " --password-change-max=TIME\n"
+ " Require maximum time between password changes\n"
+ " --password-change-warn=TIME\n"
+ " How much time to warn before password expiry\n"
+ " --password-change-inactive=TIME\n"
+ " How much time to block password after expiry\n"
+ "\n%4$sResource Management User Record Properties:%5$s\n"
+ " --disk-size=BYTES Size to assign the user on disk\n"
+ " --access-mode=MODE User home directory access mode\n"
+ " --umask=MODE Umask for user when logging in\n"
+ " --nice=NICE Nice level for user\n"
+ " --rlimit=LIMIT=VALUE[:VALUE]\n"
+ " Set resource limits\n"
+ " --tasks-max=MAX Set maximum number of per-user tasks\n"
+ " --memory-high=BYTES Set high memory threshold in bytes\n"
+ " --memory-max=BYTES Set maximum memory limit\n"
+ " --cpu-weight=WEIGHT Set CPU weight\n"
+ " --io-weight=WEIGHT Set IO weight\n"
+ "\n%4$sStorage User Record Properties:%5$s\n"
+ " --storage=STORAGE Storage type to use (luks, fscrypt, directory,\n"
+ " subvolume, cifs)\n"
+ " --image-path=PATH Path to image file/directory\n"
+ "\n%4$sLUKS Storage User Record Properties:%5$s\n"
+ " --fs-type=TYPE File system type to use in case of luks\n"
+ " storage (ext4, xfs, btrfs)\n"
+ " --luks-discard=BOOL Whether to use 'discard' feature of file system\n"
+ " --luks-cipher=CIPHER Cipher to use for LUKS encryption\n"
+ " --luks-cipher-mode=MODE Cipher mode to use for LUKS encryption\n"
+ " --luks-volume-key-size=BITS\n"
+ " Volume key size to use for LUKS encryption\n"
+ " --luks-pbkdf-type=TYPE Password-based Key Derivation Function to use\n"
+ " --luks-pbkdf-hash-algorithm=ALGORITHM\n"
+ " PBKDF hash algorithm to use\n"
+ " --luks-pbkdf-time-cost=SECS\n"
+ " Time cost for PBKDF in seconds\n"
+ " --luks-pbkdf-memory-cost=BYTES\n"
+ " Memory cost for PBKDF in bytes\n"
+ " --luks-pbkdf-parallel-threads=NUMBER\n"
+ " Number of parallel threads for PKBDF\n"
+ "\n%4$sMounting User Record Properties:%5$s\n"
+ " --nosuid=BOOL Control the 'nosuid' flag of the home mount\n"
+ " --nodev=BOOL Control the 'nodev' flag of the home mount\n"
+ " --noexec=BOOL Control the 'noexec' flag of the home mount\n"
+ "\n%4$sCIFS User Record Properties:%5$s\n"
+ " --cifs-domain=DOMAIN CIFS (Windows) domain\n"
+ " --cifs-user-name=USER CIFS (Windows) user name\n"
+ " --cifs-service=SERVICE CIFS (Windows) service to mount as home\n"
+ "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
+ " --stop-delay=SECS How long to leave user services running after\n"
+ " logout\n"
+ " --kill-processes=BOOL Whether to kill user processes when sessions\n"
+ " terminate\n"
+ " --auto-login=BOOL Try to log this user in automatically\n"
+ "\nSee the %6$s for details.\n"
+ , program_invocation_short_name
+ , ansi_highlight(), ansi_normal()
+ , ansi_underline(), ansi_normal()
+ , link
+ );
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_NO_PAGER,
+ ARG_NO_LEGEND,
+ ARG_NO_ASK_PASSWORD,
+ ARG_REALM,
+ ARG_EMAIL_ADDRESS,
+ ARG_DISK_SIZE,
+ ARG_ACCESS_MODE,
+ ARG_STORAGE,
+ ARG_FS_TYPE,
+ ARG_IMAGE_PATH,
+ ARG_UMASK,
+ ARG_LUKS_DISCARD,
+ ARG_JSON,
+ ARG_SETENV,
+ ARG_TIMEZONE,
+ ARG_LANGUAGE,
+ ARG_LOCKED,
+ ARG_SSH_AUTHORIZED_KEYS,
+ ARG_LOCATION,
+ ARG_ICON_NAME,
+ ARG_PASSWORD_HINT,
+ ARG_NICE,
+ ARG_RLIMIT,
+ ARG_NOT_BEFORE,
+ ARG_NOT_AFTER,
+ ARG_LUKS_CIPHER,
+ ARG_LUKS_CIPHER_MODE,
+ ARG_LUKS_VOLUME_KEY_SIZE,
+ ARG_NOSUID,
+ ARG_NODEV,
+ ARG_NOEXEC,
+ ARG_CIFS_DOMAIN,
+ ARG_CIFS_USER_NAME,
+ ARG_CIFS_SERVICE,
+ ARG_TASKS_MAX,
+ ARG_MEMORY_HIGH,
+ ARG_MEMORY_MAX,
+ ARG_CPU_WEIGHT,
+ ARG_IO_WEIGHT,
+ ARG_LUKS_PBKDF_TYPE,
+ ARG_LUKS_PBKDF_HASH_ALGORITHM,
+ ARG_LUKS_PBKDF_TIME_COST,
+ ARG_LUKS_PBKDF_MEMORY_COST,
+ ARG_LUKS_PBKDF_PARALLEL_THREADS,
+ ARG_RATE_LIMIT_INTERVAL,
+ ARG_RATE_LIMIT_BURST,
+ ARG_STOP_DELAY,
+ ARG_KILL_PROCESSES,
+ ARG_ENFORCE_PASSWORD_POLICY,
+ ARG_PASSWORD_CHANGE_NOW,
+ ARG_PASSWORD_CHANGE_MIN,
+ ARG_PASSWORD_CHANGE_MAX,
+ ARG_PASSWORD_CHANGE_WARN,
+ ARG_PASSWORD_CHANGE_INACTIVE,
+ ARG_EXPORT_FORMAT,
+ ARG_AUTO_LOGIN,
+ ARG_PKCS11_TOKEN_URI,
+ ARG_AND_RESIZE,
+ ARG_AND_CHANGE_PASSWORD,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "host", required_argument, NULL, 'H' },
+ { "machine", required_argument, NULL, 'M' },
+ { "identity", required_argument, NULL, 'I' },
+ { "real-name", required_argument, NULL, 'c' },
+ { "comment", required_argument, NULL, 'c' }, /* Compat alias to keep thing in sync with useradd(8) */
+ { "realm", required_argument, NULL, ARG_REALM },
+ { "email-address", required_argument, NULL, ARG_EMAIL_ADDRESS },
+ { "location", required_argument, NULL, ARG_LOCATION },
+ { "password-hint", required_argument, NULL, ARG_PASSWORD_HINT },
+ { "icon-name", required_argument, NULL, ARG_ICON_NAME },
+ { "home-dir", required_argument, NULL, 'd' }, /* Compatible with useradd(8) */
+ { "uid", required_argument, NULL, 'u' }, /* Compatible with useradd(8) */
+ { "member-of", required_argument, NULL, 'G' },
+ { "groups", required_argument, NULL, 'G' }, /* Compat alias to keep thing in sync with useradd(8) */
+ { "skel", required_argument, NULL, 'k' }, /* Compatible with useradd(8) */
+ { "shell", required_argument, NULL, 's' }, /* Compatible with useradd(8) */
+ { "setenv", required_argument, NULL, ARG_SETENV },
+ { "timezone", required_argument, NULL, ARG_TIMEZONE },
+ { "language", required_argument, NULL, ARG_LANGUAGE },
+ { "locked", required_argument, NULL, ARG_LOCKED },
+ { "not-before", required_argument, NULL, ARG_NOT_BEFORE },
+ { "not-after", required_argument, NULL, ARG_NOT_AFTER },
+ { "expiredate", required_argument, NULL, 'e' }, /* Compat alias to keep thing in sync with useradd(8) */
+ { "ssh-authorized-keys", required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS },
+ { "disk-size", required_argument, NULL, ARG_DISK_SIZE },
+ { "access-mode", required_argument, NULL, ARG_ACCESS_MODE },
+ { "umask", required_argument, NULL, ARG_UMASK },
+ { "nice", required_argument, NULL, ARG_NICE },
+ { "rlimit", required_argument, NULL, ARG_RLIMIT },
+ { "tasks-max", required_argument, NULL, ARG_TASKS_MAX },
+ { "memory-high", required_argument, NULL, ARG_MEMORY_HIGH },
+ { "memory-max", required_argument, NULL, ARG_MEMORY_MAX },
+ { "cpu-weight", required_argument, NULL, ARG_CPU_WEIGHT },
+ { "io-weight", required_argument, NULL, ARG_IO_WEIGHT },
+ { "storage", required_argument, NULL, ARG_STORAGE },
+ { "image-path", required_argument, NULL, ARG_IMAGE_PATH },
+ { "fs-type", required_argument, NULL, ARG_FS_TYPE },
+ { "luks-discard", required_argument, NULL, ARG_LUKS_DISCARD },
+ { "luks-cipher", required_argument, NULL, ARG_LUKS_CIPHER },
+ { "luks-cipher-mode", required_argument, NULL, ARG_LUKS_CIPHER_MODE },
+ { "luks-volume-key-size", required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE },
+ { "luks-pbkdf-type", required_argument, NULL, ARG_LUKS_PBKDF_TYPE },
+ { "luks-pbkdf-hash-algorithm", required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM },
+ { "luks-pbkdf-time-cost", required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST },
+ { "luks-pbkdf-memory-cost", required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST },
+ { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
+ { "nosuid", required_argument, NULL, ARG_NOSUID },
+ { "nodev", required_argument, NULL, ARG_NODEV },
+ { "noexec", required_argument, NULL, ARG_NOEXEC },
+ { "cifs-user-name", required_argument, NULL, ARG_CIFS_USER_NAME },
+ { "cifs-domain", required_argument, NULL, ARG_CIFS_DOMAIN },
+ { "cifs-service", required_argument, NULL, ARG_CIFS_SERVICE },
+ { "rate-limit-interval", required_argument, NULL, ARG_RATE_LIMIT_INTERVAL },
+ { "rate-limit-burst", required_argument, NULL, ARG_RATE_LIMIT_BURST },
+ { "stop-delay", required_argument, NULL, ARG_STOP_DELAY },
+ { "kill-processes", required_argument, NULL, ARG_KILL_PROCESSES },
+ { "enforce-password-policy", required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY },
+ { "password-change-now", required_argument, NULL, ARG_PASSWORD_CHANGE_NOW },
+ { "password-change-min", required_argument, NULL, ARG_PASSWORD_CHANGE_MIN },
+ { "password-change-max", required_argument, NULL, ARG_PASSWORD_CHANGE_MAX },
+ { "password-change-warn", required_argument, NULL, ARG_PASSWORD_CHANGE_WARN },
+ { "password-change-inactive", required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE },
+ { "auto-login", required_argument, NULL, ARG_AUTO_LOGIN },
+ { "json", required_argument, NULL, ARG_JSON },
+ { "export-format", required_argument, NULL, ARG_EXPORT_FORMAT },
+ { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
+ { "and-resize", required_argument, NULL, ARG_AND_RESIZE },
+ { "and-change-password", required_argument, NULL, ARG_AND_CHANGE_PASSWORD },
+ {}
+ };
+
+ int r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, "hH:M:I:c:d:u:k:s:e:G:jPE", options, NULL);
+ if (c < 0)
+ break;
+
+ switch (c) {
+
+ case 'h':
+ return help(0, NULL, NULL);
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_NO_PAGER:
+ arg_pager_flags |= PAGER_DISABLE;
+ break;
+
+ case ARG_NO_LEGEND:
+ arg_legend = false;
+ break;
+
+ case ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
+ case 'H':
+ arg_transport = BUS_TRANSPORT_REMOTE;
+ arg_host = optarg;
+ break;
+
+ case 'M':
+ arg_transport = BUS_TRANSPORT_MACHINE;
+ arg_host = optarg;
+ break;
+
+ case 'I':
+ arg_identity = optarg;
+ break;
+
+ case 'c':
+ if (isempty(optarg)) {
+ r = drop_from_identity("realName");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ if (!valid_gecos(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Real name '%s' not a valid GECOS field.", optarg);
+
+ r = json_variant_set_field_string(&arg_identity_extra, "realName", optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set realName field: %m");
+
+ break;
+
+ case 'd': {
+ _cleanup_free_ char *hd = NULL;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("homeDirectory");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_path_argument_and_warn(optarg, false, &hd);
+ if (r < 0)
+ return r;
+
+ if (!valid_home(hd))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home directory '%s' not valid.", hd);
+
+ r = json_variant_set_field_string(&arg_identity_extra, "homeDirectory", hd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set homeDirectory field: %m");
+
+ break;
+ }
+
+ case ARG_REALM:
+ if (isempty(optarg)) {
+ r = drop_from_identity("realm");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = dns_name_is_valid(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Realm '%s' is not a valid DNS domain: %m", optarg);
+
+ r = json_variant_set_field_string(&arg_identity_extra, "realm", optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set realm field: %m");
+ break;
+
+ case ARG_EMAIL_ADDRESS:
+ case ARG_LOCATION:
+ case ARG_ICON_NAME:
+ case ARG_CIFS_USER_NAME:
+ case ARG_CIFS_DOMAIN:
+ case ARG_CIFS_SERVICE: {
+
+ const char *field =
+ c == ARG_EMAIL_ADDRESS ? "emailAddress" :
+ c == ARG_LOCATION ? "location" :
+ c == ARG_ICON_NAME ? "iconName" :
+ c == ARG_CIFS_USER_NAME ? "cifsUserName" :
+ c == ARG_CIFS_DOMAIN ? "cifsDomain" :
+ c == ARG_CIFS_SERVICE ? "cifsService" :
+ NULL;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = json_variant_set_field_string(&arg_identity_extra, field, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+
+ break;
+ }
+
+ case ARG_PASSWORD_HINT:
+ if (isempty(optarg)) {
+ r = drop_from_identity("passwordHint");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = json_variant_set_field_string(&arg_identity_extra_privileged, "passwordHint", optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set passwordHint field: %m");
+
+ string_erase(optarg);
+ break;
+
+ case ARG_NICE: {
+ int nc;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("niceLevel");
+ if (r < 0)
+ return r;
+ break;
+ }
+
+ r = parse_nice(optarg, &nc);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse nice level: %s", optarg);
+
+ r = json_variant_set_field_integer(&arg_identity_extra, "niceLevel", nc);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set niceLevel field: %m");
+
+ break;
+ }
+
+ case ARG_RLIMIT: {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *jcur = NULL, *jmax = NULL;
+ _cleanup_free_ char *field = NULL, *t = NULL;
+ const char *eq;
+ struct rlimit rl;
+ int l;
+
+ if (isempty(optarg)) {
+ /* Remove all resource limits */
+
+ r = drop_from_identity("resourceLimits");
+ if (r < 0)
+ return r;
+
+ arg_identity_filter_rlimits = strv_free(arg_identity_filter_rlimits);
+ arg_identity_extra_rlimits = json_variant_unref(arg_identity_extra_rlimits);
+ break;
+ }
+
+ eq = strchr(optarg, '=');
+ if (!eq)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't parse resource limit assignment: %s", optarg);
+
+ field = strndup(optarg, eq - optarg);
+ if (!field)
+ return log_oom();
+
+ l = rlimit_from_string_harder(field);
+ if (l < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown resource limit type: %s", field);
+
+ if (isempty(eq + 1)) {
+ /* Remove only the specific rlimit */
+
+ r = strv_extend(&arg_identity_filter_rlimits, rlimit_to_string(l));
+ if (r < 0)
+ return r;
+
+ r = json_variant_filter(&arg_identity_extra_rlimits, STRV_MAKE(field));
+ if (r < 0)
+ return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+ break;
+ }
+
+ r = rlimit_parse(l, eq + 1, &rl);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse resource limit value: %s", eq + 1);
+
+ r = rl.rlim_cur == RLIM_INFINITY ? json_variant_new_null(&jcur) : json_variant_new_unsigned(&jcur, rl.rlim_cur);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate current integer: %m");
+
+ r = rl.rlim_max == RLIM_INFINITY ? json_variant_new_null(&jmax) : json_variant_new_unsigned(&jmax, rl.rlim_max);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate maximum integer: %m");
+
+ r = json_build(&v,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
+ JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build resource limit: %m");
+
+ t = strjoin("RLIMIT_", rlimit_to_string(l));
+ if (!t)
+ return log_oom();
+
+ r = json_variant_set_field(&arg_identity_extra_rlimits, t, v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", rlimit_to_string(l));
+
+ break;
+ }
+
+ case 'u': {
+ uid_t uid;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("uid");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_uid(optarg, &uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID '%s'.", optarg);
+
+ if (uid_is_system(uid))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in system range, refusing.", uid);
+ if (uid_is_dynamic(uid))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in dynamic range, refusing.", uid);
+ if (uid == UID_NOBODY)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is nobody UID, refusing.", uid);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, "uid", uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set realm field: %m");
+
+ break;
+ }
+
+ case 'k':
+ case ARG_IMAGE_PATH: {
+ const char *field = c == 'k' ? "skeletonDirectory" : "imagePath";
+ _cleanup_free_ char *v = NULL;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_path_argument_and_warn(optarg, false, &v);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field_string(&arg_identity_extra_this_machine, field, v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", v);
+
+ break;
+ }
+
+ case 's':
+ if (isempty(optarg)) {
+ r = drop_from_identity("shell");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ if (!valid_shell(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Shell '%s' not valid.", optarg);
+
+ r = json_variant_set_field_string(&arg_identity_extra, "shell", optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set shell field: %m");
+
+ break;
+
+ case ARG_SETENV: {
+ _cleanup_free_ char **l = NULL, **k = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *ne = NULL;
+ JsonVariant *e;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("environment");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ if (!env_assignment_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Environment assignment '%s' not valid.", optarg);
+
+ e = json_variant_by_key(arg_identity_extra, "environment");
+ if (e) {
+ r = json_variant_strv(e, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse JSON environment field: %m");
+ }
+
+ k = strv_env_set(l, optarg);
+ if (!k)
+ return log_oom();
+
+ strv_sort(k);
+
+ r = json_variant_new_array_strv(&ne, k);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate environment list JSON: %m");
+
+ r = json_variant_set_field(&arg_identity_extra, "environment", ne);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set environent list: %m");
+
+ break;
+ }
+
+ case ARG_TIMEZONE:
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("timeZone");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ if (!timezone_is_valid(optarg, LOG_DEBUG))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' is not valid.", optarg);
+
+ r = json_variant_set_field_string(&arg_identity_extra, "timeZone", optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set timezone field: %m");
+
+ break;
+
+ case ARG_LANGUAGE:
+ if (isempty(optarg)) {
+ r = drop_from_identity("language");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ if (!locale_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale '%s' is not valid.", optarg);
+
+ r = json_variant_set_field_string(&arg_identity_extra, "preferredLanguage", optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set preferredLanguage field: %m");
+
+ break;
+
+ case ARG_NOSUID:
+ case ARG_NODEV:
+ case ARG_NOEXEC:
+ case ARG_LOCKED:
+ case ARG_KILL_PROCESSES:
+ case ARG_ENFORCE_PASSWORD_POLICY:
+ case ARG_AUTO_LOGIN:
+ case ARG_PASSWORD_CHANGE_NOW: {
+ const char *field =
+ c == ARG_LOCKED ? "locked" :
+ c == ARG_NOSUID ? "mountNoSuid" :
+ c == ARG_NODEV ? "mountNoDevices" :
+ c == ARG_NOEXEC ? "mountNoExecute" :
+ c == ARG_KILL_PROCESSES ? "killProcesses" :
+ c == ARG_ENFORCE_PASSWORD_POLICY ? "enforcePasswordPolicy" :
+ c == ARG_AUTO_LOGIN ? "autoLogin" :
+ c == ARG_PASSWORD_CHANGE_NOW ? "passwordChangeNow" :
+ NULL;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s boolean: %m", field);
+
+ r = json_variant_set_field_boolean(&arg_identity_extra, field, r > 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+
+ break;
+ }
+
+ case 'P':
+ r = json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
+
+ break;
+
+ case ARG_DISK_SIZE:
+ if (isempty(optarg)) {
+ r = drop_from_identity("diskSize");
+ if (r < 0)
+ return r;
+
+ r = drop_from_identity("diskSizeRelative");
+ if (r < 0)
+ return r;
+
+ arg_disk_size = arg_disk_size_relative = UINT64_MAX;
+ break;
+ }
+
+ r = parse_permille(optarg);
+ if (r < 0) {
+ r = parse_size(optarg, 1024, &arg_disk_size);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size '%s' not valid.", optarg);
+
+ r = drop_from_identity("diskSizeRelative");
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSize", arg_disk_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set diskSize field: %m");
+
+ arg_disk_size_relative = UINT64_MAX;
+ } else {
+ /* Normalize to UINT32_MAX == 100% */
+ arg_disk_size_relative = (uint64_t) r * UINT32_MAX / 1000U;
+
+ r = drop_from_identity("diskSize");
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSizeRelative", arg_disk_size_relative);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set diskSizeRelative field: %m");
+
+ arg_disk_size = UINT64_MAX;
+ }
+
+ break;
+
+ case ARG_ACCESS_MODE: {
+ mode_t mode;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("accessMode");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_mode(optarg, &mode);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Access mode '%s' not valid.", optarg);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, "accessMode", mode);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set access mode field: %m");
+
+ break;
+ }
+
+ case ARG_LUKS_DISCARD:
+ if (isempty(optarg)) {
+ r = drop_from_identity("luksDiscard");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --luks-discard= parameter: %s", optarg);
+
+ r = json_variant_set_field_boolean(&arg_identity_extra, "luksDiscard", r);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set discard field: %m");
+
+ break;
+
+ case ARG_LUKS_VOLUME_KEY_SIZE:
+ case ARG_LUKS_PBKDF_PARALLEL_THREADS:
+ case ARG_RATE_LIMIT_BURST: {
+ const char *field =
+ c == ARG_LUKS_VOLUME_KEY_SIZE ? "luksVolumeKeySize" :
+ c == ARG_LUKS_PBKDF_PARALLEL_THREADS ? "luksPbkdfParallelThreads" :
+ c == ARG_RATE_LIMIT_BURST ? "rateLimitBurst" : NULL;
+ unsigned n;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+ }
+
+ r = safe_atou(optarg, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+
+ break;
+ }
+
+ case ARG_UMASK: {
+ mode_t m;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("umask");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_mode(optarg, &m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse umask: %m");
+
+ r = json_variant_set_field_integer(&arg_identity_extra, "umask", m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set umask field: %m");
+
+ break;
+ }
+
+ case ARG_SSH_AUTHORIZED_KEYS: {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(strv_freep) char **l = NULL, **add = NULL;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("sshAuthorizedKeys");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ if (optarg[0] == '@') {
+ _cleanup_fclose_ FILE *f = NULL;
+
+ /* If prefixed with '@' read from a file */
+
+ f = fopen(optarg+1, "re");
+ if (!f)
+ return log_error_errno(errno, "Failed to open '%s': %m", optarg+1);
+
+ for (;;) {
+ _cleanup_free_ char *line = NULL;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_error_errno(r, "Faile dto read from '%s': %m", optarg+1);
+ if (r == 0)
+ break;
+
+ if (isempty(line))
+ continue;
+
+ if (line[0] == '#')
+ continue;
+
+ r = strv_consume(&add, TAKE_PTR(line));
+ if (r < 0)
+ return log_oom();
+ }
+ } else {
+ /* Otherwise, assume it's a literal key. Let's do some superficial checks
+ * before accept it though. */
+
+ if (string_has_cc(optarg, NULL))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Authorized key contains control characters, refusing.");
+ if (optarg[0] == '#')
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?");
+
+ add = strv_new(optarg);
+ if (!add)
+ return log_oom();
+ }
+
+ v = json_variant_ref(json_variant_by_key(arg_identity_extra_privileged, "sshAuthorizedKeys"));
+ if (v) {
+ r = json_variant_strv(v, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse SSH authorized keys list: %m");
+ }
+
+ r = strv_extend_strv(&l, add, true);
+ if (r < 0)
+ return log_oom();
+
+ v = json_variant_unref(v);
+
+ r = json_variant_new_array_strv(&v, l);
+ if (r < 0)
+ return log_oom();
+
+ r = json_variant_set_field(&arg_identity_extra_privileged, "sshAuthorizedKeys", v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set authorized keys: %m");
+
+ break;
+ }
+
+ case ARG_NOT_BEFORE:
+ case ARG_NOT_AFTER:
+ case 'e': {
+ const char *field;
+ usec_t n;
+
+ field = c == ARG_NOT_BEFORE ? "notBeforeUSec" :
+ IN_SET(c, ARG_NOT_AFTER, 'e') ? "notAfterUSec" : NULL;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ /* Note the minor discrepancy regarding -e parsing here: we support that for compat
+ * reasons, and in the original useradd(8) implementation it accepts dates in the
+ * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
+ * with greater precision. */
+ r = parse_timestamp(optarg, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s parameter: %m", field);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+ break;
+ }
+
+ case ARG_PASSWORD_CHANGE_MIN:
+ case ARG_PASSWORD_CHANGE_MAX:
+ case ARG_PASSWORD_CHANGE_WARN:
+ case ARG_PASSWORD_CHANGE_INACTIVE: {
+ const char *field;
+ usec_t n;
+
+ field = c == ARG_PASSWORD_CHANGE_MIN ? "passwordChangeMinUSec" :
+ c == ARG_PASSWORD_CHANGE_MAX ? "passwordChangeMaxUSec" :
+ c == ARG_PASSWORD_CHANGE_WARN ? "passwordChangeWarnUSec" :
+ c == ARG_PASSWORD_CHANGE_INACTIVE ? "passwordChangeInactiveUSec" :
+ NULL;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_sec(optarg, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s parameter: %m", field);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+ break;
+ }
+
+ case ARG_STORAGE:
+ case ARG_FS_TYPE:
+ case ARG_LUKS_CIPHER:
+ case ARG_LUKS_CIPHER_MODE:
+ case ARG_LUKS_PBKDF_TYPE:
+ case ARG_LUKS_PBKDF_HASH_ALGORITHM: {
+
+ const char *field =
+ c == ARG_STORAGE ? "storage" :
+ c == ARG_FS_TYPE ? "fileSystemType" :
+ c == ARG_LUKS_CIPHER ? "luksCipher" :
+ c == ARG_LUKS_CIPHER_MODE ? "luksCipherMode" :
+ c == ARG_LUKS_PBKDF_TYPE ? "luksPbkdfType" :
+ c == ARG_LUKS_PBKDF_HASH_ALGORITHM ? "luksPbkdfHashAlgorithm" : NULL;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ if (!string_is_safe(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parameter for %s field not valid: %s", field, optarg);
+
+ r = json_variant_set_field_string(
+ IN_SET(c, ARG_STORAGE, ARG_FS_TYPE) ?
+ &arg_identity_extra_this_machine :
+ &arg_identity_extra, field, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+
+ break;
+ }
+
+ case ARG_LUKS_PBKDF_TIME_COST:
+ case ARG_RATE_LIMIT_INTERVAL:
+ case ARG_STOP_DELAY: {
+ const char *field =
+ c == ARG_LUKS_PBKDF_TIME_COST ? "luksPbkdfTimeCostUSec" :
+ c == ARG_RATE_LIMIT_INTERVAL ? "rateLimitIntervalUSec" :
+ c == ARG_STOP_DELAY ? "stopDelayUSec" :
+ NULL;
+ usec_t t;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ r = parse_sec(optarg, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s field: %s", field, optarg);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, field, t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+
+ break;
+ }
+
+ case 'G': {
+ const char *p = optarg;
+
+ if (isempty(p)) {
+ r = drop_from_identity("memberOf");
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ for (;;) {
+ _cleanup_(json_variant_unrefp) JsonVariant *mo = NULL;
+ _cleanup_strv_free_ char **list = NULL;
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&p, &word, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse group list: %m");
+ if (r == 0)
+ break;
+
+ if (!valid_user_group_name(word))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid group name %s.", word);
+
+ mo = json_variant_ref(json_variant_by_key(arg_identity_extra, "memberOf"));
+
+ r = json_variant_strv(mo, &list);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse group list: %m");
+
+ r = strv_extend(&list, word);
+ if (r < 0)
+ return log_oom();
+
+ strv_sort(list);
+ strv_uniq(list);
+
+ mo = json_variant_unref(mo);
+ r = json_variant_new_array_strv(&mo, list);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create group list JSON: %m");
+
+ r = json_variant_set_field(&arg_identity_extra, "memberOf", mo);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update group list: %m");
+ }
+
+ break;
+ }
+
+ case ARG_TASKS_MAX: {
+ uint64_t u;
+
+ if (isempty(optarg)) {
+ r = drop_from_identity("tasksMax");
+ if (r < 0)
+ return r;
+ break;
+ }
+
+ r = safe_atou64(optarg, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --tasks-max= parameter: %s", optarg);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, "tasksMax", u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set tasksMax field: %m");
+
+ break;
+ }
+
+ case ARG_MEMORY_MAX:
+ case ARG_MEMORY_HIGH:
+ case ARG_LUKS_PBKDF_MEMORY_COST: {
+ const char *field =
+ c == ARG_MEMORY_MAX ? "memoryMax" :
+ c == ARG_MEMORY_HIGH ? "memoryHigh" :
+ c == ARG_LUKS_PBKDF_MEMORY_COST ? "luksPbkdfMemoryCost" : NULL;
+
+ uint64_t u;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+ break;
+ }
+
+ r = parse_size(optarg, 1024, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, field, u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+
+ break;
+ }
+
+ case ARG_CPU_WEIGHT:
+ case ARG_IO_WEIGHT: {
+ const char *field = c == ARG_CPU_WEIGHT ? "cpuWeight" :
+ c == ARG_IO_WEIGHT ? "ioWeight" : NULL;
+ uint64_t u;
+
+ assert(field);
+
+ if (isempty(optarg)) {
+ r = drop_from_identity(field);
+ if (r < 0)
+ return r;
+ break;
+ }
+
+ r = safe_atou64(optarg, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg);
+
+ if (!CGROUP_WEIGHT_IS_OK(u))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weight %" PRIu64 " is out of valid weight range.", u);
+
+ r = json_variant_set_field_unsigned(&arg_identity_extra, field, u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set %s field: %m", field);
+
+ break;
+ }
+
+ case ARG_PKCS11_TOKEN_URI: {
+ const char *p;
+
+ /* If --pkcs11-token-uri= is specified we always drop everything old */
+ FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") {
+ r = drop_from_identity(p);
+ if (r < 0)
+ return r;
+ }
+
+ if (isempty(optarg)) {
+ arg_pkcs11_token_uri = strv_free(arg_pkcs11_token_uri);
+ break;
+ }
+
+ if (!pkcs11_uri_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
+
+ r = strv_extend(&arg_pkcs11_token_uri, optarg);
+ if (r < 0)
+ return r;
+
+ strv_uniq(arg_pkcs11_token_uri);
+ break;
+ }
+
+ case 'j':
+ arg_json = true;
+ arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
+ break;
+
+ case ARG_JSON:
+ if (streq(optarg, "pretty")) {
+ arg_json = true;
+ arg_json_format_flags = JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO;
+ } else if (streq(optarg, "short")) {
+ arg_json = true;
+ arg_json_format_flags = JSON_FORMAT_NEWLINE;
+ } else if (streq(optarg, "off")) {
+ arg_json = false;
+ arg_json_format_flags = 0;
+ } else if (streq(optarg, "help")) {
+ puts("pretty\n"
+ "short\n"
+ "off");
+ return 0;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown argument to --json=: %s", optarg);
+
+ break;
+
+ case 'E':
+ if (arg_export_format == EXPORT_FORMAT_FULL)
+ arg_export_format = EXPORT_FORMAT_STRIPPED;
+ else if (arg_export_format == EXPORT_FORMAT_STRIPPED)
+ arg_export_format = EXPORT_FORMAT_MINIMAL;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specifying -E more than twice is not supported.");
+
+ arg_json = true;
+ if (arg_json_format_flags == 0)
+ arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
+ break;
+
+ case ARG_EXPORT_FORMAT:
+ if (streq(optarg, "full"))
+ arg_export_format = EXPORT_FORMAT_FULL;
+ else if (streq(optarg, "stripped"))
+ arg_export_format = EXPORT_FORMAT_STRIPPED;
+ else if (streq(optarg, "minimal"))
+ arg_export_format = EXPORT_FORMAT_MINIMAL;
+ else if (streq(optarg, "help")) {
+ puts("full\n"
+ "stripped\n"
+ "minimal");
+ return 0;
+ }
+
+ break;
+
+ case ARG_AND_RESIZE:
+ arg_and_resize = true;
+ break;
+
+ case ARG_AND_CHANGE_PASSWORD:
+ arg_and_change_password = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+ }
+
+ if (!strv_isempty(arg_pkcs11_token_uri))
+ arg_and_change_password = true;
+
+ if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
+ arg_and_resize = true;
+
+ return 1;
+}
+
+static int run(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "list", VERB_ANY, 1, VERB_DEFAULT, list_homes },
+ { "activate", 2, VERB_ANY, 0, activate_home },
+ { "deactivate", 2, VERB_ANY, 0, deactivate_home },
+ { "inspect", VERB_ANY, VERB_ANY, 0, inspect_home },
+ { "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_home },
+ { "create", VERB_ANY, 2, 0, create_home },
+ { "remove", 2, VERB_ANY, 0, remove_home },
+ { "update", VERB_ANY, 2, 0, update_home },
+ { "passwd", VERB_ANY, 2, 0, passwd_home },
+ { "resize", 2, 3, 0, resize_home },
+ { "lock", 2, VERB_ANY, 0, lock_home },
+ { "unlock", 2, VERB_ANY, 0, unlock_home },
+ { "with", 2, VERB_ANY, 0, with_home },
+ { "lock-all", VERB_ANY, 1, 0, lock_all_homes },
+ {}
+ };
+
+ int r;
+
+ log_show_color(true);
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+#include "homed-bus.h"
+#include "strv.h"
+
+int bus_message_read_secret(sd_bus_message *m, UserRecord **ret, sd_bus_error *error) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *full = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ unsigned line = 0, column = 0;
+ const char *json;
+ int r;
+
+ assert(ret);
+
+ r = sd_bus_message_read(m, "s", &json);
+ if (r < 0)
+ return r;
+
+ r = json_parse(json, JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON secret record at %u:%u: %m", line, column);
+
+ r = json_build(&full, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("secret", JSON_BUILD_VARIANT(v))));
+ if (r < 0)
+ return r;
+
+ hr = user_record_new();
+ if (!hr)
+ return -ENOMEM;
+
+ r = user_record_load(hr, full, USER_RECORD_REQUIRE_SECRET);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(hr);
+ return 0;
+}
+
+int bus_message_read_home_record(sd_bus_message *m, UserRecordLoadFlags flags, UserRecord **ret, sd_bus_error *error) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ unsigned line = 0, column = 0;
+ const char *json;
+ int r;
+
+ assert(ret);
+
+ r = sd_bus_message_read(m, "s", &json);
+ if (r < 0)
+ return r;
+
+ r = json_parse(json, JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON identity record at %u:%u: %m", line, column);
+
+ hr = user_record_new();
+ if (!hr)
+ return -ENOMEM;
+
+ r = user_record_load(hr, v, flags);
+ if (r < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "JSON data is not a valid identity record");
+
+ *ret = TAKE_PTR(hr);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "user-record.h"
+#include "json.h"
+
+int bus_message_read_secret(sd_bus_message *m, UserRecord **ret, sd_bus_error *error);
+int bus_message_read_home_record(sd_bus_message *m, UserRecordLoadFlags flags, UserRecord **ret, sd_bus_error *error);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/capability.h>
+
+#include "bus-common-errors.h"
+#include "bus-polkit.h"
+#include "fd-util.h"
+#include "homed-bus.h"
+#include "homed-home-bus.h"
+#include "homed-home.h"
+#include "strv.h"
+#include "user-record-util.h"
+#include "user-util.h"
+
+static int property_get_unix_record(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Home *h = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(h);
+
+ return sd_bus_message_append(
+ reply, "(suusss)",
+ h->user_name,
+ (uint32_t) h->uid,
+ h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+ h->record ? user_record_real_name(h->record) : NULL,
+ h->record ? user_record_home_directory(h->record) : NULL,
+ h->record ? user_record_shell(h->record) : NULL);
+}
+
+static int property_get_state(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Home *h = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(h);
+
+ return sd_bus_message_append(reply, "s", home_state_to_string(home_get_state(h)));
+}
+
+int bus_home_client_is_trusted(Home *h, sd_bus_message *message) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ uid_t euid;
+ int r;
+
+ assert(h);
+
+ if (!message)
+ return -EINVAL;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_euid(creds, &euid);
+ if (r < 0)
+ return r;
+
+ return euid == 0 || h->uid == euid;
+}
+
+int bus_home_get_record_json(
+ Home *h,
+ sd_bus_message *message,
+ char **ret,
+ bool *ret_incomplete) {
+
+ _cleanup_(user_record_unrefp) UserRecord *augmented = NULL;
+ UserRecordLoadFlags flags;
+ int r, trusted;
+
+ assert(h);
+ assert(ret);
+
+ trusted = bus_home_client_is_trusted(h, message);
+ if (trusted < 0) {
+ log_warning_errno(trusted, "Failed to determine whether client is trusted, assuming untrusted.");
+ trusted = false;
+ }
+
+ flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+ if (trusted)
+ flags |= USER_RECORD_ALLOW_PRIVILEGED;
+ else
+ flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+ r = home_augment_status(h, flags, &augmented);
+ if (r < 0)
+ return r;
+
+ r = json_variant_format(augmented->json, 0, ret);
+ if (r < 0)
+ return r;
+
+ if (ret_incomplete)
+ *ret_incomplete = augmented->incomplete;
+
+ return 0;
+}
+
+static int property_get_user_record(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *json = NULL;
+ Home *h = userdata;
+ bool incomplete;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(h);
+
+ r = bus_home_get_record_json(h, sd_bus_get_current_message(bus), &json, &incomplete);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_append(reply, "(sb)", json, incomplete);
+}
+
+int bus_home_method_activate(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_secret(message, &secret, error);
+ if (r < 0)
+ return r;
+
+ r = home_activate(h, secret, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ /* The operation is now in process, keep track of this message so that we can later reply to it. */
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_deactivate(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = home_deactivate(h, false, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_unregister(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.remove-home",
+ NULL,
+ true,
+ UID_INVALID,
+ &h->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = home_unregister(h, error);
+ if (r < 0)
+ return r;
+
+ assert(r > 0);
+
+ /* Note that home_unregister() destroyed 'h' here, so no more accesses */
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_home_method_realize(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_secret(message, &secret, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.create-home",
+ NULL,
+ true,
+ UID_INVALID,
+ &h->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = home_create(h, secret, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ h->unregister_on_failure = false;
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_remove(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.remove-home",
+ NULL,
+ true,
+ UID_INVALID,
+ &h->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = home_remove(h, error);
+ if (r < 0)
+ return r;
+ if (r > 0) /* Done already. Note that home_remove() destroyed 'h' here, so no more accesses */
+ return sd_bus_reply_method_return(message, NULL);
+
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_fixate(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_secret(message, &secret, error);
+ if (r < 0)
+ return r;
+
+ r = home_fixate(h, secret, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_authenticate(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_secret(message, &secret, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.authenticate-home",
+ NULL,
+ true,
+ h->uid,
+ &h->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = home_authenticate(h, secret, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_update_record(Home *h, sd_bus_message *message, UserRecord *hr, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+ assert(message);
+ assert(hr);
+
+ r = user_record_is_supported(hr, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.update-home",
+ NULL,
+ true,
+ UID_INVALID,
+ &h->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = home_update(h, hr, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_update(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_REQUIRE_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
+ if (r < 0)
+ return r;
+
+ return bus_home_method_update_record(h, message, hr, error);
+}
+
+int bus_home_method_resize(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ Home *h = userdata;
+ uint64_t sz;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = sd_bus_message_read(message, "t", &sz);
+ if (r < 0)
+ return r;
+
+ r = bus_message_read_secret(message, &secret, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.resize-home",
+ NULL,
+ true,
+ UID_INVALID,
+ &h->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = home_resize(h, sz, secret, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_change_password(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *new_secret = NULL, *old_secret = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_secret(message, &new_secret, error);
+ if (r < 0)
+ return r;
+
+ r = bus_message_read_secret(message, &old_secret, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.passwd-home",
+ NULL,
+ true,
+ h->uid,
+ &h->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = home_passwd(h, new_secret, old_secret, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_lock(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = home_lock(h, error);
+ if (r < 0)
+ return r;
+ if (r > 0) /* Done */
+ return sd_bus_reply_method_return(message, NULL);
+
+ /* The operation is now in process, keep track of this message so that we can later reply to it. */
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_unlock(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_secret(message, &secret, error);
+ if (r < 0)
+ return r;
+
+ r = home_unlock(h, secret, error);
+ if (r < 0)
+ return r;
+
+ assert(r == 0);
+ assert(!h->current_operation);
+
+ /* The operation is now in process, keep track of this message so that we can later reply to it. */
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_acquire(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ _cleanup_(operation_unrefp) Operation *o = NULL;
+ _cleanup_close_ int fd = -1;
+ int r, please_suspend;
+ Home *h = userdata;
+
+ assert(message);
+ assert(h);
+
+ r = bus_message_read_secret(message, &secret, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "b", &please_suspend);
+ if (r < 0)
+ return r;
+
+ /* This operation might not be something we can executed immediately, hence queue it */
+ fd = home_create_fifo(h, please_suspend);
+ if (fd < 0)
+ return sd_bus_reply_method_errnof(message, fd, "Failed to allocate fifo for %s: %m", h->user_name);
+
+ o = operation_new(OPERATION_ACQUIRE, message);
+ if (!o)
+ return -ENOMEM;
+
+ o->secret = TAKE_PTR(secret);
+ o->send_fd = TAKE_FD(fd);
+
+ r = home_schedule_operation(h, o, error);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+int bus_home_method_ref(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_close_ int fd = -1;
+ Home *h = userdata;
+ HomeState state;
+ int please_suspend, r;
+
+ assert(message);
+ assert(h);
+
+ r = sd_bus_message_read(message, "b", &please_suspend);
+ if (r < 0)
+ return r;
+
+ state = home_get_state(h);
+ switch (state) {
+ case HOME_ABSENT:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ case HOME_UNFIXATED:
+ case HOME_INACTIVE:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ default:
+ if (HOME_STATE_IS_ACTIVE(state))
+ break;
+
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ fd = home_create_fifo(h, please_suspend);
+ if (fd < 0)
+ return sd_bus_reply_method_errnof(message, fd, "Failed to allocate fifo for %s: %m", h->user_name);
+
+ return sd_bus_reply_method_return(message, "h", fd);
+}
+
+int bus_home_method_release(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(operation_unrefp) Operation *o = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(message);
+ assert(h);
+
+ o = operation_new(OPERATION_RELEASE, message);
+ if (!o)
+ return -ENOMEM;
+
+ r = home_schedule_operation(h, o, error);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+/* We map a uid_t as uint32_t bus property, let's ensure this is safe. */
+assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+
+const sd_bus_vtable home_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_PROPERTY("UserName", "s", NULL, offsetof(Home, user_name), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Home, uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("UnixRecord", "(suusss)", property_get_unix_record, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
+ SD_BUS_PROPERTY("UserRecord", "(sb)", property_get_user_record, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Activate", "s", NULL, bus_home_method_activate, SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Deactivate", NULL, NULL, bus_home_method_deactivate, 0),
+ SD_BUS_METHOD("Unregister", NULL, NULL, bus_home_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Realize", "s", NULL, bus_home_method_realize, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Remove", NULL, NULL, bus_home_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Fixate", "s", NULL, bus_home_method_fixate, SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Authenticate", "s", NULL, bus_home_method_authenticate, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Update", "s", NULL, bus_home_method_update, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Resize", "ts", NULL, bus_home_method_resize, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("ChangePassword", "ss", NULL, bus_home_method_change_password, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Lock", NULL, NULL, bus_home_method_lock, 0),
+ SD_BUS_METHOD("Unlock", "s", NULL, bus_home_method_unlock, SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Acquire", "sb", "h", bus_home_method_acquire, SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("Ref", "b", "h", bus_home_method_ref, 0),
+ SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0),
+ SD_BUS_VTABLE_END
+};
+
+int bus_home_path(Home *h, char **ret) {
+ assert(ret);
+
+ return sd_bus_path_encode("/org/freedesktop/home1/home", h->user_name, ret);
+}
+
+int bus_home_object_find(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ void *userdata,
+ void **found,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *e = NULL;
+ Manager *m = userdata;
+ uid_t uid;
+ Home *h;
+ int r;
+
+ r = sd_bus_path_decode(path, "/org/freedesktop/home1/home", &e);
+ if (r <= 0)
+ return 0;
+
+ if (parse_uid(e, &uid) >= 0)
+ h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+ else
+ h = hashmap_get(m->homes_by_name, e);
+ if (!h)
+ return 0;
+
+ *found = h;
+ return 1;
+}
+
+int bus_home_node_enumerator(
+ sd_bus *bus,
+ const char *path,
+ void *userdata,
+ char ***nodes,
+ sd_bus_error *error) {
+
+ _cleanup_strv_free_ char **l = NULL;
+ Manager *m = userdata;
+ size_t k = 0;
+ Iterator i;
+ Home *h;
+ int r;
+
+ assert(nodes);
+
+ l = new0(char*, hashmap_size(m->homes_by_uid) + 1);
+ if (!l)
+ return -ENOMEM;
+
+ HASHMAP_FOREACH(h, m->homes_by_uid, i) {
+ r = bus_home_path(h, l + k);
+ if (r < 0)
+ return r;
+ }
+
+ *nodes = TAKE_PTR(l);
+ return 1;
+}
+
+static int on_deferred_change(sd_event_source *s, void *userdata) {
+ _cleanup_free_ char *path = NULL;
+ Home *h = userdata;
+ int r;
+
+ assert(h);
+
+ h->deferred_change_event_source = sd_event_source_unref(h->deferred_change_event_source);
+
+ r = bus_home_path(h, &path);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to generate home bus path, ignoring: %m");
+ return 0;
+ }
+
+ if (h->announced)
+ r = sd_bus_emit_properties_changed_strv(h->manager->bus, path, "org.freedesktop.home1.Home", NULL);
+ else
+ r = sd_bus_emit_object_added(h->manager->bus, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to send home change event, ignoring: %m");
+ else
+ h->announced = true;
+
+ return 0;
+}
+
+int bus_home_emit_change(Home *h) {
+ int r;
+
+ assert(h);
+
+ if (h->deferred_change_event_source)
+ return 1;
+
+ if (!h->manager->event)
+ return 0;
+
+ if (IN_SET(sd_event_get_state(h->manager->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+ return 0;
+
+ r = sd_event_add_defer(h->manager->event, &h->deferred_change_event_source, on_deferred_change, h);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate deferred change event source: %m");
+
+ r = sd_event_source_set_priority(h->deferred_change_event_source, SD_EVENT_PRIORITY_IDLE+5);
+ if (r < 0)
+ log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+ (void) sd_event_source_set_description(h->deferred_change_event_source, "deferred-change-event");
+ return 1;
+}
+
+int bus_home_emit_remove(Home *h) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(h);
+
+ if (!h->announced)
+ return 0;
+
+ r = bus_home_path(h, &path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_emit_object_removed(h->manager->bus, path);
+ if (r < 0)
+ return r;
+
+ h->announced = false;
+ return 1;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "homed-home.h"
+
+int bus_home_client_is_trusted(Home *h, sd_bus_message *message);
+int bus_home_get_record_json(Home *h, sd_bus_message *message, char **ret, bool *ret_incomplete);
+
+int bus_home_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_deactivate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_realize(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_fixate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_authenticate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_update(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_update_record(Home *home, sd_bus_message *message, UserRecord *hr, sd_bus_error *error);
+int bus_home_method_resize(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_change_password(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_unlock(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_acquire(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
+
+extern const sd_bus_vtable home_vtable[];
+
+int bus_home_path(Home *h, char **ret);
+
+int bus_home_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+int bus_home_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+
+int bus_home_emit_change(Home *h);
+int bus_home_emit_remove(Home *h);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if HAVE_LINUX_MEMFD_H
+#include <linux/memfd.h>
+#endif
+
+#include <sys/mman.h>
+#include <sys/quota.h>
+#include <sys/vfs.h>
+
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "bus-common-errors.h"
+#include "env-util.h"
+#include "errno-list.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "home-util.h"
+#include "homed-home-bus.h"
+#include "homed-home.h"
+#include "missing_syscall.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "pwquality-util.h"
+#include "quota-util.h"
+#include "resize-fs.h"
+#include "set.h"
+#include "signal-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "strv.h"
+#include "user-record-sign.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+
+#define HOME_USERS_MAX 500
+#define PENDING_OPERATIONS_MAX 100
+
+assert_cc(HOME_UID_MIN <= HOME_UID_MAX);
+assert_cc(HOME_USERS_MAX <= (HOME_UID_MAX - HOME_UID_MIN + 1));
+
+static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord *secret);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(operation_hash_ops, void, trivial_hash_func, trivial_compare_func, Operation, operation_unref);
+
+static int suitable_home_record(UserRecord *hr) {
+ int r;
+
+ assert(hr);
+
+ if (!hr->user_name)
+ return -EUNATCH;
+
+ /* We are a bit more restrictive with what we accept as homed-managed user than what we accept in
+ * home records in general. Let's enforce the stricter rule here. */
+ if (!suitable_user_name(hr->user_name))
+ return -EINVAL;
+ if (!uid_is_valid(hr->uid))
+ return -EINVAL;
+
+ /* Insist we are outside of the dynamic and system range */
+ if (uid_is_system(hr->uid) || gid_is_system(user_record_gid(hr)) ||
+ uid_is_dynamic(hr->uid) || gid_is_dynamic(user_record_gid(hr)))
+ return -EADDRNOTAVAIL;
+
+ /* Insist that GID and UID match */
+ if (user_record_gid(hr) != (gid_t) hr->uid)
+ return -EBADSLT;
+
+ /* Similar for the realm */
+ if (hr->realm) {
+ r = suitable_realm(hr->realm);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
+ _cleanup_(home_freep) Home *home = NULL;
+ _cleanup_free_ char *nm = NULL, *ns = NULL;
+ int r;
+
+ assert(m);
+ assert(hr);
+
+ r = suitable_home_record(hr);
+ if (r < 0)
+ return r;
+
+ if (hashmap_contains(m->homes_by_name, hr->user_name))
+ return -EBUSY;
+
+ if (hashmap_contains(m->homes_by_uid, UID_TO_PTR(hr->uid)))
+ return -EBUSY;
+
+ if (sysfs && hashmap_contains(m->homes_by_sysfs, sysfs))
+ return -EBUSY;
+
+ if (hashmap_size(m->homes_by_name) >= HOME_USERS_MAX)
+ return -EUSERS;
+
+ nm = strdup(hr->user_name);
+ if (!nm)
+ return -ENOMEM;
+
+ if (sysfs) {
+ ns = strdup(sysfs);
+ if (!ns)
+ return -ENOMEM;
+ }
+
+ home = new(Home, 1);
+ if (!home)
+ return -ENOMEM;
+
+ *home = (Home) {
+ .manager = m,
+ .user_name = TAKE_PTR(nm),
+ .uid = hr->uid,
+ .state = _HOME_STATE_INVALID,
+ .worker_stdout_fd = -1,
+ .sysfs = TAKE_PTR(ns),
+ .signed_locally = -1,
+ };
+
+ r = hashmap_put(m->homes_by_name, home->user_name, home);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->homes_by_uid, UID_TO_PTR(home->uid), home);
+ if (r < 0)
+ return r;
+
+ if (home->sysfs) {
+ r = hashmap_put(m->homes_by_sysfs, home->sysfs, home);
+ if (r < 0)
+ return r;
+ }
+
+ r = user_record_clone(hr, USER_RECORD_LOAD_MASK_SECRET, &home->record);
+ if (r < 0)
+ return r;
+
+ (void) bus_manager_emit_auto_login_changed(m);
+ (void) bus_home_emit_change(home);
+
+ if (ret)
+ *ret = TAKE_PTR(home);
+ else
+ TAKE_PTR(home);
+
+ return 0;
+}
+
+Home *home_free(Home *h) {
+
+ if (!h)
+ return NULL;
+
+ if (h->manager) {
+ (void) bus_home_emit_remove(h);
+ (void) bus_manager_emit_auto_login_changed(h->manager);
+
+ if (h->user_name)
+ (void) hashmap_remove_value(h->manager->homes_by_name, h->user_name, h);
+
+ if (uid_is_valid(h->uid))
+ (void) hashmap_remove_value(h->manager->homes_by_uid, UID_TO_PTR(h->uid), h);
+
+ if (h->sysfs)
+ (void) hashmap_remove_value(h->manager->homes_by_sysfs, h->sysfs, h);
+
+ if (h->worker_pid > 0)
+ (void) hashmap_remove_value(h->manager->homes_by_worker_pid, PID_TO_PTR(h->worker_pid), h);
+
+ if (h->manager->gc_focus == h)
+ h->manager->gc_focus = NULL;
+ }
+
+ user_record_unref(h->record);
+ user_record_unref(h->secret);
+
+ h->worker_event_source = sd_event_source_unref(h->worker_event_source);
+ safe_close(h->worker_stdout_fd);
+ free(h->user_name);
+ free(h->sysfs);
+
+ h->ref_event_source_please_suspend = sd_event_source_unref(h->ref_event_source_please_suspend);
+ h->ref_event_source_dont_suspend = sd_event_source_unref(h->ref_event_source_dont_suspend);
+
+ h->pending_operations = ordered_set_free(h->pending_operations);
+ h->pending_event_source = sd_event_source_unref(h->pending_event_source);
+ h->deferred_change_event_source = sd_event_source_unref(h->deferred_change_event_source);
+
+ h->current_operation = operation_unref(h->current_operation);
+
+ return mfree(h);
+}
+
+int home_set_record(Home *h, UserRecord *hr) {
+ _cleanup_(user_record_unrefp) UserRecord *new_hr = NULL;
+ Home *other;
+ int r;
+
+ assert(h);
+ assert(h->user_name);
+ assert(h->record);
+ assert(hr);
+
+ if (user_record_equal(h->record, hr))
+ return 0;
+
+ r = suitable_home_record(hr);
+ if (r < 0)
+ return r;
+
+ if (!user_record_compatible(h->record, hr))
+ return -EREMCHG;
+
+ if (!FLAGS_SET(hr->mask, USER_RECORD_REGULAR) ||
+ FLAGS_SET(hr->mask, USER_RECORD_SECRET))
+ return -EINVAL;
+
+ if (FLAGS_SET(h->record->mask, USER_RECORD_STATUS)) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ /* Hmm, the existing record has status fields? If so, copy them over */
+
+ v = json_variant_ref(hr->json);
+ r = json_variant_set_field(&v, "status", json_variant_by_key(h->record->json, "status"));
+ if (r < 0)
+ return r;
+
+ new_hr = user_record_new();
+ if (!new_hr)
+ return -ENOMEM;
+
+ r = user_record_load(new_hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+ if (r < 0)
+ return r;
+
+ hr = new_hr;
+ }
+
+ other = hashmap_get(h->manager->homes_by_uid, UID_TO_PTR(hr->uid));
+ if (other && other != h)
+ return -EBUSY;
+
+ if (h->uid != hr->uid) {
+ r = hashmap_remove_and_replace(h->manager->homes_by_uid, UID_TO_PTR(h->uid), UID_TO_PTR(hr->uid), h);
+ if (r < 0)
+ return r;
+ }
+
+ user_record_unref(h->record);
+ h->record = user_record_ref(hr);
+ h->uid = h->record->uid;
+
+ /* The updated record might have a different autologin setting, trigger a PropertiesChanged event for it */
+ (void) bus_manager_emit_auto_login_changed(h->manager);
+ (void) bus_home_emit_change(h);
+
+ return 0;
+}
+
+int home_save_record(Home *h) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_free_ char *text = NULL;
+ const char *fn;
+ int r;
+
+ assert(h);
+
+ v = json_variant_ref(h->record->json);
+ r = json_variant_normalize(&v);
+ if (r < 0)
+ log_warning_errno(r, "User record could not be normalized.");
+
+ r = json_variant_format(v, JSON_FORMAT_PRETTY|JSON_FORMAT_NEWLINE, &text);
+ if (r < 0)
+ return r;
+
+ (void) mkdir("/var/lib/systemd/", 0755);
+ (void) mkdir("/var/lib/systemd/home/", 0700);
+
+ fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity");
+
+ r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int home_unlink_record(Home *h) {
+ const char *fn;
+
+ assert(h);
+
+ fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity");
+ if (unlink(fn) < 0 && errno != ENOENT)
+ return -errno;
+
+ fn = strjoina("/run/systemd/home/", h->user_name, ".ref");
+ if (unlink(fn) < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+}
+
+static void home_set_state(Home *h, HomeState state) {
+ HomeState old_state, new_state;
+
+ assert(h);
+
+ old_state = home_get_state(h);
+ h->state = state;
+ new_state = home_get_state(h); /* Query the new state, since the 'state' variable might be set to -1,
+ * in which case we synthesize an high-level state on demand */
+
+ log_info("%s: changing state %s → %s", h->user_name,
+ home_state_to_string(old_state),
+ home_state_to_string(new_state));
+
+ if (HOME_STATE_IS_EXECUTING_OPERATION(old_state) && !HOME_STATE_IS_EXECUTING_OPERATION(new_state)) {
+ /* If we just finished executing some operation, process the queue of pending operations. And
+ * enqueue it for GC too. */
+
+ home_schedule_operation(h, NULL, NULL);
+ manager_enqueue_gc(h->manager, h);
+ }
+}
+
+static int home_parse_worker_stdout(int _fd, UserRecord **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_close_ int fd = _fd; /* take possession, even on failure */
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ unsigned line, column;
+ struct stat st;
+ int r;
+
+ if (fstat(fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat stdout fd: %m");
+
+ assert(S_ISREG(st.st_mode));
+
+ if (st.st_size == 0) { /* empty record */
+ *ret = NULL;
+ return 0;
+ }
+
+ if (lseek(fd, SEEK_SET, 0) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek to beginning of memfd: %m");
+
+ f = fdopen(fd, "r");
+ if (!f)
+ return log_error_errno(errno, "Failed to reopen memfd: %m");
+
+ TAKE_FD(fd);
+
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *text = NULL;
+
+ r = read_full_stream(f, &text, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read from client: %m");
+
+ log_debug("Got from worker: %s", text);
+ rewind(f);
+ }
+
+ r = json_parse_file(f, "stdout", JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
+
+ hr = user_record_new();
+ if (!hr)
+ return log_oom();
+
+ r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load home record identity: %m");
+
+ *ret = TAKE_PTR(hr);
+ return 1;
+}
+
+static int home_verify_user_record(Home *h, UserRecord *hr, bool *ret_signed_locally, sd_bus_error *ret_error) {
+ int is_signed;
+
+ assert(h);
+ assert(hr);
+ assert(ret_signed_locally);
+
+ is_signed = manager_verify_user_record(h->manager, hr);
+ switch (is_signed) {
+
+ case USER_RECORD_SIGNED_EXCLUSIVE:
+ log_info("Home %s is signed exclusively by our key, accepting.", hr->user_name);
+ *ret_signed_locally = true;
+ return 0;
+
+ case USER_RECORD_SIGNED:
+ log_info("Home %s is signed by our key (and others), accepting.", hr->user_name);
+ *ret_signed_locally = false;
+ return 0;
+
+ case USER_RECORD_FOREIGN:
+ log_info("Home %s is signed by foreign key we like, accepting.", hr->user_name);
+ *ret_signed_locally = false;
+ return 0;
+
+ case USER_RECORD_UNSIGNED:
+ sd_bus_error_setf(ret_error, BUS_ERROR_BAD_SIGNATURE, "User record %s is not signed at all, refusing.", hr->user_name);
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Home %s contains user record that is not signed at all, refusing.", hr->user_name);
+
+ case -ENOKEY:
+ sd_bus_error_setf(ret_error, BUS_ERROR_BAD_SIGNATURE, "User record %s is not signed by any known key, refusing.", hr->user_name);
+ return log_error_errno(is_signed, "Home %s contians user record that is not signed by any known key, refusing.", hr->user_name);
+
+ default:
+ assert(is_signed < 0);
+ return log_error_errno(is_signed, "Failed to verify signature on user record for %s, refusing fixation: %m", hr->user_name);
+ }
+}
+
+static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
+ /* Converts the error numbers the worker process returned into somewhat sensible dbus errors */
+
+ switch (e) {
+
+ case -EMSGSIZE:
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot shrinked");
+ case -ETXTBSY:
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrinked offline");
+ case -ERANGE:
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
+ case -ENOLINK:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected storage backend");
+ case -EPROTONOSUPPORT:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected file system");
+ case -ENOTTY:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on storage backend");
+ case -ESOCKTNOSUPPORT:
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on file system");
+ case -ENOKEY:
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD, "Password for home %s is incorrect or not sufficient for authentication.", h->user_name);
+ case -EBADSLT:
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN, "Password for home %s is incorrect or not sufficient, and configured security token not found either.", h->user_name);
+ case -ENOANO:
+ return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
+ case -ERFKILL:
+ return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
+ case -EOWNERDEAD:
+ return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
+ case -ENOLCK:
+ return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN, "Bad PIN of security token.");
+ case -ETOOMANYREFS:
+ return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, "Bad PIN of security token, and only a few tries left.");
+ case -EUCLEAN:
+ return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT, "Bad PIN of security token, and only one try left.");
+ case -EBUSY:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+ case -ENOEXEC:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is currently not active", h->user_name);
+ case -ENOSPC:
+ return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name);
+ }
+
+ return 0;
+}
+
+static void home_count_bad_authentication(Home *h, bool save) {
+ int r;
+
+ assert(h);
+
+ r = user_record_bad_authentication(h->record);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to increase bad authentication counter, ignoring: %m");
+ return;
+ }
+
+ if (save) {
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+ }
+}
+
+static void home_fixate_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+ bool signed_locally;
+ int r;
+
+ assert(h);
+ assert(IN_SET(h->state, HOME_FIXATING, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE));
+
+ secret = TAKE_PTR(h->secret); /* Take possession */
+
+ if (ret < 0) {
+ if (ret == -ENOKEY)
+ (void) home_count_bad_authentication(h, false);
+
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Fixation failed: %m");
+ goto fail;
+ }
+ if (!hr) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Did not receive user record from worker process, fixation failed.");
+ goto fail;
+ }
+
+ r = home_verify_user_record(h, hr, &signed_locally, &error);
+ if (r < 0)
+ goto fail;
+
+ r = home_set_record(h, hr);
+ if (r < 0) {
+ log_error_errno(r, "Failed to update home record: %m");
+ goto fail;
+ }
+
+ h->signed_locally = signed_locally;
+
+ /* When we finished fixating (and don't follow-up with activation), let's count this as good authentication */
+ if (h->state == HOME_FIXATING) {
+ r = user_record_good_authentication(h->record);
+ if (r < 0)
+ log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+ }
+
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+
+ if (IN_SET(h->state, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE)) {
+
+ r = home_start_work(h, "activate", h->record, secret);
+ if (r < 0) {
+ h->current_operation = operation_result_unref(h->current_operation, r, NULL);
+ home_set_state(h, _HOME_STATE_INVALID);
+ } else
+ home_set_state(h, h->state == HOME_FIXATING_FOR_ACTIVATION ? HOME_ACTIVATING : HOME_ACTIVATING_FOR_ACQUIRE);
+
+ return;
+ }
+
+ log_debug("Fixation of %s completed.", h->user_name);
+
+ h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+
+ /* Reset the state to "invalid", which makes home_get_state() test if the image exists and returns
+ * HOME_ABSENT vs. HOME_INACTIVE as necessary. */
+ home_set_state(h, _HOME_STATE_INVALID);
+ return;
+
+fail:
+ /* If fixation fails, we stay in unfixated state! */
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ home_set_state(h, HOME_UNFIXATED);
+}
+
+static void home_activate_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(IN_SET(h->state, HOME_ACTIVATING, HOME_ACTIVATING_FOR_ACQUIRE));
+
+ if (ret < 0) {
+ if (ret == -ENOKEY)
+ home_count_bad_authentication(h, true);
+
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Activation failed: %m");
+ goto finish;
+ }
+
+ if (hr) {
+ bool signed_locally;
+
+ r = home_verify_user_record(h, hr, &signed_locally, &error);
+ if (r < 0)
+ goto finish;
+
+ r = home_set_record(h, hr);
+ if (r < 0) {
+ log_error_errno(r, "Failed to update home record, ignoring: %m");
+ goto finish;
+ }
+
+ h->signed_locally = signed_locally;
+
+ r = user_record_good_authentication(h->record);
+ if (r < 0)
+ log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+ }
+
+ log_debug("Activation of %s completed.", h->user_name);
+ r = 0;
+
+finish:
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_deactivate_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(h->state == HOME_DEACTIVATING);
+ assert(!hr); /* We don't expect a record on this operation */
+
+ if (ret < 0) {
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Deactivation of %s failed: %m", h->user_name);
+ goto finish;
+ }
+
+ log_debug("Deactivation of %s completed.", h->user_name);
+ r = 0;
+
+finish:
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_remove_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ Manager *m;
+ int r;
+
+ assert(h);
+ assert(h->state == HOME_REMOVING);
+ assert(!hr); /* We don't expect a record on this operation */
+
+ m = h->manager;
+
+ if (ret < 0 && ret != -EALREADY) {
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Removing %s failed: %m", h->user_name);
+ goto fail;
+ }
+
+ /* For a couple of storage types we can't delete the actual data storage when called (such as LUKS on
+ * partitions like USB sticks, or so). Sometimes these storage locations are among those we normally
+ * automatically discover in /home or in udev. When such a home is deleted let's hence issue a rescan
+ * after completion, so that "unfixated" entries are rediscovered. */
+ if (!IN_SET(user_record_test_image_path(h->record), USER_TEST_UNDEFINED, USER_TEST_ABSENT))
+ manager_enqueue_rescan(m);
+
+ /* The image is now removed from disk. Now also remove our stored record */
+ r = home_unlink_record(h);
+ if (r < 0) {
+ log_error_errno(r, "Removing record file failed: %m");
+ goto fail;
+ }
+
+ log_debug("Removal of %s completed.", h->user_name);
+ h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+
+ /* Unload this record from memory too now. */
+ h = home_free(h);
+ return;
+
+fail:
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_create_finish(Home *h, int ret, UserRecord *hr) {
+ int r;
+
+ assert(h);
+ assert(h->state == HOME_CREATING);
+
+ if (ret < 0) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ (void) convert_worker_errno(h, ret, &error);
+ log_error_errno(ret, "Operation on %s failed: %m", h->user_name);
+ h->current_operation = operation_result_unref(h->current_operation, ret, &error);
+
+ if (h->unregister_on_failure) {
+ (void) home_unlink_record(h);
+ h = home_free(h);
+ return;
+ }
+
+ home_set_state(h, _HOME_STATE_INVALID);
+ return;
+ }
+
+ if (hr) {
+ r = home_set_record(h, hr);
+ if (r < 0)
+ log_warning_errno(r, "Failed to update home record, ignoring: %m");
+ }
+
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to save record to disk, ignoring: %m");
+
+ log_debug("Creation of %s completed.", h->user_name);
+
+ h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+ home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_change_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+
+ if (ret < 0) {
+ if (ret == -ENOKEY)
+ (void) home_count_bad_authentication(h, true);
+
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Change operation failed: %m");
+ goto finish;
+ }
+
+ if (hr) {
+ r = home_set_record(h, hr);
+ if (r < 0)
+ log_warning_errno(r, "Failed to update home record, ignoring: %m");
+ else {
+ r = user_record_good_authentication(h->record);
+ if (r < 0)
+ log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+ }
+ }
+
+ log_debug("Change operation of %s completed.", h->user_name);
+ r = 0;
+
+finish:
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_locking_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(h->state == HOME_LOCKING);
+
+ if (ret < 0) {
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Locking operation failed: %m");
+ goto finish;
+ }
+
+ log_debug("Locking operation of %s completed.", h->user_name);
+ h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+ home_set_state(h, HOME_LOCKED);
+ return;
+
+finish:
+ /* If a specific home doesn't know the concept of locking, then that's totally OK, don't propagate
+ * the error if we are executing a LockAllHomes() operation. */
+
+ if (h->current_operation->type == OPERATION_LOCK_ALL && r == -ENOTTY)
+ h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+ else
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+
+ home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_unlocking_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(IN_SET(h->state, HOME_UNLOCKING, HOME_UNLOCKING_FOR_ACQUIRE));
+
+ if (ret < 0) {
+ if (ret == -ENOKEY)
+ (void) home_count_bad_authentication(h, true);
+
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Unlocking operation failed: %m");
+
+ /* Revert to locked state */
+ home_set_state(h, HOME_LOCKED);
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ return;
+ }
+
+ r = user_record_good_authentication(h->record);
+ if (r < 0)
+ log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+ else {
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+ }
+
+ log_debug("Unlocking operation of %s completed.", h->user_name);
+
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ home_set_state(h, _HOME_STATE_INVALID);
+ return;
+}
+
+static void home_authenticating_finish(Home *h, int ret, UserRecord *hr) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(IN_SET(h->state, HOME_AUTHENTICATING, HOME_AUTHENTICATING_WHILE_ACTIVE, HOME_AUTHENTICATING_FOR_ACQUIRE));
+
+ if (ret < 0) {
+ if (ret == -ENOKEY)
+ (void) home_count_bad_authentication(h, true);
+
+ (void) convert_worker_errno(h, ret, &error);
+ r = log_error_errno(ret, "Authentication failed: %m");
+ goto finish;
+ }
+
+ if (hr) {
+ r = home_set_record(h, hr);
+ if (r < 0)
+ log_warning_errno(r, "Failed to update home record, ignoring: %m");
+ else {
+ r = user_record_good_authentication(h->record);
+ if (r < 0)
+ log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+ }
+ }
+
+ log_debug("Authentication of %s completed.", h->user_name);
+ r = 0;
+
+finish:
+ h->current_operation = operation_result_unref(h->current_operation, r, &error);
+ home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static int home_on_worker_process(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ Home *h = userdata;
+ int ret;
+
+ assert(s);
+ assert(si);
+ assert(h);
+
+ assert(h->worker_pid == si->si_pid);
+ assert(h->worker_event_source);
+ assert(h->worker_stdout_fd >= 0);
+
+ (void) hashmap_remove_value(h->manager->homes_by_worker_pid, PID_TO_PTR(h->worker_pid), h);
+
+ h->worker_pid = 0;
+ h->worker_event_source = sd_event_source_unref(h->worker_event_source);
+
+ if (si->si_code != CLD_EXITED) {
+ assert(IN_SET(si->si_code, CLD_KILLED, CLD_DUMPED));
+ ret = log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Worker process died abnormally with signal %s.", signal_to_string(si->si_status));
+ } else if (si->si_status != EXIT_SUCCESS) {
+ /* If we received an error code via sd_notify(), use it */
+ if (h->worker_error_code != 0)
+ ret = log_debug_errno(h->worker_error_code, "Worker reported error code %s.", errno_to_name(h->worker_error_code));
+ else
+ ret = log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Worker exited with exit code %i.", si->si_status);
+ } else
+ ret = home_parse_worker_stdout(TAKE_FD(h->worker_stdout_fd), &hr);
+
+ h->worker_stdout_fd = safe_close(h->worker_stdout_fd);
+
+ switch (h->state) {
+
+ case HOME_FIXATING:
+ case HOME_FIXATING_FOR_ACTIVATION:
+ case HOME_FIXATING_FOR_ACQUIRE:
+ home_fixate_finish(h, ret, hr);
+ break;
+
+ case HOME_ACTIVATING:
+ case HOME_ACTIVATING_FOR_ACQUIRE:
+ home_activate_finish(h, ret, hr);
+ break;
+
+ case HOME_DEACTIVATING:
+ home_deactivate_finish(h, ret, hr);
+ break;
+
+ case HOME_LOCKING:
+ home_locking_finish(h, ret, hr);
+ break;
+
+ case HOME_UNLOCKING:
+ case HOME_UNLOCKING_FOR_ACQUIRE:
+ home_unlocking_finish(h, ret, hr);
+ break;
+
+ case HOME_CREATING:
+ home_create_finish(h, ret, hr);
+ break;
+
+ case HOME_REMOVING:
+ home_remove_finish(h, ret, hr);
+ break;
+
+ case HOME_UPDATING:
+ case HOME_UPDATING_WHILE_ACTIVE:
+ case HOME_RESIZING:
+ case HOME_RESIZING_WHILE_ACTIVE:
+ case HOME_PASSWD:
+ case HOME_PASSWD_WHILE_ACTIVE:
+ home_change_finish(h, ret, hr);
+ break;
+
+ case HOME_AUTHENTICATING:
+ case HOME_AUTHENTICATING_WHILE_ACTIVE:
+ case HOME_AUTHENTICATING_FOR_ACQUIRE:
+ home_authenticating_finish(h, ret, hr);
+ break;
+
+ default:
+ assert_not_reached("Unexpected state after worker exited");
+ }
+
+ return 0;
+}
+
+static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord *secret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(erase_and_freep) char *formatted = NULL;
+ _cleanup_close_ int stdin_fd = -1, stdout_fd = -1;
+ pid_t pid = 0;
+ int r;
+
+ assert(h);
+ assert(verb);
+ assert(hr);
+
+ if (h->worker_pid != 0)
+ return -EBUSY;
+
+ assert(h->worker_stdout_fd < 0);
+ assert(!h->worker_event_source);
+
+ v = json_variant_ref(hr->json);
+
+ if (secret) {
+ JsonVariant *sub = NULL;
+
+ sub = json_variant_by_key(secret->json, "secret");
+ if (!sub)
+ return -ENOKEY;
+
+ r = json_variant_set_field(&v, "secret", sub);
+ if (r < 0)
+ return r;
+ }
+
+ r = json_variant_format(v, 0, &formatted);
+ if (r < 0)
+ return r;
+
+ stdin_fd = acquire_data_fd(formatted, strlen(formatted), 0);
+ if (stdin_fd < 0)
+ return stdin_fd;
+
+ log_debug("Sending to worker: %s", formatted);
+
+ stdout_fd = memfd_create("homework-stdout", MFD_CLOEXEC);
+ if (stdout_fd < 0)
+ return -errno;
+
+ r = safe_fork_full("(sd-homework)",
+ (int[]) { stdin_fd, stdout_fd }, 2,
+ FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child */
+
+ if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) {
+ log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = rearrange_stdio(stdin_fd, stdout_fd, STDERR_FILENO);
+ if (r < 0) {
+ log_error_errno(r, "Failed to rearrange stdin/stdout/stderr: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ stdin_fd = stdout_fd = -1; /* have been invalidated by rearrange_stdio() */
+
+ execl(SYSTEMD_HOMEWORK_PATH, SYSTEMD_HOMEWORK_PATH, verb, NULL);
+ log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = sd_event_add_child(h->manager->event, &h->worker_event_source, pid, WEXITED, home_on_worker_process, h);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(h->worker_event_source, "worker");
+
+ r = hashmap_put(h->manager->homes_by_worker_pid, PID_TO_PTR(pid), h);
+ if (r < 0) {
+ h->worker_event_source = sd_event_source_unref(h->worker_event_source);
+ return r;
+ }
+
+ h->worker_stdout_fd = TAKE_FD(stdout_fd);
+ h->worker_pid = pid;
+ h->worker_error_code = 0;
+
+ return 0;
+}
+
+static int home_ratelimit(Home *h, sd_bus_error *error) {
+ int r, ret;
+
+ assert(h);
+
+ ret = user_record_ratelimit(h->record);
+ if (ret < 0)
+ return ret;
+
+ if (h->state != HOME_UNFIXATED) {
+ r = home_save_record(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to save updated record, ignoring: %m");
+ }
+
+ if (ret == 0) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ usec_t t, n;
+
+ n = now(CLOCK_REALTIME);
+ t = user_record_ratelimit_next_try(h->record);
+
+ if (t != USEC_INFINITY && t > n)
+ return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again in %s!",
+ format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
+
+ return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again later.");
+ }
+
+ return 0;
+}
+
+static int home_fixate_internal(
+ Home *h,
+ UserRecord *secret,
+ HomeState for_state,
+ sd_bus_error *error) {
+
+ int r;
+
+ assert(h);
+ assert(IN_SET(for_state, HOME_FIXATING, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE));
+
+ r = home_start_work(h, "inspect", h->record, secret);
+ if (r < 0)
+ return r;
+
+ if (for_state == HOME_FIXATING_FOR_ACTIVATION) {
+ /* Remember the secret data, since we need it for the activation again, later on. */
+ user_record_unref(h->secret);
+ h->secret = user_record_ref(secret);
+ }
+
+ home_set_state(h, for_state);
+ return 0;
+}
+
+int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+
+ switch (home_get_state(h)) {
+ case HOME_ABSENT:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ case HOME_INACTIVE:
+ case HOME_ACTIVE:
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
+ case HOME_UNFIXATED:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ r = home_ratelimit(h, error);
+ if (r < 0)
+ return r;
+
+ return home_fixate_internal(h, secret, HOME_FIXATING, error);
+}
+
+static int home_activate_internal(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+ assert(IN_SET(for_state, HOME_ACTIVATING, HOME_ACTIVATING_FOR_ACQUIRE));
+
+ r = home_start_work(h, "activate", h->record, secret);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, for_state);
+ return 0;
+}
+
+int home_activate(Home *h, UserRecord *secret, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+
+ switch (home_get_state(h)) {
+ case HOME_UNFIXATED:
+ return home_fixate_internal(h, secret, HOME_FIXATING_FOR_ACTIVATION, error);
+ case HOME_ABSENT:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ case HOME_ACTIVE:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_ACTIVE, "Home %s is already active.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_INACTIVE:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ r = home_ratelimit(h, error);
+ if (r < 0)
+ return r;
+
+ return home_activate_internal(h, secret, HOME_ACTIVATING, error);
+}
+
+static int home_authenticate_internal(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+ assert(IN_SET(for_state, HOME_AUTHENTICATING, HOME_AUTHENTICATING_WHILE_ACTIVE, HOME_AUTHENTICATING_FOR_ACQUIRE));
+
+ r = home_start_work(h, "inspect", h->record, secret);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, for_state);
+ return 0;
+}
+
+int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
+ HomeState state;
+ int r;
+
+ assert(h);
+
+ state = home_get_state(h);
+ switch (state) {
+ case HOME_ABSENT:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_UNFIXATED:
+ case HOME_INACTIVE:
+ case HOME_ACTIVE:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ r = home_ratelimit(h, error);
+ if (r < 0)
+ return r;
+
+ return home_authenticate_internal(h, secret, state == HOME_ACTIVE ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
+}
+
+static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+
+ r = home_start_work(h, force ? "deactivate-force" : "deactivate", h->record, NULL);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, HOME_DEACTIVATING);
+ return 0;
+}
+
+int home_deactivate(Home *h, bool force, sd_bus_error *error) {
+ assert(h);
+
+ switch (home_get_state(h)) {
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_ACTIVE:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ return home_deactivate_internal(h, force, error);
+}
+
+int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+
+ switch (home_get_state(h)) {
+ case HOME_INACTIVE:
+ if (h->record->storage < 0)
+ break; /* if no storage is defined we don't know what precisely to look for, hence
+ * HOME_INACTIVE is OK in that case too. */
+
+ if (IN_SET(user_record_test_image_path(h->record), USER_TEST_MAYBE, USER_TEST_UNDEFINED))
+ break; /* And if the image path test isn't conclusive, let's also go on */
+
+ _fallthrough_;
+ case HOME_UNFIXATED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Home of user %s already exists.", h->user_name);
+ case HOME_ABSENT:
+ break;
+ case HOME_ACTIVE:
+ case HOME_LOCKED:
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+ }
+
+ if (h->record->enforce_password_policy == false)
+ log_debug("Password quality check turned off for account, skipping.");
+ else {
+ r = quality_check_password(h->record, secret, error);
+ if (r < 0)
+ return r;
+ }
+
+ r = home_start_work(h, "create", h->record, secret);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, HOME_CREATING);
+ return 0;
+}
+
+int home_remove(Home *h, sd_bus_error *error) {
+ HomeState state;
+ int r;
+
+ assert(h);
+
+ state = home_get_state(h);
+ switch (state) {
+ case HOME_ABSENT: /* If the home directory is absent, then this is just like unregistering */
+ return home_unregister(h, error);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_UNFIXATED:
+ case HOME_INACTIVE:
+ break;
+ case HOME_ACTIVE:
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+ }
+
+ r = home_start_work(h, "remove", h->record, NULL);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, HOME_REMOVING);
+ return 0;
+}
+
+static int user_record_extend_with_binding(UserRecord *hr, UserRecord *with_binding, UserRecordLoadFlags flags, UserRecord **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *nr = NULL;
+ JsonVariant *binding;
+ int r;
+
+ assert(hr);
+ assert(with_binding);
+ assert(ret);
+
+ assert_se(v = json_variant_ref(hr->json));
+
+ binding = json_variant_by_key(with_binding->json, "binding");
+ if (binding) {
+ r = json_variant_set_field(&v, "binding", binding);
+ if (r < 0)
+ return r;
+ }
+
+ nr = user_record_new();
+ if (!nr)
+ return -ENOMEM;
+
+ r = user_record_load(nr, v, flags);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(nr);
+ return 0;
+}
+
+static int home_update_internal(Home *h, const char *verb, UserRecord *hr, UserRecord *secret, sd_bus_error *error) {
+ _cleanup_(user_record_unrefp) UserRecord *new_hr = NULL, *saved_secret = NULL, *signed_hr = NULL;
+ int r, c;
+
+ assert(h);
+ assert(verb);
+ assert(hr);
+
+ if (!user_record_compatible(hr, h->record))
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Updated user record is not compatible with existing one.");
+ c = user_record_compare_last_change(hr, h->record); /* refuse downgrades */
+ if (c < 0)
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_DOWNGRADE, "Refusing to update to older home record.");
+
+ if (!secret && FLAGS_SET(hr->mask, USER_RECORD_SECRET)) {
+ r = user_record_clone(hr, USER_RECORD_EXTRACT_SECRET, &saved_secret);
+ if (r < 0)
+ return r;
+
+ secret = saved_secret;
+ }
+
+ r = manager_verify_user_record(h->manager, hr);
+ switch (r) {
+
+ case USER_RECORD_UNSIGNED:
+ if (h->signed_locally <= 0) /* If the existing record is not owned by us, don't accept an
+ * unsigned new record. i.e. only implicitly sign new records
+ * that where previously signed by us too. */
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_SIGNED, "Home %s is signed and cannot be modified locally.", h->user_name);
+
+ /* The updated record is not signed, then do so now */
+ r = manager_sign_user_record(h->manager, hr, &signed_hr, error);
+ if (r < 0)
+ return r;
+
+ hr = signed_hr;
+ break;
+
+ case USER_RECORD_SIGNED_EXCLUSIVE:
+ case USER_RECORD_SIGNED:
+ case USER_RECORD_FOREIGN:
+ /* Has already been signed. Great! */
+ break;
+
+ case -ENOKEY:
+ default:
+ return r;
+ }
+
+ r = user_record_extend_with_binding(hr, h->record, USER_RECORD_LOAD_MASK_SECRET, &new_hr);
+ if (r < 0)
+ return r;
+
+ if (c == 0) {
+ /* different payload but same lastChangeUSec field? That's not cool! */
+
+ r = user_record_masked_equal(new_hr, h->record, USER_RECORD_REGULAR|USER_RECORD_PRIVILEGED|USER_RECORD_PER_MACHINE);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Home record different but timestamp remained the same, refusing.");
+ }
+
+ r = home_start_work(h, verb, new_hr, secret);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
+ HomeState state;
+ int r;
+
+ assert(h);
+ assert(hr);
+
+ state = home_get_state(h);
+ switch (state) {
+ case HOME_UNFIXATED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s has not been fixated yet.", h->user_name);
+ case HOME_ABSENT:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_INACTIVE:
+ case HOME_ACTIVE:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ r = home_ratelimit(h, error);
+ if (r < 0)
+ return r;
+
+ r = home_update_internal(h, "update", hr, NULL, error);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, state == HOME_ACTIVE ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
+ return 0;
+}
+
+int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *error) {
+ _cleanup_(user_record_unrefp) UserRecord *c = NULL;
+ HomeState state;
+ int r;
+
+ assert(h);
+
+ state = home_get_state(h);
+ switch (state) {
+ case HOME_UNFIXATED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s has not been fixated yet.", h->user_name);
+ case HOME_ABSENT:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_INACTIVE:
+ case HOME_ACTIVE:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ r = home_ratelimit(h, error);
+ if (r < 0)
+ return r;
+
+ if (disk_size == UINT64_MAX || disk_size == h->record->disk_size) {
+ if (h->record->disk_size == UINT64_MAX)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not disk size to resize to specified.");
+
+ c = user_record_ref(h->record); /* Shortcut if size is unspecified or matches the record */
+ } else {
+ _cleanup_(user_record_unrefp) UserRecord *signed_c = NULL;
+
+ if (h->signed_locally <= 0) /* Don't allow changing of records not signed only by us */
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_SIGNED, "Home %s is signed and cannot be modified locally.", h->user_name);
+
+ r = user_record_clone(h->record, USER_RECORD_LOAD_REFUSE_SECRET, &c);
+ if (r < 0)
+ return r;
+
+ r = user_record_set_disk_size(c, disk_size);
+ if (r == -ERANGE)
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "Requested size for home %s out of acceptable range.", h->user_name);
+ if (r < 0)
+ return r;
+
+ r = user_record_update_last_changed(c, false);
+ if (r == -ECHRNG)
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Record last change time of %s is newer than current time, cannot update.", h->user_name);
+ if (r < 0)
+ return r;
+
+ r = manager_sign_user_record(h->manager, c, &signed_c, error);
+ if (r < 0)
+ return r;
+
+ user_record_unref(c);
+ c = TAKE_PTR(signed_c);
+ }
+
+ r = home_update_internal(h, "resize", c, secret, error);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, state == HOME_ACTIVE ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
+ return 0;
+}
+
+static int home_may_change_password(
+ Home *h,
+ sd_bus_error *error) {
+
+ int r;
+
+ assert(h);
+
+ r = user_record_test_password_change_required(h->record);
+ if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED))
+ return 0; /* expired in some form, but chaning is allowed */
+ if (IN_SET(r, -EKEYREJECTED, -EROFS))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Expiration settings of account %s do not allow changing of password.", h->user_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test password expiry: %m");
+
+ return 0; /* not expired */
+}
+
+int home_passwd(Home *h,
+ UserRecord *new_secret,
+ UserRecord *old_secret,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *c = NULL, *merged_secret = NULL, *signed_c = NULL;
+ HomeState state;
+ int r;
+
+ assert(h);
+
+ if (h->signed_locally <= 0) /* Don't allow changing of records not signed only by us */
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_SIGNED, "Home %s is signed and cannot be modified locally.", h->user_name);
+
+ state = home_get_state(h);
+ switch (state) {
+ case HOME_UNFIXATED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s has not been fixated yet.", h->user_name);
+ case HOME_ABSENT:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_INACTIVE:
+ case HOME_ACTIVE:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ r = home_ratelimit(h, error);
+ if (r < 0)
+ return r;
+
+ r = home_may_change_password(h, error);
+ if (r < 0)
+ return r;
+
+ r = user_record_clone(h->record, USER_RECORD_LOAD_REFUSE_SECRET, &c);
+ if (r < 0)
+ return r;
+
+ merged_secret = user_record_new();
+ if (!merged_secret)
+ return -ENOMEM;
+
+ r = user_record_merge_secret(merged_secret, old_secret);
+ if (r < 0)
+ return r;
+
+ r = user_record_merge_secret(merged_secret, new_secret);
+ if (r < 0)
+ return r;
+
+ if (!strv_isempty(new_secret->password)) {
+ /* Update the password only if one is specified, otherwise let's just reuse the old password
+ * data. This is useful as a way to propagate updated user records into the LUKS backends
+ * properly. */
+
+ r = user_record_make_hashed_password(c, new_secret->password, /* extend = */ false);
+ if (r < 0)
+ return r;
+
+ r = user_record_set_password_change_now(c, -1 /* remove */);
+ if (r < 0)
+ return r;
+ }
+
+ r = user_record_update_last_changed(c, true);
+ if (r == -ECHRNG)
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Record last change time of %s is newer than current time, cannot update.", h->user_name);
+ if (r < 0)
+ return r;
+
+ r = manager_sign_user_record(h->manager, c, &signed_c, error);
+ if (r < 0)
+ return r;
+
+ if (c->enforce_password_policy == false)
+ log_debug("Password quality check turned off for account, skipping.");
+ else {
+ r = quality_check_password(c, merged_secret, error);
+ if (r < 0)
+ return r;
+ }
+
+ r = home_update_internal(h, "passwd", signed_c, merged_secret, error);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, state == HOME_ACTIVE ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
+ return 0;
+}
+
+int home_unregister(Home *h, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+
+ switch (home_get_state(h)) {
+ case HOME_UNFIXATED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s is not registered.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ break;
+ case HOME_ACTIVE:
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+ }
+
+ r = home_unlink_record(h);
+ if (r < 0)
+ return r;
+
+ /* And destroy the whole entry. The caller needs to be prepared for that. */
+ h = home_free(h);
+ return 1;
+}
+
+int home_lock(Home *h, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+
+ switch (home_get_state(h)) {
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is not active.", h->user_name);
+ case HOME_LOCKED:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
+ case HOME_ACTIVE:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ r = home_start_work(h, "lock", h->record, NULL);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, HOME_LOCKING);
+ return 0;
+}
+
+static int home_unlock_internal(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+ assert(IN_SET(for_state, HOME_UNLOCKING, HOME_UNLOCKING_FOR_ACQUIRE));
+
+ r = home_start_work(h, "unlock", h->record, secret);
+ if (r < 0)
+ return r;
+
+ home_set_state(h, for_state);
+ return 0;
+}
+
+int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error) {
+ int r;
+ assert(h);
+
+ r = home_ratelimit(h, error);
+ if (r < 0)
+ return r;
+
+ switch (home_get_state(h)) {
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ case HOME_ACTIVE:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_LOCKED, "Home %s is not locked.", h->user_name);
+ case HOME_LOCKED:
+ break;
+ default:
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+ }
+
+ return home_unlock_internal(h, secret, HOME_UNLOCKING, error);
+}
+
+HomeState home_get_state(Home *h) {
+ assert(h);
+
+ /* When the state field is initialized, it counts. */
+ if (h->state >= 0)
+ return h->state;
+
+ /* Otherwise, let's see if the home directory is mounted. If so, we assume for sure the home
+ * directory is active */
+ if (user_record_test_home_directory(h->record) == USER_TEST_MOUNTED)
+ return HOME_ACTIVE;
+
+ /* And if we see the image being gone, we report this as absent */
+ if (user_record_test_image_path(h->record) == USER_TEST_ABSENT)
+ return HOME_ABSENT;
+
+ /* And for all other cases we return "inactive". */
+ return HOME_INACTIVE;
+}
+
+void home_process_notify(Home *h, char **l) {
+ const char *e;
+ int error;
+ int r;
+
+ assert(h);
+
+ e = strv_env_get(l, "ERRNO");
+ if (!e) {
+ log_debug("Got notify message lacking ERRNO= field, ignoring.");
+ return;
+ }
+
+ r = safe_atoi(e, &error);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse receieved error number, ignoring: %s", e);
+ return;
+ }
+ if (error <= 0) {
+ log_debug("Error number is out of range: %i", error);
+ return;
+ }
+
+ h->worker_error_code = error;
+}
+
+int home_killall(Home *h) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *unit = NULL;
+ int r;
+
+ assert(h);
+
+ if (!uid_is_valid(h->uid))
+ return 0;
+
+ assert(h->uid > 0); /* We never should be UID 0 */
+
+ /* Let's kill everything matching the specified UID */
+ r = safe_fork("(sd-killer)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ gid_t gid;
+
+ /* Child */
+
+ gid = user_record_gid(h->record);
+ if (setresgid(gid, gid, gid) < 0) {
+ log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setgroups(0, NULL) < 0) {
+ log_error_errno(errno, "Failed to reset auxiliary groups list: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setresuid(h->uid, h->uid, h->uid) < 0) {
+ log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", h->uid);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (kill(-1, SIGKILL) < 0) {
+ log_error_errno(errno, "Failed to kill all processes of UID " UID_FMT ": %m", h->uid);
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ /* Let's also kill everything in the user's slice */
+ if (asprintf(&unit, "user-" UID_FMT ".slice", h->uid) < 0)
+ return log_oom();
+
+ r = sd_bus_call_method(
+ h->manager->bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "KillUnit",
+ &error,
+ NULL,
+ "ssi", unit, "all", SIGKILL);
+ if (r < 0)
+ log_full_errno(sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to kill login processes of user, ignoring: %s", bus_error_message(&error, r));
+
+ return 1;
+}
+
+static int home_get_disk_status_luks(
+ Home *h,
+ HomeState state,
+ uint64_t *ret_disk_size,
+ uint64_t *ret_disk_usage,
+ uint64_t *ret_disk_free,
+ uint64_t *ret_disk_ceiling,
+ uint64_t *ret_disk_floor) {
+
+ uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX,
+ disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX,
+ stat_used = UINT64_MAX, fs_size = UINT64_MAX, header_size = 0;
+
+ struct statfs sfs;
+ const char *hd;
+ int r;
+
+ assert(h);
+ assert(ret_disk_size);
+ assert(ret_disk_usage);
+ assert(ret_disk_free);
+ assert(ret_disk_ceiling);
+
+ if (state != HOME_ABSENT) {
+ const char *ip;
+
+ ip = user_record_image_path(h->record);
+ if (ip) {
+ struct stat st;
+
+ if (stat(ip, &st) < 0)
+ log_debug_errno(errno, "Failed to stat() %s, ignoring: %m", ip);
+ else if (S_ISREG(st.st_mode)) {
+ _cleanup_free_ char *parent = NULL;
+
+ disk_size = st.st_size;
+ stat_used = st.st_blocks * 512;
+
+ parent = dirname_malloc(ip);
+ if (!parent)
+ return log_oom();
+
+ if (statfs(parent, &sfs) < 0)
+ log_debug_errno(errno, "Failed to statfs() %s, ignoring: %m", parent);
+ else
+ disk_ceiling = stat_used + sfs.f_bsize * sfs.f_bavail;
+
+ } else if (S_ISBLK(st.st_mode)) {
+ _cleanup_free_ char *szbuf = NULL;
+ char p[SYS_BLOCK_PATH_MAX("/size")];
+
+ /* Let's read the size off sysfs, so that we don't have to open the device */
+ xsprintf_sys_block_path(p, "/size", st.st_rdev);
+ r = read_one_line_file(p, &szbuf);
+ if (r < 0)
+ log_debug_errno(r, "Failed to read %s, ignoring: %m", p);
+ else {
+ uint64_t sz;
+
+ r = safe_atou64(szbuf, &sz);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse %s, ignoring: %s", p, szbuf);
+ else
+ disk_size = sz * 512;
+ }
+ } else
+ log_debug("Image path is not a block device or regular file, not able to acquire size.");
+ }
+ }
+
+ if (!HOME_STATE_IS_ACTIVE(state))
+ goto finish;
+
+ hd = user_record_home_directory(h->record);
+ if (!hd)
+ goto finish;
+
+ if (statfs(hd, &sfs) < 0) {
+ log_debug_errno(errno, "Failed to statfs() %s, ignoring: %m", hd);
+ goto finish;
+ }
+
+ disk_free = sfs.f_bsize * sfs.f_bavail;
+ fs_size = sfs.f_bsize * sfs.f_blocks;
+ if (disk_size != UINT64_MAX && disk_size > fs_size)
+ header_size = disk_size - fs_size;
+
+ /* We take a perspective from the user here (as opposed to from the host): the used disk space is the
+ * difference from the limit and what's free. This makes a difference if sparse mode is not used: in
+ * that case the image is pre-allocated and thus appears all used from the host PoV but is not used
+ * up at all yet from the user's PoV.
+ *
+ * That said, we use use the stat() reported loopback file size as upper boundary: our footprint can
+ * never be larger than what we take up on the lowest layers. */
+
+ if (disk_size != UINT64_MAX && disk_size > disk_free) {
+ disk_usage = disk_size - disk_free;
+
+ if (stat_used != UINT64_MAX && disk_usage > stat_used)
+ disk_usage = stat_used;
+ } else
+ disk_usage = stat_used;
+
+ /* If we have the magic, determine floor preferably by magic */
+ disk_floor = minimal_size_by_fs_magic(sfs.f_type) + header_size;
+
+finish:
+ /* If we don't know the magic, go by file system name */
+ if (disk_floor == UINT64_MAX)
+ disk_floor = minimal_size_by_fs_name(user_record_file_system_type(h->record));
+
+ *ret_disk_size = disk_size;
+ *ret_disk_usage = disk_usage;
+ *ret_disk_free = disk_free;
+ *ret_disk_ceiling = disk_ceiling;
+ *ret_disk_floor = disk_floor;
+
+ return 0;
+}
+
+static int home_get_disk_status_directory(
+ Home *h,
+ HomeState state,
+ uint64_t *ret_disk_size,
+ uint64_t *ret_disk_usage,
+ uint64_t *ret_disk_free,
+ uint64_t *ret_disk_ceiling,
+ uint64_t *ret_disk_floor) {
+
+ uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX,
+ disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
+ struct statfs sfs;
+ struct dqblk req;
+ const char *path = NULL;
+ int r;
+
+ assert(ret_disk_size);
+ assert(ret_disk_usage);
+ assert(ret_disk_free);
+ assert(ret_disk_ceiling);
+ assert(ret_disk_floor);
+
+ if (HOME_STATE_IS_ACTIVE(state))
+ path = user_record_home_directory(h->record);
+
+ if (!path) {
+ if (state == HOME_ABSENT)
+ goto finish;
+
+ path = user_record_image_path(h->record);
+ }
+
+ if (!path)
+ goto finish;
+
+ if (statfs(path, &sfs) < 0)
+ log_debug_errno(errno, "Failed to statfs() %s, ignoring: %m", path);
+ else {
+ disk_free = sfs.f_bsize * sfs.f_bavail;
+ disk_size = sfs.f_bsize * sfs.f_blocks;
+
+ /* We don't initialize disk_usage from statfs() data here, since the device is likely not used
+ * by us alone, and disk_usage should only reflect our own use. */
+ }
+
+ if (IN_SET(h->record->storage, USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME)) {
+
+ r = btrfs_is_subvol(path);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether %s is a btrfs subvolume: %m", path);
+ else if (r > 0) {
+ BtrfsQuotaInfo qi;
+
+ r = btrfs_subvol_get_subtree_quota(path, 0, &qi);
+ if (r < 0)
+ log_debug_errno(r, "Failed to query btrfs subtree quota, ignoring: %m");
+ else {
+ disk_usage = qi.referenced;
+
+ if (disk_free != UINT64_MAX) {
+ disk_ceiling = qi.referenced + disk_free;
+
+ if (disk_size != UINT64_MAX && disk_ceiling > disk_size)
+ disk_ceiling = disk_size;
+ }
+
+ if (qi.referenced_max != UINT64_MAX) {
+ if (disk_size != UINT64_MAX)
+ disk_size = MIN(qi.referenced_max, disk_size);
+ else
+ disk_size = qi.referenced_max;
+ }
+
+ if (disk_size != UINT64_MAX) {
+ if (disk_size > disk_usage)
+ disk_free = disk_size - disk_usage;
+ else
+ disk_free = 0;
+ }
+ }
+
+ goto finish;
+ }
+ }
+
+ if (IN_SET(h->record->storage, USER_CLASSIC, USER_DIRECTORY, USER_FSCRYPT)) {
+ r = quotactl_path(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), path, h->uid, &req);
+ if (r < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(r)) {
+ log_debug_errno(r, "No UID quota support on %s.", path);
+ goto finish;
+ }
+
+ if (r != -ESRCH) {
+ log_debug_errno(r, "Failed to query disk quota for UID " UID_FMT ": %m", h->uid);
+ goto finish;
+ }
+
+ disk_usage = 0; /* No record of this user? then nothing was used */
+ } else {
+ if (FLAGS_SET(req.dqb_valid, QIF_SPACE) && disk_free != UINT64_MAX) {
+ disk_ceiling = req.dqb_curspace + disk_free;
+
+ if (disk_size != UINT64_MAX && disk_ceiling > disk_size)
+ disk_ceiling = disk_size;
+ }
+
+ if (FLAGS_SET(req.dqb_valid, QIF_BLIMITS)) {
+ uint64_t q;
+
+ /* Take the minimum of the quota and the available disk space here */
+ q = req.dqb_bhardlimit * QIF_DQBLKSIZE;
+ if (disk_size != UINT64_MAX)
+ disk_size = MIN(disk_size, q);
+ else
+ disk_size = q;
+ }
+ if (FLAGS_SET(req.dqb_valid, QIF_SPACE)) {
+ disk_usage = req.dqb_curspace;
+
+ if (disk_size != UINT64_MAX) {
+ if (disk_size > disk_usage)
+ disk_free = disk_size - disk_usage;
+ else
+ disk_free = 0;
+ }
+ }
+ }
+ }
+
+finish:
+ *ret_disk_size = disk_size;
+ *ret_disk_usage = disk_usage;
+ *ret_disk_free = disk_free;
+ *ret_disk_ceiling = disk_ceiling;
+ *ret_disk_floor = disk_floor;
+
+ return 0;
+}
+
+int home_augment_status(
+ Home *h,
+ UserRecordLoadFlags flags,
+ UserRecord **ret) {
+
+ uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX, disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
+ _cleanup_(json_variant_unrefp) JsonVariant *j = NULL, *v = NULL, *m = NULL, *status = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ char ids[SD_ID128_STRING_MAX];
+ HomeState state;
+ sd_id128_t id;
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ /* We are supposed to add this, this can't be on hence. */
+ assert(!FLAGS_SET(flags, USER_RECORD_STRIP_STATUS));
+
+ r = sd_id128_get_machine(&id);
+ if (r < 0)
+ return r;
+
+ state = home_get_state(h);
+
+ switch (h->record->storage) {
+
+ case USER_LUKS:
+ r = home_get_disk_status_luks(h, state, &disk_size, &disk_usage, &disk_free, &disk_ceiling, &disk_floor);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case USER_CLASSIC:
+ case USER_DIRECTORY:
+ case USER_SUBVOLUME:
+ case USER_FSCRYPT:
+ case USER_CIFS:
+ r = home_get_disk_status_directory(h, state, &disk_size, &disk_usage, &disk_free, &disk_ceiling, &disk_floor);
+ if (r < 0)
+ return r;
+
+ break;
+
+ default:
+ ; /* unset */
+ }
+
+ if (disk_floor == UINT64_MAX || (disk_usage != UINT64_MAX && disk_floor < disk_usage))
+ disk_floor = disk_usage;
+ if (disk_floor == UINT64_MAX || disk_floor < USER_DISK_SIZE_MIN)
+ disk_floor = USER_DISK_SIZE_MIN;
+ if (disk_ceiling == UINT64_MAX || disk_ceiling > USER_DISK_SIZE_MAX)
+ disk_ceiling = USER_DISK_SIZE_MAX;
+
+ r = json_build(&status,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("state", JSON_BUILD_STRING(home_state_to_string(state))),
+ JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home")),
+ JSON_BUILD_PAIR_CONDITION(disk_size != UINT64_MAX, "diskSize", JSON_BUILD_UNSIGNED(disk_size)),
+ JSON_BUILD_PAIR_CONDITION(disk_usage != UINT64_MAX, "diskUsage", JSON_BUILD_UNSIGNED(disk_usage)),
+ JSON_BUILD_PAIR_CONDITION(disk_free != UINT64_MAX, "diskFree", JSON_BUILD_UNSIGNED(disk_free)),
+ JSON_BUILD_PAIR_CONDITION(disk_ceiling != UINT64_MAX, "diskCeiling", JSON_BUILD_UNSIGNED(disk_ceiling)),
+ JSON_BUILD_PAIR_CONDITION(disk_floor != UINT64_MAX, "diskFloor", JSON_BUILD_UNSIGNED(disk_floor)),
+ JSON_BUILD_PAIR_CONDITION(h->signed_locally >= 0, "signedLocally", JSON_BUILD_BOOLEAN(h->signed_locally))
+ ));
+ if (r < 0)
+ return r;
+
+ j = json_variant_ref(h->record->json);
+ v = json_variant_ref(json_variant_by_key(j, "status"));
+ m = json_variant_ref(json_variant_by_key(v, sd_id128_to_string(id, ids)));
+
+ r = json_variant_filter(&m, STRV_MAKE("diskSize", "diskUsage", "diskFree", "diskCeiling", "diskFloor", "signedLocally"));
+ if (r < 0)
+ return r;
+
+ r = json_variant_merge(&m, status);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&v, ids, m);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&j, "status", v);
+ if (r < 0)
+ return r;
+
+ ur = user_record_new();
+ if (!ur)
+ return -ENOMEM;
+
+ r = user_record_load(ur, j, flags);
+ if (r < 0)
+ return r;
+
+ ur->incomplete =
+ FLAGS_SET(h->record->mask, USER_RECORD_PRIVILEGED) &&
+ !FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED);
+
+ *ret = TAKE_PTR(ur);
+ return 0;
+}
+
+static int on_home_ref_eof(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(operation_unrefp) Operation *o = NULL;
+ Home *h = userdata;
+
+ assert(s);
+ assert(h);
+
+ if (h->ref_event_source_please_suspend == s)
+ h->ref_event_source_please_suspend = sd_event_source_disable_unref(h->ref_event_source_please_suspend);
+
+ if (h->ref_event_source_dont_suspend == s)
+ h->ref_event_source_dont_suspend = sd_event_source_disable_unref(h->ref_event_source_dont_suspend);
+
+ if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+ return 0;
+
+ log_info("Got notification that all sessions of user %s ended, deactivating automatically.", h->user_name);
+
+ o = operation_new(OPERATION_PIPE_EOF, NULL);
+ if (!o) {
+ log_oom();
+ return 0;
+ }
+
+ home_schedule_operation(h, o, NULL);
+ return 0;
+}
+
+int home_create_fifo(Home *h, bool please_suspend) {
+ _cleanup_close_ int ret_fd = -1;
+ sd_event_source **ss;
+ const char *fn, *suffix;
+ int r;
+
+ assert(h);
+
+ if (please_suspend) {
+ suffix = ".please-suspend";
+ ss = &h->ref_event_source_please_suspend;
+ } else {
+ suffix = ".dont-suspend";
+ ss = &h->ref_event_source_dont_suspend;
+ }
+
+ fn = strjoina("/run/systemd/home/", h->user_name, suffix);
+
+ if (!*ss) {
+ _cleanup_close_ int ref_fd = -1;
+
+ (void) mkdir("/run/systemd/home/", 0755);
+ if (mkfifo(fn, 0600) < 0 && errno != EEXIST)
+ return log_error_errno(errno, "Failed to create FIFO %s: %m", fn);
+
+ ref_fd = open(fn, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (ref_fd < 0)
+ return log_error_errno(errno, "Failed to open FIFO %s for reading: %m", fn);
+
+ r = sd_event_add_io(h->manager->event, ss, ref_fd, 0, on_home_ref_eof, h);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate reference FIFO event source: %m");
+
+ (void) sd_event_source_set_description(*ss, "acquire-ref");
+
+ r = sd_event_source_set_priority(*ss, SD_EVENT_PRIORITY_IDLE-1);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_io_fd_own(*ss, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pass ownership of FIFO event fd to event source: %m");
+
+ TAKE_FD(ref_fd);
+ }
+
+ ret_fd = open(fn, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
+ if (ret_fd < 0)
+ return log_error_errno(errno, "Failed to open FIFO %s for writing: %m", fn);
+
+ return TAKE_FD(ret_fd);
+}
+
+static int home_dispatch_acquire(Home *h, Operation *o) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int (*call)(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) = NULL;
+ HomeState for_state;
+ int r;
+
+ assert(h);
+ assert(o);
+ assert(o->type == OPERATION_ACQUIRE);
+
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ for_state = HOME_FIXATING_FOR_ACQUIRE;
+ call = home_fixate_internal;
+ break;
+
+ case HOME_ABSENT:
+ r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+ break;
+
+ case HOME_INACTIVE:
+ for_state = HOME_ACTIVATING_FOR_ACQUIRE;
+ call = home_activate_internal;
+ break;
+
+ case HOME_ACTIVE:
+ for_state = HOME_AUTHENTICATING_FOR_ACQUIRE;
+ call = home_authenticate_internal;
+ break;
+
+ case HOME_LOCKED:
+ for_state = HOME_UNLOCKING_FOR_ACQUIRE;
+ call = home_unlock_internal;
+ break;
+
+ default:
+ /* All other cases means we are currently executing an operation, which means the job remains
+ * pending. */
+ return 0;
+ }
+
+ assert(!h->current_operation);
+
+ if (call) {
+ r = home_ratelimit(h, &error);
+ if (r >= 0)
+ r = call(h, o->secret, for_state, &error);
+ }
+
+ if (r != 0) /* failure or completed */
+ operation_result(o, r, &error);
+ else /* ongoing */
+ h->current_operation = operation_ref(o);
+
+ return 1;
+}
+
+static int home_dispatch_release(Home *h, Operation *o) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(o);
+ assert(o->type == OPERATION_RELEASE);
+
+ if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+ /* If there's now a reference again, then let's abort the release attempt */
+ r = sd_bus_error_setf(&error, BUS_ERROR_HOME_BUSY, "Home %s is currently referenced.", h->user_name);
+ else {
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ r = 0; /* done */
+ break;
+
+ case HOME_LOCKED:
+ r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ break;
+
+ case HOME_ACTIVE:
+ r = home_deactivate_internal(h, false, &error);
+ break;
+
+ default:
+ /* All other cases means we are currently executing an operation, which means the job remains
+ * pending. */
+ return 0;
+ }
+ }
+
+ assert(!h->current_operation);
+
+ if (r <= 0) /* failure or completed */
+ operation_result(o, r, &error);
+ else /* ongoing */
+ h->current_operation = operation_ref(o);
+
+ return 1;
+}
+
+static int home_dispatch_lock_all(Home *h, Operation *o) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(o);
+ assert(o->type == OPERATION_LOCK_ALL);
+
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ log_info("Home %s is not active, no locking necessary.", h->user_name);
+ r = 0; /* done */
+ break;
+
+ case HOME_LOCKED:
+ log_info("Home %s is already locked.", h->user_name);
+ r = 0; /* done */
+ break;
+
+ case HOME_ACTIVE:
+ log_info("Locking home %s.", h->user_name);
+ r = home_lock(h, &error);
+ break;
+
+ default:
+ /* All other cases means we are currently executing an operation, which means the job remains
+ * pending. */
+ return 0;
+ }
+
+ assert(!h->current_operation);
+
+ if (r != 0) /* failure or completed */
+ operation_result(o, r, &error);
+ else /* ongoing */
+ h->current_operation = operation_ref(o);
+
+ return 1;
+}
+
+static int home_dispatch_pipe_eof(Home *h, Operation *o) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(o);
+ assert(o->type == OPERATION_PIPE_EOF);
+
+ if (h->ref_event_source_please_suspend || h->ref_event_source_dont_suspend)
+ return 1; /* Hmm, there's a reference again, let's cancel this */
+
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ log_info("Home %s already deactivated, no automatic deactivation needed.", h->user_name);
+ break;
+
+ case HOME_DEACTIVATING:
+ log_info("Home %s is already being deactivated, automatic deactivated unnecessary.", h->user_name);
+ break;
+
+ case HOME_ACTIVE:
+ r = home_deactivate_internal(h, false, &error);
+ if (r < 0)
+ log_warning_errno(r, "Failed to deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
+ break;
+
+ case HOME_LOCKED:
+ default:
+ /* If the device is locked or any operation is being executed, let's leave this pending */
+ return 0;
+ }
+
+ /* Note that we don't call operation_fail() or operation_success() here, because this kind of
+ * operation has no message associated with it, and thus there's no need to propagate success. */
+
+ assert(!o->message);
+ return 1;
+}
+
+static int home_dispatch_deactivate_force(Home *h, Operation *o) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(o);
+ assert(o->type == OPERATION_DEACTIVATE_FORCE);
+
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ log_debug("Home %s already deactivated, no forced deactivation due to unplug needed.", h->user_name);
+ break;
+
+ case HOME_DEACTIVATING:
+ log_debug("Home %s is already being deactivated, forced deactivation due to unplug unnecessary.", h->user_name);
+ break;
+
+ case HOME_ACTIVE:
+ case HOME_LOCKED:
+ r = home_deactivate_internal(h, true, &error);
+ if (r < 0)
+ log_warning_errno(r, "Failed to forcibly deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
+ break;
+
+ default:
+ /* If any operation is being executed, let's leave this pending */
+ return 0;
+ }
+
+ /* Note that we don't call operation_fail() or operation_success() here, because this kind of
+ * operation has no message associated with it, and thus there's no need to propagate success. */
+
+ assert(!o->message);
+ return 1;
+}
+
+static int on_pending(sd_event_source *s, void *userdata) {
+ Home *h = userdata;
+ Operation *o;
+ int r;
+
+ assert(s);
+ assert(h);
+
+ o = ordered_set_first(h->pending_operations);
+ if (o) {
+ static int (* const operation_table[_OPERATION_MAX])(Home *h, Operation *o) = {
+ [OPERATION_ACQUIRE] = home_dispatch_acquire,
+ [OPERATION_RELEASE] = home_dispatch_release,
+ [OPERATION_LOCK_ALL] = home_dispatch_lock_all,
+ [OPERATION_PIPE_EOF] = home_dispatch_pipe_eof,
+ [OPERATION_DEACTIVATE_FORCE] = home_dispatch_deactivate_force,
+ };
+
+ assert(operation_table[o->type]);
+ r = operation_table[o->type](h, o);
+ if (r != 0) {
+ /* The operation completed, let's remove it from the pending list, and exit while
+ * leaving the event source enabled as it is. */
+ assert_se(ordered_set_remove(h->pending_operations, o) == o);
+ operation_unref(o);
+ return 0;
+ }
+ }
+
+ /* Nothing to do anymore, let's turn off this event source */
+ r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable event source: %m");
+
+ return 0;
+}
+
+int home_schedule_operation(Home *h, Operation *o, sd_bus_error *error) {
+ int r;
+
+ assert(h);
+
+ if (o) {
+ if (ordered_set_size(h->pending_operations) >= PENDING_OPERATIONS_MAX)
+ return sd_bus_error_setf(error, BUS_ERROR_TOO_MANY_OPERATIONS, "Too many client operations requested");
+
+ r = ordered_set_ensure_allocated(&h->pending_operations, &operation_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_set_put(h->pending_operations, o);
+ if (r < 0)
+ return r;
+
+ operation_ref(o);
+ }
+
+ if (!h->pending_event_source) {
+ r = sd_event_add_defer(h->manager->event, &h->pending_event_source, on_pending, h);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate pending defer event source: %m");
+
+ (void) sd_event_source_set_description(h->pending_event_source, "pending");
+
+ r = sd_event_source_set_priority(h->pending_event_source, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_event_source_set_enabled(h->pending_event_source, SD_EVENT_ON);
+ if (r < 0)
+ return log_error_errno(r, "Failed to trigger pending event source: %m");
+
+ return 0;
+}
+
+static int home_get_image_path_seat(Home *h, char **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+ _cleanup_free_ char *c = NULL;
+ const char *ip, *seat;
+ struct stat st;
+ int r;
+
+ assert(h);
+
+ if (user_record_storage(h->record) != USER_LUKS)
+ return -ENXIO;
+
+ ip = user_record_image_path(h->record);
+ if (!ip)
+ return -ENXIO;
+
+ if (!path_startswith(ip, "/dev/"))
+ return -ENXIO;
+
+ if (stat(ip, &st) < 0)
+ return -errno;
+
+ if (!S_ISBLK(st.st_mode))
+ return -ENOTBLK;
+
+ r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_property_value(d, "ID_SEAT", &seat);
+ if (r == -ENOENT) /* no property means seat0 */
+ seat = "seat0";
+ else if (r < 0)
+ return r;
+
+ c = strdup(seat);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(c);
+ return 0;
+}
+
+int home_auto_login(Home *h, char ***ret_seats) {
+ _cleanup_free_ char *seat = NULL, *seat2 = NULL;
+
+ assert(h);
+ assert(ret_seats);
+
+ (void) home_get_image_path_seat(h, &seat);
+
+ if (h->record->auto_login > 0 && !streq_ptr(seat, "seat0")) {
+ /* For now, when the auto-login boolean is set for a user, let's make it mean
+ * "seat0". Eventually we can extend the concept and allow configuration of any kind of seat,
+ * but let's keep simple initially, most likely the feature is interesting on single-user
+ * systems anyway, only.
+ *
+ * We filter out users marked for auto-login in we know for sure their home directory is
+ * absent. */
+
+ if (user_record_test_image_path(h->record) != USER_TEST_ABSENT) {
+ seat2 = strdup("seat0");
+ if (!seat2)
+ return -ENOMEM;
+ }
+ }
+
+ if (seat || seat2) {
+ _cleanup_strv_free_ char **list = NULL;
+ size_t i = 0;
+
+ list = new(char*, 3);
+ if (!list)
+ return -ENOMEM;
+
+ if (seat)
+ list[i++] = TAKE_PTR(seat);
+ if (seat2)
+ list[i++] = TAKE_PTR(seat2);
+
+ list[i] = NULL;
+ *ret_seats = TAKE_PTR(list);
+ return 1;
+ }
+
+ *ret_seats = NULL;
+ return 0;
+}
+
+int home_set_current_message(Home *h, sd_bus_message *m) {
+ assert(h);
+
+ if (!m)
+ return 0;
+
+ if (h->current_operation)
+ return -EBUSY;
+
+ h->current_operation = operation_new(OPERATION_IMMEDIATE, m);
+ if (!h->current_operation)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static const char* const home_state_table[_HOME_STATE_MAX] = {
+ [HOME_UNFIXATED] = "unfixated",
+ [HOME_ABSENT] = "absent",
+ [HOME_INACTIVE] = "inactive",
+ [HOME_FIXATING] = "fixating",
+ [HOME_FIXATING_FOR_ACTIVATION] = "fixating-for-activation",
+ [HOME_FIXATING_FOR_ACQUIRE] = "fixating-for-acquire",
+ [HOME_ACTIVATING] = "activating",
+ [HOME_ACTIVATING_FOR_ACQUIRE] = "activating-for-acquire",
+ [HOME_DEACTIVATING] = "deactivating",
+ [HOME_ACTIVE] = "active",
+ [HOME_LOCKING] = "locking",
+ [HOME_LOCKED] = "locked",
+ [HOME_UNLOCKING] = "unlocking",
+ [HOME_UNLOCKING_FOR_ACQUIRE] = "unlocking-for-acquire",
+ [HOME_CREATING] = "creating",
+ [HOME_REMOVING] = "removing",
+ [HOME_UPDATING] = "updating",
+ [HOME_UPDATING_WHILE_ACTIVE] = "updating-while-active",
+ [HOME_RESIZING] = "resizing",
+ [HOME_RESIZING_WHILE_ACTIVE] = "resizing-while-active",
+ [HOME_PASSWD] = "passwd",
+ [HOME_PASSWD_WHILE_ACTIVE] = "passwd-while-active",
+ [HOME_AUTHENTICATING] = "authenticating",
+ [HOME_AUTHENTICATING_WHILE_ACTIVE] = "authenticating-while-active",
+ [HOME_AUTHENTICATING_FOR_ACQUIRE] = "authenticating-for-acquire",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(home_state, HomeState);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+typedef struct Home Home;
+
+#include "homed-manager.h"
+#include "homed-operation.h"
+#include "list.h"
+#include "ordered-set.h"
+#include "user-record.h"
+
+typedef enum HomeState {
+ HOME_UNFIXATED, /* home exists, but local record does not */
+ HOME_ABSENT, /* local record exists, but home does not */
+ HOME_INACTIVE, /* record and home exist, but is not logged in */
+ HOME_FIXATING, /* generating local record from home */
+ HOME_FIXATING_FOR_ACTIVATION, /* fixating in order to activate soon */
+ HOME_FIXATING_FOR_ACQUIRE, /* fixating because Acquire() was called */
+ HOME_ACTIVATING,
+ HOME_ACTIVATING_FOR_ACQUIRE, /* activating because Acquire() was called */
+ HOME_DEACTIVATING,
+ HOME_ACTIVE, /* logged in right now */
+ HOME_LOCKING,
+ HOME_LOCKED,
+ HOME_UNLOCKING,
+ HOME_UNLOCKING_FOR_ACQUIRE, /* unlocking because Acquire() was called */
+ HOME_CREATING,
+ HOME_REMOVING,
+ HOME_UPDATING,
+ HOME_UPDATING_WHILE_ACTIVE,
+ HOME_RESIZING,
+ HOME_RESIZING_WHILE_ACTIVE,
+ HOME_PASSWD,
+ HOME_PASSWD_WHILE_ACTIVE,
+ HOME_AUTHENTICATING,
+ HOME_AUTHENTICATING_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_FOR_ACQUIRE, /* authenticating because Acquire() was called */
+ _HOME_STATE_MAX,
+ _HOME_STATE_INVALID = -1
+} HomeState;
+
+static inline bool HOME_STATE_IS_ACTIVE(HomeState state) {
+ return IN_SET(state,
+ HOME_ACTIVE,
+ HOME_UPDATING_WHILE_ACTIVE,
+ HOME_RESIZING_WHILE_ACTIVE,
+ HOME_PASSWD_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_FOR_ACQUIRE);
+}
+
+static inline bool HOME_STATE_IS_EXECUTING_OPERATION(HomeState state) {
+ return IN_SET(state,
+ HOME_FIXATING,
+ HOME_FIXATING_FOR_ACTIVATION,
+ HOME_FIXATING_FOR_ACQUIRE,
+ HOME_ACTIVATING,
+ HOME_ACTIVATING_FOR_ACQUIRE,
+ HOME_DEACTIVATING,
+ HOME_LOCKING,
+ HOME_UNLOCKING,
+ HOME_UNLOCKING_FOR_ACQUIRE,
+ HOME_CREATING,
+ HOME_REMOVING,
+ HOME_UPDATING,
+ HOME_UPDATING_WHILE_ACTIVE,
+ HOME_RESIZING,
+ HOME_RESIZING_WHILE_ACTIVE,
+ HOME_PASSWD,
+ HOME_PASSWD_WHILE_ACTIVE,
+ HOME_AUTHENTICATING,
+ HOME_AUTHENTICATING_WHILE_ACTIVE,
+ HOME_AUTHENTICATING_FOR_ACQUIRE);
+}
+
+struct Home {
+ Manager *manager;
+ char *user_name;
+ uid_t uid;
+
+ char *sysfs; /* When found via plugged in device, the sysfs path to it */
+
+ /* Note that the 'state' field is only set to a state while we are doing something (i.e. activating,
+ * deactivating, creating, removing, and such), or when the home is an "unfixated" one. When we are
+ * done with an operation we invalidate the state. This is hint for home_get_state() to check the
+ * state on request as needed from the mount table and similar.*/
+ HomeState state;
+ int signed_locally; /* signed only by us */
+
+ UserRecord *record;
+
+ pid_t worker_pid;
+ int worker_stdout_fd;
+ sd_event_source *worker_event_source;
+ int worker_error_code;
+
+ /* The message we are currently processing, and thus need to reply to on completion */
+ Operation *current_operation;
+
+ /* Stores the raw, plaintext passwords, but only for short periods of time */
+ UserRecord *secret;
+
+ /* When we create a home area and that fails, we should possibly unregister the record altogether
+ * again, which is remembered in this boolean. */
+ bool unregister_on_failure;
+
+ /* The reading side of a FIFO stored in /run/systemd/home/, the writing side being used for reference
+ * counting. The references dropped to zero as soon as we see EOF. This concept exists twice: once
+ * for clients that are fine if we suspend the home directory on system suspend, and once for cliets
+ * that are not ok with that. This allows us to determine for each home whether there are any clients
+ * that support unsuspend. */
+ sd_event_source *ref_event_source_please_suspend;
+ sd_event_source *ref_event_source_dont_suspend;
+
+ /* Any pending operations we still need to execute. These are for operations we want to queue if we
+ * can't execute them right-away. */
+ OrderedSet *pending_operations;
+
+ /* A defer event source that processes pending acquire/release/eof events. We have a common
+ * dispatcher that processes all three kinds of events. */
+ sd_event_source *pending_event_source;
+
+ /* Did we send out a D-Bus notification about this entry? */
+ bool announced;
+
+ /* Used to coalesce bus PropertiesChanged events */
+ sd_event_source *deferred_change_event_source;
+};
+
+int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret);
+Home *home_free(Home *h);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Home*, home_free);
+
+int home_set_record(Home *h, UserRecord *hr);
+int home_save_record(Home *h);
+int home_unlink_record(Home *h);
+
+int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_activate(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_deactivate(Home *h, bool force, sd_bus_error *error);
+int home_create(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_remove(Home *h, sd_bus_error *error);
+int home_update(Home *h, UserRecord *new_record, sd_bus_error *error);
+int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *error);
+int home_passwd(Home *h, UserRecord *new_secret, UserRecord *old_secret, sd_bus_error *error);
+int home_unregister(Home *h, sd_bus_error *error);
+int home_lock(Home *h, sd_bus_error *error);
+int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error);
+
+HomeState home_get_state(Home *h);
+
+void home_process_notify(Home *h, char **l);
+
+int home_killall(Home *h);
+
+int home_augment_status(Home *h, UserRecordLoadFlags flags, UserRecord **ret);
+
+int home_create_fifo(Home *h, bool please_suspend);
+int home_schedule_operation(Home *h, Operation *o, sd_bus_error *error);
+
+int home_auto_login(Home *h, char ***ret_seats);
+
+int home_set_current_message(Home *h, sd_bus_message *m);
+
+const char *home_state_to_string(HomeState state);
+HomeState home_state_from_string(const char *s);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/capability.h>
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-polkit.h"
+#include "format-util.h"
+#include "homed-bus.h"
+#include "homed-home-bus.h"
+#include "homed-manager-bus.h"
+#include "homed-manager.h"
+#include "strv.h"
+#include "user-record-sign.h"
+#include "user-record-util.h"
+#include "user-util.h"
+
+static int property_get_auto_login(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Manager *m = userdata;
+ Iterator i;
+ Home *h;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(m);
+
+ r = sd_bus_message_open_container(reply, 'a', "(sso)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(h, m->homes_by_name, i) {
+ _cleanup_(strv_freep) char **seats = NULL;
+ _cleanup_free_ char *home_path = NULL;
+ char **s;
+
+ r = home_auto_login(h, &seats);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to determine whether home '%s' is candidate for auto-login, ignoring: %m", h->user_name);
+ continue;
+ }
+ if (!r)
+ continue;
+
+ r = bus_home_path(h, &home_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate home bus path: %m");
+
+ STRV_FOREACH(s, seats) {
+ r = sd_bus_message_append(reply, "(sso)", h->user_name, *s, home_path);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int method_get_home_by_name(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *path = NULL;
+ const char *user_name;
+ Manager *m = userdata;
+ Home *h;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &user_name);
+ if (r < 0)
+ return r;
+ if (!valid_user_group_name(user_name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+ h = hashmap_get(m->homes_by_name, user_name);
+ if (!h)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+
+ r = bus_home_path(h, &path);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(
+ message, "usussso",
+ (uint32_t) h->uid,
+ home_state_to_string(home_get_state(h)),
+ h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+ h->record ? user_record_real_name(h->record) : NULL,
+ h->record ? user_record_home_directory(h->record) : NULL,
+ h->record ? user_record_shell(h->record) : NULL,
+ path);
+}
+
+static int method_get_home_by_uid(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *path = NULL;
+ Manager *m = userdata;
+ uint32_t uid;
+ int r;
+ Home *h;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "u", &uid);
+ if (r < 0)
+ return r;
+ if (!uid_is_valid(uid))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
+
+ h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+ if (!h)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
+
+ /* Note that we don't use bus_home_path() here, but build the path manually, since if we are queried
+ * for a UID we should also generate the bus path with a UID, and bus_home_path() uses our more
+ * typical bus path by name. */
+ if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
+ return -ENOMEM;
+
+ return sd_bus_reply_method_return(
+ message, "ssussso",
+ h->user_name,
+ home_state_to_string(home_get_state(h)),
+ h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+ h->record ? user_record_real_name(h->record) : NULL,
+ h->record ? user_record_home_directory(h->record) : NULL,
+ h->record ? user_record_shell(h->record) : NULL,
+ path);
+}
+
+static int method_list_homes(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *m = userdata;
+ Iterator i;
+ Home *h;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(susussso)");
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(h, m->homes_by_uid, i) {
+ _cleanup_free_ char *path = NULL;
+
+ r = bus_home_path(h, &path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(
+ reply, "(susussso)",
+ h->user_name,
+ (uint32_t) h->uid,
+ home_state_to_string(home_get_state(h)),
+ h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+ h->record ? user_record_real_name(h->record) : NULL,
+ h->record ? user_record_home_directory(h->record) : NULL,
+ h->record ? user_record_shell(h->record) : NULL,
+ path);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_get_user_record_by_name(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *json = NULL, *path = NULL;
+ Manager *m = userdata;
+ const char *user_name;
+ bool incomplete;
+ Home *h;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &user_name);
+ if (r < 0)
+ return r;
+ if (!valid_user_group_name(user_name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+ h = hashmap_get(m->homes_by_name, user_name);
+ if (!h)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+
+ r = bus_home_get_record_json(h, message, &json, &incomplete);
+ if (r < 0)
+ return r;
+
+ r = bus_home_path(h, &path);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(
+ message, "sbo",
+ json,
+ incomplete,
+ path);
+}
+
+static int method_get_user_record_by_uid(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_free_ char *json = NULL, *path = NULL;
+ Manager *m = userdata;
+ bool incomplete;
+ uint32_t uid;
+ Home *h;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "u", &uid);
+ if (r < 0)
+ return r;
+ if (!uid_is_valid(uid))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
+
+ h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+ if (!h)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
+
+ r = bus_home_get_record_json(h, message, &json, &incomplete);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
+ return -ENOMEM;
+
+ return sd_bus_reply_method_return(
+ message, "sbo",
+ json,
+ incomplete,
+ path);
+}
+
+static int generic_home_method(
+ Manager *m,
+ sd_bus_message *message,
+ sd_bus_message_handler_t handler,
+ sd_bus_error *error) {
+
+ const char *user_name;
+ Home *h;
+ int r;
+
+ r = sd_bus_message_read(message, "s", &user_name);
+ if (r < 0)
+ return r;
+
+ if (!valid_user_group_name(user_name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+ h = hashmap_get(m->homes_by_name, user_name);
+ if (!h)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+
+ return handler(message, h, error);
+}
+
+static int method_activate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_activate, error);
+}
+
+static int method_deactivate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_deactivate, error);
+}
+
+static int validate_and_allocate_home(Manager *m, UserRecord *hr, Home **ret, sd_bus_error *error) {
+ _cleanup_(user_record_unrefp) UserRecord *signed_hr = NULL;
+ struct passwd *pw;
+ struct group *gr;
+ bool signed_locally;
+ Home *other;
+ int r;
+
+ assert(m);
+ assert(hr);
+ assert(ret);
+
+ r = user_record_is_supported(hr, error);
+ if (r < 0)
+ return r;
+
+ other = hashmap_get(m->homes_by_name, hr->user_name);
+ if (other)
+ return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists already, refusing.", hr->user_name);
+
+ pw = getpwnam(hr->user_name);
+ if (pw)
+ return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists in the NSS user database, refusing.", hr->user_name);
+
+ gr = getgrnam(hr->user_name);
+ if (gr)
+ return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s conflicts with an NSS group by the same name, refusing.", hr->user_name);
+
+ r = manager_verify_user_record(m, hr);
+ switch (r) {
+
+ case USER_RECORD_UNSIGNED:
+ /* If the record is unsigned, then let's sign it with our own key */
+ r = manager_sign_user_record(m, hr, &signed_hr, error);
+ if (r < 0)
+ return r;
+
+ hr = signed_hr;
+ _fallthrough_;
+
+ case USER_RECORD_SIGNED_EXCLUSIVE:
+ signed_locally = true;
+ break;
+
+ case USER_RECORD_SIGNED:
+ case USER_RECORD_FOREIGN:
+ signed_locally = false;
+ break;
+
+ case -ENOKEY:
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_SIGNATURE, "Specified user record for %s is signed by a key we don't recognize, refusing.", hr->user_name);
+
+ default:
+ return sd_bus_error_set_errnof(error, r, "Failed to validate signature for '%s': %m", hr->user_name);
+ }
+
+ if (uid_is_valid(hr->uid)) {
+ other = hashmap_get(m->homes_by_uid, UID_TO_PTR(hr->uid));
+ if (other)
+ return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by home %s, refusing.", hr->uid, other->user_name);
+
+ pw = getpwuid(hr->uid);
+ if (pw)
+ return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by NSS user %s, refusing.", hr->uid, pw->pw_name);
+
+ gr = getgrgid(hr->uid);
+ if (gr)
+ return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use as GID by NSS group %s, refusing.", hr->uid, gr->gr_name);
+ } else {
+ r = manager_augment_record_with_uid(m, hr);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to acquire UID for '%s': %m", hr->user_name);
+ }
+
+ r = home_new(m, hr, NULL, ret);
+ if (r < 0)
+ return r;
+
+ (*ret)->signed_locally = signed_locally;
+ return r;
+}
+
+static int method_register_home(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ Manager *m = userdata;
+ Home *h;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = bus_message_read_home_record(message, USER_RECORD_LOAD_EMBEDDED, &hr, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.create-home",
+ NULL,
+ true,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = validate_and_allocate_home(m, hr, &h, error);
+ if (r < 0)
+ return r;
+
+ r = home_save_record(h);
+ if (r < 0) {
+ home_free(h);
+ return r;
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_unregister_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_unregister, error);
+}
+
+static int method_create_home(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error) {
+
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ Manager *m = userdata;
+ Home *h;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.home1.create-home",
+ NULL,
+ true,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
+ r = validate_and_allocate_home(m, hr, &h, error);
+ if (r < 0)
+ return r;
+
+ r = home_create(h, hr, error);
+ if (r < 0)
+ goto fail;
+
+ assert(r == 0);
+ h->unregister_on_failure = true;
+ assert(!h->current_operation);
+
+ r = home_set_current_message(h, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+
+fail:
+ (void) home_unlink_record(h);
+ h = home_free(h);
+ return r;
+}
+
+static int method_realize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_realize, error);
+}
+
+static int method_remove_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_remove, error);
+}
+
+static int method_fixate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_fixate, error);
+}
+
+static int method_authenticate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_authenticate, error);
+}
+
+static int method_update_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ Manager *m = userdata;
+ Home *h;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
+ if (r < 0)
+ return r;
+
+ assert(hr->user_name);
+
+ h = hashmap_get(m->homes_by_name, hr->user_name);
+ if (!h)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", hr->user_name);
+
+ return bus_home_method_update_record(h, message, hr, error);
+}
+
+static int method_resize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_resize, error);
+}
+
+static int method_change_password_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_change_password, error);
+}
+
+static int method_lock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_lock, error);
+}
+
+static int method_unlock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_unlock, error);
+}
+
+static int method_acquire_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_acquire, error);
+}
+
+static int method_ref_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_ref, error);
+}
+
+static int method_release_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return generic_home_method(userdata, message, bus_home_method_release, error);
+}
+
+static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(operation_unrefp) Operation *o = NULL;
+ bool waiting = false;
+ Manager *m = userdata;
+ Iterator i;
+ Home *h;
+ int r;
+
+ assert(m);
+
+ /* This is called from logind when we are preparing for system suspend. We enqueue a lock operation
+ * for every suitable home we have and only when all of them completed we send a reply indicating
+ * completion. */
+
+ HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+ /* Automatically suspend all homes that have at least one client referencing it that asked
+ * for "please suspend", and no client that asked for "please do not suspend". */
+ if (h->ref_event_source_dont_suspend ||
+ !h->ref_event_source_please_suspend)
+ continue;
+
+ if (!o) {
+ o = operation_new(OPERATION_LOCK_ALL, message);
+ if (!o)
+ return -ENOMEM;
+ }
+
+ log_info("Automatically locking of home of user %s.", h->user_name);
+
+ r = home_schedule_operation(h, o, error);
+ if (r < 0)
+ return r;
+
+ waiting = true;
+ }
+
+ if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will
+ * be sent as soon as the last of the lock operations completed. */
+ return 1;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+const sd_bus_vtable manager_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ SD_BUS_PROPERTY("AutoLogin", "a(sso)", property_get_auto_login, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
+ SD_BUS_METHOD("GetHomeByName", "s", "usussso", method_get_home_by_name, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetHomeByUID", "u", "ssussso", method_get_home_by_uid, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetUserRecordByName", "s", "sbo", method_get_user_record_by_name, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("GetUserRecordByUID", "u", "sbo", method_get_user_record_by_uid, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("ListHomes", NULL, "a(susussso)", method_list_homes, SD_BUS_VTABLE_UNPRIVILEGED),
+
+ /* The following methods directly execute an operation on a home area, without ref-counting, queing
+ * or anything, and are accessible through homectl. */
+ SD_BUS_METHOD("ActivateHome", "ss", NULL, method_activate_home, SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("DeactivateHome", "s", NULL, method_deactivate_home, 0),
+ SD_BUS_METHOD("RegisterHome", "s", NULL, method_register_home, SD_BUS_VTABLE_UNPRIVILEGED), /* Add JSON record to homed, but don't create actual $HOME */
+ SD_BUS_METHOD("UnregisterHome", "s", NULL, method_unregister_home, SD_BUS_VTABLE_UNPRIVILEGED), /* Remove JSON record from homed, but don't remove actual $HOME */
+ SD_BUS_METHOD("CreateHome", "s", NULL, method_create_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Add JSON record, and create $HOME for it */
+ SD_BUS_METHOD("RealizeHome", "ss", NULL, method_realize_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Create $HOME for already registered JSON entry */
+ SD_BUS_METHOD("RemoveHome", "s", NULL, method_remove_home, SD_BUS_VTABLE_UNPRIVILEGED), /* Remove JSON record and remove $HOME */
+ SD_BUS_METHOD("FixateHome", "ss", NULL, method_fixate_home, SD_BUS_VTABLE_SENSITIVE), /* Investigate $HOME and propagate contained JSON record into our database */
+ SD_BUS_METHOD("AuthenticateHome", "ss", NULL, method_authenticate_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Just check credentials */
+ SD_BUS_METHOD("UpdateHome", "s", NULL, method_update_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Update JSON record of existing user */
+ SD_BUS_METHOD("ResizeHome", "sts", NULL, method_resize_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("ChangePasswordHome", "sss", NULL, method_change_password_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("LockHome", "s", NULL, method_lock_home, 0), /* Prepare active home for system suspend: flush out passwords, suspend access */
+ SD_BUS_METHOD("UnlockHome", "ss", NULL, method_unlock_home, SD_BUS_VTABLE_SENSITIVE), /* Make $HOME usable after system resume again */
+
+ /* The following methods implement ref-counted activation, and are what the PAM module calls (and
+ * what "homectl with" runs). In contrast to the methods above which fail if an operation is already
+ * being executed on a home directory, these ones will queue the request, and are thus more
+ * reliable. Moreover, they are a bit smarter: AcquireHome() will fixate, activate, unlock, or
+ * authenticate depending on the state of the home, so that the end result is always the same
+ * (i.e. the home directory is accessible), and we always validate the specified passwords. RefHome()
+ * will not authenticate, and thus only works if home is already active. */
+ SD_BUS_METHOD("AcquireHome", "ssb", "h", method_acquire_home, SD_BUS_VTABLE_SENSITIVE),
+ SD_BUS_METHOD("RefHome", "sb", "h", method_ref_home, 0),
+ SD_BUS_METHOD("ReleaseHome", "s", NULL, method_release_home, 0),
+
+ /* An operation that acts on all homes that allow it */
+ SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0),
+
+ SD_BUS_VTABLE_END
+};
+
+static int on_deferred_auto_login(sd_event_source *s, void *userdata) {
+ Manager *m = userdata;
+ int r;
+
+ assert(m);
+
+ m->deferred_auto_login_event_source = sd_event_source_unref(m->deferred_auto_login_event_source);
+
+ r = sd_bus_emit_properties_changed(
+ m->bus,
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "AutoLogin", NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to send AutoLogin property change event, ignoring: %m");
+
+ return 0;
+}
+
+int bus_manager_emit_auto_login_changed(Manager *m) {
+ int r;
+ assert(m);
+
+ if (m->deferred_auto_login_event_source)
+ return 0;
+
+ if (!m->event)
+ return 0;
+
+ if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+ return 0;
+
+ r = sd_event_add_defer(m->event, &m->deferred_auto_login_event_source, on_deferred_auto_login, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate auto login event source: %m");
+
+ r = sd_event_source_set_priority(m->deferred_auto_login_event_source, SD_EVENT_PRIORITY_IDLE+10);
+ if (r < 0)
+ log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+ (void) sd_event_source_set_description(m->deferred_auto_login_event_source, "deferred-auto-login");
+ return 1;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+extern const sd_bus_vtable manager_vtable[];
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <grp.h>
+#include <linux/fs.h>
+#include <linux/magic.h>
+#include <openssl/pem.h>
+#include <pwd.h>
+#include <sys/ioctl.h>
+#include <sys/quota.h>
+#include <sys/stat.h>
+
+#include "btrfs-util.h"
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-polkit.h"
+#include "clean-ipc.h"
+#include "conf-files.h"
+#include "device-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "gpt.h"
+#include "home-util.h"
+#include "homed-home-bus.h"
+#include "homed-home.h"
+#include "homed-manager-bus.h"
+#include "homed-manager.h"
+#include "homed-varlink.h"
+#include "io-util.h"
+#include "mkdir.h"
+#include "process-util.h"
+#include "quota-util.h"
+#include "random-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "udev-util.h"
+#include "user-record-sign.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+
+/* Where to look for private/public keys that are used to sign the user records. We are not using
+ * CONF_PATHS_NULSTR() here since we want to insert /var/lib/systemd/home/ in the middle. And we insert that
+ * since we want to auto-generate a persistent private/public key pair if we need to. */
+#define KEY_PATHS_NULSTR \
+ "/etc/systemd/home/\0" \
+ "/run/systemd/home/\0" \
+ "/var/lib/systemd/home/\0" \
+ "/usr/local/lib/systemd/home/\0" \
+ "/usr/lib/systemd/home/\0"
+
+static bool uid_is_home(uid_t uid) {
+ return uid >= HOME_UID_MIN && uid <= HOME_UID_MAX;
+}
+/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
+
+#define UID_CLAMP_INTO_HOME_RANGE(rnd) (((uid_t) (rnd) % (HOME_UID_MAX - HOME_UID_MIN + 1)) + HOME_UID_MIN)
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_uid_hash_ops, void, trivial_hash_func, trivial_compare_func, Home, home_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_name_hash_ops, char, string_hash_func, string_compare_func, Home, home_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_worker_pid_hash_ops, void, trivial_hash_func, trivial_compare_func, Home, home_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_sysfs_hash_ops, char, path_hash_func, path_compare, Home, home_free);
+
+static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata);
+static int manager_gc_images(Manager *m);
+static int manager_enumerate_images(Manager *m);
+static int manager_assess_image(Manager *m, int dir_fd, const char *dir_path, const char *dentry_name);
+static void manager_revalidate_image(Manager *m, Home *h);
+
+static void manager_watch_home(Manager *m) {
+ struct statfs sfs;
+ int r;
+
+ assert(m);
+
+ m->inotify_event_source = sd_event_source_unref(m->inotify_event_source);
+ m->scan_slash_home = false;
+
+ if (statfs("/home/", &sfs) < 0) {
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to statfs() /home/ directory, disabling automatic scanning.");
+ return;
+ }
+
+ if (is_network_fs(&sfs)) {
+ log_info("/home/ is a network file system, disabling automatic scanning.");
+ return;
+ }
+
+ if (is_fs_type(&sfs, AUTOFS_SUPER_MAGIC)) {
+ log_info("/home/ is on autofs, disabling automatic scanning.");
+ return;
+ }
+
+ m->scan_slash_home = true;
+
+ r = sd_event_add_inotify(m->event, &m->inotify_event_source, "/home/", IN_CREATE|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF|IN_ONLYDIR|IN_MOVED_TO|IN_MOVED_FROM|IN_DELETE, on_home_inotify, m);
+ if (r < 0)
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to create inotify watch on /home/, ignoring.");
+
+ (void) sd_event_source_set_description(m->inotify_event_source, "home-inotify");
+}
+
+static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) {
+ Manager *m = userdata;
+ const char *e, *n;
+
+ assert(m);
+ assert(event);
+
+ if ((event->mask & (IN_Q_OVERFLOW|IN_MOVE_SELF|IN_DELETE_SELF|IN_IGNORED|IN_UNMOUNT)) != 0) {
+
+ if (FLAGS_SET(event->mask, IN_Q_OVERFLOW))
+ log_debug("/home/ inotify queue overflow, rescanning.");
+ else if (FLAGS_SET(event->mask, IN_MOVE_SELF))
+ log_info("/home/ moved or renamed, recreating watch and rescanning.");
+ else if (FLAGS_SET(event->mask, IN_DELETE_SELF))
+ log_info("/home/ deleted, recreating watch and rescanning.");
+ else if (FLAGS_SET(event->mask, IN_UNMOUNT))
+ log_info("/home/ unmounted, recreating watch and rescanning.");
+ else if (FLAGS_SET(event->mask, IN_IGNORED))
+ log_info("/home/ watch invalidated, recreating watch and rescanning.");
+
+ manager_watch_home(m);
+ (void) manager_gc_images(m);
+ (void) manager_enumerate_images(m);
+ (void) bus_manager_emit_auto_login_changed(m);
+ return 0;
+ }
+
+ /* For the other inotify events, let's ignore all events for file names that don't match our
+ * expectations */
+ if (isempty(event->name))
+ return 0;
+ e = endswith(event->name, FLAGS_SET(event->mask, IN_ISDIR) ? ".homedir" : ".home");
+ if (!e)
+ return 0;
+
+ n = strndupa(event->name, e - event->name);
+ if (!suitable_user_name(n))
+ return 0;
+
+ if ((event->mask & (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)) != 0) {
+ if (FLAGS_SET(event->mask, IN_CREATE))
+ log_debug("/home/%s has been created, having a look.", event->name);
+ else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
+ log_debug("/home/%s has been modified, having a look.", event->name);
+ else if (FLAGS_SET(event->mask, IN_MOVED_TO))
+ log_debug("/home/%s has been moved in, having a look.", event->name);
+
+ (void) manager_assess_image(m, -1, "/home/", event->name);
+ (void) bus_manager_emit_auto_login_changed(m);
+ }
+
+ if ((event->mask & (IN_DELETE|IN_MOVED_FROM|IN_DELETE)) != 0) {
+ Home *h;
+
+ if (FLAGS_SET(event->mask, IN_DELETE))
+ log_debug("/home/%s has been deleted, revalidating.", event->name);
+ else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
+ log_debug("/home/%s has been closed after writing, revalidating.", event->name);
+ else if (FLAGS_SET(event->mask, IN_MOVED_FROM))
+ log_debug("/home/%s has been moved away, revalidating.", event->name);
+
+ h = hashmap_get(m->homes_by_name, n);
+ if (h) {
+ manager_revalidate_image(m, h);
+ (void) bus_manager_emit_auto_login_changed(m);
+ }
+ }
+
+ return 0;
+}
+
+int manager_new(Manager **ret) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ assert(ret);
+
+ m = new0(Manager, 1);
+ if (!m)
+ return -ENOMEM;
+
+ r = sd_event_default(&m->event);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_set_watchdog(m->event, true);
+
+ m->homes_by_uid = hashmap_new(&homes_by_uid_hash_ops);
+ if (!m->homes_by_uid)
+ return -ENOMEM;
+
+ m->homes_by_name = hashmap_new(&homes_by_name_hash_ops);
+ if (!m->homes_by_name)
+ return -ENOMEM;
+
+ m->homes_by_worker_pid = hashmap_new(&homes_by_worker_pid_hash_ops);
+ if (!m->homes_by_worker_pid)
+ return -ENOMEM;
+
+ m->homes_by_sysfs = hashmap_new(&homes_by_sysfs_hash_ops);
+ if (!m->homes_by_sysfs)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
+Manager* manager_free(Manager *m) {
+ assert(m);
+
+ hashmap_free(m->homes_by_uid);
+ hashmap_free(m->homes_by_name);
+ hashmap_free(m->homes_by_worker_pid);
+ hashmap_free(m->homes_by_sysfs);
+
+ m->inotify_event_source = sd_event_source_unref(m->inotify_event_source);
+
+ bus_verify_polkit_async_registry_free(m->polkit_registry);
+
+ sd_bus_flush_close_unref(m->bus);
+ sd_event_unref(m->event);
+
+ m->notify_socket_event_source = sd_event_source_unref(m->notify_socket_event_source);
+ m->device_monitor = sd_device_monitor_unref(m->device_monitor);
+
+ m->deferred_rescan_event_source = sd_event_source_unref(m->deferred_rescan_event_source);
+ m->deferred_gc_event_source = sd_event_source_unref(m->deferred_gc_event_source);
+ m->deferred_auto_login_event_source = sd_event_source_unref(m->deferred_auto_login_event_source);
+
+ if (m->private_key)
+ EVP_PKEY_free(m->private_key);
+
+ hashmap_free(m->public_keys);
+
+ varlink_server_unref(m->varlink_server);
+
+ return mfree(m);
+}
+
+int manager_verify_user_record(Manager *m, UserRecord *hr) {
+ EVP_PKEY *pkey;
+ Iterator i;
+ int r;
+
+ assert(m);
+ assert(hr);
+
+ if (!m->private_key && hashmap_isempty(m->public_keys)) {
+ r = user_record_has_signature(hr);
+ if (r < 0)
+ return r;
+
+ return r ? -ENOKEY : USER_RECORD_UNSIGNED;
+ }
+
+ /* Is it our own? */
+ if (m->private_key) {
+ r = user_record_verify(hr, m->private_key);
+ switch (r) {
+
+ case USER_RECORD_FOREIGN:
+ /* This record is not signed by this key, but let's see below */
+ break;
+
+ case USER_RECORD_SIGNED: /* Signed by us, but also by others, let's propagate that */
+ case USER_RECORD_SIGNED_EXCLUSIVE: /* Signed by us, and nothing else, ditto */
+ case USER_RECORD_UNSIGNED: /* Not signed at all, ditto */
+ default:
+ return r;
+ }
+ }
+
+ HASHMAP_FOREACH(pkey, m->public_keys, i) {
+ r = user_record_verify(hr, pkey);
+ switch (r) {
+
+ case USER_RECORD_FOREIGN:
+ /* This record is not signed by this key, but let's see our other keys */
+ break;
+
+ case USER_RECORD_SIGNED: /* It's signed by this key we are happy with, but which is not our own. */
+ case USER_RECORD_SIGNED_EXCLUSIVE:
+ return USER_RECORD_FOREIGN;
+
+ case USER_RECORD_UNSIGNED: /* It's not signed at all */
+ default:
+ return r;
+ }
+ }
+
+ return -ENOKEY;
+}
+
+static int manager_add_home_by_record(
+ Manager *m,
+ const char *name,
+ int dir_fd,
+ const char *fname) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ unsigned line, column;
+ int r, is_signed;
+ Home *h;
+
+ assert(m);
+ assert(name);
+ assert(fname);
+
+ r = json_parse_file_at(NULL, dir_fd, fname, JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column);
+
+ hr = user_record_new();
+ if (!hr)
+ return log_oom();
+
+ r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(hr->user_name, name))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Identity's user name %s does not match file name %s, refusing.", hr->user_name, name);
+
+ is_signed = manager_verify_user_record(m, hr);
+ switch (is_signed) {
+
+ case -ENOKEY:
+ return log_warning_errno(is_signed, "User record %s is not signed by any accepted key, ignoring.", fname);
+ case USER_RECORD_UNSIGNED:
+ return log_warning_errno(SYNTHETIC_ERRNO(EPERM), "User record %s is not signed at all, ignoring.", fname);
+ case USER_RECORD_SIGNED:
+ log_info("User record %s is signed by us (and others), accepting.", fname);
+ break;
+ case USER_RECORD_SIGNED_EXCLUSIVE:
+ log_info("User record %s is signed only by us, accepting.", fname);
+ break;
+ case USER_RECORD_FOREIGN:
+ log_info("User record %s is signed by registered key from others, accepting.", fname);
+ break;
+ default:
+ assert(is_signed < 0);
+ return log_error_errno(is_signed, "Failed to verify signature of user record in %s: %m", fname);
+ }
+
+ h = hashmap_get(m->homes_by_name, name);
+ if (h) {
+ r = home_set_record(h, hr);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update home record for %s: %m", name);
+
+ /* If we acquired a record now for a previously unallocated entry, then reset the state. This
+ * makes sure home_get_state() will check for the availability of the image file dynamically
+ * in order to detect to distuingish HOME_INACTIVE and HOME_ABSENT. */
+ if (h->state == HOME_UNFIXATED)
+ h->state = _HOME_STATE_INVALID;
+ } else {
+ r = home_new(m, hr, NULL, &h);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate new home object: %m");
+
+ log_info("Added registered home for user %s.", hr->user_name);
+ }
+
+ /* Only entries we exclusively signed are writable to us, hence remember the result */
+ h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE;
+
+ return 1;
+}
+
+static int manager_enumerate_records(Manager *m) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+
+ assert(m);
+
+ d = opendir("/var/lib/systemd/home/");
+ if (!d)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to open /var/lib/systemd/home/: %m");
+
+ FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read record directory: %m")) {
+ _cleanup_free_ char *n = NULL;
+ const char *e;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ e = endswith(de->d_name, ".identity");
+ if (!e)
+ continue;
+
+ n = strndup(de->d_name, e - de->d_name);
+ if (!n)
+ return log_oom();
+
+ if (!suitable_user_name(n))
+ continue;
+
+ (void) manager_add_home_by_record(m, n, dirfd(d), de->d_name);
+ }
+
+ return 0;
+}
+
+static int search_quota(uid_t uid, const char *exclude_quota_path) {
+ struct stat exclude_st = {};
+ dev_t previous_devno = 0;
+ const char *where;
+ int r;
+
+ /* Checks whether the specified UID owns any files on the files system, but ignore any file system
+ * backing the specified file. The file is used when operating on home directories, where it's OK if
+ * the UID of them already owns files. */
+
+ if (exclude_quota_path && stat(exclude_quota_path, &exclude_st) < 0) {
+ if (errno != ENOENT)
+ return log_warning_errno(errno, "Failed to stat %s, ignoring: %m", exclude_quota_path);
+ }
+
+ /* Check a few usual suspects where regular users might own files. Note that this is by no means
+ * comprehensive, but should cover most cases. Note that in an ideal world every user would be
+ * registered in NSS and avoid our own UID range, but for all other cases, it's a good idea to be
+ * paranoid and check quota if we can. */
+ FOREACH_STRING(where, "/home/", "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
+ struct dqblk req;
+ struct stat st;
+
+ if (stat(where, &st) < 0) {
+ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to stat %s, ignoring: %m", where);
+ continue;
+ }
+
+ if (major(st.st_dev) == 0) {
+ log_debug("Directory %s is not on a real block device, not checking quota for UID use.", where);
+ continue;
+ }
+
+ if (st.st_dev == exclude_st.st_dev) { /* If an exclude path is specified, then ignore quota
+ * reported on the same block device as that path. */
+ log_debug("Directory %s is where the home directory is located, not checking quota for UID use.", where);
+ continue;
+ }
+
+ if (st.st_dev == previous_devno) { /* Does this directory have the same devno as the previous
+ * one we tested? If so, there's no point in testing this
+ * again. */
+ log_debug("Directory %s is on same device as previous tested directory, not checking quota for UID use a second time.", where);
+ continue;
+ }
+
+ previous_devno = st.st_dev;
+
+ r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), st.st_dev, uid, &req);
+ if (r < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(r))
+ log_debug_errno(r, "No UID quota support on %s, ignoring.", where);
+ else
+ log_warning_errno(r, "Failed to query quota on %s, ignoring.", where);
+
+ continue;
+ }
+
+ if ((FLAGS_SET(req.dqb_valid, QIF_SPACE) && req.dqb_curspace > 0) ||
+ (FLAGS_SET(req.dqb_valid, QIF_INODES) && req.dqb_curinodes > 0)) {
+ log_debug_errno(errno, "Quota reports UID " UID_FMT " occupies disk space on %s.", uid, where);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int manager_acquire_uid(
+ Manager *m,
+ uid_t start_uid,
+ const char *user_name,
+ const char *exclude_quota_path,
+ uid_t *ret) {
+
+ static const uint8_t hash_key[] = {
+ 0xa3, 0xb8, 0x82, 0x69, 0x9a, 0x71, 0xf7, 0xa9,
+ 0xe0, 0x7c, 0xf6, 0xf1, 0x21, 0x69, 0xd2, 0x1e
+ };
+
+ enum {
+ PHASE_SUGGESTED,
+ PHASE_HASHED,
+ PHASE_RANDOM
+ } phase = PHASE_SUGGESTED;
+
+ unsigned n_tries = 100;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ for (;;) {
+ struct passwd *pw;
+ struct group *gr;
+ uid_t candidate;
+ Home *other;
+
+ if (--n_tries <= 0)
+ return -EBUSY;
+
+ switch (phase) {
+
+ case PHASE_SUGGESTED:
+ phase = PHASE_HASHED;
+
+ if (!uid_is_home(start_uid))
+ continue;
+
+ candidate = start_uid;
+ break;
+
+ case PHASE_HASHED:
+ phase = PHASE_RANDOM;
+
+ if (!user_name)
+ continue;
+
+ candidate = UID_CLAMP_INTO_HOME_RANGE(siphash24(user_name, strlen(user_name), hash_key));
+ break;
+
+ case PHASE_RANDOM:
+ random_bytes(&candidate, sizeof(candidate));
+ candidate = UID_CLAMP_INTO_HOME_RANGE(candidate);
+ break;
+
+ default:
+ assert_not_reached("unknown phase");
+ }
+
+ other = hashmap_get(m->homes_by_uid, UID_TO_PTR(candidate));
+ if (other) {
+ log_debug("Candidate UID " UID_FMT " already used by another home directory (%s), let's try another.", candidate, other->user_name);
+ continue;
+ }
+
+ pw = getpwuid(candidate);
+ if (pw) {
+ log_debug("Candidate UID " UID_FMT " already registered by another user in NSS (%s), let's try another.", candidate, pw->pw_name);
+ continue;
+ }
+
+ gr = getgrgid((gid_t) candidate);
+ if (gr) {
+ log_debug("Candidate UID " UID_FMT " already registered by another group in NSS (%s), let's try another.", candidate, gr->gr_name);
+ continue;
+ }
+
+ r = search_ipc(candidate, (gid_t) candidate);
+ if (r < 0)
+ continue;
+ if (r > 0) {
+ log_debug_errno(r, "Candidate UID " UID_FMT " already owns IPC objects, let's try another: %m", candidate);
+ continue;
+ }
+
+ r = search_quota(candidate, exclude_quota_path);
+ if (r != 0)
+ continue;
+
+ *ret = candidate;
+ return 0;
+ }
+}
+
+static int manager_add_home_by_image(
+ Manager *m,
+ const char *user_name,
+ const char *realm,
+ const char *image_path,
+ const char *sysfs,
+ UserStorage storage,
+ uid_t start_uid) {
+
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ uid_t uid;
+ Home *h;
+ int r;
+
+ assert(m);
+
+ assert(m);
+ assert(user_name);
+ assert(image_path);
+ assert(storage >= 0);
+ assert(storage < _USER_STORAGE_MAX);
+
+ h = hashmap_get(m->homes_by_name, user_name);
+ if (h) {
+ bool same;
+
+ if (h->state != HOME_UNFIXATED) {
+ log_debug("Found an image for user %s which already has a record, skipping.", user_name);
+ return 0; /* ignore images that synthesize a user we already have a record for */
+ }
+
+ same = user_record_storage(h->record) == storage;
+ if (same) {
+ if (h->sysfs && sysfs)
+ same = path_equal(h->sysfs, sysfs);
+ else if (!!h->sysfs != !!sysfs)
+ same = false;
+ else {
+ const char *p;
+
+ p = user_record_image_path(h->record);
+ same = p && path_equal(p, image_path);
+ }
+ }
+
+ if (!same) {
+ log_debug("Found a multiple images for a user '%s', ignoring image '%s'.", user_name, image_path);
+ return 0;
+ }
+ } else {
+ /* Check NSS, in case there's another user or group by this name */
+ if (getpwnam(user_name) || getgrnam(user_name)) {
+ log_debug("Found an existing user or group by name '%s', ignoring image '%s'.", user_name, image_path);
+ return 0;
+ }
+ }
+
+ if (h && uid_is_valid(h->uid))
+ uid = h->uid;
+ else {
+ r = manager_acquire_uid(m, start_uid, user_name, IN_SET(storage, USER_SUBVOLUME, USER_DIRECTORY, USER_FSCRYPT) ? image_path : NULL, &uid);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to acquire unused UID for %s: %m", user_name);
+ }
+
+ hr = user_record_new();
+ if (!hr)
+ return log_oom();
+
+ r = user_record_synthesize(hr, user_name, realm, image_path, storage, uid, (gid_t) uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize home record for %s (image %s): %m", user_name, image_path);
+
+ if (h) {
+ r = home_set_record(h, hr);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update home record for %s: %m", user_name);
+ } else {
+ r = home_new(m, hr, sysfs, &h);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate new home object: %m");
+
+ h->state = HOME_UNFIXATED;
+
+ log_info("Discovered new home for user %s through image %s.", user_name, image_path);
+ }
+
+ return 1;
+}
+
+int manager_augment_record_with_uid(
+ Manager *m,
+ UserRecord *hr) {
+
+ const char *exclude_quota_path = NULL;
+ uid_t start_uid = UID_INVALID, uid;
+ int r;
+
+ assert(m);
+ assert(hr);
+
+ if (uid_is_valid(hr->uid))
+ return 0;
+
+ if (IN_SET(hr->storage, USER_CLASSIC, USER_SUBVOLUME, USER_DIRECTORY, USER_FSCRYPT)) {
+ const char * ip;
+
+ ip = user_record_image_path(hr);
+ if (ip) {
+ struct stat st;
+
+ if (stat(ip, &st) < 0) {
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Failed to stat(%s): %m", ip);
+ } else if (uid_is_home(st.st_uid)) {
+ start_uid = st.st_uid;
+ exclude_quota_path = ip;
+ }
+ }
+ }
+
+ r = manager_acquire_uid(m, start_uid, hr->user_name, exclude_quota_path, &uid);
+ if (r < 0)
+ return r;
+
+ log_debug("Acquired new UID " UID_FMT " for %s.", uid, hr->user_name);
+
+ r = user_record_add_binding(
+ hr,
+ _USER_STORAGE_INVALID,
+ NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ NULL,
+ NULL,
+ UINT64_MAX,
+ NULL,
+ NULL,
+ uid,
+ (gid_t) uid);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int manager_assess_image(
+ Manager *m,
+ int dir_fd,
+ const char *dir_path,
+ const char *dentry_name) {
+
+ char *luks_suffix, *directory_suffix;
+ _cleanup_free_ char *path = NULL;
+ struct stat st;
+ int r;
+
+ assert(m);
+ assert(dir_path);
+ assert(dentry_name);
+
+ luks_suffix = endswith(dentry_name, ".home");
+ if (luks_suffix)
+ directory_suffix = NULL;
+ else
+ directory_suffix = endswith(dentry_name, ".homedir");
+
+ /* Early filter out: by name */
+ if (!luks_suffix && !directory_suffix)
+ return 0;
+
+ path = path_join(dir_path, dentry_name);
+ if (!path)
+ return log_oom();
+
+ /* Follow symlinks here, to allow people to link in stuff to make them available locally. */
+ if (dir_fd >= 0)
+ r = fstatat(dir_fd, dentry_name, &st, 0);
+ else
+ r = stat(path, &st);
+ if (r < 0)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to stat directory entry '%s', ignoring: %m", dentry_name);
+
+ if (S_ISREG(st.st_mode)) {
+ _cleanup_free_ char *n = NULL, *user_name = NULL, *realm = NULL;
+
+ if (!luks_suffix)
+ return 0;
+
+ n = strndup(dentry_name, luks_suffix - dentry_name);
+ if (!n)
+ return log_oom();
+
+ r = split_user_name_realm(n, &user_name, &realm);
+ if (r == -EINVAL) /* Not the right format: ignore */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to split image name into user name/realm: %m");
+
+ return manager_add_home_by_image(m, user_name, realm, path, NULL, USER_LUKS, UID_INVALID);
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ _cleanup_free_ char *n = NULL, *user_name = NULL, *realm = NULL;
+ _cleanup_close_ int fd = -1;
+ UserStorage storage;
+
+ if (!directory_suffix)
+ return 0;
+
+ n = strndup(dentry_name, directory_suffix - dentry_name);
+ if (!n)
+ return log_oom();
+
+ r = split_user_name_realm(n, &user_name, &realm);
+ if (r == -EINVAL) /* Not the right format: ignore */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to split image name into user name/realm: %m");
+
+ if (dir_fd >= 0)
+ fd = openat(dir_fd, dentry_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+ else
+ fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to open directory '%s', ignoring: %m", path);
+
+ if (fstat(fd, &st) < 0)
+ return log_warning_errno(errno, "Failed to fstat() %s, ignoring: %m", path);
+
+ assert(S_ISDIR(st.st_mode)); /* Must hold, we used O_DIRECTORY above */
+
+ r = btrfs_is_subvol_fd(fd);
+ if (r < 0)
+ return log_warning_errno(errno, "Failed to determine whether %s is a btrfs subvolume: %m", path);
+ if (r > 0)
+ storage = USER_SUBVOLUME;
+ else {
+ struct fscrypt_policy policy;
+
+ if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) {
+
+ if (errno == ENODATA)
+ log_debug_errno(errno, "Determined %s is not fscrypt encrypted.", path);
+ else if (ERRNO_IS_NOT_SUPPORTED(errno))
+ log_debug_errno(errno, "Determined %s is not fscrypt encrypted because kernel or file system don't support it.", path);
+ else
+ log_debug_errno(errno, "FS_IOC_GET_ENCRYPTION_POLICY failed with unexpected error code on %s, ignoring: %m", path);
+
+ storage = USER_DIRECTORY;
+ } else
+ storage = USER_FSCRYPT;
+ }
+
+ return manager_add_home_by_image(m, user_name, realm, path, NULL, storage, st.st_uid);
+ }
+
+ return 0;
+}
+
+int manager_enumerate_images(Manager *m) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+
+ assert(m);
+
+ if (!m->scan_slash_home)
+ return 0;
+
+ d = opendir("/home/");
+ if (!d)
+ return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to open /home/: %m");
+
+ FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read /home/ directory: %m"))
+ (void) manager_assess_image(m, dirfd(d), "/home", de->d_name);
+
+ return 0;
+}
+
+static int manager_connect_bus(Manager *m) {
+ int r;
+
+ assert(m);
+ assert(!m->bus);
+
+ r = sd_bus_default_system(&m->bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to system bus: %m");
+
+ r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/home1", "org.freedesktop.home1.Manager", manager_vtable, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add manager object vtable: %m");
+
+ r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/home1/home", "org.freedesktop.home1.Home", home_vtable, bus_home_object_find, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add image object vtable: %m");
+
+ r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/home1/home", bus_home_node_enumerator, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add image enumerator: %m");
+
+ r = sd_bus_add_object_manager(m->bus, NULL, "/org/freedesktop/home1/home");
+ if (r < 0)
+ return log_error_errno(r, "Failed to add object manager: %m");
+
+ r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.home1", 0, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to request name: %m");
+
+ r = sd_bus_attach_event(m->bus, m->event, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach bus to event loop: %m");
+
+ (void) sd_bus_set_exit_on_disconnect(m->bus, true);
+
+ return 0;
+}
+
+static int manager_bind_varlink(Manager *m) {
+ int r;
+
+ assert(m);
+ assert(!m->varlink_server);
+
+ r = varlink_server_new(&m->varlink_server, VARLINK_SERVER_ACCOUNT_UID);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate varlink server object: %m");
+
+ varlink_server_set_userdata(m->varlink_server, m);
+
+ r = varlink_server_bind_method_many(
+ m->varlink_server,
+ "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
+ "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
+ "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register varlink methods: %m");
+
+ (void) mkdir_p("/run/systemd/userdb", 0755);
+
+ r = varlink_server_listen_address(m->varlink_server, "/run/systemd/userdb/io.systemd.Home", 0666);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to varlink socket: %m");
+
+ r = varlink_server_attach_event(m->varlink_server, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+ return 0;
+}
+
+static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
+ _cleanup_free_ void *buffer = NULL;
+ ssize_t n, m;
+
+ assert(fd >= 0);
+ assert(ret_sender);
+ assert(ret);
+
+ n = next_datagram_size_fd(fd);
+ if (n < 0)
+ return n;
+
+ buffer = malloc(n + 2);
+ if (!buffer)
+ return -ENOMEM;
+
+ if (ret_sender) {
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+ } control;
+ bool found_ucred = false;
+ struct cmsghdr *cmsg;
+ struct msghdr mh;
+ struct iovec iov;
+
+ /* Pass one extra byte, as a size check */
+ iov = IOVEC_MAKE(buffer, n + 1);
+
+ mh = (struct msghdr) {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+
+ m = recvmsg(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+ if (m < 0)
+ return -errno;
+
+ cmsg_close_all(&mh);
+
+ /* Ensure the size matches what we determined before */
+ if (m != n)
+ return -EMSGSIZE;
+
+ CMSG_FOREACH(cmsg, &mh)
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+ memcpy(ret_sender, CMSG_DATA(cmsg), sizeof(struct ucred));
+ found_ucred = true;
+ }
+
+ if (!found_ucred)
+ *ret_sender = (struct ucred) {
+ .pid = 0,
+ .uid = UID_INVALID,
+ .gid = GID_INVALID,
+ };
+ } else {
+ m = recv(fd, buffer, n + 1, MSG_DONTWAIT);
+ if (m < 0)
+ return -errno;
+
+ /* Ensure the size matches what we determined before */
+ if (m != n)
+ return -EMSGSIZE;
+ }
+
+ /* For safety reasons: let's always NUL terminate. */
+ ((char*) buffer)[n] = 0;
+ *ret = TAKE_PTR(buffer);
+
+ return 0;
+}
+
+static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_strv_free_ char **l = NULL;
+ _cleanup_free_ void *datagram = NULL;
+ struct ucred sender;
+ Manager *m = userdata;
+ ssize_t n;
+ Home *h;
+
+ assert(s);
+ assert(m);
+
+ n = read_datagram(fd, &sender, &datagram);
+ if (IN_SET(n, -EAGAIN, -EINTR))
+ return 0;
+ if (n < 0)
+ return log_error_errno(n, "Failed to read notify datagram: %m");
+
+ if (sender.pid <= 0) {
+ log_warning("Received notify datagram without valid sender PID, ignoring.");
+ return 0;
+ }
+
+ h = hashmap_get(m->homes_by_worker_pid, PID_TO_PTR(sender.pid));
+ if (!h) {
+ log_warning("Recieved notify datagram of unknown process, ignoring.");
+ return 0;
+ }
+
+ l = strv_split(datagram, "\n");
+ if (!l)
+ return log_oom();
+
+ home_process_notify(h, l);
+ return 0;
+}
+
+static int manager_listen_notify(Manager *m) {
+ _cleanup_close_ int fd = -1;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/home/notify",
+ };
+ int r;
+
+ assert(m);
+ assert(!m->notify_socket_event_source);
+
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to create listening socket: %m");
+
+ (void) mkdir_parents(sa.un.sun_path, 0755);
+ (void) sockaddr_un_unlink(&sa.un);
+
+ if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+ return log_error_errno(errno, "Failed to bind to socket: %m");
+
+ r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_io(m->event, &m->notify_socket_event_source, fd, EPOLLIN, on_notify_socket, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate event source for notify socket: %m");
+
+ (void) sd_event_source_set_description(m->notify_socket_event_source, "notify-socket");
+
+ /* Make sure we process sd_notify() before SIGCHLD for any worker, so that we always know the error
+ * number of a client before it exits. */
+ r = sd_event_source_set_priority(m->notify_socket_event_source, SD_EVENT_PRIORITY_NORMAL - 5);
+ if (r < 0)
+ return log_error_errno(r, "Failed to alter priority of NOTIFY_SOCKET event source: %m");
+
+ r = sd_event_source_set_io_fd_own(m->notify_socket_event_source, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pass ownership of notify socket: %m");
+
+ return TAKE_FD(fd);
+}
+
+static int manager_add_device(Manager *m, sd_device *d) {
+ _cleanup_free_ char *user_name = NULL, *realm = NULL, *node = NULL;
+ const char *tabletype, *parttype, *partname, *partuuid, *sysfs;
+ sd_id128_t id;
+ int r;
+
+ assert(m);
+ assert(d);
+
+ r = sd_device_get_syspath(d, &sysfs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire sysfs path of device: %m");
+
+ r = sd_device_get_property_value(d, "ID_PART_TABLE_TYPE", &tabletype);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire ID_PART_TABLE_TYPE device property, ignoring: %m");
+
+ if (!streq(tabletype, "gpt")) {
+ log_debug("Found partition (%s) on non-GPT table, ignoring.", sysfs);
+ return 0;
+ }
+
+ r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &parttype);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire ID_PART_ENTRY_TYPE device property, ignoring: %m");
+ r = sd_id128_from_string(parttype, &id);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse ID_PART_ENTRY_TYPE field '%s', ignoring: %m", parttype);
+ if (!sd_id128_equal(id, GPT_USER_HOME)) {
+ log_debug("Found partition (%s) we don't care about, ignoring.", sysfs);
+ return 0;
+ }
+
+ r = sd_device_get_property_value(d, "ID_PART_ENTRY_NAME", &partname);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to acquire ID_PART_ENTRY_NAME device property, ignoring: %m");
+
+ r = split_user_name_realm(partname, &user_name, &realm);
+ if (r == -EINVAL)
+ return log_warning_errno(r, "Found partition with correct partition type but a non-parsable partition name '%s', ignoring.", partname);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate partition name '%s': %m", partname);
+
+ r = sd_device_get_property_value(d, "ID_FS_UUID", &partuuid);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to acquire ID_FS_UUID device property, ignoring: %m");
+
+ r = sd_id128_from_string(partuuid, &id);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse ID_FS_UUID field '%s', ignoring: %m", partuuid);
+
+ if (asprintf(&node, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(id)) < 0)
+ return log_oom();
+
+ return manager_add_home_by_image(m, user_name, realm, node, sysfs, USER_LUKS, UID_INVALID);
+}
+
+static int manager_on_device(sd_device_monitor *monitor, sd_device *d, void *userdata) {
+ Manager *m = userdata;
+ int r;
+
+ assert(m);
+ assert(d);
+
+ if (device_for_action(d, DEVICE_ACTION_REMOVE)) {
+ const char *sysfs;
+ Home *h;
+
+ r = sd_device_get_syspath(d, &sysfs);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to acquire sysfs path from device: %m");
+ return 0;
+ }
+
+ log_info("block device %s has been removed.", sysfs);
+
+ /* Let's see if we previously synthesized a home record from this device, if so, let's just
+ * revalidate that. Otherwise let's revalidate them all, but asynchronously. */
+ h = hashmap_get(m->homes_by_sysfs, sysfs);
+ if (h)
+ manager_revalidate_image(m, h);
+ else
+ manager_enqueue_gc(m, NULL);
+ } else
+ (void) manager_add_device(m, d);
+
+ (void) bus_manager_emit_auto_login_changed(m);
+ return 0;
+}
+
+static int manager_watch_devices(Manager *m) {
+ int r;
+
+ assert(m);
+ assert(!m->device_monitor);
+
+ r = sd_device_monitor_new(&m->device_monitor);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate device monitor: %m");
+
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "block", NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to configure device monitor match: %m");
+
+ r = sd_device_monitor_attach_event(m->device_monitor, m->event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach device monitor to event loop: %m");
+
+ r = sd_device_monitor_start(m->device_monitor, manager_on_device, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start device monitor: %m");
+
+ return 0;
+}
+
+static int manager_enumerate_devices(Manager *m) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
+ int r;
+
+ assert(m);
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
+ if (r < 0)
+ return r;
+
+ FOREACH_DEVICE(e, d)
+ (void) manager_add_device(m, d);
+
+ return 0;
+}
+
+static int manager_load_key_pair(Manager *m) {
+ _cleanup_(fclosep) FILE *f = NULL;
+ struct stat st;
+ int r;
+
+ assert(m);
+
+ if (m->private_key) {
+ EVP_PKEY_free(m->private_key);
+ m->private_key = NULL;
+ }
+
+ r = search_and_fopen_nulstr("local.private", "re", NULL, KEY_PATHS_NULSTR, &f);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to read private key file: %m");
+
+ if (fstat(fileno(f), &st) < 0)
+ return log_error_errno(errno, "Failed to stat private key file: %m");
+
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return log_error_errno(r, "Private key file is not regular: %m");
+
+ if (st.st_uid != 0 || (st.st_mode & 0077) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Private key file is readable by more than the root user");
+
+ m->private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+ if (!m->private_key)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to load private key pair");
+
+ log_info("Successfully loaded private key pair.");
+
+ return 1;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_PKEY_CTX*, EVP_PKEY_CTX_free);
+
+static int manager_generate_key_pair(Manager *m) {
+ _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
+ _cleanup_(unlink_and_freep) char *temp_public = NULL, *temp_private = NULL;
+ _cleanup_fclose_ FILE *fpublic = NULL, *fprivate = NULL;
+ int r;
+
+ if (m->private_key) {
+ EVP_PKEY_free(m->private_key);
+ m->private_key = NULL;
+ }
+
+ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL);
+ if (!ctx)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate Ed25519 key generation context.");
+
+ if (EVP_PKEY_keygen_init(ctx) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize Ed25519 key generation context.");
+
+ log_info("Generating key pair for signing local user identity records.");
+
+ if (EVP_PKEY_keygen(ctx, &m->private_key) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate Ed25519 key pair");
+
+ log_info("Successfully created Ed25519 key pair.");
+
+ (void) mkdir_p("/var/lib/systemd/home", 0755);
+
+ /* Write out public key (note that we only do that as a help to the user, we don't make use of this ever */
+ r = fopen_temporary("/var/lib/systemd/home/local.public", &fpublic, &temp_public);
+ if (r < 0)
+ return log_error_errno(errno, "Failed ot open key file for writing: %m");
+
+ if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key.");
+
+ r = fflush_and_check(fpublic);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write private key: %m");
+
+ fpublic = safe_fclose(fpublic);
+
+ /* Write out the private key (this actually writes out both private and public, OpenSSL is confusing) */
+ r = fopen_temporary("/var/lib/systemd/home/local.private", &fprivate, &temp_private);
+ if (r < 0)
+ return log_error_errno(errno, "Failed ot open key file for writing: %m");
+
+ if (PEM_write_PrivateKey(fprivate, m->private_key, NULL, NULL, 0, NULL, 0) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write private key pair.");
+
+ r = fflush_and_check(fprivate);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write private key: %m");
+
+ fprivate = safe_fclose(fprivate);
+
+ /* Both are written now, move them into place */
+
+ if (rename(temp_public, "/var/lib/systemd/home/local.public") < 0)
+ return log_error_errno(errno, "Failed to move public key file into place: %m");
+ temp_public = mfree(temp_public);
+
+ if (rename(temp_private, "/var/lib/systemd/home/local.private") < 0) {
+ (void) unlink_noerrno("/var/lib/systemd/home/local.public"); /* try to remove the file we already created */
+ return log_error_errno(errno, "Failed to move privtate key file into place: %m");
+ }
+ temp_private = mfree(temp_private);
+
+ return 1;
+}
+
+int manager_acquire_key_pair(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Already there? */
+ if (m->private_key)
+ return 1;
+
+ /* First try to load key off disk */
+ r = manager_load_key_pair(m);
+ if (r != 0)
+ return r;
+
+ /* Didn't work, generate a new one */
+ return manager_generate_key_pair(m);
+}
+
+int manager_sign_user_record(Manager *m, UserRecord *u, UserRecord **ret, sd_bus_error *error) {
+ int r;
+
+ assert(m);
+ assert(u);
+ assert(ret);
+
+ r = manager_acquire_key_pair(m);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_KEY, "Can't sign without local key.");
+
+ return user_record_sign(u, m->private_key, ret);
+}
+
+DEFINE_PRIVATE_HASH_OPS_FULL(public_key_hash_ops, char, string_hash_func, string_compare_func, free, EVP_PKEY, EVP_PKEY_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_PKEY*, EVP_PKEY_free);
+
+static int manager_load_public_key_one(Manager *m, const char *path) {
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *fn = NULL;
+ struct stat st;
+ int r;
+
+ assert(m);
+
+ if (streq(basename(path), "local.public")) /* we already loaded the private key, which includes the public one */
+ return 0;
+
+ f = fopen(path, "re");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+
+ return log_error_errno(errno, "Failed to open public key %s: %m", path);
+ }
+
+ if (fstat(fileno(f), &st) < 0)
+ return log_error_errno(errno, "Failed to stat public key %s: %m", path);
+
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return log_error_errno(r, "Public key file %s is not a regular file: %m", path);
+
+ if (st.st_uid != 0 || (st.st_mode & 0022) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Public key file %s is writable by more than the root user, refusing.", path);
+
+ r = hashmap_ensure_allocated(&m->public_keys, &public_key_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ pkey = PEM_read_PUBKEY(f, &pkey, NULL, NULL);
+ if (!pkey)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key file %s.", path);
+
+ fn = strdup(basename(path));
+ if (!fn)
+ return log_oom();
+
+ r = hashmap_put(m->public_keys, fn, pkey);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add public key to set: %m");
+
+ TAKE_PTR(fn);
+ TAKE_PTR(pkey);
+
+ return 0;
+}
+
+static int manager_load_public_keys(Manager *m) {
+ _cleanup_strv_free_ char **files = NULL;
+ char **i;
+ int r;
+
+ assert(m);
+
+ m->public_keys = hashmap_free(m->public_keys);
+
+ r = conf_files_list_nulstr(
+ &files,
+ ".public",
+ NULL,
+ CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED,
+ KEY_PATHS_NULSTR);
+ if (r < 0)
+ return log_error_errno(r, "Failed to assemble list of public key directories: %m");
+
+ STRV_FOREACH(i, files)
+ (void) manager_load_public_key_one(m, *i);
+
+ return 0;
+}
+
+int manager_startup(Manager *m) {
+ int r;
+
+ assert(m);
+
+ r = manager_listen_notify(m);
+ if (r < 0)
+ return r;
+
+ r = manager_connect_bus(m);
+ if (r < 0)
+ return r;
+
+ r = manager_bind_varlink(m);
+ if (r < 0)
+ return r;
+
+ r = manager_load_key_pair(m); /* only try to load it, don't generate any */
+ if (r < 0)
+ return r;
+
+ r = manager_load_public_keys(m);
+ if (r < 0)
+ return r;
+
+ manager_watch_home(m);
+ (void) manager_watch_devices(m);
+
+ (void) manager_enumerate_records(m);
+ (void) manager_enumerate_images(m);
+ (void) manager_enumerate_devices(m);
+
+ /* Let's clean up home directories whose devices got removed while we were not running */
+ (void) manager_enqueue_gc(m, NULL);
+
+ return 0;
+}
+
+void manager_revalidate_image(Manager *m, Home *h) {
+ int r;
+
+ assert(m);
+ assert(h);
+
+ /* Frees an automatically discovered image, if it's synthetic and its image disappeared. Unmounts any
+ * image if it's mounted but it's image vanished. */
+
+ if (h->current_operation || !ordered_set_isempty(h->pending_operations))
+ return;
+
+ if (h->state == HOME_UNFIXATED) {
+ r = user_record_test_image_path(h->record);
+ if (r < 0)
+ log_warning_errno(r, "Can't determine if image of %s exists, freeing unfixated user: %m", h->user_name);
+ else if (r == USER_TEST_ABSENT)
+ log_info("Image for %s disappeared, freeing unfixated user.", h->user_name);
+ else
+ return;
+
+ home_free(h);
+
+ } else if (h->state < 0) {
+
+ r = user_record_test_home_directory(h->record);
+ if (r < 0) {
+ log_warning_errno(r, "Unable to determine state of home directory, ignoring: %m");
+ return;
+ }
+
+ if (r == USER_TEST_MOUNTED) {
+ r = user_record_test_image_path(h->record);
+ if (r < 0) {
+ log_warning_errno(r, "Unable to determine state of image path, ignoring: %m");
+ return;
+ }
+
+ if (r == USER_TEST_ABSENT) {
+ _cleanup_(operation_unrefp) Operation *o = NULL;
+
+ log_notice("Backing image disappeared while home directory %s was mounted, unmounting it forcibly.", h->user_name);
+ /* Wowza, the thing is mounted, but the device is gone? Act on it. */
+
+ r = home_killall(h);
+ if (r < 0)
+ log_warning_errno(r, "Failed to kill processes of user %s, ignoring: %m", h->user_name);
+
+ /* We enqueue the operation here, after all the home directory might
+ * currently already run some operation, and we can deactivate it only after
+ * that's complete. */
+ o = operation_new(OPERATION_DEACTIVATE_FORCE, NULL);
+ if (!o) {
+ log_oom();
+ return;
+ }
+
+ r = home_schedule_operation(h, o, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enqueue forced home directory %s deactivation, ignoring: %m", h->user_name);
+ }
+ }
+ }
+}
+
+int manager_gc_images(Manager *m) {
+ Home *h;
+
+ assert_se(m);
+
+ if (m->gc_focus) {
+ /* Focus on a specific home */
+
+ h = TAKE_PTR(m->gc_focus);
+ manager_revalidate_image(m, h);
+ } else {
+ /* Gc all */
+ Iterator i;
+
+ HASHMAP_FOREACH(h, m->homes_by_name, i)
+ manager_revalidate_image(m, h);
+ }
+
+ return 0;
+}
+
+static int on_deferred_rescan(sd_event_source *s, void *userdata) {
+ Manager *m = userdata;
+
+ assert(m);
+
+ m->deferred_rescan_event_source = sd_event_source_unref(m->deferred_rescan_event_source);
+
+ manager_enumerate_devices(m);
+ manager_enumerate_images(m);
+ return 0;
+}
+
+int manager_enqueue_rescan(Manager *m) {
+ int r;
+
+ assert(m);
+
+ if (m->deferred_rescan_event_source)
+ return 0;
+
+ if (!m->event)
+ return 0;
+
+ if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+ return 0;
+
+ r = sd_event_add_defer(m->event, &m->deferred_rescan_event_source, on_deferred_rescan, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate rescan event source: %m");
+
+ r = sd_event_source_set_priority(m->deferred_rescan_event_source, SD_EVENT_PRIORITY_IDLE+1);
+ if (r < 0)
+ log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+ (void) sd_event_source_set_description(m->deferred_rescan_event_source, "deferred-rescan");
+ return 1;
+}
+
+static int on_deferred_gc(sd_event_source *s, void *userdata) {
+ Manager *m = userdata;
+
+ assert(m);
+
+ m->deferred_gc_event_source = sd_event_source_unref(m->deferred_gc_event_source);
+
+ manager_gc_images(m);
+ return 0;
+}
+
+int manager_enqueue_gc(Manager *m, Home *focus) {
+ int r;
+
+ assert(m);
+
+ /* This enqueues a request to GC dead homes. It may be called with focus=NULL in which case all homes
+ * will be scanned, or with the parameter set, in which case only that home is checked. */
+
+ if (!m->event)
+ return 0;
+
+ if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+ return 0;
+
+ /* If a focus home is specified, then remember to focus just on this home. Otherwise invalidate any
+ * focus that might be set to look at all homes. */
+
+ if (m->deferred_gc_event_source) {
+ if (m->gc_focus != focus) /* not the same focus, then look at everything */
+ m->gc_focus = NULL;
+
+ return 0;
+ } else
+ m->gc_focus = focus; /* start focussed */
+
+ r = sd_event_add_defer(m->event, &m->deferred_gc_event_source, on_deferred_gc, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate gc event source: %m");
+
+ r = sd_event_source_set_priority(m->deferred_gc_event_source, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+ (void) sd_event_source_set_description(m->deferred_gc_event_source, "deferred-gc");
+ return 1;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <openssl/evp.h>
+
+#include "sd-bus.h"
+#include "sd-device.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "homed-home.h"
+#include "varlink.h"
+
+#define HOME_UID_MIN 60001
+#define HOME_UID_MAX 60513
+
+struct Manager {
+ sd_event *event;
+ sd_bus *bus;
+
+ Hashmap *polkit_registry;
+
+ Hashmap *homes_by_uid;
+ Hashmap *homes_by_name;
+ Hashmap *homes_by_worker_pid;
+ Hashmap *homes_by_sysfs;
+
+ bool scan_slash_home;
+
+ sd_event_source *inotify_event_source;
+
+ /* An even source we receieve sd_notify() messages from our worker from */
+ sd_event_source *notify_socket_event_source;
+
+ sd_device_monitor *device_monitor;
+
+ sd_event_source *deferred_rescan_event_source;
+ sd_event_source *deferred_gc_event_source;
+ sd_event_source *deferred_auto_login_event_source;
+
+ Home *gc_focus;
+
+ VarlinkServer *varlink_server;
+
+ EVP_PKEY *private_key; /* actually a pair of private and public key */
+ Hashmap *public_keys; /* key name [char*] → publick key [EVP_PKEY*] */
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
+
+int manager_augment_record_with_uid(Manager *m, UserRecord *hr);
+
+int manager_enqueue_rescan(Manager *m);
+int manager_enqueue_gc(Manager *m, Home *focus);
+
+int manager_verify_user_record(Manager *m, UserRecord *hr);
+
+int manager_acquire_key_pair(Manager *m);
+int manager_sign_user_record(Manager *m, UserRecord *u, UserRecord **ret, sd_bus_error *error);
+
+int bus_manager_emit_auto_login_changed(Manager *m);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "homed-operation.h"
+
+Operation *operation_new(OperationType type, sd_bus_message *m) {
+ Operation *o;
+
+ assert(type >= 0);
+ assert(type < _OPERATION_MAX);
+
+ o = new(Operation, 1);
+ if (!o)
+ return NULL;
+
+ *o = (Operation) {
+ .type = type,
+ .n_ref = 1,
+ .message = sd_bus_message_ref(m),
+ .send_fd = -1,
+ .result = -1,
+ };
+
+ return o;
+}
+
+static Operation *operation_free(Operation *o) {
+ int r;
+
+ if (!o)
+ return NULL;
+
+ if (o->message && o->result >= 0) {
+
+ if (o->result) {
+ /* Propagate success */
+ if (o->send_fd < 0)
+ r = sd_bus_reply_method_return(o->message, NULL);
+ else
+ r = sd_bus_reply_method_return(o->message, "h", o->send_fd);
+
+ } else {
+ /* Propagate failure */
+ if (sd_bus_error_is_set(&o->error))
+ r = sd_bus_reply_method_error(o->message, &o->error);
+ else
+ r = sd_bus_reply_method_errnof(o->message, o->ret, "Failed to execute operation: %m");
+ }
+ if (r < 0)
+ log_warning_errno(r, "Failed ot reply to %s method call, ignoring: %m", sd_bus_message_get_member(o->message));
+ }
+
+ sd_bus_message_unref(o->message);
+ user_record_unref(o->secret);
+ safe_close(o->send_fd);
+ sd_bus_error_free(&o->error);
+
+ return mfree(o);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Operation, operation, operation_free);
+
+void operation_result(Operation *o, int ret, const sd_bus_error *error) {
+ assert(o);
+
+ if (ret >= 0)
+ o->result = true;
+ else {
+ o->ret = ret;
+
+ sd_bus_error_free(&o->error);
+ sd_bus_error_copy(&o->error, error);
+
+ o->result = false;
+ }
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sd-bus.h>
+
+#include "user-record.h"
+
+typedef enum OperationType {
+ OPERATION_ACQUIRE, /* enqueued on AcquireHome() */
+ OPERATION_RELEASE, /* enqueued on ReleaseHome() */
+ OPERATION_LOCK_ALL, /* enqueued on LockAllHomes() */
+ OPERATION_PIPE_EOF, /* enqueued when we see EOF on the per-home reference pipes */
+ OPERATION_DEACTIVATE_FORCE, /* enqueued on hard $HOME unplug */
+ OPERATION_IMMEDIATE, /* this is never enqueued, it's just a marker we immediately started executing an operation without enqueuing anything first. */
+ _OPERATION_MAX,
+ _OPERATION_INVALID = -1,
+} OperationType;
+
+/* Encapsulates an operation on one or more home directories. This has two uses:
+ *
+ * 1) For queuing an operation when we need to execute one for some reason but there's already one being
+ * executed.
+ *
+ * 2) When executing an operation without enqueuing it first (OPERATION_IMMEDIATE)
+ *
+ * Note that a single operation object can encapsulate operations on multiple home directories. This is used
+ * for the LockAllHomes() operation, which is one operation but applies to all homes at once. In case the
+ * operation applies to multiple homes the reference counter is increased once for each, and thus the
+ * operation is fully completed only after it reached zero again.
+ *
+ * The object (optionally) contains a reference of the D-Bus message triggering the operation, which is
+ * replied to when the operation is fully completed, i.e. when n_ref reaches zero.
+ */
+
+typedef struct Operation {
+ unsigned n_ref;
+ OperationType type;
+ sd_bus_message *message;
+
+ UserRecord *secret;
+ int send_fd; /* pipe fd for AcquireHome() which is taken already when we start the operation */
+
+ int result; /* < 0 if not completed yet, == 0 on failure, > 0 on success */
+ sd_bus_error error;
+ int ret;
+} Operation;
+
+Operation *operation_new(OperationType type, sd_bus_message *m);
+Operation *operation_ref(Operation *operation);
+Operation *operation_unref(Operation *operation);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Operation*, operation_unref);
+
+void operation_result(Operation *o, int ret, const sd_bus_error *error);
+
+static inline Operation* operation_result_unref(Operation *o, int ret, const sd_bus_error *error) {
+ if (!o)
+ return NULL;
+
+ operation_result(o, ret, error);
+ return operation_unref(o);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "group-record.h"
+#include "homed-varlink.h"
+#include "strv.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "format-util.h"
+
+typedef struct LookupParameters {
+ const char *user_name;
+ const char *group_name;
+ union {
+ uid_t uid;
+ gid_t gid;
+ };
+ const char *service;
+} LookupParameters;
+
+static bool client_is_trusted(Varlink *link, Home *h) {
+ uid_t peer_uid;
+ int r;
+
+ assert(link);
+ assert(h);
+
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0) {
+ log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
+ return false;
+ }
+
+ return peer_uid == 0 || peer_uid == h->uid;
+}
+
+static int build_user_json(Home *h, bool trusted, JsonVariant **ret) {
+ _cleanup_(user_record_unrefp) UserRecord *augmented = NULL;
+ UserRecordLoadFlags flags;
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+ if (trusted)
+ flags |= USER_RECORD_ALLOW_PRIVILEGED;
+ else
+ flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+ r = home_augment_status(h, flags, &augmented);
+ if (r < 0)
+ return r;
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(augmented->json)),
+ JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(augmented->incomplete))));
+}
+
+static bool home_user_match_lookup_parameters(LookupParameters *p, Home *h) {
+ assert(p);
+ assert(h);
+
+ if (p->user_name && !streq(p->user_name, h->user_name))
+ return false;
+
+ if (uid_is_valid(p->uid) && h->uid != p->uid)
+ return false;
+
+ return true;
+}
+
+int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .uid = UID_INVALID,
+ };
+ Manager *m = userdata;
+ bool trusted;
+ Home *h;
+ int r;
+
+ assert(parameters);
+ assert(m);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.Home"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (uid_is_valid(p.uid))
+ h = hashmap_get(m->homes_by_uid, UID_TO_PTR(p.uid));
+ else if (p.user_name)
+ h = hashmap_get(m->homes_by_name, p.user_name);
+ else {
+ Iterator i;
+
+ /* If neither UID nor name was specified, then dump all homes. Do so with varlink_notify()
+ * for all entries but the last, so that clients can stream the results, and easily process
+ * them piecemeal. */
+
+ HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+ if (!home_user_match_lookup_parameters(&p, h))
+ continue;
+
+ if (v) {
+ /* An entry set from the previous iteration? Then send it now */
+ r = varlink_notify(link, v);
+ if (r < 0)
+ return r;
+
+ v = json_variant_unref(v);
+ }
+
+ trusted = client_is_trusted(link, h);
+
+ r = build_user_json(h, trusted, &v);
+ if (r < 0)
+ return r;
+ }
+
+ if (!v)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, v);
+ }
+
+ if (!h)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ if (!home_user_match_lookup_parameters(&p, h))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ trusted = client_is_trusted(link, h);
+
+ r = build_user_json(h, trusted, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+static int build_group_json(Home *h, JsonVariant **ret) {
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ g = group_record_new();
+ if (!g)
+ return -ENOMEM;
+
+ r = group_record_synthesize(g, h->record);
+ if (r < 0)
+ return r;
+
+ assert(!FLAGS_SET(g->mask, USER_RECORD_SECRET));
+ assert(!FLAGS_SET(g->mask, USER_RECORD_PRIVILEGED));
+
+ return json_build(ret,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(g->json))));
+}
+
+static bool home_group_match_lookup_parameters(LookupParameters *p, Home *h) {
+ assert(p);
+ assert(h);
+
+ if (p->group_name && !streq(h->user_name, p->group_name))
+ return false;
+
+ if (gid_is_valid(p->gid) && h->uid != (uid_t) p->gid)
+ return false;
+
+ return true;
+}
+
+int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ LookupParameters p = {
+ .gid = GID_INVALID,
+ };
+ Manager *m = userdata;
+ Home *h;
+ int r;
+
+ assert(parameters);
+ assert(m);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.Home"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (gid_is_valid(p.gid))
+ h = hashmap_get(m->homes_by_uid, UID_TO_PTR((uid_t) p.gid));
+ else if (p.group_name)
+ h = hashmap_get(m->homes_by_name, p.group_name);
+ else {
+ Iterator i;
+
+ HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+ if (!home_group_match_lookup_parameters(&p, h))
+ continue;
+
+ if (v) {
+ r = varlink_notify(link, v);
+ if (r < 0)
+ return r;
+
+ v = json_variant_unref(v);
+ }
+
+ r = build_group_json(h, &v);
+ if (r < 0)
+ return r;
+ }
+
+ if (!v)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, v);
+ }
+
+ if (!h)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ if (!home_group_match_lookup_parameters(&p, h))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ r = build_group_json(h, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ Manager *m = userdata;
+ LookupParameters p = {};
+ Home *h;
+ int r;
+
+ assert(parameters);
+ assert(m);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(p.service, "io.systemd.Home"))
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+ if (p.user_name) {
+ const char *last = NULL;
+ char **i;
+
+ h = hashmap_get(m->homes_by_name, p.user_name);
+ if (!h)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ if (p.group_name) {
+ if (!strv_contains(h->record->member_of, p.group_name))
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
+ }
+
+ STRV_FOREACH(i, h->record->member_of) {
+ if (last) {
+ r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
+ if (r < 0)
+ return r;
+ }
+
+ last = *i;
+ }
+
+ if (last)
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
+
+ } else if (p.group_name) {
+ const char *last = NULL;
+ Iterator i;
+
+ HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+ if (!strv_contains(h->record->member_of, p.group_name))
+ continue;
+
+ if (last) {
+ r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
+ if (r < 0)
+ return r;
+ }
+
+ last = h->user_name;
+ }
+
+ if (last)
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
+ } else {
+ const char *last_user_name = NULL, *last_group_name = NULL;
+ Iterator i;
+
+ HASHMAP_FOREACH(h, m->homes_by_name, i) {
+ char **j;
+
+ STRV_FOREACH(j, h->record->member_of) {
+
+ if (last_user_name) {
+ assert(last_group_name);
+
+ r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+
+ if (r < 0)
+ return r;
+ }
+
+ last_user_name = h->user_name;
+ last_group_name = *j;
+ }
+ }
+
+ if (last_user_name) {
+ assert(last_group_name);
+ return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+ }
+ }
+
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homed-manager.h"
+
+int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
+int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
+int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "homed-manager.h"
+#include "log.h"
+#include "main-func.h"
+#include "signal-util.h"
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ log_setup_service();
+
+ umask(0022);
+
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+ if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.Home", 1) < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
+
+ r = manager_new(&m);
+ if (r < 0)
+ return log_error_errno(r, "Could not create manager: %m");
+
+ r = manager_startup(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start up daemon: %m");
+
+ notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+ r = sd_event_loop(m->event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "homework-cifs.h"
+#include "homework-mount.h"
+#include "mount-util.h"
+#include "process-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+int home_prepare_cifs(
+ UserRecord *h,
+ bool already_activated,
+ HomeSetup *setup) {
+
+ assert(h);
+ assert(setup);
+ assert(user_record_storage(h) == USER_CIFS);
+
+ if (already_activated)
+ setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ else {
+ bool mounted = false;
+ char **pw;
+ int r;
+
+ r = home_unshare_and_mount(NULL, NULL, false);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(pw, h->password) {
+ _cleanup_(unlink_and_freep) char *p = NULL;
+ _cleanup_free_ char *options = NULL;
+ _cleanup_(fclosep) FILE *f = NULL;
+ pid_t mount_pid;
+ int exit_status;
+
+ r = fopen_temporary(NULL, &f, &p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create temporary credentials file: %m");
+
+ fprintf(f,
+ "username=%s\n"
+ "password=%s\n",
+ user_record_cifs_user_name(h),
+ *pw);
+
+ if (h->cifs_domain)
+ fprintf(f, "domain=%s\n", h->cifs_domain);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write temporary credentials file: %m");
+
+ f = safe_fclose(f);
+
+ if (asprintf(&options, "credentials=%s,uid=" UID_FMT ",forceuid,gid=" UID_FMT ",forcegid,file_mode=0%3o,dir_mode=0%3o",
+ p, h->uid, h->uid, h->access_mode, h->access_mode) < 0)
+ return log_oom();
+
+ r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child */
+ execl("/bin/mount", "/bin/mount", "-n", "-t", "cifs",
+ h->cifs_service, "/run/systemd/user-home-mount",
+ "-o", options, NULL);
+
+ log_error_errno(errno, "Failed to execute fsck: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ exit_status = wait_for_terminate_and_check("mount", mount_pid, WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS);
+ if (exit_status < 0)
+ return exit_status;
+ if (exit_status != EXIT_SUCCESS)
+ return -EPROTO;
+
+ mounted = true;
+ break;
+ }
+
+ if (!mounted)
+ return log_error_errno(ENOKEY, "Failed to mount home directory with supplied password.");
+
+ setup->root_fd = open("/run/systemd/user-home-mount", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ }
+ if (setup->root_fd < 0)
+ return log_error_errno(errno, "Failed to open home directory: %m");
+
+ return 0;
+}
+
+int home_activate_cifs(
+ UserRecord *h,
+ char ***pkcs11_decrypted_passwords,
+ UserRecord **ret_home) {
+
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ const char *hdo, *hd;
+ int r;
+
+ assert(h);
+ assert(user_record_storage(h) == USER_CIFS);
+ assert(ret_home);
+
+ if (!h->cifs_service)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
+
+ assert_se(hdo = user_record_home_directory(h));
+ hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
+
+ r = home_prepare_cifs(h, false, &setup);
+ if (r < 0)
+ return r;
+
+ r = home_refresh(h, &setup, NULL, pkcs11_decrypted_passwords, NULL, &new_home);
+ if (r < 0)
+ return r;
+
+ setup.root_fd = safe_close(setup.root_fd);
+
+ r = home_move_mount(NULL, hd);
+ if (r < 0)
+ return r;
+
+ setup.undo_mount = false;
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 1;
+}
+
+int home_create_cifs(UserRecord *h, UserRecord **ret_home) {
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ _cleanup_(closedirp) DIR *d = NULL;
+ int r, copy;
+
+ assert(h);
+ assert(user_record_storage(h) == USER_CIFS);
+ assert(ret_home);
+
+ if (!h->cifs_service)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
+
+ if (access("/sbin/mount.cifs", F_OK) < 0) {
+ if (errno == ENOENT)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), "/sbin/mount.cifs is missing.");
+
+ return log_error_errno(errno, "Unable to detect whether /sbin/mount.cifs exists: %m");
+ }
+
+ r = home_prepare_cifs(h, false, &setup);
+ if (r < 0)
+ return r;
+
+ copy = fcntl(setup.root_fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ d = fdopendir(copy);
+ if (!d) {
+ safe_close(copy);
+ return -errno;
+ }
+
+ errno = 0;
+ if (readdir_no_dot(d))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTEMPTY), "Selected CIFS directory not empty, refusing.");
+ if (errno != 0)
+ return log_error_errno(errno, "Failed to detect if CIFS directory is empty: %m");
+
+ r = home_populate(h, setup.root_fd);
+ if (r < 0)
+ return r;
+
+ r = home_sync_and_statfs(setup.root_fd, NULL);
+ if (r < 0)
+ return r;
+
+ r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET, &new_home);
+ if (r < 0)
+ return log_error_errno(r, "Failed to clone record: %m");
+
+ r = user_record_add_binding(
+ new_home,
+ USER_CIFS,
+ NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ NULL,
+ NULL,
+ UINT64_MAX,
+ NULL,
+ NULL,
+ h->uid,
+ (gid_t) h->uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add binding to record: %m");
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homework.h"
+#include "user-record.h"
+
+int home_prepare_cifs(UserRecord *h, bool already_activated, HomeSetup *setup);
+
+int home_activate_cifs(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+
+int home_create_cifs(UserRecord *h, UserRecord **ret_home);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mount.h>
+
+#include "btrfs-util.h"
+#include "fd-util.h"
+#include "homework-directory.h"
+#include "homework-quota.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tmpfile-util.h"
+#include "umask-util.h"
+
+int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup) {
+ assert(h);
+ assert(setup);
+
+ setup->root_fd = open(user_record_image_path(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (setup->root_fd < 0)
+ return log_error_errno(errno, "Failed to open home directory: %m");
+
+ return 0;
+}
+
+int home_activate_directory(
+ UserRecord *h,
+ char ***pkcs11_decrypted_passwords,
+ UserRecord **ret_home) {
+
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ const char *hdo, *hd, *ipo, *ip;
+ int r;
+
+ assert(h);
+ assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
+ assert(ret_home);
+
+ assert_se(ipo = user_record_image_path(h));
+ ip = strdupa(ipo); /* copy out, since reconciliation might cause changing of the field */
+
+ assert_se(hdo = user_record_home_directory(h));
+ hd = strdupa(hdo);
+
+ r = home_prepare(h, false, pkcs11_decrypted_passwords, &setup, &header_home);
+ if (r < 0)
+ return r;
+
+ r = home_refresh(h, &setup, header_home, pkcs11_decrypted_passwords, NULL, &new_home);
+ if (r < 0)
+ return r;
+
+ setup.root_fd = safe_close(setup.root_fd);
+
+ /* Create mount point to mount over if necessary */
+ if (!path_equal(ip, hd))
+ (void) mkdir_p(hd, 0700);
+
+ /* Create a mount point (even if the directory is already placed correctly), as a way to indicate
+ * this mount point is now "activated". Moreover, we want to set per-user
+ * MS_NOSUID/MS_NOEXEC/MS_NODEV. */
+ r = mount_verbose(LOG_ERR, ip, hd, NULL, MS_BIND, NULL);
+ if (r < 0)
+ return r;
+
+ r = mount_verbose(LOG_ERR, NULL, hd, NULL, MS_BIND|MS_REMOUNT|user_record_mount_flags(h), NULL);
+ if (r < 0) {
+ (void) umount_verbose(hd);
+ return r;
+ }
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 0;
+}
+
+int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
+ _cleanup_(rm_rf_subvolume_and_freep) char *temporary = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ _cleanup_close_ int root_fd = -1;
+ _cleanup_free_ char *d = NULL;
+ const char *ip;
+ int r;
+
+ assert(h);
+ assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME));
+ assert(ret_home);
+
+ assert_se(ip = user_record_image_path(h));
+
+ r = tempfn_random(ip, "homework", &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate temporary directory: %m");
+
+ (void) mkdir_parents(d, 0755);
+
+ switch (user_record_storage(h)) {
+
+ case USER_SUBVOLUME:
+ RUN_WITH_UMASK(0077)
+ r = btrfs_subvol_make(d);
+
+ if (r >= 0) {
+ log_info("Subvolume created.");
+
+ if (h->disk_size != UINT64_MAX) {
+
+ /* Enable quota for the subvolume we just created. Note we don't check for
+ * errors here and only log about debug level about this. */
+ r = btrfs_quota_enable(d, true);
+ if (r < 0)
+ log_debug_errno(r, "Failed to enable quota on %s, ignoring: %m", d);
+
+ r = btrfs_subvol_auto_qgroup(d, 0, false);
+ if (r < 0)
+ log_debug_errno(r, "Failed to set up automatic quota group on %s, ignoring: %m", d);
+
+ /* Actually configure the quota. We also ignore errors here, but we do log
+ * about them loudly, to keep things discoverable even though we don't
+ * consider lacking quota support in kernel fatal. */
+ (void) home_update_quota_btrfs(h, d);
+ }
+
+ break;
+ }
+ if (r != -ENOTTY)
+ return log_error_errno(r, "Failed to create temporary home directory subvolume %s: %m", d);
+
+ log_info("Creating subvolume %s is not supported, as file system does not support subvolumes. Falling back to regular directory.", d);
+ _fallthrough_;
+
+ case USER_DIRECTORY:
+
+ if (mkdir(d, 0700) < 0)
+ return log_error_errno(errno, "Failed to create temporary home directory %s: %m", d);
+
+ (void) home_update_quota_classic(h, d);
+ break;
+
+ default:
+ assert_not_reached("unexpected storage");
+ }
+
+ temporary = TAKE_PTR(d); /* Needs to be destroyed now */
+
+ root_fd = open(temporary, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (root_fd < 0)
+ return log_error_errno(errno, "Failed to open temporary home directory: %m");
+
+ r = home_populate(h, root_fd);
+ if (r < 0)
+ return r;
+
+ r = home_sync_and_statfs(root_fd, NULL);
+ if (r < 0)
+ return r;
+
+ r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET, &new_home);
+ if (r < 0)
+ return log_error_errno(r, "Failed to clone record: %m");
+
+ r = user_record_add_binding(
+ new_home,
+ user_record_storage(h),
+ ip,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ NULL,
+ NULL,
+ UINT64_MAX,
+ NULL,
+ NULL,
+ h->uid,
+ (gid_t) h->uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add binding to record: %m");
+
+ if (rename(temporary, ip) < 0)
+ return log_error_errno(errno, "Failed to rename %s to %s: %m", temporary, ip);
+
+ temporary = mfree(temporary);
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 0;
+}
+
+int home_resize_directory(
+ UserRecord *h,
+ bool already_activated,
+ char ***pkcs11_decrypted_passwords,
+ HomeSetup *setup,
+ UserRecord **ret_home) {
+
+ _cleanup_(user_record_unrefp) UserRecord *embedded_home = NULL, *new_home = NULL;
+ int r;
+
+ assert(h);
+ assert(setup);
+ assert(ret_home);
+ assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
+
+ r = home_prepare(h, already_activated, pkcs11_decrypted_passwords, setup, NULL);
+ if (r < 0)
+ return r;
+
+ r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ if (r < 0)
+ return r;
+
+ r = home_update_quota_auto(h, NULL);
+ if (ERRNO_IS_NOT_SUPPORTED(r))
+ return -ESOCKTNOSUPPORT; /* make recognizable */
+ if (r < 0)
+ return r;
+
+ r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+ if (r < 0)
+ return r;
+
+ r = home_extend_embedded_identity(new_home, h, setup);
+ if (r < 0)
+ return r;
+
+ r = home_sync_and_statfs(setup->root_fd, NULL);
+ if (r < 0)
+ return r;
+
+ r = home_setup_undo(setup);
+ if (r < 0)
+ return r;
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homework.h"
+#include "user-record.h"
+
+int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup);
+int home_activate_directory(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home);
+int home_resize_directory(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/fs.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <sys/ioctl.h>
+#include <sys/xattr.h>
+
+#include "errno-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "homework-fscrypt.h"
+#include "homework-quota.h"
+#include "memory-util.h"
+#include "missing_keyctl.h"
+#include "missing_syscall.h"
+#include "mkdir.h"
+#include "nulstr-util.h"
+#include "openssl-util.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "rm-rf.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "user-util.h"
+#include "xattr-util.h"
+
+static int fscrypt_upload_volume_key(
+ const uint8_t key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
+ const void *volume_key,
+ size_t volume_key_size,
+ key_serial_t where) {
+
+ _cleanup_free_ char *hex = NULL;
+ const char *description;
+ struct fscrypt_key key;
+ key_serial_t serial;
+
+ assert(key_descriptor);
+ assert(volume_key);
+ assert(volume_key_size > 0);
+
+ if (volume_key_size > sizeof(key.raw))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume key too long.");
+
+ hex = hexmem(key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
+ if (!hex)
+ return log_oom();
+
+ description = strjoina("fscrypt:", hex);
+
+ key = (struct fscrypt_key) {
+ .size = volume_key_size,
+ };
+ memcpy(key.raw, volume_key, volume_key_size);
+
+ /* Upload to the kernel */
+ serial = add_key("logon", description, &key, sizeof(key), where);
+ explicit_bzero_safe(&key, sizeof(key));
+
+ if (serial < 0)
+ return log_error_errno(errno, "Failed to install master key in keyring: %m");
+
+ log_info("Uploaded encryption key to kernel.");
+
+ return 0;
+}
+
+static void calculate_key_descriptor(
+ const void *key,
+ size_t key_size,
+ uint8_t ret_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE]) {
+
+ uint8_t hashed[512 / 8] = {}, hashed2[512 / 8] = {};
+
+ /* Derive the key descriptor from the volume key via double SHA512, in order to be compatible with e4crypt */
+
+ assert_se(SHA512(key, key_size, hashed) == hashed);
+ assert_se(SHA512(hashed, sizeof(hashed), hashed2) == hashed2);
+
+ assert_cc(sizeof(hashed2) >= FS_KEY_DESCRIPTOR_SIZE);
+
+ memcpy(ret_key_descriptor, hashed2, FS_KEY_DESCRIPTOR_SIZE);
+}
+
+static int fscrypt_slot_try_one(
+ const char *password,
+ const void *salt, size_t salt_size,
+ const void *encrypted, size_t encrypted_size,
+ const uint8_t match_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
+ void **ret_decrypted, size_t *ret_decrypted_size) {
+
+
+ _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+ _cleanup_(erase_and_freep) void *decrypted = NULL;
+ uint8_t key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+ int decrypted_size_out1, decrypted_size_out2;
+ uint8_t derived[512 / 8] = {};
+ size_t decrypted_size;
+ const EVP_CIPHER *cc;
+ int r;
+
+ assert(password);
+ assert(salt);
+ assert(salt_size > 0);
+ assert(encrypted);
+ assert(encrypted_size > 0);
+ assert(match_key_descriptor);
+
+ /* Our construction is like this:
+ *
+ * 1. In each key slot we store a salt value plus the encrypted volume key
+ *
+ * 2. Unlocking is via calculating PBKDF2-HMAC-SHA512 of the supplied password (in combination with
+ * the salt), then using the first 256 bit of the hash as key for decrypting the encrypted
+ * volume key in AES256 counter mode.
+ *
+ * 3. Writing a password is similar: calculate PBKDF2-HMAC-SHA512 of the supplied password (in
+ * combination with the salt), then encrypt the volume key in AES256 counter mode with the
+ * resulting hash.
+ */
+
+ if (PKCS5_PBKDF2_HMAC(
+ password, strlen(password),
+ salt, salt_size,
+ 0xFFFF, EVP_sha512(),
+ sizeof(derived), derived) != 1) {
+ r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
+ goto finish;
+ }
+
+ context = EVP_CIPHER_CTX_new();
+ if (!context) {
+ r = log_oom();
+ goto finish;
+ }
+
+ /* We use AES256 in counter mode */
+ assert_se(cc = EVP_aes_256_ctr());
+
+ /* We only use the first half of the derived key */
+ assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
+
+ if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
+ goto finish;
+ }
+
+ /* Flush out the derived key now, we don't need it anymore */
+ explicit_bzero_safe(derived, sizeof(derived));
+
+ decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2;
+ decrypted = malloc(decrypted_size);
+ if (!decrypted)
+ return log_oom();
+
+ if (EVP_DecryptUpdate(context, (uint8_t*) decrypted, &decrypted_size_out1, encrypted, encrypted_size) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decrypt volume key.");
+
+ assert((size_t) decrypted_size_out1 <= decrypted_size);
+
+ if (EVP_DecryptFinal_ex(context, (uint8_t*) decrypted_size + decrypted_size_out1, &decrypted_size_out2) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish decryption of volume key.");
+
+ assert((size_t) decrypted_size_out1 + (size_t) decrypted_size_out2 < decrypted_size);
+ decrypted_size = (size_t) decrypted_size_out1 + (size_t) decrypted_size_out2;
+
+ calculate_key_descriptor(decrypted, decrypted_size, key_descriptor);
+
+ if (memcmp(key_descriptor, match_key_descriptor, FS_KEY_DESCRIPTOR_SIZE) != 0)
+ return -ENOANO; /* don't log here */
+
+ r = fscrypt_upload_volume_key(key_descriptor, decrypted, decrypted_size, KEY_SPEC_THREAD_KEYRING);
+ if (r < 0)
+ return r;
+
+ if (ret_decrypted)
+ *ret_decrypted = TAKE_PTR(decrypted);
+ if (ret_decrypted_size)
+ *ret_decrypted_size = decrypted_size;
+
+ return 0;
+
+finish:
+ explicit_bzero_safe(derived, sizeof(derived));
+ return r;
+}
+
+static int fscrypt_slot_try_many(
+ char **passwords,
+ const void *salt, size_t salt_size,
+ const void *encrypted, size_t encrypted_size,
+ const uint8_t match_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
+ void **ret_decrypted, size_t *ret_decrypted_size) {
+
+ char **i;
+ int r;
+
+ STRV_FOREACH(i, passwords) {
+ r = fscrypt_slot_try_one(*i, salt, salt_size, encrypted, encrypted_size, match_key_descriptor, ret_decrypted, ret_decrypted_size);
+ if (r != -ENOANO)
+ return r;
+ }
+
+ return -ENOANO;
+}
+
+static int fscrypt_setup(
+ char **pkcs11_decrypted_passwords,
+ char **password,
+ HomeSetup *setup,
+ void **ret_volume_key,
+ size_t *ret_volume_key_size) {
+
+ _cleanup_free_ char *xattr_buf = NULL;
+ const char *xa;
+ int r;
+
+ assert(setup);
+ assert(setup->root_fd >= 0);
+
+ r = flistxattr_malloc(setup->root_fd, &xattr_buf);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+
+ NULSTR_FOREACH(xa, xattr_buf) {
+ _cleanup_free_ void *salt = NULL, *encrypted = NULL;
+ _cleanup_free_ char *value = NULL;
+ size_t salt_size, encrypted_size;
+ const char *nr, *e;
+ int n;
+
+ /* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32bit unsigned integer */
+ nr = startswith(xa, "trusted.fscrypt_slot");
+ if (!nr)
+ continue;
+ if (safe_atou32(nr, NULL) < 0)
+ continue;
+
+ n = fgetxattr_malloc(setup->root_fd, xa, &value);
+ if (n == -ENODATA) /* deleted by now? */
+ continue;
+ if (n < 0)
+ return log_error_errno(n, "Failed to read %s xattr: %m", xa);
+
+ e = memchr(value, ':', n);
+ if (!e)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "xattr %s lacks ':' separator: %m", xa);
+
+ r = unbase64mem(value, e - value, &salt, &salt_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to decode salt of %s: %m", xa);
+ r = unbase64mem(e+1, n - (e - value) - 1, &encrypted, &encrypted_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to decode encrypted key of %s: %m", xa);
+
+ r = fscrypt_slot_try_many(
+ pkcs11_decrypted_passwords,
+ salt, salt_size,
+ encrypted, encrypted_size,
+ setup->fscrypt_key_descriptor,
+ ret_volume_key, ret_volume_key_size);
+ if (r == -ENOANO)
+ r = fscrypt_slot_try_many(
+ password,
+ salt, salt_size,
+ encrypted, encrypted_size,
+ setup->fscrypt_key_descriptor,
+ ret_volume_key, ret_volume_key_size);
+ if (r < 0) {
+ if (r != -ENOANO)
+ return r;
+ } else
+ return 0;
+ }
+
+ return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to set up home directory with provided passwords.");
+}
+
+int home_prepare_fscrypt(
+ UserRecord *h,
+ bool already_activated,
+ char ***pkcs11_decrypted_passwords,
+ HomeSetup *setup) {
+
+ _cleanup_(erase_and_freep) void *volume_key = NULL;
+ struct fscrypt_policy policy = {};
+ size_t volume_key_size = 0;
+ const char *ip;
+ int r;
+
+ assert(h);
+ assert(setup);
+ assert(user_record_storage(h) == USER_FSCRYPT);
+
+ assert_se(ip = user_record_image_path(h));
+
+ setup->root_fd = open(ip, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (setup->root_fd < 0)
+ return log_error_errno(errno, "Failed to open home directory: %m");
+
+ if (ioctl(setup->root_fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) {
+ if (errno == ENODATA)
+ return log_error_errno(errno, "Home directory %s is not encrypted.", ip);
+ if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+ log_error_errno(errno, "File system does not support fscrypt: %m");
+ return -ENOLINK; /* make recognizable */
+ }
+ return log_error_errno(errno, "Failed to acquire encryption policy of %s: %m", ip);
+ }
+
+ memcpy(setup->fscrypt_key_descriptor, policy.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
+
+ r = fscrypt_setup(
+ pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+ h->password,
+ setup,
+ &volume_key,
+ &volume_key_size);
+ if (r < 0)
+ return r;
+
+ /* Also install the access key in the user's own keyring */
+
+ if (uid_is_valid(h->uid)) {
+ r = safe_fork("(sd-addkey)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed install encryption key in user's keyring: %m");
+ if (r == 0) {
+ gid_t gid;
+
+ /* Child */
+
+ gid = user_record_gid(h);
+ if (setresgid(gid, gid, gid) < 0) {
+ log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setgroups(0, NULL) < 0) {
+ log_error_errno(errno, "Failed to reset auxiliary groups list: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setresuid(h->uid, h->uid, h->uid) < 0) {
+ log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", h->uid);
+ _exit(EXIT_FAILURE);
+ }
+
+ r = fscrypt_upload_volume_key(
+ setup->fscrypt_key_descriptor,
+ volume_key,
+ volume_key_size,
+ KEY_SPEC_USER_KEYRING);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+ }
+
+ return 0;
+}
+
+static int fscrypt_slot_set(
+ int root_fd,
+ const void *volume_key,
+ size_t volume_key_size,
+ const char *password,
+ uint32_t nr) {
+
+ _cleanup_free_ char *salt_base64 = NULL, *encrypted_base64 = NULL, *joined = NULL;
+ char label[STRLEN("trusted.fscrypt_slot") + DECIMAL_STR_MAX(nr) + 1];
+ _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+ int r, encrypted_size_out1, encrypted_size_out2;
+ uint8_t salt[64], derived[512 / 8] = {};
+ _cleanup_free_ void *encrypted = NULL;
+ const EVP_CIPHER *cc;
+ size_t encrypted_size;
+
+ r = genuine_random_bytes(salt, sizeof(salt), RANDOM_BLOCK);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate salt: %m");
+
+ if (PKCS5_PBKDF2_HMAC(
+ password, strlen(password),
+ salt, sizeof(salt),
+ 0xFFFF, EVP_sha512(),
+ sizeof(derived), derived) != 1) {
+ r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
+ goto finish;
+ }
+
+ context = EVP_CIPHER_CTX_new();
+ if (!context) {
+ r = log_oom();
+ goto finish;
+ }
+
+ /* We use AES256 in counter mode */
+ cc = EVP_aes_256_ctr();
+
+ /* We only use the first half of the derived key */
+ assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
+
+ if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
+ goto finish;
+ }
+
+ /* Flush out the derived key now, we don't need it anymore */
+ explicit_bzero_safe(derived, sizeof(derived));
+
+ encrypted_size = volume_key_size + EVP_CIPHER_key_length(cc) * 2;
+ encrypted = malloc(encrypted_size);
+ if (!encrypted)
+ return log_oom();
+
+ if (EVP_EncryptUpdate(context, (uint8_t*) encrypted, &encrypted_size_out1, volume_key, volume_key_size) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to encrypt volume key.");
+
+ assert((size_t) encrypted_size_out1 <= encrypted_size);
+
+ if (EVP_EncryptFinal_ex(context, (uint8_t*) encrypted_size + encrypted_size_out1, &encrypted_size_out2) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish encryption of volume key.");
+
+ assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 < encrypted_size);
+ encrypted_size = (size_t) encrypted_size_out1 + (size_t) encrypted_size_out2;
+
+ r = base64mem(salt, sizeof(salt), &salt_base64);
+ if (r < 0)
+ return log_oom();
+
+ r = base64mem(encrypted, encrypted_size, &encrypted_base64);
+ if (r < 0)
+ return log_oom();
+
+ joined = strjoin(salt_base64, ":", encrypted_base64);
+ if (!joined)
+ return log_oom();
+
+ xsprintf(label, "trusted.fscrypt_slot%" PRIu32, nr);
+ if (fsetxattr(root_fd, label, joined, strlen(joined), 0) < 0)
+ return log_error_errno(errno, "Failed to write xattr %s: %m", label);
+
+ log_info("Written key slot %s.", label);
+
+ return 0;
+
+finish:
+ explicit_bzero_safe(derived, sizeof(derived));
+ return r;
+}
+
+int home_create_fscrypt(
+ UserRecord *h,
+ char **effective_passwords,
+ UserRecord **ret_home) {
+
+ _cleanup_(rm_rf_physical_and_freep) char *temporary = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ _cleanup_(erase_and_freep) void *volume_key = NULL;
+ struct fscrypt_policy policy = {};
+ size_t volume_key_size = 512 / 8;
+ _cleanup_close_ int root_fd = -1;
+ _cleanup_free_ char *d = NULL;
+ uint32_t nr = 0;
+ const char *ip;
+ char **i;
+ int r;
+
+ assert(h);
+ assert(user_record_storage(h) == USER_FSCRYPT);
+ assert(ret_home);
+
+ assert_se(ip = user_record_image_path(h));
+
+ r = tempfn_random(ip, "homework", &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate temporary directory: %m");
+
+ (void) mkdir_parents(d, 0755);
+
+ if (mkdir(d, 0700) < 0)
+ return log_error_errno(errno, "Failed to create temporary home directory %s: %m", d);
+
+ temporary = TAKE_PTR(d); /* Needs to be destroyed now */
+
+ root_fd = open(temporary, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (root_fd < 0)
+ return log_error_errno(errno, "Failed to open temporary home directory: %m");
+
+ if (ioctl(root_fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+ log_error_errno(errno, "File system does not support fscrypt: %m");
+ return -ENOLINK; /* make recognizable */
+ }
+ if (errno != ENODATA)
+ return log_error_errno(errno, "Failed to get fscrypt policy of directory: %m");
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Parent of %s already encrypted, refusing.", d);
+
+ volume_key = malloc(volume_key_size);
+ if (!volume_key)
+ return log_oom();
+
+ r = genuine_random_bytes(volume_key, volume_key_size, RANDOM_BLOCK);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire volume key: %m");
+
+ log_info("Generated volume key of size %zu.", volume_key_size);
+
+ policy = (struct fscrypt_policy) {
+ .contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS,
+ .filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS,
+ .flags = FS_POLICY_FLAGS_PAD_32,
+ };
+
+ calculate_key_descriptor(volume_key, volume_key_size, policy.master_key_descriptor);
+
+ r = fscrypt_upload_volume_key(policy.master_key_descriptor, volume_key, volume_key_size, KEY_SPEC_THREAD_KEYRING);
+ if (r < 0)
+ return r;
+
+ log_info("Uploaded volume key to kernel.");
+
+ if (ioctl(root_fd, FS_IOC_SET_ENCRYPTION_POLICY, &policy) < 0)
+ return log_error_errno(errno, "Failed to set fscrypt policy on directory: %m");
+
+ log_info("Encryption policy set.");
+
+ STRV_FOREACH(i, effective_passwords) {
+ r = fscrypt_slot_set(root_fd, volume_key, volume_key_size, *i, nr);
+ if (r < 0)
+ return r;
+
+ nr++;
+ }
+
+ (void) home_update_quota_classic(h, temporary);
+
+ r = home_populate(h, root_fd);
+ if (r < 0)
+ return r;
+
+ r = home_sync_and_statfs(root_fd, NULL);
+ if (r < 0)
+ return r;
+
+ r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET, &new_home);
+ if (r < 0)
+ return log_error_errno(r, "Failed to clone record: %m");
+
+ r = user_record_add_binding(
+ new_home,
+ USER_FSCRYPT,
+ ip,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ NULL,
+ NULL,
+ UINT64_MAX,
+ NULL,
+ NULL,
+ h->uid,
+ (gid_t) h->uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add binding to record: %m");
+
+ if (rename(temporary, ip) < 0)
+ return log_error_errno(errno, "Failed to rename %s to %s: %m", temporary, ip);
+
+ temporary = mfree(temporary);
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 0;
+}
+
+int home_passwd_fscrypt(
+ UserRecord *h,
+ HomeSetup *setup,
+ char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
+ char **effective_passwords /* new passwords */) {
+
+ _cleanup_(erase_and_freep) void *volume_key = NULL;
+ _cleanup_free_ char *xattr_buf = NULL;
+ size_t volume_key_size = 0;
+ uint32_t slot = 0;
+ const char *xa;
+ char **p;
+ int r;
+
+ assert(h);
+ assert(user_record_storage(h) == USER_FSCRYPT);
+ assert(setup);
+
+ r = fscrypt_setup(
+ pkcs11_decrypted_passwords,
+ h->password,
+ setup,
+ &volume_key,
+ &volume_key_size);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, effective_passwords) {
+ r = fscrypt_slot_set(setup->root_fd, volume_key, volume_key_size, *p, slot);
+ if (r < 0)
+ return r;
+
+ slot++;
+ }
+
+ r = flistxattr_malloc(setup->root_fd, &xattr_buf);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+
+ NULSTR_FOREACH(xa, xattr_buf) {
+ const char *nr;
+ uint32_t z;
+
+ /* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32bit unsigned integer */
+ nr = startswith(xa, "trusted.fscrypt_slot");
+ if (!nr)
+ continue;
+ if (safe_atou32(nr, &z) < 0)
+ continue;
+
+ if (z < slot)
+ continue;
+
+ if (fremovexattr(setup->root_fd, xa) < 0)
+
+ if (errno != ENODATA)
+ log_warning_errno(errno, "Failed to remove xattr %s: %m", xa);
+ }
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homework.h"
+#include "user-record.h"
+
+int home_prepare_fscrypt(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup);
+int home_create_fscrypt(UserRecord *h, char **effective_passwords, UserRecord **ret_home);
+
+int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <libfdisk.h>
+#include <linux/loop.h>
+#include <poll.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include "blkid-util.h"
+#include "blockdev-util.h"
+#include "chattr-util.h"
+#include "dm-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "fsck-util.h"
+#include "homework-luks.h"
+#include "homework-mount.h"
+#include "id128-util.h"
+#include "io-util.h"
+#include "memory-util.h"
+#include "missing_magic.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "openssl-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "resize-fs.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+/* Round down to the nearest 1K size. Note that Linux generally handles block devices with 512 blocks only,
+ * but actually doesn't accept uneven numbers in many cases. To avoid any confusion around this we'll
+ * strictly round disk sizes down to the next 1K boundary.*/
+#define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(1023))
+
+static bool supported_fstype(const char *fstype) {
+ /* Limit the set of supported file systems a bit, as protection against little tested kernel file
+ * systems. Also, we only support the resize ioctls for these file systems. */
+ return STR_IN_SET(fstype, "ext4", "btrfs", "xfs");
+}
+
+static int probe_file_system_by_fd(
+ int fd,
+ char **ret_fstype,
+ sd_id128_t *ret_uuid) {
+
+ _cleanup_(blkid_free_probep) blkid_probe b = NULL;
+ _cleanup_free_ char *s = NULL;
+ const char *fstype = NULL, *uuid = NULL;
+ sd_id128_t id;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret_fstype);
+ assert(ret_uuid);
+
+ b = blkid_new_probe();
+ if (!b)
+ return -ENOMEM;
+
+ errno = 0;
+ r = blkid_probe_set_device(b, fd, 0, 0);
+ if (r != 0)
+ return errno > 0 ? -errno : -ENOMEM;
+
+ (void) blkid_probe_enable_superblocks(b, 1);
+ (void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_UUID);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+ return -ENOPKG;
+ if (r != 0)
+ return errno > 0 ? -errno : -EIO;
+
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ if (!fstype)
+ return -ENOPKG;
+
+ (void) blkid_probe_lookup_value(b, "UUID", &uuid, NULL);
+ if (!uuid)
+ return -ENOPKG;
+
+ r = sd_id128_from_string(uuid, &id);
+ if (r < 0)
+ return r;
+
+ s = strdup(fstype);
+ if (!s)
+ return -ENOMEM;
+
+ *ret_fstype = TAKE_PTR(s);
+ *ret_uuid = id;
+
+ return 0;
+}
+
+static int probe_file_system_by_path(const char *path, char **ret_fstype, sd_id128_t *ret_uuid) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (fd < 0)
+ return -errno;
+
+ return probe_file_system_by_fd(fd, ret_fstype, ret_uuid);
+}
+
+static int block_get_size_by_fd(int fd, uint64_t *ret) {
+ struct stat st;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISBLK(st.st_mode))
+ return -ENOTBLK;
+
+ if (ioctl(fd, BLKGETSIZE64, ret) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int block_get_size_by_path(const char *path, uint64_t *ret) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (fd < 0)
+ return -errno;
+
+ return block_get_size_by_fd(fd, ret);
+}
+
+static int run_fsck(const char *node, const char *fstype) {
+ int r, exit_status;
+ pid_t fsck_pid;
+
+ assert(node);
+ assert(fstype);
+
+ r = fsck_exists(fstype);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if fsck for file system %s exists: %m", fstype);
+ if (r == 0) {
+ log_warning("No fsck for file system %s installed, ignoring.", fstype);
+ return 0;
+ }
+
+ r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &fsck_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child */
+ execl("/sbin/fsck", "/sbin/fsck", "-aTl", node, NULL);
+ log_error_errno(errno, "Failed to execute fsck: %m");
+ _exit(FSCK_OPERATIONAL_ERROR);
+ }
+
+ exit_status = wait_for_terminate_and_check("fsck", fsck_pid, WAIT_LOG_ABNORMAL);
+ if (exit_status < 0)
+ return exit_status;
+ if ((exit_status & ~FSCK_ERROR_CORRECTED) != 0) {
+ log_warning("fsck failed with exit status %i.", exit_status);
+
+ if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "File system is corrupted, refusing.");
+
+ log_warning("Ignoring fsck error.");
+ }
+
+ log_info("File system check completed.");
+
+ return 1;
+}
+
+static int luks_try_passwords(
+ struct crypt_device *cd,
+ char **passwords,
+ void *volume_key,
+ size_t *volume_key_size) {
+
+ char **pp;
+ int r;
+
+ assert(cd);
+
+ STRV_FOREACH(pp, passwords) {
+ size_t vks = *volume_key_size;
+
+ r = crypt_volume_key_get(
+ cd,
+ CRYPT_ANY_SLOT,
+ volume_key,
+ &vks,
+ *pp,
+ strlen(*pp));
+ if (r >= 0) {
+ *volume_key_size = vks;
+ return 0;
+ }
+
+ log_debug_errno(r, "Password %zu didn't work for unlocking LUKS superblock: %m", (size_t) (pp - passwords));
+ }
+
+ return -ENOKEY;
+}
+
+static int luks_setup(
+ const char *node,
+ const char *dm_name,
+ sd_id128_t uuid,
+ const char *cipher,
+ const char *cipher_mode,
+ uint64_t volume_key_size,
+ char **passwords,
+ char **pkcs11_decrypted_passwords,
+ bool discard,
+ struct crypt_device **ret,
+ sd_id128_t *ret_found_uuid,
+ void **ret_volume_key,
+ size_t *ret_volume_key_size) {
+
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_(erase_and_freep) void *vk = NULL;
+ sd_id128_t p;
+ size_t vks;
+ int r;
+
+ assert(node);
+ assert(dm_name);
+ assert(ret);
+
+ r = crypt_init(&cd, node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
+
+ crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+ r = crypt_load(cd, CRYPT_LUKS2, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load LUKS superblock: %m");
+
+ r = crypt_get_volume_key_size(cd);
+ if (r <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
+ vks = (size_t) r;
+
+ if (!sd_id128_is_null(uuid) || ret_found_uuid) {
+ const char *s;
+
+ s = crypt_get_uuid(cd);
+ if (!s)
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID.");
+
+ r = sd_id128_from_string(s, &p);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has invalid UUID.");
+
+ /* Check that the UUID matches, if specified */
+ if (!sd_id128_is_null(uuid) &&
+ !sd_id128_equal(uuid, p))
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has wrong UUID.");
+ }
+
+ if (cipher && !streq_ptr(cipher, crypt_get_cipher(cd)))
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher.");
+
+ if (cipher_mode && !streq_ptr(cipher_mode, crypt_get_cipher_mode(cd)))
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher mode.");
+
+ if (volume_key_size != UINT64_MAX && vks != volume_key_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong volume key size.");
+
+ vk = malloc(vks);
+ if (!vk)
+ return log_oom();
+
+ r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
+ if (r == -ENOKEY) {
+ r = luks_try_passwords(cd, passwords, vk, &vks);
+ if (r == -ENOKEY)
+ return log_error_errno(r, "No valid password for LUKS superblock.");
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+
+ r = crypt_activate_by_volume_key(
+ cd,
+ dm_name,
+ vk, vks,
+ discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlock LUKS superblock: %m");
+
+ log_info("Setting up LUKS device /dev/mapper/%s completed.", dm_name);
+
+ *ret = TAKE_PTR(cd);
+
+ if (ret_found_uuid) /* Return the UUID actually found if the caller wants to know */
+ *ret_found_uuid = p;
+ if (ret_volume_key)
+ *ret_volume_key = TAKE_PTR(vk);
+ if (ret_volume_key_size)
+ *ret_volume_key_size = vks;
+
+ return 0;
+}
+
+static int luks_open(
+ const char *dm_name,
+ char **passwords,
+ char **pkcs11_decrypted_passwords,
+ struct crypt_device **ret,
+ sd_id128_t *ret_found_uuid,
+ void **ret_volume_key,
+ size_t *ret_volume_key_size) {
+
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_(erase_and_freep) void *vk = NULL;
+ sd_id128_t p;
+ size_t vks;
+ int r;
+
+ assert(dm_name);
+ assert(ret);
+
+ /* Opens a LUKS device that is already set up. Re-validates the password while doing so (which also
+ * provides us with the volume key, which we want). */
+
+ r = crypt_init_by_name(&cd, dm_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+ crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+ r = crypt_load(cd, CRYPT_LUKS2, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load LUKS superblock: %m");
+
+ r = crypt_get_volume_key_size(cd);
+ if (r <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
+ vks = (size_t) r;
+
+ if (ret_found_uuid) {
+ const char *s;
+
+ s = crypt_get_uuid(cd);
+ if (!s)
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID.");
+
+ r = sd_id128_from_string(s, &p);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has invalid UUID.");
+ }
+
+ vk = malloc(vks);
+ if (!vk)
+ return log_oom();
+
+ r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
+ if (r == -ENOKEY) {
+ r = luks_try_passwords(cd, passwords, vk, &vks);
+ if (r == -ENOKEY)
+ return log_error_errno(r, "No valid password for LUKS superblock.");
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+
+ log_info("Discovered used LUKS device /dev/mapper/%s, and validated password.", dm_name);
+
+ /* This is needed so that crypt_resize() can operate correctly for pre-existing LUKS devices. We need
+ * to tell libcryptsetup the volume key explicitly, so that it is in the kernel keyring. */
+ r = crypt_activate_by_volume_key(cd, NULL, vk, vks, CRYPT_ACTIVATE_KEYRING_KEY);
+ if (r < 0)
+ return log_error_errno(r, "Failed to upload volume key again: %m");
+
+ log_info("Successfully re-activated LUKS device.");
+
+ *ret = TAKE_PTR(cd);
+
+ if (ret_found_uuid)
+ *ret_found_uuid = p;
+ if (ret_volume_key)
+ *ret_volume_key = TAKE_PTR(vk);
+ if (ret_volume_key_size)
+ *ret_volume_key_size = vks;
+
+ return 0;
+}
+
+static int fs_validate(
+ const char *dm_node,
+ sd_id128_t uuid,
+ char **ret_fstype,
+ sd_id128_t *ret_found_uuid) {
+
+ _cleanup_free_ char *fstype = NULL;
+ sd_id128_t u;
+ int r;
+
+ assert(dm_node);
+ assert(ret_fstype);
+
+ r = probe_file_system_by_path(dm_node, &fstype, &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to probe file system: %m");
+
+ /* Limit the set of supported file systems a bit, as protection against little tested kernel file
+ * systems. Also, we only support the resize ioctls for these file systems. */
+ if (!supported_fstype(fstype))
+ return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Image contains unsupported file system: %s", strna(fstype));
+
+ if (!sd_id128_is_null(uuid) &&
+ !sd_id128_equal(uuid, u))
+ return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "File system has wrong UUID.");
+
+ log_info("Probing file system completed (found %s).", fstype);
+
+ *ret_fstype = TAKE_PTR(fstype);
+
+ if (ret_found_uuid) /* Return the UUID actually found if the caller wants to know */
+ *ret_found_uuid = u;
+
+ return 0;
+}
+
+static int make_dm_names(const char *user_name, char **ret_dm_name, char **ret_dm_node) {
+ _cleanup_free_ char *name = NULL, *node = NULL;
+
+ assert(user_name);
+ assert(ret_dm_name);
+ assert(ret_dm_node);
+
+ name = strjoin("home-", user_name);
+ if (!name)
+ return log_oom();
+
+ node = path_join("/dev/mapper/", name);
+ if (!node)
+ return log_oom();
+
+ *ret_dm_name = TAKE_PTR(name);
+ *ret_dm_node = TAKE_PTR(node);
+ return 0;
+}
+
+static int luks_validate(
+ int fd,
+ const char *label,
+ sd_id128_t partition_uuid,
+ sd_id128_t *ret_partition_uuid,
+ uint64_t *ret_offset,
+ uint64_t *ret_size) {
+
+ _cleanup_(blkid_free_probep) blkid_probe b = NULL;
+ sd_id128_t found_partition_uuid = SD_ID128_NULL;
+ const char *fstype = NULL, *pttype = NULL;
+ blkid_loff_t offset = 0, size = 0;
+ blkid_partlist pl;
+ bool found = false;
+ int r, i, n;
+
+ assert(fd >= 0);
+ assert(label);
+ assert(ret_offset);
+ assert(ret_size);
+
+ b = blkid_new_probe();
+ if (!b)
+ return -ENOMEM;
+
+ errno = 0;
+ r = blkid_probe_set_device(b, fd, 0, 0);
+ if (r != 0)
+ return errno > 0 ? -errno : -ENOMEM;
+
+ (void) blkid_probe_enable_superblocks(b, 1);
+ (void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
+ (void) blkid_probe_enable_partitions(b, 1);
+ (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+ errno = 0;
+ r = blkid_do_safeprobe(b);
+ if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+ return -ENOPKG;
+ if (r != 0)
+ return errno > 0 ? -errno : -EIO;
+
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ if (streq_ptr(fstype, "crypto_LUKS")) {
+ /* Directly a LUKS image */
+ *ret_offset = 0;
+ *ret_size = UINT64_MAX; /* full disk */
+ *ret_partition_uuid = SD_ID128_NULL;
+ return 0;
+ } else if (fstype)
+ return -ENOPKG;
+
+ (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
+ if (!streq_ptr(pttype, "gpt"))
+ return -ENOPKG;
+
+ errno = 0;
+ pl = blkid_probe_get_partitions(b);
+ if (!pl)
+ return errno > 0 ? -errno : -ENOMEM;
+
+ errno = 0;
+ n = blkid_partlist_numof_partitions(pl);
+ if (n < 0)
+ return errno > 0 ? -errno : -EIO;
+
+ for (i = 0; i < n; i++) {
+ blkid_partition pp;
+ sd_id128_t id;
+ const char *sid;
+
+ errno = 0;
+ pp = blkid_partlist_get_partition(pl, i);
+ if (!pp)
+ return errno > 0 ? -errno : -EIO;
+
+ if (!streq_ptr(blkid_partition_get_type_string(pp), "773f91ef-66d4-49b5-bd83-d683bf40ad16"))
+ continue;
+
+ if (!streq_ptr(blkid_partition_get_name(pp), label))
+ continue;
+
+ sid = blkid_partition_get_uuid(pp);
+ if (sid) {
+ r = sd_id128_from_string(sid, &id);
+ if (r < 0)
+ log_debug_errno(r, "Couldn't parse partition UUID %s, weird: %m", sid);
+
+ if (!sd_id128_is_null(partition_uuid) && !sd_id128_equal(id, partition_uuid))
+ continue;
+ }
+
+ if (found)
+ return -ENOPKG;
+
+ offset = blkid_partition_get_start(pp);
+ size = blkid_partition_get_size(pp);
+ found_partition_uuid = id;
+
+ found = true;
+ }
+
+ if (!found)
+ return -ENOPKG;
+
+ if (offset < 0)
+ return -EINVAL;
+ if ((uint64_t) offset > UINT64_MAX / 512U)
+ return -EINVAL;
+ if (size <= 0)
+ return -EINVAL;
+ if ((uint64_t) size > UINT64_MAX / 512U)
+ return -EINVAL;
+
+ *ret_offset = offset * 512U;
+ *ret_size = size * 512U;
+ *ret_partition_uuid = found_partition_uuid;
+
+ return 0;
+}
+
+static int crypt_device_to_evp_cipher(struct crypt_device *cd, const EVP_CIPHER **ret) {
+ _cleanup_free_ char *cipher_name = NULL;
+ const char *cipher, *cipher_mode, *e;
+ size_t key_size, key_bits;
+ const EVP_CIPHER *cc;
+ int r;
+
+ assert(cd);
+
+ /* Let's find the right OpenSSL EVP_CIPHER object that matches the encryption settings of the LUKS
+ * device */
+
+ cipher = crypt_get_cipher(cd);
+ if (!cipher)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher from LUKS device.");
+
+ cipher_mode = crypt_get_cipher_mode(cd);
+ if (!cipher_mode)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher mode from LUKS device.");
+
+ e = strchr(cipher_mode, '-');
+ if (e)
+ cipher_mode = strndupa(cipher_mode, e - cipher_mode);
+
+ r = crypt_get_volume_key_size(cd);
+ if (r <= 0)
+ return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Cannot get volume key size from LUKS device.");
+
+ key_size = r;
+ key_bits = key_size * 8;
+ if (streq(cipher_mode, "xts"))
+ key_bits /= 2;
+
+ if (asprintf(&cipher_name, "%s-%zu-%s", cipher, key_bits, cipher_mode) < 0)
+ return log_oom();
+
+ cc = EVP_get_cipherbyname(cipher_name);
+ if (!cc)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Selected cipher mode '%s' not supported, can't encrypt JSON record.", cipher_name);
+
+ /* Verify that our key length calculations match what OpenSSL thinks */
+ r = EVP_CIPHER_key_length(cc);
+ if (r < 0 || (uint64_t) r != key_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key size of selected cipher doesn't meet out expectations.");
+
+ *ret = cc;
+ return 0;
+}
+
+static int luks_validate_home_record(
+ struct crypt_device *cd,
+ UserRecord *h,
+ const void *volume_key,
+ char ***pkcs11_decrypted_passwords,
+ UserRecord **ret_luks_home_record) {
+
+ int r, token;
+
+ assert(cd);
+ assert(h);
+
+ for (token = 0;; token++) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *rr = NULL;
+ _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *lhr = NULL;
+ _cleanup_free_ void *encrypted = NULL, *iv = NULL;
+ size_t decrypted_size, encrypted_size, iv_size;
+ int decrypted_size_out1, decrypted_size_out2;
+ _cleanup_free_ char *decrypted = NULL;
+ const char *text, *type;
+ crypt_token_info state;
+ JsonVariant *jr, *jiv;
+ unsigned line, column;
+ const EVP_CIPHER *cc;
+
+ state = crypt_token_status(cd, token, &type);
+ if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, give up */
+ break;
+ if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL))
+ continue;
+ if (state != CRYPT_TOKEN_EXTERNAL_UNKNOWN)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected token state of token %i: %i", token, (int) state);
+
+ if (!streq(type, "systemd-homed"))
+ continue;
+
+ r = crypt_token_json_get(cd, token, &text);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read LUKS token %i: %m", token);
+
+ r = json_parse(text, JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse LUKS token JSON data %u:%u: %m", line, column);
+
+ jr = json_variant_by_key(v, "record");
+ if (!jr)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS token lacks 'record' field.");
+ jiv = json_variant_by_key(v, "iv");
+ if (!jiv)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS token lacks 'iv' field.");
+
+ r = json_variant_unbase64(jr, &encrypted, &encrypted_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to base64 decode record: %m");
+
+ r = json_variant_unbase64(jiv, &iv, &iv_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to base64 decode IV: %m");
+
+ r = crypt_device_to_evp_cipher(cd, &cc);
+ if (r < 0)
+ return r;
+ if (iv_size > INT_MAX || EVP_CIPHER_iv_length(cc) != (int) iv_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IV size doesn't match.");
+
+ context = EVP_CIPHER_CTX_new();
+ if (!context)
+ return log_oom();
+
+ if (EVP_DecryptInit_ex(context, cc, NULL, volume_key, iv) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
+
+ decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2;
+ decrypted = new(char, decrypted_size);
+ if (!decrypted)
+ return log_oom();
+
+ if (EVP_DecryptUpdate(context, (uint8_t*) decrypted, &decrypted_size_out1, encrypted, encrypted_size) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decrypt JSON record.");
+
+ assert((size_t) decrypted_size_out1 <= decrypted_size);
+
+ if (EVP_DecryptFinal_ex(context, (uint8_t*) decrypted + decrypted_size_out1, &decrypted_size_out2) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish decryption of JSON record.");
+
+ assert((size_t) decrypted_size_out1 + (size_t) decrypted_size_out2 < decrypted_size);
+ decrypted_size = (size_t) decrypted_size_out1 + (size_t) decrypted_size_out2;
+
+ if (memchr(decrypted, 0, decrypted_size))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Inner NUL byte in JSON record, refusing.");
+
+ decrypted[decrypted_size] = 0;
+
+ r = json_parse(decrypted, JSON_PARSE_SENSITIVE, &rr, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse decrypted JSON record, refusing.");
+
+ lhr = user_record_new();
+ if (!lhr)
+ return log_oom();
+
+ r = user_record_load(lhr, rr, USER_RECORD_LOAD_EMBEDDED);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse user record: %m");
+
+ if (!user_record_compatible(h, lhr))
+ return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing.");
+
+ r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ *ret_luks_home_record = TAKE_PTR(lhr);
+ return 0;
+ }
+
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Couldn't find home record in LUKS2 header, refusing.");
+}
+
+static int format_luks_token_text(
+ struct crypt_device *cd,
+ UserRecord *hr,
+ const void *volume_key,
+ char **ret) {
+
+ int r, encrypted_size_out1 = 0, encrypted_size_out2 = 0, iv_size, key_size;
+ _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_free_ void *iv = NULL, *encrypted = NULL;
+ size_t text_length, encrypted_size;
+ _cleanup_free_ char *text = NULL;
+ const EVP_CIPHER *cc;
+
+ assert(cd);
+ assert(hr);
+ assert(volume_key);
+ assert(ret);
+
+ r = crypt_device_to_evp_cipher(cd, &cc);
+ if (r < 0)
+ return r;
+
+ key_size = EVP_CIPHER_key_length(cc);
+ iv_size = EVP_CIPHER_iv_length(cc);
+
+ if (iv_size > 0) {
+ iv = malloc(iv_size);
+ if (!iv)
+ return log_oom();
+
+ r = genuine_random_bytes(iv, iv_size, RANDOM_BLOCK);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate IV: %m");
+ }
+
+ context = EVP_CIPHER_CTX_new();
+ if (!context)
+ return log_oom();
+
+ if (EVP_EncryptInit_ex(context, cc, NULL, volume_key, iv) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
+
+ r = json_variant_format(hr->json, 0, &text);
+ if (r < 0)
+ return log_error_errno(r,"Failed to format user record for LUKS: %m");
+
+ text_length = strlen(text);
+ encrypted_size = text_length + 2*key_size - 1;
+
+ encrypted = malloc(encrypted_size);
+ if (!encrypted)
+ return log_oom();
+
+ if (EVP_EncryptUpdate(context, encrypted, &encrypted_size_out1, (uint8_t*) text, text_length) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to encrypt JSON record.");
+
+ assert((size_t) encrypted_size_out1 <= encrypted_size);
+
+ if (EVP_EncryptFinal_ex(context, (uint8_t*) encrypted + encrypted_size_out1, &encrypted_size_out2) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish encryption of JSON record. ");
+
+ assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 <= encrypted_size);
+
+ r = json_build(&v,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("type", JSON_BUILD_STRING("systemd-homed")),
+ JSON_BUILD_PAIR("keyslots", JSON_BUILD_EMPTY_ARRAY),
+ JSON_BUILD_PAIR("record", JSON_BUILD_BASE64(encrypted, encrypted_size_out1 + encrypted_size_out2)),
+ JSON_BUILD_PAIR("iv", JSON_BUILD_BASE64(iv, iv_size))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare LUKS JSON token object: %m");
+
+ r = json_variant_format(v, 0, ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format encrypted user record for LUKS: %m");
+
+ return 0;
+}
+
+int home_store_header_identity_luks(
+ UserRecord *h,
+ HomeSetup *setup,
+ UserRecord *old_home) {
+
+ _cleanup_(user_record_unrefp) UserRecord *header_home = NULL;
+ _cleanup_free_ char *text = NULL;
+ int token = 0, r;
+
+ assert(h);
+
+ if (!setup->crypt_device)
+ return 0;
+
+ assert(setup->volume_key);
+
+ /* Let's store the user's identity record in the LUKS2 "token" header data fields, in an encrypted
+ * fashion. Why that? If we'd rely on the record being embedded in the payload file system itself we
+ * would have to mount the file system before we can validate the JSON record, its signatures and
+ * whether it matches what we are looking for. However, kernel file system implementations are
+ * generally not ready to be used on untrusted media. Hence let's store the record independently of
+ * the file system, so that we can validate it first, and only then mount the file system. To keep
+ * things simple we use the same encryption settings for this record as for the file system itself. */
+
+ r = user_record_clone(h, USER_RECORD_EXTRACT_EMBEDDED, &header_home);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine new header record: %m");
+
+ if (old_home && user_record_equal(old_home, header_home)) {
+ log_debug("Not updating header home record.");
+ return 0;
+ }
+
+ r = format_luks_token_text(setup->crypt_device, header_home, setup->volume_key, &text);
+ if (r < 0)
+ return r;
+
+ for (;; token++) {
+ crypt_token_info state;
+ const char *type;
+
+ state = crypt_token_status(setup->crypt_device, token, &type);
+ if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, we are done */
+ break;
+ if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL))
+ continue; /* Not ours */
+ if (state != CRYPT_TOKEN_EXTERNAL_UNKNOWN)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected token state of token %i: %i", token, (int) state);
+
+ if (!streq(type, "systemd-homed"))
+ continue;
+
+ r = crypt_token_json_set(setup->crypt_device, token, text);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set JSON token for slot %i: %m", token);
+
+ /* Now, let's free the text so that for all further matching tokens we all crypt_json_token_set()
+ * with a NULL text in order to invalidate the tokens. */
+ text = mfree(text);
+ token++;
+ }
+
+ if (text)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Didn't find any record token to update.");
+
+ log_info("Wrote LUKS header user record.");
+
+ return 1;
+}
+
+static int run_fitrim(int root_fd) {
+ char buf[FORMAT_BYTES_MAX];
+ struct fstrim_range range = {
+ .len = UINT64_MAX,
+ };
+
+ /* If discarding is on, discard everything right after mounting, so that the discard setting takes
+ * effect on activation. */
+
+ assert(root_fd >= 0);
+
+ if (ioctl(root_fd, FITRIM, &range) < 0) {
+ if (IN_SET(errno, ENOTTY, EOPNOTSUPP, EBADF)) {
+ log_debug_errno(errno, "File system does not support FITRIM, not trimming.");
+ return 0;
+ }
+
+ return log_warning_errno(errno, "Failed to invoke FITRIM, ignoring: %m");
+ }
+
+ log_info("Discarded unused %s.",
+ format_bytes(buf, sizeof(buf), range.len));
+ return 1;
+}
+
+static int run_fallocate(int backing_fd, const struct stat *st) {
+ char buf[FORMAT_BYTES_MAX];
+
+ assert(backing_fd >= 0);
+ assert(st);
+
+ /* If discarding is off, let's allocate the whole image before mounting, so that the setting takes
+ * effect on activation */
+
+ if (!S_ISREG(st->st_mode))
+ return 0;
+
+ if (st->st_blocks >= DIV_ROUND_UP(st->st_size, 512)) {
+ log_info("Backing file is fully allocated already.");
+ return 0;
+ }
+
+ if (fallocate(backing_fd, FALLOC_FL_KEEP_SIZE, 0, st->st_size) < 0) {
+
+ if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+ log_debug_errno(errno, "fallocate() not supported on file system, ignoring.");
+ return 0;
+ }
+
+ if (ERRNO_IS_DISK_SPACE(errno)) {
+ log_debug_errno(errno, "Not enough disk space to fully allocate home.");
+ return -ENOSPC; /* make recognizable */
+ }
+
+ return log_error_errno(errno, "Failed to allocate backing file blocks: %m");
+ }
+
+ log_info("Allocated additional %s.",
+ format_bytes(buf, sizeof(buf), (DIV_ROUND_UP(st->st_size, 512) - st->st_blocks) * 512));
+ return 1;
+}
+
+int home_prepare_luks(
+ UserRecord *h,
+ bool already_activated,
+ const char *force_image_path,
+ char ***pkcs11_decrypted_passwords,
+ HomeSetup *setup,
+ UserRecord **ret_luks_home) {
+
+ sd_id128_t found_partition_uuid, found_luks_uuid, found_fs_uuid;
+ _cleanup_(user_record_unrefp) UserRecord *luks_home = NULL;
+ _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_(erase_and_freep) void *volume_key = NULL;
+ bool dm_activated = false, mounted = false;
+ _cleanup_close_ int root_fd = -1;
+ size_t volume_key_size = 0;
+ uint64_t offset, size;
+ int r;
+
+ assert(h);
+ assert(setup);
+ assert(setup->dm_name);
+ assert(setup->dm_node);
+
+ assert(user_record_storage(h) == USER_LUKS);
+
+ if (already_activated) {
+ struct loop_info64 info;
+ const char *n;
+
+ r = luks_open(setup->dm_name,
+ h->password,
+ pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+ &cd,
+ &found_luks_uuid,
+ &volume_key,
+ &volume_key_size);
+ if (r < 0)
+ return r;
+
+ r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+ if (r < 0)
+ return r;
+
+ n = crypt_get_device_name(cd);
+ if (!n)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine backing device for DM %s.", setup->dm_name);
+
+ r = loop_device_open(n, O_RDWR, &loop);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open loopback device %s: %m", n);
+
+ if (ioctl(loop->fd, LOOP_GET_STATUS64, &info) < 0) {
+ _cleanup_free_ char *sysfs = NULL;
+ struct stat st;
+
+ if (!IN_SET(errno, ENOTTY, EINVAL))
+ return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n);
+
+ if (ioctl(loop->fd, BLKGETSIZE64, &size) < 0)
+ return log_error_errno(r, "Failed to read block device size of %s: %m", n);
+
+ if (fstat(loop->fd, &st) < 0)
+ return log_error_errno(r, "Failed to stat block device %s: %m", n);
+ assert(S_ISBLK(st.st_mode));
+
+ if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return log_oom();
+
+ if (access(sysfs, F_OK) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine whether %s exists: %m", sysfs);
+
+ offset = 0;
+ } else {
+ _cleanup_free_ char *buffer = NULL;
+
+ if (asprintf(&sysfs, "/sys/dev/block/%u:%u/start", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return log_oom();
+
+ r = read_one_line_file(sysfs, &buffer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read partition start offset: %m");
+
+ r = safe_atou64(buffer, &offset);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse partition start offset: %m");
+
+ if (offset > UINT64_MAX / 512U)
+ return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Offset too large for 64 byte range, refusing.");
+
+ offset *= 512U;
+ }
+ } else {
+ offset = info.lo_offset;
+ size = info.lo_sizelimit;
+ }
+
+ found_partition_uuid = found_fs_uuid = SD_ID128_NULL;
+
+ log_info("Discovered used loopback device %s.", loop->node);
+
+ root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (root_fd < 0) {
+ r = log_error_errno(r, "Failed to open home directory: %m");
+ goto fail;
+ }
+ } else {
+ _cleanup_free_ char *fstype = NULL, *subdir = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *ip;
+ struct stat st;
+
+ ip = force_image_path ?: user_record_image_path(h);
+
+ subdir = path_join("/run/systemd/user-home-mount/", user_record_user_name_and_realm(h));
+ if (!subdir)
+ return log_oom();
+
+ fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+
+ if (fstat(fd, &st) < 0)
+ return log_error_errno(errno, "Failed to fstat() image file: %m");
+ if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
+ return log_error_errno(errno, "Image file %s is not a regular file or block device: %m", ip);
+
+ r = luks_validate(fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to validate disk label: %m");
+
+ if (!user_record_luks_discard(h)) {
+ r = run_fallocate(fd, &st);
+ if (r < 0)
+ return r;
+ }
+
+ r = loop_device_make(fd, O_RDWR, offset, size, 0, &loop);
+ if (r == -ENOENT) {
+ log_error_errno(r, "Loopback block device support is not available on this system.");
+ return -ENOLINK; /* make recognizable */
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate loopback context: %m");
+
+ log_info("Setting up loopback device %s completed.", loop->node ?: ip);
+
+ r = luks_setup(loop->node ?: ip,
+ setup->dm_name,
+ h->luks_uuid,
+ h->luks_cipher,
+ h->luks_cipher_mode,
+ h->luks_volume_key_size,
+ h->password,
+ pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+ user_record_luks_discard(h),
+ &cd,
+ &found_luks_uuid,
+ &volume_key,
+ &volume_key_size);
+ if (r < 0)
+ return r;
+
+ dm_activated = true;
+
+ r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+ if (r < 0)
+ goto fail;
+
+ r = fs_validate(setup->dm_node, h->file_system_uuid, &fstype, &found_fs_uuid);
+ if (r < 0)
+ goto fail;
+
+ r = run_fsck(setup->dm_node, fstype);
+ if (r < 0)
+ goto fail;
+
+ r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h));
+ if (r < 0)
+ goto fail;
+
+ mounted = true;
+
+ root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (root_fd < 0) {
+ r = log_error_errno(r, "Failed to open home directory: %m");
+ goto fail;
+ }
+
+ if (user_record_luks_discard(h))
+ (void) run_fitrim(root_fd);
+ }
+
+ setup->loop = TAKE_PTR(loop);
+ setup->crypt_device = TAKE_PTR(cd);
+ setup->root_fd = TAKE_FD(root_fd);
+ setup->found_partition_uuid = found_partition_uuid;
+ setup->found_luks_uuid = found_luks_uuid;
+ setup->found_fs_uuid = found_fs_uuid;
+ setup->partition_offset = offset;
+ setup->partition_size = size;
+ setup->volume_key = TAKE_PTR(volume_key);
+ setup->volume_key_size = volume_key_size;
+
+ setup->undo_mount = mounted;
+ setup->undo_dm = dm_activated;
+
+ if (ret_luks_home)
+ *ret_luks_home = TAKE_PTR(luks_home);
+
+ return 0;
+
+fail:
+ if (mounted)
+ (void) umount_verbose("/run/systemd/user-home-mount");
+
+ if (dm_activated)
+ (void) crypt_deactivate(cd, setup->dm_name);
+
+ return r;
+}
+
+static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, struct statfs *sfs) {
+ char buffer1[FORMAT_BYTES_MAX], buffer2[FORMAT_BYTES_MAX], buffer3[FORMAT_BYTES_MAX], buffer4[FORMAT_BYTES_MAX];
+
+ assert(sfs);
+
+ log_info("Image size is %s, file system size is %s, file system payload size is %s, file system free is %s.",
+ format_bytes(buffer1, sizeof(buffer1), host_size),
+ format_bytes(buffer2, sizeof(buffer2), encrypted_size),
+ format_bytes(buffer3, sizeof(buffer3), (uint64_t) sfs->f_blocks * (uint64_t) sfs->f_frsize),
+ format_bytes(buffer4, sizeof(buffer4), (uint64_t) sfs->f_bfree * (uint64_t) sfs->f_frsize));
+}
+
+int home_activate_luks(
+ UserRecord *h,
+ char ***pkcs11_decrypted_passwords,
+ UserRecord **ret_home) {
+
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL;
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ uint64_t host_size, encrypted_size;
+ const char *hdo, *hd;
+ struct statfs sfs;
+ int r;
+
+ assert(h);
+ assert(user_record_storage(h) == USER_LUKS);
+ assert(ret_home);
+
+ assert_se(hdo = user_record_home_directory(h));
+ hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
+
+ r = make_dm_names(h->user_name, &setup.dm_name, &setup.dm_node);
+ if (r < 0)
+ return r;
+
+ r = access(setup.dm_node, F_OK);
+ if (r < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine whether %s exists: %m", setup.dm_node);
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup.dm_node);
+
+ r = home_prepare_luks(
+ h,
+ false,
+ NULL,
+ pkcs11_decrypted_passwords,
+ &setup,
+ &luks_home_record);
+ if (r < 0)
+ return r;
+
+ r = block_get_size_by_fd(setup.loop->fd, &host_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get loopback block device size: %m");
+
+ r = block_get_size_by_path(setup.dm_node, &encrypted_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get LUKS block device size: %m");
+
+ r = home_refresh(
+ h,
+ &setup,
+ luks_home_record,
+ pkcs11_decrypted_passwords,
+ &sfs,
+ &new_home);
+ if (r < 0)
+ return r;
+
+ r = home_extend_embedded_identity(new_home, h, &setup);
+ if (r < 0)
+ return r;
+
+ setup.root_fd = safe_close(setup.root_fd);
+
+ r = home_move_mount(user_record_user_name_and_realm(h), hd);
+ if (r < 0)
+ return r;
+
+ setup.undo_mount = false;
+
+ loop_device_relinquish(setup.loop);
+
+ r = dm_deferred_remove(setup.dm_name);
+ if (r < 0)
+ log_warning_errno(r, "Failed to relinquish dm device, ignoring: %m");
+
+ setup.undo_dm = false;
+
+ log_info("Everything completed.");
+
+ print_size_summary(host_size, encrypted_size, &sfs);
+
+ *ret_home = TAKE_PTR(new_home);
+ return 1;
+}
+
+int home_deactivate_luks(UserRecord *h) {
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+ int r;
+
+ /* Note that the DM device and loopback device are set to auto-detach, hence strictly speaking we
+ * don't have to explicitly have to detach them. However, we do that nonetheless (in case of the DM
+ * device), to avoid races: by explicitly detaching them we know when the detaching is complete. We
+ * don't bother about the loopback device because unlike the DM device it doesn't have a fixed
+ * name. */
+
+ r = make_dm_names(h->user_name, &dm_name, &dm_node);
+ if (r < 0)
+ return r;
+
+ r = crypt_init_by_name(&cd, dm_name);
+ if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
+ log_debug_errno(r, "LUKS device %s is already detached.", dm_name);
+ return false;
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+ log_info("Discovered used LUKS device %s.", dm_node);
+
+ crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+ r = crypt_deactivate(cd, dm_name);
+ if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT))
+ log_debug_errno(r, "LUKS device %s is already detached.", dm_node);
+ else if (r < 0)
+ return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node);
+
+ log_info("LUKS device detaching completed.");
+ return true;
+}
+
+static int run_mkfs(
+ const char *node,
+ const char *fstype,
+ const char *label,
+ sd_id128_t uuid,
+ bool discard) {
+
+ int r;
+
+ assert(node);
+ assert(fstype);
+ assert(label);
+
+ r = mkfs_exists(fstype);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if mkfs for file system %s exists: %m", fstype);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Nt mkfs for file system %s installed.", fstype);
+
+ r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ const char *mkfs;
+ char suuid[37];
+
+ /* Child */
+
+ mkfs = strjoina("mkfs.", fstype);
+ id128_to_uuid_string(uuid, suuid);
+
+ if (streq(fstype, "ext4"))
+ execlp(mkfs, mkfs,
+ "-L", label,
+ "-U", suuid,
+ "-I", "256",
+ "-O", "has_journal",
+ "-m", "0",
+ "-E", discard ? "lazy_itable_init=1,discard" : "lazy_itable_init=1,nodiscard",
+ node, NULL);
+ else if (streq(fstype, "btrfs")) {
+ if (discard)
+ execlp(mkfs, mkfs, "-L", label, "-U", suuid, node, NULL);
+ else
+ execlp(mkfs, mkfs, "-L", label, "-U", suuid, "--nodiscard", node, NULL);
+ } else if (streq(fstype, "xfs")) {
+ const char *j;
+
+ j = strjoina("uuid=", suuid);
+ if (discard)
+ execlp(mkfs, mkfs, "-L", label, "-m", j, "-m", "reflink=1", node, NULL);
+ else
+ execlp(mkfs, mkfs, "-L", label, "-m", j, "-m", "reflink=1", "-K", node, NULL);
+ } else {
+ log_error("Cannot make file system: %s", fstype);
+ _exit(EXIT_FAILURE);
+ }
+
+ log_error_errno(errno, "Failed to execute %s: %m", mkfs);
+ _exit(EXIT_FAILURE);
+ }
+
+ return 0;
+}
+
+static struct crypt_pbkdf_type* build_good_pbkdf(struct crypt_pbkdf_type *buffer, UserRecord *hr) {
+ assert(buffer);
+ assert(hr);
+
+ *buffer = (struct crypt_pbkdf_type) {
+ .hash = user_record_luks_pbkdf_hash_algorithm(hr),
+ .type = user_record_luks_pbkdf_type(hr),
+ .time_ms = user_record_luks_pbkdf_time_cost_usec(hr) / USEC_PER_MSEC,
+ .max_memory_kb = user_record_luks_pbkdf_memory_cost(hr) / 1024,
+ .parallel_threads = user_record_luks_pbkdf_parallel_threads(hr),
+ };
+
+ return buffer;
+}
+
+static struct crypt_pbkdf_type* build_minimal_pbkdf(struct crypt_pbkdf_type *buffer, UserRecord *hr) {
+ assert(buffer);
+ assert(hr);
+
+ /* For PKCS#11 derived keys (which are generated randomly and are of high quality already) we use a
+ * minimal PBKDF */
+ *buffer = (struct crypt_pbkdf_type) {
+ .hash = user_record_luks_pbkdf_hash_algorithm(hr),
+ .type = CRYPT_KDF_PBKDF2,
+ .iterations = 1,
+ .time_ms = 1,
+ };
+
+ return buffer;
+}
+
+static int luks_format(
+ const char *node,
+ const char *dm_name,
+ sd_id128_t uuid,
+ const char *label,
+ char **pkcs11_decrypted_passwords,
+ char **effective_passwords,
+ bool discard,
+ UserRecord *hr,
+ struct crypt_device **ret) {
+
+ _cleanup_(user_record_unrefp) UserRecord *reduced = NULL;
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_(erase_and_freep) void *volume_key = NULL;
+ struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
+ _cleanup_free_ char *text = NULL;
+ size_t volume_key_size;
+ char suuid[37], **pp;
+ int slot = 0, r;
+
+ assert(node);
+ assert(dm_name);
+ assert(hr);
+ assert(ret);
+
+ r = crypt_init(&cd, node);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
+
+ crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+ /* Normally we'd, just leave volume key generation to libcryptsetup. However, we can't, since we
+ * can't extract the volume key from the library again, but we need it in order to encrypt the JSON
+ * record. Hence, let's generate it on our own, so that we can keep track of it. */
+
+ volume_key_size = user_record_luks_volume_key_size(hr);
+ volume_key = malloc(volume_key_size);
+ if (!volume_key)
+ return log_oom();
+
+ r = genuine_random_bytes(volume_key, volume_key_size, RANDOM_BLOCK);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate volume key: %m");
+
+#if HAVE_CRYPT_SET_METADATA_SIZE
+ /* Increase the metadata space to 4M, the largest LUKS2 supports */
+ r = crypt_set_metadata_size(cd, 4096U*1024U, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change LUKS2 metadata size: %m");
+#endif
+
+ build_good_pbkdf(&good_pbkdf, hr);
+ build_minimal_pbkdf(&minimal_pbkdf, hr);
+
+ r = crypt_format(cd,
+ CRYPT_LUKS2,
+ user_record_luks_cipher(hr),
+ user_record_luks_cipher_mode(hr),
+ id128_to_uuid_string(uuid, suuid),
+ volume_key,
+ volume_key_size,
+ &(struct crypt_params_luks2) {
+ .label = label,
+ .subsystem = "systemd-home",
+ .sector_size = 512U,
+ .pbkdf = &good_pbkdf,
+ });
+ if (r < 0)
+ return log_error_errno(r, "Failed to format LUKS image: %m");
+
+ log_info("LUKS formatting completed.");
+
+ STRV_FOREACH(pp, effective_passwords) {
+
+ if (strv_contains(pkcs11_decrypted_passwords, *pp)) {
+ log_debug("Using minimal PBKDF for slot %i", slot);
+ r = crypt_set_pbkdf_type(cd, &minimal_pbkdf);
+ } else {
+ log_debug("Using good PBKDF for slot %i", slot);
+ r = crypt_set_pbkdf_type(cd, &good_pbkdf);
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to tweak PBKDF for slot %i: %m", slot);
+
+ r = crypt_keyslot_add_by_volume_key(
+ cd,
+ slot,
+ volume_key,
+ volume_key_size,
+ *pp,
+ strlen(*pp));
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up LUKS password for slot %i: %m", slot);
+
+ log_info("Writing password to LUKS keyslot %i completed.", slot);
+ slot++;
+ }
+
+ r = crypt_activate_by_volume_key(
+ cd,
+ dm_name,
+ volume_key,
+ volume_key_size,
+ discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to activate LUKS superblock: %m");
+
+ log_info("LUKS activation by volume key succeeded.");
+
+ r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED, &reduced);
+ if (r < 0)
+ return log_error_errno(r, "Failed to prepare home record for LUKS: %m");
+
+ r = format_luks_token_text(cd, reduced, volume_key, &text);
+ if (r < 0)
+ return r;
+
+ r = crypt_token_json_set(cd, CRYPT_ANY_TOKEN, text);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set LUKS JSON token: %m");
+
+ log_info("Writing user record as LUKS token completed.");
+
+ if (ret)
+ *ret = TAKE_PTR(cd);
+
+ return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_context*, fdisk_unref_context);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_partition*, fdisk_unref_partition);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_parttype*, fdisk_unref_parttype);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_table*, fdisk_unref_table);
+
+static int make_partition_table(
+ int fd,
+ const char *label,
+ sd_id128_t uuid,
+ uint64_t *ret_offset,
+ uint64_t *ret_size,
+ sd_id128_t *ret_disk_uuid) {
+
+ _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *p = NULL, *q = NULL;
+ _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
+ _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+ _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
+ uint64_t offset, size;
+ sd_id128_t disk_uuid;
+ char uuids[37];
+ int r;
+
+ assert(fd >= 0);
+ assert(label);
+ assert(ret_offset);
+ assert(ret_size);
+
+ t = fdisk_new_parttype();
+ if (!t)
+ return log_oom();
+
+ r = fdisk_parttype_set_typestr(t, "773f91ef-66d4-49b5-bd83-d683bf40ad16");
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize partition type: %m");
+
+ c = fdisk_new_context();
+ if (!c)
+ return log_oom();
+
+ if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
+ return log_oom();
+
+ r = fdisk_assign_device(c, path, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open device: %m");
+
+ r = fdisk_create_disklabel(c, "gpt");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create gpt disk label: %m");
+
+ p = fdisk_new_partition();
+ if (!p)
+ return log_oom();
+
+ r = fdisk_partition_set_type(p, t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition type: %m");
+
+ r = fdisk_partition_start_follow_default(p, 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to place partition at beginning of space: %m");
+
+ r = fdisk_partition_partno_follow_default(p, 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to place partition at first free partition index: %m");
+
+ r = fdisk_partition_end_follow_default(p, 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make partition cover all free space: %m");
+
+ r = fdisk_partition_set_name(p, label);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition name: %m");
+
+ r = fdisk_partition_set_uuid(p, id128_to_uuid_string(uuid, uuids));
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition UUID: %m");
+
+ r = fdisk_add_partition(c, p, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add partition: %m");
+
+ r = fdisk_write_disklabel(c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write disk label: %m");
+
+ r = fdisk_get_disklabel_id(c, &disk_uuid_as_string);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine disk label UUID: %m");
+
+ r = sd_id128_from_string(disk_uuid_as_string, &disk_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse disk label UUID: %m");
+
+ r = fdisk_get_partition(c, 0, &q);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read created partition metadata: %m");
+
+ assert(fdisk_partition_has_start(q));
+ offset = fdisk_partition_get_start(q);
+ if (offset > UINT64_MAX / 512U)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Partition offset too large.");
+
+ assert(fdisk_partition_has_size(q));
+ size = fdisk_partition_get_size(q);
+ if (size > UINT64_MAX / 512U)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Partition size too large.");
+
+ *ret_offset = offset * 512U;
+ *ret_size = size * 512U;
+ *ret_disk_uuid = disk_uuid;
+
+ return 0;
+}
+
+static bool supported_fs_size(const char *fstype, uint64_t host_size) {
+ uint64_t m;
+
+ m = minimal_size_by_fs_name(fstype);
+ if (m == UINT64_MAX)
+ return false;
+
+ return host_size >= m;
+}
+
+static int wait_for_devlink(const char *path) {
+ _cleanup_close_ int inotify_fd = -1;
+ usec_t until;
+ int r;
+
+ /* let's wait for a device link to show up in /dev, with a time-out. This is good to do since we
+ * return a /dev/disk/by-uuid/… link to our callers and they likely want to access it right-away,
+ * hence let's wait until udev has caught up with our changes, and wait for the symlink to be
+ * created. */
+
+ until = usec_add(now(CLOCK_MONOTONIC), 45 * USEC_PER_SEC);
+
+ for (;;) {
+ _cleanup_free_ char *dn = NULL;
+ usec_t w;
+
+ if (laccess(path, F_OK) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine whether %s exists: %m", path);
+ } else
+ return 0; /* Found it */
+
+ if (inotify_fd < 0) {
+ /* We need to wait for the device symlink to show up, let's create an inotify watch for it */
+ inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (inotify_fd < 0)
+ return log_error_errno(errno, "Failed to allocate inotify fd: %m");
+ }
+
+ dn = dirname_malloc(path);
+ for (;;) {
+ if (!dn)
+ return log_oom();
+
+ log_info("Watching %s", dn);
+
+ if (inotify_add_watch(inotify_fd, dn, IN_CREATE|IN_MOVED_TO|IN_ONLYDIR|IN_DELETE_SELF|IN_MOVE_SELF) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to add watch on %s: %m", dn);
+ } else
+ break;
+
+ if (empty_or_root(dn))
+ break;
+
+ dn = dirname_malloc(dn);
+ }
+
+ w = now(CLOCK_MONOTONIC);
+ if (w >= until)
+ return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Device link %s still hasn't shown up, giving up.", path);
+
+ r = fd_wait_for_event(inotify_fd, POLLIN, usec_sub_unsigned(until, w));
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch inotify: %m");
+
+ (void) flush_fd(inotify_fd);
+ }
+}
+
+static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *ret) {
+ char buf[FORMAT_BYTES_MAX];
+ struct statfs sfs;
+ uint64_t m;
+
+ assert(h);
+ assert(parent_dir);
+ assert(ret);
+
+ if (h->disk_size != UINT64_MAX) {
+ *ret = DISK_SIZE_ROUND_DOWN(h->disk_size);
+ return 0;
+ }
+
+ if (statfs(parent_dir, &sfs) < 0)
+ return log_error_errno(errno, "statfs() on %s failed: %m", parent_dir);
+
+ m = sfs.f_bsize * sfs.f_bavail;
+
+ if (h->disk_size_relative == UINT64_MAX) {
+
+ if (m > UINT64_MAX / USER_DISK_SIZE_DEFAULT_PERCENT)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Disk size too large.");
+
+ *ret = DISK_SIZE_ROUND_DOWN(m * USER_DISK_SIZE_DEFAULT_PERCENT / 100);
+
+ log_info("Sizing home to %u%% of available disk space, which is %s.",
+ USER_DISK_SIZE_DEFAULT_PERCENT,
+ format_bytes(buf, sizeof(buf), *ret));
+ } else {
+ *ret = DISK_SIZE_ROUND_DOWN((uint64_t) ((double) m * (double) h->disk_size_relative / (double) UINT32_MAX));
+
+ log_info("Sizing home to %" PRIu64 ".%01" PRIu64 "%% of available disk space, which is %s.",
+ (h->disk_size_relative * 100) / UINT32_MAX,
+ ((h->disk_size_relative * 1000) / UINT32_MAX) % 10,
+ format_bytes(buf, sizeof(buf), *ret));
+ }
+
+ if (*ret < USER_DISK_SIZE_MIN)
+ *ret = USER_DISK_SIZE_MIN;
+
+ return 0;
+}
+
+int home_create_luks(
+ UserRecord *h,
+ char **pkcs11_decrypted_passwords,
+ char **effective_passwords,
+ UserRecord **ret_home) {
+
+ _cleanup_free_ char *dm_name = NULL, *dm_node = NULL, *subdir = NULL, *disk_uuid_path = NULL, *temporary_image_path = NULL;
+ uint64_t host_size, encrypted_size, partition_offset, partition_size;
+ bool image_created = false, dm_activated = false, mounted = false;
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ sd_id128_t partition_uuid, fs_uuid, luks_uuid, disk_uuid;
+ _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_close_ int image_fd = -1, root_fd = -1;
+ const char *fstype, *ip;
+ struct statfs sfs;
+ int r;
+
+ assert(h);
+ assert(h->storage < 0 || h->storage == USER_LUKS);
+ assert(ret_home);
+
+ assert_se(ip = user_record_image_path(h));
+
+ fstype = user_record_file_system_type(h);
+ if (!supported_fstype(fstype))
+ return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Unsupported file system type: %s", h->file_system_type);
+
+ if (sd_id128_is_null(h->partition_uuid)) {
+ r = sd_id128_randomize(&partition_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire partition UUID: %m");
+ } else
+ partition_uuid = h->partition_uuid;
+
+ if (sd_id128_is_null(h->luks_uuid)) {
+ r = sd_id128_randomize(&luks_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire LUKS UUID: %m");
+ } else
+ luks_uuid = h->luks_uuid;
+
+ if (sd_id128_is_null(h->file_system_uuid)) {
+ r = sd_id128_randomize(&fs_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire file system UUID: %m");
+ } else
+ fs_uuid = h->file_system_uuid;
+
+ r = make_dm_names(h->user_name, &dm_name, &dm_node);
+ if (r < 0)
+ return r;
+
+ r = access(dm_node, F_OK);
+ if (r < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine whether %s exists: %m", dm_node);
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", dm_node);
+
+ if (path_startswith(ip, "/dev/")) {
+ _cleanup_free_ char *sysfs = NULL;
+ uint64_t block_device_size;
+ struct stat st;
+
+ /* Let's place the home directory on a real device, i.e. an USB stick or such */
+
+ image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (image_fd < 0)
+ return log_error_errno(errno, "Failed to open device %s: %m", ip);
+
+ if (fstat(image_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat device %s: %m", ip);
+ if (!S_ISBLK(st.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Device is not a block device, refusing.");
+
+ if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return log_oom();
+ if (access(sysfs, F_OK) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to check whether %s exists: %m", sysfs);
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Operating on partitions is currently not supported, sorry. Please specify a top-level block device.");
+
+ if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
+ return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
+
+ if (ioctl(image_fd, BLKGETSIZE64, &block_device_size) < 0)
+ return log_error_errno(errno, "Failed to read block device size: %m");
+
+ if (h->disk_size == UINT64_MAX) {
+
+ /* If a relative disk size is requested, apply it relative to the block device size */
+ if (h->disk_size_relative < UINT32_MAX)
+ host_size = CLAMP(DISK_SIZE_ROUND_DOWN(block_device_size * h->disk_size_relative / UINT32_MAX),
+ USER_DISK_SIZE_MIN, USER_DISK_SIZE_MAX);
+ else
+ host_size = block_device_size; /* Otherwise, take the full device */
+
+ } else if (h->disk_size > block_device_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EMSGSIZE), "Selected disk size larger than backing block device, refusing.");
+ else
+ host_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+ if (!supported_fs_size(fstype, host_size))
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", h->file_system_type);
+
+ /* After creation we should reference this partition by its UUID instead of the block
+ * device. That's preferable since the user might have specified a device node such as
+ * /dev/sdb to us, which might look very different when replugged. */
+ if (asprintf(&disk_uuid_path, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(luks_uuid)) < 0)
+ return log_oom();
+
+ if (user_record_luks_discard(h)) {
+ if (ioctl(image_fd, BLKDISCARD, (uint64_t[]) { 0, block_device_size }) < 0)
+ log_full_errno(errno == EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to issue full-device BLKDISCARD on device, ignoring: %m");
+ else
+ log_info("Full device discard completed.");
+ }
+ } else {
+ _cleanup_free_ char *parent = NULL;
+
+ parent = dirname_malloc(ip);
+ if (!parent)
+ return log_oom();
+
+ r = mkdir_p(parent, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create parent directory %s: %m", parent);
+
+ r = calculate_disk_size(h, parent, &host_size);
+ if (r < 0)
+ return r;
+
+ if (!supported_fs_size(fstype, host_size))
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", h->file_system_type);
+
+ r = tempfn_random(ip, "homework", &temporary_image_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to derive temporary file name for %s: %m", ip);
+
+ image_fd = open(temporary_image_path, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+ if (image_fd < 0)
+ return log_error_errno(errno, "Failed to create home image %s: %m", temporary_image_path);
+
+ image_created = true;
+
+ r = chattr_fd(image_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", temporary_image_path);
+
+ if (user_record_luks_discard(h))
+ r = ftruncate(image_fd, host_size);
+ else
+ r = fallocate(image_fd, 0, 0, host_size);
+ if (r < 0) {
+ if (ERRNO_IS_DISK_SPACE(errno)) {
+ log_debug_errno(errno, "Not enough disk space to allocate home.");
+ r = -ENOSPC; /* make recognizable */
+ goto fail;
+ }
+
+ r = log_error_errno(errno, "Failed to truncate home image %s: %m", temporary_image_path);
+ goto fail;
+ }
+
+ log_info("Allocating image file completed.");
+ }
+
+ r = make_partition_table(
+ image_fd,
+ user_record_user_name_and_realm(h),
+ partition_uuid,
+ &partition_offset,
+ &partition_size,
+ &disk_uuid);
+ if (r < 0)
+ goto fail;
+
+ log_info("Writing of partition table completed.");
+
+ r = loop_device_make(image_fd, O_RDWR, partition_offset, partition_size, 0, &loop);
+ if (r < 0) {
+ if (r == -ENOENT) { /* this means /dev/loop-control doesn't exist, i.e. we are in a container
+ * or similar and loopback bock devices are not available, return a
+ * recognizable error in this case. */
+ log_error_errno(r, "Loopback block device support is not available on this system.");
+ r = -ENOLINK;
+ goto fail;
+ }
+
+ log_error_errno(r, "Failed to set up loopback device for %s: %m", temporary_image_path);
+ goto fail;
+ }
+
+ r = loop_device_flock(loop, LOCK_EX); /* make sure udev won't read before we are done */
+ if (r < 0) {
+ log_error_errno(r, "Failed to take lock on loop device: %m");
+ goto fail;
+ }
+
+ log_info("Setting up loopback device %s completed.", loop->node ?: ip);
+
+ r = luks_format(loop->node,
+ dm_name,
+ luks_uuid,
+ user_record_user_name_and_realm(h),
+ pkcs11_decrypted_passwords,
+ effective_passwords,
+ user_record_luks_discard(h),
+ h,
+ &cd);
+ if (r < 0)
+ goto fail;
+
+ dm_activated = true;
+
+ r = block_get_size_by_path(dm_node, &encrypted_size);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get encrypted block device size: %m");
+ goto fail;
+ }
+
+ log_info("Setting up LUKS device %s completed.", dm_node);
+
+ r = run_mkfs(dm_node, fstype, user_record_user_name_and_realm(h), fs_uuid, user_record_luks_discard(h));
+ if (r < 0)
+ goto fail;
+
+ log_info("Formatting file system completed.");
+
+ r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h));
+ if (r < 0)
+ goto fail;
+
+ mounted = true;
+
+ subdir = path_join("/run/systemd/user-home-mount/", user_record_user_name_and_realm(h));
+ if (!subdir) {
+ r = log_oom();
+ goto fail;
+ }
+
+ if (mkdir(subdir, 0700) < 0) {
+ r = log_error_errno(errno, "Failed to create user directory in mounted image file: %m");
+ goto fail;
+ }
+
+ root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (root_fd < 0) {
+ r = log_error_errno(errno, "Failed to open user directory in mounted image file: %m");
+ goto fail;
+ }
+
+ r = home_populate(h, root_fd);
+ if (r < 0)
+ goto fail;
+
+ r = home_sync_and_statfs(root_fd, &sfs);
+ if (r < 0)
+ goto fail;
+
+ r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_LOG, &new_home);
+ if (r < 0) {
+ log_error_errno(r, "Failed to clone record: %m");
+ goto fail;
+ }
+
+ r = user_record_add_binding(
+ new_home,
+ USER_LUKS,
+ disk_uuid_path ?: ip,
+ partition_uuid,
+ luks_uuid,
+ fs_uuid,
+ crypt_get_cipher(cd),
+ crypt_get_cipher_mode(cd),
+ luks_volume_key_size_convert(cd),
+ fstype,
+ NULL,
+ h->uid,
+ (gid_t) h->uid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to add binding to record: %m");
+ goto fail;
+ }
+
+ root_fd = safe_close(root_fd);
+
+ r = umount_verbose("/run/systemd/user-home-mount");
+ if (r < 0)
+ goto fail;
+
+ mounted = false;
+
+ r = crypt_deactivate(cd, dm_name);
+ if (r < 0) {
+ log_error_errno(r, "Failed to deactivate LUKS device: %m");
+ goto fail;
+ }
+
+ dm_activated = false;
+
+ loop = loop_device_unref(loop);
+
+ if (disk_uuid_path)
+ (void) ioctl(image_fd, BLKRRPART, 0);
+
+ /* Let's close the image fd now. If we are operating on a real block device this will release the BSD
+ * lock that ensures udev doesn't interfere with what we are doing */
+ image_fd = safe_close(image_fd);
+
+ if (temporary_image_path) {
+ if (rename(temporary_image_path, ip) < 0) {
+ log_error_errno(errno, "Failed to rename image file: %m");
+ goto fail;
+ }
+
+ log_info("Moved image file into place.");
+ }
+
+ if (disk_uuid_path)
+ (void) wait_for_devlink(disk_uuid_path);
+
+ log_info("Everything completed.");
+
+ print_size_summary(host_size, encrypted_size, &sfs);
+
+ *ret_home = TAKE_PTR(new_home);
+ return 0;
+
+fail:
+ /* Let's close all files before we unmount the file system, to avoid EBUSY */
+ root_fd = safe_close(root_fd);
+
+ if (mounted)
+ (void) umount_verbose("/run/systemd/user-home-mount");
+
+ if (dm_activated)
+ (void) crypt_deactivate(cd, dm_name);
+
+ loop = loop_device_unref(loop);
+
+ if (image_created)
+ (void) unlink(temporary_image_path);
+
+ return r;
+}
+
+int home_validate_update_luks(UserRecord *h, HomeSetup *setup) {
+ _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+ int r;
+
+ assert(h);
+ assert(setup);
+
+ r = make_dm_names(h->user_name, &dm_name, &dm_node);
+ if (r < 0)
+ return r;
+
+ r = access(dm_node, F_OK);
+ if (r < 0 && errno != ENOENT)
+ return log_error_errno(errno, "Failed to determine whether %s exists: %m", dm_node);
+
+ free_and_replace(setup->dm_name, dm_name);
+ free_and_replace(setup->dm_node, dm_node);
+
+ return r >= 0;
+}
+
+enum {
+ CAN_RESIZE_ONLINE,
+ CAN_RESIZE_OFFLINE,
+};
+
+static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) {
+ struct statfs sfs;
+
+ assert(fd >= 0);
+
+ /* Filter out bogus requests early */
+ if (old_size == 0 || old_size == UINT64_MAX ||
+ new_size == 0 || new_size == UINT64_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid resize parameters.");
+
+ if ((old_size & 511) != 0 || (new_size & 511) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Resize parameters not multiple of 512.");
+
+ if (fstatfs(fd, &sfs) < 0)
+ return log_error_errno(errno, "Failed to fstatfs() file system: %m");
+
+ if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) {
+
+ if (new_size < BTRFS_MINIMAL_SIZE)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for btrfs (needs to be 256M at least.");
+
+ /* btrfs can grow and shrink online */
+
+ } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
+
+ if (new_size < XFS_MINIMAL_SIZE)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for xfs (needs to be 14M at least).");
+
+ /* XFS can grow, but not shrink */
+ if (new_size < old_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EMSGSIZE), "Shrinking this type of file system is not supported.");
+
+ } else if (is_fs_type(&sfs, EXT4_SUPER_MAGIC)) {
+
+ if (new_size < EXT4_MINIMAL_SIZE)
+ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for ext4 (needs to be 1M at least).");
+
+ /* ext4 can grow online, and shrink offline */
+ if (new_size < old_size)
+ return CAN_RESIZE_OFFLINE;
+
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(ESOCKTNOSUPPORT), "Resizing this type of file system is not supported.");
+
+ return CAN_RESIZE_ONLINE;
+}
+
+static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard) {
+ _cleanup_free_ char *size_str = NULL;
+ bool re_open = false, re_mount = false;
+ pid_t resize_pid, fsck_pid;
+ int r, exit_status;
+
+ assert(setup);
+ assert(setup->dm_node);
+
+ /* First, unmount the file system */
+ if (setup->root_fd >= 0) {
+ setup->root_fd = safe_close(setup->root_fd);
+ re_open = true;
+ }
+
+ if (setup->undo_mount) {
+ r = umount_verbose("/run/systemd/user-home-mount");
+ if (r < 0)
+ return r;
+
+ setup->undo_mount = false;
+ re_mount = true;
+ }
+
+ log_info("Temporarary unmounting of file system completed.");
+
+ /* resize2fs requires that the file system is force checked first, do so. */
+ r = safe_fork("(e2fsck)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &fsck_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child */
+ execlp("e2fsck" ,"e2fsck", "-fp", setup->dm_node, NULL);
+ log_error_errno(errno, "Failed to execute e2fsck: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ exit_status = wait_for_terminate_and_check("e2fsck", fsck_pid, WAIT_LOG_ABNORMAL);
+ if (exit_status < 0)
+ return exit_status;
+ if ((exit_status & ~FSCK_ERROR_CORRECTED) != 0) {
+ log_warning("e2fsck failed with exit status %i.", exit_status);
+
+ if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "File system is corrupted, refusing.");
+
+ log_warning("Ignoring fsck error.");
+ }
+
+ log_info("Forced file system check completed.");
+
+ /* We use 512 sectors here, because resize2fs doesn't do byte sizes */
+ if (asprintf(&size_str, "%" PRIu64 "s", new_size / 512) < 0)
+ return log_oom();
+
+ /* Resize the thing */
+ r = safe_fork("(e2resize)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, &resize_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child */
+ execlp("resize2fs" ,"resize2fs", setup->dm_node, size_str, NULL);
+ log_error_errno(errno, "Failed to execute resize2fs: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ log_info("Offline file system resize completed.");
+
+ /* Re-establish mounts and reopen the directory */
+ if (re_mount) {
+ r = home_mount_node(setup->dm_node, "ext4", discard);
+ if (r < 0)
+ return r;
+
+ setup->undo_mount = true;
+ }
+
+ if (re_open) {
+ setup->root_fd = open("/run/systemd/user-home-mount", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (setup->root_fd < 0)
+ return log_error_errno(errno, "Failed to reopen file system: %m");
+ }
+
+ log_info("File system mounted again.");
+
+ return 0;
+}
+
+static int prepare_resize_partition(
+ int fd,
+ uint64_t partition_offset,
+ uint64_t old_partition_size,
+ uint64_t new_partition_size,
+ sd_id128_t *ret_disk_uuid,
+ struct fdisk_table **ret_table) {
+
+ _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+ _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
+ _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
+ size_t n_partitions, i;
+ sd_id128_t disk_uuid;
+ bool found = false;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret_disk_uuid);
+ assert(ret_table);
+
+ assert((partition_offset & 511) == 0);
+ assert((old_partition_size & 511) == 0);
+ assert((new_partition_size & 511) == 0);
+ assert(UINT64_MAX - old_partition_size >= partition_offset);
+ assert(UINT64_MAX - new_partition_size >= partition_offset);
+
+ if (partition_offset == 0) {
+ /* If the offset is at the beginning we assume no partition table, let's exit early. */
+ log_debug("Not rewriting partition table, operating on naked device.");
+ *ret_disk_uuid = SD_ID128_NULL;
+ *ret_table = NULL;
+ return 0;
+ }
+
+ c = fdisk_new_context();
+ if (!c)
+ return log_oom();
+
+ if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
+ return log_oom();
+
+ r = fdisk_assign_device(c, path, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open device: %m");
+
+ if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOMEDIUM), "Disk has no GPT partition table.");
+
+ r = fdisk_get_disklabel_id(c, &disk_uuid_as_string);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire disk UUID: %m");
+
+ r = sd_id128_from_string(disk_uuid_as_string, &disk_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed parse disk UUID: %m");
+
+ r = fdisk_get_partitions(c, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire partition table: %m");
+
+ n_partitions = fdisk_table_get_nents(t);
+ for (i = 0; i < n_partitions; i++) {
+ struct fdisk_partition *p;
+
+ p = fdisk_table_get_partition(t, i);
+ if (!p)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+
+ if (fdisk_partition_is_used(p) <= 0)
+ continue;
+ if (fdisk_partition_has_start(p) <= 0 || fdisk_partition_has_size(p) <= 0 || fdisk_partition_has_end(p) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found partition without a size.");
+
+ if (fdisk_partition_get_start(p) == partition_offset / 512U &&
+ fdisk_partition_get_size(p) == old_partition_size / 512U) {
+
+ if (found)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Partition found twice, refusing.");
+
+ /* Found our partition, now patch it */
+ r = fdisk_partition_size_explicit(p, 1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable explicit partition size: %m");
+
+ r = fdisk_partition_set_size(p, new_partition_size / 512U);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change partition size: %m");
+
+ found = true;
+ continue;
+
+ } else {
+ if (fdisk_partition_get_start(p) < partition_offset + new_partition_size / 512U &&
+ fdisk_partition_get_end(p) >= partition_offset / 512)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't extend, conflicting partition found.");
+ }
+ }
+
+ if (!found)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to find matching partition to resize.");
+
+ *ret_table = TAKE_PTR(t);
+ *ret_disk_uuid = disk_uuid;
+
+ return 1;
+}
+
+static int ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *userdata) {
+ char *result;
+
+ assert(c);
+
+ switch (fdisk_ask_get_type(ask)) {
+
+ case FDISK_ASKTYPE_STRING:
+ result = new(char, 37);
+ if (!result)
+ return log_oom();
+
+ fdisk_ask_string_set_result(ask, id128_to_uuid_string(*(sd_id128_t*) userdata, result));
+ break;
+
+ default:
+ log_debug("Unexpected question from libfdisk, ignoring.");
+ }
+
+ return 0;
+}
+
+static int apply_resize_partition(int fd, sd_id128_t disk_uuids, struct fdisk_table *t) {
+ _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+ _cleanup_free_ void *two_zero_lbas = NULL;
+ _cleanup_free_ char *path = NULL;
+ ssize_t n;
+ int r;
+
+ assert(fd >= 0);
+
+ if (!t) /* no partition table to apply, exit early */
+ return 0;
+
+ two_zero_lbas = malloc0(1024U);
+ if (!two_zero_lbas)
+ return log_oom();
+
+ /* libfdisk appears to get confused by the existing PMBR. Let's explicitly flush it out. */
+ n = pwrite(fd, two_zero_lbas, 1024U, 0);
+ if (n < 0)
+ return log_error_errno(errno, "Failed to wipe partition table: %m");
+ if (n != 1024)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while whiping partition table.");
+
+ c = fdisk_new_context();
+ if (!c)
+ return log_oom();
+
+ if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
+ return log_oom();
+
+ r = fdisk_assign_device(c, path, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open device: %m");
+
+ r = fdisk_create_disklabel(c, "gpt");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create GPT disk label: %m");
+
+ r = fdisk_apply_table(c, t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to apply partition table: %m");
+
+ r = fdisk_set_ask(c, ask_cb, &disk_uuids);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set libfdisk query function: %m");
+
+ r = fdisk_set_disklabel_id(c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change disklabel ID: %m");
+
+ r = fdisk_write_disklabel(c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write disk label: %m");
+
+ return 1;
+}
+
+int home_resize_luks(
+ UserRecord *h,
+ bool already_activated,
+ char ***pkcs11_decrypted_passwords,
+ HomeSetup *setup,
+ UserRecord **ret_home) {
+
+ char buffer1[FORMAT_BYTES_MAX], buffer2[FORMAT_BYTES_MAX], buffer3[FORMAT_BYTES_MAX],
+ buffer4[FORMAT_BYTES_MAX], buffer5[FORMAT_BYTES_MAX], buffer6[FORMAT_BYTES_MAX];
+ uint64_t old_image_size, new_image_size, old_fs_size, new_fs_size, crypto_offset, new_partition_size;
+ _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
+ _cleanup_(fdisk_unref_tablep) struct fdisk_table *table = NULL;
+ _cleanup_free_ char *whole_disk = NULL;
+ _cleanup_close_ int image_fd = -1;
+ sd_id128_t disk_uuid;
+ const char *ip, *ipo;
+ struct statfs sfs;
+ struct stat st;
+ int r, resize_type;
+
+ assert(h);
+ assert(user_record_storage(h) == USER_LUKS);
+ assert(setup);
+ assert(ret_home);
+
+ assert_se(ipo = user_record_image_path(h));
+ ip = strdupa(ipo); /* copy out since original might change later in home record object */
+
+ image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (image_fd < 0)
+ return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+
+ if (fstat(image_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat image file %s: %m", ip);
+ if (S_ISBLK(st.st_mode)) {
+ dev_t parent;
+
+ r = block_get_whole_disk(st.st_rdev, &parent);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire whole block device for %s: %m", ip);
+ if (r > 0) {
+ /* If we shall resize a file system on a partition device, then let's figure out the
+ * whole disk device and operate on that instead, since we need to rewrite the
+ * partition table to resize the partition. */
+
+ log_info("Operating on partition device %s, using parent device.", ip);
+
+ r = device_path_make_major_minor(st.st_mode, parent, &whole_disk);
+ if (r < 0)
+ return log_error_errno(r, "Failed to derive whole disk path for %s: %m", ip);
+
+ safe_close(image_fd);
+
+ image_fd = open(whole_disk, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (image_fd < 0)
+ return log_error_errno(errno, "Failed to open whole block device %s: %m", whole_disk);
+
+ if (fstat(image_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat whole block device %s: %m", whole_disk);
+ if (!S_ISBLK(st.st_mode))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Whole block device %s is not actually a block device, refusing.", whole_disk);
+ } else
+ log_info("Operating on whole block device %s.", ip);
+
+ if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0)
+ return log_error_errno(errno, "Failed to determine size of original block device: %m");
+
+ if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
+ return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
+
+ new_image_size = old_image_size; /* we can't resize physical block devices */
+ } else {
+ r = stat_verify_regular(&st);
+ if (r < 0)
+ return log_error_errno(r, "Image file %s is not a block device nor regular: %m", ip);
+
+ old_image_size = st.st_size;
+
+ /* Note an asymetry here: when we operate on loopback files the specified disk size we get we
+ * apply onto the loopback file as a whole. When we operate on block devices we instead apply
+ * to the partition itself only. */
+
+ new_image_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
+ if (new_image_size == old_image_size) {
+ log_info("Image size already matching, skipping operation.");
+ return 0;
+ }
+ }
+
+ r = home_prepare_luks(h, already_activated, whole_disk, pkcs11_decrypted_passwords, setup, &header_home);
+ if (r < 0)
+ return r;
+
+ r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ if (r < 0)
+ return r;
+
+ log_info("offset = %" PRIu64 ", size = %" PRIu64 ", image = %" PRIu64, setup->partition_offset, setup->partition_size, old_image_size);
+
+ if ((UINT64_MAX - setup->partition_offset) < setup->partition_size ||
+ setup->partition_offset + setup->partition_size > old_image_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Old partition doesn't fit in backing storage, refusing.");
+
+ if (S_ISREG(st.st_mode)) {
+ uint64_t partition_table_extra;
+
+ partition_table_extra = old_image_size - setup->partition_size;
+ if (new_image_size <= partition_table_extra)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than partition table metadata.");
+
+ new_partition_size = new_image_size - partition_table_extra;
+ } else {
+ assert(S_ISBLK(st.st_mode));
+
+ new_partition_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
+ if (new_partition_size == setup->partition_size) {
+ log_info("Partition size already matching, skipping operation.");
+ return 0;
+ }
+ }
+
+ if ((UINT64_MAX - setup->partition_offset) < new_partition_size ||
+ setup->partition_offset + new_partition_size > new_image_size)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New partition doesn't fit into backing storage, refusing.");
+
+ crypto_offset = crypt_get_data_offset(setup->crypt_device);
+ if (setup->partition_size / 512U <= crypto_offset)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weird, old crypto payload offset doesn't actually fit in partition size?");
+ if (new_partition_size / 512U <= crypto_offset)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than crypto payload offset?");
+
+ old_fs_size = (setup->partition_size / 512U - crypto_offset) * 512U;
+ new_fs_size = (new_partition_size / 512U - crypto_offset) * 512U;
+
+ /* Before we start doing anything, let's figure out if we actually can */
+ resize_type = can_resize_fs(setup->root_fd, old_fs_size, new_fs_size);
+ if (resize_type < 0)
+ return resize_type;
+ if (resize_type == CAN_RESIZE_OFFLINE && already_activated)
+ return log_error_errno(SYNTHETIC_ERRNO(ETXTBSY), "File systems of this type can only be resized offline, but is currently online.");
+
+ log_info("Ready to resize image size %s → %s, partition size %s → %s, file system size %s → %s.",
+ format_bytes(buffer1, sizeof(buffer1), old_image_size),
+ format_bytes(buffer2, sizeof(buffer2), new_image_size),
+ format_bytes(buffer3, sizeof(buffer3), setup->partition_size),
+ format_bytes(buffer4, sizeof(buffer4), new_partition_size),
+ format_bytes(buffer5, sizeof(buffer5), old_fs_size),
+ format_bytes(buffer6, sizeof(buffer6), new_fs_size));
+
+ r = prepare_resize_partition(
+ image_fd,
+ setup->partition_offset,
+ setup->partition_size,
+ new_partition_size,
+ &disk_uuid,
+ &table);
+ if (r < 0)
+ return r;
+
+ if (new_fs_size > old_fs_size) {
+
+ if (S_ISREG(st.st_mode)) {
+ /* Grow file size */
+
+ if (user_record_luks_discard(h))
+ r = ftruncate(image_fd, new_image_size);
+ else
+ r = fallocate(image_fd, 0, 0, new_image_size);
+ if (r < 0) {
+ if (ERRNO_IS_DISK_SPACE(errno)) {
+ log_debug_errno(errno, "Not enough disk space to grow home.");
+ return -ENOSPC; /* make recognizable */
+ }
+
+ return log_error_errno(errno, "Failed to grow image file %s: %m", ip);
+ }
+
+ log_info("Growing of image file completed.");
+ }
+
+ /* Make sure loopback device sees the new bigger size */
+ r = loop_device_refresh_size(setup->loop, UINT64_MAX, new_partition_size);
+ if (r == -ENOTTY)
+ log_debug_errno(r, "Device is not a loopback device, not refreshing size.");
+ else if (r < 0)
+ return log_error_errno(r, "Failed to refresh loopback device size: %m");
+ else
+ log_info("Refreshing loop device size completed.");
+
+ r = apply_resize_partition(image_fd, disk_uuid, table);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ log_info("Growing of partition completed.");
+
+ if (ioctl(image_fd, BLKRRPART, 0) < 0)
+ log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
+
+ /* Tell LUKS about the new bigger size too */
+ r = crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512U);
+ if (r < 0)
+ return log_error_errno(r, "Failed to grow LUKS device: %m");
+
+ log_info("LUKS device growing completed.");
+ } else {
+ r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+ if (r < 0)
+ return r;
+
+ if (S_ISREG(st.st_mode)) {
+ if (user_record_luks_discard(h))
+ /* Before we shrink, let's trim the file system, so that we need less space on disk during the shrinking */
+ (void) run_fitrim(setup->root_fd);
+ else {
+ /* If discard is off, let's ensure all backing blocks are allocated, so that our resize operation doesn't fail half-way */
+ r = run_fallocate(image_fd, &st);
+ if (r < 0)
+ return r;
+ }
+ }
+ }
+
+ /* Now resize the file system */
+ if (resize_type == CAN_RESIZE_ONLINE)
+ r = resize_fs(setup->root_fd, new_fs_size, NULL);
+ else
+ r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h));
+ if (r < 0)
+ return log_error_errno(r, "Failed to resize file system: %m");
+
+ log_info("File system resizing completed.");
+
+ /* Immediately sync afterwards */
+ r = home_sync_and_statfs(setup->root_fd, NULL);
+ if (r < 0)
+ return r;
+
+ if (new_fs_size < old_fs_size) {
+
+ /* Shrink the LUKS device now, matching the new file system size */
+ r = crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to shrink LUKS device: %m");
+
+ log_info("LUKS device shrinking completed.");
+
+ if (S_ISREG(st.st_mode)) {
+ /* Shrink the image file */
+ if (ftruncate(image_fd, new_image_size) < 0)
+ return log_error_errno(errno, "Failed to shrink image file %s: %m", ip);
+
+ log_info("Shrinking of image file completed.");
+ }
+
+ /* Refresh the loop devices size */
+ r = loop_device_refresh_size(setup->loop, UINT64_MAX, new_partition_size);
+ if (r == -ENOTTY)
+ log_debug_errno(r, "Device is not a loopback device, not refreshing size.");
+ else if (r < 0)
+ return log_error_errno(r, "Failed to refresh loopback device size: %m");
+ else
+ log_info("Refreshing loop device size completed.");
+
+ r = apply_resize_partition(image_fd, disk_uuid, table);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ log_info("Shrinking of partition completed.");
+
+ if (ioctl(image_fd, BLKRRPART, 0) < 0)
+ log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
+ } else {
+ r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+ if (r < 0)
+ return r;
+ }
+
+ r = home_store_header_identity_luks(new_home, setup, header_home);
+ if (r < 0)
+ return r;
+
+ r = home_extend_embedded_identity(new_home, h, setup);
+ if (r < 0)
+ return r;
+
+ if (user_record_luks_discard(h))
+ (void) run_fitrim(setup->root_fd);
+
+ r = home_sync_and_statfs(setup->root_fd, &sfs);
+ if (r < 0)
+ return r;
+
+ r = home_setup_undo(setup);
+ if (r < 0)
+ return r;
+
+ log_info("Everything completed.");
+
+ print_size_summary(new_image_size, new_fs_size, &sfs);
+
+ *ret_home = TAKE_PTR(new_home);
+ return 0;
+}
+
+int home_passwd_luks(
+ UserRecord *h,
+ HomeSetup *setup,
+ char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
+ char **effective_passwords /* new passwords */) {
+
+ size_t volume_key_size, i, max_key_slots, n_effective;
+ _cleanup_(erase_and_freep) void *volume_key = NULL;
+ struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
+ const char *type;
+ int r;
+
+ assert(h);
+ assert(user_record_storage(h) == USER_LUKS);
+ assert(setup);
+
+ type = crypt_get_type(setup->crypt_device);
+ if (!type)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine crypto device type.");
+
+ r = crypt_keyslot_max(type);
+ if (r <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine number of key slots.");
+ max_key_slots = r;
+
+ r = crypt_get_volume_key_size(setup->crypt_device);
+ if (r <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine volume key size.");
+ volume_key_size = (size_t) r;
+
+ volume_key = malloc(volume_key_size);
+ if (!volume_key)
+ return log_oom();
+
+ r = luks_try_passwords(setup->crypt_device, pkcs11_decrypted_passwords, volume_key, &volume_key_size);
+ if (r == -ENOKEY) {
+ r = luks_try_passwords(setup->crypt_device, h->password, volume_key, &volume_key_size);
+ if (r == -ENOKEY)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+
+ n_effective = strv_length(effective_passwords);
+
+ build_good_pbkdf(&good_pbkdf, h);
+ build_minimal_pbkdf(&minimal_pbkdf, h);
+
+ for (i = 0; i < max_key_slots; i++) {
+ r = crypt_keyslot_destroy(setup->crypt_device, i);
+ if (r < 0 && !IN_SET(r, -ENOENT, -EINVAL)) /* Returns EINVAL or ENOENT if there's no key in this slot already */
+ return log_error_errno(r, "Failed to destroy LUKS password: %m");
+
+ if (i >= n_effective) {
+ if (r >= 0)
+ log_info("Destroyed LUKS key slot %zu.", i);
+ continue;
+ }
+
+ if (strv_find(pkcs11_decrypted_passwords, effective_passwords[i])) {
+ log_debug("Using minimal PBKDF for slot %zu", i);
+ r = crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
+ } else {
+ log_debug("Using good PBKDF for slot %zu", i);
+ r = crypt_set_pbkdf_type(setup->crypt_device, &good_pbkdf);
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to tweak PBKDF for slot %zu: %m", i);
+
+ r = crypt_keyslot_add_by_volume_key(
+ setup->crypt_device,
+ i,
+ volume_key,
+ volume_key_size,
+ effective_passwords[i],
+ strlen(effective_passwords[i]));
+ if (r < 0)
+ return log_error_errno(r, "Failed to set up LUKS password: %m");
+
+ log_info("Updated LUKS key slot %zu.", i);
+ }
+
+ return 1;
+}
+
+int home_lock_luks(UserRecord *h) {
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+ _cleanup_close_ int root_fd = -1;
+ const char *p;
+ int r;
+
+ assert(h);
+
+ assert_se(p = user_record_home_directory(h));
+ root_fd = open(p, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+ if (root_fd < 0)
+ return log_error_errno(errno, "Failed to open home directory: %m");
+
+ r = make_dm_names(h->user_name, &dm_name, &dm_node);
+ if (r < 0)
+ return r;
+
+ r = crypt_init_by_name(&cd, dm_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+ log_info("Discovered used LUKS device %s.", dm_node);
+ crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+ if (syncfs(root_fd) < 0) /* Snake oil, but let's better be safe than sorry */
+ return log_error_errno(errno, "Failed to synchronize file system %s: %m", p);
+
+ root_fd = safe_close(root_fd);
+
+ log_info("File system synchronized.");
+
+ /* Note that we don't invoke FIFREEZE here, it appears libcryptsetup/device-mapper already does that on its own for us */
+
+ r = crypt_suspend(cd, dm_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to suspend cryptsetup device: %s: %m", dm_node);
+
+ log_info("LUKS device suspended.");
+ return 0;
+}
+
+static int luks_try_resume(
+ struct crypt_device *cd,
+ const char *dm_name,
+ char **password) {
+
+ char **pp;
+ int r;
+
+ assert(cd);
+ assert(dm_name);
+
+ STRV_FOREACH(pp, password) {
+ r = crypt_resume_by_passphrase(
+ cd,
+ dm_name,
+ CRYPT_ANY_SLOT,
+ *pp,
+ strlen(*pp));
+ if (r >= 0) {
+ log_info("Resumed LUKS device %s.", dm_name);
+ return 0;
+ }
+
+ log_debug_errno(r, "Password %zu didn't work for resuming device: %m", (size_t) (pp - password));
+ }
+
+ return -ENOKEY;
+}
+
+int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
+ _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+ _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+ int r;
+
+ assert(h);
+
+ r = make_dm_names(h->user_name, &dm_name, &dm_node);
+ if (r < 0)
+ return r;
+
+ r = crypt_init_by_name(&cd, dm_name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+ log_info("Discovered used LUKS device %s.", dm_node);
+ crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+ r = luks_try_resume(cd, dm_name, pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL);
+ if (r == -ENOKEY) {
+ r = luks_try_resume(cd, dm_name, h->password);
+ if (r == -ENOKEY)
+ return log_error_errno(r, "No valid password for LUKS superblock.");
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to resume LUKS superblock: %m");
+
+ log_info("LUKS device resumed.");
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "crypt-util.h"
+#include "homework.h"
+#include "user-record.h"
+
+int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_luks_home);
+
+int home_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_deactivate_luks(UserRecord *h);
+
+int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home);
+
+int home_create_luks(UserRecord *h, char **pkcs11_decrypted_passwords, char **effective_passwords, UserRecord **ret_home);
+
+int home_validate_update_luks(UserRecord *h, HomeSetup *setup);
+
+int home_resize_luks(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
+
+int home_passwd_luks(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
+
+int home_lock_luks(UserRecord *h);
+int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords);
+
+static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
+ int k;
+
+ assert(cd);
+
+ /* Convert the "int" to uint64_t, which we usually use for byte sizes stored on disk. */
+
+ k = crypt_get_volume_key_size(cd);
+ if (k <= 0)
+ return UINT64_MAX;
+
+ return (uint64_t) k;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sched.h>
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "homework-mount.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "string-util.h"
+
+static const char *mount_options_for_fstype(const char *fstype) {
+ if (streq(fstype, "ext4"))
+ return "noquota,user_xattr";
+ if (streq(fstype, "xfs"))
+ return "noquota";
+ if (streq(fstype, "btrfs"))
+ return "noacl";
+ return NULL;
+}
+
+int home_mount_node(const char *node, const char *fstype, bool discard) {
+ _cleanup_free_ char *joined = NULL;
+ const char *options, *discard_option;
+ int r;
+
+ options = mount_options_for_fstype(fstype);
+
+ discard_option = discard ? "discard" : "nodiscard";
+
+ if (options) {
+ joined = strjoin(options, ",", discard_option);
+ if (!joined)
+ return log_oom();
+
+ options = joined;
+ } else
+ options = discard_option;
+
+ r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, MS_NODEV|MS_NOSUID|MS_RELATIME, strempty(options));
+ if (r < 0)
+ return r;
+
+ log_info("Mounting file system completed.");
+ return 0;
+}
+
+int home_unshare_and_mount(const char *node, const char *fstype, bool discard) {
+ int r;
+
+ if (unshare(CLONE_NEWNS) < 0)
+ return log_error_errno(errno, "Couldn't unshare file system namespace: %m");
+
+ r = mount_verbose(LOG_ERR, "/run", "/run", NULL, MS_SLAVE|MS_REC, NULL); /* Mark /run as MS_SLAVE in our new namespace */
+ if (r < 0)
+ return r;
+
+ (void) mkdir_p("/run/systemd/user-home-mount", 0700);
+
+ if (node)
+ return home_mount_node(node, fstype, discard);
+
+ return 0;
+}
+
+int home_move_mount(const char *user_name_and_realm, const char *target) {
+ _cleanup_free_ char *subdir = NULL;
+ const char *d;
+ int r;
+
+ assert(user_name_and_realm);
+ assert(target);
+
+ if (user_name_and_realm) {
+ subdir = path_join("/run/systemd/user-home-mount/", user_name_and_realm);
+ if (!subdir)
+ return log_oom();
+
+ d = subdir;
+ } else
+ d = "/run/systemd/user-home-mount/";
+
+ (void) mkdir_p(target, 0700);
+
+ r = mount_verbose(LOG_ERR, d, target, NULL, MS_BIND, NULL);
+ if (r < 0)
+ return r;
+
+ r = umount_verbose("/run/systemd/user-home-mount");
+ if (r < 0)
+ return r;
+
+ log_info("Moving to final mount point %s completed.", target);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+int home_mount_node(const char *node, const char *fstype, bool discard);
+int home_unshare_and_mount(const char *node, const char *fstype, bool discard);
+int home_move_mount(const char *user_name_and_realm, const char *target);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "hexdecoct.h"
+#include "homework-pkcs11.h"
+#include "pkcs11-util.h"
+#include "strv.h"
+
+int pkcs11_callback(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_SLOT_ID slot_id,
+ const CK_SLOT_INFO *slot_info,
+ const CK_TOKEN_INFO *token_info,
+ P11KitUri *uri,
+ void *userdata) {
+
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+ struct pkcs11_callback_data *data = userdata;
+ _cleanup_free_ char *token_label = NULL;
+ CK_TOKEN_INFO updated_token_info;
+ size_t decrypted_key_size;
+ CK_OBJECT_HANDLE object;
+ char **i;
+ CK_RV rv;
+ int r;
+
+ assert(m);
+ assert(slot_info);
+ assert(token_info);
+ assert(uri);
+ assert(data);
+
+ /* Special return values:
+ *
+ * -ENOANO → if we need a PIN but have none
+ * -ERFKILL → if a "protected authentication path" is needed but we have no OK to use it
+ * -EOWNERDEAD → if the PIN is locked
+ * -ENOLCK → if the supplied PIN is incorrect
+ * -ETOOMANYREFS → ditto, but only a few tries left
+ * -EUCLEAN → ditto, but only a single try left
+ */
+
+ token_label = pkcs11_token_label(token_info);
+ if (!token_label)
+ return log_oom();
+
+ if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
+
+ if (data->secret->pkcs11_protected_authentication_path_permitted <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ERFKILL), "Security token requires authentication through protected authentication path.");
+
+ rv = m->C_Login(session, CKU_USER, NULL, 0);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+
+ log_info("Successully logged into security token '%s' via protected authentication path.", token_label);
+ goto decrypt;
+ }
+
+ if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
+ log_info("No login into security token '%s' required.", token_label);
+ goto decrypt;
+ }
+
+ if (strv_isempty(data->secret->pkcs11_pin))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security Token requires PIN.");
+
+ STRV_FOREACH(i, data->secret->pkcs11_pin) {
+ rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
+ if (rv == CKR_OK) {
+ log_info("Successfully logged into security token '%s' with PIN.", token_label);
+ goto decrypt;
+ }
+ if (rv == CKR_PIN_LOCKED)
+ return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD), "PIN of security token is blocked. Please unblock it first.");
+ if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+ }
+
+ rv = m->C_GetTokenInfo(slot_id, &updated_token_info);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire updated security token information for slot %lu: %s", slot_id, p11_kit_strerror(rv));
+
+ if (FLAGS_SET(updated_token_info.flags, CKF_USER_PIN_FINAL_TRY))
+ return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "PIN of security token incorrect, only a single try left.");
+ if (FLAGS_SET(updated_token_info.flags, CKF_USER_PIN_COUNT_LOW))
+ return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS), "PIN of security token incorrect, only a few tries left.");
+
+ return log_error_errno(SYNTHETIC_ERRNO(ENOLCK), "PIN of security token incorrect.");
+
+decrypt:
+ r = pkcs11_token_find_private_key(m, session, uri, &object);
+ if (r < 0)
+ return r;
+
+ r = pkcs11_token_decrypt_data(m, session, object, data->encrypted_key->data, data->encrypted_key->size, &decrypted_key, &decrypted_key_size);
+ if (r < 0)
+ return r;
+
+ if (base64mem(decrypted_key, decrypted_key_size, &data->decrypted_password) < 0)
+ return log_oom();
+
+ return 1;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if HAVE_P11KIT
+#include "memory-util.h"
+#include "user-record.h"
+#include "pkcs11-util.h"
+
+struct pkcs11_callback_data {
+ UserRecord *user_record;
+ UserRecord *secret;
+ Pkcs11EncryptedKey *encrypted_key;
+ char *decrypted_password;
+};
+
+static inline void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
+ erase_and_free(data->decrypted_password);
+}
+
+int pkcs11_callback(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slot_id, const CK_SLOT_INFO *slot_info, const CK_TOKEN_INFO *token_info, P11KitUri *uri, void *userdata);
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <sys/quota.h>
+
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "errno-util.h"
+#include "format-util.h"
+#include "homework-quota.h"
+#include "missing_magic.h"
+#include "quota-util.h"
+#include "stat-util.h"
+#include "user-util.h"
+
+int home_update_quota_btrfs(UserRecord *h, const char *path) {
+ int r;
+
+ assert(h);
+ assert(path);
+
+ if (h->disk_size == UINT64_MAX)
+ return 0;
+
+ /* If the user wants quota, enable it */
+ r = btrfs_quota_enable(path, true);
+ if (r == -ENOTTY)
+ return log_error_errno(r, "No btrfs quota support on subvolume %s.", path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable btrfs quota support on %s.", path);
+
+ r = btrfs_qgroup_set_limit(path, 0, h->disk_size);
+ if (r < 0)
+ return log_error_errno(r, "Faled to set disk quota on subvolume %s: %m", path);
+
+ log_info("Set btrfs quota.");
+
+ return 0;
+}
+
+int home_update_quota_classic(UserRecord *h, const char *path) {
+ struct dqblk req;
+ dev_t devno;
+ int r;
+
+ assert(h);
+ assert(uid_is_valid(h->uid));
+ assert(path);
+
+ if (h->disk_size == UINT64_MAX)
+ return 0;
+
+ r = get_block_device(path, &devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine block device of %s: %m", path);
+ if (devno == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system %s not backed by a block device.", path);
+
+ r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), devno, h->uid, &req);
+ if (r < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(r))
+ return log_error_errno(r, "No UID quota support on %s.", path);
+
+ if (r != -ESRCH)
+ return log_error_errno(r, "Failed to query disk quota for UID " UID_FMT ": %m", h->uid);
+
+ zero(req);
+ } else {
+ /* Shortcut things if everything is set up properly already */
+ if (FLAGS_SET(req.dqb_valid, QIF_BLIMITS) && h->disk_size / QIF_DQBLKSIZE == req.dqb_bhardlimit) {
+ log_info("Configured quota already matches the intended setting, not updating quota.");
+ return 0;
+ }
+ }
+
+ req.dqb_valid = QIF_BLIMITS;
+ req.dqb_bsoftlimit = req.dqb_bhardlimit = h->disk_size / QIF_DQBLKSIZE;
+
+ r = quotactl_devno(QCMD_FIXED(Q_SETQUOTA, USRQUOTA), devno, h->uid, &req);
+ if (r < 0) {
+ if (r == -ESRCH)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "UID quota not available on %s.", path);
+
+ return log_error_errno(r, "Failed to set disk quota for UID " UID_FMT ": %m", h->uid);
+ }
+
+ log_info("Updated per-UID quota.");
+
+ return 0;
+}
+
+int home_update_quota_auto(UserRecord *h, const char *path) {
+ struct statfs sfs;
+ int r;
+
+ assert(h);
+
+ if (h->disk_size == UINT64_MAX)
+ return 0;
+
+ if (!path) {
+ path = user_record_image_path(h);
+ if (!path)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home record lacks image path.");
+ }
+
+ if (statfs(path, &sfs) < 0)
+ return log_error_errno(errno, "Failed to statfs() file system: %m");
+
+ if (is_fs_type(&sfs, XFS_SB_MAGIC) ||
+ is_fs_type(&sfs, EXT4_SUPER_MAGIC))
+ return home_update_quota_classic(h, path);
+
+ if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) {
+
+ r = btrfs_is_subvol(path);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to test if %s is a subvolume: %m", path);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Directory %s is not a subvolume, cannot apply quota.", path);
+
+ return home_update_quota_btrfs(h, path);
+ }
+
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Type of directory %s not known, cannot apply quota.", path);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "user-record.h"
+
+int home_update_quota_btrfs(UserRecord *h, const char *path);
+int home_update_quota_classic(UserRecord *h, const char *path);
+int home_update_quota_auto(UserRecord *h, const char *path);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stddef.h>
+#include <sys/mount.h>
+
+#include "chown-recursive.h"
+#include "copy.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "home-util.h"
+#include "homework-cifs.h"
+#include "homework-directory.h"
+#include "homework-fscrypt.h"
+#include "homework-luks.h"
+#include "homework-mount.h"
+#include "homework-pkcs11.h"
+#include "homework.h"
+#include "main-func.h"
+#include "memory-util.h"
+#include "missing_magic.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "pkcs11-util.h"
+#include "rm-rf.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "user-util.h"
+#include "virt.h"
+
+/* Make sure a bad password always results in a 3s delay, no matter what */
+#define BAD_PASSWORD_DELAY_USEC (3 * USEC_PER_SEC)
+
+int user_record_authenticate(
+ UserRecord *h,
+ UserRecord *secret,
+ char ***pkcs11_decrypted_passwords) {
+
+ bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false,
+ pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false;
+ size_t n;
+ int r;
+
+ assert(h);
+ assert(secret);
+
+ /* Tries to authenticate a user record with the supplied secrets. i.e. checks whether at least one
+ * supplied plaintext passwords matches a hashed password field of the user record. Or if a
+ * configured PKCS#11 token is around and can unlock the record.
+ *
+ * Note that the pkcs11_decrypted_passwords parameter is both an input and and output parameter: it
+ * is a list of configured, decrypted PKCS#11 passwords. We typically have to call this function
+ * multiple times over the course of an operation (think: on login we authenticate the host user
+ * record, the record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a
+ * list of passwords we already decrypted, so that we don't have to do the (slow an potentially
+ * interactive) PKCS#11 dance for the relevant token again and again. */
+
+ /* First, let's see if the supplied plain-text passwords work? */
+ r = user_record_test_secret(h, secret);
+ if (r == -ENOKEY) {
+ log_info_errno(r, "None of the supplied plaintext passwords unlocks the user record's hashed passwords.");
+ need_password = true;
+ } else if (r == -ENXIO)
+ log_debug_errno(r, "User record has no hashed passwords, plaintext passwords not tested.");
+ else if (r < 0)
+ return log_error_errno(r, "Failed to validate password of record: %m");
+ else {
+ log_info("Provided password unlocks user record.");
+ return 0;
+ }
+
+ /* Second, let's see if any of the PKCS#11 security tokens are plugged in and help us */
+ for (n = 0; n < h->n_pkcs11_encrypted_key; n++) {
+#if HAVE_P11KIT
+ _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
+ .user_record = h,
+ .secret = secret,
+ .encrypted_key = h->pkcs11_encrypted_key + n,
+ };
+ char **pp;
+
+ /* See if any of the previously calculated passwords work */
+ STRV_FOREACH(pp, *pkcs11_decrypted_passwords) {
+ r = test_password_one(data.encrypted_key->hashed_password, *pp);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m");
+ if (r > 0) {
+ log_info("Previously acquired PKCS#11 password unlocks user record.");
+ return 0;
+ }
+ }
+
+ r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data);
+ switch (r) {
+ case -EAGAIN:
+ need_token = true;
+ break;
+ case -ENOANO:
+ need_pin = true;
+ break;
+ case -ERFKILL:
+ need_protected_authentication_path_permitted = true;
+ break;
+ case -EOWNERDEAD:
+ pin_locked = true;
+ break;
+ case -ENOLCK:
+ pin_incorrect = true;
+ break;
+ case -ETOOMANYREFS:
+ pin_incorrect = pin_incorrect_few_tries_left = true;
+ break;
+ case -EUCLEAN:
+ pin_incorrect = pin_incorrect_few_tries_left = pin_incorrect_one_try_left = true;
+ break;
+ default:
+ if (r < 0)
+ return r;
+
+ r = test_password_one(data.encrypted_key->hashed_password, data.decrypted_password);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test PKCS#11 password: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Configured PKCS#11 security token %s does not decrypt encrypted key correctly.", data.encrypted_key->uri);
+
+ log_info("Decrypted password from PKCS#11 security token %s unlocks user record.", data.encrypted_key->uri);
+
+ r = strv_extend(pkcs11_decrypted_passwords, data.decrypted_password);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+ }
+#else
+ need_token = true;
+ break;
+#endif
+ }
+
+ /* Ordered by "relevance", i.e. the most "important" or "interesting" error condition is returned. */
+ if (pin_incorrect_one_try_left)
+ return -EUCLEAN;
+ if (pin_incorrect_few_tries_left)
+ return -ETOOMANYREFS;
+ if (pin_incorrect)
+ return -ENOLCK;
+ if (pin_locked)
+ return -EOWNERDEAD;
+ if (need_protected_authentication_path_permitted)
+ return -ERFKILL;
+ if (need_pin)
+ return -ENOANO;
+ if (need_token)
+ return -EBADSLT;
+ if (need_password)
+ return -ENOKEY;
+
+ /* Hmm, this means neither PCKS#11 nor classic hashed passwords were supplied, we cannot authenticate this reasonably */
+ return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED), "No hashed passwords and no PKCS#11 tokens defined, cannot authenticate user record.");
+}
+
+int home_setup_undo(HomeSetup *setup) {
+ int r = 0, q;
+
+ assert(setup);
+
+ setup->root_fd = safe_close(setup->root_fd);
+
+ if (setup->undo_mount) {
+ q = umount_verbose("/run/systemd/user-home-mount");
+ if (q < 0)
+ r = q;
+ }
+
+ if (setup->undo_dm && setup->crypt_device && setup->dm_name) {
+ q = crypt_deactivate(setup->crypt_device, setup->dm_name);
+ if (q < 0)
+ r = q;
+ }
+
+ setup->undo_mount = false;
+ setup->undo_dm = false;
+
+ setup->dm_name = mfree(setup->dm_name);
+ setup->dm_node = mfree(setup->dm_node);
+
+ setup->loop = loop_device_unref(setup->loop);
+ crypt_free(setup->crypt_device);
+ setup->crypt_device = NULL;
+
+ explicit_bzero_safe(setup->volume_key, setup->volume_key_size);
+ setup->volume_key = mfree(setup->volume_key);
+ setup->volume_key_size = 0;
+
+ return r;
+}
+
+int home_prepare(
+ UserRecord *h,
+ bool already_activated,
+ char ***pkcs11_decrypted_passwords,
+ HomeSetup *setup,
+ UserRecord **ret_header_home) {
+
+ int r;
+
+ assert(h);
+ assert(setup);
+ assert(!setup->loop);
+ assert(!setup->crypt_device);
+ assert(setup->root_fd < 0);
+ assert(!setup->undo_dm);
+ assert(!setup->undo_mount);
+
+ /* Makes a home directory accessible (through the root_fd file descriptor, not by path!). */
+
+ switch (user_record_storage(h)) {
+
+ case USER_LUKS:
+ return home_prepare_luks(h, already_activated, NULL, pkcs11_decrypted_passwords, setup, ret_header_home);
+
+ case USER_SUBVOLUME:
+ case USER_DIRECTORY:
+ r = home_prepare_directory(h, already_activated, setup);
+ break;
+
+ case USER_FSCRYPT:
+ r = home_prepare_fscrypt(h, already_activated, pkcs11_decrypted_passwords, setup);
+ break;
+
+ case USER_CIFS:
+ r = home_prepare_cifs(h, already_activated, setup);
+ break;
+
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), "Processing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+ }
+
+ if (r < 0)
+ return r;
+
+ if (ret_header_home)
+ *ret_header_home = NULL;
+
+ return r;
+}
+
+int home_sync_and_statfs(int root_fd, struct statfs *ret) {
+ assert(root_fd >= 0);
+
+ /* Let's sync this to disk, so that the disk space reported by fstatfs() below is accurate (for file
+ * systems such as btrfs where this is determined lazily). */
+
+ if (syncfs(root_fd) < 0)
+ return log_error_errno(errno, "Failed to synchronize file system: %m");
+
+ if (ret)
+ if (fstatfs(root_fd, ret) < 0)
+ return log_error_errno(errno, "Failed to statfs() file system: %m");
+
+ log_info("Synchronized disk.");
+
+ return 0;
+}
+
+static int read_identity_file(int root_fd, JsonVariant **ret) {
+ _cleanup_(fclosep) FILE *identity_file = NULL;
+ _cleanup_close_ int identity_fd = -1;
+ unsigned line, column;
+ int r;
+
+ assert(root_fd >= 0);
+ assert(ret);
+
+ identity_fd = openat(root_fd, ".identity", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
+ if (identity_fd < 0)
+ return log_error_errno(errno, "Failed to open .identity file in home directory: %m");
+
+ r = fd_verify_regular(identity_fd);
+ if (r < 0)
+ return log_error_errno(r, "Embedded identity file is not a regular file, refusing: %m");
+
+ identity_file = fdopen(identity_fd, "r");
+ if (!identity_file)
+ return log_oom();
+
+ identity_fd = -1;
+
+ r = json_parse_file(identity_file, ".identity", JSON_PARSE_SENSITIVE, ret, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "[.identity:%u:%u] Failed to parse JSON data: %m", line, column);
+
+ log_info("Read embedded .identity file.");
+
+ return 0;
+}
+
+static int write_identity_file(int root_fd, JsonVariant *v, uid_t uid) {
+ _cleanup_(json_variant_unrefp) JsonVariant *normalized = NULL;
+ _cleanup_(fclosep) FILE *identity_file = NULL;
+ _cleanup_close_ int identity_fd = -1;
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(root_fd >= 0);
+ assert(v);
+
+ normalized = json_variant_ref(v);
+
+ r = json_variant_normalize(&normalized);
+ if (r < 0)
+ log_warning_errno(r, "Failed to normalize user record, ignoring: %m");
+
+ r = tempfn_random(".identity", NULL, &fn);
+ if (r < 0)
+ return r;
+
+ identity_fd = openat(root_fd, fn, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+ if (identity_fd < 0)
+ return log_error_errno(errno, "Failed to create .identity file in home directory: %m");
+
+ identity_file = fdopen(identity_fd, "w");
+ if (!identity_file) {
+ r = log_oom();
+ goto fail;
+ }
+
+ identity_fd = -1;
+
+ json_variant_dump(normalized, JSON_FORMAT_PRETTY, identity_file, NULL);
+
+ r = fflush_and_check(identity_file);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write .identity file: %m");
+ goto fail;
+ }
+
+ if (fchown(fileno(identity_file), uid, uid) < 0) {
+ log_error_errno(r, "Failed to change ownership of identity file: %m");
+ goto fail;
+ }
+
+ if (renameat(root_fd, fn, root_fd, ".identity") < 0) {
+ r = log_error_errno(errno, "Failed to move identity file into place: %m");
+ goto fail;
+ }
+
+ log_info("Wrote embedded .identity file.");
+
+ return 0;
+
+fail:
+ (void) unlinkat(root_fd, fn, 0);
+ return r;
+}
+
+int home_load_embedded_identity(
+ UserRecord *h,
+ int root_fd,
+ UserRecord *header_home,
+ UserReconcileMode mode,
+ char ***pkcs11_decrypted_passwords,
+ UserRecord **ret_embedded_home,
+ UserRecord **ret_new_home) {
+
+ _cleanup_(user_record_unrefp) UserRecord *embedded_home = NULL, *intermediate_home = NULL, *new_home = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ int r;
+
+ assert(h);
+ assert(root_fd >= 0);
+
+ r = read_identity_file(root_fd, &v);
+ if (r < 0)
+ return r;
+
+ embedded_home = user_record_new();
+ if (!embedded_home)
+ return log_oom();
+
+ r = user_record_load(embedded_home, v, USER_RECORD_LOAD_EMBEDDED);
+ if (r < 0)
+ return r;
+
+ if (!user_record_compatible(h, embedded_home))
+ return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Hmbedded home record not compatible with host record, refusing.");
+
+ /* Insist that credentials the user supplies also unlocks any embedded records. */
+ r = user_record_authenticate(embedded_home, h, pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ /* At this point we have three records to deal with:
+ *
+ * · The record we got passed from the host
+ * · The record included in the LUKS header (only if LUKS is used)
+ * · The record in the home directory itself (~.identity)
+ *
+ * Now we have to reconcile all three, and let the newest one win. */
+
+ if (header_home) {
+ /* Note we relax the requirements here. Instead of insisting that the host record is strictly
+ * newer, let's also be OK if its equally new. If it is, we'll however insist that the
+ * embedded record must be newer, so that we update at least one of the two. */
+
+ r = user_record_reconcile(h, header_home, mode == USER_RECONCILE_REQUIRE_NEWER ? USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL : mode, &intermediate_home);
+ if (r == -EREMCHG) /* this was supposed to be checked earlier already, but let's check this again */
+ return log_error_errno(r, "Identity stored on host and in header don't match, refusing.");
+ if (r == -ESTALE)
+ return log_error_errno(r, "Embedded identity record is newer than supplied record, refusing.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to reconcile host and header identities: %m");
+ if (r == USER_RECONCILE_EMBEDDED_WON)
+ log_info("Reconciling header user identity completed (header version was newer).");
+ else if (r == USER_RECONCILE_HOST_WON) {
+ log_info("Reconciling header user identity completed (host version was newer).");
+
+ if (mode == USER_RECONCILE_REQUIRE_NEWER) /* Host version is newer than the header
+ * version, hence we'll update
+ * something. This means we can relax the
+ * requirements on the embedded
+ * identity. */
+ mode = USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL;
+ } else {
+ assert(r == USER_RECONCILE_IDENTICAL);
+ log_info("Reconciling user identities completed (host and header version were identical).");
+ }
+
+ h = intermediate_home;
+ }
+
+ r = user_record_reconcile(h, embedded_home, mode, &new_home);
+ if (r == -EREMCHG)
+ return log_error_errno(r, "Identity stored on host and in home don't match, refusing.");
+ if (r == -ESTALE)
+ return log_error_errno(r, "Embedded identity record is equally new or newer than supplied record, refusing.");
+ if (r < 0)
+ return log_error_errno(r, "Failed to reconcile host and embedded identities: %m");
+ if (r == USER_RECONCILE_EMBEDDED_WON)
+ log_info("Reconciling embedded user identity completed (embedded version was newer).");
+ else if (r == USER_RECONCILE_HOST_WON)
+ log_info("Reconciling embedded user identity completed (host version was newer).");
+ else {
+ assert(r == USER_RECONCILE_IDENTICAL);
+ log_info("Reconciling embedded user identity completed (host and embedded version were identical).");
+ }
+
+ if (ret_embedded_home)
+ *ret_embedded_home = TAKE_PTR(embedded_home);
+
+ if (ret_new_home)
+ *ret_new_home = TAKE_PTR(new_home);
+
+ return 0;
+}
+
+int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home) {
+ _cleanup_(user_record_unrefp) UserRecord *embedded = NULL;
+ int r;
+
+ assert(h);
+ assert(root_fd >= 0);
+ assert(uid_is_valid(uid));
+
+ r = user_record_clone(h, USER_RECORD_EXTRACT_EMBEDDED, &embedded);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine new embedded record: %m");
+
+ if (old_home && user_record_equal(old_home, embedded)) {
+ log_debug("Not updating embedded home record.");
+ return 0;
+ }
+
+ /* The identity has changed, let's update it in the image */
+ r = write_identity_file(root_fd, embedded->json, h->uid);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static const char *file_system_type_fd(int fd) {
+ struct statfs sfs;
+
+ assert(fd >= 0);
+
+ if (fstatfs(fd, &sfs) < 0) {
+ log_debug_errno(errno, "Failed to statfs(): %m");
+ return NULL;
+ }
+
+ if (is_fs_type(&sfs, XFS_SB_MAGIC))
+ return "xfs";
+ if (is_fs_type(&sfs, EXT4_SUPER_MAGIC))
+ return "ext4";
+ if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC))
+ return "btrfs";
+
+ return NULL;
+}
+
+int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup) {
+ int r;
+
+ assert(h);
+ assert(used);
+ assert(setup);
+
+ r = user_record_add_binding(
+ h,
+ user_record_storage(used),
+ user_record_image_path(used),
+ setup->found_partition_uuid,
+ setup->found_luks_uuid,
+ setup->found_fs_uuid,
+ setup->crypt_device ? crypt_get_cipher(setup->crypt_device) : NULL,
+ setup->crypt_device ? crypt_get_cipher_mode(setup->crypt_device) : NULL,
+ setup->crypt_device ? luks_volume_key_size_convert(setup->crypt_device) : UINT64_MAX,
+ file_system_type_fd(setup->root_fd),
+ user_record_home_directory(used),
+ used->uid,
+ (gid_t) used->uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update binding in record: %m");
+
+ return 0;
+}
+
+static int chown_recursive_directory(int root_fd, uid_t uid) {
+ int r;
+
+ assert(root_fd >= 0);
+ assert(uid_is_valid(uid));
+
+ r = fd_chown_recursive(root_fd, uid, (gid_t) uid, 0777);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change ownership of files and directories: %m");
+ if (r == 0)
+ log_info("Recursive changing of ownership not necessary, skipped.");
+ else
+ log_info("Recursive changing of ownership completed.");
+
+ return 0;
+}
+
+int home_refresh(
+ UserRecord *h,
+ HomeSetup *setup,
+ UserRecord *header_home,
+ char ***pkcs11_decrypted_passwords,
+ struct statfs *ret_statfs,
+ UserRecord **ret_new_home) {
+
+ _cleanup_(user_record_unrefp) UserRecord *embedded_home = NULL, *new_home = NULL;
+ int r;
+
+ assert(h);
+ assert(setup);
+ assert(ret_new_home);
+
+ /* When activating a home directory, does the identity work: loads the identity from the $HOME
+ * directory, reconciles it with our idea, chown()s everything. */
+
+ r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ if (r < 0)
+ return r;
+
+ r = home_store_header_identity_luks(new_home, setup, header_home);
+ if (r < 0)
+ return r;
+
+ r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+ if (r < 0)
+ return r;
+
+ r = chown_recursive_directory(setup->root_fd, h->uid);
+ if (r < 0)
+ return r;
+
+ r = home_sync_and_statfs(setup->root_fd, ret_statfs);
+ if (r < 0)
+ return r;
+
+ *ret_new_home = TAKE_PTR(new_home);
+ return 0;
+}
+
+static int home_activate(UserRecord *h, UserRecord **ret_home) {
+ _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ int r;
+
+ assert(h);
+
+ if (!h->user_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks user name, refusing.");
+ if (!uid_is_valid(h->uid))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
+ if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Activating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+ r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ r = user_record_test_home_directory_and_warn(h);
+ if (r < 0)
+ return r;
+ if (r == USER_TEST_MOUNTED)
+ return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "Home directory %s is already mounted, refusing.", user_record_home_directory(h));
+
+ r = user_record_test_image_path_and_warn(h);
+ if (r < 0)
+ return r;
+ if (r == USER_TEST_ABSENT)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Image path %s is missing, refusing.", user_record_image_path(h));
+
+ switch (user_record_storage(h)) {
+
+ case USER_LUKS:
+ r = home_activate_luks(h, &pkcs11_decrypted_passwords, &new_home);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case USER_SUBVOLUME:
+ case USER_DIRECTORY:
+ case USER_FSCRYPT:
+ r = home_activate_directory(h, &pkcs11_decrypted_passwords, &new_home);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case USER_CIFS:
+ r = home_activate_cifs(h, &pkcs11_decrypted_passwords, &new_home);
+ if (r < 0)
+ return r;
+
+ break;
+
+ default:
+ assert_not_reached("unexpected type");
+ }
+
+ /* Note that the returned object might either be a reference to an updated version of the existing
+ * home object, or a reference to a newly allocated home object. The caller has to be able to deal
+ * with both, and consider the old object out-of-date. */
+ if (user_record_equal(h, new_home)) {
+ *ret_home = NULL;
+ return 0; /* no identity change */
+ }
+
+ *ret_home = TAKE_PTR(new_home);
+ return 1; /* identity updated */
+}
+
+static int home_deactivate(UserRecord *h, bool force) {
+ bool done = false;
+ int r;
+
+ assert(h);
+
+ if (!h->user_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record incomplete, refusing.");
+ if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Deactivating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+ r = user_record_test_home_directory_and_warn(h);
+ if (r < 0)
+ return r;
+ if (r == USER_TEST_MOUNTED) {
+ if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0)
+ return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
+
+ log_info("Unmounting completed.");
+ done = true;
+ } else
+ log_info("Directory %s is already unmounted.", user_record_home_directory(h));
+
+ if (user_record_storage(h) == USER_LUKS) {
+ r = home_deactivate_luks(h);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ done = true;
+ }
+
+ if (!done)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home is not active.");
+
+ log_info("Everything completed.");
+ return 0;
+}
+
+static int copy_skel(int root_fd, const char *skel) {
+ int r;
+
+ assert(root_fd >= 0);
+
+ r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE);
+ if (r == -ENOENT) {
+ log_info("Skeleton directory %s missing, ignoring.", skel);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to copy in %s: %m", skel);
+
+ log_info("Copying in %s completed.", skel);
+ return 0;
+}
+
+static int change_access_mode(int root_fd, mode_t m) {
+ assert(root_fd >= 0);
+
+ if (fchmod(root_fd, m) < 0)
+ return log_error_errno(errno, "Failed to change access mode of top-level directory: %m");
+
+ log_info("Changed top-level directory access mode to 0%o.", m);
+ return 0;
+}
+
+int home_populate(UserRecord *h, int dir_fd) {
+ int r;
+
+ assert(h);
+ assert(dir_fd >= 0);
+
+ r = copy_skel(dir_fd, user_record_skeleton_directory(h));
+ if (r < 0)
+ return r;
+
+ r = home_store_embedded_identity(h, dir_fd, h->uid, NULL);
+ if (r < 0)
+ return r;
+
+ r = chown_recursive_directory(dir_fd, h->uid);
+ if (r < 0)
+ return r;
+
+ r = change_access_mode(dir_fd, user_record_access_mode(h));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int user_record_compile_effective_passwords(
+ UserRecord *h,
+ char ***ret_effective_passwords,
+ char ***ret_pkcs11_decrypted_passwords) {
+
+ _cleanup_(strv_free_erasep) char **effective = NULL, **pkcs11_passwords = NULL;
+ size_t n;
+ char **i;
+ int r;
+
+ assert(h);
+
+ /* We insist on at least one classic hashed password to be defined in addition to any PKCS#11 one, as
+ * a safe fallback, but also to simplify the password changing algorithm: there we require providing
+ * the old literal password only (and do not care for the old PKCS#11 token) */
+
+ if (strv_isempty(h->hashed_password))
+ return log_error_errno(EINVAL, "User record has no hashed passwords, refusing.");
+
+ /* Generates the list of plaintext passwords to propagate to LUKS/fscrypt devices, and checks whether
+ * we have a plaintext password for each hashed one. If we are missing one we'll fail, since we
+ * couldn't sync fscrypt/LUKS to the login account properly. */
+
+ STRV_FOREACH(i, h->hashed_password) {
+ bool found = false;
+ char **j;
+
+ log_debug("Looking for plaintext password for: %s", *i);
+
+ /* Let's scan all provided plaintext passwords */
+ STRV_FOREACH(j, h->password) {
+ r = test_password_one(*i, *j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test plain text password: %m");
+ if (r > 0) {
+ if (ret_effective_passwords) {
+ r = strv_extend(&effective, *j);
+ if (r < 0)
+ return log_oom();
+ }
+
+ log_debug("Found literal plaintext password.");
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Missing plaintext password for defined hashed password");
+ }
+
+ for (n = 0; n < h->n_pkcs11_encrypted_key; n++) {
+#if HAVE_P11KIT
+ _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
+ .user_record = h,
+ .secret = h,
+ .encrypted_key = h->pkcs11_encrypted_key + n,
+ };
+
+ r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data);
+ if (r == -EAGAIN)
+ return -EBADSLT;
+ if (r < 0)
+ return r;
+
+ r = test_password_one(data.encrypted_key->hashed_password, data.decrypted_password);
+ if (r < 0)
+ return log_error_errno(r, "Failed to test PKCS#11 password: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Decrypted password from token is not correct, refusing.");
+
+ if (ret_effective_passwords) {
+ r = strv_extend(&effective, data.decrypted_password);
+ if (r < 0)
+ return log_oom();
+ }
+
+ if (ret_pkcs11_decrypted_passwords) {
+ r = strv_extend(&pkcs11_passwords, data.decrypted_password);
+ if (r < 0)
+ return log_oom();
+ }
+#else
+ return -EBADSLT;
+#endif
+ }
+
+ if (ret_effective_passwords)
+ *ret_effective_passwords = TAKE_PTR(effective);
+ if (ret_pkcs11_decrypted_passwords)
+ *ret_pkcs11_decrypted_passwords = TAKE_PTR(pkcs11_passwords);
+
+ return 0;
+}
+
+static int home_create(UserRecord *h, UserRecord **ret_home) {
+ _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+ int r;
+
+ assert(h);
+
+ if (!h->user_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks name, refusing.");
+ if (!uid_is_valid(h->uid))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
+
+ r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ r = user_record_test_home_directory_and_warn(h);
+ if (r < 0)
+ return r;
+ if (r != USER_TEST_ABSENT)
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Home directory %s already exists, refusing.", user_record_home_directory(h));
+
+ /* When the user didn't specify the storage type to use, fix it to be LUKS -- unless we run in a
+ * container where loopback devices and LUKS/DM are not available. Note that we typically default to
+ * the assumption of "classic" storage for most operations. However, if we create a new home, then
+ * let's user LUKS if nothing is specified. */
+ if (h->storage < 0) {
+ UserStorage new_storage;
+
+ r = detect_container();
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether we are in a container: %m");
+ if (r > 0) {
+ new_storage = USER_DIRECTORY;
+
+ r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine file system of /home, ignoring: %m");
+
+ new_storage = r > 0 ? USER_SUBVOLUME : USER_DIRECTORY;
+ } else
+ new_storage = USER_LUKS;
+
+ r = user_record_add_binding(
+ h,
+ new_storage,
+ NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ SD_ID128_NULL,
+ NULL,
+ NULL,
+ UINT64_MAX,
+ NULL,
+ NULL,
+ UID_INVALID,
+ GID_INVALID);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change storage type to LUKS: %m");
+
+ if (!h->image_path_auto) {
+ h->image_path_auto = strjoin("/home/", user_record_user_name_and_realm(h), new_storage == USER_LUKS ? ".home" : ".homedir");
+ if (!h->image_path_auto)
+ return log_oom();
+ }
+ }
+
+ r = user_record_test_image_path_and_warn(h);
+ if (r < 0)
+ return r;
+ if (!IN_SET(r, USER_TEST_ABSENT, USER_TEST_UNDEFINED, USER_TEST_MAYBE))
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Image path %s already exists, refusing.", user_record_image_path(h));
+
+ switch (user_record_storage(h)) {
+
+ case USER_LUKS:
+ r = home_create_luks(h, pkcs11_decrypted_passwords, effective_passwords, &new_home);
+ break;
+
+ case USER_DIRECTORY:
+ case USER_SUBVOLUME:
+ r = home_create_directory_or_subvolume(h, &new_home);
+ break;
+
+ case USER_FSCRYPT:
+ r = home_create_fscrypt(h, effective_passwords, &new_home);
+ break;
+
+ case USER_CIFS:
+ r = home_create_cifs(h, &new_home);
+ break;
+
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+ "Creating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+ }
+ if (r < 0)
+ return r;
+
+ if (user_record_equal(h, new_home)) {
+ *ret_home = NULL;
+ return 0;
+ }
+
+ *ret_home = TAKE_PTR(new_home);
+ return 1;
+}
+
+static int home_remove(UserRecord *h) {
+ bool deleted = false;
+ const char *ip, *hd;
+ int r;
+
+ assert(h);
+
+ if (!h->user_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks user name, refusing.");
+ if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Removing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+ hd = user_record_home_directory(h);
+
+ r = user_record_test_home_directory_and_warn(h);
+ if (r < 0)
+ return r;
+ if (r == USER_TEST_MOUNTED)
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Directory %s is still mounted, refusing.", hd);
+
+ assert(hd);
+
+ r = user_record_test_image_path_and_warn(h);
+ if (r < 0)
+ return r;
+
+ ip = user_record_image_path(h);
+
+ switch (user_record_storage(h)) {
+
+ case USER_LUKS: {
+ struct stat st;
+
+ assert(ip);
+
+ if (stat(ip, &st) < 0) {
+ if (errno != -ENOENT)
+ return log_error_errno(errno, "Failed to stat %s: %m", ip);
+
+ } else {
+ if (S_ISREG(st.st_mode)) {
+ if (unlink(ip) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to remove %s: %m", ip);
+ } else
+ deleted = true;
+
+ } else if (S_ISBLK(st.st_mode))
+ log_info("Not removing file system on block device %s.", ip);
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Image file %s is neither block device, nor regular, refusing removal.", ip);
+ }
+
+ break;
+ }
+
+ case USER_SUBVOLUME:
+ case USER_DIRECTORY:
+ case USER_FSCRYPT:
+ assert(ip);
+
+ r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return log_warning_errno(r, "Failed to remove %s: %m", ip);
+ } else
+ deleted = true;
+
+ /* If the image path and the home directory are the same invalidate the home directory, so
+ * that we don't remove it anymore */
+ if (path_equal(ip, hd))
+ hd = NULL;
+
+ break;
+
+ case USER_CIFS:
+ /* Nothing else to do here: we won't remove remote stuff. */
+ log_info("Not removing home directory on remote server.");
+ break;
+
+ default:
+ assert_not_reached("unknown storage type");
+ }
+
+ if (hd) {
+ if (rmdir(hd) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to remove %s, ignoring: %m", hd);
+ } else
+ deleted = true;
+ }
+
+ if (deleted)
+ log_info("Everything completed.");
+ else {
+ log_notice("Nothing to remove.");
+ return -EALREADY;
+ }
+
+ return 0;
+}
+
+static int home_validate_update(UserRecord *h, HomeSetup *setup) {
+ bool has_mount = false;
+ int r;
+
+ assert(h);
+ assert(setup);
+
+ if (!h->user_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks user name, refusing.");
+ if (!uid_is_valid(h->uid))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
+ if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Processing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+ r = user_record_test_home_directory_and_warn(h);
+ if (r < 0)
+ return r;
+
+ has_mount = r == USER_TEST_MOUNTED;
+
+ r = user_record_test_image_path_and_warn(h);
+ if (r < 0)
+ return r;
+ if (r == USER_TEST_ABSENT)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Image path %s does not exist", user_record_image_path(h));
+
+ switch (user_record_storage(h)) {
+
+ case USER_DIRECTORY:
+ case USER_SUBVOLUME:
+ case USER_FSCRYPT:
+ case USER_CIFS:
+ break;
+
+ case USER_LUKS: {
+ r = home_validate_update_luks(h, setup);
+ if (r < 0)
+ return r;
+ if ((r > 0) != has_mount)
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Home mount incompletely set up.");
+
+ break;
+ }
+
+ default:
+ assert_not_reached("unexpected storage type");
+ }
+
+ return has_mount; /* return true if the home record is already active */
+}
+
+static int home_update(UserRecord *h, UserRecord **ret) {
+ _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL, *embedded_home = NULL;
+ _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ bool already_activated = false;
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ r = home_validate_update(h, &setup);
+ if (r < 0)
+ return r;
+
+ already_activated = r > 0;
+
+ r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+ if (r < 0)
+ return r;
+
+ r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ if (r < 0)
+ return r;
+
+ r = home_store_header_identity_luks(new_home, &setup, header_home);
+ if (r < 0)
+ return r;
+
+ r = home_store_embedded_identity(new_home, setup.root_fd, h->uid, embedded_home);
+ if (r < 0)
+ return r;
+
+ r = home_extend_embedded_identity(new_home, h, &setup);
+ if (r < 0)
+ return r;
+
+ r = home_sync_and_statfs(setup.root_fd, NULL);
+ if (r < 0)
+ return r;
+
+ r = home_setup_undo(&setup);
+ if (r < 0)
+ return r;
+
+ log_info("Everything completed.");
+
+ *ret = TAKE_PTR(new_home);
+ return 0;
+}
+
+static int home_resize(UserRecord *h, UserRecord **ret) {
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ bool already_activated = false;
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ if (h->disk_size == UINT64_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing.");
+
+ r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ r = home_validate_update(h, &setup);
+ if (r < 0)
+ return r;
+
+ already_activated = r > 0;
+
+ switch (user_record_storage(h)) {
+
+ case USER_LUKS:
+ return home_resize_luks(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+
+ case USER_DIRECTORY:
+ case USER_SUBVOLUME:
+ case USER_FSCRYPT:
+ return home_resize_directory(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Resizing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+ }
+}
+
+static int home_passwd(UserRecord *h, UserRecord **ret_home) {
+ _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
+ _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ bool already_activated = false;
+ int r;
+
+ assert(h);
+ assert(ret_home);
+
+ if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Changing password of home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+ r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ r = home_validate_update(h, &setup);
+ if (r < 0)
+ return r;
+
+ already_activated = r > 0;
+
+ r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+ if (r < 0)
+ return r;
+
+ r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+ if (r < 0)
+ return r;
+
+ switch (user_record_storage(h)) {
+
+ case USER_LUKS:
+ r = home_passwd_luks(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+ if (r < 0)
+ return r;
+ break;
+
+ case USER_FSCRYPT:
+ r = home_passwd_fscrypt(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+ if (r < 0)
+ return r;
+ break;
+
+ default:
+ break;
+ }
+
+ r = home_store_header_identity_luks(new_home, &setup, header_home);
+ if (r < 0)
+ return r;
+
+ r = home_store_embedded_identity(new_home, setup.root_fd, h->uid, embedded_home);
+ if (r < 0)
+ return r;
+
+ r = home_extend_embedded_identity(new_home, h, &setup);
+ if (r < 0)
+ return r;
+
+ r = home_sync_and_statfs(setup.root_fd, NULL);
+ if (r < 0)
+ return r;
+
+ r = home_setup_undo(&setup);
+ if (r < 0)
+ return r;
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 1;
+}
+
+static int home_inspect(UserRecord *h, UserRecord **ret_home) {
+ _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *new_home = NULL;
+ _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+ _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ bool already_activated = false;
+ int r;
+
+ assert(h);
+ assert(ret_home);
+
+ r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ r = home_validate_update(h, &setup);
+ if (r < 0)
+ return r;
+
+ already_activated = r > 0;
+
+ r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+ if (r < 0)
+ return r;
+
+ r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &pkcs11_decrypted_passwords, NULL, &new_home);
+ if (r < 0)
+ return r;
+
+ r = home_extend_embedded_identity(new_home, h, &setup);
+ if (r < 0)
+ return r;
+
+ r = home_setup_undo(&setup);
+ if (r < 0)
+ return r;
+
+ log_info("Everything completed.");
+
+ *ret_home = TAKE_PTR(new_home);
+ return 1;
+}
+
+static int home_lock(UserRecord *h) {
+ int r;
+
+ assert(h);
+
+ if (!h->user_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record incomplete, refusing.");
+ if (user_record_storage(h) != USER_LUKS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Locking home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+ r = user_record_test_home_directory_and_warn(h);
+ if (r < 0)
+ return r;
+ if (r != USER_TEST_MOUNTED)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home directory of %s is not mounted, can't lock.", h->user_name);
+
+ r = home_lock_luks(h);
+ if (r < 0)
+ return r;
+
+ log_info("Everything completed.");
+ return 1;
+}
+
+static int home_unlock(UserRecord *h) {
+ _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+ int r;
+
+ assert(h);
+
+ if (!h->user_name)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record incomplete, refusing.");
+ if (user_record_storage(h) != USER_LUKS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Unlocking home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+ /* Note that we don't check if $HOME is actually mounted, since we want to avoid disk accesses on
+ * that mount until we have resumed the device. */
+
+ r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ r = home_unlock_luks(h, &pkcs11_decrypted_passwords);
+ if (r < 0)
+ return r;
+
+ log_info("Everything completed.");
+ return 1;
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(user_record_unrefp) UserRecord *home = NULL, *new_home = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(fclosep) FILE *opened_file = NULL;
+ unsigned line = 0, column = 0;
+ const char *json_path = NULL;
+ FILE *json_file;
+ usec_t start;
+ int r;
+
+ start = now(CLOCK_MONOTONIC);
+
+ log_setup_service();
+
+ umask(0022);
+
+ if (argc < 2 || argc > 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes one or two arguments.");
+
+ if (argc > 2) {
+ json_path = argv[2];
+
+ opened_file = fopen(json_path, "re");
+ if (!opened_file)
+ return log_error_errno(errno, "Failed to open %s: %m", json_path);
+
+ json_file = opened_file;
+ } else {
+ json_path = "<stdin>";
+ json_file = stdin;
+ }
+
+ r = json_parse_file(json_file, json_path, JSON_PARSE_SENSITIVE, &v, &line, &column);
+ if (r < 0)
+ return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON data: %m", json_path, line, column);
+
+ home = user_record_new();
+ if (!home)
+ return log_oom();
+
+ r = user_record_load(home, v, USER_RECORD_LOAD_FULL|USER_RECORD_LOG);
+ if (r < 0)
+ return r;
+
+ /* Well known return values of these operations, that systemd-homed knows and converts to proper D-Bus errors:
+ *
+ * EMSGSIZE → file systems of this type cannnot be shrinked
+ * ETXTBSY → file systems of this type can only be shrinked offline
+ * ERANGE → file system size too small
+ * ENOLINK → system does not support selected storage backend
+ * EPROTONOSUPPORT → system does not support selected file system
+ * ENOTTY → operation not support on this storage
+ * ESOCKTNOSUPPORT → operation not support on this file system
+ * ENOKEY → password incorrect (or not sufficient, or not supplied)
+ * EBADSLT → similar, but PKCS#11 device is defined and might be able to provide password, if it was plugged in which it is not
+ * ENOANO → suitable PKCS#11 device found, but PIN is missing to unlock it
+ * ERFKILL → suitable PKCS#11 device found, but OK to ask for on-device interactive authentication not given
+ * EOWNERDEAD → suitable PKCS#11 device found, but its PIN is locked
+ * ENOLCK → suitable PKCS#11 device found, but PIN incorrect
+ * ETOOMANYREFS → suitable PKCS#11 device found, but PIN incorrect, and only few tries left
+ * EUCLEAN → suitable PKCS#11 device found, but PIN incorrect, and only one try left
+ * EBUSY → file system is currently active
+ * ENOEXEC → file system is currently not active
+ * ENOSPC → not enough disk space for operation
+ */
+
+ if (streq(argv[1], "activate"))
+ r = home_activate(home, &new_home);
+ else if (streq(argv[1], "deactivate"))
+ r = home_deactivate(home, false);
+ else if (streq(argv[1], "deactivate-force"))
+ r = home_deactivate(home, true);
+ else if (streq(argv[1], "create"))
+ r = home_create(home, &new_home);
+ else if (streq(argv[1], "remove"))
+ r = home_remove(home);
+ else if (streq(argv[1], "update"))
+ r = home_update(home, &new_home);
+ else if (streq(argv[1], "resize"))
+ r = home_resize(home, &new_home);
+ else if (streq(argv[1], "passwd"))
+ r = home_passwd(home, &new_home);
+ else if (streq(argv[1], "inspect"))
+ r = home_inspect(home, &new_home);
+ else if (streq(argv[1], "lock"))
+ r = home_lock(home);
+ else if (streq(argv[1], "unlock"))
+ r = home_unlock(home);
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb '%s'.", argv[1]);
+ if (r == -ENOKEY && !strv_isempty(home->password)) { /* There were passwords specified but they were incorrect */
+ usec_t end, n, d;
+
+ /* Make sure bad password replies always take at least 3s, and if longer multiples of 3s, so
+ * that it's not clear how long we actually needed for our calculations. */
+ n = now(CLOCK_MONOTONIC);
+ assert(n >= start);
+
+ d = usec_sub_unsigned(n, start);
+ if (d > BAD_PASSWORD_DELAY_USEC)
+ end = start + DIV_ROUND_UP(d, BAD_PASSWORD_DELAY_USEC) * BAD_PASSWORD_DELAY_USEC;
+ else
+ end = start + BAD_PASSWORD_DELAY_USEC;
+
+ if (n < end)
+ (void) usleep(usec_sub_unsigned(end, n));
+ }
+ if (r < 0)
+ return r;
+
+ /* We always pass the new record back, regardless if it changed or not. This allows our caller to
+ * prepare a fresh record, send to us, and only if it works use it without having to keep a local
+ * copy. */
+ if (new_home)
+ json_variant_dump(new_home->json, JSON_FORMAT_NEWLINE, stdout, NULL);
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/fs.h>
+#include <sys/vfs.h>
+
+#include "sd-id128.h"
+
+#include "loop-util.h"
+#include "user-record.h"
+#include "user-record-util.h"
+
+typedef struct HomeSetup {
+ char *dm_name;
+ char *dm_node;
+
+ LoopDevice *loop;
+ struct crypt_device *crypt_device;
+ int root_fd;
+ sd_id128_t found_partition_uuid;
+ sd_id128_t found_luks_uuid;
+ sd_id128_t found_fs_uuid;
+
+ uint8_t fscrypt_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+
+ void *volume_key;
+ size_t volume_key_size;
+
+ bool undo_dm;
+ bool undo_mount;
+
+ uint64_t partition_offset;
+ uint64_t partition_size;
+} HomeSetup;
+
+#define HOME_SETUP_INIT \
+ { \
+ .root_fd = -1, \
+ .partition_offset = UINT64_MAX, \
+ .partition_size = UINT64_MAX, \
+ }
+
+int home_setup_undo(HomeSetup *setup);
+
+int home_prepare(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_header_home);
+
+int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, char ***pkcs11_decrypted_passwords, struct statfs *ret_statfs, UserRecord **ret_new_home);
+
+int home_populate(UserRecord *h, int dir_fd);
+
+int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, char ***pkcs11_decrypted_passwords, UserRecord **ret_embedded_home, UserRecord **ret_new_home);
+int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home);
+int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup);
+
+int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords);
+
+int home_sync_and_statfs(int root_fd, struct statfs *ret);
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+
+systemd_homework_sources = files('''
+ home-util.c
+ home-util.h
+ homework-cifs.c
+ homework-cifs.h
+ homework-directory.c
+ homework-directory.h
+ homework-fscrypt.c
+ homework-fscrypt.h
+ homework-luks.c
+ homework-luks.h
+ homework-mount.c
+ homework-mount.h
+ homework-pkcs11.h
+ homework-quota.c
+ homework-quota.h
+ homework.c
+ homework.h
+ user-record-util.c
+ user-record-util.h
+'''.split())
+
+if conf.get('HAVE_P11KIT') == 1
+ systemd_homework_sources += files('homework-pkcs11.c')
+endif
+
+systemd_homed_sources = files('''
+ home-util.c
+ home-util.h
+ homed-bus.c
+ homed-bus.h
+ homed-home-bus.c
+ homed-home-bus.h
+ homed-home.c
+ homed-home.h
+ homed-manager-bus.c
+ homed-manager-bus.h
+ homed-manager.c
+ homed-manager.h
+ homed-operation.c
+ homed-operation.h
+ homed-varlink.c
+ homed-varlink.h
+ homed.c
+ pwquality-util.c
+ pwquality-util.h
+ user-record-sign.c
+ user-record-sign.h
+ user-record-util.c
+ user-record-util.h
+'''.split())
+
+homectl_sources = files('''
+ home-util.c
+ home-util.h
+ homectl.c
+ pwquality-util.c
+ pwquality-util.h
+ user-record-util.c
+ user-record-util.h
+'''.split())
+
+pam_systemd_home_sym = 'src/home/pam_systemd_home.sym'
+pam_systemd_home_c = files('''
+ home-util.c
+ home-util.h
+ pam_systemd_home.c
+ user-record-util.c
+ user-record-util.h
+'''.split())
+
+if conf.get('ENABLE_HOMED') == 1
+ install_data('org.freedesktop.home1.conf',
+ install_dir : dbuspolicydir)
+ install_data('org.freedesktop.home1.service',
+ install_dir : dbussystemservicedir)
+ install_data('org.freedesktop.home1.policy',
+ install_dir : polkitpolicydir)
+endif
--- /dev/null
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<busconfig>
+
+ <policy user="root">
+ <allow own="org.freedesktop.home1"/>
+ <allow send_destination="org.freedesktop.home1"/>
+ <allow receive_sender="org.freedesktop.home1"/>
+ </policy>
+
+ <policy context="default">
+ <deny send_destination="org.freedesktop.home1"/>
+
+ <!-- generic interfaces -->
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.DBus.Introspectable"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.DBus.Peer"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.DBus.Properties"
+ send_member="Get"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.DBus.Properties"
+ send_member="GetAll"/>
+
+ <!-- Manager object -->
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="GetHomeByName"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="GetHomeByUID"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="GetUserRecordByName"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="GetUserRecordByUID"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="ListHomes"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="ActivateHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="DeactivateHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="RegisterHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="UnregisterHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="CreateHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="RealizeHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="RemoveHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="FixateHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="AuthenticateHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="UpdateHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="ResizeHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="ChangePasswordHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="LockHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="UnlockHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="AcquireHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="RefHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="ReleaseHome"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="LockAllHomes"/>
+
+ <!-- Home object -->
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Activate"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Deactivate"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Unregister"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Realize"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Remove"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Fixate"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Authenticate"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Update"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Resize"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="ChangePassword"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Lock"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Unlock"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Acquire"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Ref"/>
+
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Home"
+ send_member="Release"/>
+
+ <allow receive_sender="org.freedesktop.home1"/>
+ </policy>
+
+</busconfig>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<policyconfig>
+
+ <vendor>The systemd Project</vendor>
+ <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
+
+ <action id="org.freedesktop.home1.create-home">
+ <description gettext-domain="systemd">Create a home area</description>
+ <message gettext-domain="systemd">Authentication is required to create a user's home area.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.home1.remove-home">
+ <description gettext-domain="systemd">Remove a home area</description>
+ <message gettext-domain="systemd">Authentication is required to remove a user's home area.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.home1.authenticate-home">
+ <description gettext-domain="systemd">Check credentials of a home area</description>
+ <message gettext-domain="systemd">Authentication is required to check credentials against a user's home area.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.home1.update-home">
+ <description gettext-domain="systemd">Update a home area</description>
+ <message gettext-domain="systemd">Authentication is required to update a user's home area.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.home1.resize-home">
+ <description gettext-domain="systemd">Resize a home area</description>
+ <message gettext-domain="systemd">Authentication is required to resize a user's home area.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.home1.passwd-home">
+ <description gettext-domain="systemd">Change password of a home area</description>
+ <message gettext-domain="systemd">Authentication is required to change the password of a user's home area.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ </action>
+
+</policyconfig>
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+
+[D-BUS Service]
+Name=org.freedesktop.home1
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.freedesktop.home1.service
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <security/pam_ext.h>
+#include <security/pam_modules.h>
+
+#include "sd-bus.h"
+
+#include "bus-common-errors.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "home-util.h"
+#include "memory-util.h"
+#include "pam-util.h"
+#include "parse-util.h"
+#include "strv.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+
+/* Used for the "systemd-user-record-is-homed" PAM data field, to indicate whether we know whether this user
+ * record is managed by homed or by something else. */
+#define USER_RECORD_IS_HOMED INT_TO_PTR(1)
+#define USER_RECORD_IS_OTHER INT_TO_PTR(2)
+
+static int parse_argv(
+ pam_handle_t *handle,
+ int argc, const char **argv,
+ bool *please_suspend,
+ bool *debug) {
+
+ int i;
+
+ assert(argc >= 0);
+ assert(argc == 0 || argv);
+
+ for (i = 0; i < argc; i++) {
+ const char *v;
+
+ if ((v = startswith(argv[1], "suspend="))) {
+ int k;
+
+ k = parse_boolean(v);
+ if (k < 0)
+ pam_syslog(handle, LOG_WARNING, "Failed to parse suspend-please= argument, ignoring: %s", v);
+ else if (please_suspend)
+ *please_suspend = k;
+
+ } else if ((v = startswith(argv[i], "debug="))) {
+ int k;
+
+ k = parse_boolean(v);
+ if (k < 0)
+ pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v);
+ else if (debug)
+ *debug = k;
+
+ } else
+ pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
+ }
+
+ return 0;
+}
+
+static int acquire_user_record(
+ pam_handle_t *handle,
+ UserRecord **ret_record) {
+
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ const char *username = NULL, *json = NULL;
+ const void *b = NULL;
+ int r;
+
+ assert(handle);
+
+ r = pam_get_user(handle, &username, NULL);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ if (isempty(username)) {
+ pam_syslog(handle, LOG_ERR, "User name not set.");
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
+ * user names we don't consider valid. */
+ if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username))
+ return PAM_USER_UNKNOWN;
+
+ /* Let's check if a previous run determined that this user is not managed by homed. If so, let's exit early */
+ r = pam_get_data(handle, "systemd-user-record-is-homed", &b);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ /* Failure */
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM user record is homed flag: %s", pam_strerror(handle, r));
+ return r;
+ } else if (b == NULL)
+ /* Nothing cached yet, need to acquire fresh */
+ json = NULL;
+ else if (b != USER_RECORD_IS_HOMED)
+ /* Definitely not a homed record */
+ return PAM_USER_UNKNOWN;
+ else {
+ /* It's a homed record, let's use the cache, so that we can share it between the session and
+ * the authentication hooks */
+ r = pam_get_data(handle, "systemd-user-record", (const void**) &json);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
+ return r;
+ }
+ }
+
+ if (!json) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *json_copy = NULL;
+
+ r = pam_acquire_bus_connection(handle, &bus);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "GetUserRecordByName",
+ &error,
+ &reply,
+ "s",
+ username);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
+ sd_bus_error_has_name(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
+ pam_syslog(handle, LOG_DEBUG, "systemd-homed is not available: %s", bus_error_message(&error, r));
+ goto user_unknown;
+ }
+
+ if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_HOME)) {
+ pam_syslog(handle, LOG_DEBUG, "Not a user managed by systemd-homed: %s", bus_error_message(&error, r));
+ goto user_unknown;
+ }
+
+ pam_syslog(handle, LOG_ERR, "Failed to query user record: %s", bus_error_message(&error, r));
+ return PAM_SERVICE_ERR;
+ }
+
+ r = sd_bus_message_read(reply, "sbo", &json, NULL, NULL);
+ if (r < 0)
+ return pam_bus_log_parse_error(handle, r);
+
+ json_copy = strdup(json);
+ if (!json_copy)
+ return pam_log_oom(handle);
+
+ r = pam_set_data(handle, "systemd-user-record", json_copy, pam_cleanup_free);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ TAKE_PTR(json_copy);
+
+ r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_HOMED, NULL);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag: %s", pam_strerror(handle, r));
+ return r;
+ }
+ }
+
+ r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to parse JSON user record: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ ur = user_record_new();
+ if (!ur)
+ return pam_log_oom(handle);
+
+ r = user_record_load(ur, v, USER_RECORD_LOAD_REFUSE_SECRET);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to load user record: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ if (!streq_ptr(username, ur->user_name)) {
+ pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
+ return PAM_SERVICE_ERR;
+ }
+
+ if (ret_record)
+ *ret_record = TAKE_PTR(ur);
+
+ return PAM_SUCCESS;
+
+user_unknown:
+ /* Cache this, so that we don't check again */
+ r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_OTHER, NULL);
+ if (r != PAM_SUCCESS)
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag, ignoring: %s", pam_strerror(handle, r));
+
+ return PAM_USER_UNKNOWN;
+}
+
+static int release_user_record(pam_handle_t *handle) {
+ int r, k;
+
+ r = pam_set_data(handle, "systemd-user-record", NULL, NULL);
+ if (r != PAM_SUCCESS)
+ pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r));
+
+ k = pam_set_data(handle, "systemd-user-record-is-homed", NULL, NULL);
+ if (k != PAM_SUCCESS)
+ pam_syslog(handle, LOG_ERR, "Failed to release PAM user record is homed flag: %s", pam_strerror(handle, k));
+
+ return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r;
+}
+
+static void cleanup_home_fd(pam_handle_t *handle, void *data, int error_status) {
+ safe_close(PTR_TO_FD(data));
+}
+
+static int handle_generic_user_record_error(
+ pam_handle_t *handle,
+ const char *user_name,
+ UserRecord *secret,
+ int ret,
+ const sd_bus_error *error) {
+
+ assert(user_name);
+ assert(secret);
+ assert(error);
+
+ int r;
+
+ /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
+
+ if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT)) {
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name);
+ pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
+ return PAM_PERM_DENIED;
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT)) {
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too frequent unsuccessful login attempts for user %s, try again later.", user_name);
+ pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
+ return PAM_MAXTRIES;
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
+ _cleanup_(erase_and_freep) char *newp = NULL;
+
+ /* This didn't work? Ask for an (additional?) password */
+
+ if (strv_isempty(secret->password))
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password: ");
+ else
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password incorrect or not sufficient for authentication of user %s, please try again: ", user_name);
+ if (r != PAM_SUCCESS)
+ return PAM_CONV_ERR; /* no logging here */
+
+ if (isempty(newp)) {
+ pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ r = user_record_set_password(secret, STRV_MAKE(newp), true);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
+ _cleanup_(erase_and_freep) char *newp = NULL;
+
+ if (strv_isempty(secret->password))
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token of user %s not inserted, please enter password: ", user_name);
+ else
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password incorrect or not sufficient, and configured security token of user %s not inserted, please enter password: ", user_name);
+ if (r != PAM_SUCCESS)
+ return PAM_CONV_ERR; /* no logging here */
+
+ if (isempty(newp)) {
+ pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ r = user_record_set_password(secret, STRV_MAKE(newp), true);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
+ _cleanup_(erase_and_freep) char *newp = NULL;
+
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Please enter security token PIN: ");
+ if (r != PAM_SUCCESS)
+ return PAM_CONV_ERR; /* no logging here */
+
+ if (isempty(newp)) {
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
+
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please authenticate physically on security token of user %s.", user_name);
+
+ r = user_record_set_pkcs11_protected_authentication_path_permitted(secret, true);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PKCS#11 protected authentication path permitted flag: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
+ _cleanup_(erase_and_freep) char *newp = NULL;
+
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect, please enter PIN for security token of user %s again: ", user_name);
+ if (r != PAM_SUCCESS)
+ return PAM_CONV_ERR; /* no logging here */
+
+ if (isempty(newp)) {
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
+ _cleanup_(erase_and_freep) char *newp = NULL;
+
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect (only a few tries left!), please enter PIN for security token of user %s again: ", user_name);
+ if (r != PAM_SUCCESS)
+ return PAM_CONV_ERR; /* no logging here */
+
+ if (isempty(newp)) {
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
+ _cleanup_(erase_and_freep) char *newp = NULL;
+
+ r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect (only one try left!), please enter PIN for security token of user %s again: ", user_name);
+ if (r != PAM_SUCCESS)
+ return PAM_CONV_ERR; /* no logging here */
+
+ if (isempty(newp)) {
+ pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ } else {
+ pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
+ return PAM_SERVICE_ERR;
+ }
+
+ return PAM_SUCCESS;
+}
+
+static int acquire_home(
+ pam_handle_t *handle,
+ bool please_authenticate,
+ bool please_suspend,
+ bool debug) {
+
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *secret = NULL;
+ bool do_auth = please_authenticate, home_not_active = false, home_locked = false;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ _cleanup_close_ int acquired_fd = -1;
+ const void *home_fd_ptr = NULL;
+ unsigned n_attempts = 0;
+ int r;
+
+ assert(handle);
+
+ /* This acquires a reference to a home directory in one of two ways: if please_authenticate is true,
+ * then we'll call AcquireHome() after asking the user for a password. Otherwise it tries to call
+ * RefHome() and if that fails queries the user for a password and uses AcquireHome().
+ *
+ * The idea is that the PAM authentication hook sets please_authenticate and thus always
+ * authenticates, while the other PAM hooks unset it so that they can a ref of their own without
+ * authentication if possible, but with authentication if necessary. */
+
+ /* If we already have acquired the fd, let's shortcut this */
+ r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
+ if (r == PAM_SUCCESS && PTR_TO_INT(home_fd_ptr) >= 0)
+ return PAM_SUCCESS;
+
+ r = pam_acquire_bus_connection(handle, &bus);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = acquire_user_record(handle, &ur);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
+ * might happen that the the record we stored on the host does not match the encryption password of
+ * the LUKS image in case the image was used in a different system where the password was
+ * changed. In that case it will happen that the LUKS password and the host password are
+ * different, and we handle that by collecting and passing multiple passwords in that case. Hence we
+ * treat bad passwords as a request to collect one more password and pass the new all all previously
+ * used passwords again. */
+
+ for (;;) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ if (do_auth && !secret) {
+ const char *cached_password = NULL;
+
+ secret = user_record_new();
+ if (!secret)
+ return pam_log_oom(handle);
+
+ /* If there's already a cached password, use it. But if not let's authenticate
+ * without anything, maybe some other authentication mechanism systemd-homed
+ * implements (such as PKCS#11) allows us to authenticate without anything else. */
+ r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &cached_password);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ if (!isempty(cached_password)) {
+ r = user_record_set_password(secret, STRV_MAKE(cached_password), true);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+ }
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ do_auth ? "AcquireHome" : "RefHome");
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = sd_bus_message_append(m, "s", ur->user_name);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ if (do_auth) {
+ r = bus_message_append_secret(m, secret);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+ }
+
+ r = sd_bus_message_append(m, "b", please_suspend);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+ if (r < 0) {
+
+ if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_NOT_ACTIVE))
+ /* Only on RefHome(): We can't access the home directory currently, unless
+ * it's unlocked with a password. Hence, let's try this again, this time with
+ * authentication. */
+ home_not_active = true;
+ else if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_LOCKED))
+ home_locked = true; /* Similar */
+ else {
+ r = handle_generic_user_record_error(handle, ur->user_name, secret, r, &error);
+ if (r == PAM_CONV_ERR) {
+ /* Password/PIN prompts will fail in certain environments, for example when
+ * we are called from OpenSSH's account or session hooks, or in systemd's
+ * per-service PAM logic. In that case, print a friendly message and accept
+ * failure. */
+
+ if (home_not_active)
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently not active, please log in locally first.", ur->user_name);
+ if (home_locked)
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently locked, please unlock locally first.", ur->user_name);
+
+ pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
+
+ return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
+ }
+ if (r != PAM_SUCCESS)
+ return r;
+ }
+
+ } else {
+ int fd;
+
+ r = sd_bus_message_read(reply, "h", &fd);
+ if (r < 0)
+ return pam_bus_log_parse_error(handle, r);
+
+ acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (acquired_fd < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to duplicate acquired fd: %s", bus_error_message(&error, r));
+ return PAM_SERVICE_ERR;
+ }
+
+ break;
+ }
+
+ if (++n_attempts >= 5) {
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many unsuccessful login attempts for user %s, refusing.", ur->user_name);
+ pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", ur->user_name, bus_error_message(&error, r));
+ return PAM_MAXTRIES;
+ }
+
+ /* Try again, this time with authentication if we didn't do that before. */
+ do_auth = true;
+ }
+
+ r = pam_set_data(handle, "systemd-home-fd", FD_TO_PTR(acquired_fd), cleanup_home_fd);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
+ return r;
+ }
+ TAKE_FD(acquired_fd);
+
+ if (do_auth) {
+ /* We likely just activated the home directory, let's flush out the user record, since a
+ * newer embedded user record might have been acquired from the activation. */
+
+ r = release_user_record(handle);
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
+ return r;
+ }
+
+ pam_syslog(handle, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name);
+
+ return PAM_SUCCESS;
+}
+
+static int release_home_fd(pam_handle_t *handle) {
+ const void *home_fd_ptr = NULL;
+ int r;
+
+ r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
+ if (r == PAM_NO_MODULE_DATA || PTR_TO_FD(home_fd_ptr) < 0)
+ return PAM_NO_MODULE_DATA;
+
+ r = pam_set_data(handle, "systemd-home-fd", NULL, NULL);
+ if (r != PAM_SUCCESS)
+ pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
+
+ return r;
+}
+
+_public_ PAM_EXTERN int pam_sm_authenticate(
+ pam_handle_t *handle,
+ int flags,
+ int argc, const char **argv) {
+
+ bool debug = false, suspend_please = false;
+
+ if (parse_argv(handle,
+ argc, argv,
+ &suspend_please,
+ &debug) < 0)
+ return PAM_AUTH_ERR;
+
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed authenticating");
+
+ return acquire_home(handle, /* please_authenticate= */ true, suspend_please, debug);
+}
+
+_public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_open_session(
+ pam_handle_t *handle,
+ int flags,
+ int argc, const char **argv) {
+
+ bool debug = false, suspend_please = false;
+ int r;
+
+ if (parse_argv(handle,
+ argc, argv,
+ &suspend_please,
+ &debug) < 0)
+ return PAM_SESSION_ERR;
+
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session start");
+
+ r = acquire_home(handle, /* please_authenticate = */ false, suspend_please, debug);
+ if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
+ return PAM_SUCCESS;
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = pam_putenv(handle, "SYSTEMD_HOME=1");
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
+ * not going to process the bus connection in that time, so let's better close before the daemon
+ * kicks us off because we are not processing anything. */
+ (void) pam_release_bus_connection(handle);
+ return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_close_session(
+ pam_handle_t *handle,
+ int flags,
+ int argc, const char **argv) {
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ const char *username = NULL;
+ bool debug = false;
+ int r;
+
+ if (parse_argv(handle,
+ argc, argv,
+ NULL,
+ &debug) < 0)
+ return PAM_SESSION_ERR;
+
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
+
+ /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
+ * call will be able to do its thing. */
+ r = release_home_fd(handle);
+ if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
+ return PAM_SUCCESS;
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = pam_get_user(handle, &username, NULL);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ r = pam_acquire_bus_connection(handle, &bus);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ReleaseHome");
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = sd_bus_message_append(m, "s", username);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_BUSY))
+ pam_syslog(handle, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username);
+ else {
+ pam_syslog(handle, LOG_ERR, "Failed to release user home: %s", bus_error_message(&error, r));
+ return PAM_SESSION_ERR;
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_acct_mgmt(
+ pam_handle_t *handle,
+ int flags,
+ int argc,
+ const char **argv) {
+
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ bool debug = false, please_suspend = false;
+ usec_t t;
+ int r;
+
+ if (parse_argv(handle,
+ argc, argv,
+ &please_suspend,
+ &debug) < 0)
+ return PAM_AUTH_ERR;
+
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
+
+ r = acquire_home(handle, /* please_authenticate = */ false, please_suspend, debug);
+ if (r == PAM_USER_UNKNOWN)
+ return PAM_SUCCESS; /* we don't have anything to say about users we don't manage */
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = acquire_user_record(handle, &ur);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = user_record_test_blocked(ur);
+ switch (r) {
+
+ case -ESTALE:
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is newer than current system time, prohibiting access.");
+ return PAM_ACCT_EXPIRED;
+
+ case -ENOLCK:
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is blocked, prohibiting access.");
+ return PAM_ACCT_EXPIRED;
+
+ case -EL2HLT:
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid yet, prohibiting access.");
+ return PAM_ACCT_EXPIRED;
+
+ case -EL3HLT:
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid anymore, prohibiting access.");
+ return PAM_ACCT_EXPIRED;
+
+ default:
+ if (r < 0) {
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
+ return PAM_ACCT_EXPIRED;
+ }
+
+ break;
+ }
+
+ t = user_record_ratelimit_next_try(ur);
+ if (t != USEC_INFINITY) {
+ usec_t n = now(CLOCK_REALTIME);
+
+ if (t > n) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many logins, try again in %s.",
+ format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
+
+ return PAM_MAXTRIES;
+ }
+ }
+
+ r = user_record_test_password_change_required(ur);
+ switch (r) {
+
+ case -EKEYREVOKED:
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password change required.");
+ return PAM_NEW_AUTHTOK_REQD;
+
+ case -EOWNERDEAD:
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password expired, change requird.");
+ return PAM_NEW_AUTHTOK_REQD;
+
+ case -EKEYREJECTED:
+ /* Strictly speaking this is only about password expiration, and we might want to allow
+ * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password is expired, but can't change, refusing login.");
+ return PAM_AUTHTOK_EXPIRED;
+
+ case -EKEYEXPIRED:
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password will expire soon, please change.");
+ break;
+
+ case -EROFS:
+ /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
+ break;
+
+ default:
+ if (r < 0) {
+ (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
+ return PAM_AUTHTOK_EXPIRED;
+ }
+
+ break;
+ }
+
+ return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_chauthtok(
+ pam_handle_t *handle,
+ int flags,
+ int argc,
+ const char **argv) {
+
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *old_secret = NULL, *new_secret = NULL;
+ const char *old_password = NULL, *new_password = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ unsigned n_attempts = 0;
+ bool debug = false;
+ int r;
+
+ if (parse_argv(handle,
+ argc, argv,
+ NULL,
+ &debug) < 0)
+ return PAM_AUTH_ERR;
+
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
+
+ r = pam_acquire_bus_connection(handle, &bus);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = acquire_user_record(handle, &ur);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ /* Start with cached credentials */
+ r = pam_get_item(handle, PAM_OLDAUTHTOK, (const void**) &old_password);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get old password: %s", pam_strerror(handle, r));
+ return r;
+ }
+ r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &new_password);
+ if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ if (isempty(new_password)) {
+ /* No, it's not cached, then let's ask for the password and its verification, and cache
+ * it. */
+
+ r = pam_get_authtok_noverify(handle, &new_password, "New password: ");
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to get new password: %s", pam_strerror(handle, r));
+ return r;
+ }
+ if (isempty(new_password)) {
+ pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+ return PAM_AUTHTOK_ERR;
+ }
+
+ r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to get password again: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
+ // that, and instead assume the password was already verified once when it is found to be
+ // cached already. needs to be fixed in pam_pwquality
+ }
+
+ /* Now everything is cached and checked, let's exit from the preliminary check */
+ if (FLAGS_SET(flags, PAM_PRELIM_CHECK))
+ return PAM_SUCCESS;
+
+
+ old_secret = user_record_new();
+ if (!old_secret)
+ return pam_log_oom(handle);
+
+ if (!isempty(old_password)) {
+ r = user_record_set_password(old_secret, STRV_MAKE(old_password), true);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store old password: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+ }
+
+ new_secret = user_record_new();
+ if (!new_secret)
+ return pam_log_oom(handle);
+
+ r = user_record_set_password(new_secret, STRV_MAKE(new_password), true);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to store new password: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ for (;;) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "ChangePasswordHome");
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = sd_bus_message_append(m, "s", ur->user_name);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = bus_message_append_secret(m, new_secret);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = bus_message_append_secret(m, old_secret);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error);
+ if (r == PAM_CONV_ERR) {
+ pam_syslog(handle, LOG_ERR, "Failed to prompt for password/prompt.");
+ return PAM_CONV_ERR;
+ }
+ if (r != PAM_SUCCESS)
+ return r;
+ } else {
+ pam_syslog(handle, LOG_NOTICE, "Successfully changed password for user %s.", ur->user_name);
+ return PAM_SUCCESS;
+ }
+
+ if (++n_attempts >= 5)
+ break;
+
+ /* Try again */
+ };
+
+ pam_syslog(handle, LOG_NOTICE, "Failed to change password for user %s: %m", ur->user_name);
+ return PAM_MAXTRIES;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+{
+global:
+ pam_sm_authenticate;
+ pam_sm_setcred;
+ pam_sm_open_session;
+ pam_sm_close_session;
+ pam_sm_acct_mgmt;
+ pam_sm_chauthtok;
+local: *;
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+
+#if HAVE_PWQUALITY
+/* pwquality.h uses size_t but doesn't include sys/types.h on its own */
+#include <sys/types.h>
+#include <pwquality.h>
+#endif
+
+#include "bus-common-errors.h"
+#include "home-util.h"
+#include "memory-util.h"
+#include "pwquality-util.h"
+#include "strv.h"
+
+#if HAVE_PWQUALITY
+DEFINE_TRIVIAL_CLEANUP_FUNC(pwquality_settings_t*, pwquality_free_settings);
+
+static void pwquality_maybe_disable_dictionary(
+ pwquality_settings_t *pwq) {
+
+ char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+ const char *path;
+ int r;
+
+ r = pwquality_get_str_value(pwq, PWQ_SETTING_DICT_PATH, &path);
+ if (r < 0) {
+ log_warning("Failed to read libpwquality dictionary path, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
+ return;
+ }
+
+ // REMOVE THIS AS SOON AS https://github.com/libpwquality/libpwquality/pull/21 IS MERGED AND RELEASED
+ if (isempty(path))
+ path = "/usr/share/cracklib/pw_dict.pwd.gz";
+
+ if (isempty(path)) {
+ log_warning("Weird, no dictionary file configured, ignoring.");
+ return;
+ }
+
+ if (access(path, F_OK) >= 0)
+ return;
+
+ if (errno != ENOENT) {
+ log_warning_errno(errno, "Failed to check if dictionary file %s exists, ignoring: %m", path);
+ return;
+ }
+
+ r = pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, 0);
+ if (r < 0) {
+ log_warning("Failed to disable libpwquality dictionary check, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
+ return;
+ }
+}
+
+int quality_check_password(
+ UserRecord *hr,
+ UserRecord *secret,
+ sd_bus_error *error) {
+
+ _cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
+ char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp;
+ void *auxerror;
+ int r;
+
+ assert(hr);
+ assert(secret);
+
+ pwq = pwquality_default_settings();
+ if (!pwq)
+ return log_oom();
+
+ r = pwquality_read_config(pwq, NULL, &auxerror);
+ if (r < 0)
+ log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuation, ignoring: %s",
+ pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+ pwquality_maybe_disable_dictionary(pwq);
+
+ /* This is a bit more complex than one might think at first. pwquality_check() would like to know the
+ * old password to make security checks. We support arbitrary numbers of passwords however, hence we
+ * call the function once for each combination of old and new password. */
+
+ /* Iterate through all new passwords */
+ STRV_FOREACH(pp, secret->password) {
+ bool called = false;
+ char **old;
+
+ r = test_password_many(hr->hashed_password, *pp);
+ if (r < 0)
+ return r;
+ if (r == 0) /* This is an old password as it isn't listed in the hashedPassword field, skip it */
+ continue;
+
+ /* Check this password against all old passwords */
+ STRV_FOREACH(old, secret->password) {
+
+ if (streq(*pp, *old))
+ continue;
+
+ r = test_password_many(hr->hashed_password, *old);
+ if (r < 0)
+ return r;
+ if (r > 0) /* This is a new password, not suitable as old password */
+ continue;
+
+ r = pwquality_check(pwq, *pp, *old, hr->user_name, &auxerror);
+ if (r < 0)
+ return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
+ pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+ called = true;
+ }
+
+ if (called)
+ continue;
+
+ /* If there are no old passwords, let's call pwquality_check() without any. */
+ r = pwquality_check(pwq, *pp, NULL, hr->user_name, &auxerror);
+ if (r < 0)
+ return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
+ pwquality_strerror(buf, sizeof(buf), r, auxerror));
+ }
+
+ return 0;
+}
+
+#define N_SUGGESTIONS 6
+
+int suggest_passwords(void) {
+ _cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
+ _cleanup_strv_free_erase_ char **suggestions = NULL;
+ _cleanup_(erase_and_freep) char *joined = NULL;
+ char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+ void *auxerror;
+ size_t i;
+ int r;
+
+ pwq = pwquality_default_settings();
+ if (!pwq)
+ return log_oom();
+
+ r = pwquality_read_config(pwq, NULL, &auxerror);
+ if (r < 0)
+ log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuation, ignoring: %s",
+ pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+ pwquality_maybe_disable_dictionary(pwq);
+
+ suggestions = new0(char*, N_SUGGESTIONS);
+ if (!suggestions)
+ return log_oom();
+
+ for (i = 0; i < N_SUGGESTIONS; i++) {
+ r = pwquality_generate(pwq, 64, suggestions + i);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate password, ignoring: %s",
+ pwquality_strerror(buf, sizeof(buf), r, NULL));
+ }
+
+ joined = strv_join(suggestions, " ");
+ if (!joined)
+ return log_oom();
+
+ log_info("Password suggestions: %s", joined);
+ return 0;
+}
+
+#else
+
+int quality_check_password(
+ UserRecord *hr,
+ UserRecord *secret,
+ sd_bus_error *error) {
+
+ assert(hr);
+ assert(secret);
+
+ return 0;
+}
+
+int suggest_passwords(void) {
+ return 0;
+}
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+#include "user-record.h"
+
+int quality_check_password(UserRecord *hr, UserRecord *secret, sd_bus_error *error);
+
+int suggest_passwords(void);
--- /dev/null
+#include <openssl/pem.h>
+
+#include "fd-util.h"
+#include "user-record-sign.h"
+#include "fileio.h"
+
+static int user_record_signable_json(UserRecord *ur, char **ret) {
+ _cleanup_(user_record_unrefp) UserRecord *reduced = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *j = NULL;
+ int r;
+
+ assert(ur);
+ assert(ret);
+
+ r = user_record_clone(ur, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_STRIP_SECRET|USER_RECORD_STRIP_BINDING|USER_RECORD_STRIP_STATUS|USER_RECORD_STRIP_SIGNATURE, &reduced);
+ if (r < 0)
+ return r;
+
+ j = json_variant_ref(reduced->json);
+
+ r = json_variant_normalize(&j);
+ if (r < 0)
+ return r;
+
+ return json_variant_format(j, 0, ret);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_MD_CTX*, EVP_MD_CTX_free);
+
+int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *encoded = NULL, *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL;
+ _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
+ _cleanup_free_ char *text = NULL, *key = NULL;
+ size_t signature_size = 0, key_size = 0;
+ _cleanup_free_ void *signature = NULL;
+ _cleanup_fclose_ FILE *mf = NULL;
+ int r;
+
+ assert(ur);
+ assert(private_key);
+ assert(ret);
+
+ r = user_record_signable_json(ur, &text);
+ if (r < 0)
+ return r;
+
+ md_ctx = EVP_MD_CTX_new();
+ if (!md_ctx)
+ return -ENOMEM;
+
+ if (EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, private_key) <= 0)
+ return -EIO;
+
+ /* Request signature size */
+ if (EVP_DigestSign(md_ctx, NULL, &signature_size, (uint8_t*) text, strlen(text)) <= 0)
+ return -EIO;
+
+ signature = malloc(signature_size);
+ if (!signature)
+ return -ENOMEM;
+
+ if (EVP_DigestSign(md_ctx, signature, &signature_size, (uint8_t*) text, strlen(text)) <= 0)
+ return -EIO;
+
+ mf = open_memstream_unlocked(&key, &key_size);
+ if (!mf)
+ return -ENOMEM;
+
+ if (PEM_write_PUBKEY(mf, private_key) <= 0)
+ return -EIO;
+
+ r = fflush_and_check(mf);
+ if (r < 0)
+ return r;
+
+ r = json_build(&encoded, JSON_BUILD_ARRAY(
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature, signature_size)),
+ JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key)))));
+ if (r < 0)
+ return r;
+
+ v = json_variant_ref(ur->json);
+
+ r = json_variant_set_field(&v, "signature", encoded);
+ if (r < 0)
+ return r;
+
+ if (DEBUG_LOGGING)
+ json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
+
+ signed_ur = user_record_new();
+ if (!signed_ur)
+ return log_oom();
+
+ r = user_record_load(signed_ur, v, USER_RECORD_LOAD_FULL);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(signed_ur);
+ return 0;
+}
+
+int user_record_verify(UserRecord *ur, EVP_PKEY *public_key) {
+ _cleanup_free_ char *text = NULL;
+ unsigned n_good = 0, n_bad = 0;
+ JsonVariant *array, *e;
+ int r;
+
+ assert(ur);
+ assert(public_key);
+
+ array = json_variant_by_key(ur->json, "signature");
+ if (!array)
+ return USER_RECORD_UNSIGNED;
+
+ if (!json_variant_is_array(array))
+ return -EINVAL;
+
+ if (json_variant_elements(array) == 0)
+ return USER_RECORD_UNSIGNED;
+
+ r = user_record_signable_json(ur, &text);
+ if (r < 0)
+ return r;
+
+ JSON_VARIANT_ARRAY_FOREACH(e, array) {
+ _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
+ _cleanup_free_ void *signature = NULL;
+ size_t signature_size = 0;
+ JsonVariant *data;
+
+ if (!json_variant_is_object(e))
+ return -EINVAL;
+
+ data = json_variant_by_key(e, "data");
+ if (!data)
+ return -EINVAL;
+
+ r = json_variant_unbase64(data, &signature, &signature_size);
+ if (r < 0)
+ return r;
+
+ md_ctx = EVP_MD_CTX_new();
+ if (!md_ctx)
+ return -ENOMEM;
+
+ if (EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, public_key) <= 0)
+ return -EIO;
+
+ if (EVP_DigestVerify(md_ctx, signature, signature_size, (uint8_t*) text, strlen(text)) <= 0) {
+ n_bad ++;
+ continue;
+ }
+
+ n_good ++;
+ }
+
+ return n_good > 0 ? (n_bad == 0 ? USER_RECORD_SIGNED_EXCLUSIVE : USER_RECORD_SIGNED) :
+ (n_bad == 0 ? USER_RECORD_UNSIGNED : USER_RECORD_FOREIGN);
+}
+
+int user_record_has_signature(UserRecord *ur) {
+ JsonVariant *array;
+
+ array = json_variant_by_key(ur->json, "signature");
+ if (!array)
+ return false;
+
+ if (!json_variant_is_array(array))
+ return -EINVAL;
+
+ return json_variant_elements(array) > 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <openssl/evp.h>
+
+#include "user-record.h"
+
+int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret);
+
+enum {
+ USER_RECORD_UNSIGNED, /* user record has no signature */
+ USER_RECORD_SIGNED_EXCLUSIVE, /* user record has only a signature by our own key */
+ USER_RECORD_SIGNED, /* user record is signed by us, but by others too */
+ USER_RECORD_FOREIGN, /* user record is not signed by us, but by others */
+};
+
+int user_record_verify(UserRecord *ur, EVP_PKEY *public_key);
+
+int user_record_has_signature(UserRecord *ur);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "home-util.h"
+#include "id128-util.h"
+#include "libcrypt-util.h"
+#include "mountpoint-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "user-record-util.h"
+#include "user-util.h"
+
+int user_record_synthesize(
+ UserRecord *h,
+ const char *user_name,
+ const char *realm,
+ const char *image_path,
+ UserStorage storage,
+ uid_t uid,
+ gid_t gid) {
+
+ _cleanup_free_ char *hd = NULL, *un = NULL, *ip = NULL, *rr = NULL, *user_name_and_realm = NULL;
+ char smid[SD_ID128_STRING_MAX];
+ sd_id128_t mid;
+ int r;
+
+ assert(h);
+ assert(user_name);
+ assert(image_path);
+ assert(IN_SET(storage, USER_LUKS, USER_SUBVOLUME, USER_FSCRYPT, USER_DIRECTORY));
+ assert(uid_is_valid(uid));
+ assert(gid_is_valid(gid));
+
+ /* Fill in a home record from just a username and an image path. */
+
+ if (h->json)
+ return -EBUSY;
+
+ if (!suitable_user_name(user_name))
+ return -EINVAL;
+
+ if (realm) {
+ r = suitable_realm(realm);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+ }
+
+ if (!suitable_image_path(image_path))
+ return -EINVAL;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+
+ un = strdup(user_name);
+ if (!un)
+ return -ENOMEM;
+
+ if (realm) {
+ rr = strdup(realm);
+ if (!rr)
+ return -ENOMEM;
+
+ user_name_and_realm = strjoin(user_name, "@", realm);
+ if (!user_name_and_realm)
+ return -ENOMEM;
+ }
+
+ ip = strdup(image_path);
+ if (!ip)
+ return -ENOMEM;
+
+ hd = path_join("/home/", user_name);
+ if (!hd)
+ return -ENOMEM;
+
+ r = json_build(&h->json,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
+ JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(realm)),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
+ JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path)),
+ JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd)),
+ JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage))),
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid))))))));
+ if (r < 0)
+ return r;
+
+ free_and_replace(h->user_name, un);
+ free_and_replace(h->realm, rr);
+ free_and_replace(h->user_name_and_realm_auto, user_name_and_realm);
+ free_and_replace(h->image_path, ip);
+ free_and_replace(h->home_directory, hd);
+ h->storage = storage;
+ h->uid = uid;
+
+ h->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
+ return 0;
+}
+
+int group_record_synthesize(GroupRecord *g, UserRecord *h) {
+ _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL;
+ char smid[SD_ID128_STRING_MAX];
+ sd_id128_t mid;
+ int r;
+
+ assert(g);
+ assert(h);
+
+ if (g->json)
+ return -EBUSY;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+
+ un = strdup(h->user_name);
+ if (!un)
+ return -ENOMEM;
+
+ if (h->realm) {
+ rr = strdup(h->realm);
+ if (!rr)
+ return -ENOMEM;
+
+ group_name_and_realm = strjoin(un, "@", rr);
+ if (!group_name_and_realm)
+ return -ENOMEM;
+ }
+
+ r = json_build(&g->json,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(un)),
+ JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(rr)),
+ JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h))))))),
+ JSON_BUILD_PAIR_CONDITION(h->disposition >= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h)))),
+ JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
+ if (r < 0)
+ return r;
+
+ free_and_replace(g->group_name, un);
+ free_and_replace(g->realm, rr);
+ free_and_replace(g->group_name_and_realm_auto, group_name_and_realm);
+ g->gid = user_record_gid(h);
+ g->disposition = h->disposition;
+
+ g->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
+ return 0;
+}
+
+int user_record_reconcile(
+ UserRecord *host,
+ UserRecord *embedded,
+ UserReconcileMode mode,
+ UserRecord **ret) {
+
+ int r, result;
+
+ /* Reconciles the identity record stored on the host with the one embedded in a $HOME
+ * directory. Returns the following error codes:
+ *
+ * -EINVAL: one of the records not valid
+ * -REMCHG: identity records are not about the same user
+ * -ESTALE: embedded identity record is equally new or newer than supplied record
+ *
+ * Return the new record to use, which is either the the embedded record updated with the host
+ * binding or the host record. In both cases the secret data is stripped. */
+
+ assert(host);
+ assert(embedded);
+
+ /* Make sure both records are initialized */
+ if (!host->json || !embedded->json)
+ return -EINVAL;
+
+ /* Ensure these records actually contain user data */
+ if (!(embedded->mask & host->mask & USER_RECORD_REGULAR))
+ return -EINVAL;
+
+ /* Make sure the user name and realm matches */
+ if (!user_record_compatible(host, embedded))
+ return -EREMCHG;
+
+ /* Embedded identities may not contain secrets or binding info*/
+ if ((embedded->mask & (USER_RECORD_SECRET|USER_RECORD_BINDING)) != 0)
+ return -EINVAL;
+
+ /* The embedded record checked out, let's now figure out which of the two identities we'll consider
+ * in effect from now on. We do this by checking the last change timestamp, and in doubt always let
+ * the embedded data win. */
+ if (host->last_change_usec != UINT64_MAX &&
+ (embedded->last_change_usec == UINT64_MAX || host->last_change_usec > embedded->last_change_usec))
+
+ /* The host version is definitely newer, either because it has a version at all and the
+ * embedded version doesn't or because it is numerically newer. */
+ result = USER_RECONCILE_HOST_WON;
+
+ else if (host->last_change_usec == embedded->last_change_usec) {
+
+ /* The nominal version number of the host and the embedded identity is the same. If so, let's
+ * verify that, and tell the caller if we are ignoring embedded data. */
+
+ r = user_record_masked_equal(host, embedded, USER_RECORD_REGULAR|USER_RECORD_PRIVILEGED|USER_RECORD_PER_MACHINE);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ if (mode == USER_RECONCILE_REQUIRE_NEWER)
+ return -ESTALE;
+
+ result = USER_RECONCILE_IDENTICAL;
+ } else
+ result = USER_RECONCILE_HOST_WON;
+ } else {
+ _cleanup_(json_variant_unrefp) JsonVariant *extended = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *merged = NULL;
+ JsonVariant *e;
+
+ /* The embedded version is newer */
+
+ if (mode == USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL)
+ return -ESTALE;
+
+ /* Copy in the binding data */
+ extended = json_variant_ref(embedded->json);
+
+ e = json_variant_by_key(host->json, "binding");
+ if (e) {
+ r = json_variant_set_field(&extended, "binding", e);
+ if (r < 0)
+ return r;
+ }
+
+ merged = user_record_new();
+ if (!merged)
+ return -ENOMEM;
+
+ r = user_record_load(merged, extended, USER_RECORD_LOAD_MASK_SECRET);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(merged);
+ return USER_RECONCILE_EMBEDDED_WON; /* update */
+ }
+
+ /* Strip out secrets */
+ r = user_record_clone(host, USER_RECORD_LOAD_MASK_SECRET, ret);
+ if (r < 0)
+ return r;
+
+ return result;
+}
+
+int user_record_add_binding(
+ UserRecord *h,
+ UserStorage storage,
+ const char *image_path,
+ sd_id128_t partition_uuid,
+ sd_id128_t luks_uuid,
+ sd_id128_t fs_uuid,
+ const char *luks_cipher,
+ const char *luks_cipher_mode,
+ uint64_t luks_volume_key_size,
+ const char *file_system_type,
+ const char *home_directory,
+ uid_t uid,
+ gid_t gid) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
+ char smid[SD_ID128_STRING_MAX], partition_uuids[37], luks_uuids[37], fs_uuids[37];
+ _cleanup_free_ char *ip = NULL, *hd = NULL;
+ sd_id128_t mid;
+ int r;
+
+ assert(h);
+
+ if (!h->json)
+ return -EUNATCH;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+ sd_id128_to_string(mid, smid);
+
+ if (image_path) {
+ ip = strdup(image_path);
+ if (!ip)
+ return -ENOMEM;
+ }
+
+ if (home_directory) {
+ hd = strdup(home_directory);
+ if (!hd)
+ return -ENOMEM;
+ }
+
+ r = json_build(&new_binding_entry,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid, partition_uuids))),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid, luks_uuids))),
+ JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid, fs_uuids))),
+ JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
+ JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
+ JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
+ JSON_BUILD_PAIR_CONDITION(!!file_system_type, "fileSystemType", JSON_BUILD_STRING(file_system_type)),
+ JSON_BUILD_PAIR_CONDITION(!!home_directory, "homeDirectory", JSON_BUILD_STRING(home_directory)),
+ JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid), "uid", JSON_BUILD_UNSIGNED(uid)),
+ JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid), "gid", JSON_BUILD_UNSIGNED(gid)),
+ JSON_BUILD_PAIR_CONDITION(storage >= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage)))));
+ if (r < 0)
+ return r;
+
+ binding = json_variant_ref(json_variant_by_key(h->json, "binding"));
+ if (binding) {
+ _cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
+
+ /* Merge the new entry with an old one, if that exists */
+ be = json_variant_ref(json_variant_by_key(binding, smid));
+ if (be) {
+ r = json_variant_merge(&be, new_binding_entry);
+ if (r < 0)
+ return r;
+
+ json_variant_unref(new_binding_entry);
+ new_binding_entry = TAKE_PTR(be);
+ }
+ }
+
+ r = json_variant_set_field(&binding, smid, new_binding_entry);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&h->json, "binding", binding);
+ if (r < 0)
+ return r;
+
+ if (storage >= 0)
+ h->storage = storage;
+
+ if (ip)
+ free_and_replace(h->image_path, ip);
+
+ if (!sd_id128_is_null(partition_uuid))
+ h->partition_uuid = partition_uuid;
+
+ if (!sd_id128_is_null(luks_uuid))
+ h->luks_uuid = luks_uuid;
+
+ if (!sd_id128_is_null(fs_uuid))
+ h->file_system_uuid = fs_uuid;
+
+ if (hd)
+ free_and_replace(h->home_directory, hd);
+
+ if (uid_is_valid(uid))
+ h->uid = uid;
+
+ h->mask |= USER_RECORD_BINDING;
+ return 1;
+}
+
+int user_record_test_home_directory(UserRecord *h) {
+ const char *hd;
+ int r;
+
+ assert(h);
+
+ /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
+
+ hd = user_record_home_directory(h);
+ if (!hd)
+ return -ENXIO;
+
+ r = is_dir(hd, false);
+ if (r == -ENOENT)
+ return USER_TEST_ABSENT;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOTDIR;
+
+ r = path_is_mount_point(hd, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return USER_TEST_MOUNTED;
+
+ /* If the image path and the home directory are identical, then it's OK if the directory is
+ * populated. */
+ if (IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) {
+ const char *ip;
+
+ ip = user_record_image_path(h);
+ if (ip && path_equal(ip, hd))
+ return USER_TEST_EXISTS;
+ }
+
+ /* Otherwise it's not OK */
+ r = dir_is_empty(hd);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBUSY;
+
+ return USER_TEST_EXISTS;
+}
+
+int user_record_test_home_directory_and_warn(UserRecord *h) {
+ int r;
+
+ assert(h);
+
+ r = user_record_test_home_directory(h);
+ if (r == -ENXIO)
+ return log_error_errno(r, "User record lacks home directory, refusing.");
+ if (r == -ENOTDIR)
+ return log_error_errno(r, "Home directory %s is not a directory, refusing.", user_record_home_directory(h));
+ if (r == -EBUSY)
+ return log_error_errno(r, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h));
+ if (r < 0)
+ return log_error_errno(r, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h));
+
+ return r;
+}
+
+int user_record_test_image_path(UserRecord *h) {
+ const char *ip;
+ struct stat st;
+
+ assert(h);
+
+ if (user_record_storage(h) == USER_CIFS)
+ return USER_TEST_UNDEFINED;
+
+ ip = user_record_image_path(h);
+ if (!ip)
+ return -ENXIO;
+
+ if (stat(ip, &st) < 0) {
+ if (errno == ENOENT)
+ return USER_TEST_ABSENT;
+
+ return -errno;
+ }
+
+ switch (user_record_storage(h)) {
+
+ case USER_LUKS:
+ if (S_ISREG(st.st_mode))
+ return USER_TEST_EXISTS;
+ if (S_ISBLK(st.st_mode)) {
+ /* For block devices we can't really be sure if the device referenced actually is the
+ * fs we look for or some other file system (think: what does /dev/sdb1 refer
+ * to?). Hence, let's return USER_TEST_MAYBE as an ambigious return value for these
+ * case, except if the device path used is one of the paths that is based on a
+ * filesystem or partition UUID or label, because in those cases we can be sure we
+ * are referring to the right device. */
+
+ if (PATH_STARTSWITH_SET(ip,
+ "/dev/disk/by-uuid/",
+ "/dev/disk/by-partuuid/",
+ "/dev/disk/by-partlabel/",
+ "/dev/disk/by-label/"))
+ return USER_TEST_EXISTS;
+
+ return USER_TEST_MAYBE;
+ }
+
+ return -EBADFD;
+
+ case USER_CLASSIC:
+ case USER_DIRECTORY:
+ case USER_SUBVOLUME:
+ case USER_FSCRYPT:
+ if (S_ISDIR(st.st_mode))
+ return USER_TEST_EXISTS;
+
+ return -ENOTDIR;
+
+ default:
+ assert_not_reached("Unexpected record type");
+ }
+}
+
+int user_record_test_image_path_and_warn(UserRecord *h) {
+ int r;
+
+ assert(h);
+
+ r = user_record_test_image_path(h);
+ if (r == -ENXIO)
+ return log_error_errno(r, "User record lacks image path, refusing.");
+ if (r == -EBADFD)
+ return log_error_errno(r, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h));
+ if (r == -ENOTDIR)
+ return log_error_errno(r, "Image path %s is not a directory, refusing.", user_record_image_path(h));
+ if (r < 0)
+ return log_error_errno(r, "Failed to test whether image path %s exists: %m", user_record_image_path(h));
+
+ return r;
+}
+
+int user_record_test_secret(UserRecord *h, UserRecord *secret) {
+ char **i;
+ int r;
+
+ assert(h);
+
+ /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
+
+ if (strv_isempty(h->hashed_password))
+ return -ENXIO;
+
+ STRV_FOREACH(i, secret->password) {
+ r = test_password_many(h->hashed_password, *i);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+ }
+
+ return -ENOKEY;
+}
+
+int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
+ _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine = NULL, *midv = NULL, *midav = NULL, *ne = NULL;
+ _cleanup_free_ JsonVariant **array = NULL;
+ char smid[SD_ID128_STRING_MAX];
+ size_t idx = SIZE_MAX, n;
+ JsonVariant *per_machine;
+ sd_id128_t mid;
+ int r;
+
+ assert(h);
+
+ if (!h->json)
+ return -EUNATCH;
+
+ if (disk_size < USER_DISK_SIZE_MIN || disk_size > USER_DISK_SIZE_MAX)
+ return -ERANGE;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+
+ sd_id128_to_string(mid, smid);
+
+ r = json_variant_new_string(&midv, smid);
+ if (r < 0)
+ return r;
+
+ r = json_variant_new_array(&midav, (JsonVariant*[]) { midv }, 1);
+ if (r < 0)
+ return r;
+
+ per_machine = json_variant_by_key(h->json, "perMachine");
+ if (per_machine) {
+ size_t i;
+
+ if (!json_variant_is_array(per_machine))
+ return -EINVAL;
+
+ n = json_variant_elements(per_machine);
+
+ array = new(JsonVariant*, n + 1);
+ if (!array)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ JsonVariant *m;
+
+ array[i] = json_variant_by_index(per_machine, i);
+
+ if (!json_variant_is_object(array[i]))
+ return -EINVAL;
+
+ m = json_variant_by_key(array[i], "matchMachineId");
+ if (!m) {
+ /* No machineId field? Let's ignore this, but invalidate what we found so far */
+ idx = SIZE_MAX;
+ continue;
+ }
+
+ if (json_variant_equal(m, midv) ||
+ json_variant_equal(m, midav)) {
+ /* Matches exactly what we are looking for. Let's use this */
+ idx = i;
+ continue;
+ }
+
+ r = per_machine_id_match(m, JSON_PERMISSIVE);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ /* Also matches what we are looking for, but with a broader match. In this
+ * case let's ignore this entry, and add a new specific one to the end. */
+ idx = SIZE_MAX;
+ }
+
+ if (idx == SIZE_MAX)
+ idx = n++; /* Nothing suitable found, place new entry at end */
+ else
+ ne = json_variant_ref(array[idx]);
+
+ } else {
+ array = new(JsonVariant*, 1);
+ if (!array)
+ return -ENOMEM;
+
+ idx = 0;
+ n = 1;
+ }
+
+ if (!ne) {
+ r = json_variant_set_field(&ne, "matchMachineId", midav);
+ if (r < 0)
+ return r;
+ }
+
+ r = json_variant_set_field_unsigned(&ne, "diskSize", disk_size);
+ if (r < 0)
+ return r;
+
+ assert(idx < n);
+ array[idx] = ne;
+
+ r = json_variant_new_array(&new_per_machine, array, n);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&h->json, "perMachine", new_per_machine);
+ if (r < 0)
+ return r;
+
+ h->disk_size = disk_size;
+ h->mask |= USER_RECORD_PER_MACHINE;
+ return 0;
+}
+
+int user_record_update_last_changed(UserRecord *h, bool with_password) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ usec_t n;
+ int r;
+
+ assert(h);
+
+ if (!h->json)
+ return -EUNATCH;
+
+ n = now(CLOCK_REALTIME);
+
+ /* refuse downgrading */
+ if (h->last_change_usec != UINT64_MAX && h->last_change_usec >= n)
+ return -ECHRNG;
+ if (h->last_password_change_usec != UINT64_MAX && h->last_password_change_usec >= n)
+ return -ECHRNG;
+
+ v = json_variant_ref(h->json);
+
+ r = json_variant_set_field_unsigned(&v, "lastChangeUSec", n);
+ if (r < 0)
+ return r;
+
+ if (with_password) {
+ r = json_variant_set_field_unsigned(&v, "lastPasswordChangeUSec", n);
+ if (r < 0)
+ return r;
+
+ h->last_password_change_usec = n;
+ }
+
+ h->last_change_usec = n;
+
+ json_variant_unref(h->json);
+ h->json = TAKE_PTR(v);
+
+ h->mask |= USER_RECORD_REGULAR;
+ return 0;
+}
+
+int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
+ _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
+ _cleanup_strv_free_ char **np = NULL;
+ char **i;
+ int r;
+
+ assert(h);
+ assert(secret);
+
+ /* Initializes the hashed password list from the specified plaintext passwords */
+
+ if (extend) {
+ np = strv_copy(h->hashed_password);
+ if (!np)
+ return -ENOMEM;
+
+ strv_uniq(np);
+ }
+
+ STRV_FOREACH(i, secret) {
+ _cleanup_free_ char *salt = NULL;
+ struct crypt_data cd = {};
+ char *k;
+
+ r = make_salt(&salt);
+ if (r < 0)
+ return r;
+
+ errno = 0;
+ k = crypt_r(*i, salt, &cd);
+ if (!k)
+ return errno_or_else(EINVAL);
+
+ r = strv_extend(&np, k);
+ if (r < 0)
+ return r;
+ }
+
+ priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
+
+ if (strv_isempty(np))
+ r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *new_array = NULL;
+
+ r = json_variant_new_array_strv(&new_array, np);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&priv, "hashedPassword", new_array);
+ if (r < 0)
+ return r;
+ }
+
+ r = json_variant_set_field(&h->json, "privileged", priv);
+ if (r < 0)
+ return r;
+
+ strv_free_and_replace(h->hashed_password, np);
+
+ SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
+ return 0;
+}
+
+int user_record_set_hashed_password(UserRecord *h, char **hashed_password) {
+ _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
+ _cleanup_strv_free_ char **copy = NULL;
+ int r;
+
+ assert(h);
+
+ priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
+
+ if (strv_isempty(hashed_password))
+ r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+
+ copy = strv_copy(hashed_password);
+ if (!copy)
+ return -ENOMEM;
+
+ strv_uniq(copy);
+
+ r = json_variant_new_array_strv(&array, copy);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&priv, "hashedPassword", array);
+ }
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&h->json, "privileged", priv);
+ if (r < 0)
+ return r;
+
+ strv_free_and_replace(h->hashed_password, copy);
+
+ SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
+ return 0;
+}
+
+int user_record_set_password(UserRecord *h, char **password, bool prepend) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ _cleanup_(strv_free_erasep) char **e = NULL;
+ int r;
+
+ assert(h);
+
+ if (prepend) {
+ e = strv_copy(password);
+ if (!e)
+ return -ENOMEM;
+
+ r = strv_extend_strv(&e, h->password, true);
+ if (r < 0)
+ return r;
+
+ strv_uniq(e);
+
+ if (strv_equal(h->password, e))
+ return 0;
+
+ } else {
+ if (strv_equal(h->password, password))
+ return 0;
+
+ e = strv_copy(password);
+ if (!e)
+ return -ENOMEM;
+
+ strv_uniq(e);
+ }
+
+ w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+ if (strv_isempty(e))
+ r = json_variant_filter(&w, STRV_MAKE("password"));
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
+
+ r = json_variant_new_array_strv(&l, e);
+ if (r < 0)
+ return r;
+
+ json_variant_sensitive(l);
+
+ r = json_variant_set_field(&w, "password", l);
+ }
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&h->json, "secret", w);
+ if (r < 0)
+ return r;
+
+ strv_free_and_replace(h->password, e);
+
+ SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+ return 0;
+}
+
+int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ _cleanup_(strv_free_erasep) char **e = NULL;
+ int r;
+
+ assert(h);
+
+ if (prepend) {
+ e = strv_copy(pin);
+ if (!e)
+ return -ENOMEM;
+
+ r = strv_extend_strv(&e, h->pkcs11_pin, true);
+ if (r < 0)
+ return r;
+
+ strv_uniq(e);
+
+ if (strv_equal(h->pkcs11_pin, e))
+ return 0;
+
+ } else {
+ if (strv_equal(h->pkcs11_pin, pin))
+ return 0;
+
+ e = strv_copy(pin);
+ if (!e)
+ return -ENOMEM;
+
+ strv_uniq(e);
+ }
+
+ w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+ if (strv_isempty(e))
+ r = json_variant_filter(&w, STRV_MAKE("pkcs11Pin"));
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
+
+ r = json_variant_new_array_strv(&l, e);
+ if (r < 0)
+ return r;
+
+ json_variant_sensitive(l);
+
+ r = json_variant_set_field(&w, "pkcs11Pin", l);
+ }
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&h->json, "secret", w);
+ if (r < 0)
+ return r;
+
+ strv_free_and_replace(h->pkcs11_pin, e);
+
+ SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+ return 0;
+}
+
+int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ int r;
+
+ assert(h);
+
+ w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+ if (b < 0)
+ r = json_variant_filter(&w, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
+ else
+ r = json_variant_set_field_boolean(&w, "pkcs11ProtectedAuthenticationPathPermitted", b);
+ if (r < 0)
+ return r;
+
+ if (json_variant_is_blank_object(w))
+ r = json_variant_filter(&h->json, STRV_MAKE("secret"));
+ else
+ r = json_variant_set_field(&h->json, "secret", w);
+ if (r < 0)
+ return r;
+
+ h->pkcs11_protected_authentication_path_permitted = b;
+
+ SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+ return 0;
+}
+
+static bool per_machine_entry_empty(JsonVariant *v) {
+ const char *k;
+ _unused_ JsonVariant *e;
+
+ JSON_VARIANT_OBJECT_FOREACH(k, e, v)
+ if (!STR_IN_SET(k, "matchMachineId", "matchHostname"))
+ return false;
+
+ return true;
+}
+
+int user_record_set_password_change_now(UserRecord *h, int b) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ JsonVariant *per_machine;
+ int r;
+
+ assert(h);
+
+ w = json_variant_ref(h->json);
+
+ if (b < 0)
+ r = json_variant_filter(&w, STRV_MAKE("passwordChangeNow"));
+ else
+ r = json_variant_set_field_boolean(&w, "passwordChangeNow", b);
+ if (r < 0)
+ return r;
+
+ /* Also drop the field from all perMachine entries */
+ per_machine = json_variant_by_key(w, "perMachine");
+ if (per_machine) {
+ _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+ JsonVariant *e;
+
+ JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
+ _cleanup_(json_variant_unrefp) JsonVariant *z = NULL;
+
+ if (!json_variant_is_object(e))
+ return -EINVAL;
+
+ z = json_variant_ref(e);
+
+ r = json_variant_filter(&z, STRV_MAKE("passwordChangeNow"));
+ if (r < 0)
+ return r;
+
+ if (per_machine_entry_empty(z))
+ continue;
+
+ r = json_variant_append_array(&array, z);
+ if (r < 0)
+ return r;
+ }
+
+ if (json_variant_is_blank_array(array))
+ r = json_variant_filter(&w, STRV_MAKE("perMachine"));
+ else
+ r = json_variant_set_field(&w, "perMachine", array);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(h->mask, USER_RECORD_PER_MACHINE, !json_variant_is_blank_array(array));
+ }
+
+ json_variant_unref(h->json);
+ h->json = TAKE_PTR(w);
+
+ h->password_change_now = b;
+
+ return 0;
+}
+
+int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
+ int r;
+
+ assert(h);
+
+ /* Merges the secrets from 'secret' into 'h'. */
+
+ r = user_record_set_password(h, secret->password, true);
+ if (r < 0)
+ return r;
+
+ r = user_record_set_pkcs11_pin(h, secret->pkcs11_pin, true);
+ if (r < 0)
+ return r;
+
+ if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
+ r = user_record_set_pkcs11_protected_authentication_path_permitted(h, secret->pkcs11_protected_authentication_path_permitted);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int user_record_good_authentication(UserRecord *h) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
+ char buf[SD_ID128_STRING_MAX];
+ uint64_t counter, usec;
+ sd_id128_t mid;
+ int r;
+
+ assert(h);
+
+ switch (h->good_authentication_counter) {
+ case UINT64_MAX:
+ counter = 1;
+ break;
+ case UINT64_MAX-1:
+ counter = h->good_authentication_counter; /* saturate */
+ break;
+ default:
+ counter = h->good_authentication_counter + 1;
+ break;
+ }
+
+ usec = now(CLOCK_REALTIME);
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+
+ v = json_variant_ref(h->json);
+ w = json_variant_ref(json_variant_by_key(v, "status"));
+ z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+
+ r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field_unsigned(&z, "lastGoodAuthenticationUSec", usec);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&w, buf, z);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&v, "status", w);
+ if (r < 0)
+ return r;
+
+ json_variant_unref(h->json);
+ h->json = TAKE_PTR(v);
+
+ h->good_authentication_counter = counter;
+ h->last_good_authentication_usec = usec;
+
+ h->mask |= USER_RECORD_STATUS;
+ return 0;
+}
+
+int user_record_bad_authentication(UserRecord *h) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
+ char buf[SD_ID128_STRING_MAX];
+ uint64_t counter, usec;
+ sd_id128_t mid;
+ int r;
+
+ assert(h);
+
+ switch (h->bad_authentication_counter) {
+ case UINT64_MAX:
+ counter = 1;
+ break;
+ case UINT64_MAX-1:
+ counter = h->bad_authentication_counter; /* saturate */
+ break;
+ default:
+ counter = h->bad_authentication_counter + 1;
+ break;
+ }
+
+ usec = now(CLOCK_REALTIME);
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+
+ v = json_variant_ref(h->json);
+ w = json_variant_ref(json_variant_by_key(v, "status"));
+ z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+
+ r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field_unsigned(&z, "lastBadAuthenticationUSec", usec);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&w, buf, z);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&v, "status", w);
+ if (r < 0)
+ return r;
+
+ json_variant_unref(h->json);
+ h->json = TAKE_PTR(v);
+
+ h->bad_authentication_counter = counter;
+ h->last_bad_authentication_usec = usec;
+
+ h->mask |= USER_RECORD_STATUS;
+ return 0;
+}
+
+int user_record_ratelimit(UserRecord *h) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
+ usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
+ char buf[SD_ID128_STRING_MAX];
+ sd_id128_t mid;
+ int r;
+
+ assert(h);
+
+ usec = now(CLOCK_REALTIME);
+
+ if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec)
+ /* Hmm, time is running backwards? Say no! */
+ return 0;
+ else if (h->ratelimit_begin_usec == UINT64_MAX ||
+ usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
+ /* Fresh start */
+ new_ratelimit_begin_usec = usec;
+ new_ratelimit_count = 1;
+ } else if (h->ratelimit_count < user_record_ratelimit_burst(h)) {
+ /* Count up */
+ new_ratelimit_begin_usec = h->ratelimit_begin_usec;
+ new_ratelimit_count = h->ratelimit_count + 1;
+ } else
+ /* Limit hit */
+ return 0;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+
+ v = json_variant_ref(h->json);
+ w = json_variant_ref(json_variant_by_key(v, "status"));
+ z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+
+ r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field_unsigned(&z, "rateLimitCount", new_ratelimit_count);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&w, buf, z);
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&v, "status", w);
+ if (r < 0)
+ return r;
+
+ json_variant_unref(h->json);
+ h->json = TAKE_PTR(v);
+
+ h->ratelimit_begin_usec = new_ratelimit_begin_usec;
+ h->ratelimit_count = new_ratelimit_count;
+
+ h->mask |= USER_RECORD_STATUS;
+ return 1;
+}
+
+int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
+ assert(hr);
+
+ if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
+
+ if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
+
+ if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
+
+ if (hr->service && !streq(hr->service, "io.systemd.Home"))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "user-record.h"
+#include "group-record.h"
+
+int user_record_synthesize(UserRecord *h, const char *user_name, const char *realm, const char *image_path, UserStorage storage, uid_t uid, gid_t gid);
+int group_record_synthesize(GroupRecord *g, UserRecord *u);
+
+typedef enum UserReconcileMode {
+ USER_RECONCILE_ANY,
+ USER_RECONCILE_REQUIRE_NEWER, /* host version must be newer than embedded version */
+ USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, /* similar, but may also be equal */
+ _USER_RECONCILE_MODE_MAX,
+ _USER_RECONCILE_MODE_INVALID = -1,
+} UserReconcileMode;
+
+enum { /* return values */
+ USER_RECONCILE_HOST_WON,
+ USER_RECONCILE_EMBEDDED_WON,
+ USER_RECONCILE_IDENTICAL,
+};
+
+int user_record_reconcile(UserRecord *host, UserRecord *embedded, UserReconcileMode mode, UserRecord **ret);
+int user_record_add_binding(UserRecord *h, UserStorage storage, const char *image_path, sd_id128_t partition_uuid, sd_id128_t luks_uuid, sd_id128_t fs_uuid, const char *luks_cipher, const char *luks_cipher_mode, uint64_t luks_volume_key_size, const char *file_system_type, const char *home_directory, uid_t uid, gid_t gid);
+
+/* Results of the two test functions below. */
+enum {
+ USER_TEST_UNDEFINED, /* Returned by user_record_test_image_path() if the storage type knows no image paths */
+ USER_TEST_ABSENT,
+ USER_TEST_EXISTS,
+ USER_TEST_MOUNTED, /* Only applies to user_record_test_home_directory(), when the home directory exists. */
+ USER_TEST_MAYBE, /* Only applies to LUKS devices: block device exists, but we don't know if it's the right one */
+};
+
+int user_record_test_home_directory(UserRecord *h);
+int user_record_test_home_directory_and_warn(UserRecord *h);
+int user_record_test_image_path(UserRecord *h);
+int user_record_test_image_path_and_warn(UserRecord *h);
+
+int user_record_test_secret(UserRecord *h, UserRecord *secret);
+
+int user_record_update_last_changed(UserRecord *h, bool with_password);
+int user_record_set_disk_size(UserRecord *h, uint64_t disk_size);
+int user_record_set_password(UserRecord *h, char **password, bool prepend);
+int user_record_make_hashed_password(UserRecord *h, char **password, bool extend);
+int user_record_set_hashed_password(UserRecord *h, char **hashed_password);
+int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend);
+int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b);
+int user_record_set_password_change_now(UserRecord *h, int b);
+int user_record_merge_secret(UserRecord *h, UserRecord *secret);
+int user_record_good_authentication(UserRecord *h);
+int user_record_bad_authentication(UserRecord *h);
+int user_record_ratelimit(UserRecord *h);
+
+int user_record_is_supported(UserRecord *hr, sd_bus_error *error);
#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "def.h"
#include "env-file-label.h"
#include "env-file.h"
#include <stdio.h>
#include "alloc-util.h"
+#include "gpt.h"
#include "id128-print.h"
#include "main-func.h"
#include "pretty-print.h"
+#include "strv.h"
+#include "format-table.h"
#include "terminal-util.h"
#include "util.h"
#include "verbs.h"
return id128_pretty_print(id, arg_mode);
}
+static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first) {
+ int r;
+
+ if (arg_mode == ID128_PRINT_PRETTY) {
+ _cleanup_free_ char *id = NULL;
+
+ id = strreplace(name, "-", "_");
+ if (!id)
+ return log_oom();
+
+ ascii_strupper(id);
+
+ r = id128_pretty_print_sample(id, uuid);
+ if (r < 0)
+ return r;
+ if (!first)
+ puts("");
+ return 0;
+
+ } else {
+ if (!*table) {
+ *table = table_new("name", "id");
+ if (!*table)
+ return log_oom();
+ table_set_width(*table, 0);
+ }
+
+ return table_add_many(*table,
+ TABLE_STRING, name,
+ arg_mode == ID128_PRINT_ID128 ? TABLE_ID128 : TABLE_UUID,
+ uuid);
+ }
+}
+
+static int verb_show(int argc, char **argv, void *userdata) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ char **p;
+ int r;
+
+ argv = strv_skip(argv, 1);
+ if (strv_isempty(argv))
+ for (const GptPartitionType *e = gpt_partition_type_table; e->name; e++) {
+ r = show_one(&table, e->name, e->uuid, e == gpt_partition_type_table);
+ if (r < 0)
+ return r;
+ }
+ else
+ STRV_FOREACH(p, argv) {
+ sd_id128_t uuid;
+ bool have_uuid;
+ const char *id;
+
+ /* Check if the argument is an actual UUID first */
+ have_uuid = sd_id128_from_string(*p, &uuid) >= 0;
+
+ if (have_uuid)
+ id = gpt_partition_type_uuid_to_string(uuid) ?: "XYZ";
+ else {
+ r = gpt_partition_type_uuid_from_string(*p, &uuid);
+ if (r < 0)
+ return log_error_errno(r, "Unknown identifier \"%s\".", *p);
+
+ id = *p;
+ }
+
+ r = show_one(&table, id, uuid, p == argv);
+ if (r < 0)
+ return r;
+ }
+
+ if (table) {
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print table: %m");
+ }
+
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
printf("%s [OPTIONS...] COMMAND\n\n"
"%sGenerate and print 128bit identifiers.%s\n"
"\nCommands:\n"
- " new Generate a new id128 string\n"
+ " new Generate a new ID\n"
" machine-id Print the ID of current machine\n"
" boot-id Print the ID of current boot\n"
" invocation-id Print the ID of current invocation\n"
+ " show [NAME] Print one or more well-known IDs\n"
" help Show this help\n"
"\nOptions:\n"
" -h --help Show this help\n"
{ "machine-id", VERB_ANY, 1, 0, verb_machine_id },
{ "boot-id", VERB_ANY, 1, 0, verb_boot_id },
{ "invocation-id", VERB_ANY, 1, 0, verb_invocation_id },
+ { "show", VERB_ANY, VERB_ANY, 0, verb_show },
{ "help", VERB_ANY, VERB_ANY, 0, verb_help },
{}
};
if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
return -EIO;
+ if (curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)
+ return -EIO;
+
+ if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, 60L) != CURLE_OK)
+ return -EIO;
+
+ if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 30L) != CURLE_OK)
+ return -EIO;
+
*ret = TAKE_PTR(c);
return 0;
}
#include "alloc-util.h"
#include "btrfs-util.h"
-#include "chattr-util.h"
#include "copy.h"
#include "fd-util.h"
#include "fs-util.h"
if (converted_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", t);
- r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
+ (void) import_set_nocow_and_log(converted_fd, t);
log_info("Unpacking QCOW2 file.");
if (i->output_fd < 0)
return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
- r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
-
+ (void) import_set_nocow_and_log(i->output_fd, i->temp_path);
return 0;
}
#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "def.h"
#include "fd-util.h"
#include "float.h"
#include "alloc-util.h"
#include "btrfs-util.h"
-#include "chattr-util.h"
#include "copy.h"
#include "curl-util.h"
#include "fd-util.h"
if (converted_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", t);
- r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
+ (void) import_set_nocow_and_log(converted_fd, t);
log_info("Unpacking QCOW2 file.");
if (dfd < 0)
return log_error_errno(errno, "Failed to create writable copy of image: %m");
- /* Turn off COW writing. This should greatly improve
- * performance on COW file systems like btrfs, since it
- * reduces fragmentation caused by not allowing in-place
- * writes. */
- r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to set file attributes on %s: %m", tp);
+ /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
+ * since it reduces fragmentation caused by not allowing in-place writes. */
+ (void) import_set_nocow_and_log(dfd, tp);
r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, COPY_REFLINK);
if (r < 0) {
if (r < 0)
return r;
- r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", i->temp_path);
-
+ (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
return 0;
}
r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
else if (arg_file)
r = sd_journal_open_files(j, (const char**) arg_file, 0);
- else if (arg_machine)
+ else if (arg_machine) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ /* FIXME: replace with D-Bus call OpenMachineRootDirectory() so that things also work with raw disk images */
r = sd_journal_open_container(j, arg_machine, 0);
- else
+#pragma GCC diagnostic pop
+ } else
r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
if (r < 0)
log_error_errno(r, "Failed to open %s: %m",
return 0;
}
-static int journal_file_fstat(JournalFile *f) {
+int journal_file_fstat(JournalFile *f) {
int r;
assert(f);
int journal_file_set_offline(JournalFile *f, bool wait);
bool journal_file_is_offlining(JournalFile *f);
JournalFile* journal_file_close(JournalFile *j);
+int journal_file_fstat(JournalFile *f);
DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_close);
int journal_file_open_reliably(
char *path;
char *prefix;
+ char *namespace;
OrderedHashmap *files;
IteratedCache *files_cache;
#include "sigbus.h"
#include "string-table.h"
#include "strv.h"
+#include "stdio-util.h"
#include "syslog-util.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
#include "varlink.h"
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
-
#define PROCESS_INOTIFY_INTERVAL 1024 /* Every 1,024 messages processed */
-#if HAVE_PCRE2
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
-
-static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
- int errorcode, r;
- PCRE2_SIZE erroroffset;
- pcre2_code *p;
-
- p = pcre2_compile((PCRE2_SPTR8) pattern,
- PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
- if (!p) {
- unsigned char buf[LINE_MAX];
-
- r = pcre2_get_error_message(errorcode, buf, sizeof buf);
-
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Bad pattern \"%s\": %s", pattern,
- r < 0 ? "unknown error" : (char *)buf);
- }
-
- *out = p;
- return 0;
-}
-
-#endif
-
enum {
/* Special values for arg_lines */
ARG_LINES_DEFAULT = -2,
static char **arg_file = NULL;
static bool arg_file_stdin = false;
static int arg_priorities = 0xFF;
+static Set *arg_facilities = NULL;
static char *arg_verify_key = NULL;
#if HAVE_GCRYPT
static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
static bool arg_catalog = false;
static bool arg_reverse = false;
static int arg_journal_type = 0;
+static int arg_namespace_flags = 0;
static char *arg_root = NULL;
static const char *arg_machine = NULL;
+static const char *arg_namespace = NULL;
static uint64_t arg_vacuum_size = 0;
static uint64_t arg_vacuum_n_files = 0;
static usec_t arg_vacuum_time = 0;
static char **arg_output_fields = NULL;
-
#if HAVE_PCRE2
static const char *arg_pattern = NULL;
static pcre2_code *arg_compiled_pattern = NULL;
LIST_FIELDS(struct BootId, boot_list);
} BootId;
+#if HAVE_PCRE2
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
+
+static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
+ int errorcode, r;
+ PCRE2_SIZE erroroffset;
+ pcre2_code *p;
+
+ p = pcre2_compile((PCRE2_SPTR8) pattern,
+ PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
+ if (!p) {
+ unsigned char buf[LINE_MAX];
+
+ r = pcre2_get_error_message(errorcode, buf, sizeof buf);
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad pattern \"%s\": %s", pattern,
+ r < 0 ? "unknown error" : (char *)buf);
+ }
+
+ *out = p;
+ return 0;
+}
+
+#endif
+
static int add_matches_for_device(sd_journal *j, const char *devpath) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
sd_device *d = NULL;
return 1;
}
+static int help_facilities(void) {
+ if (!arg_quiet)
+ puts("Available facilities:");
+
+ for (int i = 0; i < LOG_NFACILITIES; i++) {
+ _cleanup_free_ char *t = NULL;
+
+ if (log_facility_unshifted_to_string_alloc(i, &t))
+ return log_oom();
+ puts(t);
+ }
+
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
if (r < 0)
return log_oom();
- printf("%s [OPTIONS...] [MATCHES...]\n\n"
- "%sQuery the journal.%s\n\n"
- "Options:\n"
+ printf("%1$s [OPTIONS...] [MATCHES...]\n\n"
+ "%5$sQuery the journal.%6$s\n\n"
+ "%3$sOptions:%4$s\n"
" --system Show the system journal\n"
" --user Show the user journal for the current user\n"
" -M --machine=CONTAINER Operate on local container\n"
" --user-unit=UNIT Show logs from the specified user unit\n"
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
" -p --priority=RANGE Show entries with the specified priority\n"
+ " --facility=FACILITY... Show entries with the specified facilities\n"
" -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
" --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n"
" -e --pager-end Immediately jump to the end in the pager\n"
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
" --root=ROOT Operate on files below a root directory\n"
+ " --namespace=NAMESPACE Show journal data from specified namespace\n"
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
" --force Override of the FSS key pair with --setup-keys\n"
- "\nCommands:\n"
+ "\n%3$sCommands:%4$s\n"
" -h --help Show this help text\n"
" --version Show package version\n"
" -N --fields List all field names currently used\n"
" --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
" --setup-keys Generate a new FSS key pair\n"
- "\nSee the %s for details.\n"
+ "\nSee the %2$s for details.\n"
, program_invocation_short_name
- , ansi_highlight(), ansi_normal()
, link
+ , ansi_underline(), ansi_normal()
+ , ansi_highlight(), ansi_normal()
);
return 0;
ARG_SYSTEM,
ARG_ROOT,
ARG_HEADER,
+ ARG_FACILITY,
ARG_SETUP_KEYS,
ARG_FILE,
ARG_INTERVAL,
ARG_VACUUM_TIME,
ARG_NO_HOSTNAME,
ARG_OUTPUT_FIELDS,
+ ARG_NAMESPACE,
};
static const struct option options[] = {
{ "header", no_argument, NULL, ARG_HEADER },
{ "identifier", required_argument, NULL, 't' },
{ "priority", required_argument, NULL, 'p' },
+ { "facility", required_argument, NULL, ARG_FACILITY },
{ "grep", required_argument, NULL, 'g' },
{ "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE },
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
{ "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
{ "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
+ { "namespace", required_argument, NULL, ARG_NAMESPACE },
{}
};
}
arg_output = output_mode_from_string(optarg);
- if (arg_output < 0) {
- log_error("Unknown output format '%s'.", optarg);
- return -EINVAL;
- }
+ if (arg_output < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown output format '%s'.", optarg);
if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
arg_quiet = true;
arg_lines = ARG_LINES_ALL;
else {
r = safe_atoi(optarg, &arg_lines);
- if (r < 0 || arg_lines < 0) {
- log_error("Failed to parse lines '%s'", optarg);
- return -EINVAL;
- }
+ if (r < 0 || arg_lines < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse lines '%s'", optarg);
}
} else {
arg_lines = 10;
arg_machine = optarg;
break;
+ case ARG_NAMESPACE:
+ if (streq(optarg, "*")) {
+ arg_namespace_flags = SD_JOURNAL_ALL_NAMESPACES;
+ arg_namespace = NULL;
+ } else if (startswith(optarg, "+")) {
+ arg_namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE;
+ arg_namespace = optarg + 1;
+ } else if (isempty(optarg)) {
+ arg_namespace_flags = 0;
+ arg_namespace = NULL;
+ } else {
+ arg_namespace_flags = 0;
+ arg_namespace = optarg;
+ }
+
+ break;
+
case 'D':
arg_directory = optarg;
break;
case ARG_VACUUM_SIZE:
r = parse_size(optarg, 1024, &arg_vacuum_size);
- if (r < 0) {
- log_error("Failed to parse vacuum size: %s", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse vacuum size: %s", optarg);
arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_FILES:
r = safe_atou64(optarg, &arg_vacuum_n_files);
- if (r < 0) {
- log_error("Failed to parse vacuum files: %s", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse vacuum files: %s", optarg);
arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_TIME:
r = parse_sec(optarg, &arg_vacuum_time);
- if (r < 0) {
- log_error("Failed to parse vacuum time: %s", optarg);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse vacuum time: %s", optarg);
arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
break;
case ARG_VERIFY_KEY:
- arg_action = ACTION_VERIFY;
r = free_and_strdup(&arg_verify_key, optarg);
if (r < 0)
return r;
* in ps or htop output. */
memset(optarg, 'x', strlen(optarg));
+ arg_action = ACTION_VERIFY;
arg_merge = false;
break;
case ARG_INTERVAL:
r = parse_sec(optarg, &arg_interval);
- if (r < 0 || arg_interval <= 0) {
- log_error("Failed to parse sealing key change interval: %s", optarg);
- return -EINVAL;
- }
+ if (r < 0 || arg_interval <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse sealing key change interval: %s", optarg);
break;
#else
case ARG_SETUP_KEYS:
case ARG_VERIFY_KEY:
case ARG_INTERVAL:
case ARG_FORCE:
- log_error("Compiled without forward-secure sealing support.");
- return -EOPNOTSUPP;
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Compiled without forward-secure sealing support.");
#endif
case 'p': {
dots = strstr(optarg, "..");
if (dots) {
- char *a;
+ _cleanup_free_ char *a = NULL;
int from, to, i;
/* a range */
from = log_level_from_string(a);
to = log_level_from_string(dots + 2);
- free(a);
- if (from < 0 || to < 0) {
- log_error("Failed to parse log level range %s", optarg);
- return -EINVAL;
- }
+ if (from < 0 || to < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse log level range %s", optarg);
arg_priorities = 0;
int p, i;
p = log_level_from_string(optarg);
- if (p < 0) {
- log_error("Unknown log level %s", optarg);
- return -EINVAL;
- }
+ if (p < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Unknown log level %s", optarg);
arg_priorities = 0;
break;
}
+ case ARG_FACILITY: {
+ const char *p;
+
+ for (p = optarg;;) {
+ _cleanup_free_ char *fac = NULL;
+ int num;
+
+ r = extract_first_word(&p, &fac, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse facilities: %s", optarg);
+ if (r == 0)
+ break;
+
+ if (streq(fac, "help")) {
+ help_facilities();
+ return 0;
+ }
+
+ num = log_facility_unshifted_from_string(fac);
+ if (num < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad --facility= argument \"%s\".", fac);
+
+ r = set_ensure_allocated(&arg_facilities, NULL);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(arg_facilities, INT_TO_PTR(num));
+ if (r < 0)
+ return log_oom();
+ }
+
+ break;
+ }
+
#if HAVE_PCRE2
case 'g':
arg_pattern = optarg;
case 'S':
r = parse_timestamp(optarg, &arg_since);
- if (r < 0) {
- log_error("Failed to parse timestamp: %s", optarg);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse timestamp: %s", optarg);
arg_since_set = true;
break;
case 'U':
r = parse_timestamp(optarg, &arg_until);
- if (r < 0) {
- log_error("Failed to parse timestamp: %s", optarg);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse timestamp: %s", optarg);
arg_until_set = true;
break;
* so take the slow path if log location is specified. */
if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) &&
!arg_directory && !arg_file && !arg_root)
-
return add_match_this_boot(j, arg_machine);
boot_id = arg_boot_id;
return 0;
}
+static int add_facilities(sd_journal *j) {
+ void *p;
+ Iterator it;
+ int r;
+
+ SET_FOREACH(p, arg_facilities, it) {
+ char match[STRLEN("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
+
+ xsprintf(match, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
+
+ r = sd_journal_add_match(j, match, strlen(match));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match: %m");
+ }
+
+ return 0;
+}
+
static int add_syslog_identifier(sd_journal *j) {
int r;
char **i;
static int simple_varlink_call(const char *option, const char *method) {
_cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
- const char *error;
+ const char *error, *fn;
int r;
if (arg_machine)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "%s is not supported in conjunction with --machine=.", option);
- r = varlink_connect_address(&link, "/run/systemd/journal/io.systemd.journal");
+ fn = arg_namespace ?
+ strjoina("/run/systemd/journal.", arg_namespace, "/io.systemd.journal") :
+ "/run/systemd/journal/io.systemd.journal";
+
+ r = varlink_connect_address(&link, fn);
if (r < 0)
- return log_error_errno(r, "Failed to connect to /run/systemd/journal/io.systemd.journal: %m");
+ return log_error_errno(r, "Failed to connect to %s: %m", fn);
(void) varlink_set_description(link, "journal");
(void) varlink_set_relative_timeout(link, USEC_INFINITY);
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
else if (arg_root)
r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT);
- else if (arg_file_stdin) {
- int ifd = STDIN_FILENO;
- r = sd_journal_open_files_fd(&j, &ifd, 1, 0);
- } else if (arg_file)
+ else if (arg_file_stdin)
+ r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, 0);
+ else if (arg_file)
r = sd_journal_open_files(&j, (const char**) arg_file, 0);
else if (arg_machine) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (geteuid() != 0) {
/* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of
* the container, thus we need root privileges to override them. */
- log_error("Using the --machine= switch requires root privileges.");
- r = -EPERM;
+ r = log_error_errno(SYNTHETIC_ERRNO(EPERM), "Using the --machine= switch requires root privileges.");
goto finish;
}
if (r < 0)
safe_close(fd);
} else
- r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
+ r = sd_journal_open_namespace(
+ &j,
+ arg_namespace,
+ (arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) |
+ arg_namespace_flags | arg_journal_type);
if (r < 0) {
log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
goto finish;
if (r < 0)
goto finish;
+ r = add_facilities(j);
+ if (r < 0)
+ goto finish;
+
r = add_matches(j, argv + optind);
if (r < 0)
goto finish;
strv_free(arg_file);
+ set_free(arg_facilities);
strv_free(arg_syslog_identifier);
strv_free(arg_system_units);
strv_free(arg_user_units);
if (r < 0)
return r;
- c = new0(ClientContext, 1);
+ c = new(ClientContext, 1);
if (!c)
return -ENOMEM;
- c->pid = pid;
-
- c->uid = UID_INVALID;
- c->gid = GID_INVALID;
- c->auditid = AUDIT_SESSION_INVALID;
- c->loginuid = UID_INVALID;
- c->owner_uid = UID_INVALID;
- c->lru_index = PRIOQ_IDX_NULL;
- c->timestamp = USEC_INFINITY;
- c->extra_fields_mtime = NSEC_INFINITY;
- c->log_level_max = -1;
- c->log_ratelimit_interval = s->ratelimit_interval;
- c->log_ratelimit_burst = s->ratelimit_burst;
+ *c = (ClientContext) {
+ .pid = pid,
+ .uid = UID_INVALID,
+ .gid = GID_INVALID,
+ .auditid = AUDIT_SESSION_INVALID,
+ .loginuid = UID_INVALID,
+ .owner_uid = UID_INVALID,
+ .lru_index = PRIOQ_IDX_NULL,
+ .timestamp = USEC_INFINITY,
+ .extra_fields_mtime = NSEC_INFINITY,
+ .log_level_max = -1,
+ .log_ratelimit_interval = s->ratelimit_interval,
+ .log_ratelimit_burst = s->ratelimit_burst,
+ };
r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
if (r < 0) {
log_warning_errno(r, "Failed to acquire our own context, ignoring: %m");
}
- if (!s->pid1_context) {
+ if (!s->namespace && !s->pid1_context) {
+ /* Acquire PID1's context, but only if we are in non-namespaced mode, since PID 1 is only
+ * going to log to the non-namespaced journal instance. */
r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context);
if (r < 0)
#include "string-util.h"
void server_forward_kmsg(
- Server *s,
- int priority,
- const char *identifier,
- const char *message,
- const struct ucred *ucred) {
+ Server *s,
+ int priority,
+ const char *identifier,
+ const char *message,
+ const struct ucred *ucred) {
_cleanup_free_ char *ident_buf = NULL;
struct iovec iovec[5];
}
int server_open_kernel_seqnum(Server *s) {
- _cleanup_close_ int fd;
+ _cleanup_close_ int fd = -1;
+ const char *fn;
uint64_t *p;
int r;
assert(s);
- /* We store the seqnum we last read in an mmaped file. That
- * way we can just use it like a variable, but it is
- * persistent and automatically flushed at reboot. */
+ /* We store the seqnum we last read in an mmaped file. That way we can just use it like a variable,
+ * but it is persistent and automatically flushed at reboot. */
- fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
+ if (!s->read_kmsg)
+ return 0;
+
+ fn = strjoina(s->runtime_directory, "/kernel-seqnum");
+ fd = open(fn, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
if (fd < 0) {
- log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m");
+ log_error_errno(errno, "Failed to open %s, ignoring: %m", fn);
return 0;
}
}
}
-int server_open_native_socket(Server *s) {
-
- static const union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/journal/socket",
- };
+int server_open_native_socket(Server *s, const char *native_socket) {
int r;
assert(s);
+ assert(native_socket);
if (s->native_fd < 0) {
+ union sockaddr_union sa;
+ size_t sa_len;
+
+ r = sockaddr_un_set_path(&sa.un, native_socket);
+ if (r < 0)
+ return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", native_socket);
+ sa_len = r;
+
s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->native_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
(void) sockaddr_un_unlink(&sa.un);
- r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ r = bind(s->native_fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
const char *label,
size_t label_len);
-int server_open_native_socket(Server *s);
+int server_open_native_socket(Server *s, const char *native_socket);
free(r);
}
-_pure_ static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
+static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
unsigned i;
assert(g);
#include "missing_audit.h"
#include "mkdir.h"
#include "parse-util.h"
+#include "path-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "rm-rf.h"
#define DEFERRED_CLOSES_MAX (4096)
-static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used, uint64_t *ret_free) {
+#define IDLE_TIMEOUT_USEC (30*USEC_PER_SEC)
+
+static int determine_path_usage(
+ Server *s,
+ const char *path,
+ uint64_t *ret_used,
+ uint64_t *ret_free) {
+
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
struct statvfs ss;
+ assert(s);
+ assert(path);
assert(ret_used);
assert(ret_free);
storage->metrics.min_use = MAX(storage->metrics.min_use, storage->space.vfs_used);
}
+static JournalStorage* server_current_storage(Server *s) {
+ assert(s);
+
+ return s->system_journal ? &s->system_storage : &s->runtime_storage;
+}
+
static int determine_space(Server *s, uint64_t *available, uint64_t *limit) {
JournalStorage *js;
int r;
assert(s);
- js = s->system_journal ? &s->system_storage : &s->runtime_storage;
+ js = server_current_storage(s);
r = cache_space_refresh(s, js);
if (r >= 0) {
assert(s);
if (!storage)
- storage = s->system_journal ? &s->system_storage : &s->runtime_storage;
+ storage = server_current_storage(s);
if (cache_space_refresh(s, storage) < 0)
return;
return r;
}
-static bool flushed_flag_is_set(void) {
- return access("/run/systemd/journal/flushed", F_OK) >= 0;
+static bool flushed_flag_is_set(Server *s) {
+ const char *fn;
+
+ assert(s);
+
+ /* We don't support the "flushing" concept for namespace instances, we assume them to always have
+ * access to /var */
+ if (s->namespace)
+ return true;
+
+ fn = strjoina(s->runtime_directory, "/flushed");
+ return access(fn, F_OK) >= 0;
}
static int system_journal_open(Server *s, bool flush_requested, bool relinquish_requested) {
if (!s->system_journal &&
IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
- (flush_requested || flushed_flag_is_set()) &&
+ (flush_requested || flushed_flag_is_set(s)) &&
!relinquish_requested) {
- /* If in auto mode: first try to create the machine
- * path, but not the prefix.
+ /* If in auto mode: first try to create the machine path, but not the prefix.
*
- * If in persistent mode: create /var/log/journal and
- * the machine path */
+ * If in persistent mode: create /var/log/journal and the machine path */
if (s->storage == STORAGE_PERSISTENT)
- (void) mkdir_p("/var/log/journal/", 0755);
+ (void) mkdir_parents(s->system_storage.path, 0755);
(void) mkdir(s->system_storage.path, 0755);
r = 0;
}
- /* If the runtime journal is open, and we're post-flush, we're
- * recovering from a failed system journal rotate (ENOSPC)
- * for which the runtime journal was reopened.
+ /* If the runtime journal is open, and we're post-flush, we're recovering from a failed
+ * system journal rotate (ENOSPC) for which the runtime journal was reopened.
*
- * Perform an implicit flush to var, leaving the runtime
- * journal closed, now that the system journal is back.
+ * Perform an implicit flush to var, leaving the runtime journal closed, now that the system
+ * journal is back.
*/
if (!flush_requested)
(void) server_flush_to_var(s, true);
} else {
- /* OK, we really need the runtime journal, so create
- * it if necessary. */
+ /* OK, we really need the runtime journal, so create it if necessary. */
- (void) mkdir("/run/log", 0755);
- (void) mkdir("/run/log/journal", 0755);
- (void) mkdir_parents(fn, 0750);
+ (void) mkdir_parents(s->runtime_storage.path, 0755);
+ (void) mkdir(s->runtime_storage.path, 0750);
r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_storage.metrics, &s->runtime_journal);
if (r < 0)
static JournalFile* find_journal(Server *s, uid_t uid) {
_cleanup_free_ char *p = NULL;
- int r;
JournalFile *f;
- sd_id128_t machine;
+ int r;
assert(s);
- /* A rotate that fails to create the new journal (ENOSPC) leaves the
- * rotated journal as NULL. Unless we revisit opening, even after
- * space is made available we'll continue to return NULL indefinitely.
+ /* A rotate that fails to create the new journal (ENOSPC) leaves the rotated journal as NULL. Unless
+ * we revisit opening, even after space is made available we'll continue to return NULL indefinitely.
*
- * system_journal_open() is a noop if the journals are already open, so
- * we can just call it here to recover from failed rotates (or anything
- * else that's left the journals as NULL).
+ * system_journal_open() is a noop if the journals are already open, so we can just call it here to
+ * recover from failed rotates (or anything else that's left the journals as NULL).
*
* Fixes https://github.com/systemd/systemd/issues/3968 */
(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
- * to guarantee proper order as soon as we flush /run to
- * /var and close the runtime file. */
+ /* We split up user logs only on /var, not on /run. If the runtime file is open, we write to it
+ * exclusively, in order to guarantee proper order as soon as we flush /run to /var and close the
+ * runtime file. */
if (s->runtime_journal)
return s->runtime_journal;
if (f)
return f;
- r = sd_id128_get_machine(&machine);
- if (r < 0) {
- log_debug_errno(r, "Failed to determine machine ID, using system log: %m");
- return s->system_journal;
- }
-
- if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal",
- SD_ID128_FORMAT_VAL(machine), uid) < 0) {
+ if (asprintf(&p, "%s/user-" UID_FMT ".journal", s->system_storage.path, uid) < 0) {
log_oom();
return s->system_journal;
}
+ /* Too many open? Then let's close one (or more) */
while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
- /* Too many open? Then let's close one */
- f = ordered_hashmap_steal_first(s->user_journals);
- assert(f);
+ assert_se(f = ordered_hashmap_steal_first(s->user_journals));
(void) journal_file_close(f);
}
if (r < 0)
return s->system_journal;
- server_add_acls(f, uid);
-
r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f);
if (r < 0) {
(void) journal_file_close(f);
return s->system_journal;
}
+ server_add_acls(f, uid);
return f;
}
}
server_add_acls(*f, uid);
-
return r;
}
}
}
-static int open_user_journal_directory(Server *s, DIR **ret_dir, char **ret_path) {
- _cleanup_closedir_ DIR *dir = NULL;
- _cleanup_free_ char *path = NULL;
- sd_id128_t machine;
+static int vacuum_offline_user_journals(Server *s) {
+ _cleanup_closedir_ DIR *d = NULL;
int r;
assert(s);
- r = sd_id128_get_machine(&machine);
- if (r < 0)
- return log_error_errno(r, "Failed to determine machine ID, ignoring: %m");
+ d = opendir(s->system_storage.path);
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
- if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/", SD_ID128_FORMAT_VAL(machine)) < 0)
- return log_oom();
+ return log_error_errno(errno, "Failed to open %s: %m", s->system_storage.path);
+ }
+
+ for (;;) {
+ _cleanup_free_ char *u = NULL, *full = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *a, *b;
+ struct dirent *de;
+ JournalFile *f;
+ uid_t uid;
+
+ errno = 0;
+ de = readdir_no_dot(d);
+ if (!de) {
+ if (errno != 0)
+ log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", s->system_storage.path);
+
+ break;
+ }
+
+ a = startswith(de->d_name, "user-");
+ if (!a)
+ continue;
+ b = endswith(de->d_name, ".journal");
+ if (!b)
+ continue;
+
+ u = strndup(a, b-a);
+ if (!u)
+ return log_oom();
+
+ r = parse_uid(u, &uid);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name);
+ continue;
+ }
+
+ /* Already rotated in the above loop? i.e. is it an open user journal? */
+ if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid)))
+ continue;
+
+ full = path_join(s->system_storage.path, de->d_name);
+ if (!full)
+ return log_oom();
+
+ fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
+ if (fd < 0) {
+ log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to open journal file '%s' for rotation: %m", full);
+ continue;
+ }
+
+ /* Make some room in the set of deferred close()s */
+ server_vacuum_deferred_closes(s);
+
+ /* Open the file briefly, so that we can archive it */
+ r = journal_file_open(fd,
+ full,
+ O_RDWR,
+ 0640,
+ s->compress.enabled,
+ s->compress.threshold_bytes,
+ s->seal,
+ &s->system_storage.metrics,
+ s->mmap,
+ s->deferred_closes,
+ NULL,
+ &f);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full);
+
+ r = journal_file_dispose(dirfd(d), de->d_name);
+ if (r < 0)
+ log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full);
+ else
+ log_debug("Successfully moved %s out of the way.", full);
+
+ continue;
+ }
+
+ TAKE_FD(fd); /* Donated to journal_file_open() */
- dir = opendir(path);
- if (!dir)
- return log_error_errno(errno, "Failed to open user journal directory '%s': %m", path);
+ r = journal_file_archive(f);
+ if (r < 0)
+ log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full);
- if (ret_dir)
- *ret_dir = TAKE_PTR(dir);
- if (ret_path)
- *ret_path = TAKE_PTR(path);
+ f = journal_initiate_close(f, s->deferred_closes);
+ }
return 0;
}
void server_rotate(Server *s) {
- _cleanup_free_ char *path = NULL;
- _cleanup_closedir_ DIR *d = NULL;
JournalFile *f;
Iterator i;
void *k;
ordered_hashmap_remove(s->user_journals, k);
}
- /* Finally, also rotate all user journals we currently do not have open. (But do so only if we actually have
- * access to /var, i.e. are not in the log-to-runtime-journal mode). */
- if (!s->runtime_journal &&
- open_user_journal_directory(s, &d, &path) >= 0) {
-
- struct dirent *de;
-
- FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", path)) {
- _cleanup_free_ char *u = NULL, *full = NULL;
- _cleanup_close_ int fd = -1;
- const char *a, *b;
- uid_t uid;
-
- a = startswith(de->d_name, "user-");
- if (!a)
- continue;
- b = endswith(de->d_name, ".journal");
- if (!b)
- continue;
-
- u = strndup(a, b-a);
- if (!u) {
- log_oom();
- break;
- }
-
- r = parse_uid(u, &uid);
- if (r < 0) {
- log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name);
- continue;
- }
-
- /* Already rotated in the above loop? i.e. is it an open user journal? */
- if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid)))
- continue;
-
- full = strjoin(path, de->d_name);
- if (!full) {
- log_oom();
- break;
- }
-
- fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
- if (fd < 0) {
- log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
- "Failed to open journal file '%s' for rotation: %m", full);
- continue;
- }
-
- /* Make some room in the set of deferred close()s */
- server_vacuum_deferred_closes(s);
-
- /* Open the file briefly, so that we can archive it */
- r = journal_file_open(fd,
- full,
- O_RDWR,
- 0640,
- s->compress.enabled,
- s->compress.threshold_bytes,
- s->seal,
- &s->system_storage.metrics,
- s->mmap,
- s->deferred_closes,
- NULL,
- &f);
- if (r < 0) {
- log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full);
-
- r = journal_file_dispose(dirfd(d), de->d_name);
- if (r < 0)
- log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full);
- else
- log_debug("Successfully moved %s out of the way.", full);
-
- continue;
- }
-
- TAKE_FD(fd); /* Donated to journal_file_open() */
-
- r = journal_file_archive(f);
- if (r < 0)
- log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full);
-
- f = journal_initiate_close(f, s->deferred_closes);
- }
- }
+ /* Finally, also rotate all user journals we currently do not have open. (But do so only if we
+ * actually have access to /var, i.e. are not in the log-to-runtime-journal mode). */
+ if (!s->runtime_journal)
+ (void) vacuum_offline_user_journals(s);
server_process_deferred_closes(s);
}
if (!x)
return;
- free(s->hostname_field);
- s->hostname_field = x;
+ free_and_replace(s->hostname_field, x);
}
static bool shall_try_append_again(JournalFile *f, int r) {
if (!isempty(s->hostname_field))
iovec[n++] = IOVEC_MAKE_STRING(s->hostname_field);
+ if (!isempty(s->namespace_field))
+ iovec[n++] = IOVEC_MAKE_STRING(s->namespace_field);
+
assert(n <= m);
if (s->split_mode == SPLIT_UID && c && uid_is_valid(c->uid))
}
int server_flush_to_var(Server *s, bool require_flag_file) {
- sd_journal *j = NULL;
char ts[FORMAT_TIMESPAN_MAX];
- usec_t start;
+ sd_journal *j = NULL;
+ const char *fn;
unsigned n = 0;
+ usec_t start;
int r, k;
assert(s);
if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
return 0;
- if (!s->runtime_journal)
+ if (s->namespace) /* Flushing concept does not exist for namespace instances */
return 0;
- if (require_flag_file && !flushed_flag_is_set())
+ if (!s->runtime_journal) /* Nothing to flush? */
+ return 0;
+
+ if (require_flag_file && !flushed_flag_is_set(s))
return 0;
(void) system_journal_open(s, true, false);
if (!s->system_journal)
return 0;
- log_debug("Flushing to /var...");
+ log_debug("Flushing to %s...", s->system_storage.path);
start = now(CLOCK_MONOTONIC);
s->runtime_journal = journal_file_close(s->runtime_journal);
if (r >= 0)
- (void) rm_rf("/run/log/journal", REMOVE_ROOT);
+ (void) rm_rf(s->runtime_storage.path, REMOVE_ROOT);
sd_journal_close(j);
server_driver_message(s, 0, NULL,
- LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.",
+ LOG_MESSAGE("Time spent on flushing to %s is %s for %u entries.",
+ s->system_storage.path,
format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0),
n),
NULL);
- k = touch("/run/systemd/journal/flushed");
+ fn = strjoina(s->runtime_directory, "/flushed");
+ k = touch(fn);
if (k < 0)
- log_warning_errno(k, "Failed to touch /run/systemd/journal/flushed, ignoring: %m");
+ log_warning_errno(k, "Failed to touch %s, ignoring: %m", fn);
+ server_refresh_idle_timer(s);
return r;
}
static int server_relinquish_var(Server *s) {
+ const char *fn;
assert(s);
if (s->storage == STORAGE_NONE)
return 0;
+ if (s->namespace) /* Concept does not exist for namespaced instances */
+ return -EOPNOTSUPP;
+
if (s->runtime_journal && !s->system_journal)
return 0;
- log_debug("Relinquishing /var...");
+ log_debug("Relinquishing %s...", s->system_storage.path);
(void) system_journal_open(s, false, true);
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");
+ fn = strjoina(s->runtime_directory, "/flushed");
+ if (unlink(fn) < 0 && errno != ENOENT)
+ log_warning_errno(errno, "Failed to unlink %s, ignoring: %m", fn);
+ server_refresh_idle_timer(s);
return 0;
}
-int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+int server_process_datagram(
+ sd_event_source *es,
+ int fd,
+ uint32_t revents,
+ void *userdata) {
+
Server *s = userdata;
struct ucred *ucred = NULL;
struct timeval *tv = NULL;
}
close_many(fds, n_fds);
+
+ server_refresh_idle_timer(s);
return 0;
}
server_vacuum(s, false);
server_space_usage_message(s, NULL);
+
+ server_refresh_idle_timer(s);
}
static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
assert(s);
+ if (s->namespace) {
+ log_error("Received SIGUSR1 signal from PID " PID_FMT ", but flushing runtime journals not supported for namespaced instances.", si->ssi_pid);
+ return 0;
+ }
+
log_info("Received SIGUSR1 signal from PID " PID_FMT ", as request to flush runtime journal.", si->ssi_pid);
server_full_flush(s);
}
static void server_full_rotate(Server *s) {
+ const char *fn;
int r;
assert(s);
patch_min_use(&s->runtime_storage);
/* Let clients know when the most recent rotation happened. */
- r = write_timestamp_file_atomic("/run/systemd/journal/rotated", now(CLOCK_MONOTONIC));
+ fn = strjoina(s->runtime_directory, "/rotated");
+ r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
if (r < 0)
- log_warning_errno(r, "Failed to write /run/systemd/journal/rotated, ignoring: %m");
+ log_warning_errno(r, "Failed to write %s, ignoring: %m", fn);
}
static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
}
static void server_full_sync(Server *s) {
+ const char *fn;
int r;
assert(s);
server_sync(s);
/* Let clients know when the most recent sync happened. */
- r = write_timestamp_file_atomic("/run/systemd/journal/synced", now(CLOCK_MONOTONIC));
+ fn = strjoina(s->runtime_directory, "/synced");
+ r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
if (r < 0)
- log_warning_errno(r, "Failed to write /run/systemd/journal/synced, ignoring: %m");
+ log_warning_errno(r, "Failed to write %s, ignoring: %m", fn);
return;
}
}
static int server_parse_config_file(Server *s) {
+ int r;
+
assert(s);
+ if (s->namespace) {
+ const char *namespaced;
+
+ /* If we are running in namespace mode, load the namespace specific configuration file, and nothing else */
+ namespaced = strjoina(PKGSYSCONFDIR "/journald@", s->namespace, ".conf");
+
+ r = config_parse(
+ NULL,
+ namespaced, NULL,
+ "Journal\0",
+ config_item_perf_lookup, journald_gperf_lookup,
+ CONFIG_PARSE_WARN, s);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf",
CONF_PATHS_NULSTR("systemd/journald.conf.d"),
"Journal\0",
}
static int server_connect_notify(Server *s) {
- union sockaddr_union sa = {};
+ union sockaddr_union sa;
+ socklen_t sa_len;
const char *e;
- int r, salen;
+ int r;
assert(s);
assert(s->notify_fd < 0);
if (!e)
return 0;
- salen = sockaddr_un_set_path(&sa.un, e);
- if (salen < 0)
- return log_error_errno(salen, "NOTIFY_SOCKET set to invalid value '%s': %m", e);
+ r = sockaddr_un_set_path(&sa.un, e);
+ if (r < 0)
+ return log_error_errno(r, "NOTIFY_SOCKET set to invalid value '%s': %m", e);
+ sa_len = r;
s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->notify_fd < 0)
(void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE);
- r = connect(s->notify_fd, &sa.sa, salen);
+ r = connect(s->notify_fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "Failed to connect to notify socket: %m");
if (json_variant_elements(parameters) > 0)
return varlink_error_invalid_parameter(link, parameters);
+ if (s->namespace)
+ return varlink_error(link, "io.systemd.Journal.NotSupportedByNamespaces", NULL);
log_info("Received client request to flush runtime journal.");
server_full_flush(s);
if (json_variant_elements(parameters) > 0)
return varlink_error_invalid_parameter(link, parameters);
+ if (s->namespace)
+ return varlink_error(link, "io.systemd.Journal.NotSupportedByNamespaces", NULL);
- log_info("Received client request to relinquish /var access.");
+ log_info("Received client request to relinquish %s access.", s->system_storage.path);
server_relinquish_var(s);
return varlink_reply(link, NULL);
}
-static int server_open_varlink(Server *s) {
+static int vl_connect(VarlinkServer *server, Varlink *link, void *userdata) {
+ Server *s = userdata;
+
+ assert(server);
+ assert(link);
+ assert(s);
+
+ (void) server_start_or_stop_idle_timer(s); /* maybe we are no longer idle */
+
+ return 0;
+}
+
+static void vl_disconnect(VarlinkServer *server, Varlink *link, void *userdata) {
+ Server *s = userdata;
+
+ assert(server);
+ assert(link);
+ assert(s);
+
+ (void) server_start_or_stop_idle_timer(s); /* maybe we are idle now */
+}
+
+static int server_open_varlink(Server *s, const char *socket, int fd) {
int r;
assert(s);
if (r < 0)
return r;
- r = varlink_server_listen_address(s->varlink_server, "/run/systemd/journal/io.systemd.journal", 0600);
+ r = varlink_server_bind_connect(s->varlink_server, vl_connect);
+ if (r < 0)
+ return r;
+
+ r = varlink_server_bind_disconnect(s->varlink_server, vl_disconnect);
+ if (r < 0)
+ return r;
+
+ if (fd < 0)
+ r = varlink_server_listen_address(s->varlink_server, socket, 0600);
+ else
+ r = varlink_server_listen_fd(s->varlink_server, fd);
if (r < 0)
return r;
return 0;
}
-int server_init(Server *s) {
+static bool server_is_idle(Server *s) {
+ assert(s);
+
+ /* The server for the main namespace is never idle */
+ if (!s->namespace)
+ return false;
+
+ /* If a retention maximum is set larger than the idle time we need to be running to enforce it, hence
+ * turn off the idle logic. */
+ if (s->max_retention_usec > IDLE_TIMEOUT_USEC)
+ return false;
+
+ /* We aren't idle if we have a varlink client */
+ if (varlink_server_current_connections(s->varlink_server) > 0)
+ return false;
+
+ /* If we have stdout streams we aren't idle */
+ if (s->n_stdout_streams > 0)
+ return false;
+
+ return true;
+}
+
+static int server_idle_handler(sd_event_source *source, uint64_t usec, void *userdata) {
+ Server *s = userdata;
+
+ assert(source);
+ assert(s);
+
+ log_debug("Server is idle, exiting.");
+ sd_event_exit(s->event, 0);
+ return 0;
+}
+
+int server_start_or_stop_idle_timer(Server *s) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL;
+ usec_t when;
+ int r;
+
+ assert(s);
+
+ if (!server_is_idle(s)) {
+ s->idle_event_source = sd_event_source_disable_unref(s->idle_event_source);
+ return 0;
+ }
+
+ if (s->idle_event_source)
+ return 1;
+
+ r = sd_event_now(s->event, CLOCK_MONOTONIC, &when);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine current time: %m");
+
+ r = sd_event_add_time(s->event, &source, CLOCK_MONOTONIC, usec_add(when, IDLE_TIMEOUT_USEC), 0, server_idle_handler, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate idle timer: %m");
+
+ r = sd_event_source_set_priority(source, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set idle timer priority: %m");
+
+ (void) sd_event_source_set_description(source, "idle-timer");
+
+ s->idle_event_source = TAKE_PTR(source);
+ return 1;
+}
+
+int server_refresh_idle_timer(Server *s) {
+ usec_t when;
+ int r;
+
+ assert(s);
+
+ if (!s->idle_event_source)
+ return 0;
+
+ r = sd_event_now(s->event, CLOCK_MONOTONIC, &when);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine current time: %m");
+
+ r = sd_event_source_set_time(s->idle_event_source, usec_add(when, IDLE_TIMEOUT_USEC));
+ if (r < 0)
+ return log_error_errno(r, "Failed to refresh idle timer: %m");
+
+ return 1;
+}
+
+static int set_namespace(Server *s, const char *namespace) {
+ assert(s);
+
+ if (!namespace)
+ return 0;
+
+ if (!log_namespace_name_valid(namespace))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified namespace name not valid, refusing: %s", namespace);
+
+ s->namespace = strdup(namespace);
+ if (!s->namespace)
+ return log_oom();
+
+ s->namespace_field = strjoin("_NAMESPACE=", namespace);
+ if (!s->namespace_field)
+ return log_oom();
+
+ return 1;
+}
+
+int server_init(Server *s, const char *namespace) {
+ const char *native_socket, *syslog_socket, *stdout_socket, *varlink_socket, *e;
_cleanup_fdset_free_ FDSet *fds = NULL;
- int n, r, fd;
+ int n, r, fd, varlink_fd = -1;
bool no_sockets;
assert(s);
.compress.enabled = true,
.compress.threshold_bytes = (uint64_t) -1,
.seal = true,
- .read_kmsg = true,
.watchdog_usec = USEC_INFINITY,
.system_storage.name = "System Journal",
};
+ r = set_namespace(s, namespace);
+ if (r < 0)
+ return r;
+
+ /* By default, only read from /dev/kmsg if are the main namespace */
+ s->read_kmsg = !s->namespace;
+ s->storage = s->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO;
+
journal_reset_metrics(&s->system_storage.metrics);
journal_reset_metrics(&s->runtime_storage.metrics);
server_parse_config_file(s);
- r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX);
- if (r < 0)
- log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+ if (!s->namespace) {
+ /* Parse kernel command line, but only if we are not a namespace instance */
+ r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+ }
- if (!!s->ratelimit_interval ^ !!s->ratelimit_burst) {
+ if (!!s->ratelimit_interval != !!s->ratelimit_burst) { /* One set to 0 and the other not? */
log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",
s->ratelimit_interval, s->ratelimit_burst);
s->ratelimit_interval = s->ratelimit_burst = 0;
}
- (void) mkdir_p("/run/systemd/journal", 0755);
+ e = getenv("RUNTIME_DIRECTORY");
+ if (e)
+ s->runtime_directory = strdup(e);
+ else if (s->namespace)
+ s->runtime_directory = strjoin("/run/systemd/journal.", s->namespace);
+ else
+ s->runtime_directory = strdup("/run/systemd/journal");
+ if (!s->runtime_directory)
+ return log_oom();
+
+ (void) mkdir_p(s->runtime_directory, 0755);
s->user_journals = ordered_hashmap_new(NULL);
if (!s->user_journals)
if (n < 0)
return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
+ native_socket = strjoina(s->runtime_directory, "/socket");
+ stdout_socket = strjoina(s->runtime_directory, "/stdout");
+ syslog_socket = strjoina(s->runtime_directory, "/dev-log");
+ varlink_socket = strjoina(s->runtime_directory, "/io.systemd.journal");
+
for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
- if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) {
+ if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, native_socket, 0) > 0) {
if (s->native_fd >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
s->native_fd = fd;
- } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) {
+ } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, stdout_socket, 0) > 0) {
if (s->stdout_fd >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
s->stdout_fd = fd;
- } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0 ||
- sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/dev-log", 0) > 0) {
+ } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, syslog_socket, 0) > 0) {
if (s->syslog_fd >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
s->syslog_fd = fd;
+ } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, varlink_socket, 0) > 0) {
+
+ if (varlink_fd >= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Too many varlink sockets passed.");
+
+ varlink_fd = fd;
} else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
if (s->audit_fd >= 0)
fds = fdset_free(fds);
}
- no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0;
+ no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0 && varlink_fd < 0;
/* always open stdout, syslog, native, and kmsg sockets */
/* systemd-journald.socket: /run/systemd/journal/stdout */
- r = server_open_stdout_socket(s);
+ r = server_open_stdout_socket(s, stdout_socket);
if (r < 0)
return r;
/* systemd-journald-dev-log.socket: /run/systemd/journal/dev-log */
- r = server_open_syslog_socket(s);
+ r = server_open_syslog_socket(s, syslog_socket);
if (r < 0)
return r;
/* systemd-journald.socket: /run/systemd/journal/socket */
- r = server_open_native_socket(s);
+ r = server_open_native_socket(s, native_socket);
if (r < 0)
return r;
return r;
}
- r = server_open_varlink(s);
+ r = server_open_varlink(s, varlink_socket, varlink_fd);
if (r < 0)
return r;
s->ratelimit = journal_ratelimit_new();
if (!s->ratelimit)
- return -ENOMEM;
+ return log_oom();
r = cg_get_root_path(&s->cgroup_root);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to acquire cgroup root path: %m");
server_cache_hostname(s);
server_cache_boot_id(s);
server_cache_machine_id(s);
- s->runtime_storage.path = path_join("/run/log/journal", SERVER_MACHINE_ID(s));
- s->system_storage.path = path_join("/var/log/journal", SERVER_MACHINE_ID(s));
- if (!s->runtime_storage.path || !s->system_storage.path)
- return -ENOMEM;
+ if (s->namespace)
+ s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s), ".", s->namespace);
+ else
+ s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s));
+ if (!s->runtime_storage.path)
+ return log_oom();
+
+ e = getenv("LOGS_DIRECTORY");
+ if (e)
+ s->system_storage.path = strdup(e);
+ else if (s->namespace)
+ s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s), ".", s->namespace);
+ else
+ s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s));
+ if (!s->system_storage.path)
+ return log_oom();
(void) server_connect_notify(s);
(void) client_context_acquire_default(s);
- return system_journal_open(s, false, false);
+ r = system_journal_open(s, false, false);
+ if (r < 0)
+ return r;
+
+ server_start_or_stop_idle_timer(s);
+ return 0;
}
void server_maybe_append_tags(Server *s) {
void server_done(Server *s) {
assert(s);
+ free(s->namespace);
+ free(s->namespace_field);
+
set_free_with_destructor(s->deferred_closes, journal_file_close);
while (s->stdout_streams)
sd_event_source_unref(s->hostname_event_source);
sd_event_source_unref(s->notify_event_source);
sd_event_source_unref(s->watchdog_event_source);
+ sd_event_source_unref(s->idle_event_source);
sd_event_unref(s->event);
safe_close(s->syslog_fd);
free(s->hostname_field);
free(s->runtime_storage.path);
free(s->system_storage.path);
+ free(s->runtime_directory);
mmap_cache_unref(s->mmap);
}
} JournalStorage;
struct Server {
+ char *namespace;
+
int syslog_fd;
int native_fd;
int stdout_fd;
sd_event_source *hostname_event_source;
sd_event_source *notify_event_source;
sd_event_source *watchdog_event_source;
+ sd_event_source *idle_event_source;
JournalFile *runtime_journal;
JournalFile *system_journal;
char machine_id_field[sizeof("_MACHINE_ID=") + 32];
char boot_id_field[sizeof("_BOOT_ID=") + 32];
char *hostname_field;
+ char *namespace_field;
+ char *runtime_directory;
/* Cached cgroup root, so that we don't have to query that all the time */
char *cgroup_root;
#define SERVER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID="))
/* Extra fields for any log messages */
-#define N_IOVEC_META_FIELDS 22
+#define N_IOVEC_META_FIELDS 23
/* Extra fields for log messages that contain OBJECT_PID= (i.e. log about another process) */
#define N_IOVEC_OBJECT_FIELDS 18
const char *split_mode_to_string(SplitMode s) _const_;
SplitMode split_mode_from_string(const char *s) _pure_;
-int server_init(Server *s);
+int server_init(Server *s, const char *namespace);
void server_done(Server *s);
void server_sync(Server *s);
int server_vacuum(Server *s, bool verbose);
void server_maybe_append_tags(Server *s);
int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata);
void server_space_usage_message(Server *s, JournalStorage *storage);
+
+int server_start_or_stop_idle_timer(Server *s);
+int server_refresh_idle_timer(Server *s);
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "io-util.h"
#include "journald-console.h"
#include "journald-context.h"
if (s->in_notify_queue)
LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
+
+ (void) server_start_or_stop_idle_timer(s->server); /* Maybe we are idle now? */
}
if (s->event_source) {
}
static int stdout_stream_save(StdoutStream *s) {
- _cleanup_free_ char *temp_path = NULL;
+ _cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
return log_warning_errno(errno, "Failed to stat connected stream: %m");
/* We use device and inode numbers as identifier for the stream */
- if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0)
+ r = asprintf(&s->state_file, "%s/streams/%lu:%lu", s->server->runtime_directory, (unsigned long) st.st_dev, (unsigned long) st.st_ino);
+ if (r < 0)
return log_oom();
}
- (void) mkdir_p("/run/systemd/journal/streams", 0755);
+ (void) mkdir_parents(s->state_file, 0755);
r = fopen_temporary(s->state_file, &f, &temp_path);
if (r < 0)
goto fail;
}
+ temp_path = mfree(temp_path);
+
if (!s->fdstore && !s->in_notify_queue) {
LIST_PREPEND(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
s->in_notify_queue = true;
fail:
(void) unlink(s->state_file);
-
- if (temp_path)
- (void) unlink(temp_path);
-
return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
}
if (r < 0)
return log_error_errno(r, "Failed to generate stream ID: %m");
- stream = new0(StdoutStream, 1);
+ stream = new(StdoutStream, 1);
if (!stream)
return log_oom();
- stream->fd = -1;
- stream->priority = LOG_INFO;
+ *stream = (StdoutStream) {
+ .fd = -1,
+ .priority = LOG_INFO,
+ };
xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
s->n_stdout_streams++;
+ (void) server_start_or_stop_idle_timer(s); /* Maybe no longer idle? */
+
if (ret)
*ret = stream;
- stream = NULL;
-
+ TAKE_PTR(stream);
return 0;
}
if (r < 0)
return r;
- fd = -1;
+ TAKE_FD(fd);
return 0;
}
assert(fname);
if (!stream->state_file) {
- stream->state_file = path_join("/run/systemd/journal/streams", fname);
+ stream->state_file = path_join(stream->server->runtime_directory, "streams", fname);
if (!stream->state_file)
return log_oom();
}
int server_restore_streams(Server *s, FDSet *fds) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
+ const char *path;
int r;
- d = opendir("/run/systemd/journal/streams");
+ path = strjoina(s->runtime_directory, "/streams");
+ d = opendir(path);
if (!d) {
if (errno == ENOENT)
return 0;
- return log_warning_errno(errno, "Failed to enumerate /run/systemd/journal/streams: %m");
+ return log_warning_errno(errno, "Failed to enumerate %s: %m", path);
}
FOREACH_DIRENT(de, d, goto fail) {
/* No file descriptor? Then let's delete the state file */
log_debug("Cannot restore stream file %s", de->d_name);
if (unlinkat(dirfd(d), de->d_name, 0) < 0)
- log_warning_errno(errno, "Failed to remove /run/systemd/journal/streams/%s: %m",
- de->d_name);
+ log_warning_errno(errno, "Failed to remove %s/%s: %m", path, de->d_name);
continue;
}
return log_error_errno(errno, "Failed to read streams directory: %m");
}
-int server_open_stdout_socket(Server *s) {
- static const union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/journal/stdout",
- };
+int server_open_stdout_socket(Server *s, const char *stdout_socket) {
int r;
assert(s);
+ assert(stdout_socket);
if (s->stdout_fd < 0) {
+ union sockaddr_union sa;
+ socklen_t sa_len;
+
+ r = sockaddr_un_set_path(&sa.un, stdout_socket);
+ if (r < 0)
+ return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", stdout_socket);
+ sa_len = r;
+
s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->stdout_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
(void) sockaddr_un_unlink(&sa.un);
- r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ r = bind(s->stdout_fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
#include "fdset.h"
#include "journald-server.h"
-int server_open_stdout_socket(Server *s);
+int server_open_stdout_socket(Server *s, const char *stdout_socket);
int server_restore_streams(Server *s, FDSet *fds);
void stdout_stream_free(StdoutStream *s);
/* Warn once every 30s if we missed syslog message */
#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
-static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
+static void forward_syslog_iovec(
+ Server *s,
+ const struct iovec *iovec,
+ unsigned n_iovec,
+ const struct ucred *ucred,
+ const struct timeval *tv) {
+
+ union sockaddr_union sa;
- static const union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/journal/syslog",
- };
struct msghdr msghdr = {
.msg_iov = (struct iovec *) iovec,
.msg_iovlen = n_iovec,
- .msg_name = (struct sockaddr*) &sa.sa,
- .msg_namelen = SOCKADDR_UN_LEN(sa.un),
};
struct cmsghdr *cmsg;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control;
+ const char *j;
+ int r;
assert(s);
assert(iovec);
assert(n_iovec > 0);
+ j = strjoina(s->runtime_directory, "/syslog");
+ r = sockaddr_un_set_path(&sa.un, j);
+ if (r < 0) {
+ log_debug_errno(r, "Forwarding socket path %s too long for AF_UNIX, not forwarding: %m", j);
+ return;
+ }
+
+ msghdr.msg_name = &sa.sa;
+ msghdr.msg_namelen = r;
+
if (ucred) {
zero(control);
msghdr.msg_control = &control;
server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
}
-int server_open_syslog_socket(Server *s) {
-
- static const union sockaddr_union sa = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/journal/dev-log",
- };
+int server_open_syslog_socket(Server *s, const char *syslog_socket) {
int r;
assert(s);
+ assert(syslog_socket);
if (s->syslog_fd < 0) {
+ union sockaddr_union sa;
+ socklen_t sa_len;
+
+ r = sockaddr_un_set_path(&sa.un, syslog_socket);
+ if (r < 0)
+ return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", syslog_socket);
+ sa_len = r;
+
s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s->syslog_fd < 0)
return log_error_errno(errno, "socket() failed: %m");
(void) sockaddr_un_unlink(&sa.un);
- r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ r = bind(s->syslog_fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv);
void server_process_syslog_message(Server *s, const char *buf, size_t buf_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len);
-int server_open_syslog_socket(Server *s);
+int server_open_syslog_socket(Server *s, const char *syslog_socket);
void server_maybe_warn_forward_syslog_missed(Server *s);
#include "sigbus.h"
int main(int argc, char *argv[]) {
+ const char *namespace;
Server server;
int r;
- if (argc > 1) {
- log_error("This program does not take arguments.");
+ if (argc > 2) {
+ log_error("This program takes one or no arguments.");
return EXIT_FAILURE;
}
+ namespace = argc > 1 ? empty_to_null(argv[1]) : NULL;
+
log_set_prohibit_ipc(true);
log_set_target(LOG_TARGET_AUTO);
log_set_facility(LOG_SYSLOG);
sigbus_install();
- r = server_init(&server);
+ r = server_init(&server, namespace);
if (r < 0)
goto finish;
server_flush_to_var(&server, true);
server_flush_dev_kmsg(&server);
- log_debug("systemd-journald running as pid "PID_FMT, getpid_cached());
+ if (server.namespace)
+ log_debug("systemd-journald running as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace);
+ else
+ log_debug("systemd-journald running as PID "PID_FMT" for the system.", getpid_cached());
+
server_driver_message(&server, 0,
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_START_STR,
LOG_MESSAGE("Journal started"),
usec_t t = USEC_INFINITY, n;
r = sd_event_get_state(server.event);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to get event loop state: %m");
goto finish;
+ }
if (r == SD_EVENT_FINISHED)
break;
server_maybe_warn_forward_syslog_missed(&server);
}
- log_debug("systemd-journald stopped as pid "PID_FMT, getpid_cached());
+ if (server.namespace)
+ log_debug("systemd-journald stopped as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace);
+ else
+ log_debug("systemd-journald stopped as PID "PID_FMT" for the system.", getpid_cached());
+
server_driver_message(&server, 0,
"MESSAGE_ID=" SD_MESSAGE_JOURNAL_STOP_STR,
LOG_MESSAGE("Journal stopped"),
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
+#include "syslog-util.h"
#define JOURNAL_FILES_MAX 7168
if (size < 2)
return false;
- if (startswith(data, "__"))
+ if (((char*) data)[0] == '_' && ((char*) data)[1] == '_')
return false;
b = data;
static Match *match_new(Match *p, MatchType t) {
Match *m;
- m = new0(Match, 1);
+ m = new(Match, 1);
if (!m)
return NULL;
- m->type = t;
+ *m = (Match) {
+ .type = t,
+ .parent = p,
+ };
- if (p) {
- m->parent = p;
+ if (p)
LIST_PREPEND(matches, p->matches, m);
- }
return m;
}
detach_location(j);
}
-_pure_ static int compare_with_location(JournalFile *f, Location *l) {
+_pure_ static int compare_with_location(const JournalFile *f, const Location *l, const JournalFile *current_file) {
int r;
assert(f);
assert(l);
+ assert(current_file);
assert(f->location_type == LOCATION_SEEK);
assert(IN_SET(l->type, LOCATION_DISCRETE, LOCATION_SEEK));
l->realtime_set &&
f->current_realtime == l->realtime &&
l->xor_hash_set &&
- f->current_xor_hash == l->xor_hash)
+ f->current_xor_hash == l->xor_hash &&
+ f != current_file)
return 0;
if (l->seqnum_set &&
if (j->current_location.type == LOCATION_DISCRETE) {
int k;
- k = compare_with_location(f, &j->current_location);
+ k = compare_with_location(f, &j->current_location, j->current_file);
found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
} else
static int dirname_is_machine_id(const char *fn) {
sd_id128_t id, machine;
+ const char *e;
int r;
+ /* Returns true if the specified directory name matches the local machine ID */
+
r = sd_id128_get_machine(&machine);
if (r < 0)
return r;
- r = sd_id128_from_string(fn, &id);
+ e = strchr(fn, '.');
+ if (e) {
+ const char *k;
+
+ /* Looks like it has a namespace suffix. Verify that. */
+ if (!log_namespace_name_valid(e + 1))
+ return false;
+
+ k = strndupa(fn, e - fn);
+ r = sd_id128_from_string(k, &id);
+ } else
+ r = sd_id128_from_string(fn, &id);
if (r < 0)
return r;
return sd_id128_equal(id, machine);
}
+static int dirname_has_namespace(const char *fn, const char *namespace) {
+ const char *e;
+
+ /* Returns true if the specified directory name matches the specified namespace */
+
+ e = strchr(fn, '.');
+ if (e) {
+ const char *k;
+
+ if (!namespace)
+ return false;
+
+ if (!streq(e + 1, namespace))
+ return false;
+
+ k = strndupa(fn, e - fn);
+ return id128_is_valid(k);
+ }
+
+ if (namespace)
+ return false;
+
+ return id128_is_valid(fn);
+}
+
static bool dirent_is_journal_file(const struct dirent *de) {
assert(de);
+ /* Returns true if the specified directory entry looks like a journal file we might be interested in */
+
if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
endswith(de->d_name, ".journal~");
}
-static bool dirent_is_id128_subdir(const struct dirent *de) {
+static bool dirent_is_journal_subdir(const struct dirent *de) {
+ const char *e, *n;
assert(de);
+ /* returns true if the specified directory entry looks like a directory that might contain journal
+ * files we might be interested in, i.e. is either a 128bit ID or a 128bit ID suffixed by a
+ * namespace. */
+
if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
return false;
- return id128_is_valid(de->d_name);
+ e = strchr(de->d_name, '.');
+ if (!e)
+ return id128_is_valid(de->d_name); /* No namespace */
+
+ n = strndupa(de->d_name, e - de->d_name);
+ if (!id128_is_valid(n))
+ return false;
+
+ return log_namespace_name_valid(e + 1);
}
static int directory_open(sd_journal *j, const char *path, DIR **ret) {
if (dirent_is_journal_file(de))
(void) add_file_by_name(j, m->path, de->d_name);
- if (m->is_root && dirent_is_id128_subdir(de))
+ if (m->is_root && dirent_is_journal_subdir(de))
(void) add_directory(j, m->path, de->d_name);
}
}
}
-static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
+static int add_directory(
+ sd_journal *j,
+ const char *prefix,
+ const char *dirname) {
+
_cleanup_free_ char *path = NULL;
_cleanup_closedir_ DIR *d = NULL;
Directory *m;
!((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
return 0;
+ if (!(FLAGS_SET(j->flags, SD_JOURNAL_ALL_NAMESPACES) ||
+ dirname_has_namespace(dirname, j->namespace) > 0 ||
+ (FLAGS_SET(j->flags, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) && dirname_has_namespace(dirname, NULL) > 0)))
+ return 0;
+
r = directory_open(j, path, &d);
if (r < 0) {
log_debug_errno(r, "Failed to open directory '%s': %m", path);
m = hashmap_get(j->directories_by_path, path);
if (!m) {
- m = new0(Directory, 1);
+ m = new(Directory, 1);
if (!m) {
r = -ENOMEM;
goto fail;
}
- m->is_root = false;
- m->path = path;
+ *m = (Directory) {
+ .is_root = false,
+ .path = path,
+ };
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
free(m);
return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
}
-static sd_journal *journal_new(int flags, const char *path) {
+static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
j = new0(sd_journal, 1);
j->path = t;
}
+ if (namespace) {
+ j->namespace = strdup(namespace);
+ if (!j->namespace)
+ return NULL;
+ }
+
j->files = ordered_hashmap_new(&path_hash_ops);
if (!j->files)
return NULL;
#define OPEN_ALLOWED_FLAGS \
(SD_JOURNAL_LOCAL_ONLY | \
SD_JOURNAL_RUNTIME_ONLY | \
- SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
+ SD_JOURNAL_SYSTEM | \
+ SD_JOURNAL_CURRENT_USER | \
+ SD_JOURNAL_ALL_NAMESPACES | \
+ SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
-_public_ int sd_journal_open(sd_journal **ret, int flags) {
+_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert_return(ret, -EINVAL);
assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, namespace);
if (!j)
return -ENOMEM;
return 0;
}
+_public_ int sd_journal_open(sd_journal **ret, int flags) {
+ return sd_journal_open_namespace(ret, NULL, flags);
+}
+
#define OPEN_CONTAINER_ALLOWED_FLAGS \
(SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
char *p;
int r;
- /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
+ /* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in
* combination with sd_journal_open_directory_fd(). */
assert_return(machine, -EINVAL);
if (!streq_ptr(class, "container"))
return -EIO;
- j = journal_new(flags, root);
+ j = journal_new(flags, root, NULL);
if (!j)
return -ENOMEM;
assert_return(path, -EINVAL);
assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
- j = journal_new(flags, path);
+ j = journal_new(flags, path, NULL);
if (!j)
return -ENOMEM;
assert_return(ret, -EINVAL);
assert_return(flags == 0, -EINVAL);
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
if (!S_ISDIR(st.st_mode))
return -EBADFD;
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
assert_return(n_fds > 0, -EBADF);
assert_return(flags == 0, -EINVAL);
- j = journal_new(flags, NULL);
+ j = journal_new(flags, NULL, NULL);
if (!j)
return -ENOMEM;
free(j->path);
free(j->prefix);
+ free(j->namespace);
free(j->unique_field);
free(j->fields_buffer);
free(j);
log_debug("Reiteration complete.");
}
-static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
+static void process_inotify_event(sd_journal *j, const struct inotify_event *e) {
Directory *d;
assert(j);
assert_return(!journal_pid_changed(j), -ECHILD);
if (j->inotify_fd < 0) {
+ Iterator i;
+ JournalFile *f;
/* This is the first invocation, hence create the
* inotify watch */
if (r < 0)
return r;
+ /* Server might have done some vacuuming while we weren't watching.
+ Get rid of the deleted files now so they don't stay around indefinitely. */
+ ORDERED_HASHMAP_FOREACH(f, j->files, i) {
+ r = journal_file_fstat(f);
+ if (r < 0) {
+ log_debug_errno(r,"Failed to fstat() journal file '%s' : %m", f->path);
+ continue;
+ }
+
+ if (f->last_stat.st_nlink <= 0)
+ remove_file_real(j, f);
+ }
+
/* The journal might have changed since the context
* object was created and we weren't watching before,
* hence don't wait for anything, and return
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
read -r -d '' -a line < /proc/cmdline
for i in "${line[@]}"; do
[[ "${i#initrd=*}" != "$i" ]] && continue
+ [[ "${i#BOOT_IMAGE=*}" != "$i" ]] && continue
BOOT_OPTIONS+=("$i")
done
fi
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
# SPDX-License-Identifier: LGPL-2.1+
#include "sd-ndisc.h"
#include "alloc-util.h"
+#include "arphrd-list.h"
#include "condition.h"
#include "conf-parser.h"
#include "device-util.h"
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wifi_iftype, enum nl80211_iftype);
+char *link_get_type_string(unsigned short iftype, sd_device *device) {
+ const char *t, *devtype;
+ char *p;
+
+ if (device &&
+ sd_device_get_devtype(device, &devtype) >= 0 &&
+ !isempty(devtype))
+ return strdup(devtype);
+
+ t = arphrd_to_name(iftype);
+ if (!t)
+ return NULL;
+
+ p = strdup(t);
+ if (!p)
+ return NULL;
+
+ ascii_strlower(p);
+ return p;
+}
+
bool net_match_config(Set *match_mac,
Set *match_permanent_mac,
char * const *match_paths,
char * const *match_wifi_iftype,
char * const *match_ssid,
Set *match_bssid,
+ unsigned short iftype,
sd_device *device,
const struct ether_addr *dev_mac,
const struct ether_addr *dev_permanent_mac,
const char *ssid,
const struct ether_addr *bssid) {
- const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
+ const char *dev_path = NULL, *dev_driver = NULL, *mac_str;
+ _cleanup_free_ char *dev_type;
+
+ dev_type = link_get_type_string(iftype, device);
if (device) {
(void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
(void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
- (void) sd_device_get_devtype(device, &dev_type);
-
if (!dev_name)
(void) sd_device_get_sysname(device, &dev_name);
if (!dev_mac &&
#define LINK_BRIDGE_PORT_PRIORITY_INVALID 128
#define LINK_BRIDGE_PORT_PRIORITY_MAX 63
+char *link_get_type_string(unsigned short iftype, sd_device *device);
bool net_match_config(Set *match_mac,
Set *match_permanent_mac,
char * const *match_path,
char * const *match_wifi_iftype,
char * const *match_ssid,
Set *match_bssid,
+ unsigned short iftype,
sd_device *device,
const struct ether_addr *dev_mac,
const struct ether_addr *dev_permanent_mac,
}
static usec_t client_timeout_compute_random(usec_t val) {
- return val - val / 10 +
- (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
+ return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC;
}
static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
usec_t max_retransmit_duration = 0;
uint8_t max_retransmit_count = 0;
char time_string[FORMAT_TIMESPAN_MAX];
- uint32_t expire = 0;
assert(s);
assert(client);
max_retransmit_time = DHCP6_REB_MAX_RT;
if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
- r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
- &expire);
+ uint32_t expire = 0;
+
+ r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire);
if (r < 0) {
client_stop(client, r);
return 0;
return 0;
}
- if (max_retransmit_count &&
+ if (max_retransmit_count > 0 &&
client->retransmit_count >= max_retransmit_count) {
client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
return 0;
if (r >= 0)
client->retransmit_count++;
- if (!client->retransmit_time) {
+ if (client->retransmit_time == 0) {
client->retransmit_time =
client_timeout_compute_random(init_retransmit_time);
client->retransmit_time += init_retransmit_time / 10;
} else {
- if (max_retransmit_time &&
+ if (max_retransmit_time > 0 &&
client->retransmit_time > max_retransmit_time / 2)
client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
else
if (r < 0)
goto error;
- if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
+ if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
max_retransmit_duration / USEC_PER_SEC);
return 0;
}
+_public_ int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
+ unsigned char *ret_prefixlen) {
+ assert_return(p, -EINVAL);
+ assert_return(ret_in6_addr, -EINVAL);
+ assert_return(ret_prefixlen, -EINVAL);
+
+ *ret_in6_addr = p->opt.in6_addr;
+ *ret_prefixlen = p->opt.prefixlen;
+
+ return 0;
+}
+
_public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
assert_return(p, -EINVAL);
LIBSYSTEMD_245 {
global:
+ sd_bus_enqueue_for_read;
+ sd_bus_message_dump;
sd_bus_message_sensitive;
sd_event_add_child_pidfd;
sd_event_source_get_child_pidfd;
sd_event_source_get_child_process_own;
sd_event_source_set_child_process_own;
sd_event_source_send_child_signal;
+ sd_journal_open_namespace;
} LIBSYSTEMD_243;
SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_UNMANAGED_INTERFACE, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_HOME, EEXIST),
+ SD_BUS_ERROR_MAP(BUS_ERROR_UID_IN_USE, EEXIST),
+ SD_BUS_ERROR_MAP(BUS_ERROR_USER_NAME_EXISTS, EEXIST),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_EXISTS, EEXIST),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_ALREADY_ACTIVE, EALREADY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_ALREADY_FIXATED, EALREADY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_UNFIXATED, EADDRNOTAVAIL),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_ACTIVE, EALREADY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_ABSENT, EREMOTE),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_BUSY, EBUSY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PASSWORD, ENOKEY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_LOW_PASSWORD_QUALITY, EUCLEAN),
+ SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN, EBADSLT),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_NEEDED, ENOANO),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, ERFKILL),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_LOCKED, EOWNERDEAD),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN, ENOLCK),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, ETOOMANYREFS),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT, EUCLEAN),
+ SD_BUS_ERROR_MAP(BUS_ERROR_BAD_SIGNATURE, EKEYREJECTED),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_RECORD_MISMATCH, EUCLEAN),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_RECORD_DOWNGRADE, ESTALE),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_RECORD_SIGNED, EROFS),
+ SD_BUS_ERROR_MAP(BUS_ERROR_BAD_HOME_SIZE, ERANGE),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_KEY, ENOPKG),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_LOCKED, ENOEXEC),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_LOCKED, ENOEXEC),
+ SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS, ENOBUFS),
+ SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS),
+
SD_BUS_ERROR_MAP_END
};
#define BUS_ERROR_SPEED_METER_INACTIVE "org.freedesktop.network1.SpeedMeterInactive"
#define BUS_ERROR_UNMANAGED_INTERFACE "org.freedesktop.network1.UnmanagedInterface"
+#define BUS_ERROR_NO_SUCH_HOME "org.freedesktop.home1.NoSuchHome"
+#define BUS_ERROR_UID_IN_USE "org.freedesktop.home1.UIDInUse"
+#define BUS_ERROR_USER_NAME_EXISTS "org.freedesktop.home1.UserNameExists"
+#define BUS_ERROR_HOME_EXISTS "org.freedesktop.home1.HomeExists"
+#define BUS_ERROR_HOME_ALREADY_ACTIVE "org.freedesktop.home1.HomeAlreadyActive"
+#define BUS_ERROR_HOME_ALREADY_FIXATED "org.freedesktop.home1.HomeAlreadyFixated"
+#define BUS_ERROR_HOME_UNFIXATED "org.freedesktop.home1.HomeUnfixated"
+#define BUS_ERROR_HOME_NOT_ACTIVE "org.freedesktop.home1.HomeNotActive"
+#define BUS_ERROR_HOME_ABSENT "org.freedesktop.home1.HomeAbsent"
+#define BUS_ERROR_HOME_BUSY "org.freedesktop.home1.HomeBusy"
+#define BUS_ERROR_BAD_PASSWORD "org.freedesktop.home1.BadPassword"
+#define BUS_ERROR_LOW_PASSWORD_QUALITY "org.freedesktop.home1.LowPasswordQuality"
+#define BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN "org.freedesktop.home1.BadPasswordAndNoToken"
+#define BUS_ERROR_TOKEN_PIN_NEEDED "org.freedesktop.home1.TokenPinNeeded"
+#define BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED "org.freedesktop.home1.TokenProtectedAuthenticationPathNeeded"
+#define BUS_ERROR_TOKEN_PIN_LOCKED "org.freedesktop.home1.TokenPinLocked"
+#define BUS_ERROR_TOKEN_BAD_PIN "org.freedesktop.home1.BadPin"
+#define BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT "org.freedesktop.home1.BadPinFewTriesLeft"
+#define BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT "org.freedesktop.home1.BadPinOneTryLeft"
+#define BUS_ERROR_BAD_SIGNATURE "org.freedesktop.home1.BadSignature"
+#define BUS_ERROR_HOME_RECORD_MISMATCH "org.freedesktop.home1.RecordMismatch"
+#define BUS_ERROR_HOME_RECORD_DOWNGRADE "org.freedesktop.home1.RecordDowngrade"
+#define BUS_ERROR_HOME_RECORD_SIGNED "org.freedesktop.home1.RecordSigned"
+#define BUS_ERROR_BAD_HOME_SIZE "org.freedesktop.home1.BadHomeSize"
+#define BUS_ERROR_NO_PRIVATE_KEY "org.freedesktop.home1.NoPrivateKey"
+#define BUS_ERROR_HOME_LOCKED "org.freedesktop.home1.HomeLocked"
+#define BUS_ERROR_HOME_NOT_LOCKED "org.freedesktop.home1.HomeNotLocked"
+#define BUS_ERROR_NO_DISK_SPACE "org.freedesktop.home1.NoDiskSpace"
+#define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
+#define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
+
BUS_ERROR_MAP_ELF_USE(bus_common_errors);
#include "terminal-util.h"
#include "util.h"
-static char *indent(unsigned level, unsigned flags) {
+static char *indent(unsigned level, uint64_t flags) {
char *p;
unsigned n, i = 0;
n = 0;
- if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0)
+ if (flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0)
level -= 1;
- if (flags & BUS_MESSAGE_DUMP_WITH_HEADER)
+ if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER)
n += 2;
p = new(char, n + level*8 + 1);
if (!p)
return NULL;
- if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
+ if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER) {
p[i++] = ' ';
p[i++] = ' ';
}
return p;
}
-int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
+_public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) {
unsigned level = 1;
int r;
if (!f)
f = stdout;
- if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
+ if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER) {
fprintf(f,
"%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64,
m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
bus_creds_dump(&m->creds, f, true);
}
- r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY));
+ r = sd_bus_message_rewind(m, !(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY));
if (r < 0)
return log_error_errno(r, "Failed to rewind: %m");
- if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
+ if (!(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
_cleanup_free_ char *prefix = NULL;
prefix = indent(0, flags);
}
}
- if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
+ if (!(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
_cleanup_free_ char *prefix = NULL;
prefix = indent(0, flags);
#include "sd-bus.h"
-enum {
- BUS_MESSAGE_DUMP_WITH_HEADER = 1 << 0,
- BUS_MESSAGE_DUMP_SUBTREE_ONLY = 1 << 1,
-};
-
-int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags);
-
int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse);
int bus_pcap_header(size_t snaplen, FILE *f);
case _SD_BUS_VTABLE_SIGNAL:
fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member);
if (bus_vtable_has_names(vtable))
- names = strempty(v->x.method.names);
+ names = strempty(v->x.signal.names);
introspect_write_arguments(i, strempty(v->x.signal.signature), &names, NULL);
introspect_write_flags(i, v->type, v->flags);
fputs(" </signal>\n", i->f);
return bus->close_on_exit;
}
+
+_public_ int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m) {
+ int r;
+
+ assert_return(bus, -EINVAL);
+ assert_return(bus = bus_resolve(bus), -ENOPKG);
+ assert_return(m, -EINVAL);
+ assert_return(m->sealed, -EINVAL);
+ assert_return(!bus_pid_changed(bus), -ECHILD);
+
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+
+ /* Re-enqueue a message for reading. This is primarily useful for PolicyKit-style authentication,
+ * where we accept a message, then determine we need to interactively authenticate the user, and then
+ * we want to process the message again. */
+
+ r = bus_rqueue_make_room(bus);
+ if (r < 0)
+ return r;
+
+ bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(m, bus);
+ return 0;
+}
strna(sd_bus_message_get_member(m)),
pid,
strna(label));
- /* bus_message_dump(m); */
+ /* sd_bus_message_dump(m); */
/* sd_bus_message_rewind(m, true); */
if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
}
#endif
- assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
+ assert_se(sd_bus_message_dump(m, NULL, SD_BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
assert_se(bus_message_get_blob(m, &blob, &sz) >= 0);
assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, &n) >= 0);
blob = NULL;
- assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
+ assert_se(sd_bus_message_dump(n, NULL, SD_BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
m = sd_bus_message_unref(m);
assert_se(sd_bus_message_append(m, "as", 0) >= 0);
assert_se(sd_bus_message_seal(m, 4712, 0) >= 0);
- assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
+ assert_se(sd_bus_message_dump(m, NULL, SD_BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
return EXIT_SUCCESS;
}
r = sd_bus_message_seal(m, 4711, 0);
assert_se(r >= 0);
- bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(m, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
ms = open_memstream_unlocked(&first, &first_size);
- bus_message_dump(m, ms, 0);
+ sd_bus_message_dump(m, ms, 0);
fflush(ms);
assert_se(!ferror(ms));
r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, &m);
assert_se(r >= 0);
- bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(m, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
fclose(ms);
ms = open_memstream_unlocked(&second, &second_size);
- bus_message_dump(m, ms, 0);
+ sd_bus_message_dump(m, ms, 0);
fflush(ms);
assert_se(!ferror(ms));
assert_se(first_size == second_size);
fclose(ms);
ms = open_memstream_unlocked(&third, &third_size);
- bus_message_dump(copy, ms, 0);
+ sd_bus_message_dump(copy, ms, 0);
fflush(ms);
assert_se(!ferror(ms));
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
assert_se(r >= 0);
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
assert_se(r >= 0);
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
assert_se(r > 0);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
- bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
static int client(struct context *c) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert_se(sd_bus_new(&bus) >= 0);
static void* thread_server(void *p) {
_cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL;
_cleanup_close_ int fd = -1;
- union sockaddr_union u = {};
+ union sockaddr_union u;
const char *path = p;
- int salen;
+ int r;
log_debug("Initializing server");
assert_se(symlink(basename(suffixed), suffixed2) >= 0);
(void) usleep(100 * USEC_PER_MSEC);
- salen = sockaddr_un_set_path(&u.un, path);
- assert_se(salen >= 0);
+ socklen_t sa_len;
+ r = sockaddr_un_set_path(&u.un, path);
+ assert_se(r >= 0);
+ sa_len = r;
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
assert_se(fd >= 0);
- assert_se(bind(fd, &u.sa, salen) >= 0);
+ assert_se(bind(fd, &u.sa, sa_len) >= 0);
usleep(100 * USEC_PER_MSEC);
assert_se(listen(fd, SOMAXCONN) >= 0);
const int *fds,
unsigned n_fds) {
- union sockaddr_union sockaddr = {};
+ union sockaddr_union sockaddr;
struct iovec iovec;
struct msghdr msghdr = {
.msg_iov = &iovec,
struct cmsghdr *cmsg = NULL;
const char *e;
bool send_ucred;
- int r, salen;
+ int r;
if (!state) {
r = -EINVAL;
if (!e)
return 0;
- salen = sockaddr_un_set_path(&sockaddr.un, e);
- if (salen < 0) {
- r = salen;
+ r = sockaddr_un_set_path(&sockaddr.un, e);
+ if (r < 0)
goto finish;
- }
+ msghdr.msg_namelen = r;
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
if (fd < 0) {
(void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
iovec = IOVEC_MAKE_STRING(state);
- msghdr.msg_namelen = salen;
send_ucred =
(pid != 0 && pid != getpid_cached()) ||
value = strchr(line, '=');
if (!value)
- return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ return log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
"Key-value pair expected but got \"%s\", ignoring", line);
value[0] = '\0';
line++;
if (isempty(line + 1) || isempty(value))
- return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ return log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
"Empty %s in \"%s=%s\", ignoring",
isempty(line + 1) ? "key" : "value",
line, value);
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **match_list = NULL;
uint32_t line_number = 0;
- char *match = NULL;
int r = 0, err;
f = fopen(filename, "re");
break;
if (line[0] == ' ') {
- log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
- "Match expected but got indented property \"%s\", ignoring line", line);
- r = -EINVAL;
+ r = log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
+ "Match expected but got indented property \"%s\", ignoring line", line);
break;
}
/* start of record, first match */
state = HW_MATCH;
- match = strdup(line);
- if (!match)
- return -ENOMEM;
-
- err = strv_consume(&match_list, match);
+ err = strv_extend(&match_list, line);
if (err < 0)
return err;
case HW_MATCH:
if (len == 0) {
- log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
- "Property expected, ignoring record with no properties");
- r = -EINVAL;
+ r = log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
+ "Property expected, ignoring record with no properties");
state = HW_NONE;
- strv_clear(match_list);
+ match_list = strv_free(match_list);
break;
}
if (line[0] != ' ') {
/* another match */
- match = strdup(line);
- if (!match)
- return -ENOMEM;
-
- err = strv_consume(&match_list, match);
+ err = strv_extend(&match_list, line);
if (err < 0)
return err;
if (len == 0) {
/* end of record */
state = HW_NONE;
- strv_clear(match_list);
+ match_list = strv_free(match_list);
break;
}
if (line[0] != ' ') {
- log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
- "Property or empty line expected, got \"%s\", ignoring record", line);
- r = -EINVAL;
+ r = log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
+ "Property or empty line expected, got \"%s\", ignoring record", line);
state = HW_NONE;
- strv_clear(match_list);
+ match_list = strv_free(match_list);
break;
}
}
if (state == HW_MATCH)
- log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+ log_syntax(NULL, LOG_WARNING, filename, line_number, 0,
"Property expected, ignoring record with no properties");
return r;
return memcmp(a, b, 16);
}
+sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
+ /* Stolen from generate_random_uuid() of drivers/char/random.c
+ * in the kernel sources */
+
+ /* Set UUID version to 4 --- truly random generation */
+ id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
+
+ /* Set the UUID variant to DCE */
+ id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
+
+ return id;
+}
+
DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
void id128_hash_func(const sd_id128_t *p, struct siphash *state);
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
extern const struct hash_ops id128_hash_ops;
+
+sd_id128_t id128_make_v4_uuid(sd_id128_t id);
return 0;
}
-static sd_id128_t make_v4_uuid(sd_id128_t id) {
- /* Stolen from generate_random_uuid() of drivers/char/random.c
- * in the kernel sources */
-
- /* Set UUID version to 4 --- truly random generation */
- id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
-
- /* Set the UUID variant to DCE */
- id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
-
- return id;
-}
-
_public_ int sd_id128_randomize(sd_id128_t *ret) {
sd_id128_t t;
int r;
* only guarantee this for newly generated UUIDs, not for
* pre-existing ones. */
- *ret = make_v4_uuid(t);
+ *ret = id128_make_v4_uuid(t);
return 0;
}
/* We chop off the trailing 16 bytes */
memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
- *ret = make_v4_uuid(result);
+ *ret = id128_make_v4_uuid(result);
return 0;
}
return 0;
}
+int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+
+ r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8);
+ if (r < 0)
+ return r;
+
+ r = add_rtattr(m, type, &data, sizeof(int8_t));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+
+ r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16);
+ if (r < 0)
+ return r;
+
+ r = add_rtattr(m, type, &data, sizeof(int16_t));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+
+ r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32);
+ if (r < 0)
+ return r;
+
+ r = add_rtattr(m, type, &data, sizeof(int32_t));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) {
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+
+ r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64);
+ if (r < 0)
+ return r;
+
+ r = add_rtattr(m, type, &data, sizeof(int64_t));
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
int r;
[IFLA_CAN_BITTIMING] = { .size = sizeof(struct can_bittiming) },
[IFLA_CAN_RESTART_MS] = { .type = NETLINK_TYPE_U32 },
[IFLA_CAN_CTRLMODE] = { .size = sizeof(struct can_ctrlmode) },
+ [IFLA_CAN_TERMINATION] = { .type = NETLINK_TYPE_U16 },
};
static const NLType rtnl_link_info_data_macsec_types[] = {
.types = rtnl_nexthop_types,
};
+static const NLType rtnl_tca_option_data_cake_types[] = {
+ [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
+ [TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 },
+ [TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 },
+};
+
static const NLType rtnl_tca_option_data_codel_types[] = {
[TCA_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
[TCA_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
[TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NETLINK_TYPE_U32 },
};
+static const NLType rtnl_tca_option_data_gred_types[] = {
+ [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) },
+};
+
+static const NLType rtnl_tca_option_data_htb_types[] = {
+ [TCA_HTB_PARMS] = { .size = sizeof(struct tc_htb_opt) },
+ [TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) },
+ [TCA_HTB_CTAB] = { .size = TC_RTAB_SIZE },
+ [TCA_HTB_RTAB] = { .size = TC_RTAB_SIZE },
+ [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
+ [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
+};
+
+static const NLType rtnl_tca_option_data_sfb_types[] = {
+ [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
+};
+
static const NLType rtnl_tca_option_data_tbf_types[] = {
[TCA_TBF_PARMS] = { .size = sizeof(struct tc_tbf_qopt) },
[TCA_TBF_RTAB] = { .size = TC_RTAB_SIZE },
};
static const char* const nl_union_tca_option_data_table[] = {
+ [NL_UNION_TCA_OPTION_DATA_CAKE] = "cake",
[NL_UNION_TCA_OPTION_DATA_CODEL] = "codel",
[NL_UNION_TCA_OPTION_DATA_FQ] = "fq",
[NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel",
+ [NL_UNION_TCA_OPTION_DATA_GRED] = "gred",
+ [NL_UNION_TCA_OPTION_DATA_HTB] = "htb",
+ [NL_UNION_TCA_OPTION_DATA_SFB] = "sfb",
[NL_UNION_TCA_OPTION_DATA_TBF] = "tbf",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_tca_option_data, NLUnionTCAOptionData);
static const NLTypeSystem rtnl_tca_option_data_type_systems[] = {
+ [NL_UNION_TCA_OPTION_DATA_CAKE] = { .count = ELEMENTSOF(rtnl_tca_option_data_cake_types),
+ .types = rtnl_tca_option_data_cake_types },
[NL_UNION_TCA_OPTION_DATA_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_codel_types),
.types = rtnl_tca_option_data_codel_types },
[NL_UNION_TCA_OPTION_DATA_FQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types),
.types = rtnl_tca_option_data_fq_types },
[NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types),
.types = rtnl_tca_option_data_fq_codel_types },
+ [NL_UNION_TCA_OPTION_DATA_GRED] = { .count = ELEMENTSOF(rtnl_tca_option_data_gred_types),
+ .types = rtnl_tca_option_data_gred_types },
+ [NL_UNION_TCA_OPTION_DATA_HTB] = { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types),
+ .types = rtnl_tca_option_data_htb_types },
+ [NL_UNION_TCA_OPTION_DATA_SFB] = { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types),
+ .types = rtnl_tca_option_data_sfb_types },
[NL_UNION_TCA_OPTION_DATA_TBF] = { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types),
.types = rtnl_tca_option_data_tbf_types },
};
.match = TCA_KIND,
};
-static const NLType rtnl_qdisc_types[] = {
+static const NLType rtnl_tca_types[] = {
[TCA_KIND] = { .type = NETLINK_TYPE_STRING },
[TCA_OPTIONS] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
[TCA_INGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
[TCA_EGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
};
-static const NLTypeSystem rtnl_qdisc_type_system = {
- .count = ELEMENTSOF(rtnl_qdisc_types),
- .types = rtnl_qdisc_types,
+static const NLTypeSystem rtnl_tca_type_system = {
+ .count = ELEMENTSOF(rtnl_tca_types),
+ .types = rtnl_tca_types,
};
static const NLType error_types[] = {
[RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
- [RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
- [RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
- [RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_NEWTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_DELTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+ [RTM_GETTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
};
const NLTypeSystem rtnl_type_system_root = {
NETLINK_TYPE_U16, /* NLA_U16 */
NETLINK_TYPE_U32, /* NLA_U32 */
NETLINK_TYPE_U64, /* NLA_U64 */
+ NETLINK_TYPE_S8, /* NLA_S8 */
+ NETLINK_TYPE_S16, /* NLA_S16 */
+ NETLINK_TYPE_S32, /* NLA_S32 */
+ NETLINK_TYPE_S64, /* NLA_S64 */
NETLINK_TYPE_STRING, /* NLA_STRING */
NETLINK_TYPE_FLAG, /* NLA_FLAG */
NETLINK_TYPE_IN_ADDR,
NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_;
typedef enum NLUnionTCAOptionData {
+ NL_UNION_TCA_OPTION_DATA_CAKE,
NL_UNION_TCA_OPTION_DATA_CODEL,
NL_UNION_TCA_OPTION_DATA_FQ,
NL_UNION_TCA_OPTION_DATA_FQ_CODEL,
+ NL_UNION_TCA_OPTION_DATA_GRED,
+ NL_UNION_TCA_OPTION_DATA_HTB,
+ NL_UNION_TCA_OPTION_DATA_SFB,
NL_UNION_TCA_OPTION_DATA_TBF,
_NL_UNION_TCA_OPTION_DATA_MAX,
_NL_UNION_TCA_OPTION_DATA_INVALID = -1,
return ret;
}
+int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+ int r;
+
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(*rtnl, message, 0, &reply);
+ if (r == -EINVAL)
+ return -ENODEV; /* The device does not exist */
+ if (r < 0)
+ return r;
+
+ return sd_rtnl_message_link_get_type(reply, ret);
+}
+
int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
struct nlmsgerr *err;
int r;
return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC);
}
+static inline bool rtnl_message_type_is_tclass(uint16_t type) {
+ return IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
+}
+
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);
int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);
int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names);
int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name);
+int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret);
int rtnl_log_parse_error(int r);
int rtnl_log_create_error(int r);
return 0;
}
+
+int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) {
+ struct tcmsg *tcm;
+ int r;
+
+ assert_return(rtnl_message_type_is_tclass(nlmsg_type), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = message_new(rtnl, ret, nlmsg_type);
+ if (r < 0)
+ return r;
+
+ if (nlmsg_type == RTM_NEWTCLASS)
+ (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+
+ tcm = NLMSG_DATA((*ret)->hdr);
+ tcm->tcm_family = tcm_family;
+ tcm->tcm_ifindex = tcm_ifindex;
+
+ return 0;
+}
+
+int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent) {
+ struct tcmsg *tcm;
+
+ assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
+
+ tcm = NLMSG_DATA(m->hdr);
+ tcm->tcm_parent = parent;
+
+ return 0;
+}
+
+int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle) {
+ struct tcmsg *tcm;
+
+ assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
+
+ tcm = NLMSG_DATA(m->hdr);
+ tcm->tcm_handle = handle;
+
+ return 0;
+}
}
static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
+ [LINK_OPERSTATE_MISSING] = "missing",
[LINK_OPERSTATE_OFF] = "off",
[LINK_OPERSTATE_NO_CARRIER] = "no-carrier",
[LINK_OPERSTATE_DORMANT] = "dormant",
};
DEFINE_STRING_TABLE_LOOKUP(link_address_state, LinkAddressState);
+
+int parse_operational_state_range(const char *str, LinkOperationalStateRange *out) {
+ LinkOperationalStateRange range = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
+ _cleanup_free_ const char *min = NULL;
+ const char *p;
+
+ assert(str);
+ assert(out);
+
+ p = strchr(str, ':');
+ if (p) {
+ min = strndup(str, p - str);
+
+ if (!isempty(p + 1)) {
+ range.max = link_operstate_from_string(p + 1);
+ if (range.max < 0)
+ return -EINVAL;
+ }
+ } else
+ min = strdup(str);
+
+ if (!min)
+ return -ENOMEM;
+
+ if (!isempty(min)) {
+ range.min = link_operstate_from_string(min);
+ if (range.min < 0)
+ return -EINVAL;
+ }
+
+ /* Fail on empty strings. */
+ if (range.min == _LINK_OPERSTATE_INVALID && range.max == _LINK_OPERSTATE_INVALID)
+ return -EINVAL;
+
+ if (range.min == _LINK_OPERSTATE_INVALID)
+ range.min = LINK_OPERSTATE_MISSING;
+ if (range.max == _LINK_OPERSTATE_INVALID)
+ range.max = LINK_OPERSTATE_ROUTABLE;
+
+ if (range.min > range.max)
+ return -EINVAL;
+
+ *out = range;
+
+ return 0;
+}
bool network_is_online(void);
typedef enum LinkOperationalState {
+ LINK_OPERSTATE_MISSING,
LINK_OPERSTATE_OFF,
LINK_OPERSTATE_NO_CARRIER,
LINK_OPERSTATE_DORMANT,
const char* link_address_state_to_string(LinkAddressState s) _const_;
LinkAddressState link_address_state_from_string(const char *s) _pure_;
+
+typedef struct LinkOperationalStateRange {
+ LinkOperationalState min;
+ LinkOperationalState max;
+} LinkOperationalStateRange;
+
+#define LINK_OPERSTATE_RANGE_DEFAULT (LinkOperationalStateRange) { LINK_OPERSTATE_DEGRADED, \
+ LINK_OPERSTATE_ROUTABLE }
+
+int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
#include <sys/types.h>
#include <unistd.h>
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "env-file-label.h"
#include "env-file.h"
#include "env-util.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-message.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "def.h"
#include "keymap-util.h"
#include "locale-util.h"
_cleanup_free_ char **l_unset = NULL;
_cleanup_strv_free_ char **l_set = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
size_t c_set, c_unset;
LocaleVariable p;
int r;
SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat"
SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat"
-# 'Plugable UD-160' USB hub, sound, network, graphics adapter
+# 'Plugable' USB hub, sound, network, graphics adapter
SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="000[13]", ENV{ID_AUTOSEAT}="1"
-# 'Plugable UD-PRO8' USB hub, sound, network, graphics adapter
-SUBSYSTEM=="usb", ATTR{idVendor}=="1a40", ATTR{idProduct}=="0201", ENV{ID_AUTOSEAT}="1"
-
# qemu (version 2.4+) has a PCI-PCI bridge (-device pci-bridge-seat) to group
# devices belonging to one seat. See:
# http://git.qemu.org/?p=qemu.git;a=blob;f=docs/multiseat.txt
show_journal_by_unit(
stdout,
i.scope,
+ NULL,
arg_output,
0,
i.timestamp.monotonic,
show_journal_by_unit(
stdout,
i.slice,
+ NULL,
arg_output,
0,
i.timestamp.monotonic,
static int activate(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
- char *short_argv[3];
int r, i;
assert(bus);
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
if (argc < 2) {
- short_argv[0] = argv[0];
- short_argv[1] = (char*) "";
- short_argv[2] = NULL;
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1/session/auto",
+ "org.freedesktop.login1.Session",
+ streq(argv[0], "lock-session") ? "Lock" :
+ streq(argv[0], "unlock-session") ? "Unlock" :
+ streq(argv[0], "terminate-session") ? "Terminate" :
+ "Activate",
+ &error, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
- argv = short_argv;
- argc = 2;
+ return 0;
}
for (i = 1; i < argc; i++) {
#include "terminal-util.h"
#include "udev-util.h"
#include "user-util.h"
+#include "userdb.h"
void manager_reset_config(Manager *m) {
assert(m);
int manager_add_user(
Manager *m,
- uid_t uid,
- gid_t gid,
- const char *name,
- const char *home,
+ UserRecord *ur,
User **ret_user) {
User *u;
int r;
assert(m);
- assert(name);
+ assert(ur);
- u = hashmap_get(m->users, UID_TO_PTR(uid));
+ u = hashmap_get(m->users, UID_TO_PTR(ur->uid));
if (!u) {
- r = user_new(&u, m, uid, gid, name, home);
+ r = user_new(&u, m, ur);
if (r < 0)
return r;
}
const char *name,
User **ret_user) {
- const char *home = NULL;
- uid_t uid;
- gid_t gid;
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
int r;
assert(m);
assert(name);
- r = get_user_creds(&name, &uid, &gid, &home, NULL, 0);
+ r = userdb_by_name(name, 0, &ur);
if (r < 0)
return r;
- return manager_add_user(m, uid, gid, name, home, ret_user);
+ return manager_add_user(m, ur, ret_user);
}
-int manager_add_user_by_uid(Manager *m, uid_t uid, User **ret_user) {
- struct passwd *p;
+int manager_add_user_by_uid(
+ Manager *m,
+ uid_t uid,
+ User **ret_user) {
+
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ int r;
assert(m);
+ assert(uid_is_valid(uid));
- errno = 0;
- p = getpwuid(uid);
- if (!p)
- return errno_or_else(ENOENT);
+ r = userdb_by_uid(uid, 0, &ur);
+ if (r < 0)
+ return r;
- return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, ret_user);
+ return manager_add_user(m, ur, ret_user);
}
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **ret) {
#include "bootspec.h"
#include "bus-common-errors.h"
#include "bus-error.h"
+#include "bus-polkit.h"
#include "bus-unit-util.h"
#include "bus-util.h"
#include "cgroup-util.h"
#include "device-util.h"
#include "dirent-util.h"
-#include "efivars.h"
#include "efi-loader.h"
+#include "efivars.h"
#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
r = sd_bus_message_append(reply, "(susso)",
session->id,
- (uint32_t) session->user->uid,
- session->user->name,
+ (uint32_t) session->user->user_record->uid,
+ session->user->user_record->user_name,
session->seat ? session->seat->id : "",
p);
if (r < 0)
return -ENOMEM;
r = sd_bus_message_append(reply, "(uso)",
- (uint32_t) user->uid,
- user->name,
+ (uint32_t) user->user_record->uid,
+ user->user_record->user_name,
p);
if (r < 0)
return r;
if (r < 0)
return r;
+ /* PolicyKit is done by bus_session_method_activate() */
+
return bus_session_method_activate(message, session, error);
}
return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT,
"Session %s not on seat %s", session_name, seat_name);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.chvt",
+ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = session_activate(session);
if (r < 0)
return r;
* count, and non-login sessions do not count either. */
HASHMAP_FOREACH(session, m->sessions, i)
if (session->class == SESSION_USER &&
- session->user->uid != uid)
+ session->user->user_record->uid != uid)
return true;
return false;
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-label.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "logind-dbus.h"
#include "logind-seat-dbus.h"
if (session->seat != s)
return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.chvt",
+ NULL,
+ false,
+ UID_INVALID,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = session_activate(session);
if (r < 0)
return r;
return r;
if (to <= 0)
- return -EINVAL;
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.chvt",
+ NULL,
+ false,
+ UID_INVALID,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
r = seat_switch_to(s, to);
if (r < 0)
assert(message);
assert(s);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.chvt",
+ NULL,
+ false,
+ UID_INVALID,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = seat_switch_to_next(s);
if (r < 0)
return r;
assert(message);
assert(s);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.chvt",
+ NULL,
+ false,
+ UID_INVALID,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = seat_switch_to_previous(s);
if (r < 0)
return r;
"ACTIVE=%s\n"
"ACTIVE_UID="UID_FMT"\n",
s->active->id,
- s->active->user->uid);
+ s->active->user->user_record->uid);
}
if (s->sessions) {
LIST_FOREACH(sessions_by_seat, i, s->sessions)
fprintf(f,
UID_FMT"%c",
- i->user->uid,
+ i->user->user_record->uid,
i->sessions_by_seat_next ? ' ' : '\n');
}
r = devnode_acl_all(s->id,
false,
- !!old_active, old_active ? old_active->user->uid : 0,
- !!s->active, s->active ? s->active->user->uid : 0);
+ !!old_active, old_active ? old_active->user->user_record->uid : 0,
+ !!s->active, s->active ? s->active->user->user_record->uid : 0);
if (r < 0)
return log_error_errno(r, "Failed to apply ACLs: %m");
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-label.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "fd-util.h"
#include "logind-brightness.h"
if (!p)
return -ENOMEM;
- return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p);
+ return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->user_record->uid, p);
}
static int property_get_name(
assert(reply);
assert(s);
- return sd_bus_message_append(reply, "s", s->user->name);
+ return sd_bus_message_append(reply, "s", s->user->user_record->user_name);
}
static int property_get_seat(
"org.freedesktop.login1.manage",
NULL,
false,
- s->user->uid,
+ s->user->user_record->uid,
&s->manager->polkit_registry,
error);
if (r < 0)
assert(message);
assert(s);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_ADMIN,
+ "org.freedesktop.login1.chvt",
+ NULL,
+ false,
+ UID_INVALID,
+ &s->manager->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Will call us back */
+
r = session_activate(s);
if (r < 0)
return r;
"org.freedesktop.login1.lock-sessions",
NULL,
false,
- s->user->uid,
+ s->user->user_record->uid,
&s->manager->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- if (uid != 0 && uid != s->user->uid)
+ if (uid != 0 && uid != s->user->user_record->uid)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint");
- session_set_idle_hint(s, b);
+ r = session_set_idle_hint(s, b);
+ if (r == -ENOTTY)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
+ if (r < 0)
+ return r;
return sd_bus_reply_method_return(message, NULL);
}
if (r < 0)
return r;
- if (uid != 0 && uid != s->user->uid)
+ if (uid != 0 && uid != s->user->user_record->uid)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
session_set_locked_hint(s, b);
"org.freedesktop.login1.manage",
NULL,
false,
- s->user->uid,
+ s->user->user_record->uid,
&s->manager->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- if (uid != 0 && (force || uid != s->user->uid))
+ if (uid != 0 && (force || uid != s->user->user_record->uid))
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
r = session_set_controller(s, sd_bus_message_get_sender(message), force, true);
if (r < 0)
return r;
- if (uid != 0 && uid != s->user->uid)
+ if (uid != 0 && uid != s->user->user_record->uid)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness.");
r = sd_device_new_from_subsystem_sysname(&d, subsystem, name);
"session_fd=%d seat=%s vtnr=%u",
s->id,
p,
- (uint32_t) s->user->uid,
+ (uint32_t) s->user->user_record->uid,
s->user->runtime_path,
fifo_fd,
s->seat ? s->seat->id : "",
p,
s->user->runtime_path,
fifo_fd,
- (uint32_t) s->user->uid,
+ (uint32_t) s->user->user_record->uid,
s->seat ? s->seat->id : "",
(uint32_t) s->vtnr,
false);
"IS_DISPLAY=%i\n"
"STATE=%s\n"
"REMOTE=%i\n",
- s->user->uid,
- s->user->name,
+ s->user->user_record->uid,
+ s->user->user_record->user_name,
session_is_active(s),
s->user->display == s,
session_state_to_string(session_get_state(s)),
if (!scope)
return log_oom();
- description = strjoina("Session ", s->id, " of user ", s->user->name);
+ description = strjoina("Session ", s->id, " of user ", s->user->user_record->user_name);
r = manager_start_scope(
s->manager,
"systemd-user-sessions.service",
s->user->runtime_dir_service,
s->user->service),
- s->user->home,
+ user_record_home_directory(s->user->user_record),
properties,
error,
&s->scope_job);
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR,
"SESSION_ID=%s", s->id,
- "USER_ID=%s", s->user->name,
+ "USER_ID=%s", s->user->user_record->user_name,
"LEADER="PID_FMT, s->leader,
- LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name));
+ LOG_MESSAGE("New session %s of user %s.", s->id, s->user->user_record->user_name));
if (!dual_timestamp_is_set(&s->timestamp))
dual_timestamp_get(&s->timestamp);
s->scope_job = mfree(s->scope_job);
/* Optionally, let's kill everything that's left now. */
- if (force || manager_shall_kill(s->manager, s->user->name)) {
+ if (force ||
+ (s->user->user_record->kill_processes != 0 &&
+ (s->user->user_record->kill_processes > 0 ||
+ manager_shall_kill(s->manager, s->user->user_record->user_name)))) {
r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job);
if (r < 0) {
* Session stop is quite significant on its own, let's log it. */
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
"SESSION_ID=%s", s->id,
- "USER_ID=%s", s->user->name,
+ "USER_ID=%s", s->user->user_record->user_name,
"LEADER="PID_FMT, s->leader,
LOG_MESSAGE("Session %s logged out. Waiting for processes to exit.", s->id));
}
log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SESSION_STOP_STR,
"SESSION_ID=%s", s->id,
- "USER_ID=%s", s->user->name,
+ "USER_ID=%s", s->user->user_record->user_name,
"LEADER="PID_FMT, s->leader,
LOG_MESSAGE("Removed session %s.", s->id));
}
int session_get_idle_hint(Session *s, dual_timestamp *t) {
- usec_t atime = 0, n;
+ usec_t atime = 0;
int r;
assert(s);
- /* Explicit idle hint is set */
- if (s->idle_hint) {
+ /* Graphical sessions have an explicit idle hint */
+ if (SESSION_TYPE_IS_GRAPHICAL(s->type)) {
if (t)
*t = s->idle_hint_timestamp;
return s->idle_hint;
}
- /* Graphical sessions should really implement a real
- * idle hint logic */
- if (SESSION_TYPE_IS_GRAPHICAL(s->type))
- goto dont_know;
-
- /* For sessions with an explicitly configured tty, let's check
- * its atime */
+ /* For sessions with an explicitly configured tty, let's check its atime */
if (s->tty) {
r = get_tty_atime(s->tty, &atime);
if (r >= 0)
goto found_atime;
}
- /* For sessions with a leader but no explicitly configured
- * tty, let's check the controlling tty of the leader */
+ /* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of
+ * the leader */
if (pid_is_valid(s->leader)) {
r = get_process_ctty_atime(s->leader, &atime);
if (r >= 0)
goto found_atime;
}
-dont_know:
if (t)
- *t = s->idle_hint_timestamp;
+ *t = DUAL_TIMESTAMP_NULL;
- return 0;
+ return false;
found_atime:
if (t)
dual_timestamp_from_realtime(t, atime);
- n = now(CLOCK_REALTIME);
-
if (s->manager->idle_action_usec <= 0)
- return 0;
+ return false;
- return atime + s->manager->idle_action_usec <= n;
+ return usec_add(atime, s->manager->idle_action_usec) <= now(CLOCK_REALTIME);
}
-void session_set_idle_hint(Session *s, bool b) {
+int session_set_idle_hint(Session *s, bool b) {
assert(s);
+ if (!SESSION_TYPE_IS_GRAPHICAL(s->type))
+ return -ENOTTY;
+
if (s->idle_hint == b)
- return;
+ return 0;
s->idle_hint = b;
dual_timestamp_get(&s->idle_hint_timestamp);
user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
+
+ return 1;
}
int session_get_locked_hint(Session *s) {
if (vt < 0)
return vt;
- r = fchown(vt, s->user->uid, -1);
+ r = fchown(vt, s->user->user_record->uid, -1);
if (r < 0) {
r = log_error_errno(errno,
"Cannot change owner of /dev/tty%u: %m",
int session_activate(Session *s);
bool session_is_active(Session *s);
int session_get_idle_hint(Session *s, dual_timestamp *t);
-void session_set_idle_hint(Session *s, bool b);
+int session_set_idle_hint(Session *s, bool b);
int session_get_locked_hint(Session *s);
void session_set_locked_hint(Session *s, bool b);
int session_create_fifo(Session *s);
#include <errno.h>
#include "alloc-util.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "format-util.h"
#include "logind-dbus.h"
#include "strv.h"
#include "user-util.h"
+static int property_get_uid(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ User *u = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ return sd_bus_message_append(reply, "u", (uint32_t) u->user_record->uid);
+}
+
+static int property_get_gid(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ User *u = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ return sd_bus_message_append(reply, "u", (uint32_t) u->user_record->gid);
+}
+
+static int property_get_name(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ User *u = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ return sd_bus_message_append(reply, "s", u->user_record->user_name);
+}
+
static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", User, user_get_state, user_state_to_string);
static int property_get_display(
"org.freedesktop.login1.manage",
NULL,
false,
- u->uid,
+ u->user_record->uid,
&u->manager->polkit_registry,
error);
if (r < 0)
"org.freedesktop.login1.manage",
NULL,
false,
- u->uid,
+ u->user_record->uid,
&u->manager->polkit_registry,
error);
if (r < 0)
const sd_bus_vtable user_vtable[] = {
SD_BUS_VTABLE_START(0),
- SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UID", "u", property_get_uid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("GID", "u", property_get_gid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST),
assert(u);
- if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0)
+ if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->user_record->uid) < 0)
return NULL;
return s;
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
new_user ? "UserNew" : "UserRemoved",
- "uo", (uint32_t) u->uid, p);
+ "uo", (uint32_t) u->user_record->uid, p);
}
int user_send_changed(User *u, const char *properties, ...) {
int user_new(User **ret,
Manager *m,
- uid_t uid,
- gid_t gid,
- const char *name,
- const char *home) {
+ UserRecord *ur) {
_cleanup_(user_freep) User *u = NULL;
char lu[DECIMAL_STR_MAX(uid_t) + 1];
assert(ret);
assert(m);
- assert(name);
+ assert(ur);
+
+ if (!ur->user_name)
+ return -EINVAL;
+
+ if (!uid_is_valid(ur->uid))
+ return -EINVAL;
u = new(User, 1);
if (!u)
*u = (User) {
.manager = m,
- .uid = uid,
- .gid = gid,
+ .user_record = user_record_ref(ur),
.last_session_timestamp = USEC_INFINITY,
};
- u->name = strdup(name);
- if (!u->name)
- return -ENOMEM;
-
- u->home = strdup(home);
- if (!u->home)
- return -ENOMEM;
-
- path_simplify(u->home, true);
-
- if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
+ if (asprintf(&u->state_file, "/run/systemd/users/" UID_FMT, ur->uid) < 0)
return -ENOMEM;
- if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
+ if (asprintf(&u->runtime_path, "/run/user/" UID_FMT, ur->uid) < 0)
return -ENOMEM;
- xsprintf(lu, UID_FMT, uid);
+ xsprintf(lu, UID_FMT, ur->uid);
r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
if (r < 0)
return r;
if (r < 0)
return r;
- r = hashmap_put(m->users, UID_TO_PTR(uid), u);
+ r = hashmap_put(m->users, UID_TO_PTR(ur->uid), u);
if (r < 0)
return r;
if (u->slice)
hashmap_remove_value(u->manager->user_units, u->slice, u);
- hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
+ hashmap_remove_value(u->manager->users, UID_TO_PTR(u->user_record->uid), u);
- (void) sd_event_source_unref(u->timer_event_source);
+ sd_event_source_unref(u->timer_event_source);
u->service_job = mfree(u->service_job);
u->slice = mfree(u->slice);
u->runtime_path = mfree(u->runtime_path);
u->state_file = mfree(u->state_file);
- u->name = mfree(u->name);
- u->home = mfree(u->home);
+
+ user_record_unref(u->user_record);
return mfree(u);
}
"NAME=%s\n"
"STATE=%s\n" /* friendly user-facing state */
"STOPPING=%s\n", /* low-level state */
- u->name,
+ u->user_record->user_name,
user_state_to_string(user_get_state(u)),
yes_no(u->stopping));
"Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
}
+static int update_slice_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+ _cleanup_(user_record_unrefp) UserRecord *ur = userdata;
+
+ assert(m);
+ assert(ur);
+
+ if (sd_bus_message_is_method_error(m, NULL)) {
+ log_warning_errno(sd_bus_message_get_errno(m),
+ "Failed to update slice of %s, ignoring: %s",
+ ur->user_name,
+ sd_bus_message_get_error(m)->message);
+
+ return 0;
+ }
+
+ log_debug("Successfully set slice parameters of %s.", ur->user_name);
+ return 0;
+}
+
+static int user_update_slice(User *u) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ int r;
+
+ assert(u);
+
+ if (u->user_record->tasks_max == UINT64_MAX &&
+ u->user_record->memory_high == UINT64_MAX &&
+ u->user_record->memory_max == UINT64_MAX &&
+ u->user_record->cpu_weight == UINT64_MAX &&
+ u->user_record->io_weight == UINT64_MAX)
+ return 0;
+
+ r = sd_bus_message_new_method_call(
+ u->manager->bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "SetUnitProperties");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "sb", u->slice, true);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_open_container(m, 'a', "(sv)");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ const struct {
+ const char *name;
+ uint64_t value;
+ } settings[] = {
+ { "TasksMax", u->user_record->tasks_max },
+ { "MemoryMax", u->user_record->memory_max },
+ { "MemoryHigh", u->user_record->memory_high },
+ { "CPUWeight", u->user_record->cpu_weight },
+ { "IOWeight", u->user_record->io_weight },
+ };
+
+ for (size_t i = 0; i < ELEMENTSOF(settings); i++)
+ if (settings[i].value != UINT64_MAX) {
+ r = sd_bus_message_append(m, "(sv)", settings[i].name, "t", settings[i].value);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call_async(u->manager->bus, NULL, m, update_slice_callback, u->user_record, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change user slice properties: %m");
+
+ /* Ref the user record pointer, so that the slot keeps it pinned */
+ user_record_ref(u->user_record);
+
+ return 0;
+}
+
int user_start(User *u) {
assert(u);
u->stopping = false;
if (!u->started)
- log_debug("Starting services for new user %s.", u->name);
+ log_debug("Starting services for new user %s.", u->user_record->user_name);
/* Save the user data so far, because pam_systemd will read the XDG_RUNTIME_DIR out of it while starting up
* systemd --user. We need to do user_save_internal() because we have not "officially" started yet. */
user_save_internal(u);
+ /* Set slice parameters */
+ (void) user_update_slice(u);
+
/* Start user@UID.service */
user_start_service(u);
* done. This is called as a result of an earlier user_done() when all jobs are completed. */
if (u->started)
- log_debug("User %s logged out.", u->name);
+ log_debug("User %s logged out.", u->user_record->user_name);
LIST_FOREACH(sessions_by_user, s, u->sessions) {
k = session_finalize(s);
* cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
* a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
* and do it only for normal users. */
- if (u->manager->remove_ipc && !uid_is_system(u->uid)) {
- k = clean_ipc_by_uid(u->uid);
+ if (u->manager->remove_ipc && !uid_is_system(u->user_record->uid)) {
+ k = clean_ipc_by_uid(u->user_record->uid);
if (k < 0)
r = k;
}
_cleanup_free_ char *cc = NULL;
char *p = NULL;
- cc = cescape(u->name);
+ cc = cescape(u->user_record->user_name);
if (!cc)
return -ENOMEM;
return false;
}
+static usec_t user_get_stop_delay(User *u) {
+ assert(u);
+
+ if (u->user_record->stop_delay_usec != UINT64_MAX)
+ return u->user_record->stop_delay_usec;
+
+ if (user_record_removable(u->user_record) > 0)
+ return 0; /* For removable users lower the stop delay to zero */
+
+ return u->manager->user_stop_delay;
+}
+
bool user_may_gc(User *u, bool drop_not_started) {
int r;
return false;
if (u->last_session_timestamp != USEC_INFINITY) {
+ usec_t user_stop_delay;
+
/* All sessions have been closed. Let's see if we shall leave the user record around for a bit */
- if (u->manager->user_stop_delay == USEC_INFINITY)
+ user_stop_delay = user_get_stop_delay(u);
+
+ if (user_stop_delay == USEC_INFINITY)
return false; /* Leave it around forever! */
- if (u->manager->user_stop_delay > 0 &&
- now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, u->manager->user_stop_delay))
+ if (user_stop_delay > 0 &&
+ now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, user_stop_delay))
return false; /* Leave it around for a bit longer. */
}
/* This elects a primary session for each user, which we call the "display". We try to keep the assignment
* stable, but we "upgrade" to better choices. */
- log_debug("Electing new display for user %s", u->name);
+ log_debug("Electing new display for user %s", u->user_record->user_name);
LIST_FOREACH(sessions_by_user, s, u->sessions) {
if (!elect_display_filter(s)) {
}
void user_update_last_session_timer(User *u) {
+ usec_t user_stop_delay;
int r;
assert(u);
assert(!u->timer_event_source);
- if (IN_SET(u->manager->user_stop_delay, 0, USEC_INFINITY))
+ user_stop_delay = user_get_stop_delay(u);
+ if (IN_SET(user_stop_delay, 0, USEC_INFINITY))
return;
if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) {
r = sd_event_add_time(u->manager->event,
&u->timer_event_source,
CLOCK_MONOTONIC,
- usec_add(u->last_session_timestamp, u->manager->user_stop_delay), 0,
+ usec_add(u->last_session_timestamp, user_stop_delay), 0,
user_stop_timeout_callback, u);
if (r < 0)
log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m");
char s[FORMAT_TIMESPAN_MAX];
log_debug("Last session of user '%s' logged out, terminating user context in %s.",
- u->name,
- format_timespan(s, sizeof(s), u->manager->user_stop_delay, USEC_PER_MSEC));
+ u->user_record->user_name,
+ format_timespan(s, sizeof(s), user_stop_delay, USEC_PER_MSEC));
}
}
#include "conf-parser.h"
#include "list.h"
#include "logind.h"
+#include "user-record.h"
typedef enum UserState {
USER_OFFLINE, /* Not logged in at all */
struct User {
Manager *manager;
- uid_t uid;
- gid_t gid;
- char *name;
- char *home;
+
+ UserRecord *user_record;
+
char *state_file;
char *runtime_path;
LIST_FIELDS(User, gc_queue);
};
-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name, const char *home);
+int user_new(User **out, Manager *m, UserRecord *ur);
User *user_free(User *u);
DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
#include "alloc-util.h"
#include "bus-error.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "cgroup-util.h"
#include "def.h"
#include "device-util.h"
#include "list.h"
#include "set.h"
#include "time-util.h"
+#include "user-record.h"
typedef struct Manager Manager;
Hashmap *seats;
Hashmap *sessions;
Hashmap *sessions_by_leader;
- Hashmap *users;
+ Hashmap *users; /* indexed by UID */
Hashmap *inhibitors;
Hashmap *buttons;
Hashmap *brightness_writers;
int manager_add_button(Manager *m, const char *name, Button **ret_button);
int manager_add_seat(Manager *m, const char *id, Seat **ret_seat);
int manager_add_session(Manager *m, const char *id, Session **ret_session);
-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **ret_user);
+int manager_add_user(Manager *m, UserRecord *ur, User **ret_user);
int manager_add_user_by_name(Manager *m, const char *name, User **ret_user);
int manager_add_user_by_uid(Manager *m, uid_t uid, User **ret_user);
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **ret_inhibitor);
<action id="org.freedesktop.login1.attach-device">
<description gettext-domain="systemd">Allow attaching devices to seats</description>
- <message gettext-domain="systemd">Authentication is required for attaching a device to a seat.</message>
+ <message gettext-domain="systemd">Authentication is required to attach a device to a seat.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.flush-devices">
<description gettext-domain="systemd">Flush device to seat attachments</description>
- <message gettext-domain="systemd">Authentication is required for resetting how devices are attached to seats.</message>
+ <message gettext-domain="systemd">Authentication is required to reset how devices are attached to seats.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.power-off">
<description gettext-domain="systemd">Power off the system</description>
- <message gettext-domain="systemd">Authentication is required for powering off the system.</message>
+ <message gettext-domain="systemd">Authentication is required to power off the system.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.power-off-multiple-sessions">
<description gettext-domain="systemd">Power off the system while other users are logged in</description>
- <message gettext-domain="systemd">Authentication is required for powering off the system while other users are logged in.</message>
+ <message gettext-domain="systemd">Authentication is required to power off the system while other users are logged in.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
</action>
<action id="org.freedesktop.login1.power-off-ignore-inhibit">
- <description gettext-domain="systemd">Power off the system while an application asked to inhibit it</description>
- <message gettext-domain="systemd">Authentication is required for powering off the system while an application asked to inhibit it.</message>
+ <description gettext-domain="systemd">Power off the system while an application is inhibiting this</description>
+ <message gettext-domain="systemd">Authentication is required to power off the system while an application is inhibiting this.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.reboot">
<description gettext-domain="systemd">Reboot the system</description>
- <message gettext-domain="systemd">Authentication is required for rebooting the system.</message>
+ <message gettext-domain="systemd">Authentication is required to reboot the system.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.reboot-multiple-sessions">
<description gettext-domain="systemd">Reboot the system while other users are logged in</description>
- <message gettext-domain="systemd">Authentication is required for rebooting the system while other users are logged in.</message>
+ <message gettext-domain="systemd">Authentication is required to reboot the system while other users are logged in.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
</action>
<action id="org.freedesktop.login1.reboot-ignore-inhibit">
- <description gettext-domain="systemd">Reboot the system while an application asked to inhibit it</description>
- <message gettext-domain="systemd">Authentication is required for rebooting the system while an application asked to inhibit it.</message>
+ <description gettext-domain="systemd">Reboot the system while an application is inhibiting this</description>
+ <message gettext-domain="systemd">Authentication is required to reboot the system while an application is inhibiting this.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.halt">
<description gettext-domain="systemd">Halt the system</description>
- <message gettext-domain="systemd">Authentication is required for halting the system.</message>
+ <message gettext-domain="systemd">Authentication is required to halt the system.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.halt-multiple-sessions">
<description gettext-domain="systemd">Halt the system while other users are logged in</description>
- <message gettext-domain="systemd">Authentication is required for halting the system while other users are logged in.</message>
+ <message gettext-domain="systemd">Authentication is required to halt the system while other users are logged in.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
</action>
<action id="org.freedesktop.login1.halt-ignore-inhibit">
- <description gettext-domain="systemd">Halt the system while an application asked to inhibit it</description>
- <message gettext-domain="systemd">Authentication is required for halting the system while an application asked to inhibit it.</message>
+ <description gettext-domain="systemd">Halt the system while an application is inhibiting this</description>
+ <message gettext-domain="systemd">Authentication is required to halt the system while an application is inhibiting this.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.suspend">
<description gettext-domain="systemd">Suspend the system</description>
- <message gettext-domain="systemd">Authentication is required for suspending the system.</message>
+ <message gettext-domain="systemd">Authentication is required to suspend the system.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.suspend-multiple-sessions">
<description gettext-domain="systemd">Suspend the system while other users are logged in</description>
- <message gettext-domain="systemd">Authentication is required for suspending the system while other users are logged in.</message>
+ <message gettext-domain="systemd">Authentication is required to suspend the system while other users are logged in.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
</action>
<action id="org.freedesktop.login1.suspend-ignore-inhibit">
- <description gettext-domain="systemd">Suspend the system while an application asked to inhibit it</description>
- <message gettext-domain="systemd">Authentication is required for suspending the system while an application asked to inhibit it.</message>
+ <description gettext-domain="systemd">Suspend the system while an application is inhibiting this</description>
+ <message gettext-domain="systemd">Authentication is required to suspend the system while an application is inhibiting this.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.hibernate">
<description gettext-domain="systemd">Hibernate the system</description>
- <message gettext-domain="systemd">Authentication is required for hibernating the system.</message>
+ <message gettext-domain="systemd">Authentication is required to hibernate the system.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.hibernate-multiple-sessions">
<description gettext-domain="systemd">Hibernate the system while other users are logged in</description>
- <message gettext-domain="systemd">Authentication is required for hibernating the system while other users are logged in.</message>
+ <message gettext-domain="systemd">Authentication is required to hibernate the system while other users are logged in.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
</action>
<action id="org.freedesktop.login1.hibernate-ignore-inhibit">
- <description gettext-domain="systemd">Hibernate the system while an application asked to inhibit it</description>
- <message gettext-domain="systemd">Authentication is required for hibernating the system while an application asked to inhibit it.</message>
+ <description gettext-domain="systemd">Hibernate the system while an application is inhibiting this</description>
+ <message gettext-domain="systemd">Authentication is required to hibernate the system while an application is inhibiting this.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<action id="org.freedesktop.login1.manage">
<description gettext-domain="systemd">Manage active sessions, users and seats</description>
- <message gettext-domain="systemd">Authentication is required for managing active sessions, users and seats.</message>
+ <message gettext-domain="systemd">Authentication is required to manage active sessions, users and seats.</message>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
</defaults>
</action>
+ <action id="org.freedesktop.login1.chvt">
+ <description gettext-domain="systemd">Change Session</description>
+ <message gettext-domain="systemd">Authentication is required to change the virtual terminal.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
</policyconfig>
#include <security/pam_modutil.h>
#include <sys/file.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "fs-util.h"
#include "hostname-util.h"
+#include "locale-util.h"
#include "login-util.h"
#include "macro.h"
+#include "pam-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "rlimit-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "user-util.h"
+#include "userdb.h"
#define LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
return 0;
}
-static int get_user_data(
+static int acquire_user_record(
pam_handle_t *handle,
- const char **ret_username,
- struct passwd **ret_pw) {
+ UserRecord **ret_record) {
- const char *username = NULL;
- struct passwd *pw = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ const char *username = NULL, *json = NULL;
int r;
assert(handle);
- assert(ret_username);
- assert(ret_pw);
r = pam_get_user(handle, &username, NULL);
if (r != PAM_SUCCESS) {
if (isempty(username)) {
pam_syslog(handle, LOG_ERR, "User name not valid.");
- return PAM_AUTH_ERR;
+ return PAM_SERVICE_ERR;
}
- pw = pam_modutil_getpwnam(handle, username);
- if (!pw) {
- pam_syslog(handle, LOG_ERR, "Failed to get user data.");
- return PAM_USER_UNKNOWN;
+ /* If pam_systemd_homed (or some other module) already acqired the user record we can reuse it
+ * here. */
+ r = pam_get_data(handle, "systemd-user-record", (const void**) &json);
+ if (r != PAM_SUCCESS || !json) {
+ _cleanup_free_ char *formatted = NULL;
+
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ /* Request the record ourselves */
+ r = userdb_by_name(username, 0, &ur);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r));
+ return PAM_USER_UNKNOWN;
+ }
+
+ r = json_variant_format(ur->json, 0, &formatted);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ /* And cache it for everyone else */
+ r = pam_set_data(handle, "systemd-user-record", formatted, pam_cleanup_free);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ TAKE_PTR(formatted);
+ } else {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ /* Parse cached record */
+ r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to parse JSON user record: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ ur = user_record_new();
+ if (!ur)
+ return pam_log_oom(handle);
+
+ r = user_record_load(ur, v, USER_RECORD_LOAD_REFUSE_SECRET);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to load user record: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ /* Safety check if cached record actually matches what we are looking for */
+ if (!streq_ptr(username, ur->user_name)) {
+ pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
+ return PAM_SERVICE_ERR;
+ }
+ }
+
+ if (!uid_is_valid(ur->uid)) {
+ pam_syslog(handle, LOG_ERR, "Acquired user record does not have a UID.");
+ return PAM_SERVICE_ERR;
}
- *ret_pw = pw;
- *ret_username = username;
+ if (ret_record)
+ *ret_record = TAKE_PTR(ur);
return PAM_SUCCESS;
}
}
static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
- union sockaddr_union sa = {};
- _cleanup_free_ char *p = NULL, *tty = NULL;
+ union sockaddr_union sa;
+ socklen_t sa_len;
+ _cleanup_free_ char *p = NULL, *sys_path = NULL, *tty = NULL;
_cleanup_close_ int fd = -1;
struct ucred ucred;
- int v, r, salen;
+ int v, r;
+ dev_t display_ctty;
assert(display);
assert(vtnr);
r = socket_from_display(display, &p);
if (r < 0)
return r;
- salen = sockaddr_un_set_path(&sa.un, p);
- if (salen < 0)
- return salen;
+ r = sockaddr_un_set_path(&sa.un, p);
+ if (r < 0)
+ return r;
+ sa_len = r;
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
- if (connect(fd, &sa.sa, salen) < 0)
+ if (connect(fd, &sa.sa, sa_len) < 0)
return -errno;
r = getpeercred(fd, &ucred);
if (r < 0)
return r;
- r = get_ctty(ucred.pid, NULL, &tty);
+ r = get_ctty_devnr(ucred.pid, &display_ctty);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&sys_path, "/sys/dev/char/%d:%d", major(display_ctty), minor(display_ctty)) < 0)
+ return -ENOMEM;
+ r = readlink_value(sys_path, &tty);
if (r < 0)
return r;
return PAM_SUCCESS;
if (asprintf(&t, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
- goto error;
+ return pam_log_oom(handle);
r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", t, 0);
- if (r != PAM_SUCCESS)
- goto error;
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set bus variable: %s", pam_strerror(handle, r));
+ return r;
+ }
return PAM_SUCCESS;
-
-error:
- pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
- return r;
}
static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
int r;
if (isempty(limit))
- return 0;
+ return PAM_SUCCESS;
if (streq(limit, "infinity")) {
r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", (uint64_t)-1);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
- return r;
- }
- } else {
- r = parse_permille(limit);
- if (r >= 0) {
- r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
- return r;
- }
- } else {
- r = parse_size(limit, 1024, &val);
- if (r >= 0) {
- r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
- return r;
- }
- } else
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max: %s, ignoring.", limit);
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ return PAM_SUCCESS;
}
- return 0;
+ r = parse_permille(limit);
+ if (r >= 0) {
+ r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ return PAM_SUCCESS;
+ }
+
+ r = parse_size(limit, 1024, &val);
+ if (r >= 0) {
+ r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ return PAM_SUCCESS;
+ }
+
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit);
+ return PAM_SUCCESS;
}
static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
/* No need to parse "infinity" here, it will be set by default later in scope_init() */
if (isempty(limit) || streq(limit, "infinity"))
- return 0;
+ return PAM_SUCCESS;
r = parse_sec(limit, &val);
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
- return r;
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
} else
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit);
- return 0;
+ return PAM_SUCCESS;
}
static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
/* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
if (isempty(limit) || streq(limit, "infinity"))
- return 0;
+ return PAM_SUCCESS;
r = safe_atou64(limit, &val);
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
- return r;
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
} else
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max: %s, ignoring.", limit);
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit);
- return 0;
+ return PAM_SUCCESS;
}
static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) {
int r;
if (isempty(limit))
- return 0;
+ return PAM_SUCCESS;
r = cg_weight_parse(limit, &val);
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", field, "t", val);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
- return r;
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
} else if (streq(field, "CPUWeight"))
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit);
else
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit);
- return 0;
+ return PAM_SUCCESS;
}
static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
return false;
}
+static int pam_putenv_and_log(pam_handle_t *handle, const char *e, bool debug) {
+ int r;
+
+ assert(handle);
+ assert(e);
+
+ r = pam_putenv(handle, e);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable %s: %s", e, pam_strerror(handle, r));
+ return r;
+ }
+
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PAM environment variable %s set based on user record.", e);
+
+ return PAM_SUCCESS;
+}
+
+static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool debug) {
+ char **i;
+ int r;
+
+ assert(handle);
+ assert(ur);
+
+ if (ur->umask != MODE_INVALID) {
+ umask(ur->umask);
+
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Set user umask to %04o based on user record.", ur->umask);
+ }
+
+ STRV_FOREACH(i, ur->environment) {
+ _cleanup_free_ char *n = NULL;
+ const char *e;
+
+ assert_se(e = strchr(*i, '=')); /* environment was already validated while parsing JSON record, this thus must hold */
+
+ n = strndup(*i, e - *i);
+ if (!n)
+ return pam_log_oom(handle);
+
+ if (pam_getenv(handle, n)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PAM environment variable $%s already set, not changing based on record.", *i);
+ continue;
+ }
+
+ r = pam_putenv_and_log(handle, *i, debug);
+ if (r != PAM_SUCCESS)
+ return r;
+ }
+
+ if (ur->email_address) {
+ if (pam_getenv(handle, "EMAIL")) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PAM environment variable $EMAIL already set, not changing based on user record.");
+ } else {
+ _cleanup_free_ char *joined = NULL;
+
+ joined = strjoin("EMAIL=", ur->email_address);
+ if (!joined)
+ return pam_log_oom(handle);
+
+ r = pam_putenv_and_log(handle, joined, debug);
+ if (r != PAM_SUCCESS)
+ return r;
+ }
+ }
+
+ if (ur->time_zone) {
+ if (pam_getenv(handle, "TZ")) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PAM environment variable $TZ already set, not changing based on user record.");
+ } else if (!timezone_is_valid(ur->time_zone, LOG_DEBUG)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Time zone specified in user record is not valid locally, not setting $TZ.");
+ } else {
+ _cleanup_free_ char *joined = NULL;
+
+ joined = strjoin("TZ=:", ur->time_zone);
+ if (!joined)
+ return pam_log_oom(handle);
+
+ r = pam_putenv_and_log(handle, joined, debug);
+ if (r != PAM_SUCCESS)
+ return r;
+ }
+ }
+
+ if (ur->preferred_language) {
+ if (pam_getenv(handle, "LANG")) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "PAM environment variable $LANG already set, not changing based on user record.");
+ } else if (!locale_is_valid(ur->preferred_language)) {
+ if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid locally, not setting $LANG.");
+ } else {
+ _cleanup_free_ char *joined = NULL;
+
+ joined = strjoin("LANG=", ur->preferred_language);
+ if (!joined)
+ return pam_log_oom(handle);
+
+ r = pam_putenv_and_log(handle, joined, debug);
+ if (r != PAM_SUCCESS)
+ return r;
+ }
+ }
+
+ if (nice_is_valid(ur->nice_level)) {
+ if (nice(ur->nice_level) < 0)
+ pam_syslog(handle, LOG_ERR, "Failed to set nice level to %i, ignoring: %s", ur->nice_level, strerror_safe(errno));
+ else if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Nice level set, based on user record.");
+ }
+
+ for (int rl = 0; rl < _RLIMIT_MAX; rl++) {
+
+ if (!ur->rlimits[rl])
+ continue;
+
+ r = setrlimit_closest(rl, ur->rlimits[rl]);
+ if (r < 0)
+ pam_syslog(handle, LOG_ERR, "Failed to set resource limit %s, ignoring: %s", rlimit_to_string(rl), strerror_safe(r));
+ else if (debug)
+ pam_syslog(handle, LOG_DEBUG, "Resource limit %s set, based on user record.", rlimit_to_string(rl));
+ }
+
+ return PAM_SUCCESS;
+}
+
_public_ PAM_EXTERN int pam_sm_open_session(
pam_handle_t *handle,
int flags,
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
const char
- *username, *id, *object_path, *runtime_path,
+ *id, *object_path, *runtime_path,
*service = NULL,
*tty = NULL, *display = NULL,
*remote_user = NULL, *remote_host = NULL,
*class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
*memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL, *runtime_max_sec = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
int session_fd = -1, existing, r;
bool debug = false, remote;
- struct passwd *pw;
uint32_t vtnr = 0;
uid_t original_uid;
if (debug)
pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
- r = get_user_data(handle, &username, &pw);
+ r = acquire_user_record(handle, &ur);
if (r != PAM_SUCCESS)
return r;
if (streq_ptr(service, "systemd-user")) {
char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
- xsprintf(rt, "/run/user/"UID_FMT, pw->pw_uid);
- if (validate_runtime_directory(handle, rt, pw->pw_uid)) {
+ xsprintf(rt, "/run/user/"UID_FMT, ur->uid);
+ if (validate_runtime_directory(handle, rt, ur->uid)) {
r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
if (r != PAM_SUCCESS) {
pam_syslog(handle, LOG_ERR, "Failed to set runtime dir: %s", pam_strerror(handle, r));
}
}
- r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
+ r = export_legacy_dbus_address(handle, ur->uid, rt);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ r = apply_user_record_settings(handle, ur, debug);
if (r != PAM_SUCCESS)
return r;
/* Talk to logind over the message bus */
- r = sd_bus_open_system(&bus);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
- return PAM_SESSION_ERR;
- }
+ r = pam_acquire_bus_connection(handle, &bus);
+ if (r != PAM_SUCCESS)
+ return r;
if (debug) {
pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
"uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
- pw->pw_uid, getpid_cached(),
+ ur->uid, getpid_cached(),
strempty(service),
type, class, strempty(desktop),
strempty(seat), vtnr, strempty(tty), strempty(display),
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"CreateSession");
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror_safe(r));
- return PAM_SESSION_ERR;
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
r = sd_bus_message_append(m, "uusssssussbss",
- (uint32_t) pw->pw_uid,
+ (uint32_t) ur->uid,
0,
service,
type,
remote,
remote_user,
remote_host);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
- return PAM_SESSION_ERR;
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
r = sd_bus_message_open_container(m, 'a', "(sv)");
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror_safe(r));
- return PAM_SYSTEM_ERR;
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
r = append_session_memory_max(handle, m, memory_max);
- if (r < 0)
- return PAM_SESSION_ERR;
+ if (r != PAM_SUCCESS)
+ return r;
r = append_session_runtime_max_sec(handle, m, runtime_max_sec);
- if (r < 0)
- return PAM_SESSION_ERR;
+ if (r != PAM_SUCCESS)
+ return r;
r = append_session_tasks_max(handle, m, tasks_max);
- if (r < 0)
- return PAM_SESSION_ERR;
+ if (r != PAM_SUCCESS)
+ return r;
r = append_session_cg_weight(handle, m, cpu_weight, "CPUWeight");
- if (r < 0)
- return PAM_SESSION_ERR;
+ if (r != PAM_SUCCESS)
+ return r;
r = append_session_cg_weight(handle, m, io_weight, "IOWeight");
- if (r < 0)
- return PAM_SESSION_ERR;
+ if (r != PAM_SUCCESS)
+ return r;
r = sd_bus_message_close_container(m);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror_safe(r));
- return PAM_SYSTEM_ERR;
- }
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
&seat,
&vtnr,
&existing);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror_safe(r));
- return PAM_SESSION_ERR;
- }
+ if (r < 0)
+ return pam_bus_log_parse_error(handle, r);
if (debug)
pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
if (r != PAM_SUCCESS)
return r;
- if (original_uid == pw->pw_uid) {
+ if (original_uid == ur->uid) {
/* Don't set $XDG_RUNTIME_DIR if the user we now
* authenticated for does not match the original user
* of the session. We do this in order not to result
* in privileged apps clobbering the runtime directory
* unnecessarily. */
- if (validate_runtime_directory(handle, runtime_path, pw->pw_uid)) {
+ if (validate_runtime_directory(handle, runtime_path, ur->uid)) {
r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
if (r != PAM_SUCCESS)
return r;
}
- r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
+ r = export_legacy_dbus_address(handle, ur->uid, runtime_path);
if (r != PAM_SUCCESS)
return r;
}
}
}
+ r = apply_user_record_settings(handle, ur, debug);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
+ * not going to use the bus connection in that time, so let's better close before the daemon kicks us
+ * off because we are not processing anything. */
+ (void) pam_release_bus_connection(handle);
return PAM_SUCCESS;
}
int flags,
int argc, const char **argv) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
const void *existing = NULL;
const char *id;
int r;
id = pam_getenv(handle, "XDG_SESSION_ID");
if (id && !existing) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- /* Before we go and close the FIFO we need to tell
- * logind that this is a clean session shutdown, so
- * that it doesn't just go and slaughter us
- * immediately after closing the fd */
+ /* Before we go and close the FIFO we need to tell logind that this is a clean session
+ * shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
- r = sd_bus_open_system(&bus);
- if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
- return PAM_SESSION_ERR;
- }
+ r = pam_acquire_bus_connection(handle, &bus);
+ if (r != PAM_SUCCESS)
+ return r;
r = sd_bus_call_method(bus,
"org.freedesktop.login1",
}
}
- /* Note that we are knowingly leaking the FIFO fd here. This
- * way, logind can watch us die. If we closed it here it would
- * not have any clue when that is completed. Given that one
- * cannot really have multiple PAM sessions open from the same
- * process this means we will leak one FD at max. */
+ /* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we
+ * closed it here it would not have any clue when that is completed. Given that one cannot really
+ * have multiple PAM sessions open from the same process this means we will leak one FD at max. */
return PAM_SUCCESS;
}
#include "alloc-util.h"
#include "bus-label.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "copy.h"
#include "dissect-image.h"
#include "bus-common-errors.h"
#include "bus-internal.h"
#include "bus-label.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "copy.h"
#include "env-file.h"
show_journal_by_unit(
stdout,
i->unit,
+ NULL,
arg_output,
0,
i->timestamp.monotonic,
#include "alloc-util.h"
#include "btrfs-util.h"
#include "bus-common-errors.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "cgroup-util.h"
#include "errno-util.h"
#include "alloc-util.h"
#include "bus-error.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "cgroup-util.h"
#include "dirent-util.h"
#include "fd-util.h"
if (arg_full)
table_set_width(table, 0);
- r = table_set_sort(table, 0, SIZE_MAX);
+ r = table_set_sort(table, (size_t) 0, (size_t) SIZE_MAX);
if (r < 0)
return log_error_errno(r, "Failed to set sort index: %m");
networkd-util.h
networkd-wifi.c
networkd-wifi.h
+ tc/cake.c
+ tc/cake.h
tc/codel.c
tc/codel.h
+ tc/fifo.c
+ tc/fifo.h
tc/fq.c
tc/fq.h
tc/fq-codel.c
tc/fq-codel.h
+ tc/gred.c
+ tc/gred.h
+ tc/htb.c
+ tc/htb.h
tc/netem.c
tc/netem.h
tc/qdisc.c
tc/qdisc.h
+ tc/sfb.c
+ tc/sfb.h
tc/sfq.c
tc/sfq.h
tc/tbf.c
tc/tbf.h
tc/tc-util.c
tc/tc-util.h
+ tc/tc.c
+ tc/tc.h
+ tc/tclass.c
+ tc/tclass.h
+ tc/teql.c
+ tc/teql.h
'''.split())
systemd_networkd_sources = files('networkd.c')
assert(t);
t->pmtudisc = true;
- t->fou_encap_type = FOU_ENCAP_DIRECT;
+ t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
t->isatap = -1;
}
t->pmtudisc = true;
t->gre_erspan_sequence = -1;
- t->fou_encap_type = FOU_ENCAP_DIRECT;
+ t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
}
static void ip6gre_init(NetDev *n) {
#include "sd-network.h"
#include "alloc-util.h"
-#include "arphrd-list.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "format-util.h"
+#include "glob-util.h"
#include "hwdb-util.h"
#include "local-addresses.h"
#include "locale-util.h"
#include "macro.h"
#include "main-func.h"
#include "netlink-util.h"
+#include "network-internal.h"
#include "pager.h"
#include "parse-util.h"
#include "pretty-print.h"
static bool arg_full = false;
static unsigned arg_lines = 10;
-static char *link_get_type_string(unsigned short iftype, sd_device *d) {
- const char *t, *devtype;
- char *p;
-
- if (d &&
- sd_device_get_devtype(d, &devtype) >= 0 &&
- !isempty(devtype))
- return strdup(devtype);
-
- t = arphrd_to_name(iftype);
- if (!t)
- return NULL;
-
- p = strdup(t);
- if (!p)
- return NULL;
-
- ascii_strlower(p);
- return p;
-}
-
static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
assert(on);
assert(off);
/* ethtool info */
int autonegotiation;
- size_t speed;
+ uint64_t speed;
Duplex duplex;
NetDevPort port;
return 0;
}
-static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
+static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) {
_cleanup_strv_free_ char **altnames = NULL;
const char *name;
int ifindex, r;
if (patterns) {
char str[DECIMAL_STR_MAX(int)];
+ size_t pos;
+
+ assert(matched_patterns);
xsprintf(str, "%i", ifindex);
- if (!strv_fnmatch(patterns, str, 0) && !strv_fnmatch(patterns, name, 0)) {
+ if (!strv_fnmatch_full(patterns, str, 0, &pos) &&
+ !strv_fnmatch_full(patterns, name, 0, &pos)) {
bool match = false;
char **p;
STRV_FOREACH(p, altnames)
- if (strv_fnmatch(patterns, *p, 0)) {
+ if (strv_fnmatch_full(patterns, *p, 0, &pos)) {
match = true;
break;
}
if (!match)
return 0;
}
+
+ matched_patterns[pos] = true;
}
r = sd_rtnl_message_link_get_type(m, &info->iftype);
if (r < 0)
return log_error_errno(r, "Failed to enumerate links: %m");
+ _cleanup_free_ bool *matched_patterns = NULL;
+ if (patterns) {
+ matched_patterns = new0(bool, strv_length(patterns));
+ if (!matched_patterns)
+ return log_oom();
+ }
+
for (i = reply; i; i = sd_netlink_message_next(i)) {
if (!GREEDY_REALLOC0(links, allocated, c + 2)) /* We keep one trailing one as marker */
return -ENOMEM;
- r = decode_link(i, links + c, patterns);
+ r = decode_link(i, links + c, patterns, matched_patterns);
if (r < 0)
return r;
if (r == 0)
c++;
}
+ /* Look if we matched all our arguments that are not globs. It
+ * is OK for a glob to match nothing, but not for an exact argument. */
+ for (size_t pos = 0; pos < strv_length(patterns); pos++) {
+ if (matched_patterns[pos])
+ continue;
+
+ if (string_is_glob(patterns[pos]))
+ log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
+ patterns[pos]);
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+ "Interface \"%s\" not found.", patterns[pos]);
+ }
+
typesafe_qsort(links, c, link_info_compare);
if (bus)
*ret = TAKE_PTR(links);
+ if (patterns && c == 0)
+ log_warning("No interfaces matched.");
+
return (int) c;
}
return -ENODATA;
}
+static int dump_list(Table *table, const char *prefix, char * const *l) {
+ int r;
+
+ if (strv_isempty(l))
+ return 0;
+
+ r = table_add_many(table,
+ TABLE_EMPTY,
+ TABLE_STRING, prefix,
+ TABLE_STRV, l);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ return 0;
+}
+
static int dump_gateways(
sd_netlink *rtnl,
sd_hwdb *hwdb,
Table *table,
int ifindex) {
_cleanup_free_ struct local_address *local = NULL;
+ _cleanup_strv_free_ char **buf = NULL;
int r, n, i;
assert(rtnl);
assert(table);
n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
- if (n < 0)
+ if (n <= 0)
return n;
for (i = 0; i < n; i++) {
_cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL;
-
- r = table_add_many(table,
- TABLE_EMPTY,
- TABLE_STRING, i == 0 ? "Gateway:" : "");
- if (r < 0)
- return table_log_add_error(r);
+ char name[IF_NAMESIZE+1];
r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
if (r < 0)
r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
if (r < 0)
- log_debug_errno(r, "Could not get description of gateway: %m");
+ log_debug_errno(r, "Could not get description of gateway, ignoring: %m");
if (description) {
with_description = strjoin(gateway, " (", description, ")");
if (!with_description)
- return -ENOMEM;
+ return log_oom();
}
- /* Show interface name for the entry if we show
- * entries for all interfaces */
- if (ifindex <= 0) {
- char name[IF_NAMESIZE+1];
-
- r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway,
- format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
- } else
- r = table_add_cell(table, NULL, TABLE_STRING, with_description ?: gateway);
+ /* Show interface name for the entry if we show entries for all interfaces */
+ r = strv_extendf(&buf, "%s%s%s",
+ with_description ?: gateway,
+ ifindex <= 0 ? " on " : "",
+ ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
if (r < 0)
- return table_log_add_error(r);
+ return log_oom();
}
- return 0;
+ return dump_list(table, "Gateway:", buf);
}
static int dump_addresses(
_cleanup_free_ struct local_address *local = NULL;
_cleanup_free_ char *dhcp4_address = NULL;
+ _cleanup_strv_free_ char **buf = NULL;
int r, n, i;
assert(rtnl);
assert(table);
n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
- if (n < 0)
+ if (n <= 0)
return n;
(void) sd_network_link_get_dhcp4_address(ifindex, &dhcp4_address);
for (i = 0; i < n; i++) {
_cleanup_free_ char *pretty = NULL;
-
- r = table_add_many(table,
- TABLE_EMPTY,
- TABLE_STRING, i == 0 ? "Address:" : "");
- if (r < 0)
- return table_log_add_error(r);
+ char name[IF_NAMESIZE+1];
r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
if (r < 0)
return log_oom();
}
- if (ifindex <= 0) {
- char name[IF_NAMESIZE+1];
-
- r = table_add_cell_stringf(table, NULL, "%s on %s", pretty,
- format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
- } else
- r = table_add_cell(table, NULL, TABLE_STRING, pretty);
+ r = strv_extendf(&buf, "%s%s%s",
+ pretty,
+ ifindex <= 0 ? " on " : "",
+ ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
if (r < 0)
- return table_log_add_error(r);
+ return log_oom();
}
- return 0;
+ return dump_list(table, "Address:", buf);
}
static int dump_address_labels(sd_netlink *rtnl) {
if (arg_full)
table_set_width(table, 0);
- r = table_set_sort(table, 0, SIZE_MAX);
+ r = table_set_sort(table, (size_t) 0, (size_t) SIZE_MAX);
if (r < 0)
return r;
}
static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
+ _cleanup_strv_free_ char **buf = NULL;
_cleanup_fclose_ FILE *f = NULL;
- int r, c = 0;
+ int r;
assert(table);
assert(prefix);
if (r == 0)
break;
- r = table_add_many(table,
- TABLE_EMPTY,
- TABLE_STRING, c == 0 ? prefix : "");
- if (r < 0)
- return table_log_add_error(r);
-
(void) sd_lldp_neighbor_get_system_name(n, &system_name);
(void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
(void) sd_lldp_neighbor_get_port_description(n, &port_description);
- r = table_add_cell_stringf(table, NULL,
- "%s on port %s%s%s%s",
- strna(system_name), strna(port_id),
- isempty(port_description) ? "" : " (",
- strempty(port_description),
- isempty(port_description) ? "" : ")");
+ r = strv_extendf(&buf, "%s on port %s%s%s%s",
+ strna(system_name),
+ strna(port_id),
+ isempty(port_description) ? "" : " (",
+ strempty(port_description),
+ isempty(port_description) ? "" : ")");
if (r < 0)
- return table_log_add_error(r);
-
- c++;
+ return log_oom();
}
- return c;
+ return dump_list(table, prefix, buf);
}
static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
return 0;
}
-static int dump_list(Table *table, const char *prefix, char **l) {
- char **i;
- int r;
-
- if (strv_isempty(l))
- return 0;
-
- STRV_FOREACH(i, l) {
- r = table_add_many(table,
- TABLE_EMPTY,
- TABLE_STRING, i == l ? prefix : "",
- TABLE_STRING, *i);
- if (r < 0)
- return table_log_add_error(r);
- }
-
- return 0;
-}
-
#define DUMP_STATS_ONE(name, val_name) \
r = table_add_many(table, \
TABLE_EMPTY, \
_cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
_cleanup_(table_unrefp) Table *table = NULL;
TableCell *cell;
- char **p;
int r;
assert(rtnl);
if (r < 0)
return table_log_add_error(r);
- STRV_FOREACH(p, info->alternative_names) {
- if (p == info->alternative_names)
- r = table_add_many(table,
- TABLE_EMPTY,
- TABLE_STRING, "Alternative Names:",
- TABLE_STRING, *p);
- else
- r = table_add_many(table,
- TABLE_EMPTY,
- TABLE_EMPTY,
- TABLE_STRING, *p);
- if (r < 0)
- return table_log_add_error(r);
- }
+ r = dump_list(table, "Alternative Names:", info->alternative_names);
+ if (r < 0)
+ return r;
if (path) {
r = table_add_many(table,
r = table_add_many(table,
TABLE_EMPTY,
TABLE_STRING, "Speed:",
- TABLE_BPS, (uint64_t) info->speed);
+ TABLE_BPS, info->speed);
if (r < 0)
return table_log_add_error(r);
}
return k;
}
+static int link_force_renew_one(sd_bus *bus, int index, const char *name) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.network1",
+ "/org/freedesktop/network1",
+ "org.freedesktop.network1.Manager",
+ "ForceRenewLink",
+ &error,
+ NULL,
+ "i", index);
+ if (r < 0)
+ return log_error_errno(r, "Failed to force renew dynamic configuration of interface %s: %s",
+ name, bus_error_message(&error, r));
+
+ return 0;
+}
+
+static int link_force_renew(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ int index, i, k = 0, r;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect system bus: %m");
+
+ for (i = 1; i < argc; i++) {
+ index = resolve_interface_or_warn(&rtnl, argv[i]);
+ if (index < 0)
+ return index;
+
+ r = link_force_renew_one(bus, index, argv[i]);
+ if (r < 0 && k >= 0)
+ k = r;
+ }
+
+ return k;
+}
+
static int verb_reload(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
" label Show current address label entries in the kernel\n"
" delete DEVICES... Delete virtual netdevs\n"
" renew DEVICES... Renew dynamic configurations\n"
+ " forcerenew DEVICES... Trigger DHCP reconfiguration of all connected clients\n"
" reconfigure DEVICES... Reconfigure interfaces\n"
" reload Reload .network and .netdev files\n"
"\nOptions:\n"
{ "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
{ "delete", 2, VERB_ANY, 0, link_delete },
{ "renew", 2, VERB_ANY, 0, link_renew },
+ { "forcerenew", 2, VERB_ANY, 0, link_force_renew },
{ "reconfigure", 2, VERB_ANY, 0, verb_reconfigure },
{ "reload", 1, 1, 0, verb_reload },
{}
#define ADDRESSES_PER_LINK_MAX 2048U
#define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U
+int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret) {
+ assert(link);
+ assert(ret);
+
+ /* see RFC4291 section 2.5.1 */
+ ret->s6_addr[8] = link->mac.ether_addr_octet[0];
+ ret->s6_addr[8] ^= 1 << 1;
+ ret->s6_addr[9] = link->mac.ether_addr_octet[1];
+ ret->s6_addr[10] = link->mac.ether_addr_octet[2];
+ ret->s6_addr[11] = 0xff;
+ ret->s6_addr[12] = 0xfe;
+ ret->s6_addr[13] = link->mac.ether_addr_octet[3];
+ ret->s6_addr[14] = link->mac.ether_addr_octet[4];
+ ret->s6_addr[15] = link->mac.ether_addr_octet[5];
+
+ return 0;
+}
+
int address_new(Address **ret) {
_cleanup_(address_freep) Address *address = NULL;
}
}
-DEFINE_PRIVATE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
+DEFINE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
bool address_equal(Address *a1, Address *a2) {
if (a1 == a2)
}
}
+ n->scope_set = true;
n = NULL;
return 0;
}
address->section->filename, address->section->line);
}
+ if (!address->scope_set && in_addr_is_localhost(address->family, &address->in_addr) > 0)
+ address->scope = RT_SCOPE_HOST;
+
return 0;
}
union in_addr_union in_addr;
union in_addr_union in_addr_peer;
+ bool scope_set:1;
bool ip_masquerade_done:1;
bool manage_temporary_address:1;
bool home_address:1;
int address_section_verify(Address *a);
int configure_ipv4_duplicate_address_detection(Link *link, Address *address);
+int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret);
+
DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
+extern const struct hash_ops address_hash_ops;
+
CONFIG_PARSER_PROTOTYPE(config_parse_address);
CONFIG_PARSER_PROTOTYPE(config_parse_broadcast);
CONFIG_PARSER_PROTOTYPE(config_parse_label);
#include "networkd-manager.h"
#include "string-util.h"
+#define CAN_TERMINATION_OHM_VALUE 120
+
static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
};
if (link->network->can_bitrate > UINT32_MAX) {
- log_link_error(link, "bitrate (%zu) too big.", link->network->can_bitrate);
+ log_link_error(link, "bitrate (%" PRIu64 ") too big.", link->network->can_bitrate);
return -ERANGE;
}
return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m");
}
+ if (link->network->can_termination >= 0) {
+
+ log_link_debug(link, "%sabling can-termination", link->network->can_termination ? "En" : "Dis");
+
+ r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION,
+ link->network->can_termination ? CAN_TERMINATION_OHM_VALUE : 0);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_CAN_TERMINATION 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");
ret->raw_data_len = count;
return 0;
}
-
-int config_parse_ip_service_type(
- 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) {
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- if (streq(rvalue, "CS4"))
- *((int *)data) = IPTOS_CLASS_CS4;
- else if (streq(rvalue, "CS6"))
- *((int *)data) = IPTOS_CLASS_CS6;
- else
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Failed to parse IPServiceType type '%s', ignoring.", rvalue);
-
- return 0;
-}
CONFIG_PARSER_PROTOTYPE(config_parse_duid_type);
CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata);
-CONFIG_PARSER_PROTOTYPE(config_parse_ip_service_type);
break;
}
case DHCP_OPTION_DATA_STRING:
- sz = cunescape(p, 0, &q);
+ sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
if (sz < 0) {
log_syntax(unit, LOG_ERR, filename, line, sz,
"Failed to decode DHCPv4 option data, ignoring assignment: %s", p);
size_t n_addresses = 0, n_allocated = 0;
unsigned i;
- log_link_debug(link, "Copying DNS server information from %s", link->ifname);
+ log_link_debug(link, "Copying DNS server information from link");
if (!link->network)
return 0;
if (!link->network)
return 0;
- log_link_debug(link, "Copying NTP server information from %s", link->ifname);
+ log_link_debug(link, "Copying NTP server information from link");
STRV_FOREACH(a, link->network->ntp) {
union in_addr_union ia;
if (!link->network)
return 0;
- log_link_debug(link, "Copying SIP server information from %s", link->ifname);
+ log_link_debug(link, "Copying SIP server information from link");
STRV_FOREACH(a, link->network->sip) {
union in_addr_union ia;
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <netinet/in.h>
+#include <netinet/ip.h>
#include <linux/if.h>
#include <linux/if_arp.h>
/* It seems kernel does not support that the prefix route cannot be configured with
* route table. Let's once drop the config and reconfigure them later. */
- log_link_message_debug_errno(link, m, r, "Could not set DHCPv4 route, retrying later: %m");
+ log_link_message_debug_errno(link, m, r, "Could not set DHCPv4 route, retrying later");
link->dhcp4_route_failed = true;
link->manager->dhcp4_prefix_root_cannot_set_table = true;
} else if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 route: %m");
+ log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 route");
link_enter_failed(link);
return 1;
}
if (!link->network) /* link went down while we configured the IP addresses? */
return 0;
- if (!link->network->dhcp_use_routes)
- return 0;
-
if (!link_has_carrier(link) && !link->network->configure_without_carrier)
/* During configuring addresses, the link lost its carrier. As networkd is dropping
* the addresses now, let's not configure the routes either. */
}
}
- for (i = 0; i < n; i++) {
- _cleanup_(route_freep) Route *route = NULL;
-
- /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
- the DHCP client MUST ignore the Static Routes option. */
- if (classless_route &&
- sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
- continue;
-
- r = route_new(&route);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate route: %m");
-
- route->family = AF_INET;
- route->protocol = RTPROT_DHCP;
- assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
- assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
- assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
- route->priority = link->network->dhcp_route_metric;
- route->table = table;
- route->mtu = link->network->dhcp_route_mtu;
- route->scope = route_scope_from_address(route, &address);
- if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
- route->prefsrc.in = address;
+ if (link->network->dhcp_use_routes) {
+ for (i = 0; i < n; i++) {
+ _cleanup_(route_freep) Route *route = NULL;
- if (set_contains(link->dhcp_routes, route))
- continue;
+ /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
+ the DHCP client MUST ignore the Static Routes option. */
+ if (classless_route &&
+ sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
+ continue;
- r = dhcp_route_configure(&route, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set route: %m");
+ r = route_new(&route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
+
+ route->family = AF_INET;
+ route->protocol = RTPROT_DHCP;
+ assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
+ assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
+ route->priority = link->network->dhcp_route_metric;
+ route->table = table;
+ route->mtu = link->network->dhcp_route_mtu;
+ route->scope = route_scope_from_address(route, &address);
+ if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
+ route->prefsrc.in = address;
+
+ if (set_contains(link->dhcp_routes, route))
+ continue;
+
+ r = dhcp_route_configure(&route, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set route: %m");
+ }
}
r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
return 0;
}
+int config_parse_dhcp_ip_service_type(
+ 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) {
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (streq(rvalue, "CS4"))
+ *((int *)data) = IPTOS_CLASS_CS4;
+ else if (streq(rvalue, "CS6"))
+ *((int *)data) = IPTOS_CLASS_CS6;
+ else
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to parse IPServiceType type '%s', ignoring.", rvalue);
+
+ return 0;
+}
+
static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
[DHCP_CLIENT_ID_MAC] = "mac",
[DHCP_CLIENT_ID_DUID] = "duid",
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type);
%%
Network.SpeedMeter, config_parse_bool, 0, offsetof(Manager, use_speed_meter)
Network.SpeedMeterIntervalSec, config_parse_sec, 0, offsetof(Manager, speed_meter_interval_usec)
+Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes)
DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid)
DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid)
#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "dns-domain.h"
#include "networkd-link-bus.h"
return sd_bus_reply_method_return(message, NULL);
}
+int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Link *l = userdata;
+ int r;
+
+ assert(l);
+
+ if (!l->network)
+ return sd_bus_error_setf(error, BUS_ERROR_UNMANAGED_INTERFACE,
+ "Interface %s is not managed by systemd-networkd",
+ l->ifname);
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.network1.forcerenew",
+ NULL, true, UID_INVALID,
+ &l->manager->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Polkit will call us back */
+
+ if (l->dhcp_server) {
+ r = sd_dhcp_server_forcerenew(l->dhcp_server);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Link *l = userdata;
int r;
if (r < 0)
return r;
+ link_set_state(l, LINK_STATE_INITIALIZED);
+ r = link_save(l);
+ if (r < 0)
+ return r;
+ link_clean(l);
+
return sd_bus_reply_method_return(message, NULL);
}
SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Renew", NULL, NULL, bus_link_method_renew, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ForceRenew", NULL, NULL, bus_link_method_force_renew, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reconfigure", NULL, NULL, bus_link_method_reconfigure, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error);
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-wifi.h"
-#include "qdisc.h"
#include "set.h"
#include "socket-util.h"
#include "stat-util.h"
#include "string-table.h"
#include "strv.h"
#include "sysctl-util.h"
+#include "tc.h"
#include "tmpfile-util.h"
#include "udev-util.h"
#include "util.h"
if (!link->routing_policy_rules_configured)
return;
- if (!link->qdiscs_configured)
+ if (!link->tc_configured)
return;
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
static int link_request_set_addresses(Link *link) {
AddressLabel *label;
Address *ad;
+ Prefix *p;
int r;
assert(link);
link->address_messages++;
}
+ if (IN_SET(link->network->router_prefix_delegation,
+ RADV_PREFIX_DELEGATION_STATIC,
+ RADV_PREFIX_DELEGATION_BOTH))
+ LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
+ _cleanup_(address_freep) Address *address = NULL;
+
+ if (!p->assign)
+ continue;
+
+ r = address_new(&address);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate address: %m");
+
+ r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
+ if (r < 0)
+ return r;
+
+ r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
+ if (r < 0)
+ return r;
+
+ address->family = AF_INET6;
+ r = address_configure(address, link, address_handler, true);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set addresses: %m");
+ if (r > 0)
+ link->address_messages++;
+ }
+
LIST_FOREACH(labels, label, link->network->address_labels) {
r = address_label_configure(label, link, NULL, false);
if (r < 0)
log_link_debug(link, "Starting IPv6 Router Advertisements");
+ r = radv_emit_dns(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure DNS or Domains in IPv6 Router Advertisement: %m");
+
r = sd_radv_start(link->radv);
if (r < 0 && r != -EBUSY)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
}
+ if (link_dhcp6_enabled(link) && link->network->dhcp6_without_ra) {
+ assert(link->dhcp6_client);
+ assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
+
+ r = dhcp6_request_address(link, true);
+ if (r < 0 && r != -EBUSY)
+ return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
+ else
+ log_link_debug(link, "Acquiring DHCPv6 lease");
+ }
+
(void) dhcp6_request_prefix_delegation(link);
return 0;
if (!link_ipv6ll_enabled(link))
ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE;
else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0)
- /* The file may not exist. And event if it exists, when stable_secret is unset,
+ /* The file may not exist. And even if it exists, when stable_secret is unset,
* reading the file fails with EIO. */
ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
else
if (strv_isempty(carrier->network->bind_carrier))
continue;
- if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) {
+ if (strv_fnmatch(carrier->network->bind_carrier, link->ifname)) {
r = link_put_carrier(link, carrier, &link->bound_by_links);
if (r < 0)
return r;
m = link->manager;
HASHMAP_FOREACH (carrier, m->links, i) {
- if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) {
+ if (strv_fnmatch(link->network->bind_carrier, carrier->ifname)) {
r = link_put_carrier(link, carrier, &link->bound_to_links);
if (r < 0)
return r;
return 0;
}
-static int link_configure_qdiscs(Link *link) {
- QDisc *qdisc;
+static int link_configure_traffic_control(Link *link) {
+ TrafficControl *tc;
Iterator i;
int r;
- link->qdiscs_configured = false;
- link->qdisc_messages = 0;
+ link->tc_configured = false;
+ link->tc_messages = 0;
- ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i) {
- r = qdisc_configure(link, qdisc);
+ ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section, i) {
+ r = traffic_control_configure(link, tc);
if (r < 0)
return r;
}
- if (link->qdisc_messages == 0)
- link->qdiscs_configured = true;
+ if (link->tc_messages == 0)
+ link->tc_configured = true;
else
- log_link_debug(link, "Configuring queuing discipline (qdisc)");
+ log_link_debug(link, "Configuring traffic control");
return 0;
}
assert(link->network);
assert(link->state == LINK_STATE_INITIALIZED);
- r = link_configure_qdiscs(link);
+ r = link_configure_traffic_control(link);
if (r < 0)
return r;
Network *network;
int r;
- if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
- return 0;
-
if (m) {
_cleanup_strv_free_ char **s = NULL;
strv_free_and_replace(link->alternative_names, s);
}
- r = network_get(link->manager, link->sd_device, link->ifname, link->alternative_names,
+ r = network_get(link->manager, link->iftype, link->sd_device, link->ifname, link->alternative_names,
&link->mac, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network);
if (r == -ENOENT) {
link_enter_unmanaged(link);
return r;
link_set_state(link, LINK_STATE_INITIALIZED);
+ link_dirty(link);
/* link_configure_duid() returns 0 if it requests product UUID. In that case,
* link_configure() is called later asynchronously. */
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
+ if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
+ return 0;
+
r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK,
link->ifindex);
if (r < 0)
if (r < 0)
return r;
- r = network_get(link->manager, link->sd_device, link->ifname, link->alternative_names,
+ r = network_get(link->manager, link->iftype, link->sd_device, link->ifname, link->alternative_names,
&link->mac, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network);
if (r == -ENOENT) {
link_enter_unmanaged(link);
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
yes_no(link->network->required_for_online));
- fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s\n",
- strempty(link_operstate_to_string(link->network->required_operstate_for_online)));
+ fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s",
+ strempty(link_operstate_to_string(link->network->required_operstate_for_online.min)));
+
+ if (link->network->required_operstate_for_online.max != LINK_OPERSTATE_RANGE_DEFAULT.max)
+ fprintf(f, ":%s",
+ strempty(link_operstate_to_string(link->network->required_operstate_for_online.max)));
+
+ fprintf(f, "\n");
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
const char *err_msg = NULL;
(void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg);
- return log_link_full(link, level, err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : "");
+ return log_link_full(link, level, err,
+ "%s: %s%s%s%m",
+ msg,
+ strempty(err_msg),
+ err_msg && !endswith(err_msg, ".") ? "." : "",
+ err_msg ? " " : "");
}
unsigned nexthop_messages;
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
- unsigned qdisc_messages;
+ unsigned tc_messages;
unsigned enslaving;
Set *addresses;
bool static_routes_ready:1;
bool static_nexthops_configured:1;
bool routing_policy_rules_configured:1;
- bool qdiscs_configured:1;
+ bool tc_configured:1;
bool setting_mtu:1;
bool setting_genmode:1;
bool ipv6_mtu_set:1;
#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "networkd-link-bus.h"
#include "networkd-link.h"
#include "networkd-manager-bus.h"
return call_link_method(userdata, message, bus_link_method_renew, error);
}
+static int bus_method_force_renew_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return call_link_method(userdata, message, bus_link_method_force_renew, error);
+}
+
static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_reconfigure, error);
}
SD_BUS_METHOD("RevertLinkNTP", "i", NULL, bus_method_revert_link_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RevertLinkDNS", "i", NULL, bus_method_revert_link_dns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RenewLink", "i", NULL, bus_method_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ForceRenewLink", "i", NULL, bus_method_force_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ReconfigureLink", "i", NULL, bus_method_reconfigure_link, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reload", NULL, NULL, bus_method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
#include "sd-netlink.h"
#include "alloc-util.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "conf-parser.h"
#include "def.h"
log_link_debug(link,
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
- type == RTM_DELROUTE ? "Forgetting" : route ? "Received remembered" : "Remembering",
+ (!route && !link->manager->manage_foreign_routes) || type == RTM_DELROUTE ? "Forgetting" :
+ route ? "Received remembered" : "Remembering",
strna(buf_dst), strempty(buf_dst_prefixlen),
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
switch (type) {
case RTM_NEWROUTE:
- if (!route) {
+ if (!route && link->manager->manage_foreign_routes) {
/* A route appeared that we did not request */
r = route_add_foreign(link, tmp, &route);
if (r < 0) {
_cleanup_free_ char *from = NULL, *to = NULL;
RoutingPolicyRule *rule = NULL;
const char *iif = NULL, *oif = NULL;
+ uint32_t suppress_prefixlen;
Manager *m = userdata;
unsigned flags;
uint16_t type;
return 0;
}
+ r = sd_netlink_message_read(message, FRA_UID_RANGE, sizeof(tmp->uid_range), &tmp->uid_range);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_UID_RANGE attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_PREFIXLEN, &suppress_prefixlen);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_PREFIXLEN attribute, ignoring: %m");
+ return 0;
+ }
+ if (r >= 0)
+ tmp->suppress_prefixlen = (int) suppress_prefixlen;
+
(void) routing_policy_rule_get(m, tmp, &rule);
if (DEBUG_LOGGING) {
*m = (Manager) {
.speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
+ .manage_foreign_routes = true,
};
m->state_file = strdup("/run/systemd/netif/state");
bool enumerating:1;
bool dirty:1;
bool restarting:1;
+ bool manage_foreign_routes;
Set *dirty_links;
#include "networkd-manager.h"
#include "networkd-ndisc.h"
#include "networkd-route.h"
+#include "string-util.h"
#include "strv.h"
#define NDISC_DNSSL_MAX 64U
#define NDISC_RDNSS_MAX 64U
#define NDISC_PREFIX_LFT_MIN 7200U
+#define DAD_CONFLICTS_IDGEN_RETRIES_RFC7217 3
+
+/* https://tools.ietf.org/html/rfc5453 */
+/* https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xml */
+
+#define SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291 ((struct in6_addr) { .s6_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } })
+#define SUBNET_ROUTER_ANYCAST_PREFIXLEN 8
+#define RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291 ((struct in6_addr) { .s6_addr = { 0x02, 0x00, 0x5E, 0xFF, 0xFE } })
+#define RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN 5
+#define RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291 ((struct in6_addr) { .s6_addr = { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } })
+#define RESERVED_SUBNET_ANYCAST_PREFIXLEN 7
+
+#define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
+
+static bool stableprivate_address_is_valid(const struct in6_addr *addr) {
+ assert(addr);
+
+ /* According to rfc4291, generated address should not be in the following ranges. */
+
+ if (memcmp(addr, &SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291, SUBNET_ROUTER_ANYCAST_PREFIXLEN) == 0)
+ return false;
+
+ if (memcmp(addr, &RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291, RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN) == 0)
+ return false;
+
+ if (memcmp(addr, &RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291, RESERVED_SUBNET_ANYCAST_PREFIXLEN) == 0)
+ return false;
+
+ return true;
+}
+
+static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr *addr) {
+ sd_id128_t secret_key;
+ struct siphash state;
+ uint64_t rid;
+ size_t l;
+ int r;
+
+ /* According to rfc7217 section 5.1
+ * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) */
+
+ r = sd_id128_get_machine_app_specific(NDISC_APP_ID, &secret_key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate key: %m");
+
+ siphash24_init(&state, secret_key.bytes);
+
+ l = MAX(DIV_ROUND_UP(prefix_len, 8), 8);
+ siphash24_compress(prefix, l, &state);
+ siphash24_compress(link->ifname, strlen(link->ifname), &state);
+ siphash24_compress(&link->mac, sizeof(struct ether_addr), &state);
+ siphash24_compress(&dad_counter, sizeof(uint8_t), &state);
+
+ rid = htole64(siphash24_finalize(&state));
+
+ memcpy(addr->s6_addr, prefix->s6_addr, l);
+ memcpy((uint8_t *) &addr->s6_addr + l, &rid, 16 - l);
+
+ return 0;
+}
+
static int ndisc_netlink_route_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
return 0;
}
+static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint32_t lifetime_preferred, Address *address, Set **ret) {
+ _cleanup_set_free_free_ Set *addresses = NULL;
+ struct in6_addr addr;
+ IPv6Token *j;
+ Iterator i;
+ int r;
+
+ assert(link);
+ assert(address);
+ assert(ret);
+
+ addresses = set_new(&address_hash_ops);
+ if (!addresses)
+ return log_oom();
+
+ addr = address->in_addr.in6;
+ ORDERED_HASHMAP_FOREACH(j, link->network->ipv6_tokens, i) {
+ bool have_address = false;
+ _cleanup_(address_freep) Address *new_address = NULL;
+
+ r = address_new(&new_address);
+ if (r < 0)
+ return log_oom();
+
+ *new_address = *address;
+
+ if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE
+ && memcmp(&j->prefix, &addr, FAMILY_ADDRESS_SIZE(address->family)) == 0) {
+ /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop
+ does not actually attempt Duplicate Address Detection; the counter will be incremented
+ only when the address generation algorithm produces an invalid address, and the loop
+ may exit with an address which ends up being unusable due to duplication on the link.
+ */
+ for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) {
+ r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address->in_addr.in6);
+ if (r < 0)
+ break;
+
+ if (stableprivate_address_is_valid(&new_address->in_addr.in6)) {
+ have_address = true;
+ break;
+ }
+ }
+ } else if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_STATIC) {
+ memcpy(((uint8_t *)&new_address->in_addr.in6) + 8, ((uint8_t *) &j->prefix) + 8, 8);
+ have_address = true;
+ }
+
+ if (have_address) {
+ new_address->prefixlen = prefixlen;
+ new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
+ new_address->cinfo.ifa_prefered = lifetime_preferred;
+
+ r = set_put(addresses, new_address);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to store address: %m");
+ TAKE_PTR(new_address);
+ }
+ }
+
+ /* fall back to EUI-64 if no tokens provided addresses */
+ if (set_isempty(addresses)) {
+ _cleanup_(address_freep) Address *new_address = NULL;
+
+ r = address_new(&new_address);
+ if (r < 0)
+ return log_oom();
+
+ *new_address = *address;
+
+ r = generate_ipv6_eui_64_address(link, &new_address->in_addr.in6);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m");
+
+ new_address->prefixlen = prefixlen;
+ new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
+ new_address->cinfo.ifa_prefered = lifetime_preferred;
+
+ r = set_put(addresses, new_address);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to store address: %m");
+ TAKE_PTR(new_address);
+ }
+
+ *ret = TAKE_PTR(addresses);
+
+ return 0;
+}
+
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
- _cleanup_(address_freep) Address *address = NULL;
- Address *existing_address;
uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
- usec_t time_now;
+ _cleanup_set_free_free_ Set *addresses = NULL;
+ _cleanup_(address_freep) Address *address = NULL;
unsigned prefixlen;
+ usec_t time_now;
+ Address *existing_address, *a;
+ Iterator i;
int r;
assert(link);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
- if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
- memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
- else {
- /* see RFC4291 section 2.5.1 */
- address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0];
- address->in_addr.in6.s6_addr[8] ^= 1 << 1;
- address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1];
- address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
- address->in_addr.in6.s6_addr[11] = 0xff;
- address->in_addr.in6.s6_addr[12] = 0xfe;
- address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
- address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
- address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
- }
- address->prefixlen = prefixlen;
- address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
- address->cinfo.ifa_prefered = lifetime_preferred;
-
- /* see RFC4862 section 5.5.3.e */
- r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address);
- if (r > 0) {
- lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
- if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
- address->cinfo.ifa_valid = lifetime_valid;
- else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
- address->cinfo.ifa_valid = lifetime_remaining;
+ r = ndisc_router_generate_addresses(link, prefixlen, lifetime_preferred, address, &addresses);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m");
+
+ SET_FOREACH(a, addresses, i) {
+ /* see RFC4862 section 5.5.3.e */
+ r = address_get(link, a->family, &a->in_addr, a->prefixlen, &existing_address);
+ if (r > 0) {
+ lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
+ if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
+ a->cinfo.ifa_valid = lifetime_valid;
+ else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
+ a->cinfo.ifa_valid = lifetime_remaining;
+ else
+ a->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
+ } else if (lifetime_valid > 0)
+ a->cinfo.ifa_valid = lifetime_valid;
else
- address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
- } else if (lifetime_valid > 0)
- address->cinfo.ifa_valid = lifetime_valid;
- else
- return 0; /* see RFC4862 section 5.5.3.d */
+ return 0; /* see RFC4862 section 5.5.3.d */
- if (address->cinfo.ifa_valid == 0)
- return 0;
+ if (a->cinfo.ifa_valid == 0)
+ continue;
- r = address_configure(address, link, ndisc_netlink_address_message_handler, true);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
- link_enter_failed(link);
- return r;
+ r = address_configure(a, link, ndisc_netlink_address_message_handler, true);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
+ link_enter_failed(link);
+ return r;
+ }
+
+ if (r > 0)
+ link->ndisc_messages++;
}
- if (r > 0)
- link->ndisc_messages++;
return 0;
}
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
- if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
+ if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) &&
+ link->network->ipv6_accept_ra_start_dhcp6_client) {
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
if (r < 0 && r != -EBUSY)
link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
}
+int ipv6token_new(IPv6Token **ret) {
+ IPv6Token *p;
+
+ p = new(IPv6Token, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (IPv6Token) {
+ .address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_NONE,
+ };
+
+ *ret = TAKE_PTR(p);
+
+ return 0;
+}
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ ipv6_token_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ IPv6Token,
+ free);
+
int config_parse_ndisc_black_listed_prefix(
const char *unit,
const char *filename,
return 0;
}
+
+int config_parse_address_generation_type(
+ 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_free_ IPv6Token *token = NULL;
+ union in_addr_union buffer;
+ Network *network = data;
+ const char *p;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ network->ipv6_tokens = ordered_hashmap_free(network->ipv6_tokens);
+ return 0;
+ }
+
+ r = ipv6token_new(&token);
+ if (r < 0)
+ return log_oom();
+
+ if ((p = startswith(rvalue, "static:")))
+ token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC;
+ else if ((p = startswith(rvalue, "prefixstable:")))
+ token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE;
+ else {
+ token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC;
+ p = rvalue;
+ }
+
+ r = in_addr_from_string(AF_INET6, p, &buffer);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse IPv6 %s, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (in_addr_is_null(AF_INET6, &buffer)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "IPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ token->prefix = buffer.in6;
+
+ r = ordered_hashmap_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ r = ordered_hashmap_put(network->ipv6_tokens, &token->prefix, token);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store IPv6 token '%s'", rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(token);
+
+ return 0;
+}
#include "networkd-link.h"
#include "time-util.h"
+typedef struct IPv6Token IPv6Token;
+
+typedef enum IPv6TokenAddressGeneration {
+ IPV6_TOKEN_ADDRESS_GENERATION_NONE,
+ IPV6_TOKEN_ADDRESS_GENERATION_STATIC,
+ IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE,
+ _IPV6_TOKEN_ADDRESS_GENERATION_MAX,
+ _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -1,
+} IPv6TokenAddressGeneration;
+
typedef struct NDiscRDNSS {
usec_t valid_until;
struct in6_addr address;
/* The domain name follows immediately. */
} NDiscDNSSL;
+struct IPv6Token {
+ IPv6TokenAddressGeneration address_generation_type;
+
+ uint8_t dad_counter;
+ struct in6_addr prefix;
+};
+
+int ipv6token_new(IPv6Token **ret);
+DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6Token *, freep);
+
static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
}
void ndisc_flush(Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_black_listed_prefix);
+CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "qdisc.h"
+#include "tclass.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_mac)
-Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_permanent_mac)
-Match.Path, config_parse_match_strv, 0, offsetof(Network, match_path)
-Match.Driver, config_parse_match_strv, 0, offsetof(Network, match_driver)
-Match.Type, config_parse_match_strv, 0, offsetof(Network, match_type)
-Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match_wlan_iftype)
-Match.SSID, config_parse_match_strv, 0, offsetof(Network, match_ssid)
-Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match_bssid)
-Match.Name, config_parse_match_ifnames, 1, offsetof(Network, match_name)
-Match.Property, config_parse_match_property, 0, offsetof(Network, match_property)
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
-Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
-Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu)
-Link.ARP, config_parse_tristate, 0, offsetof(Network, arp)
-Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast)
-Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
-Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
-Link.RequiredForOnline, config_parse_required_for_online, 0, 0
-Network.Description, config_parse_string, 0, offsetof(Network, description)
-Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
-Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name)
-Network.VLAN, config_parse_stacked_netdev, NETDEV_KIND_VLAN, offsetof(Network, stacked_netdev_names)
-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.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names)
-Network.Xfrm, config_parse_stacked_netdev, NETDEV_KIND_XFRM, 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_link_local_address_family, 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)
-Network.Address, config_parse_address, 0, 0
-Network.Gateway, config_parse_gateway, 0, 0
-Network.Domains, config_parse_domains, 0, 0
-Network.DNS, config_parse_dns, 0, 0
-Network.DNSDefaultRoute, config_parse_tristate, 0, offsetof(Network, dns_default_route)
-Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
-Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
-Network.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Network, dns_over_tls_mode)
-Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
-Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, 0
-Network.NTP, config_parse_ntp, 0, offsetof(Network, ntp)
-Network.IPForward, config_parse_address_family_with_kernel, 0, offsetof(Network, ip_forward)
-Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
-Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
-Network.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
-Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
-Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits)
-Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit)
-Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp)
-Network.IPv6MTUBytes, config_parse_mtu, AF_INET6, offsetof(Network, ipv6_mtu)
-Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave)
-Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave)
-Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
-Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
-Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0
-Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
-Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier)
-Network.IgnoreCarrierLoss, config_parse_bool, 0, offsetof(Network, ignore_carrier_loss)
-Network.KeepConfiguration, config_parse_keep_configuration, 0, offsetof(Network, keep_configuration)
-Address.Address, config_parse_address, 0, 0
-Address.Peer, config_parse_address, 0, 0
-Address.Broadcast, config_parse_broadcast, 0, 0
-Address.Label, config_parse_label, 0, 0
-Address.PreferredLifetime, config_parse_lifetime, 0, 0
-Address.HomeAddress, config_parse_address_flags, 0, 0
-Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0
-Address.PrefixRoute, config_parse_address_flags, 0, 0 /* deprecated */
-Address.AddPrefixRoute, config_parse_address_flags, 0, 0
-Address.AutoJoin, config_parse_address_flags, 0, 0
-Address.DuplicateAddressDetection, config_parse_duplicate_address_detection, 0, 0
-Address.Scope, config_parse_address_scope, 0, 0
-IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
-IPv6AddressLabel.Label, config_parse_address_label, 0, 0
-Neighbor.Address, config_parse_neighbor_address, 0, 0
-Neighbor.LinkLayerAddress, config_parse_neighbor_lladdr, 0, 0
-Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0 /* deprecated */
-RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0
-RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0
-RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0
-RoutingPolicyRule.FirewallMark, config_parse_routing_policy_rule_fwmark_mask, 0, 0
-RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix, 0, 0
-RoutingPolicyRule.To, config_parse_routing_policy_rule_prefix, 0, 0
-RoutingPolicyRule.IncomingInterface, config_parse_routing_policy_rule_device, 0, 0
-RoutingPolicyRule.OutgoingInterface, config_parse_routing_policy_rule_device, 0, 0
-RoutingPolicyRule.IPProtocol, config_parse_routing_policy_rule_ip_protocol, 0, 0
-RoutingPolicyRule.SourcePort, config_parse_routing_policy_rule_port_range, 0, 0
-RoutingPolicyRule.DestinationPort, config_parse_routing_policy_rule_port_range, 0, 0
-RoutingPolicyRule.InvertRule, config_parse_routing_policy_rule_invert, 0, 0
-RoutingPolicyRule.Family, config_parse_routing_policy_rule_family, 0, 0
-Route.Gateway, config_parse_gateway, 0, 0
-Route.Destination, config_parse_destination, 0, 0
-Route.Source, config_parse_destination, 0, 0
-Route.Metric, config_parse_route_priority, 0, 0
-Route.Scope, config_parse_route_scope, 0, 0
-Route.PreferredSource, config_parse_preferred_src, 0, 0
-Route.Table, config_parse_route_table, 0, 0
-Route.MTUBytes, config_parse_route_mtu, AF_UNSPEC, 0
-Route.GatewayOnLink, config_parse_gateway_onlink, 0, 0
-Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0
-Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0
-Route.Protocol, config_parse_route_protocol, 0, 0
-Route.Type, config_parse_route_type, 0, 0
-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
-Route.MultiPathRoute, config_parse_multipath_route, 0, 0
-NextHop.Id, config_parse_nexthop_id, 0, 0
-NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
-DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
-DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
-DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)
-DHCPv4.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
-DHCPv4.UseSIP, config_parse_bool, 0, offsetof(Network, dhcp_use_sip)
-DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
-DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
-DHCPv4.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
-DHCPv4.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
-DHCPv4.RequestOptions, config_parse_dhcp_request_options, 0, 0
-DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
-DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
-DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
-DHCPv4.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
-DHCPv4.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
-DHCPv4.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0
-DHCPv4.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class)
-DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid)
-DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
-DHCPv4.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
-DHCPv4.RouteTable, config_parse_section_route_table, 0, 0
-DHCPv4.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
-DHCPv4.IAID, config_parse_iaid, 0, 0
-DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
-DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
-DHCPv4.SendDecline, config_parse_bool, 0, offsetof(Network, dhcp_send_decline)
-DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0
-DHCPv4.IPServiceType, config_parse_ip_service_type, 0, offsetof(Network, ip_service_type)
-DHCPv4.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_options)
-DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu)
-DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns)
-DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp)
-DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
-DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
-DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0
-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)
-DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0
-DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp)
-DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0
-DHCPServer.EmitSIP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_sip)
-DHCPServer.SIP, config_parse_dhcp_server_sip, 0, 0
-DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router)
-DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone)
-DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
-DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
-DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
-DHCPServer.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_options)
-Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
-Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu)
-Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin)
-Bridge.FastLeave, config_parse_tristate, 0, offsetof(Network, fast_leave)
-Bridge.AllowPortToBeRoot, config_parse_tristate, 0, offsetof(Network, allow_port_to_be_root)
-Bridge.UnicastFlood, config_parse_tristate, 0, offsetof(Network, unicast_flood)
-Bridge.MulticastFlood, config_parse_tristate, 0, offsetof(Network, multicast_flood)
-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
-Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0
-IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec)
-IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
-IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
-IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0
-IPv6PrefixDelegation.EmitDNS, config_parse_bool, 0, offsetof(Network, router_emit_dns)
-IPv6PrefixDelegation.DNS, config_parse_radv_dns, 0, 0
-IPv6PrefixDelegation.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains)
-IPv6PrefixDelegation.Domains, config_parse_radv_search_domains, 0, 0
-IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec)
-IPv6Prefix.Prefix, config_parse_prefix, 0, 0
-IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0
-IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
-IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
-IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
-IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
-IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
-CAN.BitRate, config_parse_si_size, 0, offsetof(Network, can_bitrate)
-CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
-CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
-CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling)
-TrafficControlQueueingDiscipline.Parent, config_parse_tc_qdiscs_parent, 0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec, config_parse_tc_network_emulator_delay, 0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_tc_network_emulator_delay, 0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_tc_network_emulator_rate, 0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate, config_parse_tc_network_emulator_rate, 0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_tc_network_emulator_packet_limit, 0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterRate, config_parse_tc_token_buffer_filter_size, 0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterBurst, config_parse_tc_token_buffer_filter_size, 0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterLimitSize, config_parse_tc_token_buffer_filter_size, 0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterMTUBytes, config_parse_tc_token_buffer_filter_size, 0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterMPUBytes, config_parse_tc_token_buffer_filter_size, 0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterPeakRate, config_parse_tc_token_buffer_filter_size, 0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterLatencySec, config_parse_tc_token_buffer_filter_latency, 0, 0
-TrafficControlQueueingDiscipline.StochasticFairnessQueueingPerturbPeriodSec, config_parse_tc_stochastic_fairness_queueing_perturb_period, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayPacketLimit, config_parse_tc_fair_queuing_controlled_delay_u32, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayMemoryLimit, config_parse_tc_fair_queuing_controlled_delay_size, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayFlows, config_parse_tc_fair_queuing_controlled_delay_u32, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayQuantum, config_parse_tc_fair_queuing_controlled_delay_size, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayTargetSec, config_parse_tc_fair_queuing_controlled_delay_usec, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayIntervalSec, config_parse_tc_fair_queuing_controlled_delay_usec, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayCEThresholdSec, config_parse_tc_fair_queuing_controlled_delay_usec, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayECN, config_parse_tc_fair_queuing_controlled_delay_bool, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacketLimit, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingFlowLimit, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingQuantum, config_parse_tc_fair_queue_traffic_policing_size, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingInitialQuantum, config_parse_tc_fair_queue_traffic_policing_size, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingMaximumRate, config_parse_tc_fair_queue_traffic_policing_max_rate, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingBuckets, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingOrphanMask, config_parse_tc_fair_queue_traffic_policing_u32, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacing, config_parse_tc_fair_queue_traffic_policing_bool, 0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingCEThresholdSec, config_parse_tc_fair_queue_traffic_policing_usec, 0, 0
-TrafficControlQueueingDiscipline.ControlledDelayPacketLimit, config_parse_tc_controlled_delay_u32, 0, 0
-TrafficControlQueueingDiscipline.ControlledDelayTargetSec, config_parse_tc_controlled_delay_usec, 0, 0
-TrafficControlQueueingDiscipline.ControlledDelayIntervalSec, config_parse_tc_controlled_delay_usec, 0, 0
-TrafficControlQueueingDiscipline.ControlledDelayCEThresholdSec, config_parse_tc_controlled_delay_usec, 0, 0
-TrafficControlQueueingDiscipline.ControlledDelayECN, config_parse_tc_controlled_delay_bool, 0, 0
+Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_mac)
+Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_permanent_mac)
+Match.Path, config_parse_match_strv, 0, offsetof(Network, match_path)
+Match.Driver, config_parse_match_strv, 0, offsetof(Network, match_driver)
+Match.Type, config_parse_match_strv, 0, offsetof(Network, match_type)
+Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match_wlan_iftype)
+Match.SSID, config_parse_match_strv, 0, offsetof(Network, match_ssid)
+Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match_bssid)
+Match.Name, config_parse_match_ifnames, 1, offsetof(Network, match_name)
+Match.Property, config_parse_match_property, 0, offsetof(Network, match_property)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions)
+Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
+Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu)
+Link.ARP, config_parse_tristate, 0, offsetof(Network, arp)
+Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast)
+Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
+Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
+Link.RequiredForOnline, config_parse_required_for_online, 0, 0
+Network.Description, config_parse_string, 0, offsetof(Network, description)
+Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
+Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name)
+Network.VLAN, config_parse_stacked_netdev, NETDEV_KIND_VLAN, offsetof(Network, stacked_netdev_names)
+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.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names)
+Network.Xfrm, config_parse_stacked_netdev, NETDEV_KIND_XFRM, 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_link_local_address_family, 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_address_generation_type, 0, 0
+Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode)
+Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit)
+Network.Address, config_parse_address, 0, 0
+Network.Gateway, config_parse_gateway, 0, 0
+Network.Domains, config_parse_domains, 0, 0
+Network.DNS, config_parse_dns, 0, 0
+Network.DNSDefaultRoute, config_parse_tristate, 0, offsetof(Network, dns_default_route)
+Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
+Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
+Network.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Network, dns_over_tls_mode)
+Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode)
+Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, 0
+Network.NTP, config_parse_ntp, 0, offsetof(Network, ntp)
+Network.IPForward, config_parse_address_family_with_kernel, 0, offsetof(Network, ip_forward)
+Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade)
+Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions)
+Network.IPv6AcceptRA, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
+Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra)
+Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits)
+Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit)
+Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp)
+Network.IPv6MTUBytes, config_parse_mtu, AF_INET6, offsetof(Network, ipv6_mtu)
+Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave)
+Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave)
+Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
+Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
+Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0
+Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
+Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier)
+Network.IgnoreCarrierLoss, config_parse_bool, 0, offsetof(Network, ignore_carrier_loss)
+Network.KeepConfiguration, config_parse_keep_configuration, 0, offsetof(Network, keep_configuration)
+Address.Address, config_parse_address, 0, 0
+Address.Peer, config_parse_address, 0, 0
+Address.Broadcast, config_parse_broadcast, 0, 0
+Address.Label, config_parse_label, 0, 0
+Address.PreferredLifetime, config_parse_lifetime, 0, 0
+Address.HomeAddress, config_parse_address_flags, 0, 0
+Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0
+Address.PrefixRoute, config_parse_address_flags, 0, 0 /* deprecated */
+Address.AddPrefixRoute, config_parse_address_flags, 0, 0
+Address.AutoJoin, config_parse_address_flags, 0, 0
+Address.DuplicateAddressDetection, config_parse_duplicate_address_detection, 0, 0
+Address.Scope, config_parse_address_scope, 0, 0
+IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0
+IPv6AddressLabel.Label, config_parse_address_label, 0, 0
+Neighbor.Address, config_parse_neighbor_address, 0, 0
+Neighbor.LinkLayerAddress, config_parse_neighbor_lladdr, 0, 0
+Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0 /* deprecated */
+RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0
+RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0
+RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0
+RoutingPolicyRule.FirewallMark, config_parse_routing_policy_rule_fwmark_mask, 0, 0
+RoutingPolicyRule.From, config_parse_routing_policy_rule_prefix, 0, 0
+RoutingPolicyRule.To, config_parse_routing_policy_rule_prefix, 0, 0
+RoutingPolicyRule.IncomingInterface, config_parse_routing_policy_rule_device, 0, 0
+RoutingPolicyRule.OutgoingInterface, config_parse_routing_policy_rule_device, 0, 0
+RoutingPolicyRule.IPProtocol, config_parse_routing_policy_rule_ip_protocol, 0, 0
+RoutingPolicyRule.SourcePort, config_parse_routing_policy_rule_port_range, 0, 0
+RoutingPolicyRule.DestinationPort, config_parse_routing_policy_rule_port_range, 0, 0
+RoutingPolicyRule.InvertRule, config_parse_routing_policy_rule_invert, 0, 0
+RoutingPolicyRule.Family, config_parse_routing_policy_rule_family, 0, 0
+RoutingPolicyRule.User, config_parse_routing_policy_rule_uid_range, 0, 0
+RoutingPolicyRule.SuppressPrefixLength, config_parse_routing_policy_rule_suppress_prefixlen, 0, 0
+Route.Gateway, config_parse_gateway, 0, 0
+Route.Destination, config_parse_destination, 0, 0
+Route.Source, config_parse_destination, 0, 0
+Route.Metric, config_parse_route_priority, 0, 0
+Route.Scope, config_parse_route_scope, 0, 0
+Route.PreferredSource, config_parse_preferred_src, 0, 0
+Route.Table, config_parse_route_table, 0, 0
+Route.MTUBytes, config_parse_route_mtu, AF_UNSPEC, 0
+Route.GatewayOnLink, config_parse_gateway_onlink, 0, 0
+Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0
+Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0
+Route.Protocol, config_parse_route_protocol, 0, 0
+Route.Type, config_parse_route_type, 0, 0
+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
+Route.MultiPathRoute, config_parse_multipath_route, 0, 0
+NextHop.Id, config_parse_nexthop_id, 0, 0
+NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
+DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
+DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
+DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)
+DHCPv4.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
+DHCPv4.UseSIP, config_parse_bool, 0, offsetof(Network, dhcp_use_sip)
+DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCPv4.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCPv4.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
+DHCPv4.RequestOptions, config_parse_dhcp_request_options, 0, 0
+DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
+DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
+DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
+DHCPv4.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCPv4.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
+DHCPv4.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0
+DHCPv4.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class)
+DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid)
+DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
+DHCPv4.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+DHCPv4.RouteTable, config_parse_section_route_table, 0, 0
+DHCPv4.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
+DHCPv4.IAID, config_parse_iaid, 0, 0
+DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
+DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
+DHCPv4.SendDecline, config_parse_bool, 0, offsetof(Network, dhcp_send_decline)
+DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0
+DHCPv4.IPServiceType, config_parse_dhcp_ip_service_type, 0, offsetof(Network, ip_service_type)
+DHCPv4.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_client_send_options)
+DHCPv4.RouteMTUBytes, config_parse_mtu, AF_INET, offsetof(Network, dhcp_route_mtu)
+DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns)
+DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp)
+DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
+DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
+DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0
+DHCPv6.WithoutRA, config_parse_bool, 0, offsetof(Network, dhcp6_without_ra)
+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.DHCPv6Client, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
+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)
+DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0
+DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp)
+DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0
+DHCPServer.EmitSIP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_sip)
+DHCPServer.SIP, config_parse_dhcp_server_sip, 0, 0
+DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router)
+DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone)
+DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone)
+DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset)
+DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size)
+DHCPServer.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_options)
+Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost)
+Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu)
+Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin)
+Bridge.FastLeave, config_parse_tristate, 0, offsetof(Network, fast_leave)
+Bridge.AllowPortToBeRoot, config_parse_tristate, 0, offsetof(Network, allow_port_to_be_root)
+Bridge.UnicastFlood, config_parse_tristate, 0, offsetof(Network, unicast_flood)
+Bridge.MulticastFlood, config_parse_tristate, 0, offsetof(Network, multicast_flood)
+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
+Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0
+IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec)
+IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed)
+IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information)
+IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0
+IPv6PrefixDelegation.EmitDNS, config_parse_bool, 0, offsetof(Network, router_emit_dns)
+IPv6PrefixDelegation.DNS, config_parse_radv_dns, 0, 0
+IPv6PrefixDelegation.EmitDomains, config_parse_bool, 0, offsetof(Network, router_emit_domains)
+IPv6PrefixDelegation.Domains, config_parse_radv_search_domains, 0, 0
+IPv6PrefixDelegation.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec)
+IPv6Prefix.Prefix, config_parse_prefix, 0, 0
+IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0
+IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
+IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
+IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
+IPv6Prefix.Assign, config_parse_prefix_assign, 0, 0
+IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
+IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
+CAN.BitRate, config_parse_si_uint64, 0, offsetof(Network, can_bitrate)
+CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
+CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
+CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling)
+CAN.Termination, config_parse_tristate, 0, offsetof(Network, can_termination)
+QDisc.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
+QDisc.Handle, config_parse_qdisc_handle, _QDISC_KIND_INVALID, 0
+CAKE.Parent, config_parse_qdisc_parent, QDISC_KIND_CAKE, 0
+CAKE.Handle, config_parse_qdisc_handle, QDISC_KIND_CAKE, 0
+CAKE.Bandwidth, config_parse_cake_bandwidth, QDISC_KIND_CAKE, 0
+CAKE.Overhead, config_parse_cake_overhead, QDISC_KIND_CAKE, 0
+ControlledDelay.Parent, config_parse_qdisc_parent, QDISC_KIND_CODEL, 0
+ControlledDelay.Handle, config_parse_qdisc_handle, QDISC_KIND_CODEL, 0
+ControlledDelay.PacketLimit, config_parse_controlled_delay_u32, QDISC_KIND_CODEL, 0
+ControlledDelay.TargetSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0
+ControlledDelay.IntervalSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0
+ControlledDelay.CEThresholdSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0
+ControlledDelay.ECN, config_parse_controlled_delay_bool, QDISC_KIND_CODEL, 0
+PFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO, 0
+PFIFO.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO, 0
+PFIFO.PacketLimit, config_parse_fifo_size, QDISC_KIND_PFIFO, 0
+FairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_FQ, 0
+FairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_FQ, 0
+FairQueueing.PacketLimit, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0
+FairQueueing.FlowLimit, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0
+FairQueueing.Quantum, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0
+FairQueueing.InitialQuantum, config_parse_fair_queueing_size, QDISC_KIND_FQ, 0
+FairQueueing.MaximumRate, config_parse_fair_queueing_max_rate, QDISC_KIND_FQ, 0
+FairQueueing.Buckets, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0
+FairQueueing.OrphanMask, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0
+FairQueueing.Pacing, config_parse_fair_queueing_bool, QDISC_KIND_FQ, 0
+FairQueueing.CEThresholdSec, config_parse_fair_queueing_usec, QDISC_KIND_FQ, 0
+FairQueueingControlledDelay.Parent, config_parse_qdisc_parent, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.Handle, config_parse_qdisc_handle, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.PacketLimit, config_parse_fair_queueing_controlled_delay_u32, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.MemoryLimit, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.Flows, config_parse_fair_queueing_controlled_delay_u32, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.Quantum, config_parse_fair_queueing_controlled_delay_size, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.TargetSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.IntervalSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.CEThresholdSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0
+FairQueueingControlledDelay.ECN, config_parse_fair_queueing_controlled_delay_bool, QDISC_KIND_FQ_CODEL, 0
+GenericRandomEarlyDetection.Parent, config_parse_qdisc_parent, QDISC_KIND_GRED, 0
+GenericRandomEarlyDetection.Handle, config_parse_qdisc_handle, QDISC_KIND_GRED, 0
+GenericRandomEarlyDetection.VirtualQueues, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0
+GenericRandomEarlyDetection.DefaultVirtualQueue, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0
+GenericRandomEarlyDetection.GenericRIO, config_parse_generic_random_early_detection_bool, QDISC_KIND_GRED, 0
+HierarchyTokenBucket.Parent, config_parse_qdisc_parent, QDISC_KIND_HTB, 0
+HierarchyTokenBucket.Handle, config_parse_qdisc_handle, QDISC_KIND_HTB, 0
+HierarchyTokenBucket.DefaultClass, config_parse_hierarchy_token_bucket_default_class, QDISC_KIND_HTB, 0
+HierarchyTokenBucketClass.Parent, config_parse_tclass_parent, TCLASS_KIND_HTB, 0
+HierarchyTokenBucketClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_HTB, 0
+HierarchyTokenBucketClass.Priority, config_parse_hierarchy_token_bucket_u32, TCLASS_KIND_HTB, 0
+HierarchyTokenBucketClass.Rate, config_parse_hierarchy_token_bucket_rate, TCLASS_KIND_HTB, 0
+HierarchyTokenBucketClass.CeilRate, config_parse_hierarchy_token_bucket_rate, TCLASS_KIND_HTB, 0
+NetworkEmulator.Parent, config_parse_qdisc_parent, QDISC_KIND_NETEM, 0
+NetworkEmulator.Handle, config_parse_qdisc_handle, QDISC_KIND_NETEM, 0
+NetworkEmulator.DelaySec, config_parse_network_emulator_delay, QDISC_KIND_NETEM, 0
+NetworkEmulator.DelayJitterSec, config_parse_network_emulator_delay, QDISC_KIND_NETEM, 0
+NetworkEmulator.LossRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0
+NetworkEmulator.DuplicateRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0
+NetworkEmulator.PacketLimit, config_parse_network_emulator_packet_limit, QDISC_KIND_NETEM, 0
+StochasticFairBlue.Parent, config_parse_qdisc_parent, QDISC_KIND_SFB, 0
+StochasticFairBlue.Handle, config_parse_qdisc_handle, QDISC_KIND_SFB, 0
+StochasticFairBlue.PacketLimit, config_parse_stochastic_fair_blue_u32, QDISC_KIND_SFB, 0
+StochasticFairnessQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_SFQ, 0
+StochasticFairnessQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_SFQ, 0
+StochasticFairnessQueueing.PerturbPeriodSec, config_parse_stochastic_fairness_queueing_perturb_period, QDISC_KIND_SFQ, 0
+TokenBucketFilter.Parent, config_parse_qdisc_parent, QDISC_KIND_TBF, 0
+TokenBucketFilter.Handle, config_parse_qdisc_handle, QDISC_KIND_TBF, 0
+TokenBucketFilter.Rate, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0
+TokenBucketFilter.Burst, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0
+TokenBucketFilter.LimitSize, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0
+TokenBucketFilter.MTUBytes, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0
+TokenBucketFilter.MPUBytes, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0
+TokenBucketFilter.PeakRate, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0
+TokenBucketFilter.LatencySec, config_parse_token_bucket_filter_latency, QDISC_KIND_TBF, 0
+TrivialLinkEqualizer.Parent, config_parse_qdisc_parent, QDISC_KIND_TEQL, 0
+TrivialLinkEqualizer.Handle, config_parse_qdisc_handle, QDISC_KIND_TEQL, 0
+TrivialLinkEqualizer.Id, config_parse_trivial_link_equalizer_id, QDISC_KIND_TEQL, 0
/* backwards compatibility: do not add new entries to this section */
-Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
-DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS, config_parse_dhcp_use_dns, 0, 0
-DHCP.UseNTP, config_parse_dhcp_use_ntp, 0, 0
-DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
-DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
-DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
-DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
-DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
-DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
-DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
-DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
-DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
-DHCP.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
-DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
-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.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
-DHCP.RouteTable, config_parse_section_route_table, 0, 0
-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.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
-DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
-DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
-DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
+Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
+DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
+DHCP.UseDNS, config_parse_dhcp_use_dns, 0, 0
+DHCP.UseNTP, config_parse_dhcp_use_ntp, 0, 0
+DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
+DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
+DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
+DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
+DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCP.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
+DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
+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.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+DHCP.RouteTable, config_parse_section_route_table, 0, 0
+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.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
+DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
+DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
+TrafficControlQueueingDiscipline.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec, config_parse_network_emulator_delay, 0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_network_emulator_delay, 0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_network_emulator_rate, 0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate, config_parse_network_emulator_rate, 0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_network_emulator_packet_limit, 0, 0
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
+#include "tc.h"
#include "util.h"
/* Let's assume that anything above this number is a user misconfiguration. */
Prefix *prefix, *prefix_next;
Route *route, *route_next;
FdbEntry *fdb, *fdb_next;
- QDisc *qdisc;
+ TrafficControl *tc;
Iterator i;
assert(network);
strv_isempty(network->match_path) && strv_isempty(network->match_driver) &&
strv_isempty(network->match_type) && strv_isempty(network->match_name) &&
strv_isempty(network->match_property) && strv_isempty(network->match_ssid) && !network->conditions)
- log_warning("%s: No valid settings found in the [Match] section. "
- "The file will match all interfaces. "
- "If that is intended, please add Name=* in the [Match] section.",
- network->filename);
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: No valid settings found in the [Match] section, ignoring file. "
+ "To match all interfaces, add Name=* in the [Match] section.",
+ network->filename);
/* skip out early if configuration does not match the environment */
if (!condition_test_list(network->conditions, NULL, NULL, NULL))
routing_policy_rule_free(rule);
bool has_root = false, has_clsact = false;
- ORDERED_HASHMAP_FOREACH(qdisc, network->qdiscs_by_section, i)
- if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0)
- qdisc_free(qdisc);
+ ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section, i)
+ if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
+ traffic_control_free(tc);
return 0;
}
.n_ref = 1,
.required_for_online = true,
- .required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
+ .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
.dhcp = ADDRESS_FAMILY_NO,
.dhcp_critical = -1,
.dhcp_use_ntp = true,
.ipv6_accept_ra_use_onlink_prefix = true,
.ipv6_accept_ra_route_table = RT_TABLE_MAIN,
.ipv6_accept_ra_route_table_set = false,
+ .ipv6_accept_ra_start_dhcp6_client = true,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
.can_triple_sampling = -1,
+ .can_termination = -1,
.ip_service_type = -1,
};
"IPv6Prefix\0"
"IPv6RoutePrefix\0"
"TrafficControlQueueingDiscipline\0"
- "CAN\0",
+ "CAN\0"
+ "QDisc\0"
+ "CAKE\0"
+ "ControlledDelay\0"
+ "PFIFO\0"
+ "FairQueueing\0"
+ "FairQueueingControlledDelay\0"
+ "GenericRandomEarlyDetection\0"
+ "HierarchyTokenBucket\0"
+ "HierarchyTokenBucketClass\0"
+ "NetworkEmulator\0"
+ "StochasticFairBlue\0"
+ "StochasticFairnessQueueing\0"
+ "TokenBucketFilter\0"
+ "TrivialLinkEqualizer\0",
config_item_perf_lookup, network_network_gperf_lookup,
CONFIG_PARSE_WARN, network);
if (r < 0)
hashmap_free(network->prefixes_by_section);
hashmap_free(network->route_prefixes_by_section);
hashmap_free(network->rules_by_section);
- ordered_hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
+ ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
if (network->manager &&
network->manager->duids_requesting_uuid)
ordered_hashmap_free(network->dhcp_client_send_options);
ordered_hashmap_free(network->dhcp_server_send_options);
+ ordered_hashmap_free(network->ipv6_tokens);
return mfree(network);
}
return 0;
}
-int network_get(Manager *manager, sd_device *device,
+int network_get(Manager *manager, unsigned short iftype, sd_device *device,
const char *ifname, char * const *alternative_names,
const struct ether_addr *address, const struct ether_addr *permanent_address,
enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid,
network->match_path, network->match_driver,
network->match_type, network->match_name, network->match_property,
network->match_wlan_iftype, network->match_ssid, network->match_bssid,
- device, address, permanent_address,
+ iftype, device, address, permanent_address,
ifname, alternative_names, wlan_iftype, ssid, bssid)) {
if (network->match_name && device) {
const char *attr;
void *userdata) {
Network *network = data;
- LinkOperationalState s;
+ LinkOperationalStateRange range;
bool required = true;
int r;
if (isempty(rvalue)) {
network->required_for_online = true;
- network->required_operstate_for_online = LINK_OPERSTATE_DEGRADED;
+ network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
return 0;
}
- s = link_operstate_from_string(rvalue);
- if (s < 0) {
+ r = parse_operational_state_range(rvalue, &range);
+ if (r < 0) {
r = parse_boolean(rvalue);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
}
required = r;
- s = LINK_OPERSTATE_DEGRADED;
+ range = LINK_OPERSTATE_RANGE_DEFAULT;
}
network->required_for_online = required;
- network->required_operstate_for_online = s;
+ network->required_operstate_for_online = range;
return 0;
}
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-rx.h"
#include "networkd-lldp-tx.h"
+#include "networkd-ndisc.h"
#include "networkd-neighbor.h"
#include "networkd-nexthop.h"
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-util.h"
#include "ordered-set.h"
-#include "qdisc.h"
#include "resolve-util.h"
typedef enum IPv6PrivacyExtensions {
/* DHCPv6 Client support*/
bool dhcp6_use_dns;
bool dhcp6_use_ntp;
+ bool dhcp6_without_ra;
uint8_t dhcp6_pd_length;
struct in6_addr dhcp6_pd_address;
uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
/* CAN support */
- size_t can_bitrate;
+ uint64_t can_bitrate;
unsigned can_sample_point;
usec_t can_restart_us;
int can_triple_sampling;
+ int can_termination;
AddressFamily ip_forward;
bool ip_masquerade;
bool ipv6_accept_ra_use_dns;
bool ipv6_accept_ra_use_autonomous_prefix;
bool ipv6_accept_ra_use_onlink_prefix;
+ bool ipv6_accept_ra_start_dhcp6_client;
bool active_slave;
bool primary_slave;
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;
+ OrderedHashmap *ipv6_tokens;
- union in_addr_union ipv6_token;
IPv6PrivacyExtensions ipv6_privacy_extensions;
struct ether_addr *mac;
bool iaid_set;
bool required_for_online; /* Is this network required to be considered online? */
- LinkOperationalState required_operstate_for_online;
+ LinkOperationalStateRange required_operstate_for_online;
LLDPMode lldp_mode; /* LLDP reception */
LLDPEmit lldp_emit; /* LLDP transmission */
Hashmap *prefixes_by_section;
Hashmap *route_prefixes_by_section;
Hashmap *rules_by_section;
- OrderedHashmap *qdiscs_by_section;
+ OrderedHashmap *tc_by_section;
/* All kinds of DNS configuration */
struct in_addr_data *dns;
int network_verify(Network *network);
int network_get_by_name(Manager *manager, const char *name, Network **ret);
-int network_get(Manager *manager, sd_device *device, const char *ifname, char * const *alternative_names,
+int network_get(Manager *manager, unsigned short iftype, sd_device *device, const char *ifname, char * const *alternative_names,
const struct ether_addr *mac, const struct ether_addr *permanent_mac,
enum nl80211_iftype wlan_iftype, const char *ssid,
const struct ether_addr *bssid, Network **ret);
return 0;
}
+int config_parse_prefix_assign(
+ 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_(prefix_free_or_set_invalidp) Prefix *p = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = prefix_new_static(network, filename, section_line, &p);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ p->assign = r;
+ p = NULL;
+
+ return 0;
+}
+
int config_parse_route_prefix(const char *unit,
const char *filename,
unsigned line,
static int radv_set_dns(Link *link, Link *uplink) {
_cleanup_free_ struct in6_addr *dns = NULL;
- size_t n_dns;
usec_t lifetime_usec;
+ size_t n_dns;
int r;
if (!link->network->router_emit_dns)
return 0;
if (link->network->router_dns) {
- dns = newdup(struct in6_addr, link->network->router_dns,
- link->network->n_router_dns);
+ struct in6_addr *p;
+
+ dns = new(struct in6_addr, link->network->n_router_dns);
if (!dns)
return -ENOMEM;
- n_dns = link->network->n_router_dns;
+ p = dns;
+ for (size_t i = 0; i < link->network->n_router_dns; i++)
+ if (IN6_IS_ADDR_UNSPECIFIED(&link->network->router_dns[i])) {
+ if (!IN6_IS_ADDR_UNSPECIFIED(&link->ipv6ll_address))
+ *(p++) = link->ipv6ll_address;
+ } else
+ *(p++) = link->network->router_dns[i];
+
+ n_dns = p - dns;
lifetime_usec = link->network->router_dns_lifetime_usec;
goto set_dns;
}
- return radv_emit_dns(link);
+ return 0;
}
int config_parse_radv_dns(
if (r == 0)
break;
- if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
- struct in6_addr *m;
+ if (streq(w, "_link_local"))
+ a = IN_ADDR_NULL;
+ else {
+ r = in_addr_from_string(AF_INET6, w, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DNS server address, ignoring: %s", w);
+ continue;
+ }
- m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
- if (!m)
- return log_oom();
+ if (in_addr_is_null(AF_INET6, &a)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "DNS server address is null, ignoring: %s", w);
+ continue;
+ }
+ }
- m[n->n_router_dns++] = a.in6;
- n->router_dns = m;
+ struct in6_addr *m;
+ m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
+ if (!m)
+ return log_oom();
- } else
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "Failed to parse DNS server address, ignoring: %s", w);
+ m[n->n_router_dns++] = a.in6;
+ n->router_dns = m;
}
return 0;
sd_radv_prefix *radv_prefix;
+ bool assign;
+
LIST_FIELDS(Prefix, prefixes);
};
CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);
#include "string-util.h"
#include "strxcpyx.h"
#include "sysctl-util.h"
-#include "util.h"
+#include "vrf.h"
#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
if (r < 0)
return r;
- if (streq(rvalue, "dhcp")) {
+ if (streq(rvalue, "_dhcp")) {
n->gateway_from_dhcp = true;
TAKE_PTR(n);
return 0;
route->section->filename, route->section->line);
}
+ if (!route->table_set && network->vrf) {
+ route->table = VRF(network->vrf)->table;
+ route->table_set = true;
+ }
+
if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
route->table = RT_TABLE_LOCAL;
#include "alloc-util.h"
#include "conf-parser.h"
#include "fileio.h"
+#include "format-util.h"
#include "ip-protocol-list.h"
#include "networkd-routing-policy-rule.h"
#include "netlink-util.h"
#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
int routing_policy_rule_new(RoutingPolicyRule **ret) {
RoutingPolicyRule *rule;
*rule = (RoutingPolicyRule) {
.table = RT_TABLE_MAIN,
+ .uid_range.start = UID_INVALID,
+ .uid_range.end = UID_INVALID,
+ .suppress_prefixlen = -1,
};
*ret = rule;
dest->protocol = src->protocol;
dest->sport = src->sport;
dest->dport = src->dport;
+ dest->uid_range = src->uid_range;
+ dest->suppress_prefixlen = src->suppress_prefixlen;
return 0;
}
siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state);
siphash24_compress(&rule->priority, sizeof(rule->priority), state);
siphash24_compress(&rule->table, sizeof(rule->table), state);
+ siphash24_compress(&rule->suppress_prefixlen, sizeof(rule->suppress_prefixlen), state);
siphash24_compress(&rule->protocol, sizeof(rule->protocol), state);
siphash24_compress(&rule->sport, sizeof(rule->sport), state);
siphash24_compress(&rule->dport, sizeof(rule->dport), state);
+ siphash24_compress(&rule->uid_range, sizeof(rule->uid_range), state);
if (rule->iif)
siphash24_compress(rule->iif, strlen(rule->iif), state);
if (r != 0)
return r;
+ r = CMP(a->suppress_prefixlen, b->suppress_prefixlen);
+ if (r != 0)
+ return r;
+
r = CMP(a->protocol, b->protocol);
if (r != 0)
return r;
if (r != 0)
return r;
+ r = memcmp(&a->uid_range, &b->uid_range, sizeof(a->uid_range));
+ if (r != 0)
+ return r;
+
r = strcmp_ptr(a->iif, b->iif);
if (r != 0)
return r;
return log_link_error_errno(link, r, "Could not append FRA_DPORT_RANGE attribute: %m");
}
+ if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) {
+ r = sd_netlink_message_append_data(m, FRA_UID_RANGE, &rule->uid_range, sizeof(rule->uid_range));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append FRA_UID_RANGE attribute: %m");
+ }
+
if (rule->invert_rule) {
r = sd_rtnl_message_routing_policy_rule_set_flags(m, FIB_RULE_INVERT);
if (r < 0)
return log_link_error_errno(link, r, "Could not append FIB_RULE_INVERT attribute: %m");
}
+ if (rule->suppress_prefixlen >= 0) {
+ r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_PREFIXLEN, (uint32_t) rule->suppress_prefixlen);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_PREFIXLEN attribute: %m");
+ }
+
rule->link = link;
r = netlink_call_async(link->manager->rtnl, NULL, m,
return 0;
}
+int config_parse_routing_policy_rule_uid_range(
+ 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_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
+ Network *network = userdata;
+ uid_t start, end;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = routing_policy_rule_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = get_user_creds(&rvalue, &start, NULL, NULL, NULL, 0);
+ if (r >= 0)
+ end = start;
+ else {
+ r = parse_uid_range(rvalue, &start, &end);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Invalid uid or uid range '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+ }
+
+ n->uid_range.start = start;
+ n->uid_range.end = end;
+ n = NULL;
+
+ return 0;
+}
+
+int config_parse_routing_policy_rule_suppress_prefixlen(
+ 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_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
+ Network *network = userdata;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = routing_policy_rule_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = parse_ip_prefix_length(rvalue, &n->suppress_prefixlen);
+ if (r == -ERANGE) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length outside of valid range 0-128, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ n = NULL;
+
+ return 0;
+}
+
static int routing_policy_rule_read_full_file(const char *state_file, char **ret) {
_cleanup_free_ char *s = NULL;
size_t size;
space = true;
}
+ if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) {
+ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+ fprintf(f, "%suidrange="UID_FMT"-"UID_FMT,
+ space ? " " : "",
+ rule->uid_range.start, rule->uid_range.end);
+ space = true;
+ }
+
+ if (rule->suppress_prefixlen >= 0) {
+ fprintf(f, "%ssuppress_prefixlen=%d",
+ space ? " " : "",
+ rule->suppress_prefixlen);
+ space = true;
+ }
+
fprintf(f, "%stable=%"PRIu32 "\n",
space ? " " : "",
rule->table);
continue;
}
} else if (streq(a, "fwmark")) {
-
r = parse_fwmark_fwmask(b, &rule->fwmark, &rule->fwmask);
if (r < 0) {
log_error_errno(r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a);
continue;
}
} else if (streq(a, "iif")) {
-
if (free_and_strdup(&rule->iif, b) < 0)
return log_oom();
continue;
}
} else if (streq(a, "sourceport")) {
-
r = parse_ip_port_range(b, &low, &high);
if (r < 0) {
- log_error_errno(r, "Invalid routing policy rule source port range, ignoring assignment:'%s'", b);
+ log_error_errno(r, "Invalid routing policy rule source port range, ignoring assignment: '%s'", b);
continue;
}
rule->sport.start = low;
rule->sport.end = high;
-
} else if (streq(a, "destinationport")) {
-
r = parse_ip_port_range(b, &low, &high);
if (r < 0) {
- log_error_errno(r, "Invalid routing policy rule destination port range, ignoring assignment:'%s'", b);
+ log_error_errno(r, "Invalid routing policy rule destination port range, ignoring assignment: '%s'", b);
continue;
}
rule->dport.start = low;
rule->dport.end = high;
+ } else if (streq(a, "uidrange")) {
+ uid_t lower, upper;
+
+ r = parse_uid_range(b, &lower, &upper);
+ if (r < 0) {
+ log_error_errno(r, "Invalid routing policy rule uid range, ignoring assignment: '%s'", b);
+ continue;
+ }
+
+ rule->uid_range.start = lower;
+ rule->uid_range.end = upper;
+ } else if (streq(a, "suppress_prefixlen")) {
+ r = parse_ip_prefix_length(b, &rule->suppress_prefixlen);
+ if (r == -ERANGE) {
+ log_error_errno(r, "Prefix length outside of valid range 0-128, ignoring: %s", b);
+ continue;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", b);
+ continue;
+ }
}
}
struct fib_rule_port_range sport;
struct fib_rule_port_range dport;
+ struct fib_rule_uid_range uid_range;
+
+ int suppress_prefixlen;
LIST_FIELDS(RoutingPolicyRule, rules);
};
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_ip_protocol);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_invert);
CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_family);
+CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_uid_range);
+CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_suppress_prefixlen);
[Network]
#SpeedMeter=no
#SpeedMeterIntervalSec=10sec
+#ManageForeignRoutes=yes
[DHCP]
#DUIDType=vendor
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
</action>
+ <action id="org.freedesktop.network1.forcerenew">
+ <description gettext-domain="systemd">DHCP server sends force renew message</description>
+ <message gettext-domain="systemd">Authentication is required to send force renew message.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
<action id="org.freedesktop.network1.renew">
<description gettext-domain="systemd">Renew dynamic addresses</description>
<message gettext-domain="systemd">Authentication is required to renew dynamic addresses.</message>
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "cake.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "string-util.h"
+
+static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ CommonApplicationsKeptEnhanced *c;
+ int r;
+
+ assert(link);
+ assert(qdisc);
+ assert(req);
+
+ c = CAKE(qdisc);
+
+ r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "cake");
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+ if (c->bandwidth > 0) {
+ r = sd_netlink_message_append_u64(req, TCA_CAKE_BASE_RATE64, c->bandwidth);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_BASE_RATE64 attribute: %m");
+ }
+
+ r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_OVERHEAD attribute: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+ return 0;
+}
+
+int config_parse_cake_bandwidth(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ Network *network = data;
+ uint64_t k;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ c = CAKE(qdisc);
+
+ if (isempty(rvalue)) {
+ c->bandwidth = 0;
+
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = parse_size(rvalue, 1000, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ c->bandwidth = k/8;
+ qdisc = NULL;
+
+ return 0;
+}
+
+int config_parse_cake_overhead(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ Network *network = data;
+ int32_t v;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ c = CAKE(qdisc);
+
+ if (isempty(rvalue)) {
+ c->overhead = 0;
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = safe_atoi32(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse 'Overhead=', ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+ if (v < -64 || v > 256) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid 'Overhead=', ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+
+ c->overhead = v;
+ qdisc = NULL;
+ return 0;
+}
+
+const QDiscVTable cake_vtable = {
+ .object_size = sizeof(CommonApplicationsKeptEnhanced),
+ .tca_kind = "cake",
+ .fill_message = cake_fill_message,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct CommonApplicationsKeptEnhanced {
+ QDisc meta;
+
+ int overhead;
+ uint64_t bandwidth;
+
+} CommonApplicationsKeptEnhanced;
+
+DEFINE_QDISC_CAST(CAKE, CommonApplicationsKeptEnhanced);
+extern const QDiscVTable cake_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_bandwidth);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_overhead);
return 0;
}
-int config_parse_tc_controlled_delay_u32(
+int config_parse_controlled_delay_u32(
const char *unit,
const char *filename,
unsigned line,
return 0;
}
-int config_parse_tc_controlled_delay_usec(
+int config_parse_controlled_delay_usec(
const char *unit,
const char *filename,
unsigned line,
cd = CODEL(qdisc);
- if (streq(lvalue, "ControlledDelayTargetSec"))
+ if (streq(lvalue, "TargetSec"))
p = &cd->target_usec;
- else if (streq(lvalue, "ControlledDelayIntervalSec"))
+ else if (streq(lvalue, "IntervalSec"))
p = &cd->interval_usec;
- else if (streq(lvalue, "ControlledDelayCEThresholdSec"))
+ else if (streq(lvalue, "CEThresholdSec"))
p = &cd->ce_threshold_usec;
else
assert_not_reached("Invalid lvalue");
if (isempty(rvalue)) {
- if (streq(lvalue, "ControlledDelayCEThresholdSec"))
+ if (streq(lvalue, "CEThresholdSec"))
*p = USEC_INFINITY;
else
*p = 0;
return 0;
}
-int config_parse_tc_controlled_delay_bool(
+int config_parse_controlled_delay_bool(
const char *unit,
const char *filename,
unsigned line,
DEFINE_QDISC_CAST(CODEL, ControlledDelay);
extern const QDiscVTable codel_vtable;
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_controlled_delay_u32);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_controlled_delay_usec);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_controlled_delay_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_controlled_delay_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_controlled_delay_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_controlled_delay_bool);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "fifo.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ struct tc_fifo_qopt opt = {};
+ FirstInFirstOut *fifo;
+ int r;
+
+ assert(link);
+ assert(qdisc);
+ assert(req);
+
+ fifo = PFIFO(qdisc);
+
+ opt.limit = fifo->limit;
+
+ r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_fifo_qopt));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m");
+
+ return 0;
+}
+
+int config_parse_fifo_size(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ Network *network = data;
+ FirstInFirstOut *fifo;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_PFIFO, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ fifo = PFIFO(qdisc);
+
+ if (isempty(rvalue)) {
+ fifo->limit = 0;
+
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &fifo->limit);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ qdisc = NULL;
+ return 0;
+}
+
+const QDiscVTable pfifo_vtable = {
+ .object_size = sizeof(FirstInFirstOut),
+ .tca_kind = "pfifo",
+ .fill_message = fifo_fill_message,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct FirstInFirstOut {
+ QDisc meta;
+
+ uint32_t limit;
+} FirstInFirstOut;
+
+DEFINE_QDISC_CAST(PFIFO, FirstInFirstOut);
+extern const QDiscVTable pfifo_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_fifo_size);
#include "qdisc.h"
#include "string-util.h"
-static int fair_queuing_controlled_delay_init(QDisc *qdisc) {
- FairQueuingControlledDelay *fqcd;
+static int fair_queueing_controlled_delay_init(QDisc *qdisc) {
+ FairQueueingControlledDelay *fqcd;
assert(qdisc);
return 0;
}
-static int fair_queuing_controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
- FairQueuingControlledDelay *fqcd;
+static int fair_queueing_controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ FairQueueingControlledDelay *fqcd;
int r;
assert(link);
return 0;
}
-int config_parse_tc_fair_queuing_controlled_delay_u32(
+int config_parse_fair_queueing_controlled_delay_u32(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueuingControlledDelay *fqcd;
+ FairQueueingControlledDelay *fqcd;
Network *network = data;
uint32_t *p;
int r;
fqcd = FQ_CODEL(qdisc);
- if (streq(lvalue, "FairQueuingControlledDelayPacketLimit"))
+ if (streq(lvalue, "PacketLimit"))
p = &fqcd->packet_limit;
- else if (streq(lvalue, "FairQueuingControlledDelayFlows"))
+ else if (streq(lvalue, "Flows"))
p = &fqcd->flows;
else
assert_not_reached("Invalid lvalue.");
return 0;
}
-int config_parse_tc_fair_queuing_controlled_delay_usec(
+int config_parse_fair_queueing_controlled_delay_usec(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueuingControlledDelay *fqcd;
+ FairQueueingControlledDelay *fqcd;
Network *network = data;
usec_t *p;
int r;
fqcd = FQ_CODEL(qdisc);
- if (streq(lvalue, "FairQueuingControlledDelayTargetSec"))
+ if (streq(lvalue, "TargetSec"))
p = &fqcd->target_usec;
- else if (streq(lvalue, "FairQueuingControlledDelayIntervalSec"))
+ else if (streq(lvalue, "IntervalSec"))
p = &fqcd->interval_usec;
- else if (streq(lvalue, "FairQueuingControlledDelayCEThresholdSec"))
+ else if (streq(lvalue, "CEThresholdSec"))
p = &fqcd->ce_threshold_usec;
else
assert_not_reached("Invalid lvalue.");
if (isempty(rvalue)) {
- if (streq(lvalue, "FairQueuingControlledDelayCEThresholdSec"))
+ if (streq(lvalue, "CEThresholdSec"))
*p = USEC_INFINITY;
else
*p = 0;
return 0;
}
-int config_parse_tc_fair_queuing_controlled_delay_bool(
+int config_parse_fair_queueing_controlled_delay_bool(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueuingControlledDelay *fqcd;
+ FairQueueingControlledDelay *fqcd;
Network *network = data;
int r;
return 0;
}
-int config_parse_tc_fair_queuing_controlled_delay_size(
+int config_parse_fair_queueing_controlled_delay_size(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueuingControlledDelay *fqcd;
+ FairQueueingControlledDelay *fqcd;
Network *network = data;
uint64_t sz;
uint32_t *p;
fqcd = FQ_CODEL(qdisc);
- if (streq(lvalue, "FairQueuingControlledDelayMemoryLimit"))
+ if (streq(lvalue, "MemoryLimit"))
p = &fqcd->memory_limit;
- else if (streq(lvalue, "FairQueuingControlledDelayQuantum"))
+ else if (streq(lvalue, "Quantum"))
p = &fqcd->quantum;
else
assert_not_reached("Invalid lvalue.");
if (isempty(rvalue)) {
- if (streq(lvalue, "FairQueuingControlledMemoryLimit"))
+ if (streq(lvalue, "MemoryLimit"))
*p = UINT32_MAX;
else
*p = 0;
}
const QDiscVTable fq_codel_vtable = {
- .object_size = sizeof(FairQueuingControlledDelay),
+ .object_size = sizeof(FairQueueingControlledDelay),
.tca_kind = "fq_codel",
- .init = fair_queuing_controlled_delay_init,
- .fill_message = fair_queuing_controlled_delay_fill_message,
+ .init = fair_queueing_controlled_delay_init,
+ .fill_message = fair_queueing_controlled_delay_fill_message,
};
#include "qdisc.h"
#include "time-util.h"
-typedef struct FairQueuingControlledDelay {
+typedef struct FairQueueingControlledDelay {
QDisc meta;
uint32_t packet_limit;
usec_t interval_usec;
usec_t ce_threshold_usec;
int ecn;
-} FairQueuingControlledDelay;
+} FairQueueingControlledDelay;
-DEFINE_QDISC_CAST(FQ_CODEL, FairQueuingControlledDelay);
+DEFINE_QDISC_CAST(FQ_CODEL, FairQueueingControlledDelay);
extern const QDiscVTable fq_codel_vtable;
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_u32);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_usec);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_bool);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_size);
#include "string-util.h"
#include "util.h"
-static int fair_queue_traffic_policing_init(QDisc *qdisc) {
- FairQueueTrafficPolicing *fq;
+static int fair_queueing_init(QDisc *qdisc) {
+ FairQueueing *fq;
assert(qdisc);
return 0;
}
-static int fair_queue_traffic_policing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
- FairQueueTrafficPolicing *fq;
+static int fair_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ FairQueueing *fq;
int r;
assert(link);
return 0;
}
-int config_parse_tc_fair_queue_traffic_policing_u32(
+int config_parse_fair_queueing_u32(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueueTrafficPolicing *fq;
+ FairQueueing *fq;
Network *network = data;
uint32_t *p;
int r;
fq = FQ(qdisc);
- if (streq(lvalue, "FairQueueTrafficPolicingPacketLimit"))
+ if (streq(lvalue, "PacketLimit"))
p = &fq->packet_limit;
- else if (streq(lvalue, "FairQueueTrafficPolicingFlowLimit"))
+ else if (streq(lvalue, "FlowLimit"))
p = &fq->flow_limit;
- else if (streq(lvalue, "FairQueueTrafficPolicingBuckets"))
+ else if (streq(lvalue, "Buckets"))
p = &fq->buckets;
- else if (streq(lvalue, "FairQueueTrafficPolicingOrphanMask"))
+ else if (streq(lvalue, "OrphanMask"))
p = &fq->orphan_mask;
else
assert_not_reached("Invalid lvalue");
return 0;
}
-int config_parse_tc_fair_queue_traffic_policing_size(
+int config_parse_fair_queueing_size(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueueTrafficPolicing *fq;
+ FairQueueing *fq;
Network *network = data;
uint64_t sz;
uint32_t *p;
fq = FQ(qdisc);
- if (streq(lvalue, "FairQueueTrafficPolicingQuantum"))
+ if (streq(lvalue, "Quantum"))
p = &fq->quantum;
- else if (streq(lvalue, "FairQueueTrafficPolicingInitialQuantum"))
+ else if (streq(lvalue, "InitialQuantum"))
p = &fq->initial_quantum;
else
assert_not_reached("Invalid lvalue");
return 0;
}
-int config_parse_tc_fair_queue_traffic_policing_bool(
+int config_parse_fair_queueing_bool(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueueTrafficPolicing *fq;
+ FairQueueing *fq;
Network *network = data;
int r;
return 0;
}
-int config_parse_tc_fair_queue_traffic_policing_usec(
+int config_parse_fair_queueing_usec(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueueTrafficPolicing *fq;
+ FairQueueing *fq;
Network *network = data;
usec_t sec;
int r;
return 0;
}
-int config_parse_tc_fair_queue_traffic_policing_max_rate(
+int config_parse_fair_queueing_max_rate(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
- FairQueueTrafficPolicing *fq;
+ FairQueueing *fq;
Network *network = data;
uint64_t sz;
int r;
}
const QDiscVTable fq_vtable = {
- .init = fair_queue_traffic_policing_init,
- .object_size = sizeof(FairQueueTrafficPolicing),
+ .init = fair_queueing_init,
+ .object_size = sizeof(FairQueueing),
.tca_kind = "fq",
- .fill_message = fair_queue_traffic_policing_fill_message,
+ .fill_message = fair_queueing_fill_message,
};
#include "conf-parser.h"
#include "qdisc.h"
-typedef struct FairQueueTrafficPolicing {
+typedef struct FairQueueing {
QDisc meta;
uint32_t packet_limit;
uint32_t orphan_mask;
int pacing;
usec_t ce_threshold_usec;
-} FairQueueTrafficPolicing;
+} FairQueueing;
-DEFINE_QDISC_CAST(FQ, FairQueueTrafficPolicing);
+DEFINE_QDISC_CAST(FQ, FairQueueing);
extern const QDiscVTable fq_vtable;
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_u32);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_size);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_bool);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_usec);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_max_rate);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_max_rate);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "string-util.h"
+
+static int generic_random_early_detection_init(QDisc *qdisc) {
+ GenericRandomEarlyDetection *gred;
+
+ assert(qdisc);
+
+ gred = GRED(qdisc);
+
+ gred->grio = -1;
+
+ return 0;
+}
+
+static int generic_random_early_detection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ GenericRandomEarlyDetection *gred;
+ struct tc_gred_sopt opt = {};
+ int r;
+
+ assert(link);
+ assert(qdisc);
+ assert(req);
+
+ gred = GRED(qdisc);
+
+ opt.DPs = gred->virtual_queues;
+ opt.def_DP = gred->default_virtual_queue;
+
+ if (gred->grio >= 0)
+ opt.grio = gred->grio;
+
+ r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "gred");
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+ r = sd_netlink_message_append_data(req, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_GRED_DPS attribute: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+ return 0;
+}
+
+static int generic_random_early_detection_verify(QDisc *qdisc) {
+ GenericRandomEarlyDetection *gred = GRED(qdisc);
+
+ if (gred->default_virtual_queue >= gred->virtual_queues)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: DefaultVirtualQueue= must be less than VirtualQueues=. "
+ "Ignoring [GenericRandomEarlyDetection] section from line %u.",
+ qdisc->section->filename, qdisc->section->line);
+
+ return 0;
+}
+
+int config_parse_generic_random_early_detection_u32(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ GenericRandomEarlyDetection *gred;
+ Network *network = data;
+ uint32_t *p;
+ uint32_t v;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ gred = GRED(qdisc);
+
+ if (streq(lvalue, "VirtualQueues"))
+ p = &gred->virtual_queues;
+ else if (streq(lvalue, "DefaultVirtualQueue"))
+ p = &gred->default_virtual_queue;
+ else
+ assert_not_reached("Invalid lvalue.");
+
+ if (isempty(rvalue)) {
+ *p = 0;
+
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ if (v > MAX_DPs) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ }
+
+ *p = v;
+ qdisc = NULL;
+
+ return 0;
+}
+int config_parse_generic_random_early_detection_bool(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ GenericRandomEarlyDetection *gred;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ gred = GRED(qdisc);
+
+ if (isempty(rvalue)) {
+ gred->grio = -1;
+
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ gred->grio = r;
+ qdisc = NULL;
+
+ return 0;
+}
+
+const QDiscVTable gred_vtable = {
+ .object_size = sizeof(GenericRandomEarlyDetection),
+ .tca_kind = "gred",
+ .init = generic_random_early_detection_init,
+ .fill_message = generic_random_early_detection_fill_message,
+ .verify = generic_random_early_detection_verify,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct GenericRandomEarlyDetection {
+ QDisc meta;
+
+ uint32_t virtual_queues;
+ uint32_t default_virtual_queue;
+ int grio;
+} GenericRandomEarlyDetection;
+
+DEFINE_QDISC_CAST(GRED, GenericRandomEarlyDetection);
+extern const QDiscVTable gred_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_bool);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "htb.h"
+#include "string-util.h"
+#include "tc-util.h"
+
+static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ HierarchyTokenBucket *htb;
+ struct tc_htb_glob opt = {
+ .rate2quantum = 10,
+ .version = 3,
+ };
+ int r;
+
+ assert(link);
+ assert(qdisc);
+ assert(req);
+
+ htb = HTB(qdisc);
+
+ opt.defcls = htb->default_class;
+
+ r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+ r = sd_netlink_message_append_data(req, TCA_HTB_INIT, &opt, sizeof(opt));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_HTB_INIT attribute: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+ return 0;
+}
+
+int config_parse_hierarchy_token_bucket_default_class(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ HierarchyTokenBucket *htb;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ htb = HTB(qdisc);
+
+ if (isempty(rvalue)) {
+ htb->default_class = 0;
+
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = safe_atou32_full(rvalue, 16, &htb->default_class);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ qdisc = NULL;
+
+ return 0;
+}
+
+const QDiscVTable htb_vtable = {
+ .object_size = sizeof(HierarchyTokenBucket),
+ .tca_kind = "htb",
+ .fill_message = hierarchy_token_bucket_fill_message,
+};
+
+static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
+ HierarchyTokenBucketClass *htb;
+ struct tc_htb_opt opt = {};
+ uint32_t rtab[256], ctab[256], mtu = 1600; /* Ethernet packet length */
+ int r;
+
+ assert(link);
+ assert(tclass);
+ assert(req);
+
+ htb = TCLASS_TO_HTB(tclass);
+
+ if (htb->ceil_rate == 0)
+ htb->ceil_rate = htb->rate;
+
+ opt.prio = htb->priority;
+ opt.rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate;
+ opt.ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate;
+ r = tc_transmit_time(htb->rate, mtu, &opt.buffer);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to calculate buffer size: %m");
+
+ r = tc_transmit_time(htb->ceil_rate, mtu, &opt.cbuffer);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to calculate ceil buffer size: %m");
+
+ r = tc_fill_ratespec_and_table(&opt.rate, rtab, mtu);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to calculate rate table: %m");
+
+ r = tc_fill_ratespec_and_table(&opt.ceil, ctab, mtu);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to calculate ceil rate table: %m");
+
+ r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+ r = sd_netlink_message_append_data(req, TCA_HTB_PARMS, &opt, sizeof(opt));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_HTB_PARMS attribute: %m");
+
+ if (htb->rate >= (1ULL << 32)) {
+ r = sd_netlink_message_append_u64(req, TCA_HTB_RATE64, htb->rate);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_HTB_RATE64 attribute: %m");
+ }
+
+ if (htb->ceil_rate >= (1ULL << 32)) {
+ r = sd_netlink_message_append_u64(req, TCA_HTB_CEIL64, htb->ceil_rate);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_HTB_CEIL64 attribute: %m");
+ }
+
+ r = sd_netlink_message_append_data(req, TCA_HTB_RTAB, rtab, sizeof(rtab));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_HTB_RTAB attribute: %m");
+
+ r = sd_netlink_message_append_data(req, TCA_HTB_CTAB, ctab, sizeof(ctab));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_HTB_CTAB attribute: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+ return 0;
+}
+
+int config_parse_hierarchy_token_bucket_u32(
+ 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+ HierarchyTokenBucketClass *htb;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to create traffic control class, ignoring assignment: %m");
+
+ htb = TCLASS_TO_HTB(tclass);
+
+ if (isempty(rvalue)) {
+ htb->priority = 0;
+
+ tclass = NULL;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &htb->priority);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ tclass = NULL;
+
+ return 0;
+}
+
+int config_parse_hierarchy_token_bucket_rate(
+ 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+ HierarchyTokenBucketClass *htb;
+ Network *network = data;
+ uint64_t *v;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to create traffic control class, ignoring assignment: %m");
+
+ htb = TCLASS_TO_HTB(tclass);
+ if (streq(lvalue, "Rate"))
+ v = &htb->rate;
+ else if (streq(lvalue, "CeilRate"))
+ v = &htb->ceil_rate;
+ else
+ assert_not_reached("Invalid lvalue");
+
+ if (isempty(rvalue)) {
+ *v = 0;
+
+ tclass = NULL;
+ return 0;
+ }
+
+ r = parse_size(rvalue, 1000, v);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ *v /= 8;
+ tclass = NULL;
+
+ return 0;
+}
+
+const TClassVTable htb_tclass_vtable = {
+ .object_size = sizeof(HierarchyTokenBucketClass),
+ .tca_kind = "htb",
+ .fill_message = hierarchy_token_bucket_class_fill_message,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+#include "tclass.h"
+
+typedef struct HierarchyTokenBucket {
+ QDisc meta;
+
+ uint32_t default_class;
+} HierarchyTokenBucket;
+
+DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket);
+extern const QDiscVTable htb_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class);
+
+typedef struct HierarchyTokenBucketClass {
+ TClass meta;
+
+ uint32_t priority;
+ uint64_t rate;
+ uint64_t ceil_rate;
+} HierarchyTokenBucketClass;
+
+DEFINE_TCLASS_CAST(HTB, HierarchyTokenBucketClass);
+extern const TClassVTable htb_tclass_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_rate);
#include "networkd-manager.h"
#include "parse-util.h"
#include "qdisc.h"
-#include "string-util.h"
+#include "strv.h"
#include "tc-util.h"
static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
return 0;
}
-int config_parse_tc_network_emulator_delay(
+int config_parse_network_emulator_delay(
const char *unit,
const char *filename,
unsigned line,
ne = NETEM(qdisc);
if (isempty(rvalue)) {
- if (streq(lvalue, "NetworkEmulatorDelaySec"))
+ if (STR_IN_SET(lvalue, "DelaySec", "NetworkEmulatorDelaySec"))
ne->delay = USEC_INFINITY;
- else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
+ else if (STR_IN_SET(lvalue, "DelayJitterSec", "NetworkEmulatorDelayJitterSec"))
ne->jitter = USEC_INFINITY;
qdisc = NULL;
return 0;
}
- if (streq(lvalue, "NetworkEmulatorDelaySec"))
+ if (STR_IN_SET(lvalue, "DelaySec", "NetworkEmulatorDelaySec"))
ne->delay = u;
- else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
+ else if (STR_IN_SET(lvalue, "DelayJitterSec", "NetworkEmulatorDelayJitterSec"))
ne->jitter = u;
qdisc = NULL;
return 0;
}
-int config_parse_tc_network_emulator_rate(
+int config_parse_network_emulator_rate(
const char *unit,
const char *filename,
unsigned line,
ne = NETEM(qdisc);
if (isempty(rvalue)) {
- if (streq(lvalue, "NetworkEmulatorLossRate"))
+ if (STR_IN_SET(lvalue, "LossRate", "NetworkEmulatorLossRate"))
ne->loss = 0;
- else if (streq(lvalue, "NetworkEmulatorDuplicateRate"))
+ else if (STR_IN_SET(lvalue, "DuplicateRate", "NetworkEmulatorDuplicateRate"))
ne->duplicate = 0;
qdisc = NULL;
return 0;
}
- if (streq(lvalue, "NetworkEmulatorLossRate"))
+ if (STR_IN_SET(lvalue, "LossRate", "NetworkEmulatorLossRate"))
ne->loss = rate;
- else if (streq(lvalue, "NetworkEmulatorDuplicateRate"))
+ else if (STR_IN_SET(lvalue, "DuplicateRate", "NetworkEmulatorDuplicateRate"))
ne->duplicate = rate;
qdisc = NULL;
return 0;
}
-int config_parse_tc_network_emulator_packet_limit(
+int config_parse_network_emulator_packet_limit(
const char *unit,
const char *filename,
unsigned line,
r = safe_atou(rvalue, &ne->limit);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse 'NetworkEmulatorPacketLimit=', ignoring assignment: %s",
- rvalue);
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
return 0;
}
DEFINE_QDISC_CAST(NETEM, NetworkEmulator);
extern const QDiscVTable netem_vtable;
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_rate);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_packet_limit);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_emulator_delay);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_emulator_rate);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_emulator_packet_limit);
#include "qdisc.h"
#include "set.h"
#include "string-util.h"
+#include "strv.h"
+#include "tc-util.h"
const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
+ [QDISC_KIND_CAKE] = &cake_vtable,
[QDISC_KIND_CODEL] = &codel_vtable,
[QDISC_KIND_FQ] = &fq_vtable,
[QDISC_KIND_FQ_CODEL] = &fq_codel_vtable,
+ [QDISC_KIND_GRED] = &gred_vtable,
+ [QDISC_KIND_HTB] = &htb_vtable,
[QDISC_KIND_NETEM] = &netem_vtable,
+ [QDISC_KIND_PFIFO] = &pfifo_vtable,
+ [QDISC_KIND_SFB] = &sfb_vtable,
[QDISC_KIND_SFQ] = &sfq_vtable,
[QDISC_KIND_TBF] = &tbf_vtable,
+ [QDISC_KIND_TEQL] = &teql_vtable,
};
static int qdisc_new(QDiscKind kind, QDisc **ret) {
return -ENOMEM;
*qdisc = (QDisc) {
+ .meta.kind = TC_KIND_QDISC,
.family = AF_UNSPEC,
.parent = TC_H_ROOT,
.kind = kind,
if (!qdisc)
return -ENOMEM;
+ qdisc->meta.kind = TC_KIND_QDISC,
qdisc->family = AF_UNSPEC;
qdisc->parent = TC_H_ROOT;
qdisc->kind = kind;
int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(qdisc_freep) QDisc *qdisc = NULL;
- QDisc *existing;
+ TrafficControl *existing;
+ QDisc *q = NULL;
int r;
assert(network);
if (r < 0)
return r;
- existing = ordered_hashmap_get(network->qdiscs_by_section, n);
+ existing = ordered_hashmap_get(network->tc_by_section, n);
if (existing) {
- if (existing->kind != _QDISC_KIND_INVALID &&
+ if (existing->kind != TC_KIND_QDISC)
+ return -EINVAL;
+
+ q = TC_TO_QDISC(existing);
+
+ if (q->kind != _QDISC_KIND_INVALID &&
kind != _QDISC_KIND_INVALID &&
- existing->kind != kind)
+ q->kind != kind)
return -EINVAL;
- if (existing->kind == kind || kind == _QDISC_KIND_INVALID) {
- *ret = existing;
+ if (q->kind == kind || kind == _QDISC_KIND_INVALID) {
+ *ret = q;
return 0;
}
}
if (r < 0)
return r;
- if (existing) {
- qdisc->family = existing->family;
- qdisc->handle = existing->handle;
- qdisc->parent = existing->parent;
- qdisc->tca_kind = TAKE_PTR(existing->tca_kind);
+ if (q) {
+ qdisc->family = q->family;
+ qdisc->handle = q->handle;
+ qdisc->parent = q->parent;
+ qdisc->tca_kind = TAKE_PTR(q->tca_kind);
- qdisc_free(ordered_hashmap_remove(network->qdiscs_by_section, n));
+ qdisc_free(q);
}
qdisc->network = network;
qdisc->section = TAKE_PTR(n);
- r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
+ r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
if (r < 0)
return r;
- r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
+ r = ordered_hashmap_put(network->tc_by_section, qdisc->section, TC(qdisc));
if (r < 0)
return r;
return;
if (qdisc->network && qdisc->section)
- ordered_hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
+ ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section);
network_config_section_free(qdisc->section);
int r;
assert(link);
- assert(link->qdisc_messages > 0);
- link->qdisc_messages--;
+ assert(link->tc_messages > 0);
+ link->tc_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
- log_link_message_error_errno(link, m, r, "Could not set QDisc: %m");
+ log_link_message_error_errno(link, m, r, "Could not set QDisc");
link_enter_failed(link);
return 1;
}
- if (link->qdisc_messages == 0) {
- log_link_debug(link, "QDisc configured");
- link->qdiscs_configured = true;
+ if (link->tc_messages == 0) {
+ log_link_debug(link, "Traffic control configured");
+ link->tc_configured = true;
link_check_ready(link);
}
}
if (QDISC_VTABLE(qdisc)) {
- r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+ if (QDISC_VTABLE(qdisc)->fill_tca_kind) {
+ r = QDISC_VTABLE(qdisc)->fill_tca_kind(link, qdisc, req);
+ if (r < 0)
+ return r;
+ } else {
+ r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+ }
- r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req);
- if (r < 0)
- return r;
+ if (QDISC_VTABLE(qdisc)->fill_message) {
+ r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req);
+ if (r < 0)
+ return r;
+ }
} else {
r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
- link->qdisc_messages++;
+ link->tc_messages++;
return 0;
}
if (qdisc->parent == TC_H_ROOT) {
if (*has_root)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: More than one root TrafficControlQueueingDiscipline sections are defined. "
- "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+ "%s: More than one root qdisc section is defined. "
+ "Ignoring the qdisc section from line %u.",
qdisc->section->filename, qdisc->section->line);
*has_root = true;
} else if (qdisc->parent == TC_H_CLSACT) { /* TC_H_CLSACT == TC_H_INGRESS */
if (*has_clsact)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: More than one clsact or ingress TrafficControlQueueingDiscipline sections are defined. "
- "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+ "%s: More than one clsact or ingress qdisc section is defined. "
+ "Ignoring the qdisc section from line %u.",
qdisc->section->filename, qdisc->section->line);
*has_clsact = true;
}
return 0;
}
-int config_parse_tc_qdiscs_parent(
+int config_parse_qdisc_parent(
const char *unit,
const char *filename,
unsigned line,
assert(rvalue);
assert(data);
- r = qdisc_new_static(_QDISC_KIND_INVALID, network, filename, section_line, &qdisc);
+ r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
if (r < 0)
return r;
if (streq(rvalue, "root")) {
qdisc->parent = TC_H_ROOT;
- qdisc->handle = TC_H_UNSPEC;
+ if (qdisc->handle == 0)
+ qdisc->handle = TC_H_UNSPEC;
} else if (streq(rvalue, "clsact")) {
qdisc->parent = TC_H_CLSACT;
qdisc->handle = TC_H_MAKE(TC_H_CLSACT, 0);
qdisc->parent = TC_H_INGRESS;
qdisc->handle = TC_H_MAKE(TC_H_INGRESS, 0);
} else {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse 'Parent=', ignoring assignment: %s",
- rvalue);
- return 0;
+ r = parse_handle(rvalue, &qdisc->parent);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse 'Parent=', ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
}
- if (streq(rvalue, "root"))
- qdisc->tca_kind = mfree(qdisc->tca_kind);
- else {
+ if (STR_IN_SET(rvalue, "clsact", "ingress")) {
r = free_and_strdup(&qdisc->tca_kind, rvalue);
if (r < 0)
return log_oom();
+ } else
+ qdisc->tca_kind = mfree(qdisc->tca_kind);
+
+ qdisc = NULL;
+
+ return 0;
+}
+
+int config_parse_qdisc_handle(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ Network *network = data;
+ uint16_t n;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ qdisc->handle = TC_H_UNSPEC;
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = safe_atou16_full(rvalue, 16, &n);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse 'Handle=', ignoring assignment: %s",
+ rvalue);
+ return 0;
}
+ qdisc->handle = (uint32_t) n << 16;
qdisc = NULL;
return 0;
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
+#include "tc.h"
typedef enum QDiscKind {
+ QDISC_KIND_CAKE,
QDISC_KIND_CODEL,
QDISC_KIND_FQ,
QDISC_KIND_FQ_CODEL,
+ QDISC_KIND_GRED,
+ QDISC_KIND_HTB,
QDISC_KIND_NETEM,
+ QDISC_KIND_PFIFO,
+ QDISC_KIND_SFB,
QDISC_KIND_SFQ,
QDISC_KIND_TBF,
+ QDISC_KIND_TEQL,
_QDISC_KIND_MAX,
_QDISC_KIND_INVALID = -1,
} QDiscKind;
typedef struct QDisc {
+ TrafficControl meta;
+
NetworkConfigSection *section;
Network *network;
const char *tca_kind;
/* called in qdisc_new() */
int (*init)(QDisc *qdisc);
+ int (*fill_tca_kind)(Link *link, QDisc *qdisc, sd_netlink_message *m);
int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m);
int (*verify)(QDisc *qdisc);
} QDiscVTable;
DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent);
+DEFINE_TC_CAST(QDISC, QDisc);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_parent);
+CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle);
+#include "cake.h"
#include "codel.h"
+#include "fifo.h"
#include "fq-codel.h"
#include "fq.h"
+#include "gred.h"
+#include "htb.h"
#include "netem.h"
+#include "sfb.h"
#include "sfq.h"
#include "tbf.h"
+#include "teql.h"
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "sfb.h"
+#include "string-util.h"
+
+static int stochastic_fair_blue_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ StochasticFairBlue *sfb;
+ struct tc_sfb_qopt opt = {
+ .rehash_interval = 600*1000,
+ .warmup_time = 60*1000,
+ .penalty_rate = 10,
+ .penalty_burst = 20,
+ .increment = (SFB_MAX_PROB + 1000) / 2000,
+ .decrement = (SFB_MAX_PROB + 10000) / 20000,
+ .max = 25,
+ .bin_size = 20,
+ };
+ int r;
+
+ assert(link);
+ assert(qdisc);
+ assert(req);
+
+ sfb = SFB(qdisc);
+
+ opt.limit = sfb->packet_limit;
+
+ r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "sfb");
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+ r = sd_netlink_message_append_data(req, TCA_SFB_PARMS, &opt, sizeof(struct tc_sfb_qopt));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_SFB_PARMS attribute: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+ return 0;
+}
+
+int config_parse_stochastic_fair_blue_u32(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ StochasticFairBlue *sfb;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_SFB, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ sfb = SFB(qdisc);
+
+ if (isempty(rvalue)) {
+ sfb->packet_limit = 0;
+
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &sfb->packet_limit);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ qdisc = NULL;
+
+ return 0;
+}
+
+const QDiscVTable sfb_vtable = {
+ .object_size = sizeof(StochasticFairBlue),
+ .tca_kind = "sfb",
+ .fill_message = stochastic_fair_blue_fill_message,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct StochasticFairBlue {
+ QDisc meta;
+
+ uint32_t packet_limit;
+} StochasticFairBlue;
+
+DEFINE_QDISC_CAST(SFB, StochasticFairBlue);
+extern const QDiscVTable sfb_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_stochastic_fair_blue_u32);
return 0;
}
-int config_parse_tc_stochastic_fairness_queueing_perturb_period(
+int config_parse_stochastic_fairness_queueing_perturb_period(
const char *unit,
const char *filename,
unsigned line,
DEFINE_QDISC_CAST(SFQ, StochasticFairnessQueueing);
extern const QDiscVTable sfq_vtable;
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_stochastic_fairness_queueing_perturb_period);
+CONFIG_PARSER_PROTOTYPE(config_parse_stochastic_fairness_queueing_perturb_period);
#include "tc-util.h"
#include "util.h"
-static int token_buffer_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
uint32_t rtab[256], ptab[256];
struct tc_tbf_qopt opt = {};
- TokenBufferFilter *tbf;
+ TokenBucketFilter *tbf;
int r;
assert(link);
return 0;
}
-int config_parse_tc_token_buffer_filter_size(
+int config_parse_token_bucket_filter_size(
const char *unit,
const char *filename,
unsigned line,
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
Network *network = data;
- TokenBufferFilter *tbf;
+ TokenBucketFilter *tbf;
uint64_t k;
int r;
tbf = TBF(qdisc);
if (isempty(rvalue)) {
- if (streq(lvalue, "TokenBufferFilterRate"))
+ if (streq(lvalue, "Rate"))
tbf->rate = 0;
- else if (streq(lvalue, "TokenBufferFilterBurst"))
+ else if (streq(lvalue, "Burst"))
tbf->burst = 0;
- else if (streq(lvalue, "TokenBufferFilterLimitSize"))
+ else if (streq(lvalue, "LimitSize"))
tbf->limit = 0;
- else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
+ else if (streq(lvalue, "MTUBytes"))
tbf->mtu = 0;
- else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
+ else if (streq(lvalue, "MPUBytes"))
tbf->mpu = 0;
- else if (streq(lvalue, "TokenBufferFilterPeakRate"))
+ else if (streq(lvalue, "PeakRate"))
tbf->peak_rate = 0;
qdisc = NULL;
return 0;
}
- if (streq(lvalue, "TokenBufferFilterRate"))
+ if (streq(lvalue, "Rate"))
tbf->rate = k / 8;
- else if (streq(lvalue, "TokenBufferFilterBurst"))
+ else if (streq(lvalue, "Burst"))
tbf->burst = k;
- else if (streq(lvalue, "TokenBufferFilterLimitSize"))
+ else if (streq(lvalue, "LimitSize"))
tbf->limit = k;
- else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
+ else if (streq(lvalue, "MPUBytes"))
tbf->mpu = k;
- else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
+ else if (streq(lvalue, "MTUBytes"))
tbf->mtu = k;
- else if (streq(lvalue, "TokenBufferFilterPeakRate"))
+ else if (streq(lvalue, "PeakRate"))
tbf->peak_rate = k / 8;
qdisc = NULL;
return 0;
}
-int config_parse_tc_token_buffer_filter_latency(
+int config_parse_token_bucket_filter_latency(
const char *unit,
const char *filename,
unsigned line,
_cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
Network *network = data;
- TokenBufferFilter *tbf;
+ TokenBucketFilter *tbf;
usec_t u;
int r;
return 0;
}
-static int token_buffer_filter_verify(QDisc *qdisc) {
- TokenBufferFilter *tbf = TBF(qdisc);
+static int token_bucket_filter_verify(QDisc *qdisc) {
+ TokenBucketFilter *tbf = TBF(qdisc);
if (tbf->limit > 0 && tbf->latency > 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: Specifying both TokenBufferFilterLimitSize= and TokenBufferFilterLatencySec= is not allowed. "
- "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+ "%s: Specifying both LimitSize= and LatencySec= is not allowed. "
+ "Ignoring [TokenBucketFilter] section from line %u.",
qdisc->section->filename, qdisc->section->line);
if (tbf->limit == 0 && tbf->latency == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: Either TokenBufferFilterLimitSize= or TokenBufferFilterLatencySec= is required. "
- "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+ "%s: Either LimitSize= or LatencySec= is required. "
+ "Ignoring [TokenBucketFilter] section from line %u.",
qdisc->section->filename, qdisc->section->line);
if (tbf->rate == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: TokenBufferFilterRate= is mandatory. "
- "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+ "%s: Rate= is mandatory. "
+ "Ignoring [TokenBucketFilter] section from line %u.",
qdisc->section->filename, qdisc->section->line);
if (tbf->burst == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: TokenBufferFilterBurst= is mandatory. "
- "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+ "%s: Burst= is mandatory. "
+ "Ignoring [TokenBucketFilter] section from line %u.",
qdisc->section->filename, qdisc->section->line);
if (tbf->peak_rate > 0 && tbf->mtu == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: TokenBufferFilterMTUBytes= is mandatory when TokenBufferFilterPeakRate= is specified. "
- "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+ "%s: MTUBytes= is mandatory when PeakRate= is specified. "
+ "Ignoring [TokenBucketFilter] section from line %u.",
qdisc->section->filename, qdisc->section->line);
return 0;
}
const QDiscVTable tbf_vtable = {
- .object_size = sizeof(TokenBufferFilter),
+ .object_size = sizeof(TokenBucketFilter),
.tca_kind = "tbf",
- .fill_message = token_buffer_filter_fill_message,
- .verify = token_buffer_filter_verify
+ .fill_message = token_bucket_filter_fill_message,
+ .verify = token_bucket_filter_verify
};
#include "qdisc.h"
#include "time-util.h"
-typedef struct TokenBufferFilter {
+typedef struct TokenBucketFilter {
QDisc meta;
uint64_t rate;
usec_t latency;
size_t limit;
size_t mpu;
-} TokenBufferFilter;
+} TokenBucketFilter;
-DEFINE_QDISC_CAST(TBF, TokenBufferFilter);
+DEFINE_QDISC_CAST(TBF, TokenBucketFilter);
extern const QDiscVTable tbf_vtable;
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_latency);
+CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_size);
* Copyright © 2019 VMware, Inc. */
#include "alloc-util.h"
+#include "extract-word.h"
#include "fileio.h"
#include "parse-util.h"
#include "tc-util.h"
rate->linklayer = TC_LINKLAYER_ETHERNET;
return 0;
}
+
+int parse_handle(const char *t, uint32_t *ret) {
+ _cleanup_free_ char *word = NULL;
+ uint16_t major, minor;
+ int r;
+
+ assert(t);
+ assert(ret);
+
+ /* Extract the major number. */
+ r = extract_first_word(&t, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+ if (!t)
+ return -EINVAL;
+
+ r = safe_atou16_full(word, 16, &major);
+ if (r < 0)
+ return r;
+
+ r = safe_atou16_full(t, 16, &minor);
+ if (r < 0)
+ return r;
+
+ *ret = ((uint32_t) major << 16) | minor;
+ return 0;
+}
int parse_tc_percent(const char *s, uint32_t *percent);
int tc_transmit_time(uint64_t rate, uint32_t size, uint32_t *ret);
int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_t mtu);
+int parse_handle(const char *t, uint32_t *ret);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "macro.h"
+#include "qdisc.h"
+#include "tc.h"
+#include "tclass.h"
+
+void traffic_control_free(TrafficControl *tc) {
+ if (!tc)
+ return;
+
+ switch (tc->kind) {
+ case TC_KIND_QDISC:
+ qdisc_free(TC_TO_QDISC(tc));
+ break;
+ case TC_KIND_TCLASS:
+ tclass_free(TC_TO_TCLASS(tc));
+ break;
+ default:
+ assert_not_reached("Invalid traffic control type");
+ }
+}
+
+int traffic_control_configure(Link *link, TrafficControl *tc) {
+ assert(link);
+ assert(tc);
+
+ switch(tc->kind) {
+ case TC_KIND_QDISC:
+ return qdisc_configure(link, TC_TO_QDISC(tc));
+ case TC_KIND_TCLASS:
+ return tclass_configure(link, TC_TO_TCLASS(tc));
+ default:
+ assert_not_reached("Invalid traffic control type");
+ }
+}
+
+int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) {
+ assert(tc);
+
+ switch(tc->kind) {
+ case TC_KIND_QDISC:
+ return qdisc_section_verify(TC_TO_QDISC(tc), qdisc_has_root, qdisc_has_clsact);
+ case TC_KIND_TCLASS:
+ return tclass_section_verify(TC_TO_TCLASS(tc));
+ default:
+ assert_not_reached("Invalid traffic control type");
+ }
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "networkd-link.h"
+
+typedef enum TrafficControlKind {
+ TC_KIND_QDISC,
+ TC_KIND_TCLASS,
+ TC_KIND_FILTER,
+ _TC_KIND_MAX,
+ _TC_KIND_INVALID = -1,
+} TrafficControlKind;
+
+typedef struct TrafficControl {
+ TrafficControlKind kind;
+} TrafficControl;
+
+/* For casting a tc into the various tc kinds */
+#define DEFINE_TC_CAST(UPPERCASE, MixedCase) \
+ static inline MixedCase* TC_TO_##UPPERCASE(TrafficControl *tc) { \
+ if (_unlikely_(!tc || tc->kind != TC_KIND_##UPPERCASE)) \
+ return NULL; \
+ \
+ return (MixedCase*) tc; \
+ }
+
+/* For casting the various tc kinds into a tc */
+#define TC(tc) (&(tc)->meta)
+
+void traffic_control_free(TrafficControl *tc);
+int traffic_control_configure(Link *link, TrafficControl *tc);
+int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tc-util.h"
+#include "tclass.h"
+
+const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
+ [TCLASS_KIND_HTB] = &htb_tclass_vtable,
+};
+
+static int tclass_new(TClassKind kind, TClass **ret) {
+ TClass *tclass;
+ int r;
+
+ tclass = malloc0(tclass_vtable[kind]->object_size);
+ if (!tclass)
+ return -ENOMEM;
+
+ tclass->meta.kind = TC_KIND_TCLASS,
+ tclass->parent = TC_H_ROOT;
+ tclass->kind = kind;
+
+ if (TCLASS_VTABLE(tclass)->init) {
+ r = TCLASS_VTABLE(tclass)->init(tclass);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(tclass);
+
+ return 0;
+}
+
+int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
+ _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(tclass_freep) TClass *tclass = NULL;
+ TrafficControl *existing;
+ int r;
+
+ assert(network);
+ assert(ret);
+ assert(filename);
+ assert(section_line > 0);
+
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ existing = ordered_hashmap_get(network->tc_by_section, n);
+ if (existing) {
+ TClass *t;
+
+ if (existing->kind != TC_KIND_TCLASS)
+ return -EINVAL;
+
+ t = TC_TO_TCLASS(existing);
+
+ if (t->kind != kind)
+ return -EINVAL;
+
+ *ret = t;
+ return 0;
+ }
+
+ r = tclass_new(kind, &tclass);
+ if (r < 0)
+ return r;
+
+ tclass->network = network;
+ tclass->section = TAKE_PTR(n);
+
+ r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(tclass);
+ return 0;
+}
+
+void tclass_free(TClass *tclass) {
+ if (!tclass)
+ return;
+
+ if (tclass->network && tclass->section)
+ ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
+
+ network_config_section_free(tclass->section);
+
+ free(tclass);
+}
+
+static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->tc_messages > 0);
+ link->tc_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_message_error_errno(link, m, r, "Could not set TClass");
+ link_enter_failed(link);
+ return 1;
+ }
+
+ if (link->tc_messages == 0) {
+ log_link_debug(link, "Traffic control configured");
+ link->tc_configured = true;
+ link_check_ready(link);
+ }
+
+ return 1;
+}
+
+int tclass_configure(Link *link, TClass *tclass) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+ assert(link->ifindex > 0);
+
+ r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m");
+
+ r = sd_rtnl_message_set_tclass_parent(req, tclass->parent);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
+
+ if (tclass->classid != TC_H_UNSPEC) {
+ r = sd_rtnl_message_set_tclass_handle(req, tclass->classid);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
+ }
+
+ r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+
+ if (TCLASS_VTABLE(tclass)->fill_message) {
+ r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req);
+ if (r < 0)
+ return r;
+ }
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_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);
+ link->tc_messages++;
+
+ return 0;
+}
+
+int tclass_section_verify(TClass *tclass) {
+ int r;
+
+ assert(tclass);
+
+ if (section_is_invalid(tclass->section))
+ return -EINVAL;
+
+ if (TCLASS_VTABLE(tclass)->verify) {
+ r = TCLASS_VTABLE(tclass)->verify(tclass);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int config_parse_tclass_parent(
+ 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = tclass_new_static(ltype, network, filename, section_line, &tclass);
+ if (r < 0)
+ return r;
+
+ if (streq(rvalue, "root"))
+ tclass->parent = TC_H_ROOT;
+ else {
+ r = parse_handle(rvalue, &tclass->parent);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse 'Parent=', ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+ }
+
+ tclass = NULL;
+
+ return 0;
+}
+
+int config_parse_tclass_classid(
+ 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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = tclass_new_static(ltype, network, filename, section_line, &tclass);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ tclass->classid = TC_H_UNSPEC;
+ tclass = NULL;
+ return 0;
+ }
+
+ r = parse_handle(rvalue, &tclass->classid);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse 'ClassId=', ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+
+ tclass = NULL;
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd-util.h"
+#include "tc.h"
+
+typedef enum TClassKind {
+ TCLASS_KIND_HTB,
+ _TCLASS_KIND_MAX,
+ _TCLASS_KIND_INVALID = -1,
+} TClassKind;
+
+typedef struct TClass {
+ TrafficControl meta;
+
+ NetworkConfigSection *section;
+ Network *network;
+
+ uint32_t classid;
+ uint32_t parent;
+
+ TClassKind kind;
+} TClass;
+
+typedef struct TClassVTable {
+ size_t object_size;
+ const char *tca_kind;
+ /* called in tclass_new() */
+ int (*init)(TClass *tclass);
+ int (*fill_message)(Link *link, TClass *tclass, sd_netlink_message *m);
+ int (*verify)(TClass *tclass);
+} TClassVTable;
+
+extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX];
+
+#define TCLASS_VTABLE(t) ((t)->kind != _TCLASS_KIND_INVALID ? tclass_vtable[(t)->kind] : NULL)
+
+/* For casting a tclass into the various tclass kinds */
+#define DEFINE_TCLASS_CAST(UPPERCASE, MixedCase) \
+ static inline MixedCase* TCLASS_TO_##UPPERCASE(TClass *t) { \
+ if (_unlikely_(!t || t->kind != TCLASS_KIND_##UPPERCASE)) \
+ return NULL; \
+ \
+ return (MixedCase*) t; \
+ }
+
+/* For casting the various tclass kinds into a tclass */
+#define TCLASS(t) (&(t)->meta)
+
+void tclass_free(TClass *tclass);
+int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
+
+int tclass_configure(Link *link, TClass *tclass);
+int tclass_section_verify(TClass *tclass);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free);
+
+DEFINE_TC_CAST(TCLASS, TClass);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent);
+CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid);
+
+#include "htb.h"
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "macro.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "teql.h"
+
+static int trivial_link_equalizer_fill_tca_kind(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+ char kind[STRLEN("teql") + DECIMAL_STR_MAX(unsigned)];
+ TrivialLinkEqualizer *teql;
+ int r;
+
+ assert(link);
+ assert(qdisc);
+ assert(req);
+
+ teql = TEQL(qdisc);
+
+ xsprintf(kind, "teql%u", teql->id);
+ r = sd_netlink_message_append_string(req, TCA_KIND, kind);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+
+ return 0;
+}
+
+const QDiscVTable teql_vtable = {
+ .object_size = sizeof(TrivialLinkEqualizer),
+ .fill_tca_kind = trivial_link_equalizer_fill_tca_kind,
+};
+
+int config_parse_trivial_link_equalizer_id(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ TrivialLinkEqualizer *teql;
+ Network *network = data;
+ unsigned id;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_TEQL, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+
+ teql = TEQL(qdisc);
+
+ if (isempty(rvalue)) {
+ teql->id = 0;
+
+ qdisc = NULL;
+ return 0;
+ }
+
+ r = safe_atou(rvalue, &id);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+ if (id > INT_MAX) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "'%s=' is too large, ignoring assignment: %s",
+ lvalue, rvalue);
+ }
+
+ teql->id = id;
+
+ qdisc = NULL;
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct TrivialLinkEqualizer {
+ QDisc meta;
+
+ unsigned id;
+} TrivialLinkEqualizer;
+
+DEFINE_QDISC_CAST(TEQL, TrivialLinkEqualizer);
+extern const QDiscVTable teql_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_trivial_link_equalizer_id);
static void test_network_get(Manager *manager, sd_device *loopback) {
Network *network;
const struct ether_addr mac = ETHER_ADDR_NULL;
+ int r;
- /* let's assume that the test machine does not have a .network file
- that applies to the loopback device... */
- assert_se(network_get(manager, loopback, "lo", NULL, &mac, &mac, 0, NULL, NULL, &network) == -ENOENT);
- assert_se(!network);
+ /* Let's hope that the test machine does not have a .network file that applies to loopback device…
+ * But it is still possible, so let's allow that case too. */
+ r = network_get(manager, 0, loopback, "lo", NULL, &mac, &mac, 0, NULL, NULL, &network);
+ if (r == -ENOENT)
+ /* The expected case */
+ assert_se(!network);
+ else if (r >= 0)
+ assert_se(network);
+ else
+ assert_not_reached("bad error!");
}
static void test_address_equality(void) {
.manager = m,
.ifname = TAKE_PTR(n),
.ifindex = ifindex,
- .required_operstate = LINK_OPERSTATE_DEGRADED,
+ .required_operstate = LINK_OPERSTATE_RANGE_DEFAULT,
};
r = hashmap_put(m->links_by_name, l->ifname, l);
int link_update_monitor(Link *l) {
_cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *state = NULL;
- LinkOperationalState s;
int r, ret = 0;
assert(l);
r = sd_network_link_get_required_operstate_for_online(l->ifindex, &required_operstate);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get required operational state, ignoring: %m");
+ else if (isempty(required_operstate))
+ l->required_operstate = LINK_OPERSTATE_RANGE_DEFAULT;
else {
- s = link_operstate_from_string(required_operstate);
- if (s < 0)
+ r = parse_operational_state_range(required_operstate, &l->required_operstate);
+ if (r < 0)
ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),
"Failed to parse required operational state, ignoring: %m");
- else
- l->required_operstate = s;
}
r = sd_network_link_get_operational_state(l->ifindex, &operstate);
if (r < 0)
ret = log_link_debug_errno(l, r, "Failed to get operational state, ignoring: %m");
else {
+ LinkOperationalState s;
+
s = link_operstate_from_string(operstate);
if (s < 0)
ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),
unsigned flags;
bool required_for_online;
- LinkOperationalState required_operstate;
+ LinkOperationalStateRange required_operstate;
LinkOperationalState operational_state;
char *state;
};
return true;
/* ignore interfaces we explicitly are asked to ignore */
- return strv_fnmatch(m->ignore, link->ifname, 0);
+ return strv_fnmatch(m->ignore, link->ifname);
}
-static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) {
+static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
/* This returns the following:
* -EAGAIN: not processed by udev or networkd
* 0: operstate is not enough
return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
"link is being processed by networkd");
- if (s < 0)
- s = m->required_operstate >= 0 ? m->required_operstate : l->required_operstate;
+ if (s.min < 0)
+ s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
+ : l->required_operstate.min;
- if (l->operational_state < s) {
- log_link_debug(l, "Operational state '%s' is below '%s'",
+ if (s.max < 0)
+ s.max = m->required_operstate.max >= 0 ? m->required_operstate.max
+ : l->required_operstate.max;
+
+ if (l->operational_state < s.min || l->operational_state > s.max) {
+ log_link_debug(l, "Operational state '%s' is not in range ['%s':'%s']",
link_operstate_to_string(l->operational_state),
- link_operstate_to_string(s));
+ link_operstate_to_string(s.min), link_operstate_to_string(s.max));
return 0;
}
if (!hashmap_isempty(m->interfaces)) {
/* wait for all the links given on the command line to appear */
HASHMAP_FOREACH_KEY(p, ifname, m->interfaces, i) {
- LinkOperationalState s = PTR_TO_INT(p);
+ LinkOperationalStateRange *range = p;
l = hashmap_get(m->links_by_name, ifname);
+ if (!l && range->min == LINK_OPERSTATE_MISSING) {
+ one_ready = true;
+ continue;
+ }
+
if (!l) {
log_debug("still waiting for %s", ifname);
if (!m->any)
continue;
}
- if (manager_link_is_online(m, l, s) <= 0) {
+ if (manager_link_is_online(m, l, *range) <= 0) {
if (!m->any)
return false;
continue;
continue;
}
- r = manager_link_is_online(m, l, _LINK_OPERSTATE_INVALID);
+ r = manager_link_is_online(m, l,
+ (LinkOperationalStateRange) { _LINK_OPERSTATE_INVALID,
+ _LINK_OPERSTATE_INVALID });
if (r < 0 && !m->any)
return false;
if (r > 0)
}
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
- LinkOperationalState required_operstate,
+ LinkOperationalStateRange required_operstate,
bool any, usec_t timeout) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
Hashmap *interfaces;
char **ignore;
- LinkOperationalState required_operstate;
+ LinkOperationalStateRange required_operstate;
bool any;
sd_netlink *rtnl;
void manager_free(Manager *m);
int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
- LinkOperationalState required_operstate,
+ LinkOperationalStateRange required_operstate,
bool any, usec_t timeout);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
static usec_t arg_timeout = 120 * USEC_PER_SEC;
static Hashmap *arg_interfaces = NULL;
static char **arg_ignore = NULL;
-static LinkOperationalState arg_required_operstate = _LINK_OPERSTATE_INVALID;
+static LinkOperationalStateRange arg_required_operstate = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
static bool arg_any = false;
-STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_keyp);
+STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
static int help(void) {
" -h --help Show this help\n"
" --version Print version string\n"
" -q --quiet Do not show status information\n"
- " -i --interface=INTERFACE[:OPERSTATE]\n"
+ " -i --interface=INTERFACE[:MIN_OPERSTATE[:MAX_OPERSTATE]]\n"
" Block until at least these interfaces have appeared\n"
" --ignore=INTERFACE Don't take these interfaces into account\n"
- " -o --operational-state=OPERSTATE\n"
+ " -o --operational-state=MIN_OPERSTATE[:MAX_OPERSTATE]\n"
" Required operational state\n"
" --any Wait until at least one of the interfaces is online\n"
" --timeout=SECS Maximum time to wait for network connectivity\n"
return 0;
}
-static int parse_interface_with_operstate(const char *str) {
+static int parse_interface_with_operstate_range(const char *str) {
_cleanup_free_ char *ifname = NULL;
- LinkOperationalState s;
+ _cleanup_free_ LinkOperationalStateRange *range;
const char *p;
int r;
assert(str);
+ range = new(LinkOperationalStateRange, 1);
+ if (!range)
+ return log_oom();
+
p = strchr(str, ':');
if (p) {
- if (isempty(p + 1))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Operational state is empty.");
-
- s = link_operstate_from_string(p + 1);
- if (s < 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Invalid operational state '%s'", p + 1);
+ r = parse_operational_state_range(p + 1, range);
+ if (r < 0)
+ log_error_errno(r, "Invalid operational state range '%s'", p + 1);
ifname = strndup(optarg, p - optarg);
} else {
- s = _LINK_OPERSTATE_INVALID;
+ range->min = _LINK_OPERSTATE_INVALID;
+ range->max = _LINK_OPERSTATE_INVALID;
ifname = strdup(str);
}
if (!ifname)
if (r < 0)
return log_oom();
- r = hashmap_put(arg_interfaces, ifname, INT_TO_PTR(s));
+ r = hashmap_put(arg_interfaces, ifname, TAKE_PTR(range));
if (r < 0)
return log_error_errno(r, "Failed to store interface name: %m");
if (r == 0)
return version();
case 'i':
- r = parse_interface_with_operstate(optarg);
+ r = parse_interface_with_operstate_range(optarg);
if (r < 0)
return r;
break;
break;
case 'o': {
- LinkOperationalState s;
+ LinkOperationalStateRange range;
+
+ r = parse_operational_state_range(optarg, &range);
+ if (r < 0)
+ return log_error_errno(r, "Invalid operational state range '%s'", optarg);
- s = link_operstate_from_string(optarg);
- if (s < 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Invalid operational state '%s'", optarg);
+ arg_required_operstate = range;
- arg_required_operstate = s;
break;
}
case ARG_ANY:
r = mount_verbose(m->graceful ? LOG_DEBUG : LOG_ERR, NULL, where, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
if (r < 0) {
- umount_verbose(where);
+ (void) umount_verbose(where);
return m->graceful ? 0 : r;
}
return log_error_errno(errno, "setsid() failed: %m");
if (arg_private_network)
- loopback_setup();
+ (void) loopback_setup();
if (arg_expose_ports) {
r = expose_port_send_rtnl(rtnl_socket);
r = setup_dev_console(console);
if (r < 0)
- return log_error_errno(r, "Failed to setup /dev/console: %m");
+ return log_error_errno(r, "Failed to set up /dev/console: %m");
r = send_one_fd(master_pty_socket, master, 0);
if (r < 0)
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|
- (arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0)|
+ (arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)|
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
+ if (r == -EUCLEAN)
+ return log_error_errno(r, "File system check for image failed: %m");
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to mount image root file system: %m");
}
r = determine_uid_shift(directory);
if (dissected_image) {
/* Now we know the uid shift, let's now mount everything else that might be in the image. */
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
- DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
+ DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK));
+ if (r == -EUCLEAN)
+ return log_error_errno(r, "File system check for image failed: %m");
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to mount image file system: %m");
}
if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
loop->fd,
arg_image,
arg_root_hash, arg_root_hash_size,
- DISSECT_IMAGE_REQUIRE_ROOT,
+ DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK,
&dissected_image);
if (r == -ENOPKG) {
/* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
log_notice("Note that the disk image needs to\n"
" a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
" b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
- " c) or follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n"
+ " c) or follow https://systemd.io/DISCOVERABLE_PARTITIONS\n"
" d) or contain a file system without a partition table\n"
"in order to be bootable with systemd-nspawn.");
goto finish;
#include <nss.h>
#include <pthread.h>
-#include "sd-bus.h"
-
-#include "alloc-util.h"
-#include "bus-common-errors.h"
-#include "dirent-util.h"
#include "env-util.h"
+#include "errno-util.h"
#include "fd-util.h"
-#include "format-util.h"
-#include "fs-util.h"
-#include "list.h"
+#include "group-record-nss.h"
#include "macro.h"
#include "nss-util.h"
#include "signal-util.h"
-#include "stdio-util.h"
-#include "string-util.h"
+#include "strv.h"
#include "user-util.h"
-#include "util.h"
-
-#define DYNAMIC_USER_GECOS "Dynamic User"
-#define DYNAMIC_USER_PASSWD "*" /* locked */
-#define DYNAMIC_USER_DIR "/"
-#define DYNAMIC_USER_SHELL NOLOGIN
+#include "userdb-glue.h"
+#include "userdb.h"
static const struct passwd root_passwd = {
.pw_name = (char*) "root",
.gr_mem = (char*[]) { NULL },
};
-typedef struct UserEntry UserEntry;
-typedef struct GetentData GetentData;
+typedef struct GetentData {
+ /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really reentrant since it
+ * shares the reading position in the stream with all other threads', we need to protect the data in
+ * UserDBIterator from multithreaded programs which may call setpwent(), getpwent_r(), or endpwent()
+ * simultaneously. So, each function locks the data by using the mutex below. */
+ pthread_mutex_t mutex;
+ UserDBIterator *iterator;
-struct UserEntry {
- uid_t id;
- char *name;
+ /* Applies to group iterations only: true while we iterate over groups defined through NSS, false
+ * otherwise. */
+ bool by_membership;
+} GetentData;
- GetentData *data;
- LIST_FIELDS(UserEntry, entries);
+static GetentData getpwent_data = {
+ .mutex = PTHREAD_MUTEX_INITIALIZER
};
-struct GetentData {
- /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really
- * reentrant since it shares the reading position in the stream with all other threads',
- * we need to protect the data in UserEntry from multithreaded programs which may call
- * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the
- * data by using the mutex below. */
- pthread_mutex_t mutex;
-
- UserEntry *position;
- LIST_HEAD(UserEntry, entries);
+static GetentData getgrent_data = {
+ .mutex = PTHREAD_MUTEX_INITIALIZER
};
-static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
-static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
-
NSS_GETPW_PROTOTYPES(systemd);
NSS_GETGR_PROTOTYPES(systemd);
-enum nss_status _nss_systemd_endpwent(void) _public_;
-enum nss_status _nss_systemd_setpwent(int stayopen) _public_;
-enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_;
-enum nss_status _nss_systemd_endgrent(void) _public_;
-enum nss_status _nss_systemd_setgrent(int stayopen) _public_;
-enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_;
-
-static int direct_lookup_name(const char *name, uid_t *ret) {
- _cleanup_free_ char *s = NULL;
- const char *path;
- int r;
-
- assert(name);
-
- /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
- * namespace and subject to proper authentication. However, there's one problem: if our module is called from
- * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
- * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
-
- path = strjoina("/run/systemd/dynamic-uid/direct:", name);
- r = readlink_malloc(path, &s);
- if (r < 0)
- return r;
-
- return parse_uid(s, ret);
-}
-
-static int direct_lookup_uid(uid_t uid, char **ret) {
- char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
- int r;
-
- xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
-
- r = readlink_malloc(path, &s);
- if (r < 0)
- return r;
- if (!valid_user_group_name(s)) { /* extra safety check */
- free(s);
- return -EINVAL;
- }
-
- *ret = s;
- return 0;
-}
+NSS_PWENT_PROTOTYPES(systemd);
+NSS_GRENT_PROTOTYPES(systemd);
+NSS_INITGROUPS_PROTOTYPE(systemd);
enum nss_status _nss_systemd_getpwnam_r(
const char *name,
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- uint32_t translated;
- size_t l;
- int bypass, r;
+ enum nss_status status;
+ int e;
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
assert(pwd);
+ assert(errnop);
- /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
- * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
+ /* If the username is not valid, then we don't know it. Ideally libc would filter these for us
+ * anyway. We don't generate EINVAL here, because it isn't really out business to complain about
+ * invalid user names. */
if (!valid_user_group_name(name))
return NSS_STATUS_NOTFOUND;
/* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
if (streq(name, root_passwd.pw_name)) {
*pwd = root_passwd;
return NSS_STATUS_SUCCESS;
}
- if (synthesize_nobody() &&
- streq(name, nobody_passwd.pw_name)) {
- *pwd = nobody_passwd;
- return NSS_STATUS_SUCCESS;
- }
- }
-
- /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
- if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
- return NSS_STATUS_NOTFOUND;
- bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
- if (bypass <= 0) {
- r = sd_bus_open_system(&bus);
- if (r < 0)
- bypass = 1;
- }
-
- if (bypass > 0) {
- r = direct_lookup_name(name, (uid_t*) &translated);
- if (r == -ENOENT)
- return NSS_STATUS_NOTFOUND;
- if (r < 0)
- goto fail;
- } else {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByName",
- &error,
- &reply,
- "s",
- name);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ if (streq(name, nobody_passwd.pw_name)) {
+ if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
- goto fail;
+ *pwd = nobody_passwd;
+ return NSS_STATUS_SUCCESS;
}
- r = sd_bus_message_read(reply, "u", &translated);
- if (r < 0)
- goto fail;
- }
+ } else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
+ return NSS_STATUS_NOTFOUND;
- l = strlen(name);
- if (buflen < l+1) {
+ status = userdb_getpwnam(name, pwd, buffer, buflen, &e);
+ if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
+ *errnop = e;
+ return status;
}
- memcpy(buffer, name, l+1);
-
- pwd->pw_name = buffer;
- pwd->pw_uid = (uid_t) translated;
- pwd->pw_gid = (uid_t) translated;
- pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
- pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
- pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
- pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
+ return status;
}
enum nss_status _nss_systemd_getpwuid_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *direct = NULL;
- const char *translated;
- size_t l;
- int bypass, r;
+ enum nss_status status;
+ int e;
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
+ assert(pwd);
+ assert(errnop);
+
if (!uid_is_valid(uid))
return NSS_STATUS_NOTFOUND;
/* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
if (uid == root_passwd.pw_uid) {
*pwd = root_passwd;
return NSS_STATUS_SUCCESS;
}
- if (synthesize_nobody() &&
- uid == nobody_passwd.pw_uid) {
- *pwd = nobody_passwd;
- return NSS_STATUS_SUCCESS;
- }
- }
- if (!uid_is_dynamic(uid))
- return NSS_STATUS_NOTFOUND;
-
- if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
- return NSS_STATUS_NOTFOUND;
-
- bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
- if (bypass <= 0) {
- r = sd_bus_open_system(&bus);
- if (r < 0)
- bypass = 1;
- }
-
- if (bypass > 0) {
- r = direct_lookup_uid(uid, &direct);
- if (r == -ENOENT)
- return NSS_STATUS_NOTFOUND;
- if (r < 0)
- goto fail;
-
- translated = direct;
-
- } else {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByUID",
- &error,
- &reply,
- "u",
- (uint32_t) uid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ if (uid == nobody_passwd.pw_uid) {
+ if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
- goto fail;
+ *pwd = nobody_passwd;
+ return NSS_STATUS_SUCCESS;
}
- r = sd_bus_message_read(reply, "s", &translated);
- if (r < 0)
- goto fail;
- }
+ } else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
+ return NSS_STATUS_NOTFOUND;
- l = strlen(translated) + 1;
- if (buflen < l) {
+ status = userdb_getpwuid(uid, pwd, buffer, buflen, &e);
+ if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
+ *errnop = e;
+ return status;
}
- memcpy(buffer, translated, l);
-
- pwd->pw_name = buffer;
- pwd->pw_uid = uid;
- pwd->pw_gid = uid;
- pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
- pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
- pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
- pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
+ return status;
}
#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- uint32_t translated;
- size_t l;
- int bypass, r;
+ enum nss_status status;
+ int e;
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(name);
assert(gr);
+ assert(errnop);
if (!valid_user_group_name(name))
return NSS_STATUS_NOTFOUND;
- /* Synthesize records for root and nobody, in case they are missing form /etc/group */
+ /* Synthesize records for root and nobody, in case they are missing from /etc/group */
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
if (streq(name, root_group.gr_name)) {
*gr = root_group;
return NSS_STATUS_SUCCESS;
}
- if (synthesize_nobody() &&
- streq(name, nobody_group.gr_name)) {
- *gr = nobody_group;
- return NSS_STATUS_SUCCESS;
- }
- }
-
- if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
- return NSS_STATUS_NOTFOUND;
-
- bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
- if (bypass <= 0) {
- r = sd_bus_open_system(&bus);
- if (r < 0)
- bypass = 1;
- }
- if (bypass > 0) {
- r = direct_lookup_name(name, (uid_t*) &translated);
- if (r == -ENOENT)
- return NSS_STATUS_NOTFOUND;
- if (r < 0)
- goto fail;
- } else {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByName",
- &error,
- &reply,
- "s",
- name);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ if (streq(name, nobody_group.gr_name)) {
+ if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
- goto fail;
+ *gr = nobody_group;
+ return NSS_STATUS_SUCCESS;
}
- r = sd_bus_message_read(reply, "u", &translated);
- if (r < 0)
- goto fail;
- }
+ } else if (STR_IN_SET(name, root_group.gr_name, nobody_group.gr_name))
+ return NSS_STATUS_NOTFOUND;
- l = sizeof(char*) + strlen(name) + 1;
- if (buflen < l) {
+ status = userdb_getgrnam(name, gr, buffer, buflen, &e);
+ if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
+ *errnop = e;
+ return status;
}
- memzero(buffer, sizeof(char*));
- strcpy(buffer + sizeof(char*), name);
-
- gr->gr_name = buffer + sizeof(char*);
- gr->gr_gid = (gid_t) translated;
- gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
- gr->gr_mem = (char**) buffer;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
+ return status;
}
enum nss_status _nss_systemd_getgrgid_r(
char *buffer, size_t buflen,
int *errnop) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *direct = NULL;
- const char *translated;
- size_t l;
- int bypass, r;
+ enum nss_status status;
+ int e;
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
+ assert(gr);
+ assert(errnop);
+
if (!gid_is_valid(gid))
return NSS_STATUS_NOTFOUND;
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
if (gid == root_group.gr_gid) {
*gr = root_group;
return NSS_STATUS_SUCCESS;
}
- if (synthesize_nobody() &&
- gid == nobody_group.gr_gid) {
- *gr = nobody_group;
- return NSS_STATUS_SUCCESS;
- }
- }
-
- if (!gid_is_dynamic(gid))
- return NSS_STATUS_NOTFOUND;
-
- if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
- return NSS_STATUS_NOTFOUND;
-
- bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
- if (bypass <= 0) {
- r = sd_bus_open_system(&bus);
- if (r < 0)
- bypass = 1;
- }
- if (bypass > 0) {
- r = direct_lookup_uid(gid, &direct);
- if (r == -ENOENT)
- return NSS_STATUS_NOTFOUND;
- if (r < 0)
- goto fail;
-
- translated = direct;
-
- } else {
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LookupDynamicUserByUID",
- &error,
- &reply,
- "u",
- (uint32_t) gid);
- if (r < 0) {
- if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+ if (gid == nobody_group.gr_gid) {
+ if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
- goto fail;
+ *gr = nobody_group;
+ return NSS_STATUS_SUCCESS;
}
- r = sd_bus_message_read(reply, "s", &translated);
- if (r < 0)
- goto fail;
- }
+ } else if (gid == root_group.gr_gid || gid == nobody_group.gr_gid)
+ return NSS_STATUS_NOTFOUND;
- l = sizeof(char*) + strlen(translated) + 1;
- if (buflen < l) {
+ status = userdb_getgrgid(gid, gr, buffer, buflen, &e);
+ if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
UNPROTECT_ERRNO;
- *errnop = ERANGE;
- return NSS_STATUS_TRYAGAIN;
+ *errnop = e;
+ return status;
}
- memzero(buffer, sizeof(char*));
- strcpy(buffer + sizeof(char*), translated);
-
- gr->gr_name = buffer + sizeof(char*);
- gr->gr_gid = gid;
- gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
- gr->gr_mem = (char**) buffer;
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- UNPROTECT_ERRNO;
- *errnop = -r;
- return NSS_STATUS_UNAVAIL;
-}
-
-static void user_entry_free(UserEntry *p) {
- if (!p)
- return;
-
- if (p->data)
- LIST_REMOVE(entries, p->data->entries, p);
-
- free(p->name);
- free(p);
-}
-
-static int user_entry_add(GetentData *data, const char *name, uid_t id) {
- UserEntry *p;
-
- assert(data);
-
- /* This happens when User= or Group= already exists statically. */
- if (!uid_is_dynamic(id))
- return -EINVAL;
-
- p = new0(UserEntry, 1);
- if (!p)
- return -ENOMEM;
-
- p->name = strdup(name);
- if (!p->name) {
- free(p);
- return -ENOMEM;
- }
- p->id = id;
- p->data = data;
-
- LIST_PREPEND(entries, data->entries, p);
-
- return 0;
-}
-
-static void systemd_endent(GetentData *data) {
- UserEntry *p;
-
- assert(data);
-
- while ((p = data->entries))
- user_entry_free(p);
-
- data->position = NULL;
+ return status;
}
static enum nss_status nss_systemd_endent(GetentData *p) {
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
+ assert(p);
+
assert_se(pthread_mutex_lock(&p->mutex) == 0);
- systemd_endent(p);
+ p->iterator = userdb_iterator_free(p->iterator);
+ p->by_membership = false;
assert_se(pthread_mutex_unlock(&p->mutex) == 0);
return NSS_STATUS_SUCCESS;
return nss_systemd_endent(&getgrent_data);
}
-static int direct_enumeration(GetentData *p) {
- _cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
- int r;
+enum nss_status _nss_systemd_setpwent(int stayopen) {
+ enum nss_status ret;
- assert(p);
+ PROTECT_ERRNO;
+ BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- d = opendir("/run/systemd/dynamic-uid/");
- if (!d)
- return -errno;
+ if (userdb_nss_compat_is_enabled() <= 0)
+ return NSS_STATUS_NOTFOUND;
- FOREACH_DIRENT(de, d, return -errno) {
- _cleanup_free_ char *name = NULL;
- uid_t uid, verified;
+ assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
- if (!dirent_is_file(de))
- continue;
+ getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
+ getpwent_data.by_membership = false;
- r = parse_uid(de->d_name, &uid);
- if (r < 0)
- continue;
+ ret = userdb_all(nss_glue_userdb_flags(), &getpwent_data.iterator) < 0 ?
+ NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
- r = direct_lookup_uid(uid, &name);
- if (r == -ENOMEM)
- return r;
- if (r < 0)
- continue;
+ assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
+ return ret;
+}
- r = direct_lookup_name(name, &verified);
- if (r < 0)
- continue;
+enum nss_status _nss_systemd_setgrent(int stayopen) {
+ enum nss_status ret;
- if (uid != verified)
- continue;
+ PROTECT_ERRNO;
+ BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- r = user_entry_add(p, name, uid);
- if (r == -ENOMEM)
- return r;
- if (r < 0)
- continue;
- }
+ if (userdb_nss_compat_is_enabled() <= 0)
+ return NSS_STATUS_NOTFOUND;
- return 0;
+ assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+
+ getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
+ getpwent_data.by_membership = false;
+
+ ret = groupdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator) < 0 ?
+ NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
+
+ assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
+ return ret;
}
-static enum nss_status systemd_setent(GetentData *p) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- const char *name;
- uid_t id;
- int bypass, r;
+enum nss_status _nss_systemd_getpwent_r(
+ struct passwd *result,
+ char *buffer, size_t buflen,
+ int *errnop) {
+
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ enum nss_status ret;
+ int r;
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- assert(p);
+ assert(result);
+ assert(errnop);
- assert_se(pthread_mutex_lock(&p->mutex) == 0);
+ r = userdb_nss_compat_is_enabled();
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (!r)
+ return NSS_STATUS_NOTFOUND;
- systemd_endent(p);
+ assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
- if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+ if (!getpwent_data.iterator) {
+ UNPROTECT_ERRNO;
+ *errnop = EHOSTDOWN;
+ ret = NSS_STATUS_UNAVAIL;
goto finish;
-
- bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
-
- if (bypass <= 0) {
- r = sd_bus_open_system(&bus);
- if (r < 0)
- bypass = 1;
}
- if (bypass > 0) {
- r = direct_enumeration(p);
- if (r < 0)
- goto fail;
-
+ r = userdb_iterator_get(getpwent_data.iterator, &ur);
+ if (r == -ESRCH) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto finish;
+ }
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ ret = NSS_STATUS_UNAVAIL;
goto finish;
}
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetDynamicUsers",
- &error,
- &reply,
- NULL);
- if (r < 0)
- goto fail;
-
- r = sd_bus_message_enter_container(reply, 'a', "(us)");
- if (r < 0)
- goto fail;
-
- while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) {
- r = user_entry_add(p, name, id);
- if (r == -ENOMEM)
- goto fail;
- if (r < 0)
- continue;
+ r = nss_pack_user_record(ur, result, buffer, buflen);
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ ret = NSS_STATUS_TRYAGAIN;
+ goto finish;
}
- if (r < 0)
- goto fail;
- r = sd_bus_message_exit_container(reply);
- if (r < 0)
- goto fail;
+ ret = NSS_STATUS_SUCCESS;
finish:
- p->position = p->entries;
- assert_se(pthread_mutex_unlock(&p->mutex) == 0);
-
- return NSS_STATUS_SUCCESS;
-
-fail:
- systemd_endent(p);
- assert_se(pthread_mutex_unlock(&p->mutex) == 0);
-
- return NSS_STATUS_UNAVAIL;
-}
-
-enum nss_status _nss_systemd_setpwent(int stayopen) {
- return systemd_setent(&getpwent_data);
+ assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
+ return ret;
}
-enum nss_status _nss_systemd_setgrent(int stayopen) {
- return systemd_setent(&getgrent_data);
-}
+enum nss_status _nss_systemd_getgrent_r(
+ struct group *result,
+ char *buffer, size_t buflen,
+ int *errnop) {
-enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+ _cleanup_free_ char **members = NULL;
enum nss_status ret;
- UserEntry *p;
- size_t len;
+ int r;
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
assert(result);
- assert(buffer);
assert(errnop);
- assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
+ r = userdb_nss_compat_is_enabled();
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (!r)
+ return NSS_STATUS_UNAVAIL;
- LIST_FOREACH(entries, p, getpwent_data.position) {
- len = strlen(p->name) + 1;
- if (buflen < len) {
+ assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+
+ if (!getgrent_data.iterator) {
+ UNPROTECT_ERRNO;
+ *errnop = EHOSTDOWN;
+ ret = NSS_STATUS_UNAVAIL;
+ goto finish;
+ }
+
+ if (!getgrent_data.by_membership) {
+ r = groupdb_iterator_get(getgrent_data.iterator, &gr);
+ if (r == -ESRCH) {
+ /* So we finished iterating native groups now. let's now continue with iterating
+ * native memberships, and generate additional group entries for any groups
+ * referenced there that are defined in NSS only. This means for those groups there
+ * will be two or more entries generated during iteration, but this is apparently how
+ * this is supposed to work, and what other implementations do too. Clients are
+ * supposed to merge the group records found during iteration automatically. */
+ getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
+
+ r = membershipdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator);
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ ret = NSS_STATUS_UNAVAIL;
+ goto finish;
+ }
+
+ getgrent_data.by_membership = true;
+ } else if (r < 0) {
UNPROTECT_ERRNO;
- *errnop = ERANGE;
- ret = NSS_STATUS_TRYAGAIN;
- goto finalize;
+ *errnop = -r;
+ ret = NSS_STATUS_UNAVAIL;
+ goto finish;
+ } else if (!STR_IN_SET(gr->group_name, root_group.gr_name, nobody_group.gr_name)) {
+ r = membershipdb_by_group_strv(gr->group_name, nss_glue_userdb_flags(), &members);
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ ret = NSS_STATUS_UNAVAIL;
+ goto finish;
+ }
}
+ }
- memcpy(buffer, p->name, len);
-
- result->pw_name = buffer;
- result->pw_uid = p->id;
- result->pw_gid = p->id;
- result->pw_gecos = (char*) DYNAMIC_USER_GECOS;
- result->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
- result->pw_dir = (char*) DYNAMIC_USER_DIR;
- result->pw_shell = (char*) DYNAMIC_USER_SHELL;
- break;
+ if (getgrent_data.by_membership) {
+ _cleanup_close_ int lock_fd = -1;
+
+ for (;;) {
+ _cleanup_free_ char *user_name = NULL, *group_name = NULL;
+
+ r = membershipdb_iterator_get(getgrent_data.iterator, &user_name, &group_name);
+ if (r == -ESRCH) {
+ ret = NSS_STATUS_NOTFOUND;
+ goto finish;
+ }
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ ret = NSS_STATUS_UNAVAIL;
+ goto finish;
+ }
+
+ if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
+ continue;
+ if (STR_IN_SET(group_name, root_group.gr_name, nobody_group.gr_name))
+ continue;
+
+ /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
+ if (lock_fd < 0) {
+ lock_fd = userdb_nss_compat_disable();
+ if (lock_fd < 0 && lock_fd != -EBUSY) {
+ UNPROTECT_ERRNO;
+ *errnop = -lock_fd;
+ ret = NSS_STATUS_UNAVAIL;
+ goto finish;
+ }
+ }
+
+ r = nss_group_record_by_name(group_name, &gr);
+ if (r == -ESRCH)
+ continue;
+ if (r < 0) {
+ log_debug_errno(r, "Failed to do NSS check for group '%s', ignoring: %m", group_name);
+ continue;
+ }
+
+ members = strv_new(user_name);
+ if (!members) {
+ UNPROTECT_ERRNO;
+ *errnop = ENOMEM;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ /* Note that we currently generate one group entry per user that is part of a
+ * group. It's a bit ugly, but equivalent to generating a single entry with a set of
+ * members in them. */
+ break;
+ }
}
- if (!p) {
- ret = NSS_STATUS_NOTFOUND;
- goto finalize;
+
+ r = nss_pack_group_record(gr, members, result, buffer, buflen);
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ ret = NSS_STATUS_TRYAGAIN;
+ goto finish;
}
- /* On success, step to the next entry. */
- p = p->entries_next;
ret = NSS_STATUS_SUCCESS;
-finalize:
- /* Save position for the next call. */
- getpwent_data.position = p;
-
- assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
-
+finish:
+ assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
return ret;
}
-enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) {
- enum nss_status ret;
- UserEntry *p;
- size_t len;
+enum nss_status _nss_systemd_initgroups_dyn(
+ const char *user_name,
+ gid_t gid,
+ long *start,
+ long *size,
+ gid_t **groupsp,
+ long int limit,
+ int *errnop) {
+
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ bool any = false;
+ int r;
PROTECT_ERRNO;
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
- assert(result);
- assert(buffer);
+ assert(user_name);
+ assert(start);
+ assert(size);
+ assert(groupsp);
assert(errnop);
- assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+ if (!valid_user_group_name(user_name))
+ return NSS_STATUS_NOTFOUND;
+
+ /* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */
+ if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
+ return NSS_STATUS_NOTFOUND;
+
+ r = userdb_nss_compat_is_enabled();
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (!r)
+ return NSS_STATUS_NOTFOUND;
+
+ r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator);
+ if (r < 0) {
+ UNPROTECT_ERRNO;
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
- LIST_FOREACH(entries, p, getgrent_data.position) {
- len = sizeof(char*) + strlen(p->name) + 1;
- if (buflen < len) {
+ for (;;) {
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ _cleanup_free_ char *group_name = NULL;
+
+ r = membershipdb_iterator_get(iterator, NULL, &group_name);
+ if (r == -ESRCH)
+ break;
+ if (r < 0) {
UNPROTECT_ERRNO;
- *errnop = ERANGE;
- ret = NSS_STATUS_TRYAGAIN;
- goto finalize;
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
}
- memzero(buffer, sizeof(char*));
- strcpy(buffer + sizeof(char*), p->name);
+ /* The group might be defined via traditional NSS only, hence let's do a full look-up without
+ * disabling NSS. This means we are operating recursively here. */
- result->gr_name = buffer + sizeof(char*);
- result->gr_gid = p->id;
- result->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
- result->gr_mem = (char**) buffer;
- break;
- }
- if (!p) {
- ret = NSS_STATUS_NOTFOUND;
- goto finalize;
- }
+ r = groupdb_by_name(group_name, nss_glue_userdb_flags() & ~USERDB_AVOID_NSS, &g);
+ if (r == -ESRCH)
+ continue;
+ if (r < 0) {
+ log_debug_errno(r, "Failed to resolve group '%s', ignoring: %m", group_name);
+ continue;
+ }
- /* On success, step to the next entry. */
- p = p->entries_next;
- ret = NSS_STATUS_SUCCESS;
+ if (g->gid == gid)
+ continue;
-finalize:
- /* Save position for the next call. */
- getgrent_data.position = p;
+ if (*start >= *size) {
+ gid_t *new_groups;
+ long new_size;
+
+ if (limit > 0 && *size >= limit) /* Reached the limit.? */
+ break;
+
+ if (*size > LONG_MAX/2) { /* Check for overflow */
+ UNPROTECT_ERRNO;
+ *errnop = ENOMEM;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ new_size = *start * 2;
+ if (limit > 0 && new_size > limit)
+ new_size = limit;
+
+ /* Enlarge buffer */
+ new_groups = realloc(*groupsp, new_size * sizeof(**groupsp));
+ if (!new_groups) {
+ UNPROTECT_ERRNO;
+ *errnop = ENOMEM;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ *groupsp = new_groups;
+ *size = new_size;
+ }
- assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
+ (*groupsp)[(*start)++] = g->gid;
+ any = true;
+ }
- return ret;
+ return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
}
_nss_systemd_endgrent;
_nss_systemd_setgrent;
_nss_systemd_getgrent_r;
+ _nss_systemd_initgroups_dyn;
local: *;
};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "env-util.h"
+#include "fd-util.h"
+#include "group-record-nss.h"
+#include "strv.h"
+#include "user-record.h"
+#include "userdb-glue.h"
+#include "userdb.h"
+
+UserDBFlags nss_glue_userdb_flags(void) {
+ UserDBFlags flags = USERDB_AVOID_NSS;
+
+ /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
+ if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+ flags |= USERDB_AVOID_DYNAMIC_USER;
+
+ return flags;
+}
+
+int nss_pack_user_record(
+ UserRecord *hr,
+ struct passwd *pwd,
+ char *buffer,
+ size_t buflen) {
+
+ const char *rn, *hd, *shell;
+ size_t required;
+
+ assert(hr);
+ assert(pwd);
+
+ assert_se(hr->user_name);
+ required = strlen(hr->user_name) + 1;
+
+ assert_se(rn = user_record_real_name(hr));
+ required += strlen(rn) + 1;
+
+ assert_se(hd = user_record_home_directory(hr));
+ required += strlen(hd) + 1;
+
+ assert_se(shell = user_record_shell(hr));
+ required += strlen(shell) + 1;
+
+ if (buflen < required)
+ return -ERANGE;
+
+ *pwd = (struct passwd) {
+ .pw_name = buffer,
+ .pw_uid = hr->uid,
+ .pw_gid = user_record_gid(hr),
+ .pw_passwd = (char*) "x", /* means: see shadow file */
+ };
+
+ assert(buffer);
+
+ pwd->pw_gecos = stpcpy(pwd->pw_name, hr->user_name) + 1;
+ pwd->pw_dir = stpcpy(pwd->pw_gecos, rn) + 1;
+ pwd->pw_shell = stpcpy(pwd->pw_dir, hd) + 1;
+ strcpy(pwd->pw_shell, shell);
+
+ return 0;
+}
+
+enum nss_status userdb_getpwnam(
+ const char *name,
+ struct passwd *pwd,
+ char *buffer, size_t buflen,
+ int *errnop) {
+
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ int r;
+
+ assert(pwd);
+ assert(errnop);
+
+ r = userdb_nss_compat_is_enabled();
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (!r)
+ return NSS_STATUS_NOTFOUND;
+
+ r = userdb_by_name(name, nss_glue_userdb_flags(), &hr);
+ if (r == -ESRCH)
+ return NSS_STATUS_NOTFOUND;
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ r = nss_pack_user_record(hr, pwd, buffer, buflen);
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status userdb_getpwuid(
+ uid_t uid,
+ struct passwd *pwd,
+ char *buffer,
+ size_t buflen,
+ int *errnop) {
+
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ int r;
+
+ assert(pwd);
+ assert(errnop);
+
+ r = userdb_nss_compat_is_enabled();
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (!r)
+ return NSS_STATUS_NOTFOUND;
+
+ r = userdb_by_uid(uid, nss_glue_userdb_flags(), &hr);
+ if (r == -ESRCH)
+ return NSS_STATUS_NOTFOUND;
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ r = nss_pack_user_record(hr, pwd, buffer, buflen);
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+int nss_pack_group_record(
+ GroupRecord *g,
+ char **extra_members,
+ struct group *gr,
+ char *buffer,
+ size_t buflen) {
+
+ char **array = NULL, *p, **m;
+ size_t required, n = 0, i = 0;
+
+ assert(g);
+ assert(gr);
+
+ assert_se(g->group_name);
+ required = strlen(g->group_name) + 1;
+
+ STRV_FOREACH(m, g->members) {
+ required += sizeof(char*); /* space for ptr array entry */
+ required += strlen(*m) + 1;
+ n++;
+ }
+ STRV_FOREACH(m, extra_members) {
+ if (strv_contains(g->members, *m))
+ continue;
+
+ required += sizeof(char*);
+ required += strlen(*m) + 1;
+ n++;
+ }
+
+ required += sizeof(char*); /* trailing NULL in ptr array entry */
+
+ if (buflen < required)
+ return -ERANGE;
+
+ array = (char**) buffer; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
+ p = buffer + sizeof(void*) * (n + 1); /* place member strings right after the ptr array */
+
+ STRV_FOREACH(m, g->members) {
+ array[i++] = p;
+ p = stpcpy(p, *m) + 1;
+ }
+ STRV_FOREACH(m, extra_members) {
+ if (strv_contains(g->members, *m))
+ continue;
+
+ array[i++] = p;
+ p = stpcpy(p, *m) + 1;
+ }
+
+ assert_se(i == n);
+ array[n] = NULL;
+
+ *gr = (struct group) {
+ .gr_name = strcpy(p, g->group_name),
+ .gr_gid = g->gid,
+ .gr_passwd = (char*) "x", /* means: see shadow file */
+ .gr_mem = array,
+ };
+
+ return 0;
+}
+
+enum nss_status userdb_getgrnam(
+ const char *name,
+ struct group *gr,
+ char *buffer,
+ size_t buflen,
+ int *errnop) {
+
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ _cleanup_strv_free_ char **members = NULL;
+ int r;
+
+ assert(gr);
+ assert(errnop);
+
+ r = userdb_nss_compat_is_enabled();
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (!r)
+ return NSS_STATUS_NOTFOUND;
+
+ r = groupdb_by_name(name, nss_glue_userdb_flags(), &g);
+ if (r < 0 && r != -ESRCH) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ r = membershipdb_by_group_strv(name, nss_glue_userdb_flags(), &members);
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ if (!g) {
+ _cleanup_close_ int lock_fd = -1;
+
+ if (strv_isempty(members))
+ return NSS_STATUS_NOTFOUND;
+
+ /* Grmbl, so we are supposed to extend a group entry, but the group entry itself is not
+ * accessible via non-NSS. Hence let's do what we have to do, and query NSS after all to
+ * acquire it, so that we can extend it (that's because glibc's group merging feature will
+ * merge groups only if both GID and name match and thus we need to have both first). It
+ * sucks behaving recursively likely this, but it's apparently what everybody does. We break
+ * the recursion for ourselves via the userdb_nss_compat_disable() lock. */
+
+ lock_fd = userdb_nss_compat_disable();
+ if (lock_fd < 0 && lock_fd != -EBUSY)
+ return lock_fd;
+
+ r = nss_group_record_by_name(name, &g);
+ if (r == -ESRCH)
+ return NSS_STATUS_NOTFOUND;
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ }
+
+ r = nss_pack_group_record(g, members, gr, buffer, buflen);
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status userdb_getgrgid(
+ gid_t gid,
+ struct group *gr,
+ char *buffer,
+ size_t buflen,
+ int *errnop) {
+
+
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ _cleanup_strv_free_ char **members = NULL;
+ bool from_nss;
+ int r;
+
+ assert(gr);
+ assert(errnop);
+
+ r = userdb_nss_compat_is_enabled();
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+ if (!r)
+ return NSS_STATUS_NOTFOUND;
+
+ r = groupdb_by_gid(gid, nss_glue_userdb_flags(), &g);
+ if (r < 0 && r != -ESRCH) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ if (!g) {
+ _cleanup_close_ int lock_fd = -1;
+
+ /* So, quite possibly we have to extend an existing group record with additional members. But
+ * to do this we need to know the group name first. The group didn't exist via non-NSS
+ * queries though, hence let's try to acquire it here recursively via NSS. */
+
+ lock_fd = userdb_nss_compat_disable();
+ if (lock_fd < 0 && lock_fd != -EBUSY)
+ return lock_fd;
+
+ r = nss_group_record_by_gid(gid, &g);
+ if (r == -ESRCH)
+ return NSS_STATUS_NOTFOUND;
+
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ from_nss = true;
+ } else
+ from_nss = false;
+
+ r = membershipdb_by_group_strv(g->group_name, nss_glue_userdb_flags(), &members);
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* If we acquired the record via NSS then there's no reason to respond unless we have to agument the
+ * list of members of the group */
+ if (from_nss && strv_isempty(members))
+ return NSS_STATUS_NOTFOUND;
+
+ r = nss_pack_group_record(g, members, gr, buffer, buflen);
+ if (r < 0) {
+ *errnop = -r;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <nss.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include "userdb.h"
+
+UserDBFlags nss_glue_userdb_flags(void);
+
+int nss_pack_user_record(UserRecord *hr, struct passwd *pwd, char *buffer, size_t buflen);
+int nss_pack_group_record(GroupRecord *g, char **extra_members, struct group *gr, char *buffer, size_t buflen);
+
+enum nss_status userdb_getpwnam(const char *name, struct passwd *pwd, char *buffer, size_t buflen, int *errnop);
+enum nss_status userdb_getpwuid(uid_t uid, struct passwd *pwd, char *buffer, size_t buflen, int *errnop);
+
+enum nss_status userdb_getgrnam(const char *name, struct group *gr, char *buffer, size_t buflen, int *errnop);
+enum nss_status userdb_getgrgid(gid_t gid, struct group *gr, char *buffer, size_t buflen, int *errnop);
-/***
- SPDX-License-Identifier: LGPL-2.1+
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
#include <fcntl.h>
#include <sys/prctl.h>
}
static int run(int argc, char *argv[]) {
- const char *device, *type;
- _cleanup_free_ char *detected = NULL;
+ _cleanup_free_ char *device = NULL, *type = NULL, *detected = NULL;
struct stat st;
int r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"This program expects two arguments.");
- type = argv[1];
- device = argv[2];
+ /* type and device must be copied because makefs calls safe_fork, which clears argv[] */
+ type = strdup(argv[1]);
+ if (!type)
+ return -ENOMEM;
+
+ device = strdup(argv[2]);
+ if (!device)
+ return -ENOMEM;
if (stat(device, &st) < 0)
return log_error_errno(errno, "Failed to stat \"%s\": %m", device);
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+
+systemd_repart_sources = files('''
+ repart.c
+'''.split())
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <libfdisk.h>
+#include <linux/fs.h>
+#include <linux/loop.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "blkid-util.h"
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "conf-files.h"
+#include "conf-parser.h"
+#include "def.h"
+#include "efivars.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "format-table.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "gpt.h"
+#include "id128-util.h"
+#include "list.h"
+#include "locale-util.h"
+#include "main-func.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "proc-cmdline.h"
+#include "sort-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "utf8.h"
+
+/* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
+ * are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
+ * sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
+ * waste 3K per partition, which is probably fine. */
+
+static enum {
+ EMPTY_REFUSE, /* refuse empty disks, never create a partition table */
+ EMPTY_ALLOW, /* allow empty disks, create partition table if necessary */
+ EMPTY_REQUIRE, /* require an empty disk, create a partition table */
+ EMPTY_FORCE, /* make disk empty, erase everything, create a partition table always */
+} arg_empty = EMPTY_REFUSE;
+
+static bool arg_dry_run = true;
+static const char *arg_node = NULL;
+static char *arg_root = NULL;
+static char *arg_definitions = NULL;
+static bool arg_discard = true;
+static bool arg_can_factory_reset = false;
+static int arg_factory_reset = -1;
+static sd_id128_t arg_seed = SD_ID128_NULL;
+static bool arg_randomize = false;
+static int arg_pretty = -1;
+
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
+
+typedef struct Partition Partition;
+typedef struct FreeArea FreeArea;
+typedef struct Context Context;
+
+struct Partition {
+ char *definition_path;
+
+ sd_id128_t type_uuid;
+ sd_id128_t current_uuid, new_uuid;
+ char *current_label, *new_label;
+
+ bool dropped;
+ bool factory_reset;
+ int32_t priority;
+
+ uint32_t weight, padding_weight;
+
+ uint64_t current_size, new_size;
+ uint64_t size_min, size_max;
+
+ uint64_t current_padding, new_padding;
+ uint64_t padding_min, padding_max;
+
+ uint64_t partno;
+ uint64_t offset;
+
+ struct fdisk_partition *current_partition;
+ struct fdisk_partition *new_partition;
+ FreeArea *padding_area;
+ FreeArea *allocated_to_area;
+
+ LIST_FIELDS(Partition, partitions);
+};
+
+#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
+#define PARTITION_EXISTS(p) (!!(p)->current_partition)
+
+struct FreeArea {
+ Partition *after;
+ uint64_t size;
+ uint64_t allocated;
+};
+
+struct Context {
+ LIST_HEAD(Partition, partitions);
+ size_t n_partitions;
+
+ FreeArea **free_areas;
+ size_t n_free_areas, n_allocated_free_areas;
+
+ uint64_t start, end, total;
+
+ struct fdisk_context *fdisk_context;
+
+ sd_id128_t seed;
+};
+
+static uint64_t round_down_size(uint64_t v, uint64_t p) {
+ return (v / p) * p;
+}
+
+static uint64_t round_up_size(uint64_t v, uint64_t p) {
+
+ v = DIV_ROUND_UP(v, p);
+
+ if (v > UINT64_MAX / p)
+ return UINT64_MAX; /* overflow */
+
+ return v * p;
+}
+
+static Partition *partition_new(void) {
+ Partition *p;
+
+ p = new(Partition, 1);
+ if (!p)
+ return NULL;
+
+ *p = (Partition) {
+ .weight = 1000,
+ .padding_weight = 0,
+ .current_size = UINT64_MAX,
+ .new_size = UINT64_MAX,
+ .size_min = UINT64_MAX,
+ .size_max = UINT64_MAX,
+ .current_padding = UINT64_MAX,
+ .new_padding = UINT64_MAX,
+ .padding_min = UINT64_MAX,
+ .padding_max = UINT64_MAX,
+ .partno = UINT64_MAX,
+ .offset = UINT64_MAX,
+ };
+
+ return p;
+}
+
+static Partition* partition_free(Partition *p) {
+ if (!p)
+ return NULL;
+
+ free(p->current_label);
+ free(p->new_label);
+ free(p->definition_path);
+
+ if (p->current_partition)
+ fdisk_unref_partition(p->current_partition);
+ if (p->new_partition)
+ fdisk_unref_partition(p->new_partition);
+
+ return mfree(p);
+}
+
+static Partition* partition_unlink_and_free(Context *context, Partition *p) {
+ if (!p)
+ return NULL;
+
+ LIST_REMOVE(partitions, context->partitions, p);
+
+ assert(context->n_partitions > 0);
+ context->n_partitions--;
+
+ return partition_free(p);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Partition*, partition_free);
+
+static Context *context_new(sd_id128_t seed) {
+ Context *context;
+
+ context = new(Context, 1);
+ if (!context)
+ return NULL;
+
+ *context = (Context) {
+ .start = UINT64_MAX,
+ .end = UINT64_MAX,
+ .total = UINT64_MAX,
+ .seed = seed,
+ };
+
+ return context;
+}
+
+static void context_free_free_areas(Context *context) {
+ assert(context);
+
+ for (size_t i = 0; i < context->n_free_areas; i++)
+ free(context->free_areas[i]);
+
+ context->free_areas = mfree(context->free_areas);
+ context->n_free_areas = 0;
+ context->n_allocated_free_areas = 0;
+}
+
+static Context *context_free(Context *context) {
+ if (!context)
+ return NULL;
+
+ while (context->partitions)
+ partition_unlink_and_free(context, context->partitions);
+ assert(context->n_partitions == 0);
+
+ context_free_free_areas(context);
+
+ if (context->fdisk_context)
+ fdisk_unref_context(context->fdisk_context);
+
+ return mfree(context);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free);
+
+static int context_add_free_area(
+ Context *context,
+ uint64_t size,
+ Partition *after) {
+
+ FreeArea *a;
+
+ assert(context);
+ assert(!after || !after->padding_area);
+
+ if (!GREEDY_REALLOC(context->free_areas, context->n_allocated_free_areas, context->n_free_areas + 1))
+ return -ENOMEM;
+
+ a = new(FreeArea, 1);
+ if (!a)
+ return -ENOMEM;
+
+ *a = (FreeArea) {
+ .size = size,
+ .after = after,
+ };
+
+ context->free_areas[context->n_free_areas++] = a;
+
+ if (after)
+ after->padding_area = a;
+
+ return 0;
+}
+
+static bool context_drop_one_priority(Context *context) {
+ int32_t priority = 0;
+ Partition *p;
+ bool exists = false;
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->dropped)
+ continue;
+ if (p->priority < priority)
+ continue;
+ if (p->priority == priority) {
+ exists = exists || PARTITION_EXISTS(p);
+ continue;
+ }
+
+ priority = p->priority;
+ exists = PARTITION_EXISTS(p);
+ }
+
+ /* Refuse to drop partitions with 0 or negative priorities or partitions of priorities that have at
+ * least one existing priority */
+ if (priority <= 0 || exists)
+ return false;
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->priority < priority)
+ continue;
+
+ if (p->dropped)
+ continue;
+
+ p->dropped = true;
+ log_info("Can't fit partition %s of priority %" PRIi32 ", dropping.", p->definition_path, p->priority);
+ }
+
+ return true;
+}
+
+static uint64_t partition_min_size(const Partition *p) {
+ uint64_t sz;
+
+ /* Calculate the disk space we really need at minimum for this partition. If the partition already
+ * exists the current size is what we really need. If it doesn't exist yet refuse to allocate less
+ * than 4K. */
+
+ if (PARTITION_IS_FOREIGN(p)) {
+ /* Don't allow changing size of partitions not managed by us */
+ assert(p->current_size != UINT64_MAX);
+ return p->current_size;
+ }
+
+ sz = p->current_size != UINT64_MAX ? p->current_size : 4096;
+ if (p->size_min != UINT64_MAX)
+ return MAX(p->size_min, sz);
+
+ return sz;
+}
+
+static uint64_t partition_max_size(const Partition *p) {
+ /* Calculate how large the partition may become at max. This is generally the configured maximum
+ * size, except when it already exists and is larger than that. In that case it's the existing size,
+ * since we never want to shrink partitions. */
+
+ if (PARTITION_IS_FOREIGN(p)) {
+ /* Don't allow changing size of partitions not managed by us */
+ assert(p->current_size != UINT64_MAX);
+ return p->current_size;
+ }
+
+ if (p->current_size != UINT64_MAX)
+ return MAX(p->current_size, p->size_max);
+
+ return p->size_max;
+}
+
+static uint64_t partition_min_size_with_padding(const Partition *p) {
+ uint64_t sz;
+
+ /* Calculate the disk space we need for this partition plus any free space coming after it. This
+ * takes user configured padding into account as well as any additional whitespace needed to align
+ * the next partition to 4K again. */
+
+ sz = partition_min_size(p);
+
+ if (p->padding_min != UINT64_MAX)
+ sz += p->padding_min;
+
+ if (PARTITION_EXISTS(p)) {
+ /* If the partition wasn't aligned, add extra space so that any we might add will be aligned */
+ assert(p->offset != UINT64_MAX);
+ return round_up_size(p->offset + sz, 4096) - p->offset;
+ }
+
+ /* If this is a new partition we'll place it aligned, hence we just need to round up the required size here */
+ return round_up_size(sz, 4096);
+}
+
+static uint64_t free_area_available(const FreeArea *a) {
+ assert(a);
+
+ /* Determines how much of this free area is not allocated yet */
+
+ assert(a->size >= a->allocated);
+ return a->size - a->allocated;
+}
+
+static uint64_t free_area_available_for_new_partitions(const FreeArea *a) {
+ uint64_t avail;
+
+ /* Similar to free_area_available(), but takes into account that the required size and padding of the
+ * preceeding partition is honoured. */
+
+ avail = free_area_available(a);
+ if (a->after) {
+ uint64_t need, space;
+
+ need = partition_min_size_with_padding(a->after);
+
+ assert(a->after->offset != UINT64_MAX);
+ assert(a->after->current_size != UINT64_MAX);
+
+ space = round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset + avail;
+ if (need >= space)
+ return 0;
+
+ return space - need;
+ }
+
+ return avail;
+}
+
+static int free_area_compare(FreeArea *const *a, FreeArea *const*b) {
+ return CMP(free_area_available_for_new_partitions(*a),
+ free_area_available_for_new_partitions(*b));
+}
+
+static uint64_t charge_size(uint64_t total, uint64_t amount) {
+ uint64_t rounded;
+
+ assert(amount <= total);
+
+ /* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */
+ rounded = round_up_size(amount, 4096);
+ if (rounded >= total)
+ return 0;
+
+ return total - rounded;
+}
+
+static uint64_t charge_weight(uint64_t total, uint64_t amount) {
+ assert(amount <= total);
+ return total - amount;
+}
+
+static bool context_allocate_partitions(Context *context) {
+ Partition *p;
+
+ assert(context);
+
+ /* A simple first-fit algorithm, assuming the array of free areas is sorted by size in decreasing
+ * order. */
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ bool fits = false;
+ uint64_t required;
+ FreeArea *a = NULL;
+
+ /* Skip partitions we already dropped or that already exist */
+ if (p->dropped || PARTITION_EXISTS(p))
+ continue;
+
+ /* Sort by size */
+ typesafe_qsort(context->free_areas, context->n_free_areas, free_area_compare);
+
+ /* How much do we need to fit? */
+ required = partition_min_size_with_padding(p);
+ assert(required % 4096 == 0);
+
+ for (size_t i = 0; i < context->n_free_areas; i++) {
+ a = context->free_areas[i];
+
+ if (free_area_available_for_new_partitions(a) >= required) {
+ fits = true;
+ break;
+ }
+ }
+
+ if (!fits)
+ return false; /* 😢 Oh no! We can't fit this partition into any free area! */
+
+ /* Assign the partition to this free area */
+ p->allocated_to_area = a;
+
+ /* Budget the minimal partition size */
+ a->allocated += required;
+ }
+
+ return true;
+}
+
+static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
+ uint64_t weight_sum = 0;
+ Partition *p;
+
+ assert(context);
+ assert(a);
+ assert(ret);
+
+ /* Determine the sum of the weights of all partitions placed in or before the specified free area */
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->padding_area != a && p->allocated_to_area != a)
+ continue;
+
+ if (p->weight > UINT64_MAX - weight_sum)
+ goto overflow_sum;
+ weight_sum += p->weight;
+
+ if (p->padding_weight > UINT64_MAX - weight_sum)
+ goto overflow_sum;
+ weight_sum += p->padding_weight;
+ }
+
+ *ret = weight_sum;
+ return 0;
+
+overflow_sum:
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Combined weight of partition exceeds unsigned 64bit range, refusing.");
+}
+
+static int scale_by_weight(uint64_t value, uint64_t weight, uint64_t weight_sum, uint64_t *ret) {
+ assert(weight_sum >= weight);
+ assert(ret);
+
+ if (weight == 0) {
+ *ret = 0;
+ return 0;
+ }
+
+ if (value > UINT64_MAX / weight)
+ return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Scaling by weight of partition exceeds unsigned 64bit range, refusing.");
+
+ *ret = value * weight / weight_sum;
+ return 0;
+}
+
+typedef enum GrowPartitionPhase {
+ /* The first phase: we charge partitions which need more (according to constraints) than their weight-based share. */
+ PHASE_OVERCHARGE,
+
+ /* The second phase: we charge partitions which need less (according to constraints) than their weight-based share. */
+ PHASE_UNDERCHARGE,
+
+ /* The third phase: we distribute what remains among the remaining partitions, according to the weights */
+ PHASE_DISTRIBUTE,
+} GrowPartitionPhase;
+
+static int context_grow_partitions_phase(
+ Context *context,
+ FreeArea *a,
+ GrowPartitionPhase phase,
+ uint64_t *span,
+ uint64_t *weight_sum) {
+
+ Partition *p;
+ int r;
+
+ assert(context);
+ assert(a);
+
+ /* Now let's look at the intended weights and adjust them taking the minimum space assignments into
+ * account. i.e. if a partition has a small weight but a high minimum space value set it should not
+ * get any additional room from the left-overs. Similar, if two partitions have the same weight they
+ * should get the same space if possible, even if one has a smaller minimum size than the other. */
+ LIST_FOREACH(partitions, p, context->partitions) {
+
+ /* Look only at partitions associated with this free area, i.e. immediately
+ * preceeding it, or allocated into it */
+ if (p->allocated_to_area != a && p->padding_area != a)
+ continue;
+
+ if (p->new_size == UINT64_MAX) {
+ bool charge = false, try_again = false;
+ uint64_t share, rsz, xsz;
+
+ /* Calculate how much this space this partition needs if everyone would get
+ * the weight based share */
+ r = scale_by_weight(*span, p->weight, *weight_sum, &share);
+ if (r < 0)
+ return r;
+
+ rsz = partition_min_size(p);
+ xsz = partition_max_size(p);
+
+ if (phase == PHASE_OVERCHARGE && rsz > share) {
+ /* This partition needs more than its calculated share. Let's assign
+ * it that, and take this partition out of all calculations and start
+ * again. */
+
+ p->new_size = rsz;
+ charge = try_again = true;
+
+ } else if (phase == PHASE_UNDERCHARGE && xsz != UINT64_MAX && xsz < share) {
+ /* This partition accepts less than its calculated
+ * share. Let's assign it that, and take this partition out
+ * of all calculations and start again. */
+
+ p->new_size = xsz;
+ charge = try_again = true;
+
+ } else if (phase == PHASE_DISTRIBUTE) {
+ /* This partition can accept its calculated share. Let's
+ * assign it. There's no need to restart things here since
+ * assigning this shouldn't impact the shares of the other
+ * partitions. */
+
+ if (PARTITION_IS_FOREIGN(p))
+ /* Never change of foreign partitions (i.e. those we don't manage) */
+ p->new_size = p->current_size;
+ else
+ p->new_size = MAX(round_down_size(share, 4096), rsz);
+
+ charge = true;
+ }
+
+ if (charge) {
+ *span = charge_size(*span, p->new_size);
+ *weight_sum = charge_weight(*weight_sum, p->weight);
+ }
+
+ if (try_again)
+ return 0; /* try again */
+ }
+
+ if (p->new_padding == UINT64_MAX) {
+ bool charge = false, try_again = false;
+ uint64_t share;
+
+ r = scale_by_weight(*span, p->padding_weight, *weight_sum, &share);
+ if (r < 0)
+ return r;
+
+ if (phase == PHASE_OVERCHARGE && p->padding_min != UINT64_MAX && p->padding_min > share) {
+ p->new_padding = p->padding_min;
+ charge = try_again = true;
+ } else if (phase == PHASE_UNDERCHARGE && p->padding_max != UINT64_MAX && p->padding_max < share) {
+ p->new_padding = p->padding_max;
+ charge = try_again = true;
+ } else if (phase == PHASE_DISTRIBUTE) {
+
+ p->new_padding = round_down_size(share, 4096);
+ if (p->padding_min != UINT64_MAX && p->new_padding < p->padding_min)
+ p->new_padding = p->padding_min;
+
+ charge = true;
+ }
+
+ if (charge) {
+ *span = charge_size(*span, p->new_padding);
+ *weight_sum = charge_weight(*weight_sum, p->padding_weight);
+ }
+
+ if (try_again)
+ return 0; /* try again */
+ }
+ }
+
+ return 1; /* done */
+}
+
+static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
+ uint64_t weight_sum = 0, span;
+ int r;
+
+ assert(context);
+ assert(a);
+
+ r = context_sum_weights(context, a, &weight_sum);
+ if (r < 0)
+ return r;
+
+ /* Let's calculate the total area covered by this free area and the partition before it */
+ span = a->size;
+ if (a->after) {
+ assert(a->after->offset != UINT64_MAX);
+ assert(a->after->current_size != UINT64_MAX);
+
+ span += round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset;
+ }
+
+ GrowPartitionPhase phase = PHASE_OVERCHARGE;
+ for (;;) {
+ r = context_grow_partitions_phase(context, a, phase, &span, &weight_sum);
+ if (r < 0)
+ return r;
+ if (r == 0) /* not done yet, re-run this phase */
+ continue;
+
+ if (phase == PHASE_OVERCHARGE)
+ phase = PHASE_UNDERCHARGE;
+ else if (phase == PHASE_UNDERCHARGE)
+ phase = PHASE_DISTRIBUTE;
+ else if (phase == PHASE_DISTRIBUTE)
+ break;
+ }
+
+ /* We still have space left over? Donate to preceeding partition if we have one */
+ if (span > 0 && a->after && !PARTITION_IS_FOREIGN(a->after)) {
+ uint64_t m, xsz;
+
+ assert(a->after->new_size != UINT64_MAX);
+ m = a->after->new_size + span;
+
+ xsz = partition_max_size(a->after);
+ if (xsz != UINT64_MAX && m > xsz)
+ m = xsz;
+
+ span = charge_size(span, m - a->after->new_size);
+ a->after->new_size = m;
+ }
+
+ /* What? Even still some space left (maybe because there was no preceeding partition, or it had a
+ * size limit), then let's donate it to whoever wants it. */
+ if (span > 0) {
+ Partition *p;
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ uint64_t m, xsz;
+
+ if (p->allocated_to_area != a)
+ continue;
+
+ if (PARTITION_IS_FOREIGN(p))
+ continue;
+
+ assert(p->new_size != UINT64_MAX);
+ m = p->new_size + span;
+
+ xsz = partition_max_size(a->after);
+ if (xsz != UINT64_MAX && m > xsz)
+ m = xsz;
+
+ span = charge_size(span, m - p->new_size);
+ p->new_size = m;
+
+ if (span == 0)
+ break;
+ }
+ }
+
+ /* Yuck, still noone? Then make it padding */
+ if (span > 0 && a->after) {
+ assert(a->after->new_padding != UINT64_MAX);
+ a->after->new_padding += span;
+ }
+
+ return 0;
+}
+
+static int context_grow_partitions(Context *context) {
+ Partition *p;
+ int r;
+
+ assert(context);
+
+ for (size_t i = 0; i < context->n_free_areas; i++) {
+ r = context_grow_partitions_on_free_area(context, context->free_areas[i]);
+ if (r < 0)
+ return r;
+ }
+
+ /* All existing partitions that have no free space after them can't change size */
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->dropped)
+ continue;
+
+ if (!PARTITION_EXISTS(p) || p->padding_area) {
+ /* The algorithm above must have initialized this already */
+ assert(p->new_size != UINT64_MAX);
+ continue;
+ }
+
+ assert(p->new_size == UINT64_MAX);
+ p->new_size = p->current_size;
+
+ assert(p->new_padding == UINT64_MAX);
+ p->new_padding = p->current_padding;
+ }
+
+ return 0;
+}
+
+static void context_place_partitions(Context *context) {
+ uint64_t partno = 0;
+ Partition *p;
+
+ assert(context);
+
+ /* Determine next partition number to assign */
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (!PARTITION_EXISTS(p))
+ continue;
+
+ assert(p->partno != UINT64_MAX);
+ if (p->partno >= partno)
+ partno = p->partno + 1;
+ }
+
+ for (size_t i = 0; i < context->n_free_areas; i++) {
+ FreeArea *a = context->free_areas[i];
+ uint64_t start, left;
+
+ if (a->after) {
+ assert(a->after->offset != UINT64_MAX);
+ assert(a->after->new_size != UINT64_MAX);
+ assert(a->after->new_padding != UINT64_MAX);
+
+ start = a->after->offset + a->after->new_size + a->after->new_padding;
+ } else
+ start = context->start;
+
+ start = round_up_size(start, 4096);
+ left = a->size;
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->allocated_to_area != a)
+ continue;
+
+ p->offset = start;
+ p->partno = partno++;
+
+ assert(left >= p->new_size);
+ start += p->new_size;
+ left -= p->new_size;
+
+ assert(left >= p->new_padding);
+ start += p->new_padding;
+ left -= p->new_padding;
+ }
+ }
+}
+
+static int config_parse_type(
+ 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) {
+
+ sd_id128_t *type_uuid = data;
+ int r;
+
+ assert(rvalue);
+ assert(type_uuid);
+
+ r = gpt_partition_type_uuid_from_string(rvalue, type_uuid);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition type: %s", rvalue);
+
+ return 0;
+}
+
+static int config_parse_label(
+ 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_free_ char16_t *recoded = NULL;
+ char **label = data;
+ int r;
+
+ assert(rvalue);
+ assert(label);
+
+ if (!utf8_is_valid(rvalue)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Partition label not valid UTF-8, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ recoded = utf8_to_utf16(rvalue, strlen(rvalue));
+ if (!recoded)
+ return log_oom();
+
+ if (char16_strlen(recoded) > 36) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Partition label too long for GPT table, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ r = free_and_strdup(label, rvalue);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
+static int config_parse_weight(
+ 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) {
+
+ uint32_t *priority = data, v;
+ int r;
+
+ assert(rvalue);
+ assert(priority);
+
+ r = safe_atou32(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse weight value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (v > 1000U*1000U) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Weight needs to be in range 0…10000000, ignoring: %" PRIu32, v);
+ return 0;
+ }
+
+ *priority = v;
+ return 0;
+}
+
+static int config_parse_size4096(
+ 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) {
+
+ uint64_t *sz = data, parsed;
+ int r;
+
+ assert(rvalue);
+ assert(data);
+
+ r = parse_size(rvalue, 1024, &parsed);
+ if (r < 0)
+ return log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse size value: %s", rvalue);
+
+ if (ltype > 0)
+ *sz = round_up_size(parsed, 4096);
+ else if (ltype < 0)
+ *sz = round_down_size(parsed, 4096);
+ else
+ *sz = parsed;
+
+ if (*sz != parsed)
+ log_syntax(unit, LOG_NOTICE, filename, line, r, "Rounded %s= size %" PRIu64 " → %" PRIu64 ", a multiple of 4096.", lvalue, parsed, *sz);
+
+ return 0;
+}
+
+static int partition_read_definition(Partition *p, const char *path) {
+
+ ConfigTableItem table[] = {
+ { "Partition", "Type", config_parse_type, 0, &p->type_uuid },
+ { "Partition", "Label", config_parse_label, 0, &p->new_label },
+ { "Partition", "Priority", config_parse_int32, 0, &p->priority },
+ { "Partition", "Weight", config_parse_weight, 0, &p->weight },
+ { "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
+ { "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min },
+ { "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max },
+ { "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
+ { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
+ { "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
+ {}
+ };
+ int r;
+
+ r = config_parse(NULL, path, NULL, "Partition\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, p);
+ if (r < 0)
+ return r;
+
+ if (p->size_min != UINT64_MAX && p->size_max != UINT64_MAX && p->size_min > p->size_max)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "SizeMinBytes= larger than SizeMaxBytes=, refusing.");
+
+ if (p->padding_min != UINT64_MAX && p->padding_max != UINT64_MAX && p->padding_min > p->padding_max)
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "PaddingMinBytes= larger than PaddingMaxBytes=, refusing.");
+
+ if (sd_id128_is_null(p->type_uuid))
+ return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+ "Type= not defined, refusing.");
+
+ return 0;
+}
+
+static int context_read_definitions(
+ Context *context,
+ const char *directory,
+ const char *root) {
+
+ _cleanup_strv_free_ char **files = NULL;
+ Partition *last = NULL;
+ char **f;
+ int r;
+
+ assert(context);
+
+ if (directory)
+ r = conf_files_list_strv(&files, ".conf", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) STRV_MAKE(directory));
+ else
+ r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) CONF_PATHS_STRV("repart.d"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate *.conf files: %m");
+
+ STRV_FOREACH(f, files) {
+ _cleanup_(partition_freep) Partition *p = NULL;
+
+ p = partition_new();
+ if (!p)
+ return log_oom();
+
+ p->definition_path = strdup(*f);
+ if (!p->definition_path)
+ return log_oom();
+
+ r = partition_read_definition(p, *f);
+ if (r < 0)
+ return r;
+
+ LIST_INSERT_AFTER(partitions, context->partitions, last, p);
+ last = TAKE_PTR(p);
+ context->n_partitions++;
+ }
+
+ return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_context*, fdisk_unref_context);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_partition*, fdisk_unref_partition);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_parttype*, fdisk_unref_parttype);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_table*, fdisk_unref_table);
+
+static int determine_current_padding(
+ struct fdisk_context *c,
+ struct fdisk_table *t,
+ struct fdisk_partition *p,
+ uint64_t *ret) {
+
+ size_t n_partitions;
+ uint64_t offset, next = UINT64_MAX;
+
+ assert(c);
+ assert(t);
+ assert(p);
+
+ if (!fdisk_partition_has_end(p))
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!");
+
+ offset = fdisk_partition_get_end(p);
+ assert(offset < UINT64_MAX / 512);
+ offset *= 512;
+
+ n_partitions = fdisk_table_get_nents(t);
+ for (size_t i = 0; i < n_partitions; i++) {
+ struct fdisk_partition *q;
+ uint64_t start;
+
+ q = fdisk_table_get_partition(t, i);
+ if (!q)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+
+ if (fdisk_partition_is_used(q) <= 0)
+ continue;
+
+ if (!fdisk_partition_has_start(q))
+ continue;
+
+ start = fdisk_partition_get_start(q);
+ assert(start < UINT64_MAX / 512);
+ start *= 512;
+
+ if (start >= offset && (next == UINT64_MAX || next > start))
+ next = start;
+ }
+
+ if (next == UINT64_MAX) {
+ /* No later partition? In that case check the end of the usable area */
+ next = fdisk_get_last_lba(c);
+ assert(next < UINT64_MAX);
+ next++; /* The last LBA is one sector before the end */
+
+ assert(next < UINT64_MAX / 512);
+ next *= 512;
+
+ if (offset > next)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
+ }
+
+ assert(next >= offset);
+ offset = round_up_size(offset, 4096);
+ next = round_down_size(next, 4096);
+
+ if (next >= offset) /* Check again, rounding might have fucked things up */
+ *ret = next - offset;
+ else
+ *ret = 0;
+
+ return 0;
+}
+
+static int fdisk_ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *data) {
+ _cleanup_free_ char *ids = NULL;
+ int r;
+
+ if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_STRING)
+ return -EINVAL;
+
+ ids = new(char, ID128_UUID_STRING_MAX);
+ if (!ids)
+ return -ENOMEM;
+
+ r = fdisk_ask_string_set_result(ask, id128_to_uuid_string(*(sd_id128_t*) data, ids));
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(ids);
+ return 0;
+}
+
+static int fdisk_set_disklabel_id_by_uuid(struct fdisk_context *c, sd_id128_t id) {
+ int r;
+
+ r = fdisk_set_ask(c, fdisk_ask_cb, &id);
+ if (r < 0)
+ return r;
+
+ r = fdisk_set_disklabel_id(c);
+ if (r < 0)
+ return r;
+
+ return fdisk_set_ask(c, NULL, NULL);
+}
+
+#define DISK_UUID_TOKEN "disk-uuid"
+
+static int disk_acquire_uuid(Context *context, sd_id128_t *ret) {
+ union {
+ unsigned char md[SHA256_DIGEST_LENGTH];
+ sd_id128_t id;
+ } result;
+
+ assert(context);
+ assert(ret);
+
+ /* Calculate the HMAC-SHA256 of the string "disk-uuid", keyed off the machine ID. We use the machine
+ * ID as key (and not as cleartext!) since it's the machine ID we don't want to leak. */
+
+ if (!HMAC(EVP_sha256(),
+ &context->seed, sizeof(context->seed),
+ (const unsigned char*) DISK_UUID_TOKEN, strlen(DISK_UUID_TOKEN),
+ result.md, NULL))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "HMAC-SHA256 calculation failed.");
+
+ /* Take the first half, mark it as v4 UUID */
+ assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
+ *ret = id128_make_v4_uuid(result.id);
+ return 0;
+}
+
+static int context_load_partition_table(Context *context, const char *node) {
+ _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+ _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
+ uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors;
+ _cleanup_free_ char *disk_uuid_string = NULL;
+ bool from_scratch = false;
+ sd_id128_t disk_uuid;
+ size_t n_partitions;
+ int r;
+
+ assert(context);
+ assert(node);
+
+ c = fdisk_new_context();
+ if (!c)
+ return log_oom();
+
+ r = fdisk_assign_device(c, node, arg_dry_run);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open device: %m");
+
+ /* Tell udev not to interfere while we are processing the device */
+ if (flock(fdisk_get_devfd(c), arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
+ return log_error_errno(errno, "Failed to lock block device: %m");
+
+ switch (arg_empty) {
+
+ case EMPTY_REFUSE:
+ /* Refuse empty disks, insist on an existing GPT partition table */
+ if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+ return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", node);
+
+ break;
+
+ case EMPTY_REQUIRE:
+ /* Require an empty disk, refuse any existing partition table */
+ r = fdisk_has_label(c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+ if (r > 0)
+ return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", node);
+
+ from_scratch = true;
+ break;
+
+ case EMPTY_ALLOW:
+ /* Allow both an empty disk and an existing partition table, but only GPT */
+ r = fdisk_has_label(c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+ if (r > 0) {
+ if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+ return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", node);
+ } else
+ from_scratch = true;
+
+ break;
+
+ case EMPTY_FORCE:
+ /* Always reinitiaize the disk, don't consider what there was on the disk before */
+ from_scratch = true;
+ break;
+ }
+
+ if (from_scratch) {
+ r = fdisk_enable_wipe(c, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable wiping of disk signature: %m");
+
+ r = fdisk_create_disklabel(c, "gpt");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create GPT disk label: %m");
+
+ r = disk_acquire_uuid(context, &disk_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
+
+ r = fdisk_set_disklabel_id_by_uuid(c, disk_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set GPT disk label: %m");
+
+ goto add_initial_free_area;
+ }
+
+ r = fdisk_get_disklabel_id(c, &disk_uuid_string);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get current GPT disk label UUID: %m");
+
+ r = sd_id128_from_string(disk_uuid_string, &disk_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse current GPT disk label UUID: %m");
+
+ if (sd_id128_is_null(disk_uuid)) {
+ r = disk_acquire_uuid(context, &disk_uuid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
+
+ r = fdisk_set_disklabel_id(c);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set GPT disk label: %m");
+ }
+
+ r = fdisk_get_partitions(c, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire partition table: %m");
+
+ n_partitions = fdisk_table_get_nents(t);
+ for (size_t i = 0; i < n_partitions; i++) {
+ _cleanup_free_ char *label_copy = NULL;
+ Partition *pp, *last = NULL;
+ struct fdisk_partition *p;
+ struct fdisk_parttype *pt;
+ const char *pts, *ids, *label;
+ uint64_t sz, start;
+ bool found = false;
+ sd_id128_t ptid, id;
+ size_t partno;
+
+ p = fdisk_table_get_partition(t, i);
+ if (!p)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+
+ if (fdisk_partition_is_used(p) <= 0)
+ continue;
+
+ if (fdisk_partition_has_start(p) <= 0 ||
+ fdisk_partition_has_size(p) <= 0 ||
+ fdisk_partition_has_partno(p) <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a position, size or number.");
+
+ pt = fdisk_partition_get_type(p);
+ if (!pt)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition: %m");
+
+ pts = fdisk_parttype_get_string(pt);
+ if (!pts)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition as string: %m");
+
+ r = sd_id128_from_string(pts, &ptid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse partition type UUID %s: %m", pts);
+
+ ids = fdisk_partition_get_uuid(p);
+ if (!ids)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a UUID.");
+
+ r = sd_id128_from_string(ids, &id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse partition UUID %s: %m", ids);
+
+ label = fdisk_partition_get_name(p);
+ if (!isempty(label)) {
+ label_copy = strdup(label);
+ if (!label_copy)
+ return log_oom();
+ }
+
+ sz = fdisk_partition_get_size(p);
+ assert_se(sz <= UINT64_MAX/512);
+ sz *= 512;
+
+ start = fdisk_partition_get_start(p);
+ assert_se(start <= UINT64_MAX/512);
+ start *= 512;
+
+ partno = fdisk_partition_get_partno(p);
+
+ if (left_boundary == UINT64_MAX || left_boundary > start)
+ left_boundary = start;
+
+ /* Assign this existing partition to the first partition of the right type that doesn't have
+ * an existing one assigned yet. */
+ LIST_FOREACH(partitions, pp, context->partitions) {
+ last = pp;
+
+ if (!sd_id128_equal(pp->type_uuid, ptid))
+ continue;
+
+ if (!pp->current_partition) {
+ pp->current_uuid = id;
+ pp->current_size = sz;
+ pp->offset = start;
+ pp->partno = partno;
+ pp->current_label = TAKE_PTR(label_copy);
+
+ pp->current_partition = p;
+ fdisk_ref_partition(p);
+
+ r = determine_current_padding(c, t, p, &pp->current_padding);
+ if (r < 0)
+ return r;
+
+ if (pp->current_padding > 0) {
+ r = context_add_free_area(context, pp->current_padding, pp);
+ if (r < 0)
+ return r;
+ }
+
+ found = true;
+ break;
+ }
+ }
+
+ /* If we have no matching definition, create a new one. */
+ if (!found) {
+ _cleanup_(partition_freep) Partition *np = NULL;
+
+ np = partition_new();
+ if (!np)
+ return log_oom();
+
+ np->current_uuid = id;
+ np->type_uuid = ptid;
+ np->current_size = sz;
+ np->offset = start;
+ np->partno = partno;
+ np->current_label = TAKE_PTR(label_copy);
+
+ np->current_partition = p;
+ fdisk_ref_partition(p);
+
+ r = determine_current_padding(c, t, p, &np->current_padding);
+ if (r < 0)
+ return r;
+
+ if (np->current_padding > 0) {
+ r = context_add_free_area(context, np->current_padding, np);
+ if (r < 0)
+ return r;
+ }
+
+ LIST_INSERT_AFTER(partitions, context->partitions, last, TAKE_PTR(np));
+ context->n_partitions++;
+ }
+ }
+
+add_initial_free_area:
+ nsectors = fdisk_get_nsectors(c);
+ assert(nsectors <= UINT64_MAX/512);
+ nsectors *= 512;
+
+ first_lba = fdisk_get_first_lba(c);
+ assert(first_lba <= UINT64_MAX/512);
+ first_lba *= 512;
+
+ last_lba = fdisk_get_last_lba(c);
+ assert(last_lba < UINT64_MAX);
+ last_lba++;
+ assert(last_lba <= UINT64_MAX/512);
+ last_lba *= 512;
+
+ assert(last_lba >= first_lba);
+
+ if (left_boundary == UINT64_MAX) {
+ /* No partitions at all? Then the whole disk is up for grabs. */
+
+ first_lba = round_up_size(first_lba, 4096);
+ last_lba = round_down_size(last_lba, 4096);
+
+ if (last_lba > first_lba) {
+ r = context_add_free_area(context, last_lba - first_lba, NULL);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ /* Add space left of first partition */
+ assert(left_boundary >= first_lba);
+
+ first_lba = round_up_size(first_lba, 4096);
+ left_boundary = round_down_size(left_boundary, 4096);
+ last_lba = round_down_size(last_lba, 4096);
+
+ if (left_boundary > first_lba) {
+ r = context_add_free_area(context, left_boundary - first_lba, NULL);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ context->start = first_lba;
+ context->end = last_lba;
+ context->total = nsectors;
+ context->fdisk_context = TAKE_PTR(c);
+
+ return from_scratch;
+}
+
+static void context_unload_partition_table(Context *context) {
+ Partition *p, *next;
+
+ assert(context);
+
+ LIST_FOREACH_SAFE(partitions, p, next, context->partitions) {
+
+ /* Entirely remove partitions that have no configuration */
+ if (PARTITION_IS_FOREIGN(p)) {
+ partition_unlink_and_free(context, p);
+ continue;
+ }
+
+ /* Otherwise drop all data we read off the block device and everything we might have
+ * calculated based on it */
+
+ p->dropped = false;
+ p->current_size = UINT64_MAX;
+ p->new_size = UINT64_MAX;
+ p->current_padding = UINT64_MAX;
+ p->new_padding = UINT64_MAX;
+ p->partno = UINT64_MAX;
+ p->offset = UINT64_MAX;
+
+ if (p->current_partition) {
+ fdisk_unref_partition(p->current_partition);
+ p->current_partition = NULL;
+ }
+
+ if (p->new_partition) {
+ fdisk_unref_partition(p->new_partition);
+ p->new_partition = NULL;
+ }
+
+ p->padding_area = NULL;
+ p->allocated_to_area = NULL;
+
+ p->current_uuid = p->new_uuid = SD_ID128_NULL;
+ }
+
+ context->start = UINT64_MAX;
+ context->end = UINT64_MAX;
+ context->total = UINT64_MAX;
+
+ if (context->fdisk_context) {
+ fdisk_unref_context(context->fdisk_context);
+ context->fdisk_context = NULL;
+ }
+
+ context_free_free_areas(context);
+}
+
+static int format_size_change(uint64_t from, uint64_t to, char **ret) {
+ char format_buffer1[FORMAT_BYTES_MAX], format_buffer2[FORMAT_BYTES_MAX], *buf;
+
+ if (from != UINT64_MAX)
+ format_bytes(format_buffer1, sizeof(format_buffer1), from);
+ if (to != UINT64_MAX)
+ format_bytes(format_buffer2, sizeof(format_buffer2), to);
+
+ if (from != UINT64_MAX) {
+ if (from == to || to == UINT64_MAX)
+ buf = strdup(format_buffer1);
+ else
+ buf = strjoin(format_buffer1, " ", special_glyph(SPECIAL_GLYPH_ARROW), " ", format_buffer2);
+ } else if (to != UINT64_MAX)
+ buf = strjoin(special_glyph(SPECIAL_GLYPH_ARROW), " ", format_buffer2);
+ else {
+ *ret = NULL;
+ return 0;
+ }
+
+ if (!buf)
+ return log_oom();
+
+ *ret = TAKE_PTR(buf);
+ return 1;
+}
+
+static const char *partition_label(const Partition *p) {
+ assert(p);
+
+ if (p->new_label)
+ return p->new_label;
+
+ if (p->current_label)
+ return p->current_label;
+
+ return gpt_partition_type_uuid_to_string(p->type_uuid);
+}
+
+static int context_dump_partitions(Context *context, const char *node) {
+ _cleanup_(table_unrefp) Table *t = NULL;
+ uint64_t sum_padding = 0, sum_size = 0;
+ Partition *p;
+ int r;
+
+ t = table_new("type", "label", "uuid", "file", "node", "offset", "raw size", "size", "raw padding", "padding");
+ if (!t)
+ return log_oom();
+
+ if (!DEBUG_LOGGING)
+ (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 7, (size_t) 9, (size_t) -1);
+
+ (void) table_set_align_percent(t, table_get_cell(t, 0, 4), 100);
+ (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL;
+ char uuid_buffer[ID128_UUID_STRING_MAX];
+ const char *label;
+
+ if (p->dropped)
+ continue;
+
+ label = partition_label(p);
+ partname = p->partno != UINT64_MAX ? fdisk_partname(node, p->partno+1) : NULL;
+
+ r = format_size_change(p->current_size, p->new_size, &size_change);
+ if (r < 0)
+ return r;
+
+ r = format_size_change(p->current_padding, p->new_padding, &padding_change);
+ if (r < 0)
+ return r;
+
+ if (p->new_size != UINT64_MAX)
+ sum_size += p->new_size;
+ if (p->new_padding != UINT64_MAX)
+ sum_padding += p->new_padding;
+
+ r = table_add_many(
+ t,
+ TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type_uuid, uuid_buffer),
+ TABLE_STRING, label ?: "-", TABLE_SET_COLOR, label ? NULL : ansi_grey(),
+ TABLE_UUID, sd_id128_is_null(p->new_uuid) ? p->current_uuid : p->new_uuid,
+ TABLE_STRING, p->definition_path ? basename(p->definition_path) : "-", TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
+ TABLE_STRING, partname ?: "no", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
+ TABLE_UINT64, p->offset,
+ TABLE_UINT64, p->new_size,
+ TABLE_STRING, size_change, TABLE_SET_COLOR, !p->partitions_next && sum_size > 0 ? ansi_underline() : NULL,
+ TABLE_UINT64, p->new_padding,
+ TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add row to table: %m");
+ }
+
+ if (sum_padding > 0 || sum_size > 0) {
+ char s[FORMAT_BYTES_MAX];
+ const char *a, *b;
+
+ a = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", format_bytes(s, sizeof(s), sum_size));
+ b = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", format_bytes(s, sizeof(s), sum_padding));
+
+ r = table_add_many(
+ t,
+ TABLE_EMPTY,
+ TABLE_EMPTY,
+ TABLE_EMPTY,
+ TABLE_EMPTY,
+ TABLE_EMPTY,
+ TABLE_EMPTY,
+ TABLE_EMPTY,
+ TABLE_STRING, a,
+ TABLE_EMPTY,
+ TABLE_STRING, b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add row to table: %m");
+ }
+
+ r = table_print(t, stdout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to dump table: %m");
+
+ return 0;
+}
+
+static void context_bar_char_process_partition(
+ Context *context,
+ Partition *bar[],
+ size_t n,
+ Partition *p,
+ size_t *ret_start) {
+
+ uint64_t from, to, total;
+ size_t x, y;
+
+ assert(context);
+ assert(bar);
+ assert(n > 0);
+ assert(p);
+
+ if (p->dropped)
+ return;
+
+ assert(p->offset != UINT64_MAX);
+ assert(p->new_size != UINT64_MAX);
+
+ from = p->offset;
+ to = from + p->new_size;
+
+ assert(context->end >= context->start);
+ total = context->end - context->start;
+
+ assert(from >= context->start);
+ assert(from <= context->end);
+ x = (from - context->start) * n / total;
+
+ assert(to >= context->start);
+ assert(to <= context->end);
+ y = (to - context->start) * n / total;
+
+ assert(x <= y);
+ assert(y <= n);
+
+ for (size_t i = x; i < y; i++)
+ bar[i] = p;
+
+ *ret_start = x;
+}
+
+static int partition_hint(const Partition *p, const char *node, char **ret) {
+ _cleanup_free_ char *buf = NULL;
+ char ids[ID128_UUID_STRING_MAX];
+ const char *label;
+ sd_id128_t id;
+
+ /* Tries really hard to find a suitable description for this partition */
+
+ if (p->definition_path) {
+ buf = strdup(basename(p->definition_path));
+ goto done;
+ }
+
+ label = partition_label(p);
+ if (!isempty(label)) {
+ buf = strdup(label);
+ goto done;
+ }
+
+ if (p->partno != UINT64_MAX) {
+ buf = fdisk_partname(node, p->partno+1);
+ goto done;
+ }
+
+ if (!sd_id128_is_null(p->new_uuid))
+ id = p->new_uuid;
+ else if (!sd_id128_is_null(p->current_uuid))
+ id = p->current_uuid;
+ else
+ id = p->type_uuid;
+
+ buf = strdup(id128_to_uuid_string(id, ids));
+
+done:
+ if (!buf)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+static int context_dump_partition_bar(Context *context, const char *node) {
+ _cleanup_free_ Partition **bar = NULL;
+ _cleanup_free_ size_t *start_array = NULL;
+ Partition *p, *last = NULL;
+ bool z = false;
+ size_t c, j = 0;
+
+ assert((c = columns()) >= 2);
+ c -= 2; /* We do not use the leftmost and rightmost character cell */
+
+ bar = new0(Partition*, c);
+ if (!bar)
+ return log_oom();
+
+ start_array = new(size_t, context->n_partitions);
+ if (!start_array)
+ return log_oom();
+
+ LIST_FOREACH(partitions, p, context->partitions)
+ context_bar_char_process_partition(context, bar, c, p, start_array + j++);
+
+ putc(' ', stdout);
+
+ for (size_t i = 0; i < c; i++) {
+ if (bar[i]) {
+ if (last != bar[i])
+ z = !z;
+
+ fputs(z ? ansi_green() : ansi_yellow(), stdout);
+ fputs(special_glyph(SPECIAL_GLYPH_DARK_SHADE), stdout);
+ } else {
+ fputs(ansi_normal(), stdout);
+ fputs(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), stdout);
+ }
+
+ last = bar[i];
+ }
+
+ fputs(ansi_normal(), stdout);
+ putc('\n', stdout);
+
+ for (size_t i = 0; i < context->n_partitions; i++) {
+ _cleanup_free_ char **line = NULL;
+
+ line = new0(char*, c);
+ if (!line)
+ return log_oom();
+
+ j = 0;
+ LIST_FOREACH(partitions, p, context->partitions) {
+ _cleanup_free_ char *d = NULL;
+ j++;
+
+ if (i < context->n_partitions - j) {
+
+ if (line[start_array[j-1]]) {
+ const char *e;
+
+ /* Upgrade final corner to the right with a branch to the right */
+ e = startswith(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
+ if (e) {
+ d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), e);
+ if (!d)
+ return log_oom();
+ }
+ }
+
+ if (!d) {
+ d = strdup(special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
+ if (!d)
+ return log_oom();
+ }
+
+ } else if (i == context->n_partitions - j) {
+ _cleanup_free_ char *hint = NULL;
+
+ (void) partition_hint(p, node, &hint);
+
+ if (streq_ptr(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_VERTICAL)))
+ d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), " ", strna(hint));
+ else
+ d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT), " ", strna(hint));
+
+ if (!d)
+ return log_oom();
+ }
+
+ if (d)
+ free_and_replace(line[start_array[j-1]], d);
+ }
+
+ putc(' ', stdout);
+
+ j = 0;
+ while (j < c) {
+ if (line[j]) {
+ fputs(line[j], stdout);
+ j += utf8_console_width(line[j]);
+ } else {
+ putc(' ', stdout);
+ j++;
+ }
+ }
+
+ putc('\n', stdout);
+
+ for (j = 0; j < c; j++)
+ free(line[j]);
+ }
+
+ return 0;
+}
+
+static bool context_changed(const Context *context) {
+ Partition *p;
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->dropped)
+ continue;
+
+ if (p->allocated_to_area)
+ return true;
+
+ if (p->new_size != p->current_size)
+ return true;
+ }
+
+ return false;
+}
+
+static int context_wipe_partition(Context *context, Partition *p) {
+ _cleanup_(blkid_free_probep) blkid_probe probe = NULL;
+ int r;
+
+ assert(context);
+ assert(p);
+ assert(!PARTITION_EXISTS(p)); /* Safety check: never wipe existing partitions */
+
+ probe = blkid_new_probe();
+ if (!probe)
+ return log_oom();
+
+ assert(p->offset != UINT64_MAX);
+ assert(p->new_size != UINT64_MAX);
+
+ errno = 0;
+ r = blkid_probe_set_device(probe, fdisk_get_devfd(context->fdisk_context), p->offset, p->new_size);
+ if (r < 0)
+ return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to allocate device probe for partition %" PRIu64 ".", p->partno);
+
+ errno = 0;
+ if (blkid_probe_enable_superblocks(probe, true) < 0 ||
+ blkid_probe_set_superblocks_flags(probe, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_BADCSUM) < 0 ||
+ blkid_probe_enable_partitions(probe, true) < 0 ||
+ blkid_probe_set_partitions_flags(probe, BLKID_PARTS_MAGIC) < 0)
+ return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to enable superblock and partition probing for partition %" PRIu64 ".", p->partno);
+
+ for (;;) {
+ errno = 0;
+ r = blkid_do_probe(probe);
+ if (r < 0)
+ return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe for file systems.");
+ if (r > 0)
+ break;
+
+ errno = 0;
+ if (blkid_do_wipe(probe, false) < 0)
+ return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to wipe file system signature.");
+ }
+
+ log_info("Successfully wiped file system signatures from partition %" PRIu64 ".", p->partno);
+ return 0;
+}
+
+static int context_discard_range(Context *context, uint64_t offset, uint64_t size) {
+ struct stat st;
+ int fd;
+
+ assert(context);
+ assert(offset != UINT64_MAX);
+ assert(size != UINT64_MAX);
+
+ if (size <= 0)
+ return 0;
+
+ fd = fdisk_get_devfd(context->fdisk_context);
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (S_ISREG(st.st_mode)) {
+ if (fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, offset, size) < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno))
+ return -EOPNOTSUPP;
+
+ return -errno;
+ }
+
+ return 1;
+ }
+
+ if (S_ISBLK(st.st_mode)) {
+ uint64_t range[2], end;
+
+ range[0] = round_up_size(offset, 512);
+
+ end = offset + size;
+ if (end <= range[0])
+ return 0;
+
+ range[1] = round_down_size(end - range[0], 512);
+ if (range[1] <= 0)
+ return 0;
+
+ if (ioctl(fd, BLKDISCARD, range) < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno))
+ return -EOPNOTSUPP;
+
+ return -errno;
+ }
+
+ return 1;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int context_discard_partition(Context *context, Partition *p) {
+ int r;
+
+ assert(context);
+ assert(p);
+
+ assert(p->offset != UINT64_MAX);
+ assert(p->new_size != UINT64_MAX);
+ assert(!PARTITION_EXISTS(p)); /* Safety check: never discard existing partitions */
+
+ if (!arg_discard)
+ return 0;
+
+ r = context_discard_range(context, p->offset, p->new_size);
+ if (r == -EOPNOTSUPP) {
+ log_info("Storage does not support discarding, not discarding data in new partition %" PRIu64 ".", p->partno);
+ return 0;
+ }
+ if (r == 0) {
+ log_info("Partition %" PRIu64 " too short for discard, skipping.", p->partno);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to discard data for new partition %" PRIu64 ".", p->partno);
+
+ log_info("Successfully discarded data from partition %" PRIu64 ".", p->partno);
+ return 1;
+}
+
+static int context_discard_gap_after(Context *context, Partition *p) {
+ uint64_t gap, next = UINT64_MAX;
+ Partition *q;
+ int r;
+
+ assert(context);
+ assert(!p || (p->offset != UINT64_MAX && p->new_size != UINT64_MAX));
+
+ if (p)
+ gap = p->offset + p->new_size;
+ else
+ gap = context->start;
+
+ LIST_FOREACH(partitions, q, context->partitions) {
+ if (q->dropped)
+ continue;
+
+ assert(q->offset != UINT64_MAX);
+ assert(q->new_size != UINT64_MAX);
+
+ if (q->offset < gap)
+ continue;
+
+ if (next == UINT64_MAX || q->offset < next)
+ next = q->offset;
+ }
+
+ if (next == UINT64_MAX) {
+ next = context->end;
+ if (gap > next)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
+ }
+
+ assert(next >= gap);
+ r = context_discard_range(context, gap, next - gap);
+ if (r == -EOPNOTSUPP) {
+ if (p)
+ log_info("Storage does not support discarding, not discarding gap after partition %" PRIu64 ".", p->partno);
+ else
+ log_info("Storage does not support discarding, not discarding gap at beginning of disk.");
+ return 0;
+ }
+ if (r == 0) /* Too short */
+ return 0;
+ if (r < 0) {
+ if (p)
+ return log_error_errno(r, "Failed to discard gap after partition %" PRIu64 ".", p->partno);
+ else
+ return log_error_errno(r, "Failed to discard gap at beginning of disk.");
+ }
+
+ if (p)
+ log_info("Successfully discarded gap after partition %" PRIu64 ".", p->partno);
+ else
+ log_info("Successfully discarded gap at beginning of disk.");
+
+ return 0;
+}
+
+static int context_wipe_and_discard(Context *context, bool from_scratch) {
+ Partition *p;
+ int r;
+
+ assert(context);
+
+ /* Wipe and discard the contents of all partitions we are about to create. We skip the discarding if
+ * we were supposed to start from scratch anyway, as in that case we just discard the whole block
+ * device in one go early on. */
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+
+ if (!p->allocated_to_area)
+ continue;
+
+ if (!from_scratch) {
+ r = context_discard_partition(context, p);
+ if (r < 0)
+ return r;
+ }
+
+ r = context_wipe_partition(context, p);
+ if (r < 0)
+ return r;
+
+ if (!from_scratch) {
+ r = context_discard_gap_after(context, p);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (!from_scratch) {
+ r = context_discard_gap_after(context, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
+ struct {
+ sd_id128_t type_uuid;
+ uint64_t counter;
+ } _packed_ plaintext = {};
+ union {
+ unsigned char md[SHA256_DIGEST_LENGTH];
+ sd_id128_t id;
+ } result;
+
+ uint64_t k = 0;
+ Partition *q;
+ int r;
+
+ assert(context);
+ assert(p);
+ assert(ret);
+
+ /* Calculate a good UUID for the indicated partition. We want a certain degree of reproducibility,
+ * hence we won't generate the UUIDs randomly. Instead we use a cryptographic hash (precisely:
+ * HMAC-SHA256) to derive them from a single seed. The seed is generally the machine ID of the
+ * installation we are processing, but if random behaviour is desired can be random, too. We use the
+ * seed value as key for the HMAC (since the machine ID is something we generally don't want to leak)
+ * and the partition type as plaintext. The partition type is suffixed with a counter (only for the
+ * second and later partition of the same type) if we have more than one partition of the same
+ * time. Or in other words:
+ *
+ * With:
+ * SEED := /etc/machine-id
+ *
+ * If first partition instance of type TYPE_UUID:
+ * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID)
+ *
+ * For all later partition instances of type TYPE_UUID with INSTANCE being the LE64 encoded instance number:
+ * PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID || INSTANCE)
+ */
+
+ LIST_FOREACH(partitions, q, context->partitions) {
+ if (p == q)
+ break;
+
+ if (!sd_id128_equal(p->type_uuid, q->type_uuid))
+ continue;
+
+ k++;
+ }
+
+ plaintext.type_uuid = p->type_uuid;
+ plaintext.counter = htole64(k);
+
+ if (!HMAC(EVP_sha256(),
+ &context->seed, sizeof(context->seed),
+ (const unsigned char*) &plaintext, k == 0 ? sizeof(sd_id128_t) : sizeof(plaintext),
+ result.md, NULL))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "SHA256 calculation failed.");
+
+ /* Take the first half, mark it as v4 UUID */
+ assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
+ result.id = id128_make_v4_uuid(result.id);
+
+ /* Ensure this partition UUID is actually unique, and there's no remaining partition from an earlier run? */
+ LIST_FOREACH(partitions, q, context->partitions) {
+ if (p == q)
+ continue;
+
+ if (sd_id128_equal(q->current_uuid, result.id) ||
+ sd_id128_equal(q->new_uuid, result.id)) {
+ log_warning("Partition UUID calculated from seed for partition %" PRIu64 " exists already, reverting to randomized UUID.", p->partno);
+
+ r = sd_id128_randomize(&result.id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate randomized UUID: %m");
+
+ break;
+ }
+ }
+
+ *ret = result.id;
+ return 0;
+}
+
+static int partition_acquire_label(Context *context, Partition *p, char **ret) {
+ _cleanup_free_ char *label = NULL;
+ const char *prefix;
+ unsigned k = 1;
+
+ assert(context);
+ assert(p);
+ assert(ret);
+
+ prefix = gpt_partition_type_uuid_to_string(p->type_uuid);
+ if (!prefix)
+ prefix = "linux";
+
+ for (;;) {
+ const char *ll = label ?: prefix;
+ bool retry = false;
+ Partition *q;
+
+ LIST_FOREACH(partitions, q, context->partitions) {
+ if (p == q)
+ break;
+
+ if (streq_ptr(ll, q->current_label) ||
+ streq_ptr(ll, q->new_label)) {
+ retry = true;
+ break;
+ }
+ }
+
+ if (!retry)
+ break;
+
+ label = mfree(label);
+
+
+ if (asprintf(&label, "%s-%u", prefix, ++k) < 0)
+ return log_oom();
+ }
+
+ if (!label) {
+ label = strdup(prefix);
+ if (!label)
+ return log_oom();
+ }
+
+ *ret = TAKE_PTR(label);
+ return 0;
+}
+
+static int context_acquire_partition_uuids_and_labels(Context *context) {
+ Partition *p;
+ int r;
+
+ assert(context);
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ assert(sd_id128_is_null(p->new_uuid));
+ assert(!p->new_label);
+
+ /* Never touch foreign partitions */
+ if (PARTITION_IS_FOREIGN(p)) {
+ p->new_uuid = p->current_uuid;
+
+ if (p->current_label) {
+ p->new_label = strdup(p->current_label);
+ if (!p->new_label)
+ return log_oom();
+ }
+
+ continue;
+ }
+
+ if (!sd_id128_is_null(p->current_uuid))
+ p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
+ else {
+ r = partition_acquire_uuid(context, p, &p->new_uuid);
+ if (r < 0)
+ return r;
+ }
+
+ if (!isempty(p->current_label)) {
+ p->new_label = strdup(p->current_label); /* never change initialized labels */
+ if (!p->new_label)
+ return log_oom();
+ } else {
+ r = partition_acquire_label(context, p, &p->new_label);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int device_kernel_partitions_supported(int fd) {
+ struct loop_info64 info;
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return log_error_errno(fd, "Failed to fstat() image file: %m");
+ if (!S_ISBLK(st.st_mode))
+ return false;
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
+
+ if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EINVAL)
+ return true; /* not a loopback device, let's assume partition are supported */
+
+ return log_error_errno(fd, "Failed to issue LOOP_GET_STATUS64 on block device: %m");
+ }
+
+#if HAVE_VALGRIND_MEMCHECK_H
+ /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
+ VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
+ return FLAGS_SET(info.lo_flags, LO_FLAGS_PARTSCAN);
+}
+
+static int context_write_partition_table(
+ Context *context,
+ const char *node,
+ bool from_scratch) {
+
+ _cleanup_(fdisk_unref_tablep) struct fdisk_table *original_table = NULL;
+ int capable, r;
+ Partition *p;
+
+ assert(context);
+
+ if (arg_pretty > 0 ||
+ (arg_pretty < 0 && isatty(STDOUT_FILENO) > 0)) {
+
+ if (context->n_partitions == 0)
+ puts("Empty partition table.");
+ else
+ (void) context_dump_partitions(context, node);
+
+ putc('\n', stdout);
+
+ (void) context_dump_partition_bar(context, node);
+ putc('\n', stdout);
+ fflush(stdout);
+ }
+
+ if (!from_scratch && !context_changed(context)) {
+ log_info("No changes.");
+ return 0;
+ }
+
+ if (arg_dry_run) {
+ log_notice("Refusing to repartition, please re-run with --dry-run=no.");
+ return 0;
+ }
+
+ log_info("Applying changes.");
+
+ if (from_scratch) {
+ r = context_discard_range(context, 0, context->total);
+ if (r == -EOPNOTSUPP)
+ log_info("Storage does not support discarding, not discarding entire block device data.");
+ else if (r < 0)
+ return log_error_errno(r, "Failed to discard entire block device: %m");
+ else if (r > 0)
+ log_info("Discarded entire block device.");
+ }
+
+ r = fdisk_get_partitions(context->fdisk_context, &original_table);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire partition table: %m");
+
+ /* Wipe fs signatures and discard sectors where the new partitions are going to be placed and in the
+ * gaps between partitions, just to be sure. */
+ r = context_wipe_and_discard(context, from_scratch);
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+ if (p->dropped)
+ continue;
+
+ assert(p->new_size != UINT64_MAX);
+ assert(p->offset != UINT64_MAX);
+ assert(p->partno != UINT64_MAX);
+
+ if (PARTITION_EXISTS(p)) {
+ bool changed = false;
+
+ assert(p->current_partition);
+
+ if (p->new_size != p->current_size) {
+ assert(p->new_size >= p->current_size);
+ assert(p->new_size % 512 == 0);
+
+ r = fdisk_partition_size_explicit(p->current_partition, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable explicit sizing: %m");
+
+ r = fdisk_partition_set_size(p->current_partition, p->new_size / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to grow partition: %m");
+
+ log_info("Growing existing partition %" PRIu64 ".", p->partno);
+ changed = true;
+ }
+
+ if (!sd_id128_equal(p->new_uuid, p->current_uuid)) {
+ char buf[ID128_UUID_STRING_MAX];
+
+ assert(!sd_id128_is_null(p->new_uuid));
+
+ r = fdisk_partition_set_uuid(p->current_partition, id128_to_uuid_string(p->new_uuid, buf));
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition UUID: %m");
+
+ log_info("Initializing UUID of existing partition %" PRIu64 ".", p->partno);
+ changed = true;
+ }
+
+ if (!streq_ptr(p->new_label, p->current_label)) {
+ assert(!isempty(p->new_label));
+
+ r = fdisk_partition_set_name(p->current_partition, p->new_label);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition label: %m");
+
+ log_info("Setting partition label of existing partition %" PRIu64 ".", p->partno);
+ changed = true;
+ }
+
+ if (changed) {
+ assert(!PARTITION_IS_FOREIGN(p)); /* never touch foreign partitions */
+
+ r = fdisk_set_partition(context->fdisk_context, p->partno, p->current_partition);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update partition: %m");
+ }
+ } else {
+ _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
+ _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
+ char ids[ID128_UUID_STRING_MAX];
+
+ assert(!p->new_partition);
+ assert(p->offset % 512 == 0);
+ assert(p->new_size % 512 == 0);
+ assert(!sd_id128_is_null(p->new_uuid));
+ assert(!isempty(p->new_label));
+
+ t = fdisk_new_parttype();
+ if (!t)
+ return log_oom();
+
+ r = fdisk_parttype_set_typestr(t, id128_to_uuid_string(p->type_uuid, ids));
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize partition type: %m");
+
+ q = fdisk_new_partition();
+ if (!q)
+ return log_oom();
+
+ r = fdisk_partition_set_type(q, t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition type: %m");
+
+ r = fdisk_partition_size_explicit(q, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable explicit sizing: %m");
+
+ r = fdisk_partition_set_start(q, p->offset / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to position partition: %m");
+
+ r = fdisk_partition_set_size(q, p->new_size / 512);
+ if (r < 0)
+ return log_error_errno(r, "Failed to grow partition: %m");
+
+ r = fdisk_partition_set_partno(q, p->partno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition number: %m");
+
+ r = fdisk_partition_set_uuid(q, id128_to_uuid_string(p->new_uuid, ids));
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition UUID: %m");
+
+ r = fdisk_partition_set_name(q, p->new_label);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set partition label: %m");
+
+ log_info("Creating new partition %" PRIu64 ".", p->partno);
+
+ r = fdisk_add_partition(context->fdisk_context, q, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add partition: %m");
+
+ assert(!p->new_partition);
+ p->new_partition = TAKE_PTR(q);
+ }
+ }
+
+ log_info("Writing new partition table.");
+
+ r = fdisk_write_disklabel(context->fdisk_context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write partition table: %m");
+
+ capable = device_kernel_partitions_supported(fdisk_get_devfd(context->fdisk_context));
+ if (capable < 0)
+ return capable;
+ if (capable > 0) {
+ log_info("Telling kernel to reread partition table.");
+
+ if (from_scratch)
+ r = fdisk_reread_partition_table(context->fdisk_context);
+ else
+ r = fdisk_reread_changes(context->fdisk_context, original_table);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reread partition table: %m");
+ } else
+ log_notice("Not telling kernel to reread partition table, because selected image does not support kernel partition block devices.");
+
+ log_info("All done.");
+
+ return 0;
+}
+
+static int context_read_seed(Context *context, const char *root) {
+ int r;
+
+ assert(context);
+
+ if (!sd_id128_is_null(context->seed))
+ return 0;
+
+ if (!arg_randomize) {
+ _cleanup_close_ int fd = -1;
+
+ fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, NULL);
+ if (fd == -ENOENT)
+ log_info("No machine ID set, using randomized partition UUIDs.");
+ else if (fd < 0)
+ return log_error_errno(fd, "Failed to determine machine ID of image: %m");
+ else {
+ r = id128_read_fd(fd, ID128_PLAIN, &context->seed);
+ if (r == -ENOMEDIUM)
+ log_info("No machine ID set, using randomized partition UUIDs.");
+ else if (r < 0)
+ return log_error_errno(r, "Failed to parse machine ID of image: %m");
+
+ return 0;
+ }
+ }
+
+ r = sd_id128_randomize(&context->seed);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate randomized seed: %m");
+
+ return 0;
+}
+
+static int context_factory_reset(Context *context, bool from_scratch) {
+ Partition *p;
+ size_t n = 0;
+ int r;
+
+ assert(context);
+
+ if (arg_factory_reset <= 0)
+ return 0;
+
+ if (from_scratch) /* Nothing to reset if we start from scratch */
+ return 0;
+
+ if (arg_dry_run) {
+ log_notice("Refusing to factory reset, please re-run with --dry-run=no.");
+ return 0;
+ }
+
+ log_info("Applying factory reset.");
+
+ LIST_FOREACH(partitions, p, context->partitions) {
+
+ if (!p->factory_reset || !PARTITION_EXISTS(p))
+ continue;
+
+ assert(p->partno != UINT64_MAX);
+
+ log_info("Removing partition %" PRIu64 " for factory reset.", p->partno);
+
+ r = fdisk_delete_partition(context->fdisk_context, p->partno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to remove partition %" PRIu64 ": %m", p->partno);
+
+ n++;
+ }
+
+ if (n == 0) {
+ log_info("Factory reset requested, but no partitions to delete found.");
+ return 0;
+ }
+
+ r = fdisk_write_disklabel(context->fdisk_context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write disk label: %m");
+
+ log_info("Successfully deleted %zu partitions.", n);
+ return 1;
+}
+
+static int context_can_factory_reset(Context *context) {
+ Partition *p;
+
+ assert(context);
+
+ LIST_FOREACH(partitions, p, context->partitions)
+ if (p->factory_reset && PARTITION_EXISTS(p))
+ return true;
+
+ return false;
+}
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-repart", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s [OPTIONS...] [DEVICE]\n"
+ "\n%sGrow and add partitions to partition table.%s\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --dry-run=BOOL Whether to run dry-run operation\n"
+ " --empty=MODE One of refuse, allow, require, force; controls how to\n"
+ " handle empty disks lacking partition table\n"
+ " --discard=BOOL Whether to discard backing blocks for new partitions\n"
+ " --pretty=BOOL Whether to show pretty summary before executing operation\n"
+ " --factory-reset=BOOL Whether to remove data partitions before recreating\n"
+ " them\n"
+ " --can-factory-reset Test whether factory reset is defined\n"
+ " --root=PATH Operate relative to root path\n"
+ " --definitions=DIR Find partitions in specified directory\n"
+ " --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , ansi_highlight(), ansi_normal()
+ , link
+ );
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_DRY_RUN,
+ ARG_EMPTY,
+ ARG_DISCARD,
+ ARG_FACTORY_RESET,
+ ARG_CAN_FACTORY_RESET,
+ ARG_ROOT,
+ ARG_SEED,
+ ARG_PRETTY,
+ ARG_DEFINITIONS,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "dry-run", required_argument, NULL, ARG_DRY_RUN },
+ { "empty", required_argument, NULL, ARG_EMPTY },
+ { "discard", required_argument, NULL, ARG_DISCARD },
+ { "factory-reset", required_argument, NULL, ARG_FACTORY_RESET },
+ { "can-factory-reset", no_argument, NULL, ARG_CAN_FACTORY_RESET },
+ { "root", required_argument, NULL, ARG_ROOT },
+ { "seed", required_argument, NULL, ARG_SEED },
+ { "pretty", required_argument, NULL, ARG_PRETTY },
+ { "definitions", required_argument, NULL, ARG_DEFINITIONS },
+ {}
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_DRY_RUN:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --dry-run= parameter: %s", optarg);
+
+ arg_dry_run = r;
+ break;
+
+ case ARG_EMPTY:
+ if (isempty(optarg) || streq(optarg, "refuse"))
+ arg_empty = EMPTY_REFUSE;
+ else if (streq(optarg, "allow"))
+ arg_empty = EMPTY_ALLOW;
+ else if (streq(optarg, "require"))
+ arg_empty = EMPTY_REQUIRE;
+ else if (streq(optarg, "force"))
+ arg_empty = EMPTY_FORCE;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Failed to parse --empty= parameter: %s", optarg);
+ break;
+
+ case ARG_DISCARD:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --discard= parameter: %s", optarg);
+
+ arg_discard = r;
+ break;
+
+ case ARG_FACTORY_RESET:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --factory-reset= parameter: %s", optarg);
+
+ arg_factory_reset = r;
+ break;
+
+ case ARG_CAN_FACTORY_RESET:
+ arg_can_factory_reset = true;
+ break;
+
+ case ARG_ROOT:
+ r = parse_path_argument_and_warn(optarg, false, &arg_root);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_SEED:
+ if (isempty(optarg)) {
+ arg_seed = SD_ID128_NULL;
+ arg_randomize = false;
+ } else if (streq(optarg, "random"))
+ arg_randomize = true;
+ else {
+ r = sd_id128_from_string(optarg, &arg_seed);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse seed: %s", optarg);
+
+ arg_randomize = false;
+ }
+
+ break;
+
+ case ARG_PRETTY:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --pretty= parameter: %s", optarg);
+
+ arg_pretty = r;
+ break;
+
+ case ARG_DEFINITIONS:
+ r = parse_path_argument_and_warn(optarg, false, &arg_definitions);
+ if (r < 0)
+ return r;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+
+ if (argc - optind > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected at most one argument, the path to the block device.");
+
+ if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Combination of --factory-reset=yes and --empty=force/--empty=require is invalid.");
+
+ if (arg_can_factory_reset)
+ arg_dry_run = true;
+
+ arg_node = argc > optind ? argv[optind] : NULL;
+ return 1;
+}
+
+static int parse_proc_cmdline_factory_reset(void) {
+ bool b;
+ int r;
+
+ if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
+ return 0;
+
+ if (!in_initrd()) /* Never honour kernel command line factory reset request outside of the initrd */
+ return 0;
+
+ r = proc_cmdline_get_bool("systemd.factory_reset", &b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse systemd.factory_reset kernel command line argument: %m");
+ if (r > 0) {
+ arg_factory_reset = b;
+
+ if (b)
+ log_notice("Honouring factory reset requested via kernel command line.");
+ }
+
+ return 0;
+}
+
+static int parse_efi_variable_factory_reset(void) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
+ return 0;
+
+ if (!in_initrd()) /* Never honour EFI variable factory reset request outside of the initrd */
+ return 0;
+
+ r = efi_get_variable_string(EFI_VENDOR_SYSTEMD, "FactoryReset", &value);
+ if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to read EFI variable FactoryReset: %m");
+
+ r = parse_boolean(value);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse EFI variable FactoryReset: %m");
+
+ arg_factory_reset = r;
+ if (r)
+ log_notice("Honouring factory reset requested via EFI variable FactoryReset: %m");
+
+ return 0;
+}
+
+static int remove_efi_variable_factory_reset(void) {
+ int r;
+
+ r = efi_set_variable(EFI_VENDOR_SYSTEMD, "FactoryReset", NULL, 0);
+ if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to remove EFI variable FactoryReset: %m");
+
+ log_info("Successfully unset EFI variable FactoryReset.");
+ return 0;
+}
+
+static int acquire_root_devno(const char *p, int mode, char **ret) {
+ _cleanup_close_ int fd = -1;
+ struct stat st;
+ dev_t devno;
+ int r;
+
+ fd = open(p, mode);
+ if (fd < 0)
+ return -errno;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (S_ISREG(st.st_mode)) {
+ char *s;
+
+ s = strdup(p);
+ if (!s)
+ return log_oom();
+
+ *ret = s;
+ return 0;
+ }
+
+ if (S_ISBLK(st.st_mode))
+ devno = st.st_rdev;
+ else if (S_ISDIR(st.st_mode)) {
+
+ devno = st.st_dev;
+
+ if (major(st.st_dev) == 0) {
+ r = btrfs_get_block_device_fd(fd, &devno);
+ if (r == -ENOTTY) /* not btrfs */
+ return -ENODEV;
+ if (r < 0)
+ return r;
+ }
+
+ } else
+ return -ENOTBLK;
+
+ /* From dm-crypt to backing partition */
+ r = block_get_originating(devno, &devno);
+ if (r < 0)
+ log_debug_errno(r, "Failed to find underlying block device for '%s', ignoring: %m", p);
+
+ /* From partition to whole disk containing it */
+ r = block_get_whole_disk(devno, &devno);
+ if (r < 0)
+ log_debug_errno(r, "Failed to find whole disk block device for '%s', ingoring: %m", p);
+
+ return device_path_make_canonical(S_IFBLK, devno, ret);
+}
+
+static int find_root(char **ret) {
+ const char *t;
+ int r;
+
+ if (arg_node) {
+ r = acquire_root_devno(arg_node, O_RDONLY|O_CLOEXEC, ret);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine backing device of %s: %m", arg_node);
+
+ return 0;
+ }
+
+ /* Let's search for the root device. We look for two cases here: first in /, and then in /usr. The
+ * latter we check for cases where / is a tmpfs and only /usr is an actual persistent block device
+ * (think: volatile setups) */
+
+ FOREACH_STRING(t, "/", "/usr") {
+ _cleanup_free_ char *j = NULL;
+ const char *p;
+
+ if (in_initrd()) {
+ j = path_join("/sysroot", t);
+ if (!j)
+ return log_oom();
+
+ p = j;
+ } else
+ p = t;
+
+ r = acquire_root_devno(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret);
+ if (r < 0) {
+ if (r != -ENODEV)
+ return log_error_errno(r, "Failed to determine backing device of %s: %m", p);
+ } else
+ return 0;
+ }
+
+ return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(context_freep) Context* context = NULL;
+ _cleanup_free_ char *node = NULL;
+ bool from_scratch;
+ int r;
+
+ log_show_color(true);
+ log_parse_environment();
+ log_open();
+
+ if (in_initrd()) {
+ /* Default to operation on /sysroot when invoked in the initrd! */
+ arg_root = strdup("/sysroot");
+ if (!arg_root)
+ return log_oom();
+ }
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ r = parse_proc_cmdline_factory_reset();
+ if (r < 0)
+ return r;
+
+ r = parse_efi_variable_factory_reset();
+ if (r < 0)
+ return r;
+
+ context = context_new(arg_seed);
+ if (!context)
+ return log_oom();
+
+ r = context_read_definitions(context, arg_definitions, arg_root);
+ if (r < 0)
+ return r;
+
+ if (context->n_partitions <= 0 && arg_empty != EMPTY_FORCE)
+ return 0;
+
+ r = find_root(&node);
+ if (r < 0)
+ return r;
+
+ r = context_load_partition_table(context, node);
+ if (r == -EHWPOISON)
+ return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
+ * really an error when called at boot. */
+ if (r < 0)
+ return r;
+ from_scratch = r > 0; /* Starting from scratch */
+
+ if (arg_can_factory_reset) {
+ r = context_can_factory_reset(context);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return EXIT_FAILURE;
+
+ return 0;
+ }
+
+ r = context_factory_reset(context, from_scratch);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* We actually did a factory reset! */
+ r = remove_efi_variable_factory_reset();
+ if (r < 0)
+ return r;
+
+ /* Reload the reduced partition table */
+ context_unload_partition_table(context);
+ r = context_load_partition_table(context, node);
+ if (r < 0)
+ return r;
+ }
+
+#if 0
+ (void) context_dump_partitions(context, node);
+ putchar('\n');
+#endif
+
+ r = context_read_seed(context, arg_root);
+ if (r < 0)
+ return r;
+
+ /* First try to fit new partitions in, dropping by priority until it fits */
+ for (;;) {
+ if (context_allocate_partitions(context))
+ break; /* Success! */
+
+ if (!context_drop_one_priority(context))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+ "Can't fit requested partitions into free space, refusing.");
+ }
+
+ /* Now assign free space according to the weight logic */
+ r = context_grow_partitions(context);
+ if (r < 0)
+ return r;
+
+ /* Now calculate where each partition gets placed */
+ context_place_partitions(context);
+
+ /* Make sure each partition has a unique UUID and unique label */
+ r = context_acquire_partition_uuids_and_labels(context);
+ if (r < 0)
+ return r;
+
+ r = context_write_partition_table(context, node, from_scratch);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
if (r < 0)
return log_debug_errno(r, "Failed to create temporary directory: %m");
- r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP, &m);
+ r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r == -ENOPKG)
sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
else if (r == -EADDRNOTAVAIL)
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
+#include "bus-wait-for-jobs.h"
#include "def.h"
#include "dirent-util.h"
#include "env-file.h"
static bool arg_cat = false;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static const char *arg_host = NULL;
+static bool arg_enable = false;
+static bool arg_now = false;
+static bool arg_no_block = false;
static int determine_image(const char *image, bool permit_non_existing, char **ret) {
int r;
return 0;
}
+static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ UnitFileChange *changes = NULL;
+ size_t n_changes = 0;
+ int r;
+
+ if (!arg_enable)
+ return 0;
+
+ names = strv_new(path, NULL);
+ if (!names)
+ return log_oom();
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ enable ? "EnableUnitFiles" : "DisableUnitFiles");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, names);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "b", arg_runtime);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ if (enable) {
+ r = sd_bus_message_append(m, "b", false);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to %s the portable service %s: %s",
+ enable ? "enable" : "disable", path, bus_error_message(&error, r));
+
+ if (enable) {
+ r = sd_bus_message_skip(reply, "b");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
+ (void) bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
+
+ return 0;
+}
+
+static int maybe_start_stop(sd_bus *bus, const char *path, bool start, BusWaitForJobs *wait) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ char *name = (char *)basename(path), *job = NULL;
+ int r;
+
+ if (!arg_now)
+ return 0;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ start ? "StartUnit" : "StopUnit",
+ &error,
+ &reply,
+ "ss", name, "replace");
+ if (r < 0)
+ return log_error_errno(r, "Failed to %s the portable service %s: %s",
+ start ? "start" : "stop",
+ path,
+ bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "o", &job);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!arg_quiet)
+ log_info("Queued %s to %s portable service %s.", job, start ? "start" : "stop", name);
+
+ if (wait) {
+ r = bus_wait_for_jobs_add(wait, job);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch %s job for %s %s: %m",
+ job, start ? "starting" : "stopping", name);
+ }
+
+ return 0;
+}
+
+static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) {
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
+ int r;
+
+ if (!arg_enable && !arg_now)
+ return 0;
+
+ if (!arg_no_block) {
+ r = bus_wait_for_jobs_new(bus, &wait);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+ }
+
+ r = sd_bus_message_rewind(reply, true);
+ if (r < 0)
+ return r;
+ r = sd_bus_message_enter_container(reply, 'a', "(sss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ for (;;) {
+ char *type, *path, *source;
+
+ r = sd_bus_message_read(reply, "(sss)", &type, &path, &source);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0)
+ break;
+
+ if (STR_IN_SET(type, "symlink", "copy") && ENDSWITH_SET(path, ".service", ".target", ".socket")) {
+ (void) maybe_enable_disable(bus, path, true);
+ (void) maybe_start_stop(bus, path, true, wait);
+ }
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+
+ if (!arg_no_block) {
+ r = bus_wait_for_jobs(wait, arg_quiet, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_strv_free_ char **matches = NULL;
+ int r;
+
+ if (!arg_enable && !arg_now)
+ return 0;
+
+ r = determine_matches(argv[1], argv + 2, true, &matches);
+ if (r < 0)
+ return r;
+
+ r = bus_wait_for_jobs_new(bus, &wait);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.portable1",
+ "/org/freedesktop/portable1",
+ "org.freedesktop.portable1.Manager",
+ "GetImageMetadata");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", image);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, matches);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_skip(reply, "say");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_enter_container(reply, 'a', "{say}");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ for (;;) {
+ const char *name;
+
+ r = sd_bus_message_enter_container(reply, 'e', "say");
+ if (r < 0)
+ return bus_log_parse_error(r);
+ if (r == 0)
+ break;
+
+ r = sd_bus_message_read(reply, "s", &name);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_skip(reply, "ay");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ (void) maybe_start_stop(bus, name, false, wait);
+ (void) maybe_enable_disable(bus, name, false);
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ /* Stopping must always block or the detach will fail if the unit is still running */
+ r = bus_wait_for_jobs(wait, arg_quiet, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int attach_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
(void) maybe_reload(&bus);
print_changes(reply);
+
+ (void) maybe_enable_start(bus, reply);
+
return 0;
}
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+ (void) maybe_stop_disable(bus, image, argv);
+
r = sd_bus_call_method(
bus,
"org.freedesktop.portable1",
" list List available portable service images\n"
" attach NAME|PATH [PREFIX...]\n"
" Attach the specified portable service image\n"
- " detach NAME|PATH Detach the specified portable service image\n"
+ " detach NAME|PATH [PREFIX...]\n"
+ " Detach the specified portable service image\n"
" inspect NAME|PATH [PREFIX...]\n"
" Show details of specified portable service image\n"
" is-attached NAME|PATH Query if portable service image is attached\n"
" --no-reload Don't reload the system and service manager\n"
" --cat When inspecting include unit and os-release file\n"
" contents\n"
+ " --enable Immediately enable/disable the portable service\n"
+ " after attach/detach\n"
+ " --now Immediately start/stop the portable service after\n"
+ " attach/before detach\n"
+ " --no-block Don't block waiting for attach --now to complete\n"
"\nSee the %s for details.\n"
, program_invocation_short_name
, ansi_highlight()
ARG_RUNTIME,
ARG_NO_RELOAD,
ARG_CAT,
+ ARG_ENABLE,
+ ARG_NOW,
+ ARG_NO_BLOCK,
};
static const struct option options[] = {
{ "runtime", no_argument, NULL, ARG_RUNTIME },
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
{ "cat", no_argument, NULL, ARG_CAT },
+ { "enable", no_argument, NULL, ARG_ENABLE },
+ { "now", no_argument, NULL, ARG_NOW },
+ { "no-block", no_argument, NULL, ARG_NO_BLOCK },
{}
};
arg_cat = true;
break;
+ case ARG_ENABLE:
+ arg_enable = true;
+ break;
+
+ case ARG_NOW:
+ arg_now = true;
+ break;
+
+ case ARG_NO_BLOCK:
+ arg_no_block = true;
+ break;
+
case '?':
return -EINVAL;
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_images },
{ "attach", 2, VERB_ANY, 0, attach_image },
- { "detach", 2, 2, 0, detach_image },
+ { "detach", 2, VERB_ANY, 0, detach_image },
{ "inspect", 2, VERB_ANY, 0, inspect_image },
{ "is-attached", 2, 2, 0, is_image_attached },
{ "read-only", 2, 3, 0, read_only_image },
#include "alloc-util.h"
#include "btrfs-util.h"
#include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "fd-util.h"
#include "io-util.h"
#include "machine-image.h"
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-label.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "sd-daemon.h"
#include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "def.h"
#include "main-func.h"
#include "portabled-bus.h"
#include "io-util.h"
#include "log.h"
#include "main-func.h"
+#include "missing_random.h"
#include "missing_syscall.h"
#include "mkdir.h"
#include "parse-util.h"
#include "bus-util.h"
#include "dns-domain.h"
#include "escape.h"
+#include "format-table.h"
#include "format-util.h"
#include "gcrypt-util.h"
#include "main-func.h"
static int show_statistics(int argc, char **argv, void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
sd_bus *bus = userdata;
uint64_t n_current_transactions, n_total_transactions,
cache_size, n_cache_hit, n_cache_miss,
if (r < 0)
return bus_log_parse_error(r);
- printf("%sTransactions%s\n"
- "Current Transactions: %" PRIu64 "\n"
- " Total Transactions: %" PRIu64 "\n",
- ansi_highlight(),
- ansi_normal(),
- n_current_transactions,
- n_total_transactions);
-
reply = sd_bus_message_unref(reply);
r = sd_bus_get_property(bus,
if (r < 0)
return bus_log_parse_error(r);
- printf("\n%sCache%s\n"
- " Current Cache Size: %" PRIu64 "\n"
- " Cache Hits: %" PRIu64 "\n"
- " Cache Misses: %" PRIu64 "\n",
- ansi_highlight(),
- ansi_normal(),
- cache_size,
- n_cache_hit,
- n_cache_miss);
-
reply = sd_bus_message_unref(reply);
r = sd_bus_get_property(bus,
if (r < 0)
return bus_log_parse_error(r);
- printf("\n%sDNSSEC Verdicts%s\n"
- " Secure: %" PRIu64 "\n"
- " Insecure: %" PRIu64 "\n"
- " Bogus: %" PRIu64 "\n"
- " Indeterminate: %" PRIu64 "\n",
- ansi_highlight(),
- ansi_normal(),
- n_dnssec_secure,
- n_dnssec_insecure,
- n_dnssec_bogus,
- n_dnssec_indeterminate);
+ table = table_new("key", "value");
+ if (!table)
+ return log_oom();
+
+ table_set_header(table, false);
+
+ r = table_add_many(table,
+ TABLE_STRING, "Transactions",
+ TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_EMPTY,
+ TABLE_STRING, "Current Transactions:",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_UINT64, n_current_transactions,
+ TABLE_STRING, "Total Transactions:",
+ TABLE_UINT64, n_total_transactions,
+ TABLE_EMPTY, TABLE_EMPTY,
+ TABLE_STRING, "Cache",
+ TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_SET_ALIGN_PERCENT, 0,
+ TABLE_EMPTY,
+ TABLE_STRING, "Current Cache Size:",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_UINT64, cache_size,
+ TABLE_STRING, "Cache Hits:",
+ TABLE_UINT64, n_cache_hit,
+ TABLE_STRING, "Cache Misses:",
+ TABLE_UINT64, n_cache_miss,
+ TABLE_EMPTY, TABLE_EMPTY,
+ TABLE_STRING, "DNSSEC Verdicts",
+ TABLE_SET_COLOR, ansi_highlight(),
+ TABLE_SET_ALIGN_PERCENT, 0,
+ TABLE_EMPTY,
+ TABLE_STRING, "Secure:",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_UINT64, n_dnssec_secure,
+ TABLE_STRING, "Insecure:",
+ TABLE_UINT64, n_dnssec_insecure,
+ TABLE_STRING, "Bogus:",
+ TABLE_UINT64, n_dnssec_bogus,
+ TABLE_STRING, "Indeterminate:",
+ TABLE_UINT64, n_dnssec_indeterminate);
+ if (r < 0)
+ table_log_add_error(r);
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print table: %m");
return 0;
}
strv_free(p->ntas);
}
+static int dump_list(Table *table, const char *prefix, char * const *l) {
+ int r;
+
+ if (strv_isempty(l))
+ return 0;
+
+ r = table_add_many(table,
+ TABLE_STRING, prefix,
+ TABLE_STRV, l);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ return 0;
+}
+
static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
static const struct bus_properties_map property_map[] = {
{ "ScopesMask", "t", NULL, offsetof(struct link_info, scopes_mask) },
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(link_info_clear) struct link_info link_info = {};
+ _cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *p = NULL;
char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
- char **i;
int r;
assert(bus);
printf("%sLink %i (%s)%s\n",
ansi_highlight(), ifindex, name, ansi_normal());
+ table = table_new("key", "value");
+ if (!table)
+ return log_oom();
+
+ table_set_header(table, false);
+
+ r = table_add_many(table,
+ TABLE_STRING, "Current Scopes:",
+ TABLE_SET_ALIGN_PERCENT, 100);
+ if (r < 0)
+ return table_log_add_error(r);
+
if (link_info.scopes_mask == 0)
- printf(" Current Scopes: none\n");
- else
- printf(" Current Scopes:%s%s%s%s%s\n",
- link_info.scopes_mask & SD_RESOLVED_DNS ? " DNS" : "",
- link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
- link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
- link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
- link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
-
- printf("DefaultRoute setting: %s\n"
- " LLMNR setting: %s\n"
- "MulticastDNS setting: %s\n"
- " DNSOverTLS setting: %s\n"
- " DNSSEC setting: %s\n"
- " DNSSEC supported: %s\n",
- yes_no(link_info.default_route),
- strna(link_info.llmnr),
- strna(link_info.mdns),
- strna(link_info.dns_over_tls),
- strna(link_info.dnssec),
- yes_no(link_info.dnssec_supported));
-
- if (link_info.current_dns)
- printf(" Current DNS Server: %s\n", link_info.current_dns);
-
- STRV_FOREACH(i, link_info.dns) {
- printf(" %s %s\n",
- i == link_info.dns ? "DNS Servers:" : " ",
- *i);
- }
-
- STRV_FOREACH(i, link_info.domains) {
- printf(" %s %s\n",
- i == link_info.domains ? "DNS Domain:" : " ",
- *i);
- }
-
- STRV_FOREACH(i, link_info.ntas) {
- printf(" %s %s\n",
- i == link_info.ntas ? "DNSSEC NTA:" : " ",
- *i);
+ r = table_add_cell(table, NULL, TABLE_STRING, "none");
+ else {
+ _cleanup_free_ char *buf = NULL;
+ size_t len;
+
+ if (asprintf(&buf, "%s%s%s%s%s",
+ link_info.scopes_mask & SD_RESOLVED_DNS ? "DNS " : "",
+ link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? "LLMNR/IPv4 " : "",
+ link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? "LLMNR/IPv6 " : "",
+ link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4 " : "",
+ link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6 " : "") < 0)
+ return log_oom();
+
+ len = strlen(buf);
+ assert(len > 0);
+ buf[len - 1] = '\0';
+
+ r = table_add_cell(table, NULL, TABLE_STRING, buf);
}
+ if (r < 0)
+ return table_log_add_error(r);
+
+ r = table_add_many(table,
+ TABLE_STRING, "DefaultRoute setting:",
+ TABLE_BOOLEAN, link_info.default_route,
+ TABLE_STRING, "LLMNR setting:",
+ TABLE_STRING, strna(link_info.llmnr),
+ TABLE_STRING, "MulticastDNS setting:",
+ TABLE_STRING, strna(link_info.mdns),
+ TABLE_STRING, "DNSOverTLS setting:",
+ TABLE_STRING, strna(link_info.dns_over_tls),
+ TABLE_STRING, "DNSSEC setting:",
+ TABLE_STRING, strna(link_info.dnssec),
+ TABLE_STRING, "DNSSEC supported:",
+ TABLE_BOOLEAN, link_info.dnssec_supported);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ if (link_info.current_dns) {
+ r = table_add_many(table,
+ TABLE_STRING, "Current DNS Server:",
+ TABLE_STRING, link_info.current_dns);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ r = dump_list(table, "DNS Servers:", link_info.dns);
+ if (r < 0)
+ return r;
+
+ r = dump_list(table, "DNS Domain:", link_info.domains);
+ if (r < 0)
+ return r;
+
+ r = dump_list(table, "DNSSEC NTA:", link_info.ntas);
+ if (r < 0)
+ return r;
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print table: %m");
if (empty_line)
*empty_line = true;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(global_info_clear) struct global_info global_info = {};
- char **i;
+ _cleanup_(table_unrefp) Table *table = NULL;
int r;
assert(bus);
printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
- printf(" LLMNR setting: %s\n"
- "MulticastDNS setting: %s\n"
- " DNSOverTLS setting: %s\n"
- " DNSSEC setting: %s\n"
- " DNSSEC supported: %s\n",
- strna(global_info.llmnr),
- strna(global_info.mdns),
- strna(global_info.dns_over_tls),
- strna(global_info.dnssec),
- yes_no(global_info.dnssec_supported));
-
- if (global_info.current_dns)
- printf(" Current DNS Server: %s\n", global_info.current_dns);
+ table = table_new("key", "value");
+ if (!table)
+ return log_oom();
- STRV_FOREACH(i, global_info.dns) {
- printf(" %s %s\n",
- i == global_info.dns ? "DNS Servers:" : " ",
- *i);
+ table_set_header(table, false);
+
+ r = table_add_many(table,
+ TABLE_STRING, "LLMNR setting:",
+ TABLE_SET_ALIGN_PERCENT, 100,
+ TABLE_STRING, strna(global_info.llmnr),
+ TABLE_STRING, "MulticastDNS setting:",
+ TABLE_STRING, strna(global_info.mdns),
+ TABLE_STRING, "DNSOverTLS setting:",
+ TABLE_STRING, strna(global_info.dns_over_tls),
+ TABLE_STRING, "DNSSEC setting:",
+ TABLE_STRING, strna(global_info.dnssec),
+ TABLE_STRING, "DNSSEC supported:",
+ TABLE_BOOLEAN, global_info.dnssec_supported);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ if (global_info.current_dns) {
+ r = table_add_many(table,
+ TABLE_STRING, "Current DNS Server:",
+ TABLE_STRING, global_info.current_dns);
+ if (r < 0)
+ return table_log_add_error(r);
}
- STRV_FOREACH(i, global_info.fallback_dns) {
- printf("%s %s\n",
- i == global_info.fallback_dns ? "Fallback DNS Servers:" : " ",
- *i);
- }
+ r = dump_list(table, "DNS Servers:", global_info.dns);
+ if (r < 0)
+ return r;
- STRV_FOREACH(i, global_info.domains) {
- printf(" %s %s\n",
- i == global_info.domains ? "DNS Domain:" : " ",
- *i);
- }
+ r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns);
+ if (r < 0)
+ return r;
+
+ r = dump_list(table, "DNS Domain:", global_info.domains);
+ if (r < 0)
+ return r;
strv_sort(global_info.ntas);
- STRV_FOREACH(i, global_info.ntas) {
- printf(" %s %s\n",
- i == global_info.ntas ? "DNSSEC NTA:" : " ",
- *i);
- }
+ r = dump_list(table, "DNSSEC NTA:", global_info.ntas);
+ if (r < 0)
+ return r;
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print table: %m");
*empty_line = true;
#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "dns-domain.h"
#include "memory-util.h"
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
if (r < 0) {
- log_debug_errno(r, "Failed to lookup key: %m");
+ log_debug_errno(r, "Failed to look up key: %m");
return;
}
if (r == 0)
static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
-/* The first DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
-static const uint8_t root_digest1[] =
- { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
- 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
-
/* The second DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved February 2017 */
static const uint8_t root_digest2[] =
{ 0xE0, 0x6D, 0x44, 0xB8, 0x0B, 0x8F, 0x1D, 0x39, 0xA9, 0x5C, 0x0B, 0x0D, 0x7C, 0x65, 0xD0, 0x84,
if (!answer)
return -ENOMEM;
- /* Add the two RRs from https://data.iana.org/root-anchors/root-anchors.xml */
- r = add_root_ksk(answer, key, 19036, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest1, sizeof(root_digest1));
- if (r < 0)
- return r;
-
+ /* Add the currently valid RRs from https://data.iana.org/root-anchors/root-anchors.xml */
r = add_root_ksk(answer, key, 20326, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest2, sizeof(root_digest2));
if (r < 0)
return r;
+/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "missing_capability.h"
-#include "resolved-dnssd.h"
#include "resolved-dnssd-bus.h"
+#include "resolved-dnssd.h"
#include "resolved-link.h"
#include "strv.h"
#include "user-util.h"
}
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
- stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
- if (server->family == AF_INET) {
- stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
- stream->dnstls_data.validation.size = 4;
- } else {
- stream->dnstls_data.validation.data = server->address.in6.s6_addr;
- stream->dnstls_data.validation.size = 16;
+ if (server->server_name)
+ gnutls_session_set_verify_cert(gs, server->server_name, 0);
+ else {
+ stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
+ if (server->family == AF_INET) {
+ stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
+ stream->dnstls_data.validation.size = 4;
+ } else {
+ stream->dnstls_data.validation.data = server->address.in6.s6_addr;
+ stream->dnstls_data.validation.size = 16;
+ }
+ gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
}
- gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
}
if (server->server_name) {
#include <openssl/bio.h>
#include <openssl/err.h>
+#include <openssl/x509v3.h>
#include "io-util.h"
#include "resolved-dns-stream.h"
return -ENOMEM;
SSL_set_connect_state(s);
- SSL_set_session(s, server->dnstls_data.session);
+ r = SSL_set_session(s, server->dnstls_data.session);
+ if (r == 0)
+ return -EIO;
SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
X509_VERIFY_PARAM *v;
- const unsigned char *ip;
SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
v = SSL_get0_param(s);
- ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
- if (!X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)))
- return -ECONNREFUSED;
+ if (server->server_name) {
+ X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+ if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0)
+ return -ECONNREFUSED;
+ } else {
+ const unsigned char *ip;
+ ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
+ if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
+ return -ECONNREFUSED;
+ }
}
if (server->server_name) {
char errbuf[256];
ERR_error_string_n(error, errbuf, sizeof(errbuf));
- log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
- return -ECONNREFUSED;
+ return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
+ "Failed to invoke SSL_do_handshake: %s", errbuf);
}
}
int dnstls_manager_init(Manager *manager) {
int r;
+
assert(manager);
ERR_load_crypto_strings();
SSL_load_error_strings();
- manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
+ manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
if (!manager->dnstls_data.ctx)
return -ENOMEM;
- SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
- SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+ r = SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
+ if (r == 0)
+ return -EIO;
+
+ (void) SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+
r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx);
- if (r < 0)
- log_warning("Failed to load system trust store: %s", ERR_error_string(ERR_get_error(), NULL));
+ if (r == 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to load system trust store: %s",
+ ERR_error_string(ERR_get_error(), NULL));
return 0;
}
#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-polkit.h"
#include "bus-util.h"
#include "parse-util.h"
#include "resolve-util.h"
#include "af-list.h"
#include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "dirent-util.h"
#include "dns-domain.h"
#include "fd-util.h"
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
if (r < 0)
- return log_debug_errno(r, "Failed to lookup key: %m");
+ return log_debug_errno(r, "Failed to look up key: %m");
if (tentative && DNS_PACKET_NSCOUNT(p) > 0) {
/*
return r;
}
+#define NO_ECHO "(no echo) "
+#define PRESS_TAB "(press TAB for no echo) "
+#define SKIPPED "(skipped)"
+
int ask_password_tty(
int ttyfd,
const char *message,
_POLL_MAX,
};
- bool reset_tty = false, dirty = false, use_color = false;
+ bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
_cleanup_close_ int cttyfd = -1, notify = -1;
struct termios old_termios, new_termios;
char passphrase[LINE_MAX + 1] = {}, *x;
(void) loop_write(ttyfd, message, strlen(message), false);
(void) loop_write(ttyfd, " ", 1, false);
+ if (!(flags & ASK_PASSWORD_SILENT)) {
+ if (use_color)
+ (void) loop_write(ttyfd, ANSI_GREY, STRLEN(ANSI_GREY), false);
+ (void) loop_write(ttyfd, PRESS_TAB, strlen(PRESS_TAB), false);
+ press_tab_visible = true;
+ }
+
if (use_color)
(void) loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL), false);
}
+ if (press_tab_visible) {
+ assert(ttyfd >= 0);
+ backspace_chars(ttyfd, strlen(PRESS_TAB));
+ press_tab_visible = false;
+ }
+
/* We treat EOF, newline and NUL byte all as valid end markers */
if (n == 0 || c == '\n' || c == 0)
break;
if (c == 4) { /* C-d also known as EOT */
if (ttyfd >= 0)
- (void) loop_write(ttyfd, "(skipped)", 9, false);
+ (void) loop_write(ttyfd, SKIPPED, strlen(SKIPPED), false);
goto skipped;
}
* first key (and only as first key), or ... */
if (ttyfd >= 0)
- (void) loop_write(ttyfd, "(no echo) ", 10, false);
+ (void) loop_write(ttyfd, NO_ECHO, strlen(NO_ECHO), false);
} else if (ttyfd >= 0)
(void) loop_write(ttyfd, "\a", 1, false);
/* ... or by pressing TAB at any time. */
if (ttyfd >= 0)
- (void) loop_write(ttyfd, "(no echo) ", 10, false);
+ (void) loop_write(ttyfd, NO_ECHO, strlen(NO_ECHO), false);
} else if (p >= sizeof(passphrase)-1) {
goto finish;
skipped:
- if (keyname)
- (void) add_to_keyring_and_log(keyname, flags, l);
-
- *ret = TAKE_PTR(l);
- r = 0;
+ if (strv_isempty(l))
+ r = log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Password query was cancelled.");
+ else {
+ if (keyname)
+ (void) add_to_keyring_and_log(keyname, flags, l);
+
+ *ret = TAKE_PTR(l);
+ r = 0;
+ }
finish:
if (ttyfd >= 0 && reset_tty) {
static int create_socket(char **ret) {
_cleanup_free_ char *path = NULL;
- union sockaddr_union sa = {};
+ union sockaddr_union sa;
+ socklen_t sa_len;
_cleanup_close_ int fd = -1;
- int salen, r;
+ int r;
assert(ret);
if (asprintf(&path, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()) < 0)
return -ENOMEM;
- salen = sockaddr_un_set_path(&sa.un, path);
- if (salen < 0)
- return salen;
+ r = sockaddr_un_set_path(&sa.un, path);
+ if (r < 0)
+ return r;
+ sa_len = r;
- RUN_WITH_UMASK(0177) {
- if (bind(fd, &sa.sa, salen) < 0)
+ RUN_WITH_UMASK(0177)
+ if (bind(fd, &sa.sa, sa_len) < 0)
return -errno;
- }
r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
free(config->auto_entries);
free(config->auto_firmware);
free(config->console_mode);
+ free(config->random_seed_mode);
free(config->entry_oneshot);
free(config->entry_default);
r = free_and_strdup(&config->auto_firmware, p);
else if (streq(field, "console-mode"))
r = free_and_strdup(&config->console_mode, p);
+ else if (streq(field, "random-seed-mode"))
+ r = free_and_strdup(&config->random_seed_mode, p);
else {
log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
continue;
char *auto_entries;
char *auto_firmware;
char *console_mode;
+ char *random_seed_mode;
char *entry_oneshot;
char *entry_default;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-polkit.h"
+#include "strv.h"
+#include "user-util.h"
+
+static int check_good_user(sd_bus_message *m, uid_t good_user) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ uid_t sender_uid;
+ int r;
+
+ assert(m);
+
+ if (good_user == UID_INVALID)
+ return 0;
+
+ r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
+ if (r < 0)
+ return r;
+
+ /* Don't trust augmented credentials for authorization */
+ assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
+
+ r = sd_bus_creds_get_euid(creds, &sender_uid);
+ if (r < 0)
+ return r;
+
+ return sender_uid == good_user;
+}
+
+#if ENABLE_POLKIT
+static int bus_message_append_strv_key_value(
+ sd_bus_message *m,
+ const char **l) {
+
+ const char **k, **v;
+ int r;
+
+ assert(m);
+
+ r = sd_bus_message_open_container(m, 'a', "{ss}");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(k, v, l) {
+ r = sd_bus_message_append(m, "{ss}", *k, *v);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ return r;
+}
+#endif
+
+int bus_test_polkit(
+ sd_bus_message *call,
+ int capability,
+ const char *action,
+ const char **details,
+ uid_t good_user,
+ bool *_challenge,
+ sd_bus_error *ret_error) {
+
+ int r;
+
+ assert(call);
+ assert(action);
+
+ /* Tests non-interactively! */
+
+ r = check_good_user(call, good_user);
+ if (r != 0)
+ return r;
+
+ r = sd_bus_query_sender_privilege(call, capability);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ return 1;
+#if ENABLE_POLKIT
+ else {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int authorized = false, challenge = false;
+ const char *sender;
+
+ sender = sd_bus_message_get_sender(call);
+ if (!sender)
+ return -EBADMSG;
+
+ r = sd_bus_message_new_method_call(
+ call->bus,
+ &request,
+ "org.freedesktop.PolicyKit1",
+ "/org/freedesktop/PolicyKit1/Authority",
+ "org.freedesktop.PolicyKit1.Authority",
+ "CheckAuthorization");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(
+ request,
+ "(sa{sv})s",
+ "system-bus-name", 1, "name", "s", sender,
+ action);
+ if (r < 0)
+ return r;
+
+ r = bus_message_append_strv_key_value(request, details);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(request, "us", 0, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_call(call->bus, request, 0, ret_error, &reply);
+ if (r < 0) {
+ /* Treat no PK available as access denied */
+ if (sd_bus_error_has_name(ret_error, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
+ sd_bus_error_free(ret_error);
+ return -EACCES;
+ }
+
+ return r;
+ }
+
+ r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
+ if (r < 0)
+ return r;
+
+ if (authorized)
+ return 1;
+
+ if (_challenge) {
+ *_challenge = challenge;
+ return 0;
+ }
+ }
+#endif
+
+ return -EACCES;
+}
+
+#if ENABLE_POLKIT
+
+typedef struct AsyncPolkitQuery {
+ char *action;
+ char **details;
+
+ sd_bus_message *request, *reply;
+ sd_bus_slot *slot;
+
+ Hashmap *registry;
+ sd_event_source *defer_event_source;
+} AsyncPolkitQuery;
+
+static void async_polkit_query_free(AsyncPolkitQuery *q) {
+ if (!q)
+ return;
+
+ sd_bus_slot_unref(q->slot);
+
+ if (q->registry && q->request)
+ hashmap_remove(q->registry, q->request);
+
+ sd_bus_message_unref(q->request);
+ sd_bus_message_unref(q->reply);
+
+ free(q->action);
+ strv_free(q->details);
+
+ sd_event_source_disable_unref(q->defer_event_source);
+ free(q);
+}
+
+static int async_polkit_defer(sd_event_source *s, void *userdata) {
+ AsyncPolkitQuery *q = userdata;
+
+ assert(s);
+
+ /* This is called as idle event source after we processed the async polkit reply, hopefully after the
+ * method call we re-enqueued has been properly processed. */
+
+ async_polkit_query_free(q);
+ return 0;
+}
+
+static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+ AsyncPolkitQuery *q = userdata;
+ int r;
+
+ assert(reply);
+ assert(q);
+
+ assert(q->slot);
+ q->slot = sd_bus_slot_unref(q->slot);
+
+ assert(!q->reply);
+ q->reply = sd_bus_message_ref(reply);
+
+ /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the
+ * whole message processing again, and thus re-validating and re-retrieving the "userdata" field
+ * again.
+ *
+ * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
+ * i.e. after the second time the message is processed is complete. */
+
+ assert(!q->defer_event_source);
+ r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ goto fail;
+
+ r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_message_rewind(q->request, true);
+ if (r < 0)
+ goto fail;
+
+ r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
+ if (r < 0)
+ goto fail;
+
+ return 1;
+
+fail:
+ log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
+ (void) sd_bus_reply_method_errno(q->request, r, NULL);
+ async_polkit_query_free(q);
+ return r;
+}
+
+#endif
+
+int bus_verify_polkit_async(
+ sd_bus_message *call,
+ int capability,
+ const char *action,
+ const char **details,
+ bool interactive,
+ uid_t good_user,
+ Hashmap **registry,
+ sd_bus_error *ret_error) {
+
+#if ENABLE_POLKIT
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
+ AsyncPolkitQuery *q;
+ int c;
+#endif
+ const char *sender;
+ int r;
+
+ assert(call);
+ assert(action);
+ assert(registry);
+
+ r = check_good_user(call, good_user);
+ if (r != 0)
+ return r;
+
+#if ENABLE_POLKIT
+ q = hashmap_get(*registry, call);
+ if (q) {
+ int authorized, challenge;
+
+ /* This is the second invocation of this function, and there's already a response from
+ * polkit, let's process it */
+ assert(q->reply);
+
+ /* If the operation we want to authenticate changed between the first and the second time,
+ * let's not use this authentication, it might be out of date as the object and context we
+ * operate on might have changed. */
+ if (!streq(q->action, action) ||
+ !strv_equal(q->details, (char**) details))
+ return -ESTALE;
+
+ if (sd_bus_message_is_method_error(q->reply, NULL)) {
+ const sd_bus_error *e;
+
+ e = sd_bus_message_get_error(q->reply);
+
+ /* Treat no PK available as access denied */
+ if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
+ sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER))
+ return -EACCES;
+
+ /* Copy error from polkit reply */
+ sd_bus_error_copy(ret_error, e);
+ return -sd_bus_error_get_errno(e);
+ }
+
+ r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
+ if (r >= 0)
+ r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
+ if (r < 0)
+ return r;
+
+ if (authorized)
+ return 1;
+
+ if (challenge)
+ return sd_bus_error_set(ret_error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
+
+ return -EACCES;
+ }
+#endif
+
+ r = sd_bus_query_sender_privilege(call, capability);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ return 1;
+
+ sender = sd_bus_message_get_sender(call);
+ if (!sender)
+ return -EBADMSG;
+
+#if ENABLE_POLKIT
+ c = sd_bus_message_get_allow_interactive_authorization(call);
+ if (c < 0)
+ return c;
+ if (c > 0)
+ interactive = true;
+
+ r = hashmap_ensure_allocated(registry, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_call(
+ call->bus,
+ &pk,
+ "org.freedesktop.PolicyKit1",
+ "/org/freedesktop/PolicyKit1/Authority",
+ "org.freedesktop.PolicyKit1.Authority",
+ "CheckAuthorization");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(
+ pk,
+ "(sa{sv})s",
+ "system-bus-name", 1, "name", "s", sender,
+ action);
+ if (r < 0)
+ return r;
+
+ r = bus_message_append_strv_key_value(pk, details);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(pk, "us", interactive, NULL);
+ if (r < 0)
+ return r;
+
+ q = new(AsyncPolkitQuery, 1);
+ if (!q)
+ return -ENOMEM;
+
+ *q = (AsyncPolkitQuery) {
+ .request = sd_bus_message_ref(call),
+ };
+
+ q->action = strdup(action);
+ if (!q->action) {
+ async_polkit_query_free(q);
+ return -ENOMEM;
+ }
+
+ q->details = strv_copy((char**) details);
+ if (!q->details) {
+ async_polkit_query_free(q);
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(*registry, call, q);
+ if (r < 0) {
+ async_polkit_query_free(q);
+ return r;
+ }
+
+ q->registry = *registry;
+
+ r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
+ if (r < 0) {
+ async_polkit_query_free(q);
+ return r;
+ }
+
+ return 0;
+#endif
+
+ return -EACCES;
+}
+
+void bus_verify_polkit_async_registry_free(Hashmap *registry) {
+#if ENABLE_POLKIT
+ hashmap_free_with_destructor(registry, async_polkit_query_free);
+#endif
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "hashmap.h"
+
+int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+
+int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
+void bus_verify_polkit_async_registry_free(Hashmap *registry);
"RuntimeDirectoryPreserve",
"Personality",
"KeyringMode",
- "NetworkNamespacePath"))
+ "NetworkNamespacePath",
+ "LogNamespace"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "IgnoreSIGPIPE",
"ProtectKernelTunables",
"ProtectKernelModules",
"ProtectKernelLogs",
+ "ProtectClock",
"ProtectControlGroups",
"MountAPIVFS",
"CPUSchedulingResetOnFork",
#include <sys/socket.h>
#include <unistd.h>
-#include "sd-bus-protocol.h"
#include "sd-bus.h"
#include "sd-daemon.h"
#include "sd-event.h"
#include "bus-util.h"
#include "cap-list.h"
#include "cgroup-util.h"
-#include "def.h"
-#include "escape.h"
-#include "fd-util.h"
#include "mountpoint-util.h"
#include "nsflags.h"
#include "parse-util.h"
#include "path-util.h"
-#include "proc-cmdline.h"
#include "rlimit-util.h"
+#include "socket-util.h"
#include "stdio-util.h"
#include "strv.h"
#include "user-util.h"
return has_owner;
}
-static int check_good_user(sd_bus_message *m, uid_t good_user) {
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- uid_t sender_uid;
- int r;
-
- assert(m);
-
- if (good_user == UID_INVALID)
- return 0;
-
- r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
- if (r < 0)
- return r;
-
- /* Don't trust augmented credentials for authorization */
- assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
-
- r = sd_bus_creds_get_euid(creds, &sender_uid);
- if (r < 0)
- return r;
-
- return sender_uid == good_user;
-}
-
-int bus_test_polkit(
- sd_bus_message *call,
- int capability,
- const char *action,
- const char **details,
- uid_t good_user,
- bool *_challenge,
- sd_bus_error *e) {
-
- int r;
-
- assert(call);
- assert(action);
-
- /* Tests non-interactively! */
-
- r = check_good_user(call, good_user);
- if (r != 0)
- return r;
-
- r = sd_bus_query_sender_privilege(call, capability);
- if (r < 0)
- return r;
- else if (r > 0)
- return 1;
-#if ENABLE_POLKIT
- else {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- int authorized = false, challenge = false;
- const char *sender, **k, **v;
-
- sender = sd_bus_message_get_sender(call);
- if (!sender)
- return -EBADMSG;
-
- r = sd_bus_message_new_method_call(
- call->bus,
- &request,
- "org.freedesktop.PolicyKit1",
- "/org/freedesktop/PolicyKit1/Authority",
- "org.freedesktop.PolicyKit1.Authority",
- "CheckAuthorization");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(
- request,
- "(sa{sv})s",
- "system-bus-name", 1, "name", "s", sender,
- action);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(request, 'a', "{ss}");
- if (r < 0)
- return r;
-
- STRV_FOREACH_PAIR(k, v, details) {
- r = sd_bus_message_append(request, "{ss}", *k, *v);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_close_container(request);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(request, "us", 0, NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_call(call->bus, request, 0, e, &reply);
- if (r < 0) {
- /* Treat no PK available as access denied */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
- sd_bus_error_free(e);
- return -EACCES;
- }
-
- return r;
- }
-
- r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
- if (r < 0)
- return r;
-
- if (authorized)
- return 1;
-
- if (_challenge) {
- *_challenge = challenge;
- return 0;
- }
- }
-#endif
-
- return -EACCES;
-}
-
-#if ENABLE_POLKIT
-
-typedef struct AsyncPolkitQuery {
- sd_bus_message *request, *reply;
- sd_bus_message_handler_t callback;
- void *userdata;
- sd_bus_slot *slot;
- Hashmap *registry;
-} AsyncPolkitQuery;
-
-static void async_polkit_query_free(AsyncPolkitQuery *q) {
-
- if (!q)
- return;
-
- sd_bus_slot_unref(q->slot);
-
- if (q->registry && q->request)
- hashmap_remove(q->registry, q->request);
-
- sd_bus_message_unref(q->request);
- sd_bus_message_unref(q->reply);
-
- free(q);
-}
-
-static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
- _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- AsyncPolkitQuery *q = userdata;
- int r;
-
- assert(reply);
- assert(q);
-
- q->slot = sd_bus_slot_unref(q->slot);
- q->reply = sd_bus_message_ref(reply);
-
- r = sd_bus_message_rewind(q->request, true);
- if (r < 0) {
- r = sd_bus_reply_method_errno(q->request, r, NULL);
- goto finish;
- }
-
- r = q->callback(q->request, q->userdata, &error_buffer);
- r = bus_maybe_reply_error(q->request, r, &error_buffer);
-
-finish:
- async_polkit_query_free(q);
-
- return r;
-}
-
-#endif
-
-int bus_verify_polkit_async(
- sd_bus_message *call,
- int capability,
- const char *action,
- const char **details,
- bool interactive,
- uid_t good_user,
- Hashmap **registry,
- sd_bus_error *error) {
-
-#if ENABLE_POLKIT
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
- AsyncPolkitQuery *q;
- const char *sender, **k, **v;
- sd_bus_message_handler_t callback;
- void *userdata;
- int c;
-#endif
- int r;
-
- assert(call);
- assert(action);
- assert(registry);
-
- r = check_good_user(call, good_user);
- if (r != 0)
- return r;
-
-#if ENABLE_POLKIT
- q = hashmap_get(*registry, call);
- if (q) {
- int authorized, challenge;
-
- /* This is the second invocation of this function, and
- * there's already a response from polkit, let's
- * process it */
- assert(q->reply);
-
- if (sd_bus_message_is_method_error(q->reply, NULL)) {
- const sd_bus_error *e;
-
- e = sd_bus_message_get_error(q->reply);
-
- /* Treat no PK available as access denied */
- if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
- sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER))
- return -EACCES;
-
- /* Copy error from polkit reply */
- sd_bus_error_copy(error, e);
- return -sd_bus_error_get_errno(e);
- }
-
- r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
- if (r >= 0)
- r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
- if (r < 0)
- return r;
-
- if (authorized)
- return 1;
-
- if (challenge)
- return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
-
- return -EACCES;
- }
-#endif
-
- r = sd_bus_query_sender_privilege(call, capability);
- if (r < 0)
- return r;
- else if (r > 0)
- return 1;
-
-#if ENABLE_POLKIT
- if (sd_bus_get_current_message(call->bus) != call)
- return -EINVAL;
-
- callback = sd_bus_get_current_handler(call->bus);
- if (!callback)
- return -EINVAL;
-
- userdata = sd_bus_get_current_userdata(call->bus);
-
- sender = sd_bus_message_get_sender(call);
- if (!sender)
- return -EBADMSG;
-
- c = sd_bus_message_get_allow_interactive_authorization(call);
- if (c < 0)
- return c;
- if (c > 0)
- interactive = true;
-
- r = hashmap_ensure_allocated(registry, NULL);
- if (r < 0)
- return r;
-
- r = sd_bus_message_new_method_call(
- call->bus,
- &pk,
- "org.freedesktop.PolicyKit1",
- "/org/freedesktop/PolicyKit1/Authority",
- "org.freedesktop.PolicyKit1.Authority",
- "CheckAuthorization");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(
- pk,
- "(sa{sv})s",
- "system-bus-name", 1, "name", "s", sender,
- action);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(pk, 'a', "{ss}");
- if (r < 0)
- return r;
-
- STRV_FOREACH_PAIR(k, v, details) {
- r = sd_bus_message_append(pk, "{ss}", *k, *v);
- if (r < 0)
- return r;
- }
-
- r = sd_bus_message_close_container(pk);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(pk, "us", interactive, NULL);
- if (r < 0)
- return r;
-
- q = new0(AsyncPolkitQuery, 1);
- if (!q)
- return -ENOMEM;
-
- q->request = sd_bus_message_ref(call);
- q->callback = callback;
- q->userdata = userdata;
-
- r = hashmap_put(*registry, call, q);
- if (r < 0) {
- async_polkit_query_free(q);
- return r;
- }
-
- q->registry = *registry;
-
- r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
- if (r < 0) {
- async_polkit_query_free(q);
- return r;
- }
-
- return 0;
-#endif
-
- return -EACCES;
-}
-
-void bus_verify_polkit_async_registry_free(Hashmap *registry) {
-#if ENABLE_POLKIT
- hashmap_free_with_destructor(registry, async_polkit_query_free);
-#endif
-}
-
int bus_check_peercred(sd_bus *c) {
struct ucred ucred;
int fd, r;
if (r < 0)
return r;
- result = s;
+ result = strempty(s);
}
bus_print_property_value(name, expected_value, value, result);
#include "sd-bus.h"
#include "sd-event.h"
-#include "hashmap.h"
#include "macro.h"
+#include "set.h"
#include "string-util.h"
#include "time-util.h"
int bus_check_peercred(sd_bus *c);
-int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
-
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
-void bus_verify_polkit_async_registry_free(Hashmap *registry);
-
int bus_connect_system_systemd(sd_bus **_bus);
int bus_connect_user_systemd(sd_bus **_bus);
return r;
}
+ line++;
+
l = skip_leading_chars(buf, WHITESPACE);
if (*l != '\0' && strchr(COMMENTS, *l))
continue;
r = parse_line(unit,
filename,
- ++line,
+ line,
sections,
lookup,
table,
DEFINE_PARSER(uint8, uint8_t, safe_atou8);
DEFINE_PARSER(uint16, uint16_t, safe_atou16);
DEFINE_PARSER(uint32, uint32_t, safe_atou32);
+DEFINE_PARSER(int32, int32_t, safe_atoi32);
DEFINE_PARSER(uint64, uint64_t, safe_atou64);
DEFINE_PARSER(unsigned, unsigned, safe_atou);
DEFINE_PARSER(double, double, safe_atod);
return 0;
}
-int config_parse_si_size(
+int config_parse_si_uint64(
const char* unit,
const char *filename,
unsigned line,
void *data,
void *userdata) {
- size_t *sz = data;
- uint64_t v;
+ uint64_t *sz = data;
int r;
assert(filename);
assert(rvalue);
assert(data);
- r = parse_size(rvalue, 1000, &v);
- if (r >= 0 && (uint64_t) (size_t) v != v)
- r = -ERANGE;
+ r = parse_size(rvalue, 1000, sz);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
return 0;
}
- *sz = (size_t) v;
return 0;
}
CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
+CONFIG_PARSER_PROTOTYPE(config_parse_int32);
CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
CONFIG_PARSER_PROTOTYPE(config_parse_double);
CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
-CONFIG_PARSER_PROTOTYPE(config_parse_si_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
CONFIG_PARSER_PROTOTYPE(config_parse_bool);
CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "fsck-util.h"
#include "gpt.h"
#include "hexdecoct.h"
#include "hostname-util.h"
N_DEVICE_NODE_LIST_ATTEMPTS);
}
+static void check_partition_flags(
+ const char *node,
+ unsigned long long pflags,
+ unsigned long long supported) {
+
+ assert(node);
+
+ /* Mask away all flags supported by this partition's type and the three flags the UEFI spec defines generically */
+ pflags &= ~(supported | GPT_FLAG_REQUIRED_PARTITION | GPT_FLAG_NO_BLOCK_IO_PROTOCOL | GPT_FLAG_LEGACY_BIOS_BOOTABLE);
+
+ if (pflags == 0)
+ return;
+
+ /* If there are other bits set, then log about it, to make things discoverable */
+ for (unsigned i = 0; i < sizeof(pflags) * 8; i++) {
+ unsigned long long bit = 1ULL << i;
+ if (!FLAGS_SET(pflags, bit))
+ continue;
+
+ log_debug("Unexpected partition flag %llu set on %s!", bit, node);
+ }
+}
+
#endif
int dissect_image(
m->encrypted = streq_ptr(fstype, "crypto_LUKS");
- r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
- if (r < 0)
- return r;
-
+ if (!streq(usage, "filesystem")) {
+ r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
+ if (r < 0)
+ return r;
+ }
*ret = TAKE_PTR(m);
return 0;
if (sd_id128_equal(type_id, GPT_HOME)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_SRV)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
} else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
#ifdef GPT_ROOT_NATIVE
else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
#ifdef GPT_ROOT_SECONDARY
else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
#endif
else if (sd_id128_equal(type_id, GPT_SWAP)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
fstype = "swap";
} else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
if (pflags & GPT_FLAG_NO_AUTO)
continue;
if (!generic_node)
return -ENOMEM;
}
+
+ } else if (sd_id128_equal(type_id, GPT_TMP)) {
+
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
+ designator = PARTITION_TMP;
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
+
+ } else if (sd_id128_equal(type_id, GPT_VAR)) {
+
+ check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+ if (pflags & GPT_FLAG_NO_AUTO)
+ continue;
+
+ if (!FLAGS_SET(flags, DISSECT_IMAGE_RELAX_VAR_CHECK)) {
+ sd_id128_t var_uuid;
+
+ /* For /var we insist that the uuid of the partition matches the
+ * HMAC-SHA256 of the /var GPT partition type uuid, keyed by machine
+ * ID. Why? Unlike the other partitions /var is inherently
+ * installation specific, hence we need to be careful not to mount it
+ * in the wrong installation. By hashing the partition UUID from
+ * /etc/machine-id we can securely bind the partition to the
+ * installation. */
+
+ r = sd_id128_get_machine_app_specific(GPT_VAR, &var_uuid);
+ if (r < 0)
+ return r;
+
+ if (!sd_id128_equal(var_uuid, id)) {
+ log_debug("Found a /var/ partition, but its UUID didn't match our expectations, ignoring.");
+ continue;
+ }
+ }
+
+ designator = PARTITION_VAR;
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
}
if (designator != _PARTITION_DESIGNATOR_INVALID) {
return true;
}
+static int run_fsck(const char *node, const char *fstype) {
+ int r, exit_status;
+ pid_t pid;
+
+ assert(node);
+ assert(fstype);
+
+ r = fsck_exists(fstype);
+ if (r < 0) {
+ log_debug_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", fstype);
+ return 0;
+ }
+ if (r == 0) {
+ log_debug("Not checking partition %s, as fsck for %s does not exist.", node, fstype);
+ return 0;
+ }
+
+ r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_NULL_STDIO, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to fork off fsck: %m");
+ if (r == 0) {
+ /* Child */
+ execl("/sbin/fsck", "/sbin/fsck", "-aT", node, NULL);
+ log_debug_errno(errno, "Failed to execl() fsck: %m");
+ _exit(FSCK_OPERATIONAL_ERROR);
+ }
+
+ exit_status = wait_for_terminate_and_check("fsck", pid, 0);
+ if (exit_status < 0)
+ return log_debug_errno(exit_status, "Failed to fork off /sbin/fsck: %m");
+
+ if ((exit_status & ~FSCK_ERROR_CORRECTED) != FSCK_SUCCESS) {
+ log_debug("fsck failed with exit status %i.", exit_status);
+
+ if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), "File system is corrupted, refusing.");
+
+ log_debug("Ignoring fsck error.");
+ }
+
+ return 0;
+}
+
static int mount_partition(
DissectedPartition *m,
const char *where,
rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
+ if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw) {
+ r = run_fsck(node, fstype);
+ if (r < 0)
+ return r;
+ }
+
if (directory) {
r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
if (r < 0)
if (r < 0)
return r;
+ r = mount_partition(m->partitions + PARTITION_VAR, where, "/var", uid_shift, flags);
+ if (r < 0)
+ return r;
+
+ r = mount_partition(m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, flags);
+ if (r < 0)
+ return r;
+
boot_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
if (boot_mounted < 0)
return boot_mounted;
[META_HOSTNAME] = "/etc/hostname\0",
[META_MACHINE_ID] = "/etc/machine-id\0",
[META_MACHINE_INFO] = "/etc/machine-info\0",
- [META_OS_RELEASE] = "/etc/os-release\0/usr/lib/os-release\0",
+ [META_OS_RELEASE] = "/etc/os-release\0"
+ "/usr/lib/os-release\0",
};
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
[PARTITION_SWAP] = "swap",
[PARTITION_ROOT_VERITY] = "root-verity",
[PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
+ [PARTITION_TMP] = "tmp",
+ [PARTITION_VAR] = "var",
};
DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);
PARTITION_SWAP,
PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
+ PARTITION_TMP,
+ PARTITION_VAR,
_PARTITION_DESIGNATOR_MAX,
_PARTITION_DESIGNATOR_INVALID = -1
};
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7, /* Mount only non-root partitions */
DISSECT_IMAGE_VALIDATE_OS = 1 << 8, /* Refuse mounting images that aren't identifiable as OS images */
DISSECT_IMAGE_NO_UDEV = 1 << 9, /* Don't wait for udev initializing things */
+ DISSECT_IMAGE_RELAX_VAR_CHECK = 1 << 10, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */
+ DISSECT_IMAGE_FSCK = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */
} DissectImageFlags;
struct DissectedImage {
struct device_path__packed device_path__contents _packed_;
assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed));
-bool is_efi_boot(void) {
- if (detect_container() > 0)
- return false;
-
- return access("/sys/firmware/efi/", F_OK) >= 0;
-}
-
-static int read_flag(const char *varname) {
- _cleanup_free_ void *v = NULL;
- uint8_t b;
- size_t s;
- int r;
-
- if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
- return 0;
-
- r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
- if (r < 0)
- return r;
-
- if (s != 1)
- return -EINVAL;
-
- b = *(uint8_t *)v;
- return !!b;
-}
-
-bool is_efi_secure_boot(void) {
- return read_flag("SecureBoot") > 0;
-}
-
-bool is_efi_secure_boot_setup_mode(void) {
- return read_flag("SetupMode") > 0;
-}
int efi_reboot_to_firmware_supported(void) {
_cleanup_free_ void *v = NULL;
#if ENABLE_EFI
-bool is_efi_boot(void);
-bool is_efi_secure_boot(void);
-bool is_efi_secure_boot_setup_mode(void);
int efi_reboot_to_firmware_supported(void);
int efi_get_reboot_to_firmware(void);
int efi_set_reboot_to_firmware(bool value);
#else
-static inline bool is_efi_boot(void) {
- return false;
-}
-
-static inline bool is_efi_secure_boot(void) {
- return false;
-}
-
-static inline bool is_efi_secure_boot_setup_mode(void) {
- return false;
-}
-
static inline int efi_reboot_to_firmware_supported(void) {
return -EOPNOTSUPP;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
+ [NET_DEV_FEAT_RX] = "rx-checksum",
+ [NET_DEV_FEAT_TX] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
[NET_DEV_FEAT_GSO] = "tx-generic-segmentation",
[NET_DEV_FEAT_GRO] = "rx-gro",
[NET_DEV_FEAT_LRO] = "rx-lro",
}
int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
- int *ret_autonegotiation, size_t *ret_speed,
+ int *ret_autonegotiation, uint64_t *ret_speed,
Duplex *ret_duplex, NetDevPort *ret_port) {
struct ethtool_cmd ecmd = {
.cmd = ETHTOOL_GSET,
return 0;
}
-static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) {
- unsigned i;
+static int set_features_bit(
+ const struct ethtool_gstrings *strings,
+ const char *feature,
+ bool flag,
+ struct ethtool_sfeatures *sfeatures) {
+ bool found = false;
- for (i = 0; i < strings->len; i++) {
- if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature))
- return i;
- }
+ assert(strings);
+ assert(feature);
+ assert(sfeatures);
+
+ for (size_t i = 0; i < strings->len; i++)
+ if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature) ||
+ (endswith(feature, "-") && startswith((char *) &strings->data[i * ETH_GSTRING_LEN], feature))) {
+ size_t block, bit;
- return -ENODATA;
+ block = i / 32;
+ bit = i % 32;
+
+ sfeatures->features[block].valid |= 1 << bit;
+ SET_FLAG(sfeatures->features[block].requested, 1 << bit, flag);
+ found = true;
+ }
+
+ return found ? 0 : -ENODATA;
}
int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features) {
_cleanup_free_ struct ethtool_gstrings *strings = NULL;
struct ethtool_sfeatures *sfeatures;
- int block, bit, i, r;
struct ifreq ifr = {};
+ int i, r;
if (*ethtool_fd < 0) {
r = ethtool_connect_or_warn(ethtool_fd, true);
sfeatures->cmd = ETHTOOL_SFEATURES;
sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
- for (i = 0; i < _NET_DEV_FEAT_MAX; i++) {
-
+ for (i = 0; i < _NET_DEV_FEAT_MAX; i++)
if (features[i] != -1) {
-
- r = find_feature_index(strings, netdev_feature_table[i]);
+ r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
if (r < 0) {
- log_warning_errno(r, "ethtool: could not find feature: %s", netdev_feature_table[i]);
+ log_warning_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
continue;
}
-
- block = r / 32;
- bit = r % 32;
-
- sfeatures->features[block].valid |= 1 << bit;
-
- if (features[i])
- sfeatures->features[block].requested |= 1 << bit;
- else
- sfeatures->features[block].requested &= ~(1 << bit);
}
- }
ifr.ifr_data = (void *) sfeatures;
const char *ifname,
int autonegotiation,
uint32_t advertise[static N_ADVERTISE],
- size_t speed,
+ uint64_t speed,
Duplex duplex,
NetDevPort port) {
_cleanup_free_ struct ethtool_link_usettings *u = NULL;
return 0;
}
+int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
+ struct ethtool_pauseparam ecmd = {
+ .cmd = ETHTOOL_GPAUSEPARAM
+ };
+ struct ifreq ifr = {
+ .ifr_data = (void*) &ecmd
+ };
+
+ bool need_update = false;
+ int r;
+
+ if (*fd < 0) {
+ r = ethtool_connect_or_warn(fd, true);
+ if (r < 0)
+ return r;
+ }
+
+ strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+ r = ioctl(*fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return -errno;
+
+ if (rx >= 0 && ecmd.rx_pause != (uint32_t) rx) {
+ ecmd.rx_pause = rx;
+ need_update = true;
+ }
+
+ if (tx >= 0 && ecmd.tx_pause != (uint32_t) tx) {
+ ecmd.tx_pause = tx;
+ need_update = true;
+ }
+
+ if (autoneg >= 0 && ecmd.autoneg != (uint32_t) autoneg) {
+ ecmd.autoneg = autoneg;
+ need_update = true;
+ }
+
+ if (need_update) {
+ ecmd.cmd = ETHTOOL_SPAUSEPARAM;
+
+ r = ioctl(*fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return -errno;
+ }
+
+ return 0;
+}
+
int config_parse_channel(const char *unit,
const char *filename,
unsigned line,
} WakeOnLan;
typedef enum NetDevFeature {
+ NET_DEV_FEAT_RX,
+ NET_DEV_FEAT_TX,
NET_DEV_FEAT_GSO,
NET_DEV_FEAT_GRO,
NET_DEV_FEAT_LRO,
int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret);
int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
- int *ret_autonegotiation, size_t *ret_speed,
+ int *ret_autonegotiation, uint64_t *ret_speed,
Duplex *ret_duplex, NetDevPort *ret_port);
int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
int ethtool_set_speed(int *ethtool_fd, const char *ifname, unsigned speed, Duplex duplex);
int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features);
int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
int autonegotiation, uint32_t advertise[static N_ADVERTISE],
- size_t speed, Duplex duplex, NetDevPort port);
+ uint64_t speed, Duplex duplex, NetDevPort port);
int ethtool_set_channels(int *ethtool_fd, const char *ifname, netdev_channels *channels);
+int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
const char *duplex_to_string(Duplex d) _const_;
Duplex duplex_from_string(const char *d) _pure_;
#include <net/if.h>
#include <unistd.h>
+#include "sd-id128.h"
+
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-table.h"
#include "format-util.h"
#include "gunicode.h"
+#include "id128-util.h"
#include "in-addr-util.h"
#include "locale-util.h"
#include "memory-util.h"
usec_t timespan;
uint64_t size;
char string[0];
+ char **strv;
int int_val;
int8_t int8;
int16_t int16;
int percent; /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */
int ifindex;
union in_addr_union address;
+ sd_id128_t id128;
/* … add more here as we start supporting more cell data types … */
};
} TableData;
free(d->formatted);
free(d->url);
+ if (d->type == TABLE_STRV)
+ strv_free(d->strv);
+
return mfree(d);
}
case TABLE_PATH:
return strlen(data) + 1;
+ case TABLE_STRV:
+ return sizeof(char **);
+
case TABLE_BOOLEAN:
return sizeof(bool);
case TABLE_IN6_ADDR:
return sizeof(struct in6_addr);
+ case TABLE_UUID:
+ case TABLE_ID128:
+ return sizeof(sd_id128_t);
+
default:
assert_not_reached("Uh? Unexpected cell type");
}
k = table_data_size(type, data);
l = table_data_size(d->type, d->data);
-
if (k != l)
return false;
unsigned align_percent,
unsigned ellipsize_percent) {
+ _cleanup_free_ TableData *d = NULL;
size_t data_size;
- TableData *d;
data_size = table_data_size(type, data);
d->weight = weight;
d->align_percent = align_percent;
d->ellipsize_percent = ellipsize_percent;
- memcpy_safe(d->data, data, data_size);
- return d;
+ if (type == TABLE_STRV) {
+ d->strv = strv_copy(data);
+ if (!d->strv)
+ return NULL;
+ } else
+ memcpy_safe(d->data, data, data_size);
+
+ return TAKE_PTR(d);
}
int table_add_cell_full(
int ifindex;
bool b;
union in_addr_union address;
+ sd_id128_t id128;
} buffer;
switch (type) {
data = va_arg(ap, const char *);
break;
+ case TABLE_STRV:
+ data = va_arg(ap, char * const *);
+ break;
+
case TABLE_BOOLEAN:
buffer.b = va_arg(ap, int);
data = &buffer.b;
data = &buffer.address.in6;
break;
+ case TABLE_UUID:
+ case TABLE_ID128:
+ buffer.id128 = va_arg(ap, sd_id128_t);
+ data = &buffer.id128;
+ break;
+
case TABLE_SET_MINIMUM_WIDTH: {
size_t w = va_arg(ap, size_t);
case TABLE_PATH:
return path_compare(a->string, b->string);
+ case TABLE_STRV:
+ return strv_compare(a->strv, b->strv);
+
case TABLE_BOOLEAN:
if (!a->boolean && b->boolean)
return -1;
case TABLE_IN6_ADDR:
return memcmp(&a->address.in6, &b->address.in6, FAMILY_ADDRESS_SIZE(AF_INET6));
+ case TABLE_UUID:
+ case TABLE_ID128:
+ return memcmp(&a->id128, &b->id128, sizeof(sd_id128_t));
+
default:
;
}
return d->string;
+ case TABLE_STRV: {
+ char *p;
+
+ p = strv_join(d->strv, "\n");
+ if (!p)
+ return NULL;
+
+ d->formatted = p;
+ break;
+ }
+
case TABLE_BOOLEAN:
return yes_no(d->boolean);
break;
}
+ case TABLE_ID128: {
+ char *p;
+
+ p = new(char, SD_ID128_STRING_MAX);
+ if (!p)
+ return NULL;
+
+ d->formatted = sd_id128_to_string(d->id128, p);
+ break;
+ }
+
+ case TABLE_UUID: {
+ char *p;
+
+ p = new(char, ID128_UUID_STRING_MAX);
+ if (!p)
+ return NULL;
+
+ d->formatted = id128_to_uuid_string(d->id128, p);
+ break;
+ }
+
default:
assert_not_reached("Unexpected type?");
}
case TABLE_PATH:
return json_variant_new_string(ret, d->string);
+ case TABLE_STRV:
+ return json_variant_new_array_strv(ret, d->strv);
+
case TABLE_BOOLEAN:
return json_variant_new_boolean(ret, d->boolean);
case TABLE_IN6_ADDR:
return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
+ case TABLE_ID128: {
+ char buf[SD_ID128_STRING_MAX];
+ return json_variant_new_string(ret, sd_id128_to_string(d->id128, buf));
+ }
+
+ case TABLE_UUID: {
+ char buf[ID128_UUID_STRING_MAX];
+ return json_variant_new_string(ret, id128_to_uuid_string(d->id128, buf));
+ }
+
default:
return -EINVAL;
}
typedef enum TableDataType {
TABLE_EMPTY,
TABLE_STRING,
+ TABLE_STRV,
TABLE_PATH,
TABLE_BOOLEAN,
TABLE_TIMESTAMP,
TABLE_IFINDEX,
TABLE_IN_ADDR, /* Takes a union in_addr_union (or a struct in_addr) */
TABLE_IN6_ADDR, /* Takes a union in_addr_union (or a struct in6_addr) */
+ TABLE_ID128,
+ TABLE_UUID,
_TABLE_DATA_TYPE_MAX,
/* The following are not really data types, but commands for table_add_cell_many() to make changes to
"BindsTo=%%i.mount\n"
"Conflicts=shutdown.target\n"
"After=%%i.mount\n"
- "Before=shutdown.target %s\n"
+ "Before=shutdown.target %s\n",
+ program_invocation_short_name,
+ target);
+
+ if (empty_or_root(where)) /* Make sure the root fs is actually writable before we resize it */
+ fprintf(f,
+ "After=systemd-remount-fs.service\n");
+
+ fprintf(f,
"\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
"TimeoutSec=0\n",
- program_invocation_short_name,
- target,
escaped);
return generator_add_symlink(dir, where_unit, "wants", unit);
SYSTEM_DATA_UNIT_PATH "/" SPECIAL_REMOUNT_FS_SERVICE);
}
+int generator_write_blockdev_dependency(
+ FILE *f,
+ const char *what) {
+
+ _cleanup_free_ char *escaped = NULL;
+ int r;
+
+ assert(f);
+ assert(what);
+
+ if (!path_startswith(what, "/dev/"))
+ return 0;
+
+ r = unit_name_path_escape(what, &escaped);
+ if (r < 0)
+ return log_error_errno(r, "Failed to escape device node path %s: %m", what);
+
+ fprintf(f,
+ "After=blockdev@%s.target\n",
+ escaped);
+
+ return 0;
+}
+
+int generator_write_cryptsetup_unit_section(
+ FILE *f,
+ const char *source) {
+
+ assert(f);
+
+ fprintf(f,
+ "[Unit]\n"
+ "Description=Cryptography Setup for %%I\n"
+ "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
+
+ if (source)
+ fprintf(f, "SourcePath=%s\n", source);
+
+ fprintf(f,
+ "DefaultDependencies=no\n"
+ "IgnoreOnIsolate=true\n"
+ "After=cryptsetup-pre.target\n"
+ "Before=blockdev@dev-mapper-%%i.target\n"
+ "Wants=blockdev@dev-mapper-%%i.target\n");
+
+ return 0;
+}
+
+int generator_write_cryptsetup_service_section(
+ FILE *f,
+ const char *name,
+ const char *what,
+ const char *password,
+ const char *options) {
+
+ _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *password_escaped = NULL, *options_escaped = NULL;
+
+ assert(f);
+ assert(name);
+ assert(what);
+
+ name_escaped = specifier_escape(name);
+ if (!name_escaped)
+ return log_oom();
+
+ what_escaped = specifier_escape(what);
+ if (!what_escaped)
+ return log_oom();
+
+ if (password) {
+ password_escaped = specifier_escape(password);
+ if (!password_escaped)
+ return log_oom();
+ }
+
+ if (options) {
+ options_escaped = specifier_escape(options);
+ if (!options_escaped)
+ return log_oom();
+ }
+
+ fprintf(f,
+ "\n"
+ "[Service]\n"
+ "Type=oneshot\n"
+ "RemainAfterExit=yes\n"
+ "TimeoutSec=0\n" /* The binary handles timeouts on its own */
+ "KeyringMode=shared\n" /* Make sure we can share cached keys among instances */
+ "OOMScoreAdjust=500\n" /* Unlocking can allocate a lot of memory if Argon2 is used */
+ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
+ "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
+ name_escaped, what_escaped, strempty(password_escaped), strempty(options_escaped),
+ name_escaped);
+
+ return 0;
+}
+
void log_setup_generator(void) {
log_set_prohibit_ipc(true);
log_setup_service();
const char *opts,
char **filtered);
+int generator_write_blockdev_dependency(
+ FILE *f,
+ const char *what);
+
+int generator_write_cryptsetup_unit_section(
+ FILE *f,
+ const char *source);
+
+int generator_write_cryptsetup_service_section(
+ FILE *f,
+ const char *name,
+ const char *what,
+ const char *password,
+ const char *options);
+
int generator_write_device_deps(
const char *dir,
const char *what,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "gpt.h"
+#include "string-util.h"
+
+const GptPartitionType gpt_partition_type_table[] = {
+ { GPT_ROOT_X86, "root-x86" },
+ { GPT_ROOT_X86_VERITY, "root-x86-verity" },
+ { GPT_ROOT_X86_64, "root-x86-64" },
+ { GPT_ROOT_X86_64_VERITY, "root-x86-64-verity" },
+ { GPT_ROOT_ARM, "root-arm" },
+ { GPT_ROOT_ARM_VERITY, "root-arm-verity" },
+ { GPT_ROOT_ARM_64, "root-arm64" },
+ { GPT_ROOT_ARM_64_VERITY, "root-arm64-verity" },
+ { GPT_ROOT_IA64, "root-ia64" },
+ { GPT_ROOT_IA64_VERITY, "root-ia64-verity" },
+#ifdef GPT_ROOT_NATIVE
+ { GPT_ROOT_NATIVE, "root" },
+ { GPT_ROOT_NATIVE_VERITY, "root-verity" },
+#endif
+#ifdef GPT_ROOT_SECONDARY
+ { GPT_ROOT_SECONDARY, "root-secondary" },
+ { GPT_ROOT_SECONDARY_VERITY, "root-secondary-verity" },
+#endif
+ { GPT_ESP, "esp" },
+ { GPT_XBOOTLDR, "xbootldr" },
+ { GPT_SWAP, "swap" },
+ { GPT_HOME, "home" },
+ { GPT_SRV, "srv" },
+ { GPT_VAR, "var" },
+ { GPT_TMP, "tmp" },
+ { GPT_LINUX_GENERIC, "linux-generic", },
+ {}
+};
+
+const char *gpt_partition_type_uuid_to_string(sd_id128_t id) {
+ for (size_t i = 0; i < ELEMENTSOF(gpt_partition_type_table) - 1; i++)
+ if (sd_id128_equal(id, gpt_partition_type_table[i].uuid))
+ return gpt_partition_type_table[i].name;
+
+ return NULL;
+}
+
+const char *gpt_partition_type_uuid_to_string_harder(
+ sd_id128_t id,
+ char buffer[static ID128_UUID_STRING_MAX]) {
+
+ const char *s;
+
+ assert(buffer);
+
+ s = gpt_partition_type_uuid_to_string(id);
+ if (s)
+ return s;
+
+ return id128_to_uuid_string(id, buffer);
+}
+
+int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret) {
+ assert(s);
+ assert(ret);
+
+ for (size_t i = 0; i < ELEMENTSOF(gpt_partition_type_table) - 1; i++)
+ if (streq(s, gpt_partition_type_table[i].name)) {
+ *ret = gpt_partition_type_table[i].uuid;
+ return 0;
+ }
+
+ return sd_id128_from_string(s, ret);
+}
#include "sd-id128.h"
+#include "id128-util.h"
+
/* We only support root disk discovery for x86, x86-64, Itanium and ARM for
* now, since EFI for anything else doesn't really exist, and we only
* care for root partitions on the same disk as the EFI ESP. */
#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
+#define GPT_VAR SD_ID128_MAKE(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d)
+#define GPT_TMP SD_ID128_MAKE(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1)
+#define GPT_USER_HOME SD_ID128_MAKE(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16)
/* Verity partitions for the root partitions above (we only define them for the root partitions, because only they are
* are commonly read-only and hence suitable for verity). */
# define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
#endif
+#define GPT_FLAG_REQUIRED_PARTITION (1ULL << 0)
#define GPT_FLAG_NO_BLOCK_IO_PROTOCOL (1ULL << 1)
+#define GPT_FLAG_LEGACY_BIOS_BOOTABLE (1ULL << 2)
/* Flags we recognize on the root, swap, home and srv partitions when
* doing auto-discovery. These happen to be identical to what
#define GPT_FLAG_NO_AUTO (1ULL << 63)
#define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4)
+
+const char *gpt_partition_type_uuid_to_string(sd_id128_t id);
+const char *gpt_partition_type_uuid_to_string_harder(
+ sd_id128_t id,
+ char buffer[static ID128_UUID_STRING_MAX]);
+int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret);
+
+typedef struct GptPartitionType {
+ sd_id128_t uuid;
+ const char *name;
+} GptPartitionType;
+
+extern const GptPartitionType gpt_partition_type_table[];
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "group-record-nss.h"
+#include "libcrypt-util.h"
+#include "strv.h"
+
+int nss_group_to_group_record(
+ const struct group *grp,
+ const struct sgrp *sgrp,
+ GroupRecord **ret) {
+
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ int r;
+
+ assert(grp);
+ assert(ret);
+
+ if (isempty(grp->gr_name))
+ return -EINVAL;
+
+ if (sgrp && !streq_ptr(sgrp->sg_namp, grp->gr_name))
+ return -EINVAL;
+
+ g = group_record_new();
+ if (!g)
+ return -ENOMEM;
+
+ g->group_name = strdup(grp->gr_name);
+ if (!g->group_name)
+ return -ENOMEM;
+
+ g->members = strv_copy(grp->gr_mem);
+ if (!g->members)
+ return -ENOMEM;
+
+ g->gid = grp->gr_gid;
+
+ if (sgrp) {
+ if (hashed_password_valid(sgrp->sg_passwd)) {
+ g->hashed_password = strv_new(sgrp->sg_passwd);
+ if (!g->hashed_password)
+ return -ENOMEM;
+ }
+
+ r = strv_extend_strv(&g->members, sgrp->sg_mem, 1);
+ if (r < 0)
+ return r;
+
+ g->administrators = strv_copy(sgrp->sg_adm);
+ if (!g->administrators)
+ return -ENOMEM;
+ }
+
+ r = json_build(&g->json, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(g->gid)),
+ JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->members), "members", JSON_BUILD_STRV(g->members)),
+ JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->hashed_password), "privileged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRV(g->hashed_password)))),
+ JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->administrators), "administrators", JSON_BUILD_STRV(g->administrators))));
+ if (r < 0)
+ return r;
+
+ g->mask = USER_RECORD_REGULAR |
+ (!strv_isempty(g->hashed_password) ? USER_RECORD_PRIVILEGED : 0);
+
+ *ret = TAKE_PTR(g);
+ return 0;
+}
+
+int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer) {
+ size_t buflen = 4096;
+ int r;
+
+ assert(grp);
+ assert(ret_sgrp);
+ assert(ret_buffer);
+
+ for (;;) {
+ _cleanup_free_ char *buf = NULL;
+ struct sgrp sgrp, *result;
+
+ buf = malloc(buflen);
+ if (!buf)
+ return -ENOMEM;
+
+ r = getsgnam_r(grp->gr_name, &sgrp, buf, buflen, &result);
+ if (r == 0) {
+ if (!result)
+ return -ESRCH;
+
+ *ret_sgrp = *result;
+ *ret_buffer = TAKE_PTR(buf);
+ return 0;
+ }
+ if (r < 0)
+ return -EIO; /* Weird, this should not return negative! */
+ if (r != ERANGE)
+ return -r;
+
+ if (buflen > SIZE_MAX / 2)
+ return -ERANGE;
+
+ buflen *= 2;
+ buf = mfree(buf);
+ }
+}
+
+int nss_group_record_by_name(const char *name, GroupRecord **ret) {
+ _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+ struct group grp, *result;
+ bool incomplete = false;
+ size_t buflen = 4096;
+ struct sgrp sgrp;
+ int r;
+
+ assert(name);
+ assert(ret);
+
+ for (;;) {
+ buf = malloc(buflen);
+ if (!buf)
+ return -ENOMEM;
+
+ r = getgrnam_r(name, &grp, buf, buflen, &result);
+ if (r == 0) {
+ if (!result)
+ return -ESRCH;
+
+ break;
+ }
+
+ if (r < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrnam_r() returned a negative value");
+ if (r != ERANGE)
+ return -r;
+ if (buflen > SIZE_MAX / 2)
+ return -ERANGE;
+
+ buflen *= 2;
+ buf = mfree(buf);
+ }
+
+ r = nss_sgrp_for_group(result, &sgrp, &sbuf);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
+ incomplete = ERRNO_IS_PRIVILEGE(r);
+ }
+
+ r = nss_group_to_group_record(result, r >= 0 ? &sgrp : NULL, ret);
+ if (r < 0)
+ return r;
+
+ (*ret)->incomplete = incomplete;
+ return 0;
+}
+
+int nss_group_record_by_gid(gid_t gid, GroupRecord **ret) {
+ _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+ struct group grp, *result;
+ bool incomplete = false;
+ size_t buflen = 4096;
+ struct sgrp sgrp;
+ int r;
+
+ assert(ret);
+
+ for (;;) {
+ buf = malloc(buflen);
+ if (!buf)
+ return -ENOMEM;
+
+ r = getgrgid_r(gid, &grp, buf, buflen, &result);
+ if (r == 0) {
+ if (!result)
+ return -ESRCH;
+ break;
+ }
+
+ if (r < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrgid_r() returned a negative value");
+ if (r != ERANGE)
+ return -r;
+ if (buflen > SIZE_MAX / 2)
+ return -ERANGE;
+
+ buflen *= 2;
+ buf = mfree(buf);
+ }
+
+ r = nss_sgrp_for_group(result, &sgrp, &sbuf);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
+ incomplete = ERRNO_IS_PRIVILEGE(r);
+ }
+
+ r = nss_group_to_group_record(result, r >= 0 ? &sgrp : NULL, ret);
+ if (r < 0)
+ return r;
+
+ (*ret)->incomplete = incomplete;
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <grp.h>
+#include <gshadow.h>
+
+#include "group-record.h"
+
+/* Synthesize GroupRecord objects from NSS data */
+
+int nss_group_to_group_record(const struct group *grp, const struct sgrp *sgrp, GroupRecord **ret);
+int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer);
+
+int nss_group_record_by_name(const char *name, GroupRecord **ret);
+int nss_group_record_by_gid(gid_t gid, GroupRecord **ret);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "group-record-show.h"
+#include "strv.h"
+#include "user-util.h"
+#include "userdb.h"
+
+void group_record_show(GroupRecord *gr, bool show_full_user_info) {
+ int r;
+
+ printf(" Group name: %s\n",
+ group_record_group_name_and_realm(gr));
+
+ printf(" Disposition: %s\n", user_disposition_to_string(group_record_disposition(gr)));
+
+ if (gr->last_change_usec != USEC_INFINITY) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), gr->last_change_usec));
+ }
+
+ if (gid_is_valid(gr->gid))
+ printf(" GID: " GID_FMT "\n", gr->gid);
+
+ if (show_full_user_info) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+ r = membershipdb_by_group(gr->group_name, 0, &iterator);
+ if (r < 0) {
+ errno = -r;
+ printf(" Members: (can't acquire: %m)");
+ } else {
+ const char *prefix = " Members:";
+
+ for (;;) {
+ _cleanup_free_ char *user = NULL;
+
+ r = membershipdb_iterator_get(iterator, &user, NULL);
+ if (r == -ESRCH)
+ break;
+ if (r < 0) {
+ errno = -r;
+ printf("%s (can't iterate: %m\n", prefix);
+ break;
+ }
+
+ printf("%s %s\n", prefix, user);
+ prefix = " ";
+ }
+ }
+ } else {
+ const char *prefix = " Members:";
+ char **i;
+
+ STRV_FOREACH(i, gr->members) {
+ printf("%s %s\n", prefix, *i);
+ prefix = " ";
+ }
+ }
+
+ if (!strv_isempty(gr->administrators)) {
+ const char *prefix = " Admins:";
+ char **i;
+
+ STRV_FOREACH(i, gr->administrators) {
+ printf("%s %s\n", prefix, *i);
+ prefix = " ";
+ }
+ }
+
+ if (!strv_isempty(gr->hashed_password))
+ printf(" Passwords: %zu\n", strv_length(gr->hashed_password));
+
+ if (gr->service)
+ printf(" Service: %s\n", gr->service);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "group-record.h"
+
+void group_record_show(GroupRecord *gr, bool show_full_user_info);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "group-record.h"
+#include "strv.h"
+#include "user-util.h"
+
+GroupRecord* group_record_new(void) {
+ GroupRecord *h;
+
+ h = new(GroupRecord, 1);
+ if (!h)
+ return NULL;
+
+ *h = (GroupRecord) {
+ .n_ref = 1,
+ .disposition = _USER_DISPOSITION_INVALID,
+ .last_change_usec = UINT64_MAX,
+ .gid = GID_INVALID,
+ };
+
+ return h;
+}
+
+static GroupRecord *group_record_free(GroupRecord *g) {
+ if (!g)
+ return NULL;
+
+ free(g->group_name);
+ free(g->realm);
+ free(g->group_name_and_realm_auto);
+
+ strv_free(g->members);
+ free(g->service);
+ strv_free(g->administrators);
+ strv_free_erase(g->hashed_password);
+
+ json_variant_unref(g->json);
+
+ return mfree(g);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(GroupRecord, group_record, group_record_free);
+
+static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch privileged_dispatch_table[] = {
+ { "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(GroupRecord, hashed_password), JSON_SAFE },
+ {},
+ };
+
+ return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata);
+}
+
+static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch binding_dispatch_table[] = {
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
+ {},
+ };
+
+ char smid[SD_ID128_STRING_MAX];
+ JsonVariant *m;
+ sd_id128_t mid;
+ int r;
+
+ if (!variant)
+ return 0;
+
+ if (!json_variant_is_object(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+ m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ if (!m)
+ return 0;
+
+ return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata);
+}
+
+static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch per_machine_dispatch_table[] = {
+ { "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
+ { "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
+ { "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
+ { "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
+ {},
+ };
+
+ JsonVariant *e;
+ int r;
+
+ if (!variant)
+ return 0;
+
+ if (!json_variant_is_array(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+ JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+ bool matching = false;
+ JsonVariant *m;
+
+ if (!json_variant_is_object(e))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
+
+ m = json_variant_by_key(e, "matchMachineId");
+ if (m) {
+ r = per_machine_id_match(m, flags);
+ if (r < 0)
+ return r;
+
+ matching = r > 0;
+ }
+
+ if (!matching) {
+ m = json_variant_by_key(e, "matchHostname");
+ if (m) {
+ r = per_machine_hostname_match(m, flags);
+ if (r < 0)
+ return r;
+
+ matching = r > 0;
+ }
+ }
+
+ if (!matching)
+ continue;
+
+ r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch status_dispatch_table[] = {
+ { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
+ {},
+ };
+
+ char smid[SD_ID128_STRING_MAX];
+ JsonVariant *m;
+ sd_id128_t mid;
+ int r;
+
+ if (!variant)
+ return 0;
+
+ if (!json_variant_is_object(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+ m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ if (!m)
+ return 0;
+
+ return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
+}
+
+static int group_record_augment(GroupRecord *h, JsonDispatchFlags json_flags) {
+ assert(h);
+
+ if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
+ return 0;
+
+ assert(h->group_name);
+
+ if (!h->group_name_and_realm_auto && h->realm) {
+ h->group_name_and_realm_auto = strjoin(h->group_name, "@", h->realm);
+ if (!h->group_name_and_realm_auto)
+ return json_log_oom(h->json, json_flags);
+ }
+
+ return 0;
+}
+
+int group_record_load(
+ GroupRecord *h,
+ JsonVariant *v,
+ UserRecordLoadFlags load_flags) {
+
+ static const JsonDispatch group_dispatch_table[] = {
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), 0 },
+ { "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(GroupRecord, realm), 0 },
+ { "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(GroupRecord, disposition), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
+ { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(GroupRecord, last_change_usec), 0 },
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
+ { "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
+ { "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
+
+ { "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
+
+ /* Not defined for now, for groups, but let's at least generate sensible errors about it */
+ { "secret", JSON_VARIANT_OBJECT, json_dispatch_unsupported, 0, 0 },
+
+ /* Ignore the perMachine, binding and status stuff here, and process it later, so that it overrides whatever is set above */
+ { "perMachine", JSON_VARIANT_ARRAY, NULL, 0, 0 },
+ { "binding", JSON_VARIANT_OBJECT, NULL, 0, 0 },
+ { "status", JSON_VARIANT_OBJECT, NULL, 0, 0 },
+
+ /* Ignore 'signature', we check it with explicit accessors instead */
+ { "signature", JSON_VARIANT_ARRAY, NULL, 0, 0 },
+ {},
+ };
+
+ JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
+ int r;
+
+ assert(h);
+ assert(!h->json);
+
+ /* Note that this call will leave a half-initialized record around on failure! */
+
+ if ((USER_RECORD_REQUIRE_MASK(load_flags) & (USER_RECORD_SECRET|USER_RECORD_PRIVILEGED)))
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Secret and privileged section currently not available for groups, refusing.");
+
+ r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
+ if (r < 0)
+ return r;
+
+ r = json_dispatch(h->json, group_dispatch_table, NULL, json_flags, h);
+ if (r < 0)
+ return r;
+
+ /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields, since we want
+ * them to override the global options. Let's process them now. */
+
+ r = dispatch_per_machine("perMachine", json_variant_by_key(h->json, "perMachine"), json_flags, h);
+ if (r < 0)
+ return r;
+
+ r = dispatch_binding("binding", json_variant_by_key(h->json, "binding"), json_flags, h);
+ if (r < 0)
+ return r;
+
+ r = dispatch_status("status", json_variant_by_key(h->json, "status"), json_flags, h);
+ if (r < 0)
+ return r;
+
+ if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->group_name)
+ return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "Group name field missing, refusing.");
+
+ r = group_record_augment(h, json_flags);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int group_record_build(GroupRecord **ret, ...) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ va_list ap;
+ int r;
+
+ assert(ret);
+
+ va_start(ap, ret);
+ r = json_buildv(&v, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return r;
+
+ g = group_record_new();
+ if (!g)
+ return -ENOMEM;
+
+ r = group_record_load(g, v, USER_RECORD_LOAD_FULL);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(g);
+ return 0;
+}
+
+const char *group_record_group_name_and_realm(GroupRecord *h) {
+ assert(h);
+
+ /* Return the pre-initialized joined string if it is defined */
+ if (h->group_name_and_realm_auto)
+ return h->group_name_and_realm_auto;
+
+ /* If it's not defined then we cannot have a realm */
+ assert(!h->realm);
+ return h->group_name;
+}
+
+UserDisposition group_record_disposition(GroupRecord *h) {
+ assert(h);
+
+ if (h->disposition >= 0)
+ return h->disposition;
+
+ /* If not declared, derive from GID */
+
+ if (!gid_is_valid(h->gid))
+ return _USER_DISPOSITION_INVALID;
+
+ if (h->gid == 0 || h->gid == GID_NOBODY)
+ return USER_INTRINSIC;
+
+ if (gid_is_system(h->gid))
+ return USER_SYSTEM;
+
+ if (gid_is_dynamic(h->gid))
+ return USER_DYNAMIC;
+
+ if (gid_is_container(h->gid))
+ return USER_CONTAINER;
+
+ if (h->gid > INT32_MAX)
+ return USER_RESERVED;
+
+ return USER_REGULAR;
+}
+
+int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **ret) {
+ _cleanup_(group_record_unrefp) GroupRecord *c = NULL;
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ c = group_record_new();
+ if (!c)
+ return -ENOMEM;
+
+ r = group_record_load(c, h->json, flags);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(c);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "json.h"
+#include "user-record.h"
+
+typedef struct GroupRecord {
+ unsigned n_ref;
+ UserRecordMask mask;
+ bool incomplete;
+
+ char *group_name;
+ char *realm;
+ char *group_name_and_realm_auto;
+
+ UserDisposition disposition;
+ uint64_t last_change_usec;
+
+ gid_t gid;
+
+ char **members;
+
+ char *service;
+
+ /* The following exist mostly so that we can cover the full /etc/gshadow set of fields, we currently
+ * do not actually make use of these */
+ char **administrators; /* maps to 'struct sgrp' .sg_adm field */
+ char **hashed_password; /* maps to 'struct sgrp' .sg_passwd field */
+
+ JsonVariant *json;
+} GroupRecord;
+
+GroupRecord* group_record_new(void);
+GroupRecord* group_record_ref(GroupRecord *g);
+GroupRecord* group_record_unref(GroupRecord *g);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(GroupRecord*, group_record_unref);
+
+int group_record_load(GroupRecord *h, JsonVariant *v, UserRecordLoadFlags flags);
+int group_record_build(GroupRecord **ret, ...);
+int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret);
+
+const char *group_record_group_name_and_realm(GroupRecord *h);
+UserDisposition group_record_disposition(GroupRecord *h);
#include "pretty-print.h"
#include "terminal-util.h"
-int id128_pretty_print(sd_id128_t id, Id128PrettyPrintMode mode) {
- _cleanup_free_ char *man_link = NULL, *mod_link = NULL;
+int id128_pretty_print_sample(const char *name, sd_id128_t id) {
+ _cleanup_free_ char *man_link = NULL, *mod_link = NULL;
const char *on, *off;
unsigned i;
- assert(mode >= 0);
- assert(mode < _ID128_PRETTY_PRINT_MODE_MAX);
-
- if (mode == ID128_PRINT_ID128) {
- printf(SD_ID128_FORMAT_STR "\n",
- SD_ID128_FORMAT_VAL(id));
- return 0;
- } else if (mode == ID128_PRINT_UUID) {
- printf(SD_ID128_UUID_FORMAT_STR "\n",
- SD_ID128_FORMAT_VAL(id));
- return 0;
- }
-
on = ansi_highlight();
off = ansi_normal();
"As UUID:\n"
"%s" SD_ID128_UUID_FORMAT_STR "%s\n\n"
"As %s macro:\n"
- "%s#define XYZ SD_ID128_MAKE(",
+ "%s#define %s SD_ID128_MAKE(",
on, SD_ID128_FORMAT_VAL(id), off,
on, SD_ID128_FORMAT_VAL(id), off,
man_link,
- on);
+ on, name);
for (i = 0; i < 16; i++)
printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
printf(")%s\n\n", off);
printf("As Python constant:\n"
">>> import %s\n"
- ">>> %sXYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')%s\n",
+ ">>> %s%s = uuid.UUID('" SD_ID128_FORMAT_STR "')%s\n",
mod_link,
- on, SD_ID128_FORMAT_VAL(id), off);
+ on, name, SD_ID128_FORMAT_VAL(id), off);
return 0;
}
+
+int id128_pretty_print(sd_id128_t id, Id128PrettyPrintMode mode) {
+ assert(mode >= 0);
+ assert(mode < _ID128_PRETTY_PRINT_MODE_MAX);
+
+ if (mode == ID128_PRINT_ID128) {
+ printf(SD_ID128_FORMAT_STR "\n",
+ SD_ID128_FORMAT_VAL(id));
+ return 0;
+ } else if (mode == ID128_PRINT_UUID) {
+ printf(SD_ID128_UUID_FORMAT_STR "\n",
+ SD_ID128_FORMAT_VAL(id));
+ return 0;
+ } else
+ return id128_pretty_print_sample("XYZ", id);
+}
+
int id128_print_new(Id128PrettyPrintMode mode) {
sd_id128_t id;
int r;
_ID128_PRETTY_PRINT_MODE_INVALID = -1
} Id128PrettyPrintMode;
+int id128_pretty_print_sample(const char *name, sd_id128_t id);
int id128_pretty_print(sd_id128_t id, Id128PrettyPrintMode mode);
int id128_print_new(Id128PrettyPrintMode mode);
#include "alloc-util.h"
#include "btrfs-util.h"
+#include "chattr-util.h"
+#include "errno-util.h"
#include "import-util.h"
#include "log.h"
#include "macro.h"
return 0;
}
+
+int import_set_nocow_and_log(int fd, const char *path) {
+ int r;
+
+ r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+ if (r < 0)
+ return log_full_errno(
+ ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to set file attributes on %s: %m", path);
+
+ return 0;
+}
int raw_strip_suffixes(const char *name, char **ret);
int import_assign_pool_quota_and_warn(const char *path);
+
+int import_set_nocow_and_log(int fd, const char *path);
return -ENOMEM;
path_simplify(p, false);
- q = readlink_malloc(p, &dest);
+ q = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &dest, NULL);
if (q == -ENOENT)
continue;
if (q < 0) {
void *data,
void *userdata) {
- UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
+ UnitFileInstallInfo *info = userdata;
InstallContext *c = data;
int r;
if (r < 0)
return r;
- r = install_info_add(c, printed, NULL, true, &alsoinfo);
+ r = install_info_add(c, printed, NULL, true, NULL);
if (r < 0)
return r;
return 1;
}
+int journal_access_blocked(sd_journal *j) {
+ return hashmap_contains(j->errors, INT_TO_PTR(-EACCES));
+}
+
int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_users) {
Iterator it;
void *code;
return 0;
}
- if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) {
+ if (journal_access_blocked(j)) {
if (!quiet)
(void) access_check_var_log_journal(j, want_other_users);
#include "sd-journal.h"
bool journal_field_valid(const char *p, size_t l, bool allow_protected);
-
+int journal_access_blocked(sd_journal *j);
int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_users);
/* We use fake JsonVariant objects for some special values, in order to avoid memory allocations for them. Note that
* effectively this means that there are multiple ways to encode the same objects: via these magic values or as
* properly allocated JsonVariant. We convert between both on-the-fly as necessary. */
-#define JSON_VARIANT_MAGIC_TRUE ((JsonVariant*) 1)
-#define JSON_VARIANT_MAGIC_FALSE ((JsonVariant*) 2)
-#define JSON_VARIANT_MAGIC_NULL ((JsonVariant*) 3)
-#define JSON_VARIANT_MAGIC_ZERO_INTEGER ((JsonVariant*) 4)
-#define JSON_VARIANT_MAGIC_ZERO_UNSIGNED ((JsonVariant*) 5)
-#define JSON_VARIANT_MAGIC_ZERO_REAL ((JsonVariant*) 6)
-#define JSON_VARIANT_MAGIC_EMPTY_STRING ((JsonVariant*) 7)
-#define JSON_VARIANT_MAGIC_EMPTY_ARRAY ((JsonVariant*) 8)
-#define JSON_VARIANT_MAGIC_EMPTY_OBJECT ((JsonVariant*) 9)
-#define _JSON_VARIANT_MAGIC_MAX ((JsonVariant*) 10)
+enum
+{
+ _JSON_VARIANT_MAGIC_TRUE = 1,
+#define JSON_VARIANT_MAGIC_TRUE ((JsonVariant*) _JSON_VARIANT_MAGIC_TRUE)
+ _JSON_VARIANT_MAGIC_FALSE,
+#define JSON_VARIANT_MAGIC_FALSE ((JsonVariant*) _JSON_VARIANT_MAGIC_FALSE)
+ _JSON_VARIANT_MAGIC_NULL,
+#define JSON_VARIANT_MAGIC_NULL ((JsonVariant*) _JSON_VARIANT_MAGIC_NULL)
+ _JSON_VARIANT_MAGIC_ZERO_INTEGER,
+#define JSON_VARIANT_MAGIC_ZERO_INTEGER ((JsonVariant*) _JSON_VARIANT_MAGIC_ZERO_INTEGER)
+ _JSON_VARIANT_MAGIC_ZERO_UNSIGNED,
+#define JSON_VARIANT_MAGIC_ZERO_UNSIGNED ((JsonVariant*) _JSON_VARIANT_MAGIC_ZERO_UNSIGNED)
+ _JSON_VARIANT_MAGIC_ZERO_REAL,
+#define JSON_VARIANT_MAGIC_ZERO_REAL ((JsonVariant*) _JSON_VARIANT_MAGIC_ZERO_REAL)
+ _JSON_VARIANT_MAGIC_EMPTY_STRING,
+#define JSON_VARIANT_MAGIC_EMPTY_STRING ((JsonVariant*) _JSON_VARIANT_MAGIC_EMPTY_STRING)
+ _JSON_VARIANT_MAGIC_EMPTY_ARRAY,
+#define JSON_VARIANT_MAGIC_EMPTY_ARRAY ((JsonVariant*) _JSON_VARIANT_MAGIC_EMPTY_ARRAY)
+ _JSON_VARIANT_MAGIC_EMPTY_OBJECT,
+#define JSON_VARIANT_MAGIC_EMPTY_OBJECT ((JsonVariant*) _JSON_VARIANT_MAGIC_EMPTY_OBJECT)
+ __JSON_VARIANT_MAGIC_MAX
+#define _JSON_VARIANT_MAGIC_MAX ((JsonVariant*) __JSON_VARIANT_MAGIC_MAX)
+};
/* This is only safe as long as we don't define more than 4K magic pointers, i.e. the page size of the simplest
* architectures we support. That's because we rely on the fact that malloc() will never allocate from the first memory
* page, as it is a faulting page for catching NULL pointer dereferences. */
-assert_cc((uintptr_t) _JSON_VARIANT_MAGIC_MAX < 4096U);
+assert_cc((unsigned) __JSON_VARIANT_MAGIC_MAX < 4096U);
enum { /* JSON tokens */
JSON_TOKEN_END,
#include "user-util.h"
#include "utf8.h"
-/* Refuse putting together variants with a larger depth than 4K by default (as a protection against overflowing stacks
+/* Refuse putting together variants with a larger depth than 2K by default (as a protection against overflowing stacks
* if code processes JSON objects recursively. Note that we store the depth in an uint16_t, hence make sure this
* remains under 2^16.
- * The value was 16k, but it was discovered to be too high on llvm/x86-64. See also the issue #10738. */
-#define DEPTH_MAX (4U*1024U)
+ *
+ * The value first was 16k, but it was discovered to be too high on llvm/x86-64. See also:
+ * https://github.com/systemd/systemd/issues/10738
+ *
+ * The value then was 4k, but it was discovered to be too high on s390x/aarch64. See also:
+ * https://github.com/systemd/systemd/issues/14396 */
+
+#define DEPTH_MAX (2U*1024U)
assert_cc(DEPTH_MAX <= UINT16_MAX);
typedef struct JsonSource {
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
n = json_variant_string(variant);
- if (!valid_user_group_name(n))
+ if (!valid_user_group_name_compat(n))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid user/group name.", strna(name));
r = free_and_strdup(s, n);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "libcrypt-util.h"
+#include "log.h"
+#include "macro.h"
+#include "missing_stdlib.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "strv.h"
+
+int make_salt(char **ret) {
+
+#ifdef XCRYPT_VERSION_MAJOR
+ const char *e;
+ char *salt;
+
+ /* If we have libxcrypt we default to the "preferred method" (i.e. usually yescrypt), and generate it
+ * with crypt_gensalt_ra(). */
+
+ e = secure_getenv("SYSTEMD_CRYPT_PREFIX");
+ if (!e)
+ e = crypt_preferred_method();
+
+ log_debug("Generating salt for hash prefix: %s", e);
+
+ salt = crypt_gensalt_ra(e, 0, NULL, 0);
+ if (!salt)
+ return -errno;
+
+ *ret = salt;
+ return 0;
+#else
+ /* If libxcrypt is not used, we use SHA512 and generate the salt on our own since crypt_gensalt_ra()
+ * is not available. */
+
+ static const char table[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "./";
+
+ uint8_t raw[16];
+ char *salt, *j;
+ size_t i;
+ int r;
+
+ /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
+ * SHA512, i.e. is legacy-free and minimizes our deps. */
+
+ assert_cc(sizeof(table) == 64U + 1U);
+
+ /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
+ r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
+ if (r < 0)
+ return r;
+
+ salt = new(char, 3+sizeof(raw)+1+1);
+ if (!salt)
+ return -ENOMEM;
+
+ /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
+ j = stpcpy(salt, "$6$");
+ for (i = 0; i < sizeof(raw); i++)
+ j[i] = table[raw[i] & 63];
+ j[i++] = '$';
+ j[i] = 0;
+
+ *ret = salt;
+ return 0;
+#endif
+}
+
+bool hashed_password_valid(const char *s) {
+
+ /* Returns true if the specified string is a 'valid' hashed UNIX password, i.e. if starts with '$' or
+ * with '!$' (the latter being a valid, yet locked password). */
+
+ if (isempty(s))
+ return false;
+
+ return STARTSWITH_SET(s, "$", "!$");
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if HAVE_CRYPT_H
+/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be
+ * removed from glibc at some point. As part of the removal, defines for
+ * crypt(3) are dropped from unistd.h, and we must include crypt.h instead.
+ *
+ * Newer versions of glibc (v2.0+) already ship crypt.h with a definition
+ * of crypt(3) as well, so we simply include it if it is present. MariaDB,
+ * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the
+ * same way since ages without any problems.
+ */
+#include <crypt.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+int make_salt(char **ret);
+
+bool hashed_password_valid(const char *s);
#include "id128-util.h"
#include "io-util.h"
#include "journal-internal.h"
+#include "journal-util.h"
#include "json.h"
#include "log.h"
#include "logs-show.h"
}
for (;;) {
- for (;;) {
- usec_t usec;
+ usec_t usec;
- if (need_seek) {
- r = sd_journal_next(j);
- if (r < 0)
- return log_error_errno(r, "Failed to iterate through journal: %m");
- }
+ if (need_seek) {
+ r = sd_journal_next(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to iterate through journal: %m");
+ }
- if (r == 0)
- break;
+ if (r == 0)
+ break;
- need_seek = true;
+ need_seek = true;
- if (not_before > 0) {
- r = sd_journal_get_monotonic_usec(j, &usec, NULL);
+ if (not_before > 0) {
+ r = sd_journal_get_monotonic_usec(j, &usec, NULL);
- /* -ESTALE is returned if the
- timestamp is not from this boot */
- if (r == -ESTALE)
- continue;
- else if (r < 0)
- return log_error_errno(r, "Failed to get journal time: %m");
+ /* -ESTALE is returned if the timestamp is not from this boot */
+ if (r == -ESTALE)
+ continue;
+ else if (r < 0)
+ return log_error_errno(r, "Failed to get journal time: %m");
- if (usec < not_before)
- continue;
- }
+ if (usec < not_before)
+ continue;
+ }
- line++;
- maybe_print_begin_newline(f, &flags);
+ line++;
+ maybe_print_begin_newline(f, &flags);
- r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
- if (r < 0)
- return r;
- }
+ r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
+ if (r < 0)
+ return r;
+ }
- if (warn_cutoff && line < how_many && not_before > 0) {
- sd_id128_t boot_id;
- usec_t cutoff = 0;
+ if (warn_cutoff && line < how_many && not_before > 0) {
+ sd_id128_t boot_id;
+ usec_t cutoff = 0;
- /* Check whether the cutoff line is too early */
+ /* Check whether the cutoff line is too early */
- r = sd_id128_get_boot(&boot_id);
- if (r < 0)
- return log_error_errno(r, "Failed to get boot id: %m");
+ r = sd_id128_get_boot(&boot_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get boot id: %m");
- r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to get journal cutoff time: %m");
+ r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get journal cutoff time: %m");
- if (r > 0 && not_before < cutoff) {
- maybe_print_begin_newline(f, &flags);
- fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
- }
+ if (r > 0 && not_before < cutoff) {
+ maybe_print_begin_newline(f, &flags);
- warn_cutoff = false;
- }
+ /* If we logged *something* and no permission error happened, than we can reliably
+ * emit the warning about rotation. If we didn't log anything and access errors
+ * happened, emit hint about permissions. Otherwise, give a generic message, since we
+ * can't diagnose the issue. */
- if (!(flags & OUTPUT_FOLLOW))
- break;
+ bool noaccess = journal_access_blocked(j);
- r = sd_journal_wait(j, USEC_INFINITY);
- if (r < 0)
- return log_error_errno(r, "Failed to wait for journal: %m");
+ if (line == 0 && noaccess)
+ fprintf(f, "Warning: some journal files were not opened due to insufficient permissions.");
+ else if (!noaccess)
+ fprintf(f, "Warning: journal has been rotated since unit was started, output may be incomplete.\n");
+ else
+ fprintf(f, "Warning: journal has been rotated since unit was started and some journal "
+ "files were not opened due to insufficient permissions, output may be incomplete.\n");
+ }
+ warn_cutoff = false;
}
return 0;
int show_journal_by_unit(
FILE *f,
const char *unit,
+ const char *log_namespace,
OutputMode mode,
unsigned n_columns,
usec_t not_before,
if (how_many <= 0)
return 0;
- r = sd_journal_open(&j, journal_open_flags);
+ r = sd_journal_open_namespace(&j, log_namespace, journal_open_flags | SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE);
if (r < 0)
return log_error_errno(r, "Failed to open journal: %m");
int show_journal_by_unit(
FILE *f,
const char *unit,
+ const char *namespace,
OutputMode mode,
unsigned n_columns,
usec_t not_before,
if (r < 0)
return r;
- r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
+ r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r < 0)
return r;
bus-unit-util.h
bus-util.c
bus-util.h
+ bus-polkit.c
+ bus-polkit.h
bus-wait-for-jobs.c
bus-wait-for-jobs.h
bus-wait-for-units.c
fstab-util.h
generator.c
generator.h
+ gpt.c
gpt.h
+ group-record-nss.c
+ group-record-nss.h
+ group-record-show.c
+ group-record-show.h
+ group-record.c
+ group-record.h
id128-print.c
id128-print.h
ima-util.c
json-internal.h
json.c
json.h
+ libcrypt-util.c
+ libcrypt-util.h
libmount-util.h
linux/auto_dev-ioctl.h
linux/bpf.h
uid-range.h
unit-file.c
unit-file.h
+ user-record-nss.c
+ user-record-nss.h
+ user-record-show.c
+ user-record-show.h
+ user-record.c
+ user-record.h
+ userdb.c
+ userdb.h
utmp-wtmp.h
varlink.c
varlink.h
shared_sources += files('module-util.c')
endif
+if conf.get('HAVE_PAM') == 1
+ shared_sources += files('''
+ pam-util.c
+ pam-util.h
+'''.split())
+endif
+
generate_ip_protocol_list = find_program('generate-ip-protocol-list.sh')
ip_protocol_list_txt = custom_target(
'ip-protocol-list.txt',
libacl,
libblkid,
libcap,
+ libcrypt,
libcryptsetup,
libgcrypt,
libidn,
libmount,
libopenssl,
libp11kit,
+ libpam,
librt,
libseccomp,
libselinux,
r = kmod_module_new_from_lookup(ctx, module, &modlist);
if (r < 0)
return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
- "Failed to lookup module alias '%s': %m", module);
+ "Failed to look up module alias '%s': %m", module);
if (!modlist) {
log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
return -ENOMEM;
}
- if (!s) {
- s = strdup("");
- if (!s)
- return -ENOMEM;
- }
-
*ret = TAKE_PTR(s);
return 0;
typedef enum OutputFlags {
OUTPUT_SHOW_ALL = 1 << 0,
- OUTPUT_FOLLOW = 1 << 1,
- OUTPUT_WARN_CUTOFF = 1 << 2,
- OUTPUT_FULL_WIDTH = 1 << 3,
- OUTPUT_COLOR = 1 << 4,
- OUTPUT_CATALOG = 1 << 5,
- OUTPUT_BEGIN_NEWLINE = 1 << 6,
- OUTPUT_UTC = 1 << 7,
- OUTPUT_KERNEL_THREADS = 1 << 8,
- OUTPUT_NO_HOSTNAME = 1 << 9,
+ OUTPUT_WARN_CUTOFF = 1 << 1,
+ OUTPUT_FULL_WIDTH = 1 << 2,
+ OUTPUT_COLOR = 1 << 3,
+ OUTPUT_CATALOG = 1 << 4,
+ OUTPUT_BEGIN_NEWLINE = 1 << 5,
+ OUTPUT_UTC = 1 << 6,
+ OUTPUT_KERNEL_THREADS = 1 << 7,
+ OUTPUT_NO_HOSTNAME = 1 << 8,
} OutputFlags;
JsonFormatFlags output_mode_to_json_format_flags(OutputMode m);
--- /dev/null
+#include <security/pam_ext.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "errno-util.h"
+#include "macro.h"
+#include "pam-util.h"
+
+int pam_log_oom(pam_handle_t *handle) {
+ /* This is like log_oom(), but uses PAM logging */
+ pam_syslog(handle, LOG_ERR, "Out of memory.");
+ return PAM_BUF_ERR;
+}
+
+int pam_bus_log_create_error(pam_handle_t *handle, int r) {
+ /* This is like bus_log_create_error(), but uses PAM logging */
+ pam_syslog(handle, LOG_ERR, "Failed to create bus message: %s", strerror_safe(r));
+ return PAM_BUF_ERR;
+}
+
+int pam_bus_log_parse_error(pam_handle_t *handle, int r) {
+ /* This is like bus_log_parse_error(), but uses PAM logging */
+ pam_syslog(handle, LOG_ERR, "Failed to parse bus message: %s", strerror_safe(r));
+ return PAM_BUF_ERR;
+}
+
+static void cleanup_system_bus(pam_handle_t *handle, void *data, int error_status) {
+ sd_bus_flush_close_unref(data);
+}
+
+int pam_acquire_bus_connection(pam_handle_t *handle, sd_bus **ret) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ assert(handle);
+ assert(ret);
+
+ /* We cache the bus connection so that we can share it between the session and the authentication hooks */
+ r = pam_get_data(handle, "systemd-system-bus", (const void**) &bus);
+ if (r == PAM_SUCCESS && bus) {
+ *ret = sd_bus_ref(TAKE_PTR(bus)); /* Increase the reference counter, so that the PAM data stays valid */
+ return PAM_SUCCESS;
+ }
+ if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+ pam_syslog(handle, LOG_ERR, "Failed to get bus connection: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0) {
+ pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
+ return PAM_SERVICE_ERR;
+ }
+
+ r = pam_set_data(handle, "systemd-system-bus", bus, cleanup_system_bus);
+ if (r != PAM_SUCCESS) {
+ pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
+ return r;
+ }
+
+ sd_bus_ref(bus);
+ *ret = TAKE_PTR(bus);
+
+ return PAM_SUCCESS;
+}
+
+int pam_release_bus_connection(pam_handle_t *handle) {
+ int r;
+
+ r = pam_set_data(handle, "systemd-system-bus", NULL, NULL);
+ if (r != PAM_SUCCESS)
+ pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r));
+
+ return r;
+}
+
+void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status) {
+ /* A generic destructor for pam_set_data() that just frees the specified data */
+ free(data);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <security/pam_modules.h>
+
+#include "sd-bus.h"
+
+int pam_log_oom(pam_handle_t *handle);
+int pam_bus_log_create_error(pam_handle_t *handle, int r);
+int pam_bus_log_parse_error(pam_handle_t *handle, int r);
+
+int pam_acquire_bus_connection(pam_handle_t *handle, sd_bus **ret);
+int pam_release_bus_connection(pam_handle_t *handle);
+
+void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status);
return 0;
}
+static int get_paths_from_environ(const char *var, char ***paths, bool *append) {
+ const char *e;
+ int r;
+
+ assert(var);
+ assert(paths);
+ assert(append);
+
+ *append = false;
+
+ e = getenv(var);
+ if (e) {
+ const char *k;
+
+ k = endswith(e, ":");
+ if (k) {
+ e = strndupa(e, k - e);
+ *append = true;
+ }
+
+ /* FIXME: empty components in other places should be rejected. */
+
+ r = path_split_and_make_absolute(e, paths);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int lookup_paths_init(
LookupPaths *p,
UnitFileScope scope,
*persistent_attached = NULL, *runtime_attached = NULL;
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
- const char *e;
int r;
assert(p);
return r;
/* First priority is whatever has been passed to us via env vars */
- e = getenv("SYSTEMD_UNIT_PATH");
- if (e) {
- const char *k;
-
- k = endswith(e, ":");
- if (k) {
- e = strndupa(e, k - e);
- append = true;
- }
-
- /* FIXME: empty components in other places should be rejected. */
-
- r = path_split_and_make_absolute(e, &paths);
- if (r < 0)
- return r;
- }
+ r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append);
+ if (r < 0)
+ return r;
if (!paths || append) {
/* Let's figure something out. */
}
char **generator_binary_paths(UnitFileScope scope) {
+ bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
+ _cleanup_strv_free_ char **paths = NULL;
+ int r;
- switch (scope) {
+ /* First priority is whatever has been passed to us via env vars */
+ r = get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths, &append);
+ if (r < 0)
+ return NULL;
- case UNIT_FILE_SYSTEM:
- return strv_new("/run/systemd/system-generators",
- "/etc/systemd/system-generators",
- "/usr/local/lib/systemd/system-generators",
- SYSTEM_GENERATOR_PATH);
+ if (!paths || append) {
+ _cleanup_strv_free_ char **add = NULL;
- case UNIT_FILE_GLOBAL:
- case UNIT_FILE_USER:
- return strv_new("/run/systemd/user-generators",
- "/etc/systemd/user-generators",
- "/usr/local/lib/systemd/user-generators",
- USER_GENERATOR_PATH);
+ switch (scope) {
- default:
- assert_not_reached("Hmm, unexpected scope.");
+ case UNIT_FILE_SYSTEM:
+ add = strv_new("/run/systemd/system-generators",
+ "/etc/systemd/system-generators",
+ "/usr/local/lib/systemd/system-generators",
+ SYSTEM_GENERATOR_PATH);
+ break;
+
+ case UNIT_FILE_GLOBAL:
+ case UNIT_FILE_USER:
+ add = strv_new("/run/systemd/user-generators",
+ "/etc/systemd/user-generators",
+ "/usr/local/lib/systemd/user-generators",
+ USER_GENERATOR_PATH);
+ break;
+
+ default:
+ assert_not_reached("Hmm, unexpected scope.");
+ }
+
+ if (!add)
+ return NULL;
+
+ if (paths) {
+ r = strv_extend_strv(&paths, add, true);
+ if (r < 0)
+ return NULL;
+ } else
+ /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
+ * and don't have to copy anything */
+ paths = TAKE_PTR(add);
}
+
+ return TAKE_PTR(paths);
+}
+
+char **env_generator_binary_paths(bool is_system) {
+ bool append = false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */
+ _cleanup_strv_free_ char **paths = NULL;
+ _cleanup_strv_free_ char **add = NULL;
+ int r;
+
+ /* First priority is whatever has been passed to us via env vars */
+ r = get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths, &append);
+ if (r < 0)
+ return NULL;
+
+ if (!paths || append) {
+ if (is_system)
+ add = strv_new("/run/systemd/system-environment-generators",
+ "/etc/systemd/system-environment-generators",
+ "/usr/local/lib/systemd/system-environment-generators",
+ SYSTEM_ENV_GENERATOR_PATH);
+ else
+ add = strv_new("/run/systemd/user-environment-generators",
+ "/etc/systemd/user-environment-generators",
+ "/usr/local/lib/systemd/user-environment-generators",
+ USER_ENV_GENERATOR_PATH);
+
+ if (!add)
+ return NULL;
+ }
+
+ if (paths) {
+ r = strv_extend_strv(&paths, add, true);
+ if (r < 0)
+ return NULL;
+ } else
+ /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
+ * and don't have to copy anything */
+ paths = TAKE_PTR(add);
+
+ return TAKE_PTR(paths);
}
void lookup_paths_free(LookupPaths *p);
char **generator_binary_paths(UnitFileScope scope);
+char **env_generator_binary_paths(bool is_system);
#include "fileio.h"
#include "log.h"
#include "macro.h"
+#include "path-util.h"
#include "string-util.h"
#include "sysctl-util.h"
char *n;
n = strpbrk(s, "/.");
+
/* If the first separator is a slash, the path is
* assumed to be normalized and slashes remain slashes
* and dots remains dots. */
- if (!n || *n == '/')
- return s;
-
- /* Otherwise, dots become slashes and slashes become
- * dots. Fun. */
- while (n) {
- if (*n == '.')
- *n = '/';
- else
- *n = '.';
-
- n = strpbrk(n + 1, "/.");
- }
+
+ if (n && *n == '.')
+ /* Dots become slashes and slashes become dots. Fun. */
+ do {
+ if (*n == '.')
+ *n = '/';
+ else
+ *n = '.';
+
+ n = strpbrk(n + 1, "/.");
+ } while (n);
+
+ path_simplify(s, true);
+
+ /* Kill the leading slash, but keep the first character of the string in the same place. */
+ if (*s == '/' && *(s+1))
+ memmove(s, s+1, strlen(s));
return s;
}
#include "macro.h"
#include "stdio-util.h"
-#include "util.h"
+#include "string-util.h"
char *sysctl_normalize(char *s);
int sysctl_read(const char *property, char **value);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "format-util.h"
+#include "libcrypt-util.h"
+#include "strv.h"
+#include "user-record-nss.h"
+
+#define SET_IF(field, condition, value, fallback) \
+ field = (condition) ? (value) : (fallback)
+
+int nss_passwd_to_user_record(
+ const struct passwd *pwd,
+ const struct spwd *spwd,
+ UserRecord **ret) {
+
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ int r;
+
+ assert(pwd);
+ assert(ret);
+
+ if (isempty(pwd->pw_name))
+ return -EINVAL;
+
+ if (spwd && !streq_ptr(spwd->sp_namp, pwd->pw_name))
+ return -EINVAL;
+
+ hr = user_record_new();
+ if (!hr)
+ return -ENOMEM;
+
+ r = free_and_strdup(&hr->user_name, pwd->pw_name);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&hr->real_name,
+ streq_ptr(pwd->pw_gecos, hr->user_name) ? NULL : empty_to_null(pwd->pw_gecos));
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&hr->home_directory, empty_to_null(pwd->pw_dir));
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup(&hr->shell, empty_to_null(pwd->pw_shell));
+ if (r < 0)
+ return r;
+
+ hr->uid = pwd->pw_uid;
+ hr->gid = pwd->pw_gid;
+
+ if (spwd && hashed_password_valid(spwd->sp_pwdp)) {
+ strv_free_erase(hr->hashed_password);
+ hr->hashed_password = strv_new(spwd->sp_pwdp);
+ if (!hr->hashed_password)
+ return -ENOMEM;
+ } else
+ hr->hashed_password = strv_free_erase(hr->hashed_password);
+
+ /* shadow-utils suggests using "chage -E 0" (or -E 1, depending on which man page you check)
+ * for locking a whole account, hence check for that. Note that it also defines a way to lock
+ * just a password instead of the whole account, but that's mostly pointless in times of
+ * password-less authorization, hence let's not bother. */
+
+ SET_IF(hr->locked,
+ spwd && spwd->sp_expire >= 0,
+ spwd->sp_expire <= 1, -1);
+
+ SET_IF(hr->not_after_usec,
+ spwd && spwd->sp_expire > 1 && (uint64_t) spwd->sp_expire < (UINT64_MAX-1)/USEC_PER_DAY,
+ spwd->sp_expire * USEC_PER_DAY, UINT64_MAX);
+
+ SET_IF(hr->password_change_now,
+ spwd && spwd->sp_lstchg >= 0,
+ spwd->sp_lstchg == 0, -1);
+
+ SET_IF(hr->last_password_change_usec,
+ spwd && spwd->sp_lstchg > 0 && (uint64_t) spwd->sp_lstchg <= (UINT64_MAX-1)/USEC_PER_DAY,
+ spwd->sp_lstchg * USEC_PER_DAY, UINT64_MAX);
+
+ SET_IF(hr->password_change_min_usec,
+ spwd && spwd->sp_min > 0 && (uint64_t) spwd->sp_min <= (UINT64_MAX-1)/USEC_PER_DAY,
+ spwd->sp_min * USEC_PER_DAY, UINT64_MAX);
+
+ SET_IF(hr->password_change_max_usec,
+ spwd && spwd->sp_max > 0 && (uint64_t) spwd->sp_max <= (UINT64_MAX-1)/USEC_PER_DAY,
+ spwd->sp_max * USEC_PER_DAY, UINT64_MAX);
+
+ SET_IF(hr->password_change_warn_usec,
+ spwd && spwd->sp_warn > 0 && (uint64_t) spwd->sp_warn <= (UINT64_MAX-1)/USEC_PER_DAY,
+ spwd->sp_warn * USEC_PER_DAY, UINT64_MAX);
+
+ SET_IF(hr->password_change_inactive_usec,
+ spwd && spwd->sp_inact > 0 && (uint64_t) spwd->sp_inact <= (UINT64_MAX-1)/USEC_PER_DAY,
+ spwd->sp_inact * USEC_PER_DAY, UINT64_MAX);
+
+ hr->json = json_variant_unref(hr->json);
+ r = json_build(&hr->json, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(hr->user_name)),
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(hr->uid)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(hr->gid)),
+ JSON_BUILD_PAIR_CONDITION(hr->real_name, "realName", JSON_BUILD_STRING(hr->real_name)),
+ JSON_BUILD_PAIR_CONDITION(hr->home_directory, "homeDirectory", JSON_BUILD_STRING(hr->home_directory)),
+ JSON_BUILD_PAIR_CONDITION(hr->shell, "shell", JSON_BUILD_STRING(hr->shell)),
+ JSON_BUILD_PAIR_CONDITION(!strv_isempty(hr->hashed_password), "privileged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRV(hr->hashed_password)))),
+ JSON_BUILD_PAIR_CONDITION(hr->locked >= 0, "locked", JSON_BUILD_BOOLEAN(hr->locked)),
+ JSON_BUILD_PAIR_CONDITION(hr->not_after_usec != UINT64_MAX, "notAfterUSec", JSON_BUILD_UNSIGNED(hr->not_after_usec)),
+ JSON_BUILD_PAIR_CONDITION(hr->password_change_now >= 0, "passwordChangeNow", JSON_BUILD_BOOLEAN(hr->password_change_now)),
+ JSON_BUILD_PAIR_CONDITION(hr->last_password_change_usec != UINT64_MAX, "lastPasswordChangeUSec", JSON_BUILD_UNSIGNED(hr->last_password_change_usec)),
+ JSON_BUILD_PAIR_CONDITION(hr->password_change_min_usec != UINT64_MAX, "passwordChangeMinUSec", JSON_BUILD_UNSIGNED(hr->password_change_min_usec)),
+ JSON_BUILD_PAIR_CONDITION(hr->password_change_max_usec != UINT64_MAX, "passwordChangeMaxUSec", JSON_BUILD_UNSIGNED(hr->password_change_max_usec)),
+ JSON_BUILD_PAIR_CONDITION(hr->password_change_warn_usec != UINT64_MAX, "passwordChangeWarnUSec", JSON_BUILD_UNSIGNED(hr->password_change_warn_usec)),
+ JSON_BUILD_PAIR_CONDITION(hr->password_change_inactive_usec != UINT64_MAX, "passwordChangeInactiveUSec", JSON_BUILD_UNSIGNED(hr->password_change_inactive_usec))));
+
+ if (r < 0)
+ return r;
+
+ hr->mask = USER_RECORD_REGULAR |
+ (!strv_isempty(hr->hashed_password) ? USER_RECORD_PRIVILEGED : 0);
+
+ *ret = TAKE_PTR(hr);
+ return 0;
+}
+
+int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer) {
+ size_t buflen = 4096;
+ int r;
+
+ assert(pwd);
+ assert(ret_spwd);
+ assert(ret_buffer);
+
+ for (;;) {
+ _cleanup_free_ char *buf = NULL;
+ struct spwd spwd, *result;
+
+ buf = malloc(buflen);
+ if (!buf)
+ return -ENOMEM;
+
+ r = getspnam_r(pwd->pw_name, &spwd, buf, buflen, &result);
+ if (r == 0) {
+ if (!result)
+ return -ESRCH;
+
+ *ret_spwd = *result;
+ *ret_buffer = TAKE_PTR(buf);
+ return 0;
+ }
+ if (r < 0)
+ return -EIO; /* Weird, this should not return negative! */
+ if (r != ERANGE)
+ return -r;
+
+ if (buflen > SIZE_MAX / 2)
+ return -ERANGE;
+
+ buflen *= 2;
+ buf = mfree(buf);
+ }
+}
+
+int nss_user_record_by_name(const char *name, UserRecord **ret) {
+ _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+ struct passwd pwd, *result;
+ bool incomplete = false;
+ size_t buflen = 4096;
+ struct spwd spwd;
+ int r;
+
+ assert(name);
+ assert(ret);
+
+ for (;;) {
+ buf = malloc(buflen);
+ if (!buf)
+ return -ENOMEM;
+
+ r = getpwnam_r(name, &pwd, buf, buflen, &result);
+ if (r == 0) {
+ if (!result)
+ return -ESRCH;
+
+ break;
+ }
+
+ if (r < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getpwnam_r() returned a negative value");
+ if (r != ERANGE)
+ return -r;
+
+ if (buflen > SIZE_MAX / 2)
+ return -ERANGE;
+
+ buflen *= 2;
+ buf = mfree(buf);
+ }
+
+ r = nss_spwd_for_passwd(result, &spwd, &sbuf);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to do shadow lookup for user %s, ignoring: %m", name);
+ incomplete = ERRNO_IS_PRIVILEGE(r);
+ }
+
+ r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret);
+ if (r < 0)
+ return r;
+
+ (*ret)->incomplete = incomplete;
+ return 0;
+}
+
+int nss_user_record_by_uid(uid_t uid, UserRecord **ret) {
+ _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+ struct passwd pwd, *result;
+ bool incomplete = false;
+ size_t buflen = 4096;
+ struct spwd spwd;
+ int r;
+
+ assert(ret);
+
+ for (;;) {
+ buf = malloc(buflen);
+ if (!buf)
+ return -ENOMEM;
+
+ r = getpwuid_r(uid, &pwd, buf, buflen, &result);
+ if (r == 0) {
+ if (!result)
+ return -ESRCH;
+
+ break;
+ }
+ if (r < 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getpwuid_r() returned a negative value");
+ if (r != ERANGE)
+ return -r;
+
+ if (buflen > SIZE_MAX / 2)
+ return -ERANGE;
+
+ buflen *= 2;
+ buf = mfree(buf);
+ }
+
+ r = nss_spwd_for_passwd(result, &spwd, &sbuf);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to do shadow lookup for UID " UID_FMT ", ignoring: %m", uid);
+ incomplete = ERRNO_IS_PRIVILEGE(r);
+ }
+
+ r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret);
+ if (r < 0)
+ return r;
+
+ (*ret)->incomplete = incomplete;
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <pwd.h>
+#include <shadow.h>
+
+#include "user-record.h"
+
+/* Synthesizes a UserRecord object from NSS data */
+
+int nss_passwd_to_user_record(const struct passwd *pwd, const struct spwd *spwd, UserRecord **ret);
+int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer);
+
+int nss_user_record_by_name(const char *name, UserRecord **ret);
+int nss_user_record_by_uid(uid_t uid, UserRecord **ret);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "fs-util.h"
+#include "group-record.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "user-record-show.h"
+#include "user-util.h"
+#include "userdb.h"
+
+const char *user_record_state_color(const char *state) {
+ if (STR_IN_SET(state, "unfixated", "absent"))
+ return ansi_grey();
+ else if (streq(state, "active"))
+ return ansi_highlight_green();
+ else if (streq(state, "locked"))
+ return ansi_highlight_yellow();
+
+ return NULL;
+}
+
+void user_record_show(UserRecord *hr, bool show_full_group_info) {
+ const char *hd, *ip, *shell;
+ UserStorage storage;
+ usec_t t;
+ size_t k;
+ int r, b;
+
+ printf(" User name: %s\n",
+ user_record_user_name_and_realm(hr));
+
+ if (hr->state) {
+ const char *color;
+
+ color = user_record_state_color(hr->state);
+
+ printf(" State: %s%s%s\n",
+ strempty(color), hr->state, color ? ansi_normal() : "");
+ }
+
+ printf(" Disposition: %s\n", user_disposition_to_string(user_record_disposition(hr)));
+
+ if (hr->last_change_usec != USEC_INFINITY) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), hr->last_change_usec));
+ }
+
+ if (hr->last_password_change_usec != USEC_INFINITY &&
+ hr->last_password_change_usec != hr->last_change_usec) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ printf(" Last Passw.: %s\n", format_timestamp(buf, sizeof(buf), hr->last_password_change_usec));
+ }
+
+ r = user_record_test_blocked(hr);
+ switch (r) {
+
+ case -ESTALE:
+ printf(" Login OK: %sno%s (last change time is in the future)\n", ansi_highlight_red(), ansi_normal());
+ break;
+
+ case -ENOLCK:
+ printf(" Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal());
+ break;
+
+ case -EL2HLT:
+ printf(" Login OK: %sno%s (record not valid yet))\n", ansi_highlight_red(), ansi_normal());
+ break;
+
+ case -EL3HLT:
+ printf(" Login OK: %sno%s (record not valid anymore))\n", ansi_highlight_red(), ansi_normal());
+ break;
+
+ default: {
+ usec_t y;
+
+ if (r < 0) {
+ errno = -r;
+ printf(" Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal());
+ break;
+ }
+
+ if (is_nologin_shell(user_record_shell(hr))) {
+ printf(" Login OK: %sno%s (nologin shell)\n", ansi_highlight_red(), ansi_normal());
+ break;
+ }
+
+ y = user_record_ratelimit_next_try(hr);
+ if (y != USEC_INFINITY && y > now(CLOCK_REALTIME)) {
+ printf(" Login OK: %sno%s (ratelimit)\n", ansi_highlight_red(), ansi_normal());
+ break;
+ }
+
+ printf(" Login OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
+ break;
+ }}
+
+ r = user_record_test_password_change_required(hr);
+ switch (r) {
+
+ case -EKEYREVOKED:
+ printf(" Password OK: %schange now%s\n", ansi_highlight_yellow(), ansi_normal());
+ break;
+
+ case -EOWNERDEAD:
+ printf(" Password OK: %sexpired%s (change now!)\n", ansi_highlight_yellow(), ansi_normal());
+ break;
+
+ case -EKEYREJECTED:
+ printf(" Password OK: %sexpired%s (for good)\n", ansi_highlight_red(), ansi_normal());
+ break;
+
+ case -EKEYEXPIRED:
+ printf(" Password OK: %sexpires soon%s\n", ansi_highlight_yellow(), ansi_normal());
+ break;
+
+ case -ENETDOWN:
+ printf(" Password OK: %sno timestamp%s\n", ansi_highlight_red(), ansi_normal());
+ break;
+
+ case -EROFS:
+ printf(" Password OK: %schange not permitted%s\n", ansi_highlight_yellow(), ansi_normal());
+ break;
+
+ default:
+ if (r < 0) {
+ errno = -r;
+ printf(" Password OK: %sno%s (%m)\n", ansi_highlight_yellow(), ansi_normal());
+ break;
+ }
+
+ printf(" Password OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
+ break;
+ }
+
+ if (uid_is_valid(hr->uid))
+ printf(" UID: " UID_FMT "\n", hr->uid);
+ if (gid_is_valid(hr->gid)) {
+ if (show_full_group_info) {
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+
+ r = groupdb_by_gid(hr->gid, 0, &gr);
+ if (r < 0) {
+ errno = -r;
+ printf(" GID: " GID_FMT " (unresolvable: %m)\n", hr->gid);
+ } else
+ printf(" GID: " GID_FMT " (%s)\n", hr->gid, gr->group_name);
+ } else
+ printf(" GID: " GID_FMT "\n", hr->gid);
+ } else if (uid_is_valid(hr->uid)) /* Show UID as GID if not separately configured */
+ printf(" GID: " GID_FMT "\n", (gid_t) hr->uid);
+
+ if (show_full_group_info) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+ r = membershipdb_by_user(hr->user_name, 0, &iterator);
+ if (r < 0) {
+ errno = -r;
+ printf(" Aux. Groups: (can't acquire: %m)\n");
+ } else {
+ const char *prefix = " Aux. Groups:";
+
+ for (;;) {
+ _cleanup_free_ char *group = NULL;
+
+ r = membershipdb_iterator_get(iterator, NULL, &group);
+ if (r == -ESRCH)
+ break;
+ if (r < 0) {
+ errno = -r;
+ printf("%s (can't iterate: %m)\n", prefix);
+ break;
+ }
+
+ printf("%s %s\n", prefix, group);
+ prefix = " ";
+ }
+ }
+ }
+
+ if (hr->real_name && !streq(hr->real_name, hr->user_name))
+ printf(" Real Name: %s\n", hr->real_name);
+
+ hd = user_record_home_directory(hr);
+ if (hd)
+ printf(" Directory: %s\n", hd);
+
+ storage = user_record_storage(hr);
+ if (storage >= 0) /* Let's be political, and clarify which storage we like, and which we don't. About CIFS we don't complain. */
+ printf(" Storage: %s%s\n", user_storage_to_string(storage),
+ storage == USER_LUKS ? " (strong encryption)" :
+ storage == USER_FSCRYPT ? " (weak encryption)" :
+ IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME) ? " (no encryption)" : "");
+
+ ip = user_record_image_path(hr);
+ if (ip && !streq_ptr(ip, hd))
+ printf(" Image Path: %s\n", ip);
+
+ b = user_record_removable(hr);
+ if (b >= 0)
+ printf(" Removable: %s\n", yes_no(b));
+
+ shell = user_record_shell(hr);
+ if (shell)
+ printf(" Shell: %s\n", shell);
+
+ if (hr->email_address)
+ printf(" Email: %s\n", hr->email_address);
+ if (hr->location)
+ printf(" Location: %s\n", hr->location);
+ if (hr->password_hint)
+ printf(" Passw. Hint: %s\n", hr->password_hint);
+ if (hr->icon_name)
+ printf(" Icon Name: %s\n", hr->icon_name);
+
+ if (hr->time_zone)
+ printf(" Time Zone: %s\n", hr->time_zone);
+
+ if (hr->preferred_language)
+ printf(" Language: %s\n", hr->preferred_language);
+
+ if (!strv_isempty(hr->environment)) {
+ char **i;
+
+ STRV_FOREACH(i, hr->environment) {
+ printf(i == hr->environment ?
+ " Environment: %s\n" :
+ " %s\n", *i);
+ }
+ }
+
+ if (hr->locked >= 0)
+ printf(" Locked: %s\n", yes_no(hr->locked));
+
+ if (hr->not_before_usec != UINT64_MAX) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ printf(" Not Before: %s\n", format_timestamp(buf, sizeof(buf), hr->not_before_usec));
+ }
+
+ if (hr->not_after_usec != UINT64_MAX) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ printf(" Not After: %s\n", format_timestamp(buf, sizeof(buf), hr->not_after_usec));
+ }
+
+ if (hr->umask != MODE_INVALID)
+ printf(" UMask: 0%03o\n", hr->umask);
+
+ if (nice_is_valid(hr->nice_level))
+ printf(" Nice: %i\n", hr->nice_level);
+
+ for (int j = 0; j < _RLIMIT_MAX; j++) {
+ if (hr->rlimits[j])
+ printf(" Limit: RLIMIT_%s=%" PRIu64 ":%" PRIu64 "\n",
+ rlimit_to_string(j), (uint64_t) hr->rlimits[j]->rlim_cur, (uint64_t) hr->rlimits[j]->rlim_max);
+ }
+
+ if (hr->tasks_max != UINT64_MAX)
+ printf(" Tasks Max: %" PRIu64 "\n", hr->tasks_max);
+
+ if (hr->memory_high != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf(" Memory High: %s\n", format_bytes(buf, sizeof(buf), hr->memory_high));
+ }
+
+ if (hr->memory_max != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf(" Memory Max: %s\n", format_bytes(buf, sizeof(buf), hr->memory_max));
+ }
+
+ if (hr->cpu_weight != UINT64_MAX)
+ printf(" CPU Weight: %" PRIu64 "\n", hr->cpu_weight);
+
+ if (hr->io_weight != UINT64_MAX)
+ printf(" IO Weight: %" PRIu64 "\n", hr->io_weight);
+
+ if (hr->access_mode != MODE_INVALID)
+ printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr));
+
+ if (storage == USER_LUKS) {
+ printf("LUKS Discard: %s\n", yes_no(user_record_luks_discard(hr)));
+
+ if (!sd_id128_is_null(hr->luks_uuid))
+ printf(" LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid));
+ if (!sd_id128_is_null(hr->partition_uuid))
+ printf(" Part UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->partition_uuid));
+ if (!sd_id128_is_null(hr->file_system_uuid))
+ printf(" FS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->file_system_uuid));
+
+ if (hr->file_system_type)
+ printf(" File System: %s\n", user_record_file_system_type(hr));
+
+ if (hr->luks_cipher)
+ printf(" LUKS Cipher: %s\n", hr->luks_cipher);
+ if (hr->luks_cipher_mode)
+ printf(" Cipher Mode: %s\n", hr->luks_cipher_mode);
+ if (hr->luks_volume_key_size != UINT64_MAX)
+ printf(" Volume Key: %" PRIu64 "bit\n", hr->luks_volume_key_size * 8);
+
+ if (hr->luks_pbkdf_type)
+ printf(" PBKDF Type: %s\n", hr->luks_pbkdf_type);
+ if (hr->luks_pbkdf_hash_algorithm)
+ printf(" PBKDF Hash: %s\n", hr->luks_pbkdf_hash_algorithm);
+ if (hr->luks_pbkdf_time_cost_usec != UINT64_MAX) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ printf(" PBKDF Time: %s\n", format_timespan(buf, sizeof(buf), hr->luks_pbkdf_time_cost_usec, 0));
+ }
+ if (hr->luks_pbkdf_memory_cost != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf(" PBKDF Bytes: %s\n", format_bytes(buf, sizeof(buf), hr->luks_pbkdf_memory_cost));
+ }
+ if (hr->luks_pbkdf_parallel_threads != UINT64_MAX)
+ printf("PBKDF Thread: %" PRIu64 "\n", hr->luks_pbkdf_parallel_threads);
+
+ } else if (storage == USER_CIFS) {
+
+ if (hr->cifs_service)
+ printf("CIFS Service: %s\n", hr->cifs_service);
+ }
+
+ if (hr->cifs_user_name)
+ printf(" CIFS User: %s\n", user_record_cifs_user_name(hr));
+ if (hr->cifs_domain)
+ printf(" CIFS Domain: %s\n", hr->cifs_domain);
+
+ if (storage != USER_CLASSIC)
+ printf(" Mount Flags: %s %s %s\n",
+ hr->nosuid ? "nosuid" : "suid",
+ hr->nodev ? "nodev" : "dev",
+ hr->noexec ? "noexec" : "exec");
+
+ if (hr->skeleton_directory)
+ printf(" Skel. Dir.: %s\n", user_record_skeleton_directory(hr));
+
+ if (hr->disk_size != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf(" Disk Size: %s\n", format_bytes(buf, sizeof(buf), hr->disk_size));
+ }
+
+ if (hr->disk_usage != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf(" Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage));
+ }
+
+ if (hr->disk_free != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf(" Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free));
+ }
+
+ if (hr->disk_floor != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf(" Disk Floor: %s\n", format_bytes(buf, sizeof(buf), hr->disk_floor));
+ }
+
+ if (hr->disk_ceiling != UINT64_MAX) {
+ char buf[FORMAT_BYTES_MAX];
+ printf("Disk Ceiling: %s\n", format_bytes(buf, sizeof(buf), hr->disk_ceiling));
+ }
+
+ if (hr->good_authentication_counter != UINT64_MAX)
+ printf(" Good Auth.: %" PRIu64 "\n", hr->good_authentication_counter);
+
+ if (hr->last_good_authentication_usec != UINT64_MAX) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ printf(" Last Good: %s\n", format_timestamp(buf, sizeof(buf), hr->last_good_authentication_usec));
+ }
+
+ if (hr->bad_authentication_counter != UINT64_MAX)
+ printf(" Bad Auth.: %" PRIu64 "\n", hr->bad_authentication_counter);
+
+ if (hr->last_bad_authentication_usec != UINT64_MAX) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ printf(" Last Bad: %s\n", format_timestamp(buf, sizeof(buf), hr->last_bad_authentication_usec));
+ }
+
+ t = user_record_ratelimit_next_try(hr);
+ if (t != USEC_INFINITY) {
+ usec_t n = now(CLOCK_REALTIME);
+
+ if (t <= n)
+ printf(" Next Try: anytime\n");
+ else {
+ char buf[FORMAT_TIMESPAN_MAX];
+ printf(" Next Try: %sin %s%s\n",
+ ansi_highlight_red(),
+ format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC),
+ ansi_normal());
+ }
+ }
+
+ if (storage != USER_CLASSIC) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ printf(" Auth. Limit: %" PRIu64 " attempts per %s\n", user_record_ratelimit_burst(hr),
+ format_timespan(buf, sizeof(buf), user_record_ratelimit_interval_usec(hr), 0));
+ }
+
+ if (hr->enforce_password_policy >= 0)
+ printf(" Passwd Pol.: %s\n", yes_no(hr->enforce_password_policy));
+
+ if (hr->password_change_min_usec != UINT64_MAX ||
+ hr->password_change_max_usec != UINT64_MAX ||
+ hr->password_change_warn_usec != UINT64_MAX ||
+ hr->password_change_inactive_usec != UINT64_MAX) {
+
+ char buf[FORMAT_TIMESPAN_MAX];
+ printf(" Passwd Chg.:");
+
+ if (hr->password_change_min_usec != UINT64_MAX) {
+ printf(" min %s", format_timespan(buf, sizeof(buf), hr->password_change_min_usec, 0));
+
+ if (hr->password_change_max_usec != UINT64_MAX)
+ printf(" …");
+ }
+
+ if (hr->password_change_max_usec != UINT64_MAX)
+ printf(" max %s", format_timespan(buf, sizeof(buf), hr->password_change_max_usec, 0));
+
+ if (hr->password_change_warn_usec != UINT64_MAX)
+ printf("/warn %s", format_timespan(buf, sizeof(buf), hr->password_change_warn_usec, 0));
+
+ if (hr->password_change_inactive_usec != UINT64_MAX)
+ printf("/inactive %s", format_timespan(buf, sizeof(buf), hr->password_change_inactive_usec, 0));
+
+ printf("\n");
+ }
+
+ if (hr->password_change_now >= 0)
+ printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now));
+
+ if (!strv_isempty(hr->ssh_authorized_keys))
+ printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));
+
+ if (!strv_isempty(hr->pkcs11_token_uri)) {
+ char **i;
+
+ STRV_FOREACH(i, hr->pkcs11_token_uri)
+ printf(i == hr->pkcs11_token_uri ?
+ " Sec. Token: %s\n" :
+ " %s\n", *i);
+ }
+
+ k = strv_length(hr->hashed_password);
+ if (k == 0)
+ printf(" Passwords: %snone%s\n",
+ user_record_disposition(hr) == USER_REGULAR ? ansi_highlight_yellow() : ansi_normal(), ansi_normal());
+ else
+ printf(" Passwords: %zu\n", k);
+
+ if (hr->signed_locally >= 0)
+ printf(" Local Sig.: %s\n", yes_no(hr->signed_locally));
+
+ if (hr->stop_delay_usec != UINT64_MAX) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ printf(" Stop Delay: %s\n", format_timespan(buf, sizeof(buf), hr->stop_delay_usec, 0));
+ }
+
+ if (hr->auto_login >= 0)
+ printf("Autom. Login: %s\n", yes_no(hr->auto_login));
+
+ if (hr->kill_processes >= 0)
+ printf(" Kill Proc.: %s\n", yes_no(hr->kill_processes));
+
+ if (hr->service)
+ printf(" Service: %s\n", hr->service);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "user-record.h"
+
+const char *user_record_state_color(const char *state);
+
+void user_record_show(UserRecord *hr, bool show_full_group_info);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mount.h>
+
+#include "cgroup-util.h"
+#include "dns-domain.h"
+#include "env-util.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "hostname-util.h"
+#include "memory-util.h"
+#include "path-util.h"
+#include "pkcs11-util.h"
+#include "rlimit-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "strv.h"
+#include "user-record.h"
+#include "user-util.h"
+
+#define DEFAULT_RATELIMIT_BURST 30
+#define DEFAULT_RATELIMIT_INTERVAL_USEC (1*USEC_PER_MINUTE)
+
+UserRecord* user_record_new(void) {
+ UserRecord *h;
+
+ h = new(UserRecord, 1);
+ if (!h)
+ return NULL;
+
+ *h = (UserRecord) {
+ .n_ref = 1,
+ .disposition = _USER_DISPOSITION_INVALID,
+ .last_change_usec = UINT64_MAX,
+ .last_password_change_usec = UINT64_MAX,
+ .umask = MODE_INVALID,
+ .nice_level = INT_MAX,
+ .not_before_usec = UINT64_MAX,
+ .not_after_usec = UINT64_MAX,
+ .locked = -1,
+ .storage = _USER_STORAGE_INVALID,
+ .access_mode = MODE_INVALID,
+ .disk_size = UINT64_MAX,
+ .disk_size_relative = UINT64_MAX,
+ .tasks_max = UINT64_MAX,
+ .memory_high = UINT64_MAX,
+ .memory_max = UINT64_MAX,
+ .cpu_weight = UINT64_MAX,
+ .io_weight = UINT64_MAX,
+ .uid = UID_INVALID,
+ .gid = GID_INVALID,
+ .nodev = true,
+ .nosuid = true,
+ .luks_discard = -1,
+ .luks_volume_key_size = UINT64_MAX,
+ .luks_pbkdf_time_cost_usec = UINT64_MAX,
+ .luks_pbkdf_memory_cost = UINT64_MAX,
+ .luks_pbkdf_parallel_threads = UINT64_MAX,
+ .disk_usage = UINT64_MAX,
+ .disk_free = UINT64_MAX,
+ .disk_ceiling = UINT64_MAX,
+ .disk_floor = UINT64_MAX,
+ .signed_locally = -1,
+ .good_authentication_counter = UINT64_MAX,
+ .bad_authentication_counter = UINT64_MAX,
+ .last_good_authentication_usec = UINT64_MAX,
+ .last_bad_authentication_usec = UINT64_MAX,
+ .ratelimit_begin_usec = UINT64_MAX,
+ .ratelimit_count = UINT64_MAX,
+ .ratelimit_interval_usec = UINT64_MAX,
+ .ratelimit_burst = UINT64_MAX,
+ .removable = -1,
+ .enforce_password_policy = -1,
+ .auto_login = -1,
+ .stop_delay_usec = UINT64_MAX,
+ .kill_processes = -1,
+ .password_change_min_usec = UINT64_MAX,
+ .password_change_max_usec = UINT64_MAX,
+ .password_change_warn_usec = UINT64_MAX,
+ .password_change_inactive_usec = UINT64_MAX,
+ .password_change_now = -1,
+ .pkcs11_protected_authentication_path_permitted = -1,
+ };
+
+ return h;
+}
+
+static void pkcs11_encrypted_key_done(Pkcs11EncryptedKey *k) {
+ if (!k)
+ return;
+
+ free(k->uri);
+ erase_and_free(k->data);
+ erase_and_free(k->hashed_password);
+}
+
+static UserRecord* user_record_free(UserRecord *h) {
+ if (!h)
+ return NULL;
+
+ free(h->user_name);
+ free(h->realm);
+ free(h->user_name_and_realm_auto);
+ free(h->real_name);
+ free(h->email_address);
+ erase_and_free(h->password_hint);
+ free(h->location);
+ free(h->icon_name);
+
+ free(h->shell);
+
+ strv_free(h->environment);
+ free(h->time_zone);
+ free(h->preferred_language);
+ rlimit_free_all(h->rlimits);
+
+ free(h->skeleton_directory);
+
+ strv_free_erase(h->hashed_password);
+ strv_free_erase(h->ssh_authorized_keys);
+ strv_free_erase(h->password);
+ strv_free_erase(h->pkcs11_pin);
+
+ free(h->cifs_service);
+ free(h->cifs_user_name);
+ free(h->cifs_domain);
+
+ free(h->image_path);
+ free(h->image_path_auto);
+ free(h->home_directory);
+ free(h->home_directory_auto);
+
+ strv_free(h->member_of);
+
+ free(h->file_system_type);
+ free(h->luks_cipher);
+ free(h->luks_cipher_mode);
+ free(h->luks_pbkdf_hash_algorithm);
+ free(h->luks_pbkdf_type);
+
+ free(h->state);
+ free(h->service);
+
+ strv_free(h->pkcs11_token_uri);
+ for (size_t i = 0; i < h->n_pkcs11_encrypted_key; i++)
+ pkcs11_encrypted_key_done(h->pkcs11_encrypted_key + i);
+ free(h->pkcs11_encrypted_key);
+
+ json_variant_unref(h->json);
+
+ return mfree(h);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(UserRecord, user_record, user_record_free);
+
+int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ const char *n;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ n = json_variant_string(variant);
+ r = dns_name_is_valid(n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to check if JSON field '%s' is a valid DNS domain.", strna(name));
+ if (r == 0)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid DNS domain.", strna(name));
+
+ r = free_and_strdup(s, n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+static int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ const char *n;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ n = json_variant_string(variant);
+ if (!valid_gecos(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible real name.", strna(name));
+
+ r = free_and_strdup(s, n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+static int json_dispatch_nice(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ int *nl = userdata;
+ intmax_t m;
+
+ if (json_variant_is_null(variant)) {
+ *nl = INT_MAX;
+ return 0;
+ }
+
+ if (!json_variant_is_integer(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ m = json_variant_integer(variant);
+ if (m < PRIO_MIN || m >= PRIO_MAX)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not a valid nice level.", strna(name));
+
+ *nl = m;
+ return 0;
+}
+
+static int json_dispatch_rlimit_value(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ rlim_t *ret = userdata;
+
+ if (json_variant_is_null(variant))
+ *ret = RLIM_INFINITY;
+ else if (json_variant_is_unsigned(variant)) {
+ uintmax_t w;
+
+ w = json_variant_unsigned(variant);
+ if (w == RLIM_INFINITY || (uintmax_t) w != json_variant_unsigned(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "Resource limit value '%s' is out of range.", name);
+
+ *ret = (rlim_t) w;
+ } else
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit value '%s' is not an unsigned integer.", name);
+
+ return 0;
+}
+
+static int json_dispatch_rlimits(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ struct rlimit** limits = userdata;
+ JsonVariant *value;
+ const char *key;
+ int r;
+
+ assert_se(limits);
+
+ if (json_variant_is_null(variant)) {
+ rlimit_free_all(limits);
+ return 0;
+ }
+
+ if (!json_variant_is_object(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+ JSON_VARIANT_OBJECT_FOREACH(key, value, variant) {
+ JsonVariant *jcur, *jmax;
+ struct rlimit rl;
+ const char *p;
+ int l;
+
+ p = startswith(key, "RLIMIT_");
+ if (!p)
+ l = -1;
+ else
+ l = rlimit_from_string(p);
+ if (l < 0)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' not known.", key);
+
+ if (!json_variant_is_object(value))
+ return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' has invalid value.", key);
+
+ if (json_variant_elements(value) != 4)
+ return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' value is does not have two fields as expected.", key);
+
+ jcur = json_variant_by_key(value, "cur");
+ if (!jcur)
+ return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'cur' field.", key);
+ r = json_dispatch_rlimit_value("cur", jcur, flags, &rl.rlim_cur);
+ if (r < 0)
+ return r;
+
+ jmax = json_variant_by_key(value, "max");
+ if (!jmax)
+ return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'max' field.", key);
+ r = json_dispatch_rlimit_value("max", jmax, flags, &rl.rlim_max);
+ if (r < 0)
+ return r;
+
+ if (limits[l])
+ *(limits[l]) = rl;
+ else {
+ limits[l] = newdup(struct rlimit, &rl, 1);
+ if (!limits[l])
+ return log_oom();
+ }
+ }
+
+ return 0;
+}
+
+static int json_dispatch_filename_or_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ const char *n;
+ int r;
+
+ assert(s);
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ n = json_variant_string(variant);
+ if (!filename_is_valid(n) && !path_is_normalized(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid file name or normalized path.", strna(name));
+
+ r = free_and_strdup(s, n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+static int json_dispatch_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ const char *n;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ n = json_variant_string(variant);
+ if (!path_is_normalized(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a normalized file system path.", strna(name));
+ if (!path_is_absolute(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name));
+
+ r = free_and_strdup(s, n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+static int json_dispatch_home_directory(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ const char *n;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ n = json_variant_string(variant);
+ if (!valid_home(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid home directory path.", strna(name));
+
+ r = free_and_strdup(s, n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+static int json_dispatch_image_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ const char *n;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ n = json_variant_string(variant);
+ if (empty_or_root(n) || !path_is_valid(n) || !path_is_absolute(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid image path.", strna(name));
+
+ r = free_and_strdup(s, n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+static int json_dispatch_umask(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ mode_t *m = userdata;
+ uintmax_t k;
+
+ if (json_variant_is_null(variant)) {
+ *m = (mode_t) -1;
+ return 0;
+ }
+
+ if (!json_variant_is_unsigned(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
+
+ k = json_variant_unsigned(variant);
+ if (k > 0777)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' outside of valid range 0…0777.", strna(name));
+
+ *m = (mode_t) k;
+ return 0;
+}
+
+static int json_dispatch_access_mode(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ mode_t *m = userdata;
+ uintmax_t k;
+
+ if (json_variant_is_null(variant)) {
+ *m = (mode_t) -1;
+ return 0;
+ }
+
+ if (!json_variant_is_unsigned(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
+
+ k = json_variant_unsigned(variant);
+ if (k > 07777)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' outside of valid range 0…07777.", strna(name));
+
+ *m = (mode_t) k;
+ return 0;
+}
+
+static int json_dispatch_environment(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ _cleanup_strv_free_ char **n = NULL;
+ char ***l = userdata;
+ size_t i;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *l = strv_free(*l);
+ return 0;
+ }
+
+ if (!json_variant_is_array(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+ for (i = 0; i < json_variant_elements(variant); i++) {
+ _cleanup_free_ char *c = NULL;
+ JsonVariant *e;
+ const char *a;
+
+ e = json_variant_by_index(variant, i);
+ if (!json_variant_is_string(e))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
+
+ assert_se(a = json_variant_string(e));
+
+ if (!env_assignment_is_valid(a))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of environment variables.", strna(name));
+
+ c = strdup(a);
+ if (!c)
+ return json_log_oom(variant, flags);
+
+ r = strv_env_replace(&n, c);
+ if (r < 0)
+ return json_log_oom(variant, flags);
+
+ c = NULL;
+ }
+
+ strv_free_and_replace(*l, n);
+ return 0;
+}
+
+int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ UserDisposition *disposition = userdata, k;
+
+ if (json_variant_is_null(variant)) {
+ *disposition = _USER_DISPOSITION_INVALID;
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ k = user_disposition_from_string(json_variant_string(variant));
+ if (k < 0)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Disposition type '%s' not known.", json_variant_string(variant));
+
+ *disposition = k;
+ return 0;
+}
+
+static int json_dispatch_storage(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ UserStorage *storage = userdata, k;
+
+ if (json_variant_is_null(variant)) {
+ *storage = _USER_STORAGE_INVALID;
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ k = user_storage_from_string(json_variant_string(variant));
+ if (k < 0)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Storage type '%s' not known.", json_variant_string(variant));
+
+ *storage = k;
+ return 0;
+}
+
+static int json_dispatch_disk_size(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ uint64_t *size = userdata;
+ uintmax_t k;
+
+ if (json_variant_is_null(variant)) {
+ *size = UINT64_MAX;
+ return 0;
+ }
+
+ if (!json_variant_is_unsigned(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
+
+ k = json_variant_unsigned(variant);
+ if (k < USER_DISK_SIZE_MIN || k > USER_DISK_SIZE_MAX)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), USER_DISK_SIZE_MIN, USER_DISK_SIZE_MAX);
+
+ *size = k;
+ return 0;
+}
+
+static int json_dispatch_tasks_or_memory_max(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ uint64_t *limit = userdata;
+ uintmax_t k;
+
+ if (json_variant_is_null(variant)) {
+ *limit = UINT64_MAX;
+ return 0;
+ }
+
+ if (!json_variant_is_unsigned(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a integer.", strna(name));
+
+ k = json_variant_unsigned(variant);
+ if (k <= 0 || k >= UINT64_MAX)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), (uint64_t) 1, UINT64_MAX-1);
+
+ *limit = k;
+ return 0;
+}
+
+static int json_dispatch_weight(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ uint64_t *weight = userdata;
+ uintmax_t k;
+
+ if (json_variant_is_null(variant)) {
+ *weight = UINT64_MAX;
+ return 0;
+ }
+
+ if (!json_variant_is_unsigned(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a integer.", strna(name));
+
+ k = json_variant_unsigned(variant);
+ if (k <= CGROUP_WEIGHT_MIN || k >= CGROUP_WEIGHT_MAX)
+ return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), (uint64_t) CGROUP_WEIGHT_MIN, (uint64_t) CGROUP_WEIGHT_MAX);
+
+ *weight = k;
+ return 0;
+}
+
+int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ _cleanup_strv_free_ char **l = NULL;
+ char ***list = userdata;
+ JsonVariant *e;
+ int r;
+
+ if (!json_variant_is_array(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
+
+ JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+
+ if (!json_variant_is_string(e))
+ return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
+
+ if (!valid_user_group_name_compat(json_variant_string(e)))
+ return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a valid user/group name: %s", json_variant_string(e));
+
+ r = strv_extend(&l, json_variant_string(e));
+ if (r < 0)
+ return json_log(e, flags, r, "Failed to append array element: %m");
+ }
+
+ r = strv_extend_strv(list, l, true);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to merge user/group arrays: %m");
+
+ return 0;
+}
+
+static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch secret_dispatch_table[] = {
+ { "password", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, password), 0 },
+ { "pkcs11Pin", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, pkcs11_pin), 0 },
+ { "pkcs11ProtectedAuthenticationPathPermitted", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
+ {},
+ };
+
+ return json_dispatch(variant, secret_dispatch_table, NULL, flags, userdata);
+}
+
+static int dispatch_pkcs11_uri(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ char **s = userdata;
+ const char *n;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *s = mfree(*s);
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ n = json_variant_string(variant);
+ if (!pkcs11_uri_valid(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
+
+ r = free_and_strdup(s, n);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+ return 0;
+}
+
+static int dispatch_pkcs11_uri_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ _cleanup_strv_free_ char **z = NULL;
+ char ***l = userdata;
+ JsonVariant *e;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ *l = strv_free(*l);
+ return 0;
+ }
+
+ if (json_variant_is_string(variant)) {
+ const char *n;
+
+ n = json_variant_string(variant);
+ if (!pkcs11_uri_valid(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
+
+ z = strv_new(n);
+ if (!z)
+ return log_oom();
+
+ } else {
+
+ if (!json_variant_is_array(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string or array of strings.", strna(name));
+
+ JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+ const char *n;
+
+ if (!json_variant_is_string(e))
+ return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
+
+ n = json_variant_string(e);
+ if (!pkcs11_uri_valid(n))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element in '%s' is not a valid RFC7512 PKCS#11 URI: %s", strna(name), n);
+
+ r = strv_extend(&z, n);
+ if (r < 0)
+ return log_oom();
+ }
+ }
+
+ strv_free_and_replace(*l, z);
+ return 0;
+}
+
+static int dispatch_pkcs11_key_data(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ Pkcs11EncryptedKey *k = userdata;
+ size_t l;
+ void *b;
+ int r;
+
+ if (json_variant_is_null(variant)) {
+ k->data = mfree(k->data);
+ k->size = 0;
+ return 0;
+ }
+
+ if (!json_variant_is_string(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+ r = unbase64mem(json_variant_string(variant), (size_t) -1, &b, &l);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to decode encrypted PKCS#11 key: %m");
+
+ erase_and_free(k->data);
+ k->data = b;
+ k->size = l;
+
+ return 0;
+}
+
+static int dispatch_pkcs11_key(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ UserRecord *h = userdata;
+ JsonVariant *e;
+ int r;
+
+ if (!json_variant_is_array(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
+
+ JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+ Pkcs11EncryptedKey *array, *k;
+
+ static const JsonDispatch pkcs11_key_dispatch_table[] = {
+ { "uri", JSON_VARIANT_STRING, dispatch_pkcs11_uri, offsetof(Pkcs11EncryptedKey, uri), JSON_MANDATORY },
+ { "data", JSON_VARIANT_STRING, dispatch_pkcs11_key_data, 0, JSON_MANDATORY },
+ { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string, offsetof(Pkcs11EncryptedKey, hashed_password), JSON_MANDATORY },
+ {},
+ };
+
+ if (!json_variant_is_object(e))
+ return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
+
+ array = reallocarray(h->pkcs11_encrypted_key, h->n_pkcs11_encrypted_key + 1, sizeof(Pkcs11EncryptedKey));
+ if (!array)
+ return log_oom();
+
+ h->pkcs11_encrypted_key = array;
+ k = h->pkcs11_encrypted_key + h->n_pkcs11_encrypted_key;
+ *k = (Pkcs11EncryptedKey) {};
+
+ r = json_dispatch(e, pkcs11_key_dispatch_table, NULL, flags, k);
+ if (r < 0) {
+ pkcs11_encrypted_key_done(k);
+ return r;
+ }
+
+ h->n_pkcs11_encrypted_key++;
+ }
+
+ return 0;
+}
+
+static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch privileged_dispatch_table[] = {
+ { "passwordHint", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, password_hint), 0 },
+ { "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, hashed_password), JSON_SAFE },
+ { "sshAuthorizedKeys", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, ssh_authorized_keys), 0 },
+ { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY, dispatch_pkcs11_key, 0, 0 },
+ {},
+ };
+
+ return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata);
+}
+
+static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch binding_dispatch_table[] = {
+ { "imagePath", JSON_VARIANT_STRING, json_dispatch_image_path, offsetof(UserRecord, image_path), 0 },
+ { "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 },
+ { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
+ { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
+ { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
+ { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 },
+ { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
+ { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
+ { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
+ { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
+ {},
+ };
+
+ char smid[SD_ID128_STRING_MAX];
+ JsonVariant *m;
+ sd_id128_t mid;
+ int r;
+
+ if (!variant)
+ return 0;
+
+ if (!json_variant_is_object(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+ m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ if (!m)
+ return 0;
+
+ return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata);
+}
+
+int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags) {
+ sd_id128_t mid;
+ int r;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return json_log(ids, flags, r, "Failed to acquire machine ID: %m");
+
+ if (json_variant_is_string(ids)) {
+ sd_id128_t k;
+
+ r = sd_id128_from_string(json_variant_string(ids), &k);
+ if (r < 0) {
+ json_log(ids, flags, r, "%s is not a valid machine ID, ignoring: %m", json_variant_string(ids));
+ return 0;
+ }
+
+ return sd_id128_equal(mid, k);
+ }
+
+ if (json_variant_is_array(ids)) {
+ JsonVariant *e;
+
+ JSON_VARIANT_ARRAY_FOREACH(e, ids) {
+ sd_id128_t k;
+
+ if (!json_variant_is_string(e)) {
+ json_log(e, flags, 0, "Machine ID is not a string, ignoring: %m");
+ continue;
+ }
+
+ r = sd_id128_from_string(json_variant_string(e), &k);
+ if (r < 0) {
+ json_log(e, flags, r, "%s is not a valid machine ID, ignoring: %m", json_variant_string(e));
+ continue;
+ }
+
+ if (sd_id128_equal(mid, k))
+ return true;
+ }
+
+ return false;
+ }
+
+ json_log(ids, flags, 0, "Machine ID is not a string or array of strings, ignoring: %m");
+ return false;
+}
+
+int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags) {
+ _cleanup_free_ char *hn = NULL;
+ int r;
+
+ r = gethostname_strict(&hn);
+ if (r == -ENXIO) {
+ json_log(hns, flags, r, "No hostname set, not matching perMachine hostname record: %m");
+ return false;
+ }
+ if (r < 0)
+ return json_log(hns, flags, r, "Failed to acquire hostname: %m");
+
+ if (json_variant_is_string(hns))
+ return streq(json_variant_string(hns), hn);
+
+ if (json_variant_is_array(hns)) {
+ JsonVariant *e;
+
+ JSON_VARIANT_ARRAY_FOREACH(e, hns) {
+
+ if (!json_variant_is_string(e)) {
+ json_log(e, flags, 0, "Hostname is not a string, ignoring: %m");
+ continue;
+ }
+
+ if (streq(json_variant_string(hns), hn))
+ return true;
+ }
+
+ return false;
+ }
+
+ json_log(hns, flags, 0, "Hostname is not a string or array of strings, ignoring: %m");
+ return false;
+}
+
+static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch per_machine_dispatch_table[] = {
+ { "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
+ { "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
+ { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE },
+ { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 },
+ { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
+ { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
+ { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 },
+ { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE },
+ { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE },
+ { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 },
+ { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 },
+ { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 },
+ { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 },
+ { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 },
+ { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 },
+ { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 },
+ { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
+ { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 },
+ { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
+ { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
+ { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
+ { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },
+ { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 },
+ { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 },
+ { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 },
+ { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 },
+ { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 },
+ { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE },
+ { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE },
+ { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE },
+ { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 },
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
+ { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), 0 },
+ { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
+ { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
+ { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
+ { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
+ { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0, },
+ { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
+ { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
+ { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
+ { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE },
+ { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE },
+ { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
+ { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
+ { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
+ { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
+ { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
+ { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 },
+ { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 },
+ { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 },
+ { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 },
+ { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 },
+ { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 },
+ { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 },
+ { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 },
+ { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 },
+ { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
+ {},
+ };
+
+ JsonVariant *e;
+ int r;
+
+ if (!variant)
+ return 0;
+
+ if (!json_variant_is_array(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+ JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+ bool matching = false;
+ JsonVariant *m;
+
+ if (!json_variant_is_object(e))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
+
+ m = json_variant_by_key(e, "matchMachineId");
+ if (m) {
+ r = per_machine_id_match(m, flags);
+ if (r < 0)
+ return r;
+
+ matching = r > 0;
+ }
+
+ if (!matching) {
+ m = json_variant_by_key(e, "matchHostname");
+ if (m) {
+ r = per_machine_hostname_match(m, flags);
+ if (r < 0)
+ return r;
+
+ matching = r > 0;
+ }
+ }
+
+ if (!matching)
+ continue;
+
+ r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+ static const JsonDispatch status_dispatch_table[] = {
+ { "diskUsage", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_usage), 0 },
+ { "diskFree", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_free), 0 },
+ { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
+ { "diskCeiling", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_ceiling), 0 },
+ { "diskFloor", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_floor), 0 },
+ { "state", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, state), JSON_SAFE },
+ { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
+ { "signedLocally", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, signed_locally), 0 },
+ { "goodAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, good_authentication_counter), 0 },
+ { "badAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, bad_authentication_counter), 0 },
+ { "lastGoodAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_good_authentication_usec), 0 },
+ { "lastBadAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_bad_authentication_usec), 0 },
+ { "rateLimitBeginUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_begin_usec), 0 },
+ { "rateLimitCount", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_count), 0 },
+ { "removable", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, removable), 0 },
+ {},
+ };
+
+ char smid[SD_ID128_STRING_MAX];
+ JsonVariant *m;
+ sd_id128_t mid;
+ int r;
+
+ if (!variant)
+ return 0;
+
+ if (!json_variant_is_object(variant))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+ m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+ if (!m)
+ return 0;
+
+ return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
+}
+
+static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
+ assert(h);
+
+ if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
+ return 0;
+
+ assert(h->user_name);
+
+ if (!h->user_name_and_realm_auto && h->realm) {
+ h->user_name_and_realm_auto = strjoin(h->user_name, "@", h->realm);
+ if (!h->user_name_and_realm_auto)
+ return json_log_oom(h->json, json_flags);
+ }
+
+ /* Let's add in the following automatisms only for regular users, they dont make sense for any others */
+ if (user_record_disposition(h) != USER_REGULAR)
+ return 0;
+
+ if (!h->home_directory && !h->home_directory_auto) {
+ h->home_directory_auto = path_join("/home/", h->user_name);
+ if (!h->home_directory_auto)
+ return json_log_oom(h->json, json_flags);
+ }
+
+ if (!h->image_path && !h->image_path_auto) {
+ const char *suffix;
+ UserStorage storage;
+
+ storage = user_record_storage(h);
+ if (storage == USER_LUKS)
+ suffix = ".home";
+ else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
+ suffix = ".homedir";
+ else
+ suffix = NULL;
+
+ if (suffix) {
+ h->image_path_auto = strjoin("/home/", user_record_user_name_and_realm(h), suffix);
+ if (!h->image_path_auto)
+ return json_log_oom(h->json, json_flags);
+ }
+ }
+
+ return 0;
+}
+
+int user_group_record_mangle(
+ JsonVariant *v,
+ UserRecordLoadFlags load_flags,
+ JsonVariant **ret_variant,
+ UserRecordMask *ret_mask) {
+
+ static const struct {
+ UserRecordMask mask;
+ const char *name;
+ } mask_field[] = {
+ { USER_RECORD_PRIVILEGED, "privileged" },
+ { USER_RECORD_SECRET, "secret" },
+ { USER_RECORD_BINDING, "binding" },
+ { USER_RECORD_PER_MACHINE, "perMachine" },
+ { USER_RECORD_STATUS, "status" },
+ { USER_RECORD_SIGNATURE, "signature" },
+ };
+
+ JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+ JsonVariant *array[ELEMENTSOF(mask_field) * 2];
+ size_t n_retain = 0, i;
+ UserRecordMask m = 0;
+ int r;
+
+ assert((load_flags & _USER_RECORD_MASK_MAX) == 0); /* detect mistakes when accidentally passing
+ * UserRecordMask bit masks as UserRecordLoadFlags
+ * value */
+
+ assert(v);
+ assert(ret_variant);
+ assert(ret_mask);
+
+ /* Note that this function is shared with the group record parser, hence we try to be generic in our
+ * log message wording here, to cover both cases. */
+
+ if (!json_variant_is_object(v))
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is not a JSON object, refusing.");
+
+ if (USER_RECORD_ALLOW_MASK(load_flags) == 0) /* allow nothing? */
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Nothing allowed in record, refusing.");
+
+ if (USER_RECORD_STRIP_MASK(load_flags) == _USER_RECORD_MASK_MAX) /* strip everything? */
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Stripping everything from record, refusing.");
+
+ /* Check if we have the special sections and if they match our flags set */
+ for (i = 0; i < ELEMENTSOF(mask_field); i++) {
+ JsonVariant *e, *k;
+
+ if (FLAGS_SET(USER_RECORD_STRIP_MASK(load_flags), mask_field[i].mask)) {
+ if (!w)
+ w = json_variant_ref(v);
+
+ r = json_variant_filter(&w, STRV_MAKE(mask_field[i].name));
+ if (r < 0)
+ return json_log(w, json_flags, r, "Failed to remove field from variant: %m");
+
+ continue;
+ }
+
+ e = json_variant_by_key_full(v, mask_field[i].name, &k);
+ if (e) {
+ if (!FLAGS_SET(USER_RECORD_ALLOW_MASK(load_flags), mask_field[i].mask))
+ return json_log(e, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", mask_field[i].name);
+
+ if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
+ array[n_retain++] = k;
+ array[n_retain++] = e;
+ }
+
+ m |= mask_field[i].mask;
+ } else {
+ if (FLAGS_SET(USER_RECORD_REQUIRE_MASK(load_flags), mask_field[i].mask))
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks '%s' field, which is required.", mask_field[i].name);
+ }
+ }
+
+ if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
+ /* If we are supposed to strip regular items, then let's instead just allocate a new object
+ * with just the stuff we need. */
+
+ w = json_variant_unref(w);
+ r = json_variant_new_object(&w, array, n_retain);
+ if (r < 0)
+ return json_log(v, json_flags, r, "Failed to allocate new object: %m");
+ } else {
+ /* And now check if there's anything else in the record */
+ for (i = 0; i < json_variant_elements(v); i += 2) {
+ const char *f;
+ bool special = false;
+ size_t j;
+
+ assert_se(f = json_variant_string(json_variant_by_index(v, i)));
+
+ for (j = 0; j < ELEMENTSOF(mask_field); j++)
+ if (streq(f, mask_field[j].name)) { /* already covered in the loop above */
+ special = true;
+ continue;
+ }
+
+ if (!special) {
+ if ((load_flags & (USER_RECORD_ALLOW_REGULAR|USER_RECORD_REQUIRE_REGULAR)) == 0)
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", f);
+
+ m |= USER_RECORD_REGULAR;
+ break;
+ }
+ }
+ }
+
+ if (FLAGS_SET(load_flags, USER_RECORD_REQUIRE_REGULAR) && !FLAGS_SET(m, USER_RECORD_REGULAR))
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks basic identity fields, which are required.");
+
+ if (m == 0)
+ return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is empty.");
+
+ if (w)
+ *ret_variant = TAKE_PTR(w);
+ else
+ *ret_variant = json_variant_ref(v);
+
+ *ret_mask = m;
+ return 0;
+}
+
+int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_flags) {
+
+ static const JsonDispatch user_dispatch_table[] = {
+ { "userName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), 0 },
+ { "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(UserRecord, realm), 0 },
+ { "realName", JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(UserRecord, real_name), 0 },
+ { "emailAddress", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, email_address), JSON_SAFE },
+ { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE },
+ { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 },
+ { "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(UserRecord, disposition), 0 },
+ { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_change_usec), 0 },
+ { "lastPasswordChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 },
+ { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
+ { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
+ { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 },
+ { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE },
+ { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE },
+ { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 },
+ { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 },
+ { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 },
+ { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 },
+ { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 },
+ { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 },
+ { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 },
+ { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
+ { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 },
+ { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
+ { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
+ { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
+ { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },
+ { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 },
+ { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 },
+ { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 },
+ { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 },
+ { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 },
+ { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE },
+ { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE },
+ { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE },
+ { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 },
+ { "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 },
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
+ { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), 0 },
+ { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
+ { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
+ { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
+ { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
+ { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0 },
+ { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
+ { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
+ { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
+ { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE },
+ { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE },
+ { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
+ { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
+ { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
+ { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
+ { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
+ { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 },
+ { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 },
+ { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 },
+ { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 },
+ { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 },
+ { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 },
+ { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 },
+ { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 },
+ { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 },
+ { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
+
+ { "secret", JSON_VARIANT_OBJECT, dispatch_secret, 0, 0 },
+ { "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
+
+ /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */
+ { "perMachine", JSON_VARIANT_ARRAY, NULL, 0, 0 },
+ { "binding", JSON_VARIANT_OBJECT, NULL, 0, 0 },
+ { "status", JSON_VARIANT_OBJECT, NULL, 0, 0 },
+
+ /* Ignore 'signature', we check it with explicit accessors instead */
+ { "signature", JSON_VARIANT_ARRAY, NULL, 0, 0 },
+ {},
+ };
+
+ JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
+ int r;
+
+ assert(h);
+ assert(!h->json);
+
+ /* Note that this call will leave a half-initialized record around on failure! */
+
+ r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
+ if (r < 0)
+ return r;
+
+ r = json_dispatch(h->json, user_dispatch_table, NULL, json_flags, h);
+ if (r < 0)
+ return r;
+
+ /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields,
+ * since we want them to override the global options. Let's process them now. */
+
+ r = dispatch_per_machine("perMachine", json_variant_by_key(h->json, "perMachine"), json_flags, h);
+ if (r < 0)
+ return r;
+
+ r = dispatch_binding("binding", json_variant_by_key(h->json, "binding"), json_flags, h);
+ if (r < 0)
+ return r;
+
+ r = dispatch_status("status", json_variant_by_key(h->json, "status"), json_flags, h);
+ if (r < 0)
+ return r;
+
+ if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->user_name)
+ return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "User name field missing, refusing.");
+
+ r = user_record_augment(h, json_flags);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int user_record_build(UserRecord **ret, ...) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *u = NULL;
+ va_list ap;
+ int r;
+
+ assert(ret);
+
+ va_start(ap, ret);
+ r = json_buildv(&v, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return r;
+
+ u = user_record_new();
+ if (!u)
+ return -ENOMEM;
+
+ r = user_record_load(u, v, USER_RECORD_LOAD_FULL);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(u);
+ return 0;
+}
+
+const char *user_record_user_name_and_realm(UserRecord *h) {
+ assert(h);
+
+ /* Return the pre-initialized joined string if it is defined */
+ if (h->user_name_and_realm_auto)
+ return h->user_name_and_realm_auto;
+
+ /* If it's not defined then we cannot have a realm */
+ assert(!h->realm);
+ return h->user_name;
+}
+
+UserStorage user_record_storage(UserRecord *h) {
+ assert(h);
+
+ if (h->storage >= 0)
+ return h->storage;
+
+ return USER_CLASSIC;
+}
+
+const char *user_record_file_system_type(UserRecord *h) {
+ assert(h);
+
+ return h->file_system_type ?: "ext4";
+}
+
+const char *user_record_skeleton_directory(UserRecord *h) {
+ assert(h);
+
+ return h->skeleton_directory ?: "/etc/skel";
+}
+
+mode_t user_record_access_mode(UserRecord *h) {
+ assert(h);
+
+ return h->access_mode != (mode_t) -1 ? h->access_mode : 0700;
+}
+
+const char* user_record_home_directory(UserRecord *h) {
+ assert(h);
+
+ if (h->home_directory)
+ return h->home_directory;
+ if (h->home_directory_auto)
+ return h->home_directory_auto;
+
+ /* The root user is special, hence be special about it */
+ if (streq_ptr(h->user_name, "root"))
+ return "/root";
+
+ return "/";
+}
+
+const char *user_record_image_path(UserRecord *h) {
+ assert(h);
+
+ if (h->image_path)
+ return h->image_path;
+ if (h->image_path_auto)
+ return h->image_path_auto;
+
+ return IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT) ? user_record_home_directory(h) : NULL;
+}
+
+const char *user_record_cifs_user_name(UserRecord *h) {
+ assert(h);
+
+ return h->cifs_user_name ?: h->user_name;
+}
+
+unsigned long user_record_mount_flags(UserRecord *h) {
+ assert(h);
+
+ return (h->nosuid ? MS_NOSUID : 0) |
+ (h->noexec ? MS_NOEXEC : 0) |
+ (h->nodev ? MS_NODEV : 0);
+}
+
+const char *user_record_shell(UserRecord *h) {
+ assert(h);
+
+ if (h->shell)
+ return h->shell;
+
+ if (streq_ptr(h->user_name, "root"))
+ return "/bin/sh";
+
+ if (user_record_disposition(h) == USER_REGULAR)
+ return "/bin/bash";
+
+ return NOLOGIN;
+}
+
+const char *user_record_real_name(UserRecord *h) {
+ assert(h);
+
+ return h->real_name ?: h->user_name;
+}
+
+bool user_record_luks_discard(UserRecord *h) {
+ const char *ip;
+
+ assert(h);
+
+ if (h->luks_discard >= 0)
+ return h->luks_discard;
+
+ ip = user_record_image_path(h);
+ if (!ip)
+ return false;
+
+ /* Use discard by default if we are referring to a real block device, but not when operating on a
+ * loopback device. We want to optimize for SSD and flash storage after all, but we should be careful
+ * when storing stuff on top of regular file systems in loopback files as doing discard then would
+ * mean thin provisioning and we should not do that willy-nilly since it means we'll risk EIO later
+ * on should the disk space to back our file systems not be available. */
+
+ return path_startswith(ip, "/dev/");
+}
+
+const char *user_record_luks_cipher(UserRecord *h) {
+ assert(h);
+
+ return h->luks_cipher ?: "aes";
+}
+
+const char *user_record_luks_cipher_mode(UserRecord *h) {
+ assert(h);
+
+ return h->luks_cipher_mode ?: "xts-plain64";
+}
+
+uint64_t user_record_luks_volume_key_size(UserRecord *h) {
+ assert(h);
+
+ /* We return a value here that can be cast without loss into size_t which is what libcrypsetup expects */
+
+ if (h->luks_volume_key_size == UINT64_MAX)
+ return 256 / 8;
+
+ return MIN(h->luks_volume_key_size, SIZE_MAX);
+}
+
+const char* user_record_luks_pbkdf_type(UserRecord *h) {
+ assert(h);
+
+ return h->luks_pbkdf_type ?: "argon2i";
+}
+
+uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
+ assert(h);
+
+ /* Returns a value with ms granularity, since that's what libcryptsetup expects */
+
+ if (h->luks_pbkdf_time_cost_usec == UINT64_MAX)
+ return 500 * USEC_PER_MSEC; /* We default to 500ms, in contrast to libcryptsetup's 2s, which is just awfully slow on every login */
+
+ return MIN(DIV_ROUND_UP(h->luks_pbkdf_time_cost_usec, USEC_PER_MSEC), UINT32_MAX) * USEC_PER_MSEC;
+}
+
+uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h) {
+ assert(h);
+
+ /* Returns a value with kb granularity, since that's what libcryptsetup expects */
+
+ if (h->luks_pbkdf_memory_cost == UINT64_MAX)
+ return 64*1024*1024; /* We default to 64M, since this should work on smaller systems too */
+
+ return MIN(DIV_ROUND_UP(h->luks_pbkdf_memory_cost, 1024), UINT32_MAX) * 1024;
+}
+
+uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h) {
+ assert(h);
+
+ if (h->luks_pbkdf_memory_cost == UINT64_MAX)
+ return 1; /* We default to 1, since this should work on smaller systems too */
+
+ return MIN(h->luks_pbkdf_parallel_threads, UINT32_MAX);
+}
+
+const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h) {
+ assert(h);
+
+ return h->luks_pbkdf_hash_algorithm ?: "sha512";
+}
+
+gid_t user_record_gid(UserRecord *h) {
+ assert(h);
+
+ if (gid_is_valid(h->gid))
+ return h->gid;
+
+ return (gid_t) h->uid;
+}
+
+UserDisposition user_record_disposition(UserRecord *h) {
+ assert(h);
+
+ if (h->disposition >= 0)
+ return h->disposition;
+
+ /* If not declared, derive from UID */
+
+ if (!uid_is_valid(h->uid))
+ return _USER_DISPOSITION_INVALID;
+
+ if (h->uid == 0 || h->uid == UID_NOBODY)
+ return USER_INTRINSIC;
+
+ if (uid_is_system(h->uid))
+ return USER_SYSTEM;
+
+ if (uid_is_dynamic(h->uid))
+ return USER_DYNAMIC;
+
+ if (uid_is_container(h->uid))
+ return USER_CONTAINER;
+
+ if (h->uid > INT32_MAX)
+ return USER_RESERVED;
+
+ return USER_REGULAR;
+}
+
+int user_record_removable(UserRecord *h) {
+ UserStorage storage;
+ assert(h);
+
+ if (h->removable >= 0)
+ return h->removable;
+
+ /* Refuse to decide for classic records */
+ storage = user_record_storage(h);
+ if (h->storage < 0 || h->storage == USER_CLASSIC)
+ return -1;
+
+ /* For now consider only LUKS home directories with a reference by path as removable */
+ return storage == USER_LUKS && path_startswith(user_record_image_path(h), "/dev/");
+}
+
+uint64_t user_record_ratelimit_interval_usec(UserRecord *h) {
+ assert(h);
+
+ if (h->ratelimit_interval_usec == UINT64_MAX)
+ return DEFAULT_RATELIMIT_INTERVAL_USEC;
+
+ return h->ratelimit_interval_usec;
+}
+
+uint64_t user_record_ratelimit_burst(UserRecord *h) {
+ assert(h);
+
+ if (h->ratelimit_burst == UINT64_MAX)
+ return DEFAULT_RATELIMIT_BURST;
+
+ return h->ratelimit_burst;
+}
+
+bool user_record_can_authenticate(UserRecord *h) {
+ assert(h);
+
+ /* Returns true if there's some form of property configured that the user can authenticate against */
+
+ if (h->n_pkcs11_encrypted_key > 0)
+ return true;
+
+ return !strv_isempty(h->hashed_password);
+}
+
+uint64_t user_record_ratelimit_next_try(UserRecord *h) {
+ assert(h);
+
+ /* Calculates when the it's possible to login next. Returns:
+ *
+ * UINT64_MAX → Nothing known
+ * 0 → Right away
+ * Any other → Next time in CLOCK_REALTIME in usec (which could be in the past)
+ */
+
+ if (h->ratelimit_begin_usec == UINT64_MAX ||
+ h->ratelimit_count == UINT64_MAX)
+ return UINT64_MAX;
+
+ if (h->ratelimit_count < user_record_ratelimit_burst(h))
+ return 0;
+
+ return usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h));
+}
+
+bool user_record_equal(UserRecord *a, UserRecord *b) {
+ assert(a);
+ assert(b);
+
+ /* We assume that when a record is modified its JSON data is updated at the same time, hence it's
+ * sufficient to compare the JSON data. */
+
+ return json_variant_equal(a->json, b->json);
+}
+
+bool user_record_compatible(UserRecord *a, UserRecord *b) {
+ assert(a);
+ assert(b);
+
+ /* If either lacks a the regular section, we can't really decide, let's hence say they are
+ * incompatible. */
+ if (!(a->mask & b->mask & USER_RECORD_REGULAR))
+ return false;
+
+ return streq_ptr(a->user_name, b->user_name) &&
+ streq_ptr(a->realm, b->realm);
+}
+
+int user_record_compare_last_change(UserRecord *a, UserRecord *b) {
+ assert(a);
+ assert(b);
+
+ if (a->last_change_usec == b->last_change_usec)
+ return 0;
+
+ /* Always consider a record with a timestamp newer than one without */
+ if (a->last_change_usec == UINT64_MAX)
+ return -1;
+ if (b->last_change_usec == UINT64_MAX)
+ return 1;
+
+ return CMP(a->last_change_usec, b->last_change_usec);
+}
+
+int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret) {
+ _cleanup_(user_record_unrefp) UserRecord *c = NULL;
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ c = user_record_new();
+ if (!c)
+ return -ENOMEM;
+
+ r = user_record_load(c, h->json, flags);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(c);
+ return 0;
+}
+
+int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask) {
+ _cleanup_(user_record_unrefp) UserRecord *x = NULL, *y = NULL;
+ int r;
+
+ assert(a);
+ assert(b);
+
+ /* Compares the two records, but ignores anything not listed in the specified mask */
+
+ if ((a->mask & ~mask) != 0) {
+ r = user_record_clone(a, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX), &x);
+ if (r < 0)
+ return r;
+
+ a = x;
+ }
+
+ if ((b->mask & ~mask) != 0) {
+ r = user_record_clone(b, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX), &y);
+ if (r < 0)
+ return r;
+
+ b = y;
+ }
+
+ return user_record_equal(a, b);
+}
+
+int user_record_test_blocked(UserRecord *h) {
+ usec_t n;
+
+ /* Checks whether access to the specified user shall be allowed at the moment. Returns:
+ *
+ * -ESTALE: Record is from the future
+ * -ENOLCK: Record is blocked
+ * -EL2HLT: Record is not valid yet
+ * -EL3HLT: Record is not valid anymore
+ *
+ */
+
+ assert(h);
+
+ n = now(CLOCK_REALTIME);
+ if (h->last_change_usec != UINT64_MAX &&
+ h->last_change_usec > n) /* Don't allow log ins when the record is from the future */
+ return -ESTALE;
+
+ if (h->locked > 0)
+ return -ENOLCK;
+
+ if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
+ return -EL2HLT;
+ if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
+ return -EL3HLT;
+
+ return 0;
+}
+
+int user_record_test_password_change_required(UserRecord *h) {
+ bool change_permitted;
+ usec_t n;
+
+ assert(h);
+
+ /* Checks whether the user must change the password when logging in
+
+ -EKEYREVOKED: Change password now because admin said so
+ -EOWNERDEAD: Change password now because it expired
+ -EKEYREJECTED: Password is expired, no changing is allowed
+ -EKEYEXPIRED: Password is about to expire, warn user
+ -ENETDOWN: Record has expiration info but no password change timestamp
+ -EROFS: No password change required nor permitted
+ 0: No password change required, but permitted
+ */
+
+ /* If a pasword change request has been set explicitly, it overrides everything */
+ if (h->password_change_now > 0)
+ return -EKEYREVOKED;
+
+ n = now(CLOCK_REALTIME);
+
+ /* Then, let's check if password changing is currently allowed at all */
+ if (h->password_change_min_usec != UINT64_MAX) {
+
+ /* Expiry configured but no password change timestamp known? */
+ if (h->last_password_change_usec == UINT64_MAX)
+ return -ENETDOWN;
+
+ if (h->password_change_min_usec >= UINT64_MAX - h->last_password_change_usec)
+ change_permitted = false;
+ else
+ change_permitted = n >= h->last_password_change_usec + h->password_change_min_usec;
+
+ } else
+ change_permitted = true;
+
+ /* Let's check whether the password has expired. */
+ if (!(h->password_change_max_usec == UINT64_MAX ||
+ h->password_change_max_usec >= UINT64_MAX - h->last_password_change_usec)) {
+
+ uint64_t change_before;
+
+ /* Expiry configured but no password change timestamp known? */
+ if (h->last_password_change_usec == UINT64_MAX)
+ return -ENETDOWN;
+
+ /* Password is in inactive phase? */
+ if (h->password_change_inactive_usec != UINT64_MAX &&
+ h->password_change_inactive_usec < UINT64_MAX - h->password_change_max_usec) {
+ usec_t added;
+
+ added = h->password_change_inactive_usec + h->password_change_max_usec;
+ if (added < UINT64_MAX - h->last_password_change_usec &&
+ n >= h->last_password_change_usec + added)
+ return -EKEYREJECTED;
+ }
+
+ /* Password needs to be changed now? */
+ change_before = h->last_password_change_usec + h->password_change_max_usec;
+ if (n >= change_before)
+ return change_permitted ? -EOWNERDEAD : -EKEYREJECTED;
+
+ /* Warn user? */
+ if (h->password_change_warn_usec != UINT64_MAX &&
+ (change_before < h->password_change_warn_usec ||
+ n >= change_before - h->password_change_warn_usec))
+ return change_permitted ? -EKEYEXPIRED : -EROFS;
+ }
+
+ /* No password changing necessary */
+ return change_permitted ? 0 : -EROFS;
+}
+
+static const char* const user_storage_table[_USER_STORAGE_MAX] = {
+ [USER_CLASSIC] = "classic",
+ [USER_LUKS] = "luks",
+ [USER_DIRECTORY] = "directory",
+ [USER_SUBVOLUME] = "subvolume",
+ [USER_FSCRYPT] = "fscrypt",
+ [USER_CIFS] = "cifs",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(user_storage, UserStorage);
+
+static const char* const user_disposition_table[_USER_DISPOSITION_MAX] = {
+ [USER_INTRINSIC] = "intrinsic",
+ [USER_SYSTEM] = "system",
+ [USER_DYNAMIC] = "dynamic",
+ [USER_REGULAR] = "regular",
+ [USER_CONTAINER] = "container",
+ [USER_RESERVED] = "reserved",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(user_disposition, UserDisposition);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+#include "json.h"
+#include "missing_resource.h"
+#include "time-util.h"
+
+/* But some limits on disk sizes: not less than 5M, not more than 5T */
+#define USER_DISK_SIZE_MIN (UINT64_C(5)*1024*1024)
+#define USER_DISK_SIZE_MAX (UINT64_C(5)*1024*1024*1024*1024)
+
+/* The default disk size to use when nothing else is specified, relative to free disk space */
+#define USER_DISK_SIZE_DEFAULT_PERCENT 85
+
+typedef enum UserDisposition {
+ USER_INTRINSIC, /* root and nobody */
+ USER_SYSTEM, /* statically allocated users for system services */
+ USER_DYNAMIC, /* dynamically allocated users for system services */
+ USER_REGULAR, /* regular (typically human users) */
+ USER_CONTAINER, /* UID ranges allocated for container uses */
+ USER_RESERVED, /* Range above 2^31 */
+ _USER_DISPOSITION_MAX,
+ _USER_DISPOSITION_INVALID = -1,
+} UserDisposition;
+
+typedef enum UserHomeStorage {
+ USER_CLASSIC,
+ USER_LUKS,
+ USER_DIRECTORY, /* A directory, and a .identity file in it, which USER_CLASSIC lacks */
+ USER_SUBVOLUME,
+ USER_FSCRYPT,
+ USER_CIFS,
+ _USER_STORAGE_MAX,
+ _USER_STORAGE_INVALID = -1
+} UserStorage;
+
+typedef enum UserRecordMask {
+ /* The various sections an identity record may have, as bit mask */
+ USER_RECORD_REGULAR = 1U << 0,
+ USER_RECORD_SECRET = 1U << 1,
+ USER_RECORD_PRIVILEGED = 1U << 2,
+ USER_RECORD_PER_MACHINE = 1U << 3,
+ USER_RECORD_BINDING = 1U << 4,
+ USER_RECORD_STATUS = 1U << 5,
+ USER_RECORD_SIGNATURE = 1U << 6,
+ _USER_RECORD_MASK_MAX = (1U << 7)-1
+} UserRecordMask;
+
+typedef enum UserRecordLoadFlags {
+ /* A set of flags used while loading a user record from JSON data. We leave the lower 6 bits free,
+ * just as a safety precaution so that we can detect borked conversions between UserRecordMask and
+ * UserRecordLoadFlags. */
+
+ /* What to require */
+ USER_RECORD_REQUIRE_REGULAR = USER_RECORD_REGULAR << 7,
+ USER_RECORD_REQUIRE_SECRET = USER_RECORD_SECRET << 7,
+ USER_RECORD_REQUIRE_PRIVILEGED = USER_RECORD_PRIVILEGED << 7,
+ USER_RECORD_REQUIRE_PER_MACHINE = USER_RECORD_PER_MACHINE << 7,
+ USER_RECORD_REQUIRE_BINDING = USER_RECORD_BINDING << 7,
+ USER_RECORD_REQUIRE_STATUS = USER_RECORD_STATUS << 7,
+ USER_RECORD_REQUIRE_SIGNATURE = USER_RECORD_SIGNATURE << 7,
+
+ /* What to allow */
+ USER_RECORD_ALLOW_REGULAR = USER_RECORD_REGULAR << 14,
+ USER_RECORD_ALLOW_SECRET = USER_RECORD_SECRET << 14,
+ USER_RECORD_ALLOW_PRIVILEGED = USER_RECORD_PRIVILEGED << 14,
+ USER_RECORD_ALLOW_PER_MACHINE = USER_RECORD_PER_MACHINE << 14,
+ USER_RECORD_ALLOW_BINDING = USER_RECORD_BINDING << 14,
+ USER_RECORD_ALLOW_STATUS = USER_RECORD_STATUS << 14,
+ USER_RECORD_ALLOW_SIGNATURE = USER_RECORD_SIGNATURE << 14,
+
+ /* What to strip */
+ USER_RECORD_STRIP_REGULAR = USER_RECORD_REGULAR << 21,
+ USER_RECORD_STRIP_SECRET = USER_RECORD_SECRET << 21,
+ USER_RECORD_STRIP_PRIVILEGED = USER_RECORD_PRIVILEGED << 21,
+ USER_RECORD_STRIP_PER_MACHINE = USER_RECORD_PER_MACHINE << 21,
+ USER_RECORD_STRIP_BINDING = USER_RECORD_BINDING << 21,
+ USER_RECORD_STRIP_STATUS = USER_RECORD_STATUS << 21,
+ USER_RECORD_STRIP_SIGNATURE = USER_RECORD_SIGNATURE << 21,
+
+ /* Some special combinations that deserve explicit names */
+ USER_RECORD_LOAD_FULL = USER_RECORD_REQUIRE_REGULAR |
+ USER_RECORD_ALLOW_SECRET |
+ USER_RECORD_ALLOW_PRIVILEGED |
+ USER_RECORD_ALLOW_PER_MACHINE |
+ USER_RECORD_ALLOW_BINDING |
+ USER_RECORD_ALLOW_STATUS |
+ USER_RECORD_ALLOW_SIGNATURE,
+
+ USER_RECORD_LOAD_REFUSE_SECRET = USER_RECORD_REQUIRE_REGULAR |
+ USER_RECORD_ALLOW_PRIVILEGED |
+ USER_RECORD_ALLOW_PER_MACHINE |
+ USER_RECORD_ALLOW_BINDING |
+ USER_RECORD_ALLOW_STATUS |
+ USER_RECORD_ALLOW_SIGNATURE,
+
+ USER_RECORD_LOAD_MASK_SECRET = USER_RECORD_REQUIRE_REGULAR |
+ USER_RECORD_ALLOW_PRIVILEGED |
+ USER_RECORD_ALLOW_PER_MACHINE |
+ USER_RECORD_ALLOW_BINDING |
+ USER_RECORD_ALLOW_STATUS |
+ USER_RECORD_ALLOW_SIGNATURE |
+ USER_RECORD_STRIP_SECRET,
+
+ USER_RECORD_EXTRACT_SECRET = USER_RECORD_REQUIRE_SECRET |
+ USER_RECORD_STRIP_REGULAR |
+ USER_RECORD_STRIP_PRIVILEGED |
+ USER_RECORD_STRIP_PER_MACHINE |
+ USER_RECORD_STRIP_BINDING |
+ USER_RECORD_STRIP_STATUS |
+ USER_RECORD_STRIP_SIGNATURE,
+
+ USER_RECORD_LOAD_SIGNABLE = USER_RECORD_REQUIRE_REGULAR |
+ USER_RECORD_ALLOW_PRIVILEGED |
+ USER_RECORD_ALLOW_PER_MACHINE,
+
+ USER_RECORD_EXTRACT_SIGNABLE = USER_RECORD_LOAD_SIGNABLE |
+ USER_RECORD_STRIP_SECRET |
+ USER_RECORD_STRIP_BINDING |
+ USER_RECORD_STRIP_STATUS |
+ USER_RECORD_STRIP_SIGNATURE,
+
+ USER_RECORD_LOAD_EMBEDDED = USER_RECORD_REQUIRE_REGULAR |
+ USER_RECORD_ALLOW_PRIVILEGED |
+ USER_RECORD_ALLOW_PER_MACHINE |
+ USER_RECORD_ALLOW_SIGNATURE,
+
+ USER_RECORD_EXTRACT_EMBEDDED = USER_RECORD_LOAD_EMBEDDED |
+ USER_RECORD_STRIP_SECRET |
+ USER_RECORD_STRIP_BINDING |
+ USER_RECORD_STRIP_STATUS,
+
+ /* Whether to log about loader errors beyond LOG_DEBUG */
+ USER_RECORD_LOG = 1U << 28,
+
+ /* Whether to ignore errors and load what we can */
+ USER_RECORD_PERMISSIVE = 1U << 29,
+} UserRecordLoadFlags;
+
+static inline UserRecordLoadFlags USER_RECORD_REQUIRE(UserRecordMask m) {
+ assert((m & ~_USER_RECORD_MASK_MAX) == 0);
+ return m << 7;
+}
+
+static inline UserRecordLoadFlags USER_RECORD_ALLOW(UserRecordMask m) {
+ assert((m & ~_USER_RECORD_MASK_MAX) == 0);
+ return m << 14;
+}
+
+static inline UserRecordLoadFlags USER_RECORD_STRIP(UserRecordMask m) {
+ assert((m & ~_USER_RECORD_MASK_MAX) == 0);
+ return m << 21;
+}
+
+static inline UserRecordMask USER_RECORD_REQUIRE_MASK(UserRecordLoadFlags f) {
+ return (f >> 7) & _USER_RECORD_MASK_MAX;
+}
+
+static inline UserRecordMask USER_RECORD_ALLOW_MASK(UserRecordLoadFlags f) {
+ return ((f >> 14) & _USER_RECORD_MASK_MAX) | USER_RECORD_REQUIRE_MASK(f);
+}
+
+static inline UserRecordMask USER_RECORD_STRIP_MASK(UserRecordLoadFlags f) {
+ return (f >> 21) & _USER_RECORD_MASK_MAX;
+}
+
+static inline JsonDispatchFlags USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(UserRecordLoadFlags flags) {
+ return (FLAGS_SET(flags, USER_RECORD_LOG) ? JSON_LOG : 0) |
+ (FLAGS_SET(flags, USER_RECORD_PERMISSIVE) ? JSON_PERMISSIVE : 0);
+}
+
+typedef struct Pkcs11EncryptedKey {
+ /* The encrypted passphrase, which can be decrypted with the private key indicated below */
+ void *data;
+ size_t size;
+
+ /* Where to find the private key to decrypt the encrypted passphrase above */
+ char *uri;
+
+ /* What to test the decrypted passphrase against to allow access (classic UNIX password hash). Note
+ * that the decrypted passphrase is also used for unlocking LUKS and fscrypt, and if the account is
+ * backed by LUKS or fscrypt the hashed password is only an additional layer of authentication, not
+ * the only. */
+ char *hashed_password;
+} Pkcs11EncryptedKey;
+
+typedef struct UserRecord {
+ /* The following three fields are not part of the JSON record */
+ unsigned n_ref;
+ UserRecordMask mask;
+ bool incomplete; /* incomplete due to security restrictions. */
+
+ char *user_name;
+ char *realm;
+ char *user_name_and_realm_auto; /* the user_name field concatenated with '@' and the realm, if the latter is defined */
+ char *real_name;
+ char *email_address;
+ char *password_hint;
+ char *icon_name;
+ char *location;
+
+ UserDisposition disposition;
+ uint64_t last_change_usec;
+ uint64_t last_password_change_usec;
+
+ char *shell;
+ mode_t umask;
+ char **environment;
+ char *time_zone;
+ char *preferred_language;
+ int nice_level;
+ struct rlimit *rlimits[_RLIMIT_MAX];
+
+ int locked; /* prohibit activation in general */
+ uint64_t not_before_usec; /* prohibit activation before this unix time */
+ uint64_t not_after_usec; /* prohibit activation after this unix time */
+
+ UserStorage storage;
+ uint64_t disk_size;
+ uint64_t disk_size_relative; /* Disk size, relative to the free bytes of the medium, normalized to UINT32_MAX = 100% */
+ char *skeleton_directory;
+ mode_t access_mode;
+
+ uint64_t tasks_max;
+ uint64_t memory_high;
+ uint64_t memory_max;
+ uint64_t cpu_weight;
+ uint64_t io_weight;
+
+ bool nosuid;
+ bool nodev;
+ bool noexec;
+
+ char **hashed_password;
+ char **ssh_authorized_keys;
+ char **password;
+ char **pkcs11_pin;
+
+ char *cifs_domain;
+ char *cifs_user_name;
+ char *cifs_service;
+
+ char *image_path;
+ char *image_path_auto; /* when none is configured explicitly, this is where we place the implicit image */
+ char *home_directory;
+ char *home_directory_auto; /* when none is set explicitly, this is where we place the implicit home directory */
+
+ uid_t uid;
+ gid_t gid;
+
+ char **member_of;
+
+ char *file_system_type;
+ sd_id128_t partition_uuid;
+ sd_id128_t luks_uuid;
+ sd_id128_t file_system_uuid;
+
+ int luks_discard;
+ char *luks_cipher;
+ char *luks_cipher_mode;
+ uint64_t luks_volume_key_size;
+ char *luks_pbkdf_hash_algorithm;
+ char *luks_pbkdf_type;
+ uint64_t luks_pbkdf_time_cost_usec;
+ uint64_t luks_pbkdf_memory_cost;
+ uint64_t luks_pbkdf_parallel_threads;
+
+ uint64_t disk_usage;
+ uint64_t disk_free;
+ uint64_t disk_ceiling;
+ uint64_t disk_floor;
+
+ char *state;
+ char *service;
+ int signed_locally;
+
+ uint64_t good_authentication_counter;
+ uint64_t bad_authentication_counter;
+ uint64_t last_good_authentication_usec;
+ uint64_t last_bad_authentication_usec;
+
+ uint64_t ratelimit_begin_usec;
+ uint64_t ratelimit_count;
+ uint64_t ratelimit_interval_usec;
+ uint64_t ratelimit_burst;
+
+ int removable;
+ int enforce_password_policy;
+ int auto_login;
+
+ uint64_t stop_delay_usec; /* How long to leave systemd --user around on log-out */
+ int kill_processes; /* Whether to kill user processes forcibly on log-out */
+
+ /* The following exist mostly so that we can cover the full /etc/shadow set of fields */
+ uint64_t password_change_min_usec; /* maps to .sp_min */
+ uint64_t password_change_max_usec; /* maps to .sp_max */
+ uint64_t password_change_warn_usec; /* maps to .sp_warn */
+ uint64_t password_change_inactive_usec; /* maps to .sp_inact */
+ int password_change_now; /* Require a password change immediately on next login (.sp_lstchg = 0) */
+
+ char **pkcs11_token_uri;
+ Pkcs11EncryptedKey *pkcs11_encrypted_key;
+ size_t n_pkcs11_encrypted_key;
+ int pkcs11_protected_authentication_path_permitted;
+
+ JsonVariant *json;
+} UserRecord;
+
+UserRecord* user_record_new(void);
+UserRecord* user_record_ref(UserRecord *h);
+UserRecord* user_record_unref(UserRecord *h);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(UserRecord*, user_record_unref);
+
+int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags flags);
+int user_record_build(UserRecord **ret, ...);
+
+const char *user_record_user_name_and_realm(UserRecord *h);
+UserStorage user_record_storage(UserRecord *h);
+const char *user_record_file_system_type(UserRecord *h);
+const char *user_record_skeleton_directory(UserRecord *h);
+mode_t user_record_access_mode(UserRecord *h);
+const char *user_record_home_directory(UserRecord *h);
+const char *user_record_image_path(UserRecord *h);
+unsigned long user_record_mount_flags(UserRecord *h);
+const char *user_record_cifs_user_name(UserRecord *h);
+const char *user_record_shell(UserRecord *h);
+const char *user_record_real_name(UserRecord *h);
+bool user_record_luks_discard(UserRecord *h);
+const char *user_record_luks_cipher(UserRecord *h);
+const char *user_record_luks_cipher_mode(UserRecord *h);
+uint64_t user_record_luks_volume_key_size(UserRecord *h);
+const char* user_record_luks_pbkdf_type(UserRecord *h);
+usec_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h);
+uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h);
+uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h);
+const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h);
+gid_t user_record_gid(UserRecord *h);
+UserDisposition user_record_disposition(UserRecord *h);
+int user_record_removable(UserRecord *h);
+usec_t user_record_ratelimit_interval_usec(UserRecord *h);
+uint64_t user_record_ratelimit_burst(UserRecord *h);
+bool user_record_can_authenticate(UserRecord *h);
+
+bool user_record_equal(UserRecord *a, UserRecord *b);
+bool user_record_compatible(UserRecord *a, UserRecord *b);
+int user_record_compare_last_change(UserRecord *a, UserRecord *b);
+
+usec_t user_record_ratelimit_next_try(UserRecord *h);
+
+int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret);
+int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask);
+
+int user_record_test_blocked(UserRecord *h);
+int user_record_test_password_change_required(UserRecord *h);
+
+/* The following six are user by group-record.c, that's why we export them here */
+int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+
+int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags);
+int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags);
+int user_group_record_mangle(JsonVariant *v, UserRecordLoadFlags load_flags, JsonVariant **ret_variant, UserRecordMask *ret_mask);
+
+const char* user_storage_to_string(UserStorage t) _const_;
+UserStorage user_storage_from_string(const char *s) _pure_;
+
+const char* user_disposition_to_string(UserDisposition t) _const_;
+UserDisposition user_disposition_from_string(const char *s) _pure_;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/auxv.h>
+
+#include "dirent-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "group-record-nss.h"
+#include "missing_syscall.h"
+#include "parse-util.h"
+#include "set.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "user-record-nss.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "varlink.h"
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(link_hash_ops, void, trivial_hash_func, trivial_compare_func, Varlink, varlink_unref);
+
+typedef enum LookupWhat {
+ LOOKUP_USER,
+ LOOKUP_GROUP,
+ LOOKUP_MEMBERSHIP,
+ _LOOKUP_WHAT_MAX,
+} LookupWhat;
+
+struct UserDBIterator {
+ LookupWhat what;
+ Set *links;
+ bool nss_covered:1;
+ bool nss_iterating:1;
+ bool synthesize_root:1;
+ bool synthesize_nobody:1;
+ int error;
+ int nss_lock;
+ unsigned n_found;
+ sd_event *event;
+ UserRecord *found_user; /* when .what == LOOKUP_USER */
+ GroupRecord *found_group; /* when .what == LOOKUP_GROUP */
+
+ char *found_user_name, *found_group_name; /* when .what == LOOKUP_MEMBERSHIP */
+ char **members_of_group;
+ size_t index_members_of_group;
+ char *filter_user_name;
+};
+
+UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
+ if (!iterator)
+ return NULL;
+
+ set_free(iterator->links);
+
+ switch (iterator->what) {
+
+ case LOOKUP_USER:
+ user_record_unref(iterator->found_user);
+
+ if (iterator->nss_iterating)
+ endpwent();
+
+ break;
+
+ case LOOKUP_GROUP:
+ group_record_unref(iterator->found_group);
+
+ if (iterator->nss_iterating)
+ endgrent();
+
+ break;
+
+ case LOOKUP_MEMBERSHIP:
+ free(iterator->found_user_name);
+ free(iterator->found_group_name);
+ strv_free(iterator->members_of_group);
+ free(iterator->filter_user_name);
+
+ if (iterator->nss_iterating)
+ endgrent();
+
+ break;
+
+ default:
+ assert_not_reached("Unexpected state?");
+ }
+
+ sd_event_unref(iterator->event);
+ safe_close(iterator->nss_lock);
+
+ return mfree(iterator);
+}
+
+static UserDBIterator* userdb_iterator_new(LookupWhat what) {
+ UserDBIterator *i;
+
+ assert(what >= 0);
+ assert(what < _LOOKUP_WHAT_MAX);
+
+ i = new(UserDBIterator, 1);
+ if (!i)
+ return NULL;
+
+ *i = (UserDBIterator) {
+ .what = what,
+ .nss_lock = -1,
+ };
+
+ return i;
+}
+
+struct user_group_data {
+ JsonVariant *record;
+ bool incomplete;
+};
+
+static void user_group_data_release(struct user_group_data *d) {
+ json_variant_unref(d->record);
+}
+
+static int userdb_on_query_reply(
+ Varlink *link,
+ JsonVariant *parameters,
+ const char *error_id,
+ VarlinkReplyFlags flags,
+ void *userdata) {
+
+ UserDBIterator *iterator = userdata;
+ int r;
+
+ assert(iterator);
+
+ if (error_id) {
+ log_debug("Got lookup error: %s", error_id);
+
+ if (STR_IN_SET(error_id,
+ "io.systemd.UserDatabase.NoRecordFound",
+ "io.systemd.UserDatabase.ConflictingRecordFound"))
+ r = -ESRCH;
+ else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
+ r = -EHOSTDOWN;
+ else if (streq(error_id, VARLINK_ERROR_TIMEOUT))
+ r = -ETIMEDOUT;
+ else
+ r = -EIO;
+
+ goto finish;
+ }
+
+ switch (iterator->what) {
+
+ case LOOKUP_USER: {
+ _cleanup_(user_group_data_release) struct user_group_data user_data = {};
+
+ static const JsonDispatch dispatch_table[] = {
+ { "record", _JSON_VARIANT_TYPE_INVALID, json_dispatch_variant, offsetof(struct user_group_data, record), 0 },
+ { "incomplete", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct user_group_data, incomplete), 0 },
+ {}
+ };
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+
+ assert_se(!iterator->found_user);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &user_data);
+ if (r < 0)
+ goto finish;
+
+ if (!user_data.record) {
+ r = log_debug_errno(SYNTHETIC_ERRNO(EIO), "Reply is missing record key");
+ goto finish;
+ }
+
+ hr = user_record_new();
+ if (!hr) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = user_record_load(hr, user_data.record, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
+ if (r < 0)
+ goto finish;
+
+ if (!hr->service) {
+ r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "User record does not carry service information, refusing.");
+ goto finish;
+ }
+
+ hr->incomplete = user_data.incomplete;
+
+ /* We match the root user by the name since the name is our primary key. We match the nobody
+ * use by UID though, since the name might differ on OSes */
+ if (streq_ptr(hr->user_name, "root"))
+ iterator->synthesize_root = false;
+ if (hr->uid == UID_NOBODY)
+ iterator->synthesize_nobody = false;
+
+ iterator->found_user = TAKE_PTR(hr);
+ iterator->n_found++;
+
+ /* More stuff coming? then let's just exit cleanly here */
+ if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
+ return 0;
+
+ /* Otherwise, let's remove this link and exit cleanly then */
+ r = 0;
+ goto finish;
+ }
+
+ case LOOKUP_GROUP: {
+ _cleanup_(user_group_data_release) struct user_group_data group_data = {};
+
+ static const JsonDispatch dispatch_table[] = {
+ { "record", _JSON_VARIANT_TYPE_INVALID, json_dispatch_variant, offsetof(struct user_group_data, record), 0 },
+ { "incomplete", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct user_group_data, incomplete), 0 },
+ {}
+ };
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+
+ assert_se(!iterator->found_group);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &group_data);
+ if (r < 0)
+ goto finish;
+
+ if (!group_data.record) {
+ r = log_debug_errno(SYNTHETIC_ERRNO(EIO), "Reply is missing record key");
+ goto finish;
+ }
+
+ g = group_record_new();
+ if (!g) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = group_record_load(g, group_data.record, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
+ if (r < 0)
+ goto finish;
+
+ if (!g->service) {
+ r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Group record does not carry service information, refusing.");
+ goto finish;
+ }
+
+ g->incomplete = group_data.incomplete;
+
+ if (streq_ptr(g->group_name, "root"))
+ iterator->synthesize_root = false;
+ if (g->gid == GID_NOBODY)
+ iterator->synthesize_nobody = false;
+
+ iterator->found_group = TAKE_PTR(g);
+ iterator->n_found++;
+
+ if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
+ return 0;
+
+ r = 0;
+ goto finish;
+ }
+
+ case LOOKUP_MEMBERSHIP: {
+ struct membership_data {
+ const char *user_name;
+ const char *group_name;
+ } membership_data = {};
+
+ static const JsonDispatch dispatch_table[] = {
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct membership_data, user_name), JSON_SAFE },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct membership_data, group_name), JSON_SAFE },
+ {}
+ };
+
+ assert(!iterator->found_user_name);
+ assert(!iterator->found_group_name);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &membership_data);
+ if (r < 0)
+ goto finish;
+
+ iterator->found_user_name = mfree(iterator->found_user_name);
+ iterator->found_group_name = mfree(iterator->found_group_name);
+
+ iterator->found_user_name = strdup(membership_data.user_name);
+ if (!iterator->found_user_name) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ iterator->found_group_name = strdup(membership_data.group_name);
+ if (!iterator->found_group_name) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ iterator->n_found++;
+
+ if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
+ return 0;
+
+ r = 0;
+ goto finish;
+ }
+
+ default:
+ assert_not_reached("unexpected lookup");
+ }
+
+finish:
+ /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
+ * errors if at least one connection ended cleanly */
+ if (r == -ESRCH || iterator->error == 0)
+ iterator->error = -r;
+
+ assert_se(set_remove(iterator->links, link) == link);
+ link = varlink_unref(link);
+ return 0;
+}
+
+static int userdb_connect(
+ UserDBIterator *iterator,
+ const char *path,
+ const char *method,
+ bool more,
+ JsonVariant *query) {
+
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+ int r;
+
+ assert(iterator);
+ assert(path);
+ assert(method);
+
+ r = varlink_connect_address(&vl, path);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to connect to %s: %m", path);
+
+ varlink_set_userdata(vl, iterator);
+
+ if (!iterator->event) {
+ r = sd_event_new(&iterator->event);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to allocate event loop: %m");
+ }
+
+ r = varlink_attach_event(vl, iterator->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+ (void) varlink_set_description(vl, path);
+
+ r = varlink_bind_reply(vl, userdb_on_query_reply);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to bind reply callback: %m");
+
+ if (more)
+ r = varlink_observe(vl, method, query);
+ else
+ r = varlink_invoke(vl, method, query);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to invoke varlink method: %m");
+
+ r = set_ensure_allocated(&iterator->links, &link_hash_ops);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to allocate set: %m");
+
+ r = set_put(iterator->links, vl);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add varlink connection to set: %m");
+
+ TAKE_PTR(vl);
+ return r;
+}
+
+static int userdb_start_query(
+ UserDBIterator *iterator,
+ const char *method,
+ bool more,
+ JsonVariant *query,
+ UserDBFlags flags) {
+
+ _cleanup_(strv_freep) char **except = NULL, **only = NULL;
+ _cleanup_(closedirp) DIR *d = NULL;
+ struct dirent *de;
+ const char *e;
+ int r, ret = 0;
+
+ assert(iterator);
+ assert(method);
+
+ e = getenv("SYSTEMD_BYPASS_USERDB");
+ if (e) {
+ r = parse_boolean(e);
+ if (r > 0)
+ return -ENOLINK;
+ if (r < 0) {
+ except = strv_split(e, ":");
+ if (!except)
+ return -ENOMEM;
+ }
+ }
+
+ e = getenv("SYSTEMD_ONLY_USERDB");
+ if (e) {
+ only = strv_split(e, ":");
+ if (!only)
+ return -ENOMEM;
+ }
+
+ /* First, let's talk to the multiplexer, if we can */
+ if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_AVOID_DYNAMIC_USER|USERDB_AVOID_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
+ !strv_contains(except, "io.systemd.Multiplexer") &&
+ (!only || strv_contains(only, "io.systemd.Multiplexer"))) {
+ _cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
+
+ r = json_variant_set_field_string(&patched_query, "service", "io.systemd.Multiplexer");
+ if (r < 0)
+ return log_debug_errno(r, "Unable to set service JSON field: %m");
+
+ r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
+ if (r >= 0) {
+ iterator->nss_covered = true; /* The multiplexer does NSS */
+ return 0;
+ }
+ }
+
+ d = opendir("/run/systemd/userdb/");
+ if (!d) {
+ if (errno == ENOENT)
+ return -ESRCH;
+
+ return -errno;
+ }
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ _cleanup_(json_variant_unrefp) JsonVariant *patched_query = NULL;
+ _cleanup_free_ char *p = NULL;
+ bool is_nss;
+
+ if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
+ continue;
+
+ if (FLAGS_SET(flags, USERDB_AVOID_DYNAMIC_USER) &&
+ streq(de->d_name, "io.systemd.DynamicUser"))
+ continue;
+
+ /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
+ * multiplexer, since in that case it's safer to do NSS in the client side emulation below
+ * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
+ * anyway). */
+ is_nss = streq(de->d_name, "io.systemd.NameServiceSwitch");
+ if ((flags & (USERDB_AVOID_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
+ continue;
+
+ if (strv_contains(except, de->d_name))
+ continue;
+
+ if (only && !strv_contains(only, de->d_name))
+ continue;
+
+ p = path_join("/run/systemd/userdb/", de->d_name);
+ if (!p)
+ return -ENOMEM;
+
+ patched_query = json_variant_ref(query);
+ r = json_variant_set_field_string(&patched_query, "service", de->d_name);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to set service JSON field: %m");
+
+ r = userdb_connect(iterator, p, method, more, patched_query);
+ if (is_nss && r >= 0) /* Turn off fallback NSS if we found the NSS service and could connect
+ * to it */
+ iterator->nss_covered = true;
+
+ if (ret == 0 && r < 0)
+ ret = r;
+ }
+
+ if (set_isempty(iterator->links))
+ return ret; /* propagate last error we saw if we couldn't connect to anything. */
+
+ /* We connected to some services, in this case, ignore the ones we failed on */
+ return 0;
+}
+
+static int userdb_process(
+ UserDBIterator *iterator,
+ UserRecord **ret_user_record,
+ GroupRecord **ret_group_record,
+ char **ret_user_name,
+ char **ret_group_name) {
+
+ int r;
+
+ assert(iterator);
+
+ for (;;) {
+ if (iterator->what == LOOKUP_USER && iterator->found_user) {
+ if (ret_user_record)
+ *ret_user_record = TAKE_PTR(iterator->found_user);
+ else
+ iterator->found_user = user_record_unref(iterator->found_user);
+
+ if (ret_group_record)
+ *ret_group_record = NULL;
+ if (ret_user_name)
+ *ret_user_name = NULL;
+ if (ret_group_name)
+ *ret_group_name = NULL;
+
+ return 0;
+ }
+
+ if (iterator->what == LOOKUP_GROUP && iterator->found_group) {
+ if (ret_group_record)
+ *ret_group_record = TAKE_PTR(iterator->found_group);
+ else
+ iterator->found_group = group_record_unref(iterator->found_group);
+
+ if (ret_user_record)
+ *ret_user_record = NULL;
+ if (ret_user_name)
+ *ret_user_name = NULL;
+ if (ret_group_name)
+ *ret_group_name = NULL;
+
+ return 0;
+ }
+
+ if (iterator->what == LOOKUP_MEMBERSHIP && iterator->found_user_name && iterator->found_group_name) {
+ if (ret_user_name)
+ *ret_user_name = TAKE_PTR(iterator->found_user_name);
+ else
+ iterator->found_user_name = mfree(iterator->found_user_name);
+
+ if (ret_group_name)
+ *ret_group_name = TAKE_PTR(iterator->found_group_name);
+ else
+ iterator->found_group_name = mfree(iterator->found_group_name);
+
+ if (ret_user_record)
+ *ret_user_record = NULL;
+ if (ret_group_record)
+ *ret_group_record = NULL;
+
+ return 0;
+ }
+
+ if (set_isempty(iterator->links)) {
+ if (iterator->error == 0)
+ return -ESRCH;
+
+ return -abs(iterator->error);
+ }
+
+ if (!iterator->event)
+ return -ESRCH;
+
+ r = sd_event_run(iterator->event, UINT64_MAX);
+ if (r < 0)
+ return r;
+ }
+}
+
+static int synthetic_root_user_build(UserRecord **ret) {
+ return user_record_build(
+ ret,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
+ JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+static int synthetic_nobody_user_build(UserRecord **ret) {
+ return user_record_build(
+ ret,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME)),
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+ JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
+ JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+ int r;
+
+ if (!valid_user_group_name_compat(name))
+ return -EINVAL;
+
+ r = json_build(&query, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name))));
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_USER);
+ if (!iterator)
+ return -ENOMEM;
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
+ if (r >= 0) {
+ r = userdb_process(iterator, ret, NULL, NULL, NULL);
+ if (r >= 0)
+ return r;
+ }
+
+ if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
+ /* Make sure the NSS lookup doesn't recurse back to us. (EBUSY is fine here, it just means we
+ * already took the lock from our thread, which is totally OK.) */
+ r = userdb_nss_compat_disable();
+ if (r >= 0 || r == -EBUSY) {
+ iterator->nss_lock = r;
+
+ /* Client-side NSS fallback */
+ r = nss_user_record_by_name(name, ret);
+ if (r >= 0)
+ return r;
+ }
+ }
+
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+ if (streq(name, "root"))
+ return synthetic_root_user_build(ret);
+
+ if (streq(name, NOBODY_USER_NAME) && synthesize_nobody())
+ return synthetic_nobody_user_build(ret);
+ }
+
+ return r;
+}
+
+int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+ int r;
+
+ if (!uid_is_valid(uid))
+ return -EINVAL;
+
+ r = json_build(&query, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid))));
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_USER);
+ if (!iterator)
+ return -ENOMEM;
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
+ if (r >= 0) {
+ r = userdb_process(iterator, ret, NULL, NULL, NULL);
+ if (r >= 0)
+ return r;
+ }
+
+ if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
+ r = userdb_nss_compat_disable();
+ if (r >= 0 || r == -EBUSY) {
+ iterator->nss_lock = r;
+
+ /* Client-side NSS fallback */
+ r = nss_user_record_by_uid(uid, ret);
+ if (r >= 0)
+ return r;
+ }
+ }
+
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+ if (uid == 0)
+ return synthetic_root_user_build(ret);
+
+ if (uid == UID_NOBODY && synthesize_nobody())
+ return synthetic_nobody_user_build(ret);
+ }
+
+ return r;
+}
+
+int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ int r;
+
+ assert(ret);
+
+ iterator = userdb_iterator_new(LOOKUP_USER);
+ if (!iterator)
+ return -ENOMEM;
+
+ iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
+
+ if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
+ iterator->nss_lock = userdb_nss_compat_disable();
+ if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
+ return iterator->nss_lock;
+
+ setpwent();
+ iterator->nss_iterating = true;
+ } else if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(iterator);
+ return 0;
+}
+
+int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
+ int r;
+
+ assert(iterator);
+ assert(iterator->what == LOOKUP_USER);
+
+ if (iterator->nss_iterating) {
+ struct passwd *pw;
+
+ /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
+ * the more traditional sources, which are probably good to show first. */
+
+ pw = getpwent();
+ if (pw) {
+ _cleanup_free_ char *buffer = NULL;
+ bool incomplete = false;
+ struct spwd spwd;
+
+ if (streq_ptr(pw->pw_name, "root"))
+ iterator->synthesize_root = false;
+ if (pw->pw_uid == UID_NOBODY)
+ iterator->synthesize_nobody = false;
+
+ r = nss_spwd_for_passwd(pw, &spwd, &buffer);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
+ incomplete = ERRNO_IS_PRIVILEGE(r);
+ }
+
+ r = nss_passwd_to_user_record(pw, r >= 0 ? &spwd : NULL, ret);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ (*ret)->incomplete = incomplete;
+ return r;
+ }
+
+ if (errno != 0)
+ log_debug_errno(errno, "Failure to iterate NSS user database, ignoring: %m");
+
+ iterator->nss_iterating = false;
+ endpwent();
+ }
+
+ r = userdb_process(iterator, ret, NULL, NULL, NULL);
+
+ if (r < 0) {
+ if (iterator->synthesize_root) {
+ iterator->synthesize_root = false;
+ iterator->n_found++;
+ return synthetic_root_user_build(ret);
+ }
+
+ if (iterator->synthesize_nobody) {
+ iterator->synthesize_nobody = false;
+ iterator->n_found++;
+ return synthetic_nobody_user_build(ret);
+ }
+ }
+
+ /* if we found at least one entry, then ignore errors and indicate that we reached the end */
+ if (r < 0 && iterator->n_found > 0)
+ return -ESRCH;
+
+ return r;
+}
+
+static int synthetic_root_group_build(GroupRecord **ret) {
+ return group_record_build(
+ ret,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+static int synthetic_nobody_group_build(GroupRecord **ret) {
+ return group_record_build(
+ ret,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME)),
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+ JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+ int r;
+
+ if (!valid_user_group_name_compat(name))
+ return -EINVAL;
+
+ r = json_build(&query, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name))));
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_GROUP);
+ if (!iterator)
+ return -ENOMEM;
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
+ if (r >= 0) {
+ r = userdb_process(iterator, NULL, ret, NULL, NULL);
+ if (r >= 0)
+ return r;
+ }
+
+ if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
+ r = userdb_nss_compat_disable();
+ if (r >= 0 || r == -EBUSY) {
+ iterator->nss_lock = r;
+
+ r = nss_group_record_by_name(name, ret);
+ if (r >= 0)
+ return r;
+ }
+ }
+
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+ if (streq(name, "root"))
+ return synthetic_root_group_build(ret);
+
+ if (streq(name, NOBODY_GROUP_NAME) && synthesize_nobody())
+ return synthetic_nobody_group_build(ret);
+ }
+
+ return r;
+}
+
+int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+ int r;
+
+ if (!gid_is_valid(gid))
+ return -EINVAL;
+
+ r = json_build(&query, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid))));
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_GROUP);
+ if (!iterator)
+ return -ENOMEM;
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
+ if (r >= 0) {
+ r = userdb_process(iterator, NULL, ret, NULL, NULL);
+ if (r >= 0)
+ return r;
+ }
+
+ if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
+ r = userdb_nss_compat_disable();
+ if (r >= 0 || r == -EBUSY) {
+ iterator->nss_lock = r;
+
+ r = nss_group_record_by_gid(gid, ret);
+ if (r >= 0)
+ return r;
+ }
+ }
+
+ if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+ if (gid == 0)
+ return synthetic_root_group_build(ret);
+
+ if (gid == GID_NOBODY && synthesize_nobody())
+ return synthetic_nobody_group_build(ret);
+ }
+
+ return r;
+}
+
+int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ int r;
+
+ assert(ret);
+
+ iterator = userdb_iterator_new(LOOKUP_GROUP);
+ if (!iterator)
+ return -ENOMEM;
+
+ iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
+
+ if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
+ iterator->nss_lock = userdb_nss_compat_disable();
+ if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
+ return iterator->nss_lock;
+
+ setgrent();
+ iterator->nss_iterating = true;
+ } if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(iterator);
+ return 0;
+}
+
+int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
+ int r;
+
+ assert(iterator);
+ assert(iterator->what == LOOKUP_GROUP);
+
+ if (iterator->nss_iterating) {
+ struct group *gr;
+
+ errno = 0;
+ gr = getgrent();
+ if (gr) {
+ _cleanup_free_ char *buffer = NULL;
+ bool incomplete = false;
+ struct sgrp sgrp;
+
+ if (streq_ptr(gr->gr_name, "root"))
+ iterator->synthesize_root = false;
+ if (gr->gr_gid == GID_NOBODY)
+ iterator->synthesize_nobody = false;
+
+ r = nss_sgrp_for_group(gr, &sgrp, &buffer);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", gr->gr_name);
+ incomplete = ERRNO_IS_PRIVILEGE(r);
+ }
+
+ r = nss_group_to_group_record(gr, r >= 0 ? &sgrp : NULL, ret);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ (*ret)->incomplete = incomplete;
+ return r;
+ }
+
+ if (errno != 0)
+ log_debug_errno(errno, "Failure to iterate NSS group database, ignoring: %m");
+
+ iterator->nss_iterating = false;
+ endgrent();
+ }
+
+ r = userdb_process(iterator, NULL, ret, NULL, NULL);
+ if (r < 0) {
+ if (iterator->synthesize_root) {
+ iterator->synthesize_root = false;
+ iterator->n_found++;
+ return synthetic_root_group_build(ret);
+ }
+
+ if (iterator->synthesize_nobody) {
+ iterator->synthesize_nobody = false;
+ iterator->n_found++;
+ return synthetic_nobody_group_build(ret);
+ }
+ }
+
+ /* if we found at least one entry, then ignore errors and indicate that we reached the end */
+ if (r < 0 && iterator->n_found > 0)
+ return -ESRCH;
+
+ return r;
+}
+
+int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+ int r;
+
+ assert(ret);
+
+ if (!valid_user_group_name_compat(name))
+ return -EINVAL;
+
+ r = json_build(&query, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name))));
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
+ if (!iterator)
+ return -ENOMEM;
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
+ if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
+ goto finish;
+
+ iterator->nss_lock = userdb_nss_compat_disable();
+ if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
+ return iterator->nss_lock;
+
+ iterator->filter_user_name = strdup(name);
+ if (!iterator->filter_user_name)
+ return -ENOMEM;
+
+ setgrent();
+ iterator->nss_iterating = true;
+
+ r = 0;
+
+finish:
+ if (r >= 0)
+ *ret = TAKE_PTR(iterator);
+ return r;
+}
+
+int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+ int r;
+
+ assert(ret);
+
+ if (!valid_user_group_name_compat(name))
+ return -EINVAL;
+
+ r = json_build(&query, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name))));
+ if (r < 0)
+ return r;
+
+ iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
+ if (!iterator)
+ return -ENOMEM;
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
+ if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
+ goto finish;
+
+ iterator->nss_lock = userdb_nss_compat_disable();
+ if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
+ return iterator->nss_lock;
+
+ /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
+ (void) nss_group_record_by_name(name, &gr);
+ if (gr) {
+ iterator->members_of_group = strv_copy(gr->members);
+ if (!iterator->members_of_group)
+ return -ENOMEM;
+
+ iterator->index_members_of_group = 0;
+
+ iterator->found_group_name = strdup(name);
+ if (!iterator->found_group_name)
+ return -ENOMEM;
+ }
+
+ r = 0;
+
+finish:
+ if (r >= 0)
+ *ret = TAKE_PTR(iterator);
+
+ return r;
+}
+
+int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ int r;
+
+ assert(ret);
+
+ iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
+ if (!iterator)
+ return -ENOMEM;
+
+ r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
+ if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
+ goto finish;
+
+ iterator->nss_lock = userdb_nss_compat_disable();
+ if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
+ return iterator->nss_lock;
+
+ setgrent();
+ iterator->nss_iterating = true;
+
+ r = 0;
+
+finish:
+ if (r >= 0)
+ *ret = TAKE_PTR(iterator);
+
+ return r;
+}
+
+int membershipdb_iterator_get(
+ UserDBIterator *iterator,
+ char **ret_user,
+ char **ret_group) {
+
+ int r;
+
+ assert(iterator);
+
+ for (;;) {
+ /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
+ if (!iterator->members_of_group) {
+ struct group *g;
+
+ if (!iterator->nss_iterating)
+ break;
+
+ assert(!iterator->found_user_name);
+ do {
+ errno = 0;
+ g = getgrent();
+ if (!g) {
+ if (errno != 0)
+ log_debug_errno(errno, "Failure during NSS group iteration, ignoring: %m");
+ break;
+ }
+
+ } while (iterator->filter_user_name ? !strv_contains(g->gr_mem, iterator->filter_user_name) :
+ strv_isempty(g->gr_mem));
+
+ if (g) {
+ r = free_and_strdup(&iterator->found_group_name, g->gr_name);
+ if (r < 0)
+ return r;
+
+ if (iterator->filter_user_name)
+ iterator->members_of_group = strv_new(iterator->filter_user_name);
+ else
+ iterator->members_of_group = strv_copy(g->gr_mem);
+ if (!iterator->members_of_group)
+ return -ENOMEM;
+
+ iterator->index_members_of_group = 0;
+ } else {
+ iterator->nss_iterating = false;
+ endgrent();
+ break;
+ }
+ }
+
+ assert(iterator->found_group_name);
+ assert(iterator->members_of_group);
+ assert(!iterator->found_user_name);
+
+ if (iterator->members_of_group[iterator->index_members_of_group]) {
+ _cleanup_free_ char *cu = NULL, *cg = NULL;
+
+ if (ret_user) {
+ cu = strdup(iterator->members_of_group[iterator->index_members_of_group]);
+ if (!cu)
+ return -ENOMEM;
+ }
+
+ if (ret_group) {
+ cg = strdup(iterator->found_group_name);
+ if (!cg)
+ return -ENOMEM;
+ }
+
+ if (ret_user)
+ *ret_user = TAKE_PTR(cu);
+
+ if (ret_group)
+ *ret_group = TAKE_PTR(cg);
+
+ iterator->index_members_of_group++;
+ return 0;
+ }
+
+ iterator->members_of_group = strv_free(iterator->members_of_group);
+ iterator->found_group_name = mfree(iterator->found_group_name);
+ }
+
+ r = userdb_process(iterator, NULL, NULL, ret_user, ret_group);
+ if (r < 0 && iterator->n_found > 0)
+ return -ESRCH;
+
+ return r;
+}
+
+int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_strv_free_ char **members = NULL;
+ int r;
+
+ assert(name);
+ assert(ret);
+
+ r = membershipdb_by_group(name, flags, &iterator);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *user_name = NULL;
+
+ r = membershipdb_iterator_get(iterator, &user_name, NULL);
+ if (r == -ESRCH)
+ break;
+ if (r < 0)
+ return r;
+
+ r = strv_consume(&members, TAKE_PTR(user_name));
+ if (r < 0)
+ return r;
+ }
+
+ strv_sort(members);
+ strv_uniq(members);
+
+ *ret = TAKE_PTR(members);
+ return 0;
+}
+
+static int userdb_thread_sockaddr(struct sockaddr_un *ret_sa, socklen_t *ret_salen) {
+ static const uint8_t
+ k1[16] = { 0x35, 0xc1, 0x1f, 0x41, 0x59, 0xc6, 0xa0, 0xf9, 0x33, 0x4b, 0x17, 0x3d, 0xb9, 0xf6, 0x14, 0xd9 },
+ k2[16] = { 0x6a, 0x11, 0x4c, 0x37, 0xe5, 0xa3, 0x8c, 0xa6, 0x93, 0x55, 0x64, 0x8c, 0x93, 0xee, 0xa1, 0x7b };
+
+ struct siphash sh;
+ uint64_t x, y;
+ pid_t tid;
+ void *p;
+
+ assert(ret_sa);
+ assert(ret_salen);
+
+ /* This calculates an AF_UNIX socket address in the abstract namespace whose existence works as an
+ * indicator whether to emulate NSS records for complex user records that are also available via the
+ * varlink protocol. The name of the socket is picked in a way so that:
+ *
+ * → it is per-thread (by hashing from the TID)
+ *
+ * → is not guessable for foreign processes (by hashing from the — hopefully secret — AT_RANDOM
+ * value every process gets passed from the kernel
+ *
+ * By using a socket the NSS emulation can be nicely turned off for limited amounts of time only,
+ * simply controlled by the lifetime of the fd itself. By using an AF_UNIX socket in the abstract
+ * namespace the lock is automatically cleaned up when the process dies abnormally.
+ *
+ */
+
+ p = ULONG_TO_PTR(getauxval(AT_RANDOM));
+ if (!p)
+ return -EIO;
+
+ tid = gettid();
+
+ siphash24_init(&sh, k1);
+ siphash24_compress(p, 16, &sh);
+ siphash24_compress(&tid, sizeof(tid), &sh);
+ x = siphash24_finalize(&sh);
+
+ siphash24_init(&sh, k2);
+ siphash24_compress(p, 16, &sh);
+ siphash24_compress(&tid, sizeof(tid), &sh);
+ y = siphash24_finalize(&sh);
+
+ *ret_sa = (struct sockaddr_un) {
+ .sun_family = AF_UNIX,
+ };
+
+ sprintf(ret_sa->sun_path + 1, "userdb-%016" PRIx64 "%016" PRIx64, x, y);
+ *ret_salen = offsetof(struct sockaddr_un, sun_path) + 1 + 7 + 32;
+
+ return 0;
+}
+
+int userdb_nss_compat_is_enabled(void) {
+ _cleanup_close_ int fd = -1;
+ union sockaddr_union sa;
+ socklen_t salen;
+ int r;
+
+ /* Tests whether the NSS compatibility logic is currently turned on for the invoking thread. Returns
+ * true if NSS compatibility is turned on, i.e. whether NSS records shall be synthesized from complex
+ * user records. */
+
+ r = userdb_thread_sockaddr(&sa.un, &salen);
+ if (r < 0)
+ return r;
+
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ /* Try to connect(). This doesn't do anything really, except that it checks whether the socket
+ * address is bound at all. */
+ if (connect(fd, &sa.sa, salen) < 0) {
+ if (errno == ECONNREFUSED) /* the socket is not bound, hence NSS emulation shall be done */
+ return true;
+
+ return -errno;
+ }
+
+ return false;
+}
+
+int userdb_nss_compat_disable(void) {
+ _cleanup_close_ int fd = -1;
+ union sockaddr_union sa;
+ socklen_t salen;
+ int r;
+
+ /* Turn off the NSS compatibility logic for the invoking thread. By default NSS records are
+ * synthesized for all complex user records looked up via NSS. If this call is invoked this is
+ * disabled for the invoking thread, but only for it. A caller that natively supports the varlink
+ * user record protocol may use that to turn off the compatibility for NSS lookups. */
+
+ r = userdb_thread_sockaddr(&sa.un, &salen);
+ if (r < 0)
+ return r;
+
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return -errno;
+
+ if (bind(fd, &sa.sa, salen) < 0) {
+ if (errno == EADDRINUSE) /* lock already taken, convert this into a recognizable error */
+ return -EBUSY;
+
+ return -errno;
+ }
+
+ return TAKE_FD(fd);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "group-record.h"
+#include "user-record.h"
+
+/* Inquire local services for user/group records */
+
+typedef struct UserDBIterator UserDBIterator;
+
+UserDBIterator *userdb_iterator_free(UserDBIterator *iterator);
+DEFINE_TRIVIAL_CLEANUP_FUNC(UserDBIterator*, userdb_iterator_free);
+
+typedef enum UserDBFlags {
+ USERDB_AVOID_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
+ USERDB_AVOID_DYNAMIC_USER = 1 << 1, /* exclude looking up in io.systemd.DynamicUser */
+ USERDB_AVOID_MULTIPLEXER = 1 << 2, /* exclude looking up via io.systemd.Multiplexer */
+ USERDB_DONT_SYNTHESIZE = 1 << 3, /* don't synthesize root/nobody */
+} UserDBFlags;
+
+int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);
+int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret);
+int userdb_all(UserDBFlags flags, UserDBIterator **ret);
+int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret);
+
+int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret);
+int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret);
+int groupdb_all(UserDBFlags flags, UserDBIterator **ret);
+int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret);
+
+int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret);
+int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret);
+int membershipdb_all(UserDBFlags flags, UserDBIterator **ret);
+int membershipdb_iterator_get(UserDBIterator *iterator, char **user, char **group);
+int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret);
+
+int userdb_nss_compat_is_enabled(void);
+int userdb_nss_compat_disable(void);
Hashmap *methods;
VarlinkConnect connect_callback;
+ VarlinkDisconnect disconnect_callback;
sd_event *event;
int64_t event_priority;
int varlink_connect_address(Varlink **ret, const char *address) {
_cleanup_(varlink_unrefp) Varlink *v = NULL;
union sockaddr_union sockaddr;
+ socklen_t sockaddr_len;
int r;
assert_return(ret, -EINVAL);
r = sockaddr_un_set_path(&sockaddr.un, address);
if (r < 0)
return r;
+ sockaddr_len = r;
r = varlink_new(&v);
if (r < 0)
v->fd = fd_move_above_stdio(v->fd);
- if (connect(v->fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0) {
+ if (connect(v->fd, &sockaddr.sa, sockaddr_len) < 0) {
if (!IN_SET(errno, EAGAIN, EINPROGRESS))
return -errno;
}
static void varlink_detach_server(Varlink *v) {
+ VarlinkServer *saved_server;
assert(v);
if (!v->server)
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);
+ * connection from each other. This drops the dangling reference that connect_callback() set up. But
+ * before we release the references, let's call the disconnection callback if it is defined. */
+
+ saved_server = TAKE_PTR(v->server);
+
+ if (saved_server->disconnect_callback)
+ saved_server->disconnect_callback(saved_server, v, saved_server->userdata);
+
+ varlink_server_unref(saved_server);
varlink_unref(v);
}
int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t m) {
union sockaddr_union sockaddr;
+ socklen_t sockaddr_len;
_cleanup_close_ int fd = -1;
int r;
r = sockaddr_un_set_path(&sockaddr.un, address);
if (r < 0)
return r;
+ sockaddr_len = r;
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
(void) sockaddr_un_unlink(&sockaddr.un);
RUN_WITH_UMASK(~m & 0777)
- if (bind(fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+ if (bind(fd, &sockaddr.sa, sockaddr_len) < 0)
return -errno;
if (listen(fd, SOMAXCONN) < 0)
return 0;
}
+int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback) {
+ assert_return(s, -EINVAL);
+
+ if (callback && s->disconnect_callback && callback != s->disconnect_callback)
+ return -EBUSY;
+
+ s->disconnect_callback = callback;
+ return 0;
+}
+
unsigned varlink_server_connections_max(VarlinkServer *s) {
int dts;
return 0;
}
+unsigned varlink_server_current_connections(VarlinkServer *s) {
+ assert_return(s, UINT_MAX);
+
+ return s->n_connections;
+}
+
int varlink_server_set_description(VarlinkServer *s, const char *description) {
assert_return(s, -EINVAL);
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);
+typedef void (*VarlinkDisconnect)(VarlinkServer *server, Varlink *link, void *userdata);
int varlink_connect_address(Varlink **ret, const char *address);
int varlink_connect_fd(Varlink **ret, int fd);
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);
+int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect disconnect);
void* varlink_server_set_userdata(VarlinkServer *s, void *userdata);
void* varlink_server_get_userdata(VarlinkServer *s);
int varlink_server_set_connections_per_uid_max(VarlinkServer *s, unsigned m);
int varlink_server_set_connections_max(VarlinkServer *s, unsigned m);
+unsigned varlink_server_current_connections(VarlinkServer *s);
+
int varlink_server_set_description(VarlinkServer *s, const char *description);
DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref);
#include "sd-messages.h"
#include "btrfs-util.h"
+#include "bus-error.h"
#include "def.h"
#include "exec-util.h"
#include "fd-util.h"
-#include "format-util.h"
#include "fileio.h"
+#include "format-util.h"
#include "log.h"
#include "main-func.h"
#include "parse-util.h"
return r;
}
+static int lock_all_homes(void) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r;
+
+ /* Let's synchronously lock all home directories managed by homed that have been marked for it. This
+ * way the key material required to access these volumes is hopefully removed from memory. */
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to connect to system bus, ignoring: %m");
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.home1",
+ "/org/freedesktop/home1",
+ "org.freedesktop.home1.Manager",
+ "LockAllHomes");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* If homed is not running it can't have any home directories active either. */
+ r = sd_bus_message_set_auto_start(m, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable auto-start of LockAllHomes() message: %m");
+
+ r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC, &error, NULL);
+ if (r < 0) {
+ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
+ sd_bus_error_has_name(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
+ log_debug("systemd-homed is not running, skipping locking of home directories.");
+ return 0;
+ }
+
+ return log_error_errno(r, "Failed to lock home directories: %s", bus_error_message(&error, r));
+ }
+
+ log_debug("Successfully requested for all home directories to be locked.");
+ return 0;
+}
+
static int execute(char **modes, char **states) {
char *arguments[] = {
NULL,
}
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
+ (void) lock_all_homes();
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
.ai_flags = AI_ADDRCONFIG
};
- union sockaddr_union sa = {};
const char *node, *service;
int r;
if (IN_SET(arg_remote_host[0], '/', '@')) {
- int salen;
+ union sockaddr_union sa;
+ int sa_len;
- salen = sockaddr_un_set_path(&sa.un, arg_remote_host);
- if (salen < 0) {
- log_error_errno(salen, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
+ r = sockaddr_un_set_path(&sa.un, arg_remote_host);
+ if (r < 0) {
+ log_error_errno(r, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
goto fail;
}
+ sa_len = r;
- return connection_start(c, &sa.sa, salen);
+ return connection_start(c, &sa.sa, sa_len);
}
service = strrchr(arg_remote_host, ':');
#include "conf-files.h"
#include "def.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "glob-util.h"
#include "hashmap.h"
#include "log.h"
#include "main-func.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free);
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free);
+static bool test_prefix(const char *p) {
+ char **i;
+
+ if (strv_isempty(arg_prefixes))
+ return true;
+
+ STRV_FOREACH(i, arg_prefixes) {
+ const char *t;
+
+ t = path_startswith(*i, "/proc/sys/");
+ if (!t)
+ t = *i;
+
+ if (path_startswith(p, t))
+ return true;
+ }
+
+ return false;
+}
+
static Option *option_new(
const char *key,
const char *value,
_cleanup_(option_freep) Option *o = NULL;
assert(key);
- assert(value);
o = new(Option, 1);
if (!o)
*o = (Option) {
.key = strdup(key),
- .value = strdup(value),
+ .value = value ? strdup(value) : NULL,
.ignore_failure = ignore_failure,
};
- if (!o->key || !o->value)
+ if (!o->key)
+ return NULL;
+ if (value && !o->value)
return NULL;
return TAKE_PTR(o);
}
+static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure) {
+ int r;
+
+ r = sysctl_write(key, value);
+ if (r < 0) {
+ /* If the sysctl is not available in the kernel or we are running with reduced privileges and
+ * cannot write it, then log about the issue, and proceed without failing. (EROFS is treated
+ * as a permission problem here, since that's how container managers usually protected their
+ * sysctls.) In all other cases log an error and make the tool fail. */
+ if (ignore_failure || r == -EROFS || ERRNO_IS_PRIVILEGE(r))
+ log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
+ else if (r == -ENOENT)
+ log_info_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
+ else
+ return log_error_errno(r, "Couldn't write '%s' to '%s': %m", value, key);
+ }
+
+ return 0;
+}
+
static int apply_all(OrderedHashmap *sysctl_options) {
Option *option;
Iterator i;
ORDERED_HASHMAP_FOREACH(option, sysctl_options, i) {
int k;
- k = sysctl_write(option->key, option->value);
- if (k < 0) {
- /* If the sysctl is not available in the kernel or we are running with reduced
- * privileges and cannot write it, then log about the issue at LOG_NOTICE level, and
- * proceed without failing. (EROFS is treated as a permission problem here, since
- * that's how container managers usually protected their sysctls.) In all other cases
- * log an error and make the tool fail. */
+ /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
+ if (!option->value)
+ continue;
- if (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT) || option->ignore_failure)
- log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", option->value, option->key);
- else {
- log_error_errno(k, "Couldn't write '%s' to '%s': %m", option->value, option->key);
- if (r == 0)
- r = k;
- }
- }
- }
+ if (string_is_glob(option->key)) {
+ _cleanup_strv_free_ char **paths = NULL;
+ _cleanup_free_ char *pattern = NULL;
+ char **s;
- return r;
-}
+ pattern = path_join("/proc/sys", option->key);
+ if (!pattern)
+ return log_oom();
-static bool test_prefix(const char *p) {
- char **i;
+ k = glob_extend(&paths, pattern);
+ if (k < 0) {
+ if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r))
+ log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m",
+ option->key);
+ else {
+ log_error_errno(k, "Couldn't resolve glob '%s': %m",
+ option->key);
+ if (r == 0)
+ r = k;
+ }
- if (strv_isempty(arg_prefixes))
- return true;
+ } else if (strv_isempty(paths))
+ log_debug("No match for glob: %s", option->key);
- STRV_FOREACH(i, arg_prefixes) {
- const char *t;
+ STRV_FOREACH(s, paths) {
+ const char *key;
- t = path_startswith(*i, "/proc/sys/");
- if (!t)
- t = *i;
- if (path_startswith(p, t))
- return true;
+ assert_se(key = path_startswith(*s, "/proc/sys"));
+
+ if (!test_prefix(key))
+ continue;
+
+ if (ordered_hashmap_contains(sysctl_options, key)) {
+ log_info("Not setting %s (explicit setting exists).", key);
+ continue;
+ }
+
+ k = sysctl_write_or_warn(key, option->value, option->ignore_failure);
+ if (r == 0)
+ r = k;
+ }
+
+ } else {
+ k = sysctl_write_or_warn(option->key, option->value, option->ignore_failure);
+ if (r == 0)
+ r = k;
+ }
}
- return false;
+ return r;
}
-static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ignore_enoent) {
+static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
unsigned c = 0;
int r;
for (;;) {
_cleanup_(option_freep) Option *new_option = NULL;
_cleanup_free_ char *l = NULL;
- bool ignore_failure;
+ bool ignore_failure = false;
Option *existing;
char *p, *value;
int k;
continue;
value = strchr(p, '=');
- if (!value) {
- log_syntax(NULL, LOG_WARNING, path, c, 0, "Line is not an assignment, ignoring: %s", p);
- if (r == 0)
- r = -EINVAL;
- continue;
- }
+ if (value) {
+ if (p[0] == '-') {
+ ignore_failure = true;
+ p++;
+ }
- *value = 0;
- value++;
+ *value = 0;
+ value++;
+ value = strstrip(value);
- p = strstrip(p);
- ignore_failure = p[0] == '-';
- if (ignore_failure)
- p++;
+ } else {
+ if (p[0] == '-')
+ /* We have a "negative match" option. Let's continue with value==NULL. */
+ p++;
+ else {
+ log_syntax(NULL, LOG_WARNING, path, c, 0,
+ "Line is not an assignment, ignoring: %s", p);
+ if (r == 0)
+ r = -EINVAL;
+ continue;
+ }
+ }
+ p = strstrip(p);
p = sysctl_normalize(p);
- value = strstrip(value);
- if (!test_prefix(p))
+ /* We can't filter out globs at this point, we'll need to do that later. */
+ if (!string_is_glob(p) &&
+ !test_prefix(p))
continue;
- existing = ordered_hashmap_get(sysctl_options, p);
+ if (ordered_hashmap_ensure_allocated(sysctl_options, &option_hash_ops) < 0)
+ return log_oom();
+
+ existing = ordered_hashmap_get(*sysctl_options, p);
if (existing) {
- if (streq(value, existing->value)) {
+ if (streq_ptr(value, existing->value)) {
existing->ignore_failure = existing->ignore_failure || ignore_failure;
continue;
}
log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c);
- option_free(ordered_hashmap_remove(sysctl_options, p));
+ option_free(ordered_hashmap_remove(*sysctl_options, p));
}
new_option = option_new(p, value, ignore_failure);
if (!new_option)
return log_oom();
- k = ordered_hashmap_put(sysctl_options, new_option->key, new_option);
+ k = ordered_hashmap_put(*sysctl_options, new_option->key, new_option);
if (k < 0)
return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p);
umask(0022);
- sysctl_options = ordered_hashmap_new(&option_hash_ops);
- if (!sysctl_options)
- return log_oom();
-
if (argc > optind) {
int i;
r = 0;
for (i = optind; i < argc; i++) {
- k = parse_file(sysctl_options, argv[i], false);
+ k = parse_file(&sysctl_options, argv[i], false);
if (k < 0 && r == 0)
r = k;
}
}
STRV_FOREACH(f, files) {
- k = parse_file(sysctl_options, *f, true);
+ k = parse_file(&sysctl_options, *f, true);
if (k < 0 && r == 0)
r = k;
}
#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
+#include "format-table.h"
#include "format-util.h"
#include "fs-util.h"
#include "glob-util.h"
}
static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
- unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len, max_desc_len;
+ _cleanup_(table_unrefp) Table *table = NULL;
const UnitInfo *u;
- unsigned n_shown = 0;
int job_count = 0;
- bool full = arg_full || FLAGS_SET(arg_pager_flags, PAGER_DISABLE);
-
- max_id_len = STRLEN("UNIT");
- load_len = STRLEN("LOAD");
- active_len = STRLEN("ACTIVE");
- sub_len = STRLEN("SUB");
- job_len = STRLEN("JOB");
- max_desc_len = STRLEN("DESCRIPTION");
-
- for (u = unit_infos; u < unit_infos + c; u++) {
- max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0));
- load_len = MAX(load_len, strlen(u->load_state));
- active_len = MAX(active_len, strlen(u->active_state));
- sub_len = MAX(sub_len, strlen(u->sub_state));
- max_desc_len = MAX(max_desc_len, strlen(u->description));
-
- if (u->job_id != 0) {
- job_len = MAX(job_len, strlen(u->job_type));
- job_count++;
- }
-
- if (!arg_no_legend &&
- (streq(u->active_state, "failed") ||
- STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked")))
- circle_len = 2;
- }
-
- if (!arg_full && original_stdout_is_tty) {
- unsigned basic_len;
-
- id_len = MIN(max_id_len, 25u); /* as much as it needs, but at most 25 for now */
- basic_len = circle_len + 1 + id_len + 1 + load_len + 1 + active_len + 1 + sub_len + 1;
-
- if (job_count)
- basic_len += job_len + 1;
-
- if (basic_len < (unsigned) columns()) {
- unsigned extra_len, incr;
- extra_len = columns() - basic_len;
+ int r;
- /* Either UNIT already got 25, or is fully satisfied.
- * Grant up to 25 to DESC now. */
- incr = MIN(extra_len, 25u);
- desc_len = incr;
- extra_len -= incr;
+ table = table_new("", "unit", "load", "active", "sub", "job", "description");
+ if (!table)
+ return log_oom();
- /* Of the remainder give as much as the ID needs to the ID, and give the rest to the
- * description but not more than it needs. */
- if (extra_len > 0) {
- incr = MIN(max_id_len - id_len, extra_len);
- id_len += incr;
- desc_len += MIN(extra_len - incr, max_desc_len - desc_len);
- }
- } else
- desc_len = 0;
- } else {
- id_len = max_id_len;
- desc_len = max_desc_len;
- }
+ table_set_header(table, !arg_no_legend);
+ if (arg_full)
+ table_set_width(table, 0);
for (u = unit_infos; u < unit_infos + c; u++) {
- _cleanup_free_ char *e = NULL, *j = NULL;
- const char *on_underline = "", *off_underline = "";
- const char *on_loaded = "", *off_loaded = "";
- const char *on_active = "", *off_active = "";
- const char *on_circle = "", *off_circle = "";
- const char *id;
+ _cleanup_free_ char *j = NULL;
+ const char *on_underline = "", *on_loaded = "", *on_active = "";
+ const char *on_circle = "", *id;
bool circle = false, underline = false;
- if (!n_shown && !arg_no_legend) {
-
- if (circle_len > 0)
- fputs(" ", stdout);
-
- printf("%s%-*s %-*s %-*s %-*s ",
- ansi_underline(),
- id_len, "UNIT",
- load_len, "LOAD",
- active_len, "ACTIVE",
- sub_len, "SUB");
-
- if (job_count)
- printf("%-*s ", job_len, "JOB");
-
- printf("%-*.*s%s\n",
- desc_len,
- full ? -1 : (int) desc_len,
- "DESCRIPTION",
- ansi_normal());
- }
-
- n_shown++;
-
if (u + 1 < unit_infos + c &&
!streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id))) {
on_underline = ansi_underline();
- off_underline = ansi_normal();
underline = true;
}
if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) {
on_circle = ansi_highlight_yellow();
- off_circle = ansi_normal();
circle = true;
on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
- off_loaded = underline ? on_underline : ansi_normal();
} else if (streq(u->active_state, "failed") && !arg_plain) {
on_circle = ansi_highlight_red();
- off_circle = ansi_normal();
circle = true;
on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
- off_active = underline ? on_underline : ansi_normal();
+ } else {
+ on_active = on_underline;
+ on_loaded = on_underline;
}
if (u->machine) {
} else
id = u->id;
- if (arg_full) {
- e = ellipsize(id, id_len, 33);
- if (!e)
- return log_oom();
-
- id = e;
- }
-
- if (circle_len > 0)
- printf("%s%s%s ", on_circle, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_circle);
+ r = table_add_many(table,
+ TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
+ TABLE_SET_COLOR, on_circle,
+ TABLE_STRING, id,
+ TABLE_SET_COLOR, on_active,
+ TABLE_STRING, u->load_state,
+ TABLE_SET_COLOR, on_loaded,
+ TABLE_STRING, u->active_state,
+ TABLE_SET_COLOR, on_active,
+ TABLE_STRING, u->sub_state,
+ TABLE_SET_COLOR, on_active,
+ TABLE_STRING, u->job_id ? u->job_type: "",
+ TABLE_SET_COLOR, u->job_id ? on_underline : "",
+ TABLE_STRING, u->description,
+ TABLE_SET_COLOR, on_underline);
+ if (r < 0)
+ return table_log_add_error(r);
- printf("%s%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
- on_underline,
- on_active, id_len, id, off_active,
- on_loaded, load_len, u->load_state, off_loaded,
- on_active, active_len, u->active_state,
- sub_len, u->sub_state, off_active,
- job_count ? job_len + 1 : 0, u->job_id ? u->job_type : "");
+ if (u->job_id != 0)
+ job_count++;
+ }
- printf("%-*.*s%s\n",
- desc_len,
- full ? -1 : (int) desc_len,
- u->description,
- off_underline);
+ if (job_count == 0) {
+ /* There's no data in the JOB column, so let's hide it */
+ /* Also, convert all number constants to size_t so va_arg()
+ * in table_set_display() fetches a correct number of bytes from
+ * the stack */
+ r = table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 6, (size_t) -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set columns to display: %m");
}
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print the table: %m");
+
if (!arg_no_legend) {
const char *on, *off;
+ size_t records = table_get_rows(table) - 1;
- if (n_shown) {
+ if (records > 0) {
puts("\n"
"LOAD = Reflects whether the unit definition was properly loaded.\n"
"ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
}
if (arg_all || strv_contains(arg_states, "inactive"))
- printf("%s%u loaded units listed.%s\n"
+ printf("%s%zu loaded units listed.%s\n"
"To show all installed unit files use 'systemctl list-unit-files'.\n",
- on, n_shown, off);
+ on, records, off);
else if (!arg_states)
- printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
+ printf("%s%zu loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
"To show all installed unit files use 'systemctl list-unit-files'.\n",
- on, n_shown, off);
+ on, records, off);
else
- printf("%u loaded units listed.\n", n_shown);
+ printf("%zu loaded units listed.\n", records);
}
return 0;
if (r < 0)
return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r));
+ strv_uniq(deps); /* Sometimes a unit might have multiple deps on the other unit,
+ * but we still want to show it just once. */
*ret = TAKE_PTR(deps);
return 0;
}
static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
+ _cleanup_(table_unrefp) Table *table = NULL;
struct socket_info *s;
- unsigned pathlen = STRLEN("LISTEN"),
- typelen = STRLEN("TYPE") * arg_show_types,
- socklen = STRLEN("UNIT"),
- servlen = STRLEN("ACTIVATES");
const char *on, *off;
+ int r;
- for (s = socket_infos; s < socket_infos + cs; s++) {
- unsigned tmp = 0;
- char **a;
-
- socklen = MAX(socklen, strlen(s->id));
- if (arg_show_types)
- typelen = MAX(typelen, strlen(s->type));
- pathlen = MAX(pathlen, strlen(s->path) + (s->machine ? strlen(s->machine)+1 : 0));
+ table = table_new("listen", "type", "units", "activates");
+ if (!table)
+ return log_oom();
- STRV_FOREACH(a, s->triggered)
- tmp += strlen(*a) + 2*(a != s->triggered);
- servlen = MAX(servlen, tmp);
+ if (!arg_show_types) {
+ /* Hide the second (TYPE) column */
+ r = table_set_display(table, (size_t) 0, (size_t) 2, (size_t) 3, (size_t) -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set columns to display: %m");
}
- if (cs) {
- if (!arg_no_legend)
- printf("%-*s %-*.*s%-*s %s\n",
- pathlen, "LISTEN",
- typelen + arg_show_types, typelen + arg_show_types, "TYPE ",
- socklen, "UNIT",
- "ACTIVATES");
+ table_set_header(table, !arg_no_legend);
+ if (arg_full)
+ table_set_width(table, 0);
+ if (cs) {
for (s = socket_infos; s < socket_infos + cs; s++) {
- _cleanup_free_ char *j = NULL;
+ _cleanup_free_ char *j = NULL, *activates = NULL;
const char *path;
- char **a;
if (s->machine) {
j = strjoin(s->machine, ":", s->path);
} else
path = s->path;
- if (arg_show_types)
- printf("%-*s %-*s %-*s",
- pathlen, path, typelen, s->type, socklen, s->id);
- else
- printf("%-*s %-*s",
- pathlen, path, socklen, s->id);
- STRV_FOREACH(a, s->triggered)
- printf("%s %s",
- a == s->triggered ? "" : ",", *a);
- printf("\n");
+ activates = strv_join(s->triggered, ", ");
+ if (!activates)
+ return log_oom();
+
+ r = table_add_many(table,
+ TABLE_STRING, path,
+ TABLE_STRING, s->type,
+ TABLE_STRING, s->id,
+ TABLE_STRING, activates);
+ if (r < 0)
+ return table_log_add_error(r);
}
on = ansi_highlight();
off = ansi_normal();
- if (!arg_no_legend)
- printf("\n");
} else {
on = ansi_highlight_red();
off = ansi_normal();
}
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print the table: %m");
+
if (!arg_no_legend) {
- printf("%s%u sockets listed.%s\n", on, cs, off);
+ printf("\n%s%u sockets listed.%s\n", on, cs, off);
if (!arg_all)
printf("Pass --all to see loaded but inactive sockets, too.\n");
}
}
static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
+ _cleanup_(table_unrefp) Table *table = NULL;
struct timer_info *t;
- unsigned
- nextlen = STRLEN("NEXT"),
- leftlen = STRLEN("LEFT"),
- lastlen = STRLEN("LAST"),
- passedlen = STRLEN("PASSED"),
- unitlen = STRLEN("UNIT"),
- activatelen = STRLEN("ACTIVATES");
-
const char *on, *off;
+ int r;
assert(timer_infos || n == 0);
- for (t = timer_infos; t < timer_infos + n; t++) {
- unsigned ul = 0;
- char **a;
-
- if (t->next_elapse > 0) {
- char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
-
- format_timestamp(tstamp, sizeof(tstamp), t->next_elapse);
- nextlen = MAX(nextlen, strlen(tstamp) + 1);
-
- format_timestamp_relative(trel, sizeof(trel), t->next_elapse);
- leftlen = MAX(leftlen, strlen(trel));
- }
-
- if (t->last_trigger > 0) {
- char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
-
- format_timestamp(tstamp, sizeof(tstamp), t->last_trigger);
- lastlen = MAX(lastlen, strlen(tstamp) + 1);
-
- format_timestamp_relative(trel, sizeof(trel), t->last_trigger);
- passedlen = MAX(passedlen, strlen(trel));
- }
-
- unitlen = MAX(unitlen, strlen(t->id) + (t->machine ? strlen(t->machine)+1 : 0));
-
- STRV_FOREACH(a, t->triggered)
- ul += strlen(*a) + 2*(a != t->triggered);
+ table = table_new("next", "left", "last", "passed", "unit", "activates");
+ if (!table)
+ return log_oom();
- activatelen = MAX(activatelen, ul);
- }
+ table_set_header(table, !arg_no_legend);
+ if (arg_full)
+ table_set_width(table, 0);
if (n > 0) {
- if (!arg_no_legend)
- printf("%-*s %-*s %-*s %-*s %-*s %s\n",
- nextlen, "NEXT",
- leftlen, "LEFT",
- lastlen, "LAST",
- passedlen, "PASSED",
- unitlen, "UNIT",
- "ACTIVATES");
-
for (t = timer_infos; t < timer_infos + n; t++) {
- _cleanup_free_ char *j = NULL;
+ _cleanup_free_ char *j = NULL, *activates = NULL;
const char *unit;
- char tstamp1[FORMAT_TIMESTAMP_MAX] = "n/a", trel1[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
- char tstamp2[FORMAT_TIMESTAMP_MAX] = "n/a", trel2[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
- char **a;
-
- format_timestamp(tstamp1, sizeof(tstamp1), t->next_elapse);
- format_timestamp_relative(trel1, sizeof(trel1), t->next_elapse);
-
- format_timestamp(tstamp2, sizeof(tstamp2), t->last_trigger);
- format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger);
if (t->machine) {
j = strjoin(t->machine, ":", t->id);
} else
unit = t->id;
- printf("%-*s %-*s %-*s %-*s %-*s",
- nextlen, tstamp1, leftlen, trel1, lastlen, tstamp2, passedlen, trel2, unitlen, unit);
+ activates = strv_join(t->triggered, ", ");
+ if (!activates)
+ return log_oom();
- STRV_FOREACH(a, t->triggered)
- printf("%s %s",
- a == t->triggered ? "" : ",", *a);
- printf("\n");
+ r = table_add_many(table,
+ TABLE_TIMESTAMP, t->next_elapse,
+ TABLE_TIMESTAMP_RELATIVE, t->next_elapse,
+ TABLE_TIMESTAMP, t->last_trigger,
+ TABLE_TIMESTAMP_RELATIVE, t->last_trigger,
+ TABLE_STRING, unit,
+ TABLE_STRING, activates);
+ if (r < 0)
+ return table_log_add_error(r);
}
on = ansi_highlight();
off = ansi_normal();
- if (!arg_no_legend)
- printf("\n");
} else {
on = ansi_highlight_red();
off = ansi_normal();
}
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print the table: %m");
+
if (!arg_no_legend) {
- printf("%s%u timers listed.%s\n", on, n, off);
+ printf("\n%s%u timers listed.%s\n", on, n, off);
if (!arg_all)
printf("Pass --all to see loaded but inactive timers, too.\n");
}
return true;
}
-static void output_unit_file_list(const UnitFileList *units, unsigned c) {
- unsigned max_id_len, id_cols, state_cols, preset_cols;
+static int output_unit_file_list(const UnitFileList *units, unsigned c) {
+ _cleanup_(table_unrefp) Table *table = NULL;
const UnitFileList *u;
+ int r;
- max_id_len = STRLEN("UNIT FILE");
- state_cols = STRLEN("STATE");
- preset_cols = STRLEN("VENDOR PRESET");
-
- for (u = units; u < units + c; u++) {
- max_id_len = MAX(max_id_len, strlen(basename(u->path)));
- state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
- }
-
- if (!arg_full) {
- unsigned basic_cols;
-
- id_cols = MIN(max_id_len, 25u);
- basic_cols = 1 + id_cols + state_cols;
- if (basic_cols < (unsigned) columns())
- id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
- } else
- id_cols = max_id_len;
+ table = table_new("unit file", "state", "vendor preset");
+ if (!table)
+ return log_oom();
- if (!arg_no_legend && c > 0)
- printf("%s%-*s %-*s %-*s%s\n",
- ansi_underline(),
- id_cols, "UNIT FILE",
- state_cols, "STATE",
- preset_cols, "VENDOR PRESET",
- ansi_normal());
+ table_set_header(table, !arg_no_legend);
+ if (arg_full)
+ table_set_width(table, 0);
for (u = units; u < units + c; u++) {
const char *on_underline = NULL, *on_unit_color = NULL, *id;
- const char *on_preset_color = NULL, *off_preset = NULL, *unit_preset_str;
- _cleanup_free_ char *e = NULL;
- int r;
+ const char *on_preset_color = NULL, *unit_preset_str;
bool underline;
underline = u + 1 < units + c &&
on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
else if (u->state == UNIT_FILE_ENABLED)
on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
+ else
+ on_unit_color = on_underline;
id = basename(u->path);
on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
}
- if (on_underline || on_preset_color)
- off_preset = ansi_normal();
-
- e = arg_full ? NULL : ellipsize(id, id_cols, 33);
-
- printf("%s%-*s %s%-*s %s%-*s%s\n",
- strempty(on_underline),
- id_cols, e ? e : id,
- strempty(on_unit_color), state_cols, unit_file_state_to_string(u->state),
- strempty(on_preset_color), preset_cols, unit_preset_str, strempty(off_preset));
+ r = table_add_many(table,
+ TABLE_STRING, id,
+ TABLE_SET_COLOR, strempty(on_underline),
+ TABLE_STRING, unit_file_state_to_string(u->state),
+ TABLE_SET_COLOR, strempty(on_unit_color),
+ TABLE_STRING, unit_preset_str,
+ TABLE_SET_COLOR, strempty(on_preset_color));
+ if (r < 0)
+ return table_log_add_error(r);
}
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print the table: %m");
+
if (!arg_no_legend)
printf("\n%u unit files listed.\n", c);
+
+ return 0;
}
static int list_unit_files(int argc, char *argv[], void *userdata) {
(void) pager_open(arg_pager_flags);
typesafe_qsort(units, c, compare_unit_file_list);
- output_unit_file_list(units, c);
+ r = output_unit_file_list(units, c);
+ if (r < 0)
+ return r;
if (install_client_side())
for (unit = units; unit < units + c; unit++)
}
static int list_dependencies(int argc, char *argv[], void *userdata) {
- _cleanup_strv_free_ char **units = NULL;
- _cleanup_free_ char *unit = NULL;
- const char *u;
+ _cleanup_strv_free_ char **units = NULL, **done = NULL;
+ char **u, **patterns;
sd_bus *bus;
int r;
- if (argv[1]) {
- r = unit_name_mangle(argv[1], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, &unit);
- if (r < 0)
- return log_error_errno(r, "Failed to mangle unit name: %m");
-
- u = unit;
- } else
- u = SPECIAL_DEFAULT_TARGET;
-
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
+ patterns = strv_skip(argv, 1);
+ if (strv_isempty(patterns)) {
+ units = strv_new(SPECIAL_DEFAULT_TARGET);
+ if (!units)
+ return log_oom();
+ } else {
+ r = expand_names(bus, patterns, NULL, &units, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand names: %m");
+ }
+
(void) pager_open(arg_pager_flags);
- puts(u);
+ STRV_FOREACH(u, units) {
+ if (u != units)
+ puts("");
+
+ puts(*u);
+ r = list_dependencies_one(bus, *u, 0, &done, 0);
+ if (r < 0)
+ return r;
+ }
- return list_dependencies_one(bus, u, 0, &units, 0);
+ return 0;
}
struct machine_info {
return c;
}
-static void output_machines_list(struct machine_info *machine_infos, unsigned n) {
+static int output_machines_list(struct machine_info *machine_infos, unsigned n) {
+ _cleanup_(table_unrefp) Table *table = NULL;
struct machine_info *m;
- unsigned
- circle_len = 0,
- namelen = STRLEN("NAME"),
- statelen = STRLEN("STATE"),
- failedlen = STRLEN("FAILED"),
- jobslen = STRLEN("JOBS");
bool state_missing = false;
+ int r;
assert(machine_infos || n == 0);
- for (m = machine_infos; m < machine_infos + n; m++) {
- namelen = MAX(namelen,
- strlen(m->name) + (m->is_host ? STRLEN(" (host)") : 0));
- statelen = MAX(statelen, strlen_ptr(m->state));
- failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
- jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
-
- if (!arg_plain && m->state && !streq(m->state, "running"))
- circle_len = 2;
- }
-
- if (!arg_no_legend) {
- if (circle_len > 0)
- fputs(" ", stdout);
+ table = table_new("", "name", "state", "failed", "jobs");
+ if (!table)
+ return log_oom();
- printf("%-*s %-*s %-*s %-*s\n",
- namelen, "NAME",
- statelen, "STATE",
- failedlen, "FAILED",
- jobslen, "JOBS");
- }
+ table_set_header(table, !arg_no_legend);
+ if (arg_full)
+ table_set_width(table, 0);
for (m = machine_infos; m < machine_infos + n; m++) {
- const char *on_state = "", *off_state = "";
- const char *on_failed = "", *off_failed = "";
+ _cleanup_free_ char *mname = NULL;
+ const char *on_state = "", *on_failed = "";
bool circle = false;
if (streq_ptr(m->state, "degraded")) {
on_state = ansi_highlight_red();
- off_state = ansi_normal();
circle = true;
} else if (!streq_ptr(m->state, "running")) {
on_state = ansi_highlight_yellow();
- off_state = ansi_normal();
circle = true;
}
- if (m->n_failed_units > 0) {
+ if (m->n_failed_units > 0)
on_failed = ansi_highlight_red();
- off_failed = ansi_normal();
- } else
- on_failed = off_failed = "";
-
- if (circle_len > 0)
- printf("%s%s%s ", on_state, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_state);
+ else
+ on_failed = "";
if (!m->state)
state_missing = true;
if (m->is_host)
- printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
- (int) (namelen - strlen(" (host)")),
- strna(m->name),
- on_state, statelen, strna(m->state), off_state,
- on_failed, failedlen, m->n_failed_units, off_failed,
- jobslen, m->n_jobs);
- else
- printf("%-*s %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
- namelen, strna(m->name),
- on_state, statelen, strna(m->state), off_state,
- on_failed, failedlen, m->n_failed_units, off_failed,
- jobslen, m->n_jobs);
+ mname = strjoin(strna(m->name), " (host)");
+
+ r = table_add_many(table,
+ TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
+ TABLE_SET_COLOR, on_state,
+ TABLE_STRING, m->is_host ? mname : strna(m->name),
+ TABLE_STRING, strna(m->state),
+ TABLE_SET_COLOR, on_state,
+ TABLE_UINT32, m->n_failed_units,
+ TABLE_SET_COLOR, on_failed,
+ TABLE_UINT32, m->n_jobs);
+ if (r < 0)
+ return table_log_add_error(r);
}
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print the table: %m");
+
if (!arg_no_legend) {
printf("\n");
if (state_missing && geteuid() != 0)
printf("Notice: some information only available to privileged users was not shown.\n");
printf("%u machines listed.\n", n);
}
+
+ return 0;
}
static int list_machines(int argc, char *argv[], void *userdata) {
struct machine_info *machine_infos = NULL;
sd_bus *bus;
- int r;
+ int r, rc;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
(void) pager_open(arg_pager_flags);
typesafe_qsort(machine_infos, r, compare_machine_info);
- output_machines_list(machine_infos, r);
+ rc = output_machines_list(machine_infos, r);
free_machines_list(machine_infos, r);
- return 0;
+ return rc;
}
static int get_default(int argc, char *argv[], void *userdata) {
return r;
}
-static int output_waiting_jobs(sd_bus *bus, uint32_t id, const char *method, const char *prefix) {
+static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const char *method, const char *prefix) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *name, *type;
if (r < 0)
return bus_log_parse_error(r);
- while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0)
- printf("%s %u (%s/%s)\n", prefix, other_id, name, type);
+ while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0) {
+ _cleanup_free_ char *row = NULL;
+ int rc;
+
+ if (asprintf(&row, "%s %u (%s/%s)", prefix, other_id, name, type) < 0)
+ return log_oom();
+
+ rc = table_add_many(table,
+ TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
+ TABLE_STRING, row,
+ TABLE_EMPTY,
+ TABLE_EMPTY);
+ if (rc < 0)
+ return table_log_add_error(r);
+ }
+
if (r < 0)
return bus_log_parse_error(r);
const char *name, *type, *state;
};
-static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
- unsigned id_len, unit_len, type_len, state_len;
+static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
+ _cleanup_(table_unrefp) Table *table = NULL;
const struct job_info *j;
const char *on, *off;
- bool shorten = false;
+ int r;
assert(n == 0 || jobs);
printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off);
}
- return;
+ return 0;
}
(void) pager_open(arg_pager_flags);
- id_len = STRLEN("JOB");
- unit_len = STRLEN("UNIT");
- type_len = STRLEN("TYPE");
- state_len = STRLEN("STATE");
-
- for (j = jobs; j < jobs + n; j++) {
- uint32_t id = j->id;
- assert(j->name && j->type && j->state);
-
- id_len = MAX(id_len, DECIMAL_STR_WIDTH(id));
- unit_len = MAX(unit_len, strlen(j->name));
- type_len = MAX(type_len, strlen(j->type));
- state_len = MAX(state_len, strlen(j->state));
- }
-
- if (!arg_full && id_len + 1 + unit_len + type_len + 1 + state_len > columns()) {
- unit_len = MAX(33u, columns() - id_len - type_len - state_len - 3);
- shorten = true;
- }
+ table = table_new("job", "unit", "type", "state");
+ if (!table)
+ return log_oom();
- if (!arg_no_legend)
- printf("%*s %-*s %-*s %-*s\n",
- id_len, "JOB",
- unit_len, "UNIT",
- type_len, "TYPE",
- state_len, "STATE");
+ table_set_header(table, !arg_no_legend);
+ if (arg_full)
+ table_set_width(table, 0);
for (j = jobs; j < jobs + n; j++) {
- _cleanup_free_ char *e = NULL;
-
- if (streq(j->state, "running")) {
+ if (streq(j->state, "running"))
on = ansi_highlight();
- off = ansi_normal();
- } else
- on = off = "";
+ else
+ on = "";
+
- e = shorten ? ellipsize(j->name, unit_len, 33) : NULL;
- printf("%*u %s%-*s%s %-*s %s%-*s%s\n",
- id_len, j->id,
- on, unit_len, e ? e : j->name, off,
- type_len, j->type,
- on, state_len, j->state, off);
+ r = table_add_many(table,
+ TABLE_UINT, j->id,
+ TABLE_STRING, j->name,
+ TABLE_SET_COLOR, on,
+ TABLE_STRING, j->type,
+ TABLE_STRING, j->state,
+ TABLE_SET_COLOR, on);
+ if (r < 0)
+ return table_log_add_error(r);
if (arg_jobs_after)
- output_waiting_jobs(bus, j->id, "GetJobAfter", "\twaiting for job");
+ output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\twaiting for job");
if (arg_jobs_before)
- output_waiting_jobs(bus, j->id, "GetJobBefore", "\tblocking job");
+ output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\tblocking job");
}
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print the table: %m");
+
if (!arg_no_legend) {
on = ansi_highlight();
off = ansi_normal();
printf("\n%s%u jobs listed%s.\n", on, n, off);
}
+
+ return 0;
}
static bool output_show_job(struct job_info *job, char **patterns) {
(void) pager_open(arg_pager_flags);
- output_jobs_list(bus, jobs, c, skipped);
- return 0;
+ return output_jobs_list(bus, jobs, c, skipped);
}
static int cancel_job(int argc, char *argv[], void *userdata) {
int exit_code, exit_status;
+ const char *log_namespace;
+
usec_t condition_timestamp;
bool condition_result;
LIST_HEAD(UnitCondition, conditions);
printf(" (");
if (i->memory_min > 0) {
- printf("%smin: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_min));
+ printf("%smin: %s", prefix, format_bytes_cgroup_protection(buf, sizeof(buf), i->memory_min));
prefix = " ";
}
if (i->memory_low > 0) {
- printf("%slow: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_low));
+ printf("%slow: %s", prefix, format_bytes_cgroup_protection(buf, sizeof(buf), i->memory_low));
prefix = " ";
}
if (i->memory_high != CGROUP_LIMIT_MAX) {
show_journal_by_unit(
stdout,
i->id,
+ i->log_namespace,
arg_output,
0,
i->inactive_exit_timestamp_monotonic,
{ "ExecMainExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, exit_timestamp) },
{ "ExecMainCode", "i", NULL, offsetof(UnitStatusInfo, exit_code) },
{ "ExecMainStatus", "i", NULL, offsetof(UnitStatusInfo, exit_status) },
+ { "LogNamespace", "s", NULL, offsetof(UnitStatusInfo, log_namespace) },
{ "ConditionTimestamp", "t", NULL, offsetof(UnitStatusInfo, condition_timestamp) },
{ "ConditionResult", "b", NULL, offsetof(UnitStatusInfo, condition_result) },
{ "Conditions", "a(sbbsi)", map_conditions, 0 },
j = unit_file_exists(arg_scope, &paths, name);
if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL))
- return log_error_errno(j, "Failed to lookup unit file state: %m");
+ return log_error_errno(j, "Failed to look up unit file state: %m");
found_native = j != 0;
/* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled,
" help PATTERN...|PID... Show manual for one or more units\n"
" reset-failed [PATTERN...] Reset failed state for all, one, or more\n"
" units\n"
- " list-dependencies [UNIT] Recursively show units which are required\n"
- " or wanted by this unit or by which this\n"
- " unit is required or wanted"
+ " list-dependencies [UNIT...] Recursively show units which are required\n"
+ " or wanted by the units or by which those\n"
+ " units are required or wanted"
"\n%3$sUnit File Commands:%4$s\n"
" list-unit-files [PATTERN...] List installed unit files\n"
" enable [UNIT...|PATH...] Enable one or more unit files\n"
{ "link", 2, VERB_ANY, 0, enable_unit },
{ "revert", 2, VERB_ANY, 0, enable_unit },
{ "switch-root", 2, VERB_ANY, VERB_ONLINE_ONLY, switch_root },
- { "list-dependencies", VERB_ANY, 2, VERB_ONLINE_ONLY, list_dependencies },
+ { "list-dependencies", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_dependencies },
{ "set-default", 2, 2, 0, set_default },
{ "get-default", VERB_ANY, 1, 0, get_default },
{ "set-property", 3, VERB_ANY, VERB_ONLINE_ONLY, set_property },
# define _sd_pure_ __attribute__((__pure__))
#endif
+/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
+ * it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
+ * (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
+ * are probably going to use something newer anyway. */
+#ifndef _sd_deprecated_
+# if __GNUC__ >= 6
+# define _sd_deprecated_ __attribute__((__deprecated__))
+# else
+# define _sd_deprecated_
+# endif
+#endif
+
#ifndef _SD_STRINGIFY
# define _SD_XSTRINGIFY(x) #x
# define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x)
#include <inttypes.h>
#include <stdarg.h>
+#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
SD_BUS_NAME_QUEUE = 1ULL << 2
};
+enum {
+ SD_BUS_MESSAGE_DUMP_WITH_HEADER = 1ULL << 0,
+ SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY = 1ULL << 1,
+};
+
/* Callbacks */
typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r);
int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec);
int sd_bus_flush(sd_bus *bus);
+int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m);
sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus);
sd_bus_message* sd_bus_get_current_message(sd_bus *bus);
int sd_bus_message_rewind(sd_bus_message *m, int complete);
int sd_bus_message_sensitive(sd_bus_message *m);
+int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags);
+
/* Bus management */
int sd_bus_get_unique_name(sd_bus *bus, const char **unique);
/* Open flags */
enum {
- SD_JOURNAL_LOCAL_ONLY = 1 << 0,
- SD_JOURNAL_RUNTIME_ONLY = 1 << 1,
- SD_JOURNAL_SYSTEM = 1 << 2,
- SD_JOURNAL_CURRENT_USER = 1 << 3,
- SD_JOURNAL_OS_ROOT = 1 << 4,
-
- SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */
+ SD_JOURNAL_LOCAL_ONLY = 1 << 0,
+ SD_JOURNAL_RUNTIME_ONLY = 1 << 1,
+ SD_JOURNAL_SYSTEM = 1 << 2,
+ SD_JOURNAL_CURRENT_USER = 1 << 3,
+ SD_JOURNAL_OS_ROOT = 1 << 4,
+ SD_JOURNAL_ALL_NAMESPACES = 1 << 5, /* Show all namespaces, not just the default or specified one */
+ SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */
+
+ SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* deprecated name */
};
/* Wakeup event types */
};
int sd_journal_open(sd_journal **ret, int flags);
+int sd_journal_open_namespace(sd_journal **ret, const char *name_space, int flags);
int sd_journal_open_directory(sd_journal **ret, const char *path, int flags);
int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags);
int sd_journal_open_files(sd_journal **ret, const char **paths, int flags);
int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags);
-int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */
+int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; /* deprecated */
void sd_journal_close(sd_journal *j);
int sd_journal_previous(sd_journal *j);
int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
+int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data);
+int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data);
+int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data);
+int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data);
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);
int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent);
int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle);
+int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex);
+int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent);
+int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle);
+
/* genl */
int sd_genl_socket_open(sd_netlink **nl);
int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);
int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr,
unsigned char prefixlen);
+int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
+ unsigned char *ret_prefixlen);
int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink);
int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
int address_autoconfiguration);
ItemType type;
char *name;
+ char *group_name;
char *uid_path;
char *gid_path;
char *description;
STATIC_DESTRUCTOR_REGISTER(uid_range, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+static int errno_is_not_exists(int code) {
+ /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
+ * not found. */
+ return IN_SET(code, 0, ENOENT, ESRCH, EBADF, EPERM);
+}
+
static int load_user_database(void) {
_cleanup_fclose_ FILE *f = NULL;
const char *passwd_path;
static int make_backup(const char *target, const char *x) {
_cleanup_close_ int src = -1;
_cleanup_fclose_ FILE *dst = NULL;
- _cleanup_free_ char *temp = NULL;
+ _cleanup_free_ char *dst_tmp = NULL;
char *backup;
struct timespec ts[2];
struct stat st;
if (fstat(src, &st) < 0)
return -errno;
- r = fopen_temporary_label(target, x, &dst, &temp);
+ r = fopen_temporary_label(target, x, &dst, &dst_tmp);
if (r < 0)
return r;
backup = strjoina(x, "-");
/* Copy over the access mask */
- r = fchmod_and_chown(fileno(dst), st.st_mode & 07777, st.st_uid, st.st_gid);
+ r = chmod_and_chown_unsafe(dst_tmp, st.st_mode & 07777, st.st_uid, st.st_gid);
if (r < 0)
log_warning_errno(r, "Failed to change access mode or ownership of %s: %m", backup);
if (r < 0)
goto fail;
- if (rename(temp, backup) < 0) {
+ if (rename(dst_tmp, backup) < 0) {
r = -errno;
goto fail;
}
return 0;
fail:
- (void) unlink(temp);
+ (void) unlink(dst_tmp);
return r;
}
}
#endif
-static int sync_rights(FILE *from, FILE *to) {
+static int sync_rights(FILE *from, const char *to) {
struct stat st;
if (fstat(fileno(from), &st) < 0)
return -errno;
- return fchmod_and_chown(fileno(to), st.st_mode & 07777, st.st_uid, st.st_gid);
+ return chmod_and_chown_unsafe(to, st.st_mode & 07777, st.st_uid, st.st_gid);
}
static int rename_and_apply_smack(const char *temp_path, const char *dest_path) {
original = fopen(passwd_path, "re");
if (original) {
- r = sync_rights(original, passwd);
+ r = sync_rights(original, passwd_tmp);
if (r < 0)
return r;
original = fopen(shadow_path, "re");
if (original) {
- r = sync_rights(original, shadow);
+ r = sync_rights(original, shadow_tmp);
if (r < 0)
return r;
original = fopen(group_path, "re");
if (original) {
- r = sync_rights(original, group);
+ r = sync_rights(original, group_tmp);
if (r < 0)
return r;
if (original) {
struct sgrp *sg;
- r = sync_rights(original, gshadow);
+ r = sync_rights(original, gshadow_tmp);
if (r < 0)
return r;
return 0;
}
- if (!IN_SET(errno, 0, ENOENT))
+ if (!errno_is_not_exists(errno))
return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
}
return 1;
}
-static int add_group(Item *i) {
+static int get_gid_by_name(const char *name, gid_t *gid) {
void *z;
- int r;
- assert(i);
+ assert(gid);
/* Check the database directly */
- z = hashmap_get(database_by_groupname, i->name);
+ z = hashmap_get(database_by_groupname, name);
if (z) {
- log_debug("Group %s already exists.", i->name);
- i->gid = PTR_TO_GID(z);
- i->gid_set = true;
+ *gid = PTR_TO_GID(z);
return 0;
}
struct group *g;
errno = 0;
- g = getgrnam(i->name);
+ g = getgrnam(name);
if (g) {
- log_debug("Group %s already exists.", i->name);
- i->gid = g->gr_gid;
- i->gid_set = true;
+ *gid = g->gr_gid;
return 0;
}
- if (!IN_SET(errno, 0, ENOENT))
- return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
+ if (!errno_is_not_exists(errno))
+ return log_error_errno(errno, "Failed to check if group %s already exists: %m", name);
+ }
+
+ return -ENOENT;
+}
+
+static int add_group(Item *i) {
+ int r;
+
+ assert(i);
+
+ r = get_gid_by_name(i->name, &i->gid);
+ if (r != -ENOENT) {
+ if (r < 0)
+ return r;
+ log_debug("Group %s already exists.", i->name);
+ i->gid_set = true;
+ return 0;
}
/* Try to use the suggested numeric gid */
case ADD_USER: {
Item *j;
- j = ordered_hashmap_get(groups, i->name);
+ j = ordered_hashmap_get(groups, i->group_name ?: i->name);
if (j && j->todo_group) {
- /* When the group with the same name is already in queue,
+ /* When a group with the target name is already in queue,
* use the information about the group and do not create
* duplicated group entry. */
i->gid_set = j->gid_set;
i->gid = j->gid;
i->id_set_strict = true;
+ } else if (i->group_name) {
+ /* When a group name was given instead of a GID and it's
+ * not in queue, then it must already exist. */
+ r = get_gid_by_name(i->group_name, &i->gid);
+ if (r < 0)
+ return log_error_errno(r, "Group %s not found.", i->group_name);
+ i->gid_set = true;
+ i->id_set_strict = true;
} else {
r = add_group(i);
if (r < 0)
return NULL;
free(i->name);
+ free(i->group_name);
free(i->uid_path);
free(i->gid_path);
free(i->description);
_cleanup_free_ char *uid = NULL, *gid = NULL;
if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
r = parse_gid(gid, &i->gid);
- if (r < 0)
- return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
- i->gid_set = true;
- i->id_set_strict = true;
+ if (r < 0) {
+ if (valid_user_group_name(gid))
+ i->group_name = TAKE_PTR(gid);
+ else
+ return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
+ } else {
+ i->gid_set = true;
+ i->id_set_strict = true;
+ }
free_and_replace(resolved_id, uid);
}
if (!streq(resolved_id, "-")) {
print('#include "{}"'.format(header.split('/')[-1]))
print('''
+/* We want to check deprecated symbols too, without complaining */
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
const void* symbols[] = {''')
for line in open(sys.argv[1]):
[],
[]],
+ [['src/test/test-sysctl-util.c'],
+ [],
+ []],
+
[['src/test/test-user-util.c'],
[],
[]],
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include "alloc-util.h"
#include "ask-password-api.h"
-#include "log.h"
#include "strv.h"
+#include "tests.h"
-static void ask_password(void) {
+static void test_ask_password(void) {
int r;
_cleanup_strv_free_ char **ret = NULL;
- r = ask_password_tty(-1, "hello?", "da key", 0, 0, NULL, &ret);
- assert(r >= 0);
- assert(strv_length(ret) == 1);
-
- log_info("Got %s", *ret);
+ r = ask_password_tty(-1, "hello?", "da key", 0, ASK_PASSWORD_CONSOLE_COLOR, NULL, &ret);
+ if (r == -ECANCELED)
+ assert_se(ret == NULL);
+ else {
+ assert_se(r >= 0);
+ assert_se(strv_length(ret) == 1);
+ log_info("Got \"%s\"", *ret);
+ }
}
int main(int argc, char **argv) {
- log_parse_environment();
+ test_setup_logging(LOG_DEBUG);
- ask_password();
+ test_ask_password();
return EXIT_SUCCESS;
}
r = btrfs_subvol_snapshot("/xxxquotatest", "/xxxquotatest2", BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA);
if (r < 0)
- log_error_errno(r, "Failed to setup snapshot: %m");
+ log_error_errno(r, "Failed to set up snapshot: %m");
r = btrfs_qgroup_get_quota("/xxxquotatest2/beneath", 0, "a);
if (r < 0)
assert_se(expected == iec_size);
}
-static void test_config_parse_si_size_one(const char *rvalue, size_t expected) {
- size_t si_size = 0;
+static void test_config_parse_si_uint64_one(const char *rvalue, uint64_t expected) {
+ uint64_t si_uint64 = 0;
- assert_se(config_parse_si_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_size, NULL) >= 0);
- assert_se(expected == si_size);
+ assert_se(config_parse_si_uint64("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_uint64, NULL) >= 0);
+ assert_se(expected == si_uint64);
}
static void test_config_parse_int_one(const char *rvalue, int expected) {
test_config_parse_iec_size_one("garbage", 0);
}
-static void test_config_parse_si_size(void) {
- test_config_parse_si_size_one("1024", 1024);
- test_config_parse_si_size_one("2K", 2000);
- test_config_parse_si_size_one("10M", 10 * 1000 * 1000);
- test_config_parse_si_size_one("1G", 1 * 1000 * 1000 * 1000);
- test_config_parse_si_size_one("0G", 0);
- test_config_parse_si_size_one("0", 0);
-
- test_config_parse_si_size_one("-982", 0);
- test_config_parse_si_size_one("49874444198739873000000G", 0);
- test_config_parse_si_size_one("garbage", 0);
+static void test_config_parse_si_uint64(void) {
+ test_config_parse_si_uint64_one("1024", 1024);
+ test_config_parse_si_uint64_one("2K", 2000);
+ test_config_parse_si_uint64_one("10M", 10 * 1000 * 1000);
+ test_config_parse_si_uint64_one("1G", 1 * 1000 * 1000 * 1000);
+ test_config_parse_si_uint64_one("0G", 0);
+ test_config_parse_si_uint64_one("0", 0);
+
+ test_config_parse_si_uint64_one("-982", 0);
+ test_config_parse_si_uint64_one("49874444198739873000000G", 0);
+ test_config_parse_si_uint64_one("garbage", 0);
}
static void test_config_parse_int(void) {
test_config_parse_log_level();
test_config_parse_log_facility();
test_config_parse_iec_size();
- test_config_parse_si_size();
+ test_config_parse_si_uint64();
test_config_parse_int();
test_config_parse_unsigned();
test_config_parse_strv();
return EXIT_FAILURE;
}
- r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m);
+ r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
if (r < 0) {
log_error_errno(r, "Failed to dissect image: %m");
return EXIT_FAILURE;
assert_se(cunescape("A=A\\\\x0aB", UNESCAPE_RELAX, &unescaped) >= 0);
assert_se(streq_ptr(unescaped, "A=A\\x0aB"));
+ unescaped = mfree(unescaped);
+
+ assert_se(cunescape("\\x00\\x00\\x00", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+ assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
+ unescaped = mfree(unescaped);
+
+ assert_se(cunescape("\\u0000\\u0000\\u0000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+ assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
+ unescaped = mfree(unescaped);
+
+ assert_se(cunescape("\\U00000000\\U00000000\\U00000000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+ assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
+ unescaped = mfree(unescaped);
+
+ assert_se(cunescape("\\000\\000\\000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+ assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
}
static void test_shell_escape_one(const char *s, const char *bad, const char *expected) {
test(__func__, m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-privatetmp-no.service", 0, CLD_EXITED);
+ test(__func__, m, "exec-privatetmp-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
unlink("/tmp/test-exec_privatetmp");
}
test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
(void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
static void test_exec_standardinput(Manager *m) {
test(__func__, m, "exec-standardinput-data.service", 0, CLD_EXITED);
test(__func__, m, "exec-standardinput-file.service", 0, CLD_EXITED);
+ test(__func__, m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
}
static void test_exec_standardoutput(Manager *m) {
assert_se(tests);
r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
+ m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
if (manager_errno_skip_test(r))
return log_tests_skipped_errno(r, "manager_new");
assert_se(r >= 0);
#include "alloc-util.h"
#include "format-table.h"
#include "string-util.h"
+#include "strv.h"
#include "time-util.h"
static void test_issue_9549(void) {
formatted = mfree(formatted);
}
+static void test_strv(void) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ _cleanup_free_ char *formatted = NULL;
+
+ assert_se(table = table_new("foo", "bar"));
+
+ assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
+
+ assert_se(table_add_many(table,
+ TABLE_STRV, STRV_MAKE("three", "different", "lines"),
+ TABLE_STRV, STRV_MAKE("two", "lines")) >= 0);
+
+ table_set_cell_height_max(table, 1);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three… two…\n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, 2);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three two\n"
+ "different… lines\n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, 3);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+ "lines \n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, (size_t) -1);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+ "lines \n"));
+ formatted = mfree(formatted);
+
+ assert_se(table_add_many(table,
+ TABLE_STRING, "short",
+ TABLE_STRV, STRV_MAKE("a", "pair")) >= 0);
+
+ assert_se(table_add_many(table,
+ TABLE_STRV, STRV_MAKE("short2"),
+ TABLE_STRV, STRV_MAKE("a", "four", "line", "cell")) >= 0);
+
+ table_set_cell_height_max(table, 1);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three… two…\n"
+ "short a…\n"
+ "short2 a…\n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, 2);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three two\n"
+ "different… lines\n"
+ "short a\n"
+ " pair\n"
+ "short2 a\n"
+ " four…\n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, 3);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+ "lines \n"
+ "short a\n"
+ " pair\n"
+ "short2 a\n"
+ " four\n"
+ " line…\n"));
+ formatted = mfree(formatted);
+
+ table_set_cell_height_max(table, (size_t) -1);
+ assert_se(table_format(table, &formatted) >= 0);
+ fputs(formatted, stdout);
+ assert_se(streq(formatted,
+ "FOO BAR\n"
+ "three two\n"
+ "different lines\n"
+ "lines \n"
+ "short a\n"
+ " pair\n"
+ "short2 a\n"
+ " four\n"
+ " line\n"
+ " cell\n"));
+ formatted = mfree(formatted);
+}
+
int main(int argc, char *argv[]) {
_cleanup_(table_unrefp) Table *t = NULL;
test_issue_9549();
test_multiline();
+ test_strv();
return 0;
}
r = chase_symlinks(p, NULL, 0, &result, NULL);
assert_se(r > 0);
assert_se(path_equal(result, "/usr"));
+ assert_se(streq(result, "/usr")); /* we guarantee that we drop redundant slashes */
result = mfree(result);
r = chase_symlinks(p, temp, 0, &result, NULL);
assert_se(streq("/usr", result));
result = mfree(result);
+ /* Make sure that symlinks in the "root" path are not resolved, but those below are */
+ p = strjoina("/etc/..", temp, "/self");
+ assert_se(symlink(".", p) >= 0);
+ q = strjoina(p, "/top/dot/dotdota");
+ r = chase_symlinks(q, p, 0, &result, NULL);
+ assert_se(r > 0);
+ assert_se(path_equal(path_startswith(result, p), "usr"));
+ result = mfree(result);
+
cleanup:
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
STRV_FOREACH(b, (char**) table) {
_cleanup_free_ char *w = NULL;
- w = strjoin(w, *b);
+ w = strjoin(z, *b);
assert_se(w);
if (access(w, F_OK) < 0) {
continue;
}
- assert_se(rename_noreplace(AT_FDCWD, w, AT_FDCWD, y) == -EEXIST);
+ assert_se(rename_noreplace(AT_FDCWD, x, AT_FDCWD, w) == -EEXIST);
}
y = strjoin(z, "/somethingelse");
assert_se(S_ISLNK(st.st_mode));
}
+static void test_chmod_and_chown_unsafe(void) {
+ _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
+ _unused_ _cleanup_umask_ mode_t u = umask(0000);
+ struct stat st;
+ const char *p;
+
+ if (geteuid() != 0)
+ return;
+
+ log_info("/* %s */", __func__);
+
+ assert_se(mkdtemp_malloc(NULL, &d) >= 0);
+
+ p = strjoina(d, "/reg");
+ assert_se(mknod(p, S_IFREG | 0123, 0) >= 0);
+
+ assert_se(chmod_and_chown_unsafe(p, S_IFREG | 0321, 1, 2) >= 0);
+ assert_se(chmod_and_chown_unsafe(p, S_IFDIR | 0555, 3, 4) == -EINVAL);
+
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISREG(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0321);
+
+ p = strjoina(d, "/dir");
+ assert_se(mkdir(p, 0123) >= 0);
+
+ assert_se(chmod_and_chown_unsafe(p, S_IFDIR | 0321, 1, 2) >= 0);
+ assert_se(chmod_and_chown_unsafe(p, S_IFREG | 0555, 3, 4) == -EINVAL);
+
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISDIR(st.st_mode));
+ assert_se((st.st_mode & 07777) == 0321);
+
+ p = strjoina(d, "/lnk");
+ assert_se(symlink("idontexist", p) >= 0);
+
+ assert_se(chmod_and_chown_unsafe(p, S_IFLNK | 0321, 1, 2) >= 0);
+ assert_se(chmod_and_chown_unsafe(p, S_IFREG | 0555, 3, 4) == -EINVAL);
+ assert_se(chmod_and_chown_unsafe(p, S_IFDIR | 0555, 3, 4) == -EINVAL);
+
+ assert_se(lstat(p, &st) >= 0);
+ assert_se(S_ISLNK(st.st_mode));
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
test_fsync_directory_of_file();
test_rename_noreplace();
test_chmod_and_chown();
+ test_chmod_and_chown_unsafe();
return 0;
}
NULL, 0,
NULL,
NULL,
+ NULL,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0,
&(TemporaryFileSystem) { .path = (char*) "/var", .options = (char*) "ro" }, 1,
tmp_dir,
var_tmp_dir,
+ NULL,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0,
0,
NULL);
if (r < 0) {
- log_error_errno(r, "Failed to setup namespace: %m");
+ log_error_errno(r, "Failed to set up namespace: %m");
log_info("Usage:\n"
" sudo TEST_NS_PROJECTS=/home/lennart/projects ./test-ns\n"
log_info("+ %s", *p);
}
-static void print_generator_binary_paths(UnitFileScope scope) {
- _cleanup_strv_free_ char **paths;
+static void test_generator_binary_paths(UnitFileScope scope) {
+ char template[] = "/tmp/test-path-lookup.XXXXXXX";
+
+ _cleanup_strv_free_ char **gp_without_env = NULL;
+ _cleanup_strv_free_ char **env_gp_without_env = NULL;
+ _cleanup_strv_free_ char **gp_with_env = NULL;
+ _cleanup_strv_free_ char **env_gp_with_env = NULL;
+ char *systemd_generator_path = NULL;
+ char *systemd_env_generator_path = NULL;
char **dir;
+ assert_se(mkdtemp(template));
+
+ assert_se(unsetenv("SYSTEMD_GENERATOR_PATH") == 0);
+ assert_se(unsetenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH") == 0);
+
+ gp_without_env = generator_binary_paths(scope);
+ env_gp_without_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false);
+
+ log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+ STRV_FOREACH(dir, gp_without_env)
+ log_info(" %s", *dir);
+
+ log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+ STRV_FOREACH(dir, env_gp_without_env)
+ log_info(" %s", *dir);
+
+ assert_se(!strv_isempty(gp_without_env));
+ assert_se(!strv_isempty(env_gp_without_env));
+
+ systemd_generator_path = strjoina(template, "/systemd-generator-path");
+ systemd_env_generator_path = strjoina(template, "/systemd-environment-generator-path");
+ assert_se(setenv("SYSTEMD_GENERATOR_PATH", systemd_generator_path, 1) == 0);
+ assert_se(setenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", systemd_env_generator_path, 1) == 0);
+
+ gp_with_env = generator_binary_paths(scope);
+ env_gp_with_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false);
+
log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+ STRV_FOREACH(dir, gp_with_env)
+ log_info(" %s", *dir);
- paths = generator_binary_paths(scope);
- STRV_FOREACH(dir, paths)
+ log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+ STRV_FOREACH(dir, env_gp_with_env)
log_info(" %s", *dir);
+
+ assert_se(strv_equal(gp_with_env, STRV_MAKE(systemd_generator_path)));
+ assert_se(strv_equal(env_gp_with_env, STRV_MAKE(systemd_env_generator_path)));
}
int main(int argc, char **argv) {
test_user_and_global_paths();
- print_generator_binary_paths(UNIT_FILE_SYSTEM);
- print_generator_binary_paths(UNIT_FILE_USER);
+ test_generator_binary_paths(UNIT_FILE_SYSTEM);
+ test_generator_binary_paths(UNIT_FILE_USER);
return EXIT_SUCCESS;
}
assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX);
assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN);
-#if SIZEOF_PID_T >= 4
assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX);
assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN);
-#endif
}
static void test_ioprio_class_from_to_string_one(const char *val, int expected) {
log_info("/* %s */", __func__);
- assert_se(namespace_flags_to_string(0, &s) == 0 && streq(s, ""));
+ assert_se(namespace_flags_to_string(0, &s) == 0 && isempty(s));
s = mfree(s);
assert_se(namespace_flags_to_string(CLONE_NEWNS, &s) == 0 && streq(s, "mnt"));
s = mfree(s);
#include <sched.h>
#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>
info(pid_t);
info(uid_t);
info(gid_t);
+ info(socklen_t);
info(__cpu_mask);
assert_se(streq_ptr(a[3], NULL));
}
-static void test_strv_equal(void) {
+static void test_strv_compare(void) {
_cleanup_strv_free_ char **a = NULL;
_cleanup_strv_free_ char **b = NULL;
_cleanup_strv_free_ char **c = NULL;
+ _cleanup_strv_free_ char **d = NULL;
log_info("/* %s */", __func__);
a = strv_new("one", "two", "three");
assert_se(a);
b = strv_new("one", "two", "three");
- assert_se(a);
+ assert_se(b);
c = strv_new("one", "two", "three", "four");
- assert_se(a);
+ assert_se(c);
+ d = strv_new(NULL);
+ assert_se(d);
- assert_se(strv_equal(a, a));
- assert_se(strv_equal(a, b));
- assert_se(strv_equal(NULL, NULL));
+ assert_se(strv_compare(a, a) == 0);
+ assert_se(strv_compare(a, b) == 0);
+ assert_se(strv_compare(d, d) == 0);
+ assert_se(strv_compare(d, NULL) == 0);
+ assert_se(strv_compare(NULL, NULL) == 0);
- assert_se(!strv_equal(a, c));
- assert_se(!strv_equal(b, c));
- assert_se(!strv_equal(b, NULL));
+ assert_se(strv_compare(a, c) < 0);
+ assert_se(strv_compare(b, c) < 0);
+ assert_se(strv_compare(b, d) == 1);
+ assert_se(strv_compare(b, NULL) == 1);
}
static void test_strv_is_uniq(void) {
static void test_strv_fnmatch(void) {
_cleanup_strv_free_ char **v = NULL;
+ size_t pos;
log_info("/* %s */", __func__);
- assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a", 0));
+ assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a"));
- v = strv_new("*\\*");
- assert_se(!strv_fnmatch(v, "\\", 0));
- assert_se(strv_fnmatch(v, "\\", FNM_NOESCAPE));
+ v = strv_new("xxx", "*\\*", "yyy");
+ assert_se(!strv_fnmatch_full(v, "\\", 0, NULL));
+ assert_se(strv_fnmatch_full(v, "\\", FNM_NOESCAPE, &pos));
+ assert(pos == 1);
}
int main(int argc, char *argv[]) {
test_strv_insert();
test_strv_push_prepend();
test_strv_push();
- test_strv_equal();
+ test_strv_compare();
test_strv_is_uniq();
test_strv_reverse();
test_strv_shell_escape();
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "strv.h"
+#include "sysctl-util.h"
+#include "tests.h"
+
+static const char* cases[] = {
+ "a.b.c", "a/b/c",
+ "a/b/c", "a/b/c",
+ "a/b.c/d", "a/b.c/d",
+ "a.b/c.d", "a/b.c/d",
+
+ "net.ipv4.conf.enp3s0/200.forwarding", "net/ipv4/conf/enp3s0.200/forwarding",
+ "net/ipv4/conf/enp3s0.200/forwarding", "net/ipv4/conf/enp3s0.200/forwarding",
+
+ "a...b...c", "a/b/c",
+ "a///b///c", "a/b/c",
+ ".a...b...c", "a/b/c",
+ "/a///b///c", "a/b/c",
+ NULL,
+};
+
+static void test_sysctl_normalize(void) {
+ log_info("/* %s */", __func__);
+
+ const char **s, **expected;
+ STRV_FOREACH_PAIR(s, expected, cases) {
+ _cleanup_free_ char *t;
+
+ assert_se(t = strdup(*s));
+ assert_se(sysctl_normalize(t) == t);
+
+ log_info("\"%s\" → \"%s\", expected \"%s\"", *s, t, *expected);
+ assert_se(streq(t, *expected));
+ }
+}
+
+int main(int argc, char *argv[]) {
+ test_setup_logging(LOG_INFO);
+
+ test_sysctl_normalize();
+
+ return 0;
+}
#include "alloc-util.h"
#include "format-util.h"
+#include "libcrypt-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
int nresult;
nresult = merge_gid_lists(l2, ELEMENTSOF(l2), l3, ELEMENTSOF(l3), &res1);
+ assert_se(nresult >= 0);
assert_se(memcmp_nn(res1, nresult, result1, ELEMENTSOF(result1)) == 0);
nresult = merge_gid_lists(NULL, 0, l2, ELEMENTSOF(l2), &res2);
+ assert_se(nresult >= 0);
assert_se(memcmp_nn(res2, nresult, l2, ELEMENTSOF(l2)) == 0);
nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l1, ELEMENTSOF(l1), &res3);
+ assert_se(nresult >= 0);
assert_se(memcmp_nn(l1, ELEMENTSOF(l1), res3, nresult) == 0);
nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l4, ELEMENTSOF(l4), &res4);
+ assert_se(nresult >= 0);
assert_se(memcmp_nn(result2, ELEMENTSOF(result2), res4, nresult) == 0);
nresult = getgroups_alloc(&gids);
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
#include "clock-util.h"
#include "conf-files.h"
#include "def.h"
r = manager_listen_setup(m);
if (r < 0)
- return log_warning_errno(r, "Failed to setup connection socket: %m");
+ return log_warning_errno(r, "Failed to set up connection socket: %m");
/*
* Set transmit timestamp, remember it; the server will send that back
root_distance = ntp_ts_short_to_d(&ntpmsg.root_delay) / 2 + ntp_ts_short_to_d(&ntpmsg.root_dispersion);
if (root_distance > (double) m->max_root_distance_usec / (double) USEC_PER_SEC) {
- log_debug("Server has too large root distance. Disconnecting.");
+ log_info("Server has too large root distance. Disconnecting.");
return manager_connect(m);
}
static int send_passwords(const char *socket_name, char **passwords) {
_cleanup_(erase_and_freep) char *packet = NULL;
_cleanup_close_ int socket_fd = -1;
- union sockaddr_union sa = {};
+ union sockaddr_union sa;
+ socklen_t sa_len;
size_t packet_length = 1;
char **p, *d;
ssize_t n;
- int salen;
+ int r;
assert(socket_name);
- salen = sockaddr_un_set_path(&sa.un, socket_name);
- if (salen < 0)
- return salen;
+ r = sockaddr_un_set_path(&sa.un, socket_name);
+ if (r < 0)
+ return r;
+ sa_len = r;
STRV_FOREACH(p, passwords)
packet_length += strlen(*p) + 1;
if (socket_fd < 0)
return log_debug_errno(errno, "socket(): %m");
- n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
+ n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, sa_len);
if (n < 0)
return log_debug_errno(errno, "sendto(): %m");
ret = disk_identify_packet_device_command(fd, out_identify, 512);
goto check_nul_bytes;
}
- if (peripheral_device_type != 0x00) {
+ if (!IN_SET(peripheral_device_type, 0x00, 0x14)) {
ret = -1;
errno = EIO;
goto out;
Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(link_config, alternative_names_policy)
Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias)
Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(link_config, mtu)
-Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed)
+Link.BitsPerSecond, config_parse_si_uint64, 0, offsetof(link_config, speed)
Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex)
Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation)
Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol)
Link.Port, config_parse_port, 0, offsetof(link_config, port)
+Link.ReceiveChecksumOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_RX])
+Link.TransmitChecksumOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TX])
Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO])
Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO])
Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO6])
Link.Advertise, config_parse_advertise, 0, offsetof(link_config, advertise)
Link.RxBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring)
Link.TxBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring)
+Link.RxFlowControl, config_parse_tristate, 0, offsetof(link_config, rx_flow_control)
+Link.TxFlowControl, config_parse_tristate, 0, offsetof(link_config, tx_flow_control)
+Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(link_config, autoneg_flow_control)
.duplex = _DUP_INVALID,
.port = _NET_DEV_PORT_INVALID,
.autonegotiation = -1,
+ .rx_flow_control = -1,
+ .tx_flow_control = -1,
+ .autoneg_flow_control = -1,
};
for (i = 0; i < ELEMENTSOF(link->features); i++)
if (r < 0)
return r;
- if (link->speed > UINT_MAX)
- return -ERANGE;
-
if (set_isempty(link->match_mac) && set_isempty(link->match_permanent_mac) &&
strv_isempty(link->match_path) && strv_isempty(link->match_driver) && strv_isempty(link->match_type) &&
- strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions)
- log_warning("%s: No valid settings found in the [Match] section. "
- "The file will match all interfaces. "
- "If that is intended, please add OriginalName=* in the [Match] section.",
+ strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions) {
+ log_warning("%s: No valid settings found in the [Match] section, ignoring file. "
+ "To match all interfaces, add OriginalName=* in the [Match] section.",
filename);
+ return 0;
+ }
if (!condition_test_list(link->conditions, NULL, NULL, NULL)) {
log_debug("%s: Conditions do not match the system environment, skipping.", filename);
int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) {
struct ether_addr permanent_mac = {};
+ unsigned short iftype = 0;
link_config *link;
const char *name;
- int r;
+ int ifindex, r;
assert(ctx);
assert(device);
if (r < 0)
return r;
+ r = sd_device_get_ifindex(device, &ifindex);
+ if (r < 0)
+ return r;
+
+ r = rtnl_get_link_iftype(&ctx->rtnl, ifindex, &iftype);
+ if (r < 0)
+ return r;
+
r = ethtool_get_permanent_macaddr(&ctx->ethtool_fd, name, &permanent_mac);
if (r < 0)
log_device_debug_errno(device, r, "Failed to get permanent MAC address, ignoring: %m");
LIST_FOREACH(links, link, ctx->links) {
if (net_match_config(link->match_mac, link->match_permanent_mac, link->match_path, link->match_driver,
link->match_type, link->match_name, link->match_property, NULL, NULL, NULL,
- device, NULL, &permanent_mac, NULL, NULL, 0, NULL, NULL)) {
+ iftype, device, NULL, &permanent_mac, NULL, NULL, 0, NULL, NULL)) {
if (link->match_name && !strv_contains(link->match_name, "*")) {
unsigned name_assign_type = NET_NAME_UNKNOWN;
log_warning_errno(r, "Could not set ring buffer of %s: %m", old_name);
}
+ r = ethtool_set_flow_control(&ctx->ethtool_fd, old_name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
+ if (r < 0)
+ log_warning_errno(r, "Could not set flow control of %s: %m", old_name);
+
r = sd_device_get_ifindex(device, &ifindex);
if (r < 0)
return log_device_warning_errno(device, r, "Could not find ifindex: %m");
char **alternative_names;
char *alias;
uint32_t mtu;
- size_t speed;
+ uint64_t speed;
Duplex duplex;
int autonegotiation;
uint32_t advertise[N_ADVERTISE];
int features[_NET_DEV_FEAT_MAX];
netdev_channels channels;
netdev_ring_param ring;
+ int rx_flow_control;
+ int tx_flow_control;
+ int autoneg_flow_control;
LIST_FIELDS(link_config, links);
};
if (argv[optind]) {
r = udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test);
if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to lookup hwdb: %m");
+ return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
if (r == 0)
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
return r;
r = udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test);
if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to lookup hwdb: %m");
+ return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
if (r == 0)
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
return r;
#include "device-util.h"
#include "fd-util.h"
#include "missing_input.h"
+#include "parse-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "udev-builtin.h"
}
}
+static struct input_id get_input_id(sd_device *dev) {
+ const char *v;
+ struct input_id id = {};
+
+ if (sd_device_get_sysattr_value(dev, "id/bustype", &v) >= 0)
+ (void) safe_atoux16(v, &id.bustype);
+ if (sd_device_get_sysattr_value(dev, "id/vendor", &v) >= 0)
+ (void) safe_atoux16(v, &id.vendor);
+ if (sd_device_get_sysattr_value(dev, "id/product", &v) >= 0)
+ (void) safe_atoux16(v, &id.product);
+ if (sd_device_get_sysattr_value(dev, "id/version", &v) >= 0)
+ (void) safe_atoux16(v, &id.version);
+
+ return id;
+}
+
/* pointer devices */
static bool test_pointers(sd_device *dev,
+ const struct input_id *id,
const unsigned long* bitmask_ev,
const unsigned long* bitmask_abs,
const unsigned long* bitmask_key,
bool is_tablet = false;
bool is_joystick = false;
bool is_accelerometer = false;
- bool is_pointing_stick= false;
+ bool is_pointing_stick = false;
has_keys = test_bit(EV_KEY, bitmask_ev);
has_abs_coordinates = test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs);
!has_abs_coordinates)) /* mouse buttons and no axis */
is_mouse = true;
+ /* There is no such thing as an i2c mouse */
+ if (is_mouse && id->bustype == BUS_I2C)
+ is_pointing_stick = true;
+
if (is_pointing_stick)
udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1");
if (is_mouse)
}
if (pdev) {
+ struct input_id id = get_input_id(pdev);
+
/* Use this as a flag that input devices were detected, so that this
* program doesn't need to be called more than once per device */
udev_builtin_add_property(dev, test, "ID_INPUT", "1");
get_cap_mask(pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test);
get_cap_mask(pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test);
get_cap_mask(pdev, "properties", bitmask_props, sizeof(bitmask_props), test);
- is_pointer = test_pointers(dev, bitmask_ev, bitmask_abs,
+ is_pointer = test_pointers(dev, &id, bitmask_ev, bitmask_abs,
bitmask_key, bitmask_rel,
bitmask_props, test);
is_key = test_key(dev, bitmask_ev, bitmask_key, test);
op = OP_ASSIGN;
}
- r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, NULL);
+ r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr);
} else if (streq(key, "RUN")) {
if (is_match || op == OP_REMOVE)
return log_token_invalid_op(rules, key);
r = sd_device_enumerator_new(&e);
if (r < 0)
- return r;
+ return log_oom();
r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to set allowing uninitialized flag: %m");
r = device_enumerator_scan_devices(e);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to scan devices: %m");
FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
- print_record(d);
+ (void) print_record(d);
return 0;
}
assert(manager);
assert(event);
+ if (DEBUG_LOGGING) {
+ DeviceAction action;
+
+ r = device_get_action(event->dev, &action);
+ log_device_debug(event->dev, "Device (SEQNUM=%"PRIu64", ACTION=%s) ready for processing",
+ event->seqnum, r >= 0 ? device_action_to_string(action) : "<unknown>");
+ }
+
HASHMAP_FOREACH(worker, manager->workers, i) {
if (worker->state != WORKER_IDLE)
continue;
return false;
set_delaying_seqnum:
+ log_device_debug(event->dev, "SEQNUM=%" PRIu64 " blocked by SEQNUM=%" PRIu64,
+ event->seqnum, loop_event->seqnum);
+
event->delaying_seqnum = loop_event->seqnum;
return true;
}
if (r < 0)
return log_error_errno(r, "Failed to initialize device monitor: %m");
- (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
+ /* Bump receiver buffer, but only if we are not called via socket activation, as in that
+ * case systemd sets the receive buffer size for us, and the value in the .socket unit
+ * should take full effect. */
+ if (fd_uevent < 0)
+ (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
r = device_monitor_enable_receiving(manager->monitor);
if (r < 0)
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+
+systemd_userwork_sources = files('''
+ userwork.c
+'''.split())
+
+systemd_userdbd_sources = files('''
+ userdbd-manager.c
+ userdbd-manager.h
+ userdbd.c
+'''.split())
+
+userdbctl_sources = files('''
+ userdbctl.c
+'''.split())
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+#include <utmp.h>
+
+#include "dirent-util.h"
+#include "errno-list.h"
+#include "fd-util.h"
+#include "format-table.h"
+#include "format-util.h"
+#include "group-record-show.h"
+#include "main-func.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "user-record-show.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "verbs.h"
+
+static enum {
+ OUTPUT_CLASSIC,
+ OUTPUT_TABLE,
+ OUTPUT_FRIENDLY,
+ OUTPUT_JSON,
+ _OUTPUT_INVALID = -1
+} arg_output = _OUTPUT_INVALID;
+
+static PagerFlags arg_pager_flags = 0;
+static bool arg_legend = true;
+static char** arg_services = NULL;
+static UserDBFlags arg_userdb_flags = 0;
+
+STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep);
+
+static int show_user(UserRecord *ur, Table *table) {
+ int r;
+
+ assert(ur);
+
+ switch (arg_output) {
+
+ case OUTPUT_CLASSIC:
+ if (!uid_is_valid(ur->uid))
+ break;
+
+ printf("%s:x:" UID_FMT ":" GID_FMT ":%s:%s:%s\n",
+ ur->user_name,
+ ur->uid,
+ user_record_gid(ur),
+ strempty(user_record_real_name(ur)),
+ user_record_home_directory(ur),
+ user_record_shell(ur));
+
+ break;
+
+ case OUTPUT_JSON:
+ json_variant_dump(ur->json, JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_PRETTY, NULL, 0);
+ break;
+
+ case OUTPUT_FRIENDLY:
+ user_record_show(ur, true);
+
+ if (ur->incomplete) {
+ fflush(stdout);
+ log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", ur->user_name);
+ }
+
+ break;
+
+ case OUTPUT_TABLE:
+ assert(table);
+
+ r = table_add_many(
+ table,
+ TABLE_STRING, ur->user_name,
+ TABLE_STRING, user_disposition_to_string(user_record_disposition(ur)),
+ TABLE_UID, ur->uid,
+ TABLE_GID, user_record_gid(ur),
+ TABLE_STRING, empty_to_null(ur->real_name),
+ TABLE_STRING, user_record_home_directory(ur),
+ TABLE_STRING, user_record_shell(ur),
+ TABLE_INT, (int) user_record_disposition(ur));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add row to table: %m");
+
+ break;
+
+ default:
+ assert_not_reached("Unexpected output mode");
+ }
+
+ return 0;
+}
+
+static int display_user(int argc, char *argv[], void *userdata) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ bool draw_separator = false;
+ int ret = 0, r;
+
+ if (arg_output < 0)
+ arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
+
+ if (arg_output == OUTPUT_TABLE) {
+ table = table_new("name", "disposition", "uid", "gid", "realname", "home", "shell", "disposition-numeric");
+ if (!table)
+ return log_oom();
+
+ (void) table_set_align_percent(table, table_get_cell(table, 0, 2), 100);
+ (void) table_set_align_percent(table, table_get_cell(table, 0, 3), 100);
+ (void) table_set_empty_string(table, "-");
+ (void) table_set_sort(table, (size_t) 7, (size_t) 2, (size_t) -1);
+ (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6, (size_t) -1);
+ }
+
+ if (argc > 1) {
+ char **i;
+
+ STRV_FOREACH(i, argv + 1) {
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ uid_t uid;
+
+ if (parse_uid(*i, &uid) >= 0)
+ r = userdb_by_uid(uid, arg_userdb_flags, &ur);
+ else
+ r = userdb_by_name(*i, arg_userdb_flags, &ur);
+ if (r < 0) {
+ if (r == -ESRCH)
+ log_error_errno(r, "User %s does not exist.", *i);
+ else if (r == -EHOSTDOWN)
+ log_error_errno(r, "Selected user database service is not available for this request.");
+ else
+ log_error_errno(r, "Failed to find user %s: %m", *i);
+
+ if (ret >= 0)
+ ret = r;
+ } else {
+ if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+ putchar('\n');
+
+ r = show_user(ur, table);
+ if (r < 0)
+ return r;
+
+ draw_separator = true;
+ }
+ }
+ } else {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+ r = userdb_all(arg_userdb_flags, &iterator);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate users: %m");
+
+ for (;;) {
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+
+ r = userdb_iterator_get(iterator, &ur);
+ if (r == -ESRCH)
+ break;
+ if (r == -EHOSTDOWN)
+ return log_error_errno(r, "Selected user database service is not available for this request.");
+ if (r < 0)
+ return log_error_errno(r, "Failed acquire next user: %m");
+
+ if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+ putchar('\n');
+
+ r = show_user(ur, table);
+ if (r < 0)
+ return r;
+
+ draw_separator = true;
+ }
+ }
+
+ if (table) {
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to show table: %m");
+ }
+
+ return ret;
+}
+
+static int show_group(GroupRecord *gr, Table *table) {
+ int r;
+
+ assert(gr);
+
+ switch (arg_output) {
+
+ case OUTPUT_CLASSIC: {
+ _cleanup_free_ char *m = NULL;
+
+ if (!gid_is_valid(gr->gid))
+ break;
+
+ m = strv_join(gr->members, ",");
+ if (!m)
+ return log_oom();
+
+ printf("%s:x:" GID_FMT ":%s\n",
+ gr->group_name,
+ gr->gid,
+ m);
+ break;
+ }
+
+ case OUTPUT_JSON:
+ json_variant_dump(gr->json, JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_PRETTY, NULL, 0);
+ break;
+
+ case OUTPUT_FRIENDLY:
+ group_record_show(gr, true);
+
+ if (gr->incomplete) {
+ fflush(stdout);
+ log_warning("Warning: lacking rights to acquire privileged fields of group record of '%s', output incomplete.", gr->group_name);
+ }
+
+ break;
+
+ case OUTPUT_TABLE:
+ assert(table);
+
+ r = table_add_many(
+ table,
+ TABLE_STRING, gr->group_name,
+ TABLE_STRING, user_disposition_to_string(group_record_disposition(gr)),
+ TABLE_GID, gr->gid,
+ TABLE_INT, (int) group_record_disposition(gr));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add row to table: %m");
+
+ break;
+
+ default:
+ assert_not_reached("Unexpected disply mode");
+ }
+
+ return 0;
+}
+
+
+static int display_group(int argc, char *argv[], void *userdata) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ bool draw_separator = false;
+ int ret = 0, r;
+
+ if (arg_output < 0)
+ arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
+
+ if (arg_output == OUTPUT_TABLE) {
+ table = table_new("name", "disposition", "gid", "disposition-numeric");
+ if (!table)
+ return log_oom();
+
+ (void) table_set_align_percent(table, table_get_cell(table, 0, 2), 100);
+ (void) table_set_sort(table, (size_t) 3, (size_t) 2, (size_t) -1);
+ (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) -1);
+ }
+
+ if (argc > 1) {
+ char **i;
+
+ STRV_FOREACH(i, argv + 1) {
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+ gid_t gid;
+
+ if (parse_gid(*i, &gid) >= 0)
+ r = groupdb_by_gid(gid, arg_userdb_flags, &gr);
+ else
+ r = groupdb_by_name(*i, arg_userdb_flags, &gr);
+ if (r < 0) {
+ if (r == -ESRCH)
+ log_error_errno(r, "Group %s does not exist.", *i);
+ else if (r == -EHOSTDOWN)
+ log_error_errno(r, "Selected group database service is not available for this request.");
+ else
+ log_error_errno(r, "Failed to find group %s: %m", *i);
+
+ if (ret >= 0)
+ ret = r;
+ } else {
+ if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+ putchar('\n');
+
+ r = show_group(gr, table);
+ if (r < 0)
+ return r;
+
+ draw_separator = true;
+ }
+ }
+
+ } else {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+ r = groupdb_all(arg_userdb_flags, &iterator);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate groups: %m");
+
+ for (;;) {
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+
+ r = groupdb_iterator_get(iterator, &gr);
+ if (r == -ESRCH)
+ break;
+ if (r == -EHOSTDOWN)
+ return log_error_errno(r, "Selected group database service is not available for this request.");
+ if (r < 0)
+ return log_error_errno(r, "Failed acquire next group: %m");
+
+ if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+ putchar('\n');
+
+ r = show_group(gr, table);
+ if (r < 0)
+ return r;
+
+ draw_separator = true;
+ }
+
+ }
+
+ if (table) {
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to show table: %m");
+ }
+
+ return ret;
+}
+
+static int show_membership(const char *user, const char *group, Table *table) {
+ int r;
+
+ assert(user);
+ assert(group);
+
+ switch (arg_output) {
+
+ case OUTPUT_CLASSIC:
+ /* Strictly speaking there's no 'classic' output for this concept, but let's output it in
+ * similar style to the classic output for user/group info */
+
+ printf("%s:%s\n", user, group);
+ break;
+
+ case OUTPUT_JSON: {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ r = json_build(&v, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("user", JSON_BUILD_STRING(user)),
+ JSON_BUILD_PAIR("group", JSON_BUILD_STRING(group))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build JSON object: %m");
+
+ json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
+ break;
+ }
+
+ case OUTPUT_FRIENDLY:
+ /* Hmm, this is not particularly friendly, but not sure how we could do this better */
+ printf("%s: %s\n", group, user);
+ break;
+
+ case OUTPUT_TABLE:
+ assert(table);
+
+ r = table_add_many(
+ table,
+ TABLE_STRING, user,
+ TABLE_STRING, group);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add row to table: %m");
+
+ break;
+
+ default:
+ assert_not_reached("Unexpected output mode");
+ }
+
+ return 0;
+}
+
+static int display_memberships(int argc, char *argv[], void *userdata) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ int ret = 0, r;
+
+ if (arg_output < 0)
+ arg_output = OUTPUT_TABLE;
+
+ if (arg_output == OUTPUT_TABLE) {
+ table = table_new("user", "group");
+ if (!table)
+ return log_oom();
+
+ (void) table_set_sort(table, (size_t) 0, (size_t) 1, (size_t) -1);
+ }
+
+ if (argc > 1) {
+ char **i;
+
+ STRV_FOREACH(i, argv + 1) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+ if (streq(argv[0], "users-in-group")) {
+ r = membershipdb_by_group(*i, arg_userdb_flags, &iterator);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate users in group: %m");
+ } else if (streq(argv[0], "groups-of-user")) {
+ r = membershipdb_by_user(*i, arg_userdb_flags, &iterator);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate groups of user: %m");
+ } else
+ assert_not_reached("Unexpected verb");
+
+ for (;;) {
+ _cleanup_free_ char *user = NULL, *group = NULL;
+
+ r = membershipdb_iterator_get(iterator, &user, &group);
+ if (r == -ESRCH)
+ break;
+ if (r == -EHOSTDOWN)
+ return log_error_errno(r, "Selected membership database service is not available for this request.");
+ if (r < 0)
+ return log_error_errno(r, "Failed acquire next membership: %m");
+
+ r = show_membership(user, group, table);
+ if (r < 0)
+ return r;
+ }
+ }
+ } else {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+ r = membershipdb_all(arg_userdb_flags, &iterator);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate memberships: %m");
+
+ for (;;) {
+ _cleanup_free_ char *user = NULL, *group = NULL;
+
+ r = membershipdb_iterator_get(iterator, &user, &group);
+ if (r == -ESRCH)
+ break;
+ if (r == -EHOSTDOWN)
+ return log_error_errno(r, "Selected membership database service is not available for this request.");
+ if (r < 0)
+ return log_error_errno(r, "Failed acquire next membership: %m");
+
+ r = show_membership(user, group, table);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (table) {
+ r = table_print(table, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to show table: %m");
+ }
+
+ return ret;
+}
+
+static int display_services(int argc, char *argv[], void *userdata) {
+ _cleanup_(table_unrefp) Table *t = NULL;
+ _cleanup_(closedirp) DIR *d = NULL;
+ struct dirent *de;
+ int r;
+
+ d = opendir("/run/systemd/userdb/");
+ if (!d) {
+ if (errno == ENOENT) {
+ log_info("No services.");
+ return 0;
+ }
+
+ return log_error_errno(errno, "Failed to open /run/systemd/userdb/: %m");
+ }
+
+ t = table_new("service", "listening");
+ if (!t)
+ return log_oom();
+
+ (void) table_set_sort(t, (size_t) 0, (size_t) -1);
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ _cleanup_free_ char *j = NULL, *no = NULL;
+ union sockaddr_union sockaddr;
+ socklen_t sockaddr_len;
+ _cleanup_close_ int fd = -1;
+
+ j = path_join("/run/systemd/userdb/", de->d_name);
+ if (!j)
+ return log_oom();
+
+ r = sockaddr_un_set_path(&sockaddr.un, j);
+ if (r < 0)
+ return log_error_errno(r, "Path %s does not fit in AF_UNIX socket address: %m", j);
+ sockaddr_len = r;
+
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return log_error_errno(r, "Failed to allocate AF_UNIX/SOCK_STREAM socket: %m");
+
+ if (connect(fd, &sockaddr.un, sockaddr_len) < 0) {
+ no = strjoin("No (", errno_to_name(errno), ")");
+ if (!no)
+ return log_oom();
+ }
+
+ r = table_add_many(t,
+ TABLE_STRING, de->d_name,
+ TABLE_STRING, no ?: "yes",
+ TABLE_SET_COLOR, no ? ansi_highlight_red() : ansi_highlight_green());
+ if (r < 0)
+ return log_error_errno(r, "Failed to add table row: %m");
+ }
+
+ if (table_get_rows(t) <= 0) {
+ log_info("No services.");
+ return 0;
+ }
+
+ if (arg_output == OUTPUT_JSON)
+ table_print_json(t, NULL, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO);
+ else
+ table_print(t, NULL);
+
+ return 0;
+}
+
+static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ int r;
+
+ if (!valid_user_group_name(argv[1]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid user name '%s'.", argv[1]);
+
+ r = userdb_by_name(argv[1], arg_userdb_flags, &ur);
+ if (r == -ESRCH)
+ log_error_errno(r, "User %s does not exist.", argv[1]);
+ else if (r == -EHOSTDOWN)
+ log_error_errno(r, "Selected user database service is not available for this request.");
+ else if (r < 0)
+ log_error_errno(r, "Failed to find user %s: %m", argv[1]);
+
+ if (strv_isempty(ur->ssh_authorized_keys))
+ log_debug("User record for %s has no public SSH keys.", argv[1]);
+ else {
+ char **i;
+
+ STRV_FOREACH(i, ur->ssh_authorized_keys)
+ printf("%s\n", *i);
+ }
+
+ if (ur->incomplete) {
+ fflush(stdout);
+ log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", ur->user_name);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("userdbctl", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s [OPTIONS...] COMMAND ...\n\n"
+ "%sShow user and group information.%s\n"
+ "\nCommands:\n"
+ " user [USER…] Inspect user\n"
+ " group [GROUP…] Inspect group\n"
+ " users-in-group [GROUP…] Show users that are members of specified group(s)\n"
+ " groups-of-user [USER…] Show groups the specified user(s) is a member of\n"
+ " services Show enabled database services\n"
+ "\nOptions:\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-legend Do not show the headers and footers\n"
+ " --output=MODE Select output mode (classic, friendly, table, json)\n"
+ " -j Equivalent to --output=json\n"
+ " -s --service=SERVICE[:SERVICE…]\n"
+ " Query the specified service\n"
+ " --with-nss=BOOL Control whether to include glibc NSS data\n"
+ " -N Disable inclusion of glibc NSS data and disable synthesizing\n"
+ " (Same as --with-nss=no --synthesize=no)\n"
+ " --synthesize=BOOL Synthesize root/nobody user\n"
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , ansi_highlight(), ansi_normal()
+ , link
+ );
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_NO_PAGER,
+ ARG_NO_LEGEND,
+ ARG_OUTPUT,
+ ARG_WITH_NSS,
+ ARG_SYNTHESIZE,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "output", required_argument, NULL, ARG_OUTPUT },
+ { "service", required_argument, NULL, 's' },
+ { "with-nss", required_argument, NULL, ARG_WITH_NSS },
+ { "synthesize", required_argument, NULL, ARG_SYNTHESIZE },
+ {}
+ };
+
+ const char *e;
+ int r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ /* We are going to update this environment variable with our own, hence let's first read what is already set */
+ e = getenv("SYSTEMD_ONLY_USERDB");
+ if (e) {
+ char **l;
+
+ l = strv_split(e, ":");
+ if (!l)
+ return log_oom();
+
+ strv_free(arg_services);
+ arg_services = l;
+ }
+
+ for (;;) {
+ int c;
+
+ c = getopt_long(argc, argv, "hjs:N", options, NULL);
+ if (c < 0)
+ break;
+
+ switch (c) {
+
+ case 'h':
+ return help(0, NULL, NULL);
+
+ case ARG_VERSION:
+ return version();
+
+ case ARG_NO_PAGER:
+ arg_pager_flags |= PAGER_DISABLE;
+ break;
+
+ case ARG_NO_LEGEND:
+ arg_legend = false;
+ break;
+
+ case ARG_OUTPUT:
+ if (streq(optarg, "classic"))
+ arg_output = OUTPUT_CLASSIC;
+ else if (streq(optarg, "friendly"))
+ arg_output = OUTPUT_FRIENDLY;
+ else if (streq(optarg, "json"))
+ arg_output = OUTPUT_JSON;
+ else if (streq(optarg, "table"))
+ arg_output = OUTPUT_TABLE;
+ else if (streq(optarg, "help")) {
+ puts("classic\n"
+ "friendly\n"
+ "json");
+ return 0;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --output= mode: %s", optarg);
+
+ break;
+
+ case 'j':
+ arg_output = OUTPUT_JSON;
+ break;
+
+ case 's':
+ if (isempty(optarg))
+ arg_services = strv_free(arg_services);
+ else {
+ _cleanup_strv_free_ char **l = NULL;
+
+ l = strv_split(optarg, ":");
+ if (!l)
+ return log_oom();
+
+ r = strv_extend_strv(&arg_services, l, true);
+ if (r < 0)
+ return log_oom();
+ }
+
+ break;
+
+ case 'N':
+ arg_userdb_flags |= USERDB_AVOID_NSS|USERDB_DONT_SYNTHESIZE;
+ break;
+
+ case ARG_WITH_NSS:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --with-nss= parameter: %s", optarg);
+
+ SET_FLAG(arg_userdb_flags, USERDB_AVOID_NSS, !r);
+ break;
+
+ case ARG_SYNTHESIZE:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --synthesize= parameter: %s", optarg);
+
+ SET_FLAG(arg_userdb_flags, USERDB_DONT_SYNTHESIZE, !r);
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached("Unhandled option");
+ }
+ }
+
+ return 1;
+}
+
+static int run(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "user", VERB_ANY, VERB_ANY, VERB_DEFAULT, display_user },
+ { "group", VERB_ANY, VERB_ANY, 0, display_group },
+ { "users-in-group", VERB_ANY, VERB_ANY, 0, display_memberships },
+ { "groups-of-user", VERB_ANY, VERB_ANY, 0, display_memberships },
+ { "services", VERB_ANY, 1, 0, display_services },
+
+ /* This one is a helper for sshd_config's AuthorizedKeysCommand= setting, it's not a
+ * user-facing verb and thus should not appear in man pages or --help texts. */
+ { "ssh-authorized-keys", 2, 2, 0, ssh_authorized_keys },
+ {}
+ };
+
+ int r;
+
+ log_show_color(true);
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ if (arg_services) {
+ _cleanup_free_ char *e = NULL;
+
+ e = strv_join(arg_services, ":");
+ if (!e)
+ return log_oom();
+
+ if (setenv("SYSTEMD_ONLY_USERDB", e, true) < 0)
+ return log_error_errno(r, "Failed to set $SYSTEMD_ONLY_USERDB: %m");
+
+ log_info("Enabled services: %s", e);
+ } else {
+ if (unsetenv("SYSTEMD_ONLY_USERDB") < 0)
+ return log_error_errno(r, "Failed to unset $SYSTEMD_ONLY_USERDB: %m");
+ }
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "process-util.h"
+#include "set.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "umask-util.h"
+#include "userdbd-manager.h"
+
+#define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
+
+static int start_workers(Manager *m, bool explicit_request);
+
+static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = userdata;
+
+ assert(s);
+ assert(m);
+
+ for (;;) {
+ siginfo_t siginfo = {};
+ bool removed = false;
+
+ if (waitid(P_ALL, 0, &siginfo, WNOHANG|WEXITED) < 0) {
+ if (errno == ECHILD)
+ break;
+
+ log_warning_errno(errno, "Failed to invoke waitid(): %m");
+ break;
+ }
+ if (siginfo.si_pid == 0)
+ break;
+
+ if (set_remove(m->workers_dynamic, PID_TO_PTR(siginfo.si_pid)))
+ removed = true;
+ if (set_remove(m->workers_fixed, PID_TO_PTR(siginfo.si_pid)))
+ removed = true;
+
+ if (!removed) {
+ log_warning("Weird, got SIGCHLD for unknown child " PID_FMT ", ignoring.", siginfo.si_pid);
+ continue;
+ }
+
+ if (siginfo.si_code == CLD_EXITED) {
+ if (siginfo.si_status == EXIT_SUCCESS)
+ log_debug("Worker " PID_FMT " exited successfully.", siginfo.si_pid);
+ else
+ log_warning("Worker " PID_FMT " died with a failure exit status %i, ignoring.", siginfo.si_pid, siginfo.si_status);
+ } else if (siginfo.si_code == CLD_KILLED)
+ log_warning("Worker " PID_FMT " was killed by signal %s, ignoring.", siginfo.si_pid, signal_to_string(siginfo.si_status));
+ else if (siginfo.si_code == CLD_DUMPED)
+ log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", siginfo.si_pid, signal_to_string(siginfo.si_status));
+ else
+ log_warning("Can't handle SIGCHLD of this type");
+ }
+
+ (void) start_workers(m, false); /* Fill up workers again if we fell below the low watermark */
+ return 0;
+}
+
+static int on_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = userdata;
+
+ assert(s);
+ assert(m);
+
+ (void) start_workers(m, true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
+ return 0;
+}
+
+int manager_new(Manager **ret) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ m = new(Manager, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (Manager) {
+ .listen_fd = -1,
+ .worker_ratelimit = {
+ .interval = 5 * USEC_PER_SEC,
+ .burst = 50,
+ },
+ };
+
+ r = sd_event_new(&m->event);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_set_watchdog(m->event, true);
+
+ m->workers_fixed = set_new(NULL);
+ m->workers_dynamic = set_new(NULL);
+
+ if (!m->workers_fixed || !m->workers_dynamic)
+ return -ENOMEM;
+
+ r = sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, on_sigusr2, m);
+ if (r < 0)
+ return r;
+
+ r = sd_event_add_signal(m->event, &m->sigchld_event_source, SIGCHLD, on_sigchld, m);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(m);
+ return 0;
+}
+
+Manager* manager_free(Manager *m) {
+ if (!m)
+ return NULL;
+
+ set_free(m->workers_fixed);
+ set_free(m->workers_dynamic);
+
+ sd_event_source_disable_unref(m->sigusr2_event_source);
+ sd_event_source_disable_unref(m->sigchld_event_source);
+
+ sd_event_unref(m->event);
+
+ return mfree(m);
+}
+
+static size_t manager_current_workers(Manager *m) {
+ assert(m);
+
+ return set_size(m->workers_fixed) + set_size(m->workers_dynamic);
+}
+
+static int start_one_worker(Manager *m) {
+ bool fixed;
+ pid_t pid;
+ int r;
+
+ assert(m);
+
+ fixed = set_size(m->workers_fixed) < USERDB_WORKERS_MIN;
+
+ r = safe_fork("(sd-worker)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to fork new worker child: %m");
+ if (r == 0) {
+ char pids[DECIMAL_STR_MAX(pid_t)];
+ /* Child */
+
+ log_close();
+
+ r = close_all_fds(&m->listen_fd, 1);
+ if (r < 0) {
+ log_error_errno(r, "Failed to close fds in child: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ log_open();
+
+ if (m->listen_fd == 3) {
+ r = fd_cloexec(3, false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to turn off O_CLOEXEC for fd 3: %m");
+ _exit(EXIT_FAILURE);
+ }
+ } else {
+ if (dup2(m->listen_fd, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
+ log_error_errno(errno, "Failed to move listen fd to 3: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ safe_close(m->listen_fd);
+ }
+
+ xsprintf(pids, PID_FMT, pid);
+ if (setenv("LISTEN_PID", pids, 1) < 0) {
+ log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (setenv("LISTEN_FDS", "1", 1) < 0) {
+ log_error_errno(errno, "Failed to set $LISTEN_FDS: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+
+ if (setenv("USERDB_FIXED_WORKER", one_zero(fixed), 1) < 0) {
+ log_error_errno(errno, "Failed to set $USERDB_FIXED_WORKER: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* execl("/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
+ /* execl("/usr/bin/valgrind", "valgrind", "/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
+
+ execl(SYSTEMD_USERWORK_PATH, "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /* With some extra space rename_process() can make use of */
+ log_error_errno(errno, "Failed start worker process: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (fixed)
+ r = set_put(m->workers_fixed, PID_TO_PTR(pid));
+ else
+ r = set_put(m->workers_dynamic, PID_TO_PTR(pid));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add child process to set: %m");
+
+ return 0;
+}
+
+static int start_workers(Manager *m, bool explicit_request) {
+ int r;
+
+ assert(m);
+
+ for (;;) {
+ size_t n;
+
+ n = manager_current_workers(m);
+ if (n >= USERDB_WORKERS_MIN && (!explicit_request || n >= USERDB_WORKERS_MAX))
+ break;
+
+ if (!ratelimit_below(&m->worker_ratelimit)) {
+ /* If we keep starting workers too often, let's fail the whole daemon, something is wrong */
+ sd_event_exit(m->event, EXIT_FAILURE);
+
+ return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Worker threads requested too frequently, something is wrong.");
+ }
+
+ r = start_one_worker(m);
+ if (r < 0)
+ return r;
+
+ explicit_request = false;
+ }
+
+ return 0;
+}
+
+int manager_startup(Manager *m) {
+ struct timeval ts;
+ int n, r;
+
+ assert(m);
+ assert(m->listen_fd < 0);
+
+ n = sd_listen_fds(false);
+ if (n < 0)
+ return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
+ if (n > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n);
+ if (n == 1)
+ m->listen_fd = SD_LISTEN_FDS_START;
+ else {
+ union sockaddr_union sockaddr = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/userdb/io.systemd.NameServiceSwitch",
+ };
+
+ r = mkdir_p("/run/systemd/userdb", 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
+
+ m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ if (m->listen_fd < 0)
+ return log_error_errno(errno, "Failed to bind on socket: %m");
+
+ (void) sockaddr_un_unlink(&sockaddr.un);
+
+ RUN_WITH_UMASK(0000)
+ if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+ return log_error_errno(errno, "Failed to bind socket: %m");
+
+ r = symlink_idempotent("io.systemd.NameServiceSwitch", "/run/systemd/userdb/io.systemd.Multiplexer", false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
+
+ if (listen(m->listen_fd, SOMAXCONN) < 0)
+ return log_error_errno(errno, "Failed to listen on socket: %m");
+ }
+
+ /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
+ * GC'ed on idle */
+ if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, timeval_store(&ts, LISTEN_TIMEOUT_USEC), sizeof(ts)) < 0)
+ return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
+
+ return start_workers(m, false);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "varlink.h"
+#include "ratelimit.h"
+
+#define USERDB_WORKERS_MIN 3
+#define USERDB_WORKERS_MAX 4096
+
+struct Manager {
+ sd_event *event;
+
+ Set *workers_fixed; /* Workers 0…USERDB_WORKERS_MIN */
+ Set *workers_dynamic; /* Workers USERD_WORKERS_MIN+1…USERDB_WORKERS_MAX */
+
+ sd_event_source *sigusr2_event_source;
+ sd_event_source *sigchld_event_source;
+
+ int listen_fd;
+
+ RateLimit worker_ratelimit;
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "userdbd-manager.h"
+#include "log.h"
+#include "main-func.h"
+#include "signal-util.h"
+
+/* This service offers two Varlink services, both implementing io.systemd.UserDatabase:
+ *
+ * → io.systemd.NameServiceSwitch: this is a compatibility interface for glibc NSS: it response to
+ * name lookups by checking the classic NSS interfaces and responding that.
+ *
+ * → io.systemd.Multiplexer: this multiplexes lookup requests to all Varlink services that have a
+ * socket in /run/systemd/userdb/. It's supposed to simplify clients that don't want to implement
+ * the full iterative logic on their own.
+ */
+
+static int run(int argc, char *argv[]) {
+ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+ _cleanup_(manager_freep) Manager *m = NULL;
+ int r;
+
+ log_setup_service();
+
+ umask(0022);
+
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+ if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NameServiceSwitch:io.systemd.Multiplexer", 1) < 0)
+ return log_error_errno(errno, "Failed to se $SYSTEMD_BYPASS_USERDB: %m");
+
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGUSR2, -1) >= 0);
+
+ r = manager_new(&m);
+ if (r < 0)
+ return log_error_errno(r, "Could not create manager: %m");
+
+ r = manager_startup(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start up daemon: %m");
+
+ notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+ r = sd_event_loop(m->event);
+ if (r < 0)
+ return log_error_errno(r, "Event loop failed: %m");
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <poll.h>
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "env-util.h"
+#include "fd-util.h"
+#include "group-record-nss.h"
+#include "group-record.h"
+#include "main-func.h"
+#include "process-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "user-record-nss.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "varlink.h"
+
+#define ITERATIONS_MAX 64U
+#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
+#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
+#define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
+#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
+
+typedef struct LookupParameters {
+ const char *user_name;
+ const char *group_name;
+ union {
+ uid_t uid;
+ gid_t gid;
+ };
+ const char *service;
+} LookupParameters;
+
+static int add_nss_service(JsonVariant **v) {
+ _cleanup_(json_variant_unrefp) JsonVariant *status = NULL, *z = NULL;
+ char buf[SD_ID128_STRING_MAX];
+ sd_id128_t mid;
+ int r;
+
+ assert(v);
+
+ /* Patch in service field if it's missing. The assumption here is that this field is unset only for
+ * NSS records */
+
+ if (json_variant_by_key(*v, "service"))
+ return 0;
+
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return r;
+
+ status = json_variant_ref(json_variant_by_key(*v, "status"));
+ z = json_variant_ref(json_variant_by_key(status, sd_id128_to_string(mid, buf)));
+
+ if (json_variant_by_key(z, "service"))
+ return 0;
+
+ r = json_variant_set_field_string(&z, "service", "io.systemd.NameServiceSwitch");
+ if (r < 0)
+ return r;
+
+ r = json_variant_set_field(&status, buf, z);
+ if (r < 0)
+ return r;
+
+ return json_variant_set_field(v, "status", status);
+}
+
+static int build_user_json(Varlink *link, UserRecord *ur, JsonVariant **ret) {
+ _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ UserRecordLoadFlags flags;
+ uid_t peer_uid;
+ bool trusted;
+ int r;
+
+ assert(ur);
+ assert(ret);
+
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0) {
+ log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
+ trusted = false;
+ } else
+ trusted = peer_uid == 0 || peer_uid == ur->uid;
+
+ flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+ if (trusted)
+ flags |= USER_RECORD_ALLOW_PRIVILEGED;
+ else
+ flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+ r = user_record_clone(ur, flags, &stripped);
+ if (r < 0)
+ return r;
+
+ stripped->incomplete =
+ ur->incomplete ||
+ (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
+ !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
+
+ v = json_variant_ref(stripped->json);
+ r = add_nss_service(&v);
+ if (r < 0)
+ return r;
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v)),
+ JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
+}
+
+static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+ LookupParameters p = {
+ .uid = UID_INVALID,
+ };
+ int r;
+
+ assert(parameters);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
+ if (uid_is_valid(p.uid))
+ r = nss_user_record_by_uid(p.uid, &hr);
+ else if (p.user_name)
+ r = nss_user_record_by_name(p.user_name, &hr);
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+ setpwent();
+
+ for (;;) {
+ _cleanup_(user_record_unrefp) UserRecord *z = NULL;
+ _cleanup_free_ char *sbuf = NULL;
+ struct passwd *pw;
+ struct spwd spwd;
+
+ errno = 0;
+ pw = getpwent();
+ if (!pw) {
+ if (errno != 0)
+ log_debug_errno(errno, "Failure while iterating through NSS user database, ignoring: %m");
+
+ break;
+ }
+
+ r = nss_spwd_for_passwd(pw, &spwd, &sbuf);
+ if (r < 0)
+ log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
+
+ r = nss_passwd_to_user_record(pw, NULL, &z);
+ if (r < 0) {
+ endpwent();
+ return r;
+ }
+
+ if (last) {
+ r = varlink_notify(link, last);
+ if (r < 0) {
+ endpwent();
+ return r;
+ }
+
+ last = json_variant_unref(last);
+ }
+
+ r = build_user_json(link, z, &last);
+ if (r < 0) {
+ endpwent();
+ return r;
+ }
+ }
+
+ endpwent();
+
+ if (!last)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, last);
+ }
+
+ } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
+
+ if (uid_is_valid(p.uid))
+ r = userdb_by_uid(p.uid, USERDB_AVOID_MULTIPLEXER, &hr);
+ else if (p.user_name)
+ r = userdb_by_name(p.user_name, USERDB_AVOID_MULTIPLEXER, &hr);
+ else {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+ r = userdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_(user_record_unrefp) UserRecord *z = NULL;
+
+ r = userdb_iterator_get(iterator, &z);
+ if (r == -ESRCH)
+ break;
+ if (r < 0)
+ return r;
+
+ if (last) {
+ r = varlink_notify(link, last);
+ if (r < 0)
+ return r;
+
+ last = json_variant_unref(last);
+ }
+
+ r = build_user_json(link, z, &last);
+ if (r < 0)
+ return r;
+ }
+
+ if (!last)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, last);
+ }
+ } else
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+ if (r == -ESRCH)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ if (r < 0) {
+ log_debug_errno(r, "User lookup failed abnormally: %m");
+ return varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
+ }
+
+ if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
+ (p.user_name && !streq(hr->user_name, p.user_name)))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ r = build_user_json(link, hr, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+static int build_group_json(Varlink *link, GroupRecord *gr, JsonVariant **ret) {
+ _cleanup_(group_record_unrefp) GroupRecord *stripped = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ UserRecordLoadFlags flags;
+ uid_t peer_uid;
+ bool trusted;
+ int r;
+
+ assert(gr);
+ assert(ret);
+
+ r = varlink_get_peer_uid(link, &peer_uid);
+ if (r < 0) {
+ log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
+ trusted = false;
+ } else
+ trusted = peer_uid == 0;
+
+ flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+ if (trusted)
+ flags |= USER_RECORD_ALLOW_PRIVILEGED;
+ else
+ flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+ r = group_record_clone(gr, flags, &stripped);
+ if (r < 0)
+ return r;
+
+ stripped->incomplete =
+ gr->incomplete ||
+ (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
+ !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
+
+ v = json_variant_ref(gr->json);
+ r = add_nss_service(&v);
+ if (r < 0)
+ return r;
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v)),
+ JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
+}
+
+static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ LookupParameters p = {
+ .gid = GID_INVALID,
+ };
+ int r;
+
+ assert(parameters);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
+
+ if (gid_is_valid(p.gid))
+ r = nss_group_record_by_gid(p.gid, &g);
+ else if (p.group_name)
+ r = nss_group_record_by_name(p.group_name, &g);
+ else {
+ _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+ setgrent();
+
+ for (;;) {
+ _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
+ _cleanup_free_ char *sbuf = NULL;
+ struct group *grp;
+ struct sgrp sgrp;
+
+ errno = 0;
+ grp = getgrent();
+ if (!grp) {
+ if (errno != 0)
+ log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
+
+ break;
+ }
+
+ r = nss_sgrp_for_group(grp, &sgrp, &sbuf);
+ if (r < 0)
+ log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", grp->gr_name);
+
+ r = nss_group_to_group_record(grp, r >= 0 ? &sgrp : NULL, &z);
+ if (r < 0) {
+ endgrent();
+ return r;
+ }
+
+ if (last) {
+ r = varlink_notify(link, last);
+ if (r < 0) {
+ endgrent();
+ return r;
+ }
+
+ last = json_variant_unref(last);
+ }
+
+ r = build_group_json(link, z, &last);
+ if (r < 0) {
+ endgrent();
+ return r;
+ }
+ }
+
+ endgrent();
+
+ if (!last)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, last);
+ }
+
+ } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
+
+ if (gid_is_valid(p.gid))
+ r = groupdb_by_gid(p.gid, USERDB_AVOID_MULTIPLEXER, &g);
+ else if (p.group_name)
+ r = groupdb_by_name(p.group_name, USERDB_AVOID_MULTIPLEXER, &g);
+ else {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+ r = groupdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
+
+ r = groupdb_iterator_get(iterator, &z);
+ if (r == -ESRCH)
+ break;
+ if (r < 0)
+ return r;
+
+ if (last) {
+ r = varlink_notify(link, last);
+ if (r < 0)
+ return r;
+
+ last = json_variant_unref(last);
+ }
+
+ r = build_group_json(link, z, &last);
+ if (r < 0)
+ return r;
+ }
+
+ if (!last)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_reply(link, last);
+ }
+ } else
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+ if (r == -ESRCH)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Group lookup failed abnormally: %m");
+ return varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
+ }
+
+ if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
+ (p.group_name && !streq(g->group_name, p.group_name)))
+ return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+ r = build_group_json(link, g, &v);
+ if (r < 0)
+ return r;
+
+ return varlink_reply(link, v);
+}
+
+static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+ static const JsonDispatch dispatch_table[] = {
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
+ { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ {}
+ };
+
+ LookupParameters p = {};
+ int r;
+
+ assert(parameters);
+
+ r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+ if (r < 0)
+ return r;
+
+ if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
+
+ if (p.group_name) {
+ _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+ const char *last = NULL;
+ char **i;
+
+ r = nss_group_record_by_name(p.group_name, &g);
+ if (r == -ESRCH)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(i, g->members) {
+
+ if (p.user_name && !streq_ptr(p.user_name, *i))
+ continue;
+
+ if (last) {
+ r = varlink_notifyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
+ if (r < 0)
+ return r;
+ }
+
+ last = *i;
+ }
+
+ if (!last)
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
+ } else {
+ _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
+
+ setgrent();
+
+ for (;;) {
+ struct group *grp;
+ const char* two[2], **users, **i;
+
+ errno = 0;
+ grp = getgrent();
+ if (!grp) {
+ if (errno != 0)
+ log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
+
+ break;
+ }
+
+ if (p.user_name) {
+ if (!strv_contains(grp->gr_mem, p.user_name))
+ continue;
+
+ two[0] = p.user_name;
+ two[1] = NULL;
+
+ users = two;
+ } else
+ users = (const char**) grp->gr_mem;
+
+ STRV_FOREACH(i, users) {
+
+ if (last_user_name) {
+ assert(last_group_name);
+
+ r = varlink_notifyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+ if (r < 0) {
+ endgrent();
+ return r;
+ }
+
+ free(last_user_name);
+ free(last_group_name);
+ }
+
+ last_user_name = strdup(*i);
+ last_group_name = strdup(grp->gr_name);
+ if (!last_user_name || !last_group_name) {
+ endgrent();
+ return -ENOMEM;
+ }
+ }
+ }
+
+ endgrent();
+
+ if (!last_user_name) {
+ assert(!last_group_name);
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ }
+
+ assert(last_group_name);
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+ }
+
+ } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
+
+ _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+ if (p.group_name)
+ r = membershipdb_by_group(p.group_name, USERDB_AVOID_MULTIPLEXER, &iterator);
+ else if (p.user_name)
+ r = membershipdb_by_user(p.user_name, USERDB_AVOID_MULTIPLEXER, &iterator);
+ else
+ r = membershipdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *user_name = NULL, *group_name = NULL;
+
+ r = membershipdb_iterator_get(iterator, &user_name, &group_name);
+ if (r == -ESRCH)
+ break;
+ if (r < 0)
+ return r;
+
+ /* If both group + user are specified do a-posteriori filtering */
+ if (p.group_name && p.user_name && !streq(group_name, p.group_name))
+ continue;
+
+ if (last_user_name) {
+ assert(last_group_name);
+
+ r = varlink_notifyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+ if (r < 0)
+ return r;
+
+ free(last_user_name);
+ free(last_group_name);
+ }
+
+ last_user_name = TAKE_PTR(user_name);
+ last_group_name = TAKE_PTR(group_name);
+ }
+
+ if (!last_user_name) {
+ assert(!last_group_name);
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+ }
+
+ assert(last_group_name);
+
+ return varlink_replyb(link, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+ JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+ }
+
+ return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+}
+
+static int process_connection(VarlinkServer *server, int fd) {
+ _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
+ int r;
+
+ r = varlink_server_add_connection(server, fd, &vl);
+ if (r < 0) {
+ fd = safe_close(fd);
+ return log_error_errno(r, "Failed to add connection: %m");
+ }
+
+ vl = varlink_ref(vl);
+
+ for (;;) {
+ r = varlink_process(vl);
+ if (r == -ENOTCONN) {
+ log_debug("Connection terminated.");
+ break;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to process connection: %m");
+ if (r > 0)
+ continue;
+
+ r = varlink_wait(vl, CONNECTION_IDLE_USEC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait for connection events: %m");
+ if (r == 0)
+ break;
+ }
+
+ return 0;
+}
+
+static int run(int argc, char *argv[]) {
+ usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
+ _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
+ _cleanup_close_ int lock = -1;
+ unsigned n_iterations = 0;
+ int m, listen_fd, r;
+
+ log_setup_service();
+
+ m = sd_listen_fds(false);
+ if (m < 0)
+ return log_error_errno(m, "Failed to determine number of listening fds: %m");
+ if (m == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
+ if (m > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
+
+ listen_fd = SD_LISTEN_FDS_START;
+
+ r = fd_nonblock(listen_fd, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
+
+ r = varlink_server_new(&server, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate server: %m");
+
+ r = varlink_server_bind_method_many(
+ server,
+ "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
+ "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
+ "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind methods: %m");
+
+ r = getenv_bool("USERDB_FIXED_WORKER");
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
+ listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
+
+ lock = userdb_nss_compat_disable();
+ if (lock < 0)
+ return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
+
+ start_time = now(CLOCK_MONOTONIC);
+
+ for (;;) {
+ _cleanup_close_ int fd = -1;
+ usec_t n;
+
+ /* Exit the worker in regular intervals, to flush out all memory use */
+ if (n_iterations++ > ITERATIONS_MAX) {
+ log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
+ break;
+ }
+
+ n = now(CLOCK_MONOTONIC);
+ if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ log_debug("Exiting worker, ran for %s, that's enough.", format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, start_time), 0));
+ break;
+ }
+
+ if (last_busy_usec == USEC_INFINITY)
+ last_busy_usec = n;
+ else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ log_debug("Exiting worker, been idle for %s, .", format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, last_busy_usec), 0));
+ break;
+ }
+
+ (void) rename_process("systemd-userwork: waiting...");
+
+ fd = accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ if (fd < 0)
+ fd = -errno;
+
+ (void) rename_process("systemd-userwork: processing...");
+
+ if (fd == -EAGAIN)
+ continue; /* The listening socket as SO_RECVTIMEO set, hence a time-out is expected
+ * after a while, let's check if it's time to exit though. */
+ if (fd == -EINTR)
+ continue; /* Might be that somebody attached via strace, let's just continue in that
+ * case */
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to accept() from listening socket: %m");
+
+ if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
+ struct pollfd pfd = {
+ .fd = listen_fd,
+ .events = POLLIN,
+ };
+
+ /* We only slept a very short time? If so, let's see if there are more sockets
+ * pending, and if so, let's ask our parent for more workers */
+
+ if (poll(&pfd, 1, 0) < 0)
+ return log_error_errno(errno, "Failed to test for POLLIN on listening socket: %m");
+
+ if (FLAGS_SET(pfd.revents, POLLIN)) {
+ pid_t parent;
+
+ parent = getppid();
+ if (parent <= 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parent already died?");
+
+ if (kill(parent, SIGUSR1) < 0)
+ return log_error_errno(errno, "Failed to kill our own parent.");
+ }
+ }
+
+ (void) process_connection(server, TAKE_FD(fd));
+ last_busy_usec = USEC_INFINITY;
+ }
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
+/* Detailed project version that includes git commit when not built from a release.
+ * Use this in preference to PROJECT_VERSION, with the following exceptions:
+ * - where a simplified form is expected for compatiblity, for example
+ * 'udevadm version',
+ * - where a simplified machine-parsable form is more useful, for example
+ * pkgconfig files and version information written to binary files.
+ */
#define GIT_VERSION "@VCS_TAG@"
kernel.core_uses_pid = 1
# Source route verification
-net.ipv4.conf.all.rp_filter = 2
+net.ipv4.conf.default.rp_filter = 2
+net.ipv4.conf.*.rp_filter = 2
+-net.ipv4.conf.all.rp_filter
# Do not accept source routing
-net.ipv4.conf.all.accept_source_route = 0
+net.ipv4.conf.default.accept_source_route = 0
+net.ipv4.conf.*.accept_source_route = 0
+-net.ipv4.conf.all.accept_source_route
# Promote secondary addresses when the primary address is removed
-net.ipv4.conf.all.promote_secondaries = 1
+net.ipv4.conf.default.promote_secondaries = 1
+net.ipv4.conf.*.promote_secondaries = 1
+-net.ipv4.conf.all.promote_secondaries
# ping(8) without CAP_NET_ADMIN and CAP_NET_RAW
# The upper limit is set to 2^31-1. Values greater than that get rejected by
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Basic systemd setup"
RUN_IN_UNPRIVILEGED_CONTAINER=${RUN_IN_UNPRIVILEGED_CONTAINER:-yes}
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="cryptsetup systemd setup"
TEST_NO_NSPAWN=1
-#!/bin/bash -ex
+#!/usr/bin/env bash
+set -ex
# Test merging of a --job-mode=ignore-dependencies job into a previously
# installed job.
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Job-related tests"
TEST_NO_QEMU=1
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Journal-related tests"
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Resource limits-related tests"
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="SELinux tests"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/1981"
TEST_NO_QEMU=1
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2730"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467"
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3166"
TEST_NO_NSPAWN=1
cat >$initdir/test-fail-on-restart.sh <<'EOF'
-#!/bin/bash -x
+#!/usr/bin/env bash
+set -x
systemctl start fail-on-restart.service
active_state=$(systemctl show --property ActiveState fail-on-restart.service)
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3171"
TEST_NO_QEMU=1
EOF
cat >$initdir/test-socket-group.sh <<'EOF'
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
set -u
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="systemd-nspawn smoke test"
TEST_NO_NSPAWN=1
EOF
cat >$initdir/test-nspawn.sh <<'EOF'
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
set -u
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="/etc/machine-id testing"
TEST_NO_NSPAWN=1
EOF
cat >$initdir/test-machine-id-setup.sh <<'EOF'
-#!/bin/bash
+#!/usr/bin/env bash
set -e
set -x
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Dropin tests"
TEST_NO_QEMU=1
-#!/bin/bash
+#!/usr/bin/env bash
set -v -x
rm -f /test.log
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="EXTEND_TIMEOUT_USEC=usec start/runtime/stop tests"
SKIP_INITRD=yes
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="UDEV SYSTEMD_WANTS property"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="FailureAction= operation"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test cgroup delegation in the unified hierarchy"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test changing main PID"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
test `systemctl show -p MainPID --value testsuite.service` -eq $$
cat >/tmp/mainpid.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
set -eux
set -o pipefail
test `systemctl show -p MainPID --value mainpidsh.service` -eq `cat /run/mainpidsh/pid`
cat >/tmp/mainpid2.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
set -eux
set -o pipefail
test `systemctl show -p MainPID --value mainpidsh2.service` -eq `cat /run/mainpidsh2/pid`
cat >/dev/shm/mainpid3.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
set -eux
set -o pipefail
--- /dev/null
+hoge:x:300:
+baz:x:302:
+yyy:x:SYSTEM_GID_MAX:
+foo:x:301:
+ccc:x:305:
--- /dev/null
+foo:x:301:301::/:NOLOGIN
+aaa:x:303:302::/:NOLOGIN
+bbb:x:304:302::/:NOLOGIN
+ccc:x:305:305::/:NOLOGIN
+zzz:x:306:SYSTEM_GID_MAX::/:NOLOGIN
--- /dev/null
+# Ensure that the semantic for the uid:groupname syntax is correct
+#
+#Type Name ID GECOS HOMEDIR
+g hoge 300 - -
+u foo 301 - -
+
+g baz 302 - -
+u aaa 303:baz - -
+u bbb 304:baz - -
+u ccc 305 - -
+
+g yyy -
+u zzz 306:yyy
--- /dev/null
+pre:x:987:
--- /dev/null
+aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN
--- /dev/null
+pre:x:987:
--- /dev/null
+# Ensure that a preexisting system group can be used as primary
+#
+#Type Name ID GECOS HOMEDIR
+u aaa -:pre
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Sysuser-related tests"
# get this value from config.h, however the autopkgtest fails with
# it
SYSTEM_UID_MAX=$(awk 'BEGIN { uid=999 } /^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }' /etc/login.defs)
+ SYSTEM_GID_MAX=$(awk 'BEGIN { gid=999 } /^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }' /etc/login.defs)
# we can't rely on config.h to get the nologin path, as autopkgtest
# uses pre-compiled binaries, so extract it from the systemd-sysusers
NOLOGIN=$(strings $(type -p systemd-sysusers) | grep nologin)
sed -e "s/SYSTEM_UID_MAX/${SYSTEM_UID_MAX}/g" \
+ -e "s/SYSTEM_GID_MAX/${SYSTEM_GID_MAX}/g" \
-e "s#NOLOGIN#${NOLOGIN}#g" "$in"
}
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
systemd-sysusers --root=$TESTDIR 2> /dev/null
+ journalctl --sync
journalctl -t systemd-sysusers -o cat | tail -n1 > $TESTDIR/tmp/err
if ! diff -u $TESTDIR/tmp/err ${f%.*}.expected-err; then
echo "**** Unexpected error output for $f"
--- /dev/null
+Group g1 not found.
--- /dev/null
+# Ensure it is not allowed to create groups implicitly in the uid:groupname syntax
+#
+#Type Name ID GECOS HOMEDIR
+u u1 100:g1 -
-#!/bin/bash
+#!/usr/bin/env bash
set -x
set -e
-#!/bin/bash
+#!/usr/bin/env bash
set -e
set -x
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Tmpfiles related tests"
TEST_NO_QEMU=1
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test Type=exec"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Run unit tests under containers"
RUN_IN_UNPRIVILEGED_CONTAINER=yes
-#!/bin/bash
+#!/usr/bin/env bash
#set -ex
#set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test importd"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test setenv"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test StandardOutput=file:"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Ensure %j Wants directives work"
RUN_IN_UNPRIVILEGED_CONTAINER=yes
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="UDEV ID_RENAMING property"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test OnClockChange= + OnTimezoneChange="
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="plugged -> dead -> plugged issue #11997"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -e
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test OOM killer logic"
TEST_NO_NSPAWN=1
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test migrating state directory from DynamicUser=1 to DynamicUser=0 and back"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="network-generator tests"
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test MUMAPolicy= and NUMAMask= options"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Test ExecReload= (PR #13098)"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test ExecXYZEx= service unit dbus hookups"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Test oneshot unit restart on failure"
. $TEST_BASE_DIR/test-functions
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test that ExecStopPost= is always run"
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
systemd-analyze log-level debug
test -f /run/exec2
cat > /tmp/forking1.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
set -eux
test -f /run/forking1
cat > /tmp/forking2.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
set -eux
test -f /run/dbus2
cat > /tmp/notify1.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
set -eux
-#!/bin/bash
+#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Test PrivateUsers=yes on user manager"
. $TEST_BASE_DIR/test-functions
-#!/bin/bash
+#!/usr/bin/env bash
set -ex
set -o pipefail
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="test log namespaces"
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+ create_empty_image_rootdir
+
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+
+ mask_supporting_services
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+Before=getty-pre.target
+Wants=getty-pre.target
+Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+LogTarget=foobar
+EOF
+ cp testsuite.sh $initdir/
+
+ setup_testsuite
+ )
+ setup_nspawn_root
+}
+
+do_test "$@"
--- /dev/null
+#!/usr/bin/env bash
+set -ex
+
+systemd-analyze log-level debug
+
+systemd-run -p LogNamespace=foobar echo "hello world"
+
+journalctl --namespace=foobar --sync
+journalctl --namespace=foobar > /tmp/hello-world
+journalctl > /tmp/no-hello-world
+
+grep "hello world" /tmp/hello-world
+! grep "hello world" /tmp/no-hello-world
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="test systemd-repart"
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+ create_empty_image_rootdir
+
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+
+ mask_supporting_services
+ dracut_install truncate sfdisk grep
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+Before=getty-pre.target
+Wants=getty-pre.target
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+EOF
+ cp testsuite.sh $initdir/
+
+ setup_testsuite
+ )
+ setup_nspawn_root
+}
+
+do_test "$@"
--- /dev/null
+#!/usr/bin/env bash
+set -ex
+
+# Check if repart is installed, and if it isn't bail out early instead of failing
+if ! test -x /usr/bin/systemd-repart ; then
+ echo OK > /testok
+ exit 0
+fi
+
+systemd-analyze log-level debug
+
+truncate -s 1G /tmp/zzz
+
+SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8
+
+systemd-repart /tmp/zzz --empty=force --dry-run=no --seed=$SEED
+
+sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/empty
+
+cmp /tmp/empty - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: /tmp/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+EOF
+
+mkdir /tmp/definitions
+
+cat > /tmp/definitions/root.conf <<EOF
+[Partition]
+Type=root
+EOF
+
+ln -s root.conf /tmp/definitions/root2.conf
+
+cat > /tmp/definitions/home.conf <<EOF
+[Partition]
+Type=home
+EOF
+
+cat > /tmp/definitions/swap.conf <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+PaddingMinBytes=92M
+EOF
+
+systemd-repart /tmp/zzz --dry-run=no --seed=$SEED --definitions=/tmp/definitions
+
+sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/populated
+
+cmp /tmp/populated - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: /tmp/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+/tmp/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home"
+/tmp/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
+/tmp/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
+/tmp/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
+EOF
+
+cat > /tmp/definitions/swap.conf <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+EOF
+
+cat > /tmp/definitions/extra.conf <<EOF
+[Partition]
+Type=linux-generic
+EOF
+
+systemd-repart /tmp/zzz --dry-run=no --seed=$SEED --definitions=/tmp/definitions
+
+sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/populated2
+
+cmp /tmp/populated2 - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: /tmp/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+/tmp/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home"
+/tmp/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
+/tmp/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
+/tmp/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
+/tmp/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=03477476-06AD-44E8-9EF4-BC2BD7771289, name="linux-generic"
+EOF
+
+truncate -s 2G /tmp/zzz
+
+systemd-repart /tmp/zzz --dry-run=no --seed=$SEED --definitions=/tmp/definitions
+
+sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/populated3
+
+cmp /tmp/populated3 - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: /tmp/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 4194270
+/tmp/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home"
+/tmp/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
+/tmp/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
+/tmp/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
+/tmp/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=03477476-06AD-44E8-9EF4-BC2BD7771289, name="linux-generic"
+EOF
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="testing homed"
+TEST_NO_QEMU=1
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+ create_empty_image
+ mkdir -p $TESTDIR/root
+ mount ${LOOPDEV}p1 $TESTDIR/root
+
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+ mask_supporting_services
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+Before=getty-pre.target
+Wants=getty-pre.target
+
+[Service]
+ExecStart=/bin/bash -x /testsuite.sh
+Type=oneshot
+NotifyAccess=all
+EOF
+ cp testsuite.sh $initdir/
+
+ setup_testsuite
+ ) || return 1
+ setup_nspawn_root
+
+ ddebug "umount $TESTDIR/root"
+ umount $TESTDIR/root
+}
+
+do_test "$@"
--- /dev/null
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+# Check if homectl is installed, and if it isn't bail out early instead of failing
+if ! test -x /usr/bin/homectl ; then
+ echo OK > /testok
+ exit 0
+fi
+
+inspect() {
+ homectl inspect $1 | tee /tmp/a
+ userdbctl user $1 | tee /tmp/b
+ cmp /tmp/a /tmp/b
+ rm /tmp/a /tmp/b
+}
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+NEWPASSWORD=xEhErW0ndafV4s homectl create test-user --disk-size=20M
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
+inspect test-user
+
+PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
+inspect test-user
+
+SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Offline test"
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
+inspect test-user
+
+! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
+! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+
+homectl remove test-user
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+
+sleep infinity &
+echo $! > /leakedtestpid
+wait $!
--- /dev/null
+#!/bin/bash
+set -e
+TEST_DESCRIPTION="Test that KillMode=mixed does not leave left over proccesses with ExecStopPost="
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+ create_empty_image_rootdir
+
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+ mask_supporting_services
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/testsuite.sh
+Type=oneshot
+EOF
+ cat > $initdir/etc/systemd/system/issue_14566_test.service << EOF
+[Unit]
+Description=Issue 14566 Repro
+
+[Service]
+ExecStart=/repro.sh
+ExecStopPost=/bin/true
+KillMode=mixed
+EOF
+
+ cp testsuite.sh $initdir/
+ cp repro.sh $initdir/
+
+ setup_testsuite
+ )
+ setup_nspawn_root
+}
+
+do_test "$@"
--- /dev/null
+#!/bin/bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemctl start issue_14566_test
+systemctl status issue_14566_test
+
+leaked_pid=$(cat /leakedtestpid)
+
+systemctl stop issue_14566_test
+
+# Leaked PID will still be around if we're buggy.
+# I personally prefer to see 42.
+ps -p "$leaked_pid" && exit 42
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
AutoNegotiation=
WakeOnLan=
Port=
+ReceiveChecksumOffload=
+TransmitChecksumOffload=
GenericSegmentationOffload=
TCPSegmentationOffload=
TCP6SegmentationOffload=
Advertise=
RxBufferSize=
TxBufferSize=
+RxFlowControl=
+TxFlowControl=
+AutoNegotiationFlowControl=
RapidCommit=
ForceDHCPv6PDOtherInformation=
PrefixDelegationHint=
+WithoutRA=
[Route]
Destination=
Protocol=
PreferredLifetimeSec=
AddressAutoconfiguration=
ValidLifetimeSec=
+Assign=
[IPv6RoutePrefix]
Route=
LifetimeSec=
BitRate=
RestartSec=
TripleSampling=
+Termination=
[Address]
DuplicateAddressDetection=
AutoJoin=
IPProtocol=
InvertRule=
Family=
+SuppressPrefixLength=
+User=
[IPv6PrefixDelegation]
RouterPreference=
DNSLifetimeSec=
UseDomains=
RouteTable=
UseDNS=
+DHCPv6Client=
UseAutonomousPrefix=
UseOnLinkPrefix=
BlackList=
[NextHop]
Id=
Gateway=
+[QDisc]
+Parent=
+Handle=
+[NetworkEmulator]
+Parent=
+Handle=
+DelaySec=
+DelayJitterSec=
+LossRate=
+DuplicateRate=
+PacketLimit=
+[TokenBucketFilter]
+Parent=
+Handle=
+Rate=
+Burst=
+LimitSize=
+MTUBytes=
+MPUBytes=
+PeakRate=
+LatencySec=
+[StochasticFairnessQueueing]
+Parent=
+Handle=
+PerturbPeriodSec=
+[FairQueueingControlledDelay]
+Parent=
+Handle=
+PacketLimit=
+MemoryLimit=
+Flows=
+Quantum=
+TargetSec=
+IntervalSec=
+CEThresholdSec=
+ECN=
+[FairQueueing]
+Parent=
+Handle=
+PacketLimit=
+FlowLimit=
+Quantum=
+InitialQuantum=
+MaximumRate=
+Buckets=
+OrphanMask=
+Pacing=
+CEThresholdSec=
+[ControlledDelay]
+Parent=
+Handle=
+PacketLimit=
+TargetSec=
+IntervalSec=
+CEThresholdSec=
+ECN=
+[CAKE]
+Parent=
+Handle=
+Bandwidth=
+Overhead=
[TrafficControlQueueingDiscipline]
Parent=
NetworkEmulatorDelaySec=
NetworkEmulatorLossRate=
NetworkEmulatorDuplicateRate=
NetworkEmulatorPacketLimit=
-TokenBufferFilterRate=
-TokenBufferFilterBurst=
-TokenBufferFilterLimitSize=
-TokenBufferFilterMTUBytes=
-TokenBufferFilterMPUBytes=
-TokenBufferFilterPeakRate=
-TokenBufferFilterLatencySec=
-StochasticFairnessQueueingPerturbPeriodSec=
-FairQueuingControlledDelayPacketLimit=
-FairQueuingControlledDelayMemoryLimit=
-FairQueuingControlledDelayFlows=
-FairQueuingControlledDelayQuantum=
-FairQueuingControlledDelayTargetSec=
-FairQueuingControlledDelayIntervalSec=
-FairQueuingControlledDelayCEThresholdSec=
-FairQueuingControlledDelayECN=
-FairQueueTrafficPolicingPacketLimit=
-FairQueueTrafficPolicingFlowLimit=
-FairQueueTrafficPolicingQuantum=
-FairQueueTrafficPolicingInitialQuantum=
-FairQueueTrafficPolicingMaximumRate=
-FairQueueTrafficPolicingBuckets=
-FairQueueTrafficPolicingOrphanMask=
-FairQueueTrafficPolicingPacing=
-FairQueueTrafficPolicingCEThresholdSec=
-ControlledDelayPacketLimit=
-ControlledDelayTargetSec=
-ControlledDelayIntervalSec=
-ControlledDelayCEThresholdSec=
-ControlledDelayECN=
+[TrivialLinkEqualizer]
+Parent=
+Handle=
+Id=
+[HierarchyTokenBucket]
+Parent=
+Handle=
+DefaultClass=
+[HierarchyTokenBucketClass]
+Parent=
+ClassId=
+Priority=
+Rate=
+CeilRate=
+[PFIFO]
+Parent=
+Handle=
+PacketLimit=
+[GenericRandomEarlyDetection]
+Parent=
+Handle=
+VirtualQueues=
+DefaultVirtualQueue=
+GenericRIO=
+[StochasticFairBlue]
+Parent=
+Handle=
+PacketLimit=
Scope=
SendHostname=
Source=
+SuppressPrefixLength=
TCP6SegmentationOffload=
TCPSegmentationOffload=
TOS=
test-execute/exec-privatenetwork-yes.service
test-execute/exec-privatetmp-no.service
test-execute/exec-privatetmp-yes.service
+ test-execute/exec-privatetmp-disabled-by-prefix.service
test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service
test-execute/exec-protectkernellogs-yes-capabilities.service
test-execute/exec-protectkernellogs-no-capabilities.service
test-execute/exec-specifier@.service
test-execute/exec-standardinput-data.service
test-execute/exec-standardinput-file.service
+ test-execute/exec-standardinput-file-cat.service
test-execute/exec-standardoutput-file.service
test-execute/exec-standardoutput-append.service
test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
[Output]
Format=raw_btrfs
Bootable=yes
-KernelCommandLine=printk.devkmsg=on
OutputDirectory=../mkosi.output
Output=networkd-test.raw
-#!/bin/bash
+#!/usr/bin/env bash
fd=0
OPTIND=1
-#!/bin/bash -e
+#!/usr/bin/env bash
+set -e
BUILD_DIR="$($(dirname "$0")/../tools/find-build-dir.sh)"
if [ $# -gt 0 ]; then
-#!/bin/bash -e
+#!/usr/bin/env bash
+set -e
out="$1"
systemd_efi="$2"
import os
import tempfile
import subprocess
+import sys
from enum import Enum
unit_file_content = '''
[Service]
Type=oneshot
- ExecStart=/bin/sleep 2
+ ExecStart=/bin/sleep 3
ExecStart=/bin/bash -c "echo foo >> {0}"
'''.format(self.output_file)
self.unit_files[UnitFileChange.NO_CHANGE] = unit_file_content
[Service]
Type=oneshot
ExecStart=/bin/bash -c "echo foo >> {0}"
- ExecStart=/bin/sleep 2
+ ExecStart=/bin/sleep 3
'''.format(self.output_file)
self.unit_files[UnitFileChange.LINES_SWAPPED] = unit_file_content
[Service]
Type=oneshot
ExecStart=/bin/bash -c "echo bar >> {0}"
- ExecStart=/bin/sleep 2
+ ExecStart=/bin/sleep 3
ExecStart=/bin/bash -c "echo foo >> {0}"
'''.format(self.output_file)
self.unit_files[UnitFileChange.COMMAND_ADDED_BEFORE] = unit_file_content
unit_file_content = '''
[Service]
Type=oneshot
- ExecStart=/bin/sleep 2
+ ExecStart=/bin/sleep 3
ExecStart=/bin/bash -c "echo foo >> {0}"
ExecStart=/bin/bash -c "echo bar >> {0}"
'''.format(self.output_file)
[Service]
Type=oneshot
ExecStart=/bin/bash -c "echo baz >> {0}"
- ExecStart=/bin/sleep 2
+ ExecStart=/bin/sleep 3
ExecStart=/bin/bash -c "echo foo >> {0}"
ExecStart=/bin/bash -c "echo bar >> {0}"
'''.format(self.output_file)
def setup_unit(self):
self.write_unit_file(UnitFileChange.NO_CHANGE)
subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', self.unit])
+ time.sleep(1)
def test_no_change(self):
expected_output = 'foo\n'
self.reload()
if __name__ == '__main__':
- unittest.main()
+ unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3))
ExecStart=test -f /tmp/a
ExecStart=!test -f /tmp/b
ExecStart=!!test -f /tmp/c
-ExecStart=+test -f /tmp/c
ExecStartPost=rm /tmp/a /tmp/b /tmp/c
PrivateTmp=true
Description=Test for CapabilityBoundingSet
[Service]
-ExecStart=/bin/sh -x -c '! capsh --print | grep "^Bounding set .*cap_chown"'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"'
Type=oneshot
CapabilityBoundingSet=~CAP_CHOWN
[Service]
PrivateDevices=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_mknod'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
Type=oneshot
[Service]
PrivateDevices=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_sys_rawio'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
Type=oneshot
[Service]
PrivateDevices=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_mknod'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
Type=oneshot
[Service]
PrivateDevices=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_sys_rawio'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
Type=oneshot
--- /dev/null
+[Unit]
+Description=Test for PrivateTmp=yes with prefix
+
+[Service]
+ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
+ExecStart=+/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp'
+Type=oneshot
+PrivateTmp=yes
[Service]
ProtectKernelLogs=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_syslog'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
Type=oneshot
[Service]
ProtectKernelLogs=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_syslog'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
Type=oneshot
[Service]
ProtectKernelModules=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_sys_module'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
Type=oneshot
[Service]
ProtectKernelModules=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_sys_module'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
Type=oneshot
--- /dev/null
+[Unit]
+Description=Test for StandardInput=file:
+
+[Service]
+ExecStart=cat
+Type=oneshot
+StandardInput=file:/etc/os-release
+# We leave StandardOutput= unset here, to verify https://github.com/systemd/systemd/issues/14560 works
+# The "cat" tool is going to write to stdout, which fails if we dup() stdin to stdout
-#!/bin/bash
+#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
[ "$SYSTEMD_NSPAWN" ] || SYSTEMD_NSPAWN=$(which -a $BUILD_DIR/systemd-nspawn systemd-nspawn 2>/dev/null | grep '^/' -m1)
[ "$JOURNALCTL" ] || JOURNALCTL=$(which -a $BUILD_DIR/journalctl journalctl 2>/dev/null | grep '^/' -m1)
-BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
+BASICTOOLS="test env sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
DEBUGTOOLS="df free ls stty ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find vi mv"
STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
init=$PATH_TO_INIT \
console=$CONSOLE \
selinux=0 \
-printk.devkmsg=on \
$_cgroup_args \
$KERNEL_APPEND \
"
local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind
ddebug "Create $_valgrind_wrapper"
cat >$_valgrind_wrapper <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
mount -t proc proc /proc
exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
esac
cat >$_asan_wrapper <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
set -x
# 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"
+unset_ld_preload() {
+ local _dropin_dir="/etc/systemd/system/\$1.service.d"
+ mkdir -p "\$_dropin_dir"
+ printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$_dropin_dir/unset_ld_preload.conf"
+}
+
+unset_ld_preload systemd-remount-fs
+unset_ld_preload testsuite
export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
exec $ROOTLIBDIR/systemd "\$@"
local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
ddebug "Create $_strace_wrapper"
cat >$_strace_wrapper <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
exec strace -D -o /strace.out $ROOTLIBDIR/systemd "\$@"
EOF
create_rc_local() {
mkdir -p $initdir/etc/rc.d
cat >$initdir/etc/rc.d/rc.local <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
exit 0
EOF
chmod 0755 $initdir/etc/rc.d/rc.local
inst /etc/shells
inst /etc/nsswitch.conf
inst /etc/pam.conf || :
- inst /etc/securetty || :
inst /etc/os-release
inst /etc/localtime
# we want an empty environment
--- /dev/null
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+User=100-200
--- /dev/null
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[CAKE]
+Parent=root
+Handle=3a
+Overhead=128
+Bandwidth=500M
--- /dev/null
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[QDisc]
+Parent=clsact
+
+[HierarchyTokenBucket]
+Parent=root
+Handle=0002
+DefaultClass=30
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0030
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[NetworkEmulator]
+Parent=2:30
+Handle=0030
+DelaySec=50ms
+DelayJitterSec=10ms
+LossRate=20%
+PacketLimit=100
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0031
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[TrivialLinkEqualizer]
+Parent=2:31
+Handle=0031
+Id=1
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0032
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[FairQueueing]
+Parent=2:32
+Handle=0032
+PacketLimit=1000
+FlowLimit=200
+Quantum=1500
+InitialQuantum=13000
+MaximumRate=1M
+Buckets=512
+OrphanMask=511
+Pacing=yes
+CEThresholdSec=100ms
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0033
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[ControlledDelay]
+Parent=2:33
+Handle=0033
+PacketLimit=2000
+TargetSec=10ms
+IntervalSec=50ms
+ECN=yes
+CEThresholdSec=100ms
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0034
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[FairQueueingControlledDelay]
+Parent=2:34
+Handle=0034
+PacketLimit=20480
+MemoryLimit=64M
+Flows=2048
+TargetSec=10ms
+IntervalSec=200ms
+Quantum=1400
+ECN=yes
+CEThresholdSec=100ms
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0035
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[TokenBucketFilter]
+Parent=2:35
+Handle=0035
+Rate=1G
+Burst=5K
+LatencySec=70msec
+PeakRate=100G
+MTUBytes=1M
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0036
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[StochasticFairnessQueueing]
+Parent=2:36
+Handle=0036
+PerturbPeriodSec=5sec
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0037
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFO]
+Parent=2:37
+Handle=0037
+PacketLimit=100000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0038
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[GenericRandomEarlyDetection]
+Parent=2:38
+Handle=0038
+VirtualQueues=12
+DefaultVirtualQueue=10
+GenericRIO=yes
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0039
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[StochasticFairBlue]
+Parent=2:39
+Handle=0039
+PacketLimit=200000
+++ /dev/null
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[TrafficControlQueueingDiscipline]
-Parent=root
-FairQueueTrafficPolicingPacketLimit=1000
-FairQueueTrafficPolicingFlowLimit=200
-FairQueueTrafficPolicingQuantum=1500
-FairQueueTrafficPolicingInitialQuantum=13000
-FairQueueTrafficPolicingMaximumRate=1M
-FairQueueTrafficPolicingBuckets=512
-FairQueueTrafficPolicingOrphanMask=511
-FairQueueTrafficPolicingPacing=yes
-FairQueueTrafficPolicingCEThresholdSec=100ms
-
-[TrafficControlQueueingDiscipline]
-Parent=clsact
-ControlledDelayPacketLimit=2000
-ControlledDelayTargetSec=10ms
-ControlledDelayIntervalSec=50ms
-ControlledDelayECN=yes
-ControlledDelayCEThresholdSec=100ms
--- /dev/null
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.4/16
+
+[TrafficControlQueueingDiscipline]
+Parent=root
+NetworkEmulatorDelaySec=50ms
+NetworkEmulatorDelayJitterSec=10ms
+NetworkEmulatorLossRate=20%
+NetworkEmulatorPacketLimit=100
+
+[TrafficControlQueueingDiscipline]
+Parent=ingress
+++ /dev/null
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[TrafficControlQueueingDiscipline]
-Parent=root
-NetworkEmulatorDelaySec=50ms
-NetworkEmulatorDelayJitterSec=10ms
-NetworkEmulatorLossRate=20%
-NetworkEmulatorPacketLimit=100
-
-[TrafficControlQueueingDiscipline]
-Parent=ingress
-FairQueuingControlledDelayPacketLimit=20480
-FairQueuingControlledDelayMemoryLimit=64M
-FairQueuingControlledDelayFlows=2048
-FairQueuingControlledDelayTargetSec=10ms
-FairQueuingControlledDelayIntervalSec=200ms
-FairQueuingControlledDelayQuantum=1400
-FairQueuingControlledDelayECN=yes
-FairQueuingControlledDelayCEThresholdSec=100ms
+++ /dev/null
-[Match]
-Name=test1
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.4/16
-
-[TrafficControlQueueingDiscipline]
-Parent=root
-TokenBufferFilterRate=1G
-TokenBufferFilterBurst=5K
-TokenBufferFilterLatencySec=70msec
-TokenBufferFilterPeakRate=100G
-TokenBufferFilterMTUBytes=1M
-
-[TrafficControlQueueingDiscipline]
-Parent=clsact
-StochasticFairnessQueueingPerturbPeriodSec=5sec
--- /dev/null
+[Match]
+Name=dummy98
+
+[Network]
+VRF=vrf99
+Address=192.168.100.2/24
+Gateway=192.168.100.1
IPv6AcceptRA=no
[Route]
-Gateway=dhcp
+Gateway=_dhcp
Destination=10.0.0.0/8
DHCP=ipv6
[Route]
-Gateway=dhcp
+Gateway=_dhcp
Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCPv4]
+UseRoutes=no
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=prefixstable:2002:da8:1::
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=static:::1a:2b:3c:4d
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=::1a:2b:3c:4d
+IPv6Token=::fa:de:ca:fe
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=::1a:2b:3c:4d
[Network]
IPv6PrefixDelegation=yes
+[IPv6PrefixDelegation]
+DNS=_link_local 2002:da8:1:0::1
+DNSLifetimeSec=1min
+
[IPv6Prefix]
Prefix=2002:da8:1:0::/64
PreferredLifetimeSec=1000s
[Network]
DHCP=no
IPv6PrefixDelegation=yes
-Address=2001:db8:0:1::1/64
[IPv6Prefix]
-Prefix=2001:db8:0:1::4/64
+Prefix=2001:db8:0:1::/64
+
+[IPv6Prefix]
+Prefix=2001:db8:0:2::/64
+Assign=yes
[IPv6RoutePrefix]
Route=2001:db0:fff::/64
networkd_bin=shutil.which('systemd-networkd', path=which_paths)
resolved_bin=shutil.which('systemd-resolved', path=which_paths)
+udevd_bin=shutil.which('systemd-udevd', path=which_paths)
wait_online_bin=shutil.which('systemd-networkd-wait-online', path=which_paths)
networkctl_bin=shutil.which('networkctl', path=which_paths)
resolvectl_bin=shutil.which('resolvectl', path=which_paths)
return f
+def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
+ def f(func):
+ support = False
+ rc = call('ip rule add from 192.168.100.19 table 7 uidrange 200-300', stderr=subprocess.DEVNULL)
+ if rc == 0:
+ ret = run('ip rule list from 192.168.100.19 table 7', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ if ret.returncode == 0 and 'uidrange 200-300' in ret.stdout.rstrip():
+ support = True
+ call('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
+
+ if support:
+ return func
+ else:
+ return unittest.expectedFailure(func)
+
+ return f
+
def expectedFailureIfLinkFileFieldIsNotSet():
def f(func):
support = False
def f(func):
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
rc = call('ip link prop add dev dummy98 altname hogehogehogehogehoge', stderr=subprocess.DEVNULL)
+ call('ip link del dummy98', stderr=subprocess.DEVNULL)
+ if rc == 0:
+ return func
+ else:
+ return unittest.expectedFailure(func)
+
+ return f
+
+def expectedFailureIfCAKEIsNotAvailable():
+ def f(func):
+ call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
+ rc = call('tc qdisc add dev dummy98 parent root cake', stderr=subprocess.DEVNULL)
+ call('ip link del dummy98', stderr=subprocess.DEVNULL)
if rc == 0:
return func
else:
shutil.rmtree(networkd_ci_path)
copytree(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_path)
- for u in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service', 'firewalld.service']:
+ for u in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service',
+ 'systemd-udevd-kernel.socket', 'systemd-udevd-control.socket', 'systemd-udevd.service',
+ 'firewalld.service']:
if call(f'systemctl is-active --quiet {u}') == 0:
check_output(f'systemctl stop {u}')
running_units.append(u)
with open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode='w') as f:
f.write('\n'.join(drop_in))
+ drop_in = [
+ '[Service]',
+ 'ExecStart=',
+ 'ExecStart=!!' + udevd_bin,
+ ]
+
+ os.makedirs('/run/systemd/system/systemd-udevd.service.d', exist_ok=True)
+ with open('/run/systemd/system/systemd-udevd.service.d/00-override.conf', mode='w') as f:
+ f.write('\n'.join(drop_in))
+
check_output('systemctl daemon-reload')
print(check_output('systemctl cat systemd-networkd.service'))
print(check_output('systemctl cat systemd-resolved.service'))
+ print(check_output('systemctl cat systemd-udevd.service'))
check_output('systemctl restart systemd-resolved')
+ check_output('systemctl restart systemd-udevd')
def tearDownModule():
global running_units
shutil.rmtree('/run/systemd/system/systemd-networkd.service.d')
shutil.rmtree('/run/systemd/system/systemd-resolved.service.d')
+ shutil.rmtree('/run/systemd/system/systemd-udevd.service.d')
check_output('systemctl daemon-reload')
+ check_output('systemctl restart systemd-udevd.service')
for u in running_units:
check_output(f'systemctl start {u}')
'ip6gretun97',
'test1',
'veth99',
+ 'vrf99',
]
units = [
'25-bond-active-backup-slave.netdev',
'25-fibrule-invert.network',
'25-fibrule-port-range.network',
+ '25-fibrule-uidrange.network',
'25-gre-tunnel-remote-any.netdev',
'25-ip6gre-tunnel-remote-any.netdev',
'25-ipv6-address-label-section.network',
'25-neighbor-ip-dummy.network',
'25-neighbor-ip.network',
'25-nexthop.network',
- '25-qdisc-fq-codel.network',
- '25-qdisc-netem-and-fqcodel.network',
- '25-qdisc-tbf-and-sfq.network',
+ '25-qdisc-cake.network',
+ '25-qdisc-clsact-and-htb.network',
+ '25-qdisc-ingress-netem-compat.network',
'25-route-ipv6-src.network',
'25-route-static.network',
+ '25-route-vrf.network',
'25-gateway-static.network',
'25-gateway-next-static.network',
'25-sysctl-disable-ipv6.network',
'25-sysctl.network',
'25-veth-peer.network',
'25-veth.netdev',
+ '25-vrf.netdev',
'26-link-local-addressing-ipv6.network',
'configure-without-carrier.network',
'routing-policy-rule-dummy98.network',
self.assertRegex(output, 'tcp')
self.assertRegex(output, 'lookup 7')
+ @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
+ def test_routing_policy_rule_uidrange(self):
+ copy_unit_to_networkd_unit_path('25-fibrule-uidrange.network', '11-dummy.netdev')
+ start_networkd()
+ self.wait_online(['test1:degraded'])
+
+ output = check_output('ip rule')
+ print(output)
+ self.assertRegex(output, '111')
+ self.assertRegex(output, 'from 192.168.100.18')
+ self.assertRegex(output, 'lookup 7')
+ self.assertRegex(output, 'uidrange 100-200')
+
def test_route_static(self):
copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev')
start_networkd()
self.assertRegex(output, 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98')
self.assertRegex(output, 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98')
+ @expectedFailureIfModuleIsNotAvailable('vrf')
+ def test_route_vrf(self):
+ copy_unit_to_networkd_unit_path('25-route-vrf.network', '12-dummy.netdev',
+ '25-vrf.netdev', '25-vrf.network')
+ start_networkd()
+ self.wait_online(['dummy98:routable', 'vrf99:carrier'])
+
+ output = check_output('ip route show vrf vrf99')
+ print(output)
+ self.assertRegex(output, 'default via 192.168.100.1')
+
+ output = check_output('ip route show')
+ print(output)
+ self.assertNotRegex(output, 'default via 192.168.100.1')
+
def test_gateway_reconfigure(self):
copy_unit_to_networkd_unit_path('25-gateway-static.network', '12-dummy.netdev')
start_networkd()
self.assertRegex(output, '192.168.5.1')
def test_qdisc(self):
- copy_unit_to_networkd_unit_path('25-qdisc-netem-and-fqcodel.network', '12-dummy.netdev',
- '25-qdisc-tbf-and-sfq.network', '11-dummy.netdev')
+ copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
+ '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
+ check_output('modprobe sch_teql max_equalizers=2')
start_networkd()
self.wait_online(['dummy98:routable', 'test1:routable'])
- output = check_output('tc qdisc show dev dummy98')
+ output = check_output('tc qdisc show dev test1')
print(output)
self.assertRegex(output, 'qdisc netem')
self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
+ self.assertRegex(output, 'qdisc ingress')
+
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc clsact')
+
+ self.assertRegex(output, 'qdisc htb 2: root')
+ self.assertRegex(output, r'default (0x30|30)')
+
+ self.assertRegex(output, 'qdisc netem 30: parent 2:30')
+ self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
self.assertRegex(output, 'qdisc fq_codel')
self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn')
- output = check_output('tc qdisc show dev test1')
- print(output)
- self.assertRegex(output, 'qdisc tbf')
+
+ self.assertRegex(output, 'qdisc teql1 31: parent 2:31')
+
+ self.assertRegex(output, 'qdisc fq 32: parent 2:32')
+ self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
+ self.assertRegex(output, 'quantum 1500')
+ self.assertRegex(output, 'initial_quantum 13000')
+ self.assertRegex(output, 'maxrate 1Mbit')
+
+ self.assertRegex(output, 'qdisc codel 33: parent 2:33')
+ self.assertRegex(output, 'limit 2000p target 10.0ms ce_threshold 100.0ms interval 50.0ms ecn')
+
+ self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34')
+ self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn')
+
+ self.assertRegex(output, 'qdisc tbf 35: parent 2:35')
self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70.0ms')
- self.assertRegex(output, 'qdisc sfq')
+
+ self.assertRegex(output, 'qdisc sfq 36: parent 2:36')
self.assertRegex(output, 'perturb 5sec')
- def test_qdisc2(self):
- copy_unit_to_networkd_unit_path('25-qdisc-fq-codel.network', '12-dummy.netdev')
- start_networkd()
+ self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
+ self.assertRegex(output, 'limit 100000p')
+
+ self.assertRegex(output, 'qdisc gred 38: parent 2:38')
+ self.assertRegex(output, 'vqs 12 default 10 grio')
+
+ self.assertRegex(output, 'qdisc sfb 39: parent 2:39')
+ self.assertRegex(output, 'limit 200000')
+
+ output = check_output('tc class show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'class htb 2:30 root leaf 30:')
+ self.assertRegex(output, 'class htb 2:31 root leaf 31:')
+ self.assertRegex(output, 'class htb 2:32 root leaf 32:')
+ self.assertRegex(output, 'class htb 2:33 root leaf 33:')
+ self.assertRegex(output, 'class htb 2:34 root leaf 34:')
+ self.assertRegex(output, 'class htb 2:35 root leaf 35:')
+ self.assertRegex(output, 'class htb 2:36 root leaf 36:')
+ self.assertRegex(output, 'class htb 2:37 root leaf 37:')
+ self.assertRegex(output, 'class htb 2:38 root leaf 38:')
+ self.assertRegex(output, 'class htb 2:39 root leaf 39:')
+ self.assertRegex(output, 'prio 1 rate 1Mbit ceil 500Kbit')
+ @expectedFailureIfCAKEIsNotAvailable()
+ def test_qdisc_cake(self):
+ copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev')
+ start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
- self.assertRegex(output, 'qdisc fq')
- self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511 quantum 1500 initial_quantum 13000 maxrate 1Mbit')
- self.assertRegex(output, 'qdisc codel')
- self.assertRegex(output, 'limit 2000p target 10.0ms ce_threshold 100.0ms interval 50.0ms ecn')
+ self.assertRegex(output, 'qdisc cake 3a: root')
+ self.assertRegex(output, 'bandwidth 500Mbit')
+ self.assertRegex(output, 'overhead 128')
class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [
units = [
'25-veth.netdev',
'ipv6-prefix.network',
- 'ipv6-prefix-veth.network']
+ 'ipv6-prefix-veth.network',
+ 'ipv6-prefix-veth-token-static.network',
+ 'ipv6-prefix-veth-token-static-explicit.network',
+ 'ipv6-prefix-veth-token-static-multiple.network',
+ 'ipv6-prefix-veth-token-prefixstable.network']
def setUp(self):
remove_links(self.links)
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+ output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, 'fe80::')
+ self.assertRegex(output, '2002:da8:1::1')
+
+ output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, '2002:da8:1:0')
+
+ def test_ipv6_token_static(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static.network')
+ start_networkd()
+ self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
+ output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
+
+ def test_ipv6_token_static_explicit(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static-explicit.network')
+ start_networkd()
+ self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
+ output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
+
+ def test_ipv6_token_static_multiple(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static-multiple.network')
+ start_networkd()
+ self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
+ output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
+ self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
+
+ def test_ipv6_token_prefixstable(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable.network')
+ start_networkd()
+ self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
print(output)
self.assertRegex(output, '2002:da8:1:0')
'dhcp-client-ipv4-dhcp-settings.network',
'dhcp-client-ipv4-only-ipv6-disabled.network',
'dhcp-client-ipv4-only.network',
+ 'dhcp-client-ipv4-use-routes-no.network',
'dhcp-client-ipv6-only.network',
'dhcp-client-ipv6-rapid-commit.network',
'dhcp-client-keep-configuration-dhcp-on-stop.network',
self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
self.assertRegex(output, r'192.168.5.8 proto dhcp scope link src 192.168.5.181 metric 1024')
+ def test_dhcp_client_ipv4_use_routes_no(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-use-routes-no.network')
+
+ start_networkd()
+ self.wait_online(['veth-peer:carrier'])
+ start_dnsmasq(additional_options='--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', lease_time='2m')
+ self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ output = check_output('ip route show dev veth99')
+ print(output)
+ self.assertNotRegex(output, r'192.168.5.5')
+ self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024')
+ self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024')
+
def test_dhcp_client_ipv4_ipv6(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network',
'dhcp-client-ipv4-only.network')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:routable'])
- output = check_output('ip', '-6', 'route', 'show', 'dev', 'veth-peer')
+ output = check_output('ip -6 route show dev veth-peer')
print(output)
self.assertRegex(output, '2001:db8:0:1::/64 proto ra')
+ output = check_output('ip addr show dev veth99')
+ print(output)
+ self.assertNotRegex(output, '2001:db8:0:1')
+ self.assertRegex(output, '2001:db8:0:2')
+
class NetworkdMTUTests(unittest.TestCase, Utilities):
links = ['dummy98']
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
+ parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin')
parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
ns, args = parser.parse_known_args(namespace=unittest)
if ns.build_dir:
- if ns.networkd_bin or ns.resolved_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
+ if ns.networkd_bin or ns.resolved_bin or ns.udevd_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
+ udevd_bin = os.path.join(ns.build_dir, 'systemd-udevd')
wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
networkd_bin = ns.networkd_bin
if ns.resolved_bin:
resolved_bin = ns.resolved_bin
+ if ns.udevd_bin:
+ udevd_bin = ns.udevd_bin
if ns.wait_online_bin:
wait_online_bin = ns.wait_online_bin
if ns.networkctl_bin:
m4_ifdef(`HAVE_ACL',`m4_dnl
m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x
-a+ /run/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
+a+ /run/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x
+a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x
a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--,group:wheel:r--
'',``
-a+ /run/log/journal/%m - - - - d:group:adm:r-x
-a+ /run/log/journal/%m - - - - group:adm:r-x
+a+ /run/log/journal - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x
+a+ /run/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x
a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--
'')',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /run/log/journal/%m - - - - d:group:wheel:r-x
-a+ /run/log/journal/%m - - - - group:wheel:r-x
+a+ /run/log/journal - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x
+a+ /run/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x
a+ /run/log/journal/%m/*.journal* - - - - group:wheel:r--
'')')')m4_dnl
m4_ifdef(`HAVE_ACL',`m4_dnl
m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x
-a+ /var/log/journal - - - - group::r-x,group:adm:r-x,group:wheel:r-x
-a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x
-a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
+a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x
+a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x
a+ /var/log/journal/%m/system.journal - - - - group:adm:r--,group:wheel:r--
'', ``
-a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x
-a+ /var/log/journal - - - - group::r-x,group:adm:r-x
-a+ /var/log/journal/%m - - - - d:group:adm:r-x
-a+ /var/log/journal/%m - - - - group:adm:r-x
+a+ /var/log/journal - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x
+a+ /var/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x
a+ /var/log/journal/%m/system.journal - - - - group:adm:r--
'')',`m4_dnl
m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /var/log/journal - - - - d:group::r-x,d:group:wheel:r-x
-a+ /var/log/journal - - - - group::r-x,group:wheel:r-x
-a+ /var/log/journal/%m - - - - d:group:wheel:r-x
-a+ /var/log/journal/%m - - - - group:wheel:r-x
+a+ /var/log/journal - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x
+a+ /var/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x
a+ /var/log/journal/%m/system.journal - - - - group:wheel:r--
'')')')m4_dnl
-#!/bin/bash
+#!/usr/bin/env bash
set -e
which perl &>/dev/null || exit 77
'8087:0024',
# Genesys Logic (Internal Hub) (rambi)
'8087:8000',
+ # Microchip (Composite HID + CDC) (kefka)
+ '04d8:0b28',
]
# Webcams
'04ca:3016',
# LiteOn (scarlet)
'04ca:301a',
+ # Realtek (blooglet)
+ '0bda:b00c',
# Atheros (stumpy, stout)
'0cf3:3004',
# Atheros (AR3011) (mario, alex, zgb)
'8086:591c',
# iwlwifi (atlas)
'8086:2526',
+ # i915 (kefka)
+ '8086:22b1',
+ # proc_thermal (kefka)
+ '8086:22dc',
+ # xchi_hdc (kefka)
+ '8086:22b5',
+ # snd_hda (kefka)
+ '8086:2284',
+ # pcieport (kefka)
+ '8086:22c8',
+ '8086:22cc',
+ # lpc_ich (kefka)
+ '8086:229c',
+ # iosf_mbi_pci (kefka)
+ '8086:2280',
]
# Samsung
'2646:5008',
]
-################################################################################
+# Do not edit below this line. #################################################
UDEV_RULE = """\
ACTION!="add", GOTO="autosuspend_end"
-#!/bin/bash
+#!/usr/bin/env bash
# The official unmodified version of the script can be found at
# https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh
-#!/bin/bash
+#!/usr/bin/env bash
set -eu
set -o pipefail
-#!/bin/bash
+#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1+
set -ex
fuzzflag="llvm-fuzz=true"
fi
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older python 3.5
-# See: https://github.com/mesonbuild/meson/issues/6427
-pip3 install meson==0.52.1
-
meson $build -D$fuzzflag -Db_lundef=false
ninja -v -C $build fuzzers
ADDITIONAL_DEPS=(python3-libevdev
python3-pyparsing
clang
- perl)
+ perl
+ libpwquality-dev
+ libfdisk-dev
+ libp11-kit-dev
+ libssl-dev)
function info() {
echo -e "\033[33;1m$1\033[0m"
libubsan
clang
llvm
- perl)
+ perl
+ libfdisk-devel
+ libpwquality-devel
+ openssl-devel
+ p11-kit-devel)
function info() {
echo -e "\033[33;1m$1\033[0m"
sudo apt-get update -y
sudo apt-get build-dep systemd -y
sudo apt-get install -y ninja-build python3-pip python3-setuptools quota
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older python 3.5
-# See: https://github.com/mesonbuild/meson/issues/6427
-pip3 install meson==0.52.1
+# The following should be dropped when debian packaging has been updated to include them
+sudo apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
+pip3 install meson
cd $REPO_ROOT
export PATH="$HOME/.local/bin/:$PATH"
tools/oss-fuzz.sh
-timeout --preserve-status 5 ./out/fuzz-unit-file
+./out/fuzz-unit-file -max_total_time=5
git clean -dxff
wget https://app.fuzzbuzz.io/releases/cli/latest/linux/fuzzbuzz
sudo apt-get update -y
sudo apt-get build-dep systemd -y
sudo apt-get install -y ninja-build python3-pip python3-setuptools
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older python 3.5
-# See: https://github.com/mesonbuild/meson/issues/6427
-pip3 install meson==0.52.1
+# The following should be dropped when debian packaging has been updated to include them
+sudo apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
+pip3 install meson
cd $REPO_ROOT
export PATH="$HOME/.local/bin/:$PATH"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=Block Device Preparation for %f
+Documentation=man:systemd.special(7)
+StopWhenUnneeded=yes
[Service]
Type=oneshot
-ExecStart=@rootbindir@/systemctl --no-block isolate initrd-switch-root.target
+ExecStart=systemctl --no-block isolate initrd-switch-root.target
[Unit]
Description=Initrd File Systems
Documentation=man:systemd.special(7)
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
ConditionPathExists=/etc/initrd-release
After=initrd-parse-etc.service
DefaultDependencies=no
[Service]
Type=oneshot
-ExecStartPre=-@rootbindir@/systemctl daemon-reload
+ExecStartPre=-systemctl daemon-reload
# we have to retrigger initrd-fs.target after daemon-reload
-ExecStart=-@rootbindir@/systemctl --no-block start initrd-fs.target
-ExecStart=@rootbindir@/systemctl --no-block start initrd-cleanup.service
+ExecStart=-systemctl --no-block start initrd-fs.target
+ExecStart=systemctl --no-block start initrd-cleanup.service
Description=Initrd Root Device
Documentation=man:systemd.special(7)
ConditionPathExists=/etc/initrd-release
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
DefaultDependencies=no
Conflicts=shutdown.target
Description=Initrd Root File System
Documentation=man:systemd.special(7)
ConditionPathExists=/etc/initrd-release
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
DefaultDependencies=no
Conflicts=shutdown.target
[Service]
Type=oneshot
-ExecStart=@rootbindir@/systemctl --no-block switch-root /sysroot
+ExecStart=systemctl --no-block switch-root /sysroot
[Service]
Type=oneshot
-ExecStart=-@rootbindir@/udevadm info --cleanup-db
+ExecStart=-udevadm info --cleanup-db
[Unit]
Description=Initrd Default Target
Documentation=man:systemd.special(7)
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
ConditionPathExists=/etc/initrd-release
Requires=basic.target
Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-parse-etc.service
DefaultDependencies=no
Conflicts=shutdown.target
After=local-fs-pre.target
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
units = [
['basic.target', ''],
+ ['blockdev@.target', ''],
['bluetooth.target', ''],
['boot-complete.target', ''],
['cryptsetup-pre.target', 'HAVE_LIBCRYPTSETUP'],
['hibernate.target', 'ENABLE_HIBERNATE'],
['hybrid-sleep.target', 'ENABLE_HIBERNATE'],
['suspend-then-hibernate.target', 'ENABLE_HIBERNATE'],
+ ['initrd-cleanup.service', ''],
['initrd-fs.target', ''],
+ ['initrd-parse-etc.service', ''],
['initrd-root-device.target', ''],
['initrd-root-fs.target', ''],
+ ['initrd-switch-root.service', ''],
['initrd-switch-root.target', ''],
+ ['initrd-udevadm-cleanup-db.service', ''],
['initrd.target', ''],
['kexec.target', ''],
['ldconfig.service', 'ENABLE_LDCONFIG',
'sysinit.target.wants/'],
['sys-kernel-debug.mount', '',
'sysinit.target.wants/'],
+ ['sys-kernel-tracing.mount', '',
+ 'sysinit.target.wants/'],
['sysinit.target', ''],
['syslog.socket', ''],
['system-systemd\\x2dcryptsetup.slice', 'HAVE_LIBCRYPTSETUP'],
['system-update-cleanup.service', ''],
['systemd-ask-password-console.path', '',
'sysinit.target.wants/'],
+ ['systemd-ask-password-console.service', ''],
['systemd-ask-password-wall.path', '',
'multi-user.target.wants/'],
+ ['systemd-ask-password-wall.service', ''],
+ ['systemd-boot-system-token.service', 'ENABLE_EFI',
+ 'sysinit.target.wants/'],
['systemd-coredump.socket', 'ENABLE_COREDUMP',
'sockets.target.wants/'],
- ['systemd-exit.service', ''],
+ ['systemd-exit.service', ''],
+ ['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
+ 'sysinit.target.wants/'],
+ ['systemd-halt.service', ''],
['systemd-initctl.socket', '',
'sockets.target.wants/'],
+ ['systemd-journal-catalog-update.service', '',
+ 'sysinit.target.wants/'],
+ ['systemd-journal-flush.service', '',
+ 'sysinit.target.wants/'],
['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
['systemd-journal-remote.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
['systemd-journald-audit.socket', '',
'sockets.target.wants/'],
['systemd-journald.socket', '',
'sockets.target.wants/'],
+ ['systemd-kexec.service', ''],
+ ['systemd-machine-id-commit.service', '',
+ 'sysinit.target.wants/'],
+ ['systemd-journald@.socket', ''],
+ ['systemd-journald-varlink@.socket', ''],
['systemd-networkd.socket', 'ENABLE_NETWORKD'],
- ['systemd-poweroff.service', ''],
- ['systemd-reboot.service', ''],
+ ['systemd-poweroff.service', ''],
+ ['systemd-reboot.service', ''],
['systemd-rfkill.socket', 'ENABLE_RFKILL'],
+ ['systemd-sysusers.service', 'ENABLE_SYSUSERS',
+ 'sysinit.target.wants/'],
+ ['systemd-tmpfiles-clean.service', 'ENABLE_TMPFILES'],
['systemd-tmpfiles-clean.timer', 'ENABLE_TMPFILES',
'timers.target.wants/'],
+ ['systemd-tmpfiles-setup-dev.service', 'ENABLE_TMPFILES',
+ 'sysinit.target.wants/'],
+ ['systemd-tmpfiles-setup.service', 'ENABLE_TMPFILES',
+ 'sysinit.target.wants/'],
['systemd-udevd-control.socket', '',
'sockets.target.wants/'],
+ ['systemd-udev-settle.service', ''],
+ ['systemd-udev-trigger.service', '',
+ 'sysinit.target.wants/'],
['systemd-udevd-kernel.socket', '',
'sockets.target.wants/'],
+ ['systemd-userdbd.socket', 'ENABLE_USERDB',
+ 'sockets.target.wants/'],
['time-set.target', ''],
['time-sync.target', ''],
['timers.target', ''],
in_units = [
['debug-shell.service', ''],
['emergency.service', ''],
- ['initrd-cleanup.service', ''],
- ['initrd-parse-etc.service', ''],
- ['initrd-switch-root.service', ''],
- ['initrd-udevadm-cleanup-db.service', ''],
['kmod-static-nodes.service', 'HAVE_KMOD ENABLE_TMPFILES',
'sysinit.target.wants/'],
['quotaon.service', 'ENABLE_QUOTACHECK'],
['rc-local.service', 'HAVE_SYSV_COMPAT'],
['rescue.service', ''],
- ['systemd-ask-password-console.service', ''],
- ['systemd-ask-password-wall.service', ''],
['systemd-backlight@.service', 'ENABLE_BACKLIGHT'],
['systemd-binfmt.service', 'ENABLE_BINFMT',
'sysinit.target.wants/'],
['systemd-bless-boot.service', 'ENABLE_EFI HAVE_BLKID'],
['systemd-boot-check-no-failures.service', ''],
- ['systemd-boot-system-token.service', 'ENABLE_EFI',
- 'sysinit.target.wants/'],
['systemd-coredump@.service', 'ENABLE_COREDUMP'],
['systemd-pstore.service', 'ENABLE_PSTORE'],
- ['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
- 'sysinit.target.wants/'],
['systemd-fsck-root.service', ''],
['systemd-fsck@.service', ''],
- ['systemd-halt.service', ''],
['systemd-hibernate-resume@.service', 'ENABLE_HIBERNATE'],
['systemd-hibernate.service', 'ENABLE_HIBERNATE'],
['systemd-hybrid-sleep.service', 'ENABLE_HIBERNATE'],
['systemd-importd.service', 'ENABLE_IMPORTD',
'dbus-org.freedesktop.import1.service'],
['systemd-initctl.service', ''],
- ['systemd-journal-catalog-update.service', '',
- 'sysinit.target.wants/'],
- ['systemd-journal-flush.service', '',
- 'sysinit.target.wants/'],
['systemd-journal-gatewayd.service', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
['systemd-journal-remote.service', 'ENABLE_REMOTE HAVE_MICROHTTPD'],
['systemd-journal-upload.service', 'ENABLE_REMOTE HAVE_LIBCURL'],
['systemd-journald.service', '',
'sysinit.target.wants/'],
- ['systemd-kexec.service', ''],
+ ['systemd-journald@.service', ''],
['systemd-localed.service', 'ENABLE_LOCALED',
'dbus-org.freedesktop.locale1.service'],
['systemd-logind.service', 'ENABLE_LOGIND',
'multi-user.target.wants/ dbus-org.freedesktop.login1.service'],
- ['systemd-machine-id-commit.service', '',
- 'sysinit.target.wants/'],
['systemd-machined.service', 'ENABLE_MACHINED',
'dbus-org.freedesktop.machine1.service'],
['systemd-modules-load.service', 'HAVE_KMOD',
['systemd-nspawn@.service', ''],
['systemd-portabled.service', 'ENABLE_PORTABLED',
'dbus-org.freedesktop.portable1.service'],
+ ['systemd-userdbd.service', 'ENABLE_USERDB'],
+ ['systemd-homed.service', 'ENABLE_HOMED',
+ 'multi-user.target.wants/ dbus-org.freedesktop.home1.service'],
['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'],
['systemd-random-seed.service', 'ENABLE_RANDOMSEED',
'sysinit.target.wants/'],
['systemd-suspend.service', ''],
['systemd-sysctl.service', '',
'sysinit.target.wants/'],
- ['systemd-sysusers.service', 'ENABLE_SYSUSERS',
- 'sysinit.target.wants/'],
['systemd-timedated.service', 'ENABLE_TIMEDATED',
'dbus-org.freedesktop.timedate1.service'],
['systemd-timesyncd.service', 'ENABLE_TIMESYNCD'],
['systemd-time-wait-sync.service', 'ENABLE_TIMESYNCD'],
- ['systemd-tmpfiles-clean.service', 'ENABLE_TMPFILES'],
- ['systemd-tmpfiles-setup-dev.service', 'ENABLE_TMPFILES',
- 'sysinit.target.wants/'],
- ['systemd-tmpfiles-setup.service', 'ENABLE_TMPFILES',
- 'sysinit.target.wants/'],
- ['systemd-udev-settle.service', ''],
- ['systemd-udev-trigger.service', '',
- 'sysinit.target.wants/'],
['systemd-udevd.service', '',
'sysinit.target.wants/'],
['systemd-update-done.service', '',
'multi-user.target.wants/'],
['systemd-vconsole-setup.service', 'ENABLE_VCONSOLE'],
['systemd-volatile-root.service', ''],
+ ['systemd-repart.service', 'ENABLE_REPART',
+ 'sysinit.target.wants/ initrd-root-fs.target.wants/'],
['user-runtime-dir@.service', ''],
['user@.service', ''],
]
Before=sysinit.target
Documentation=man:modprobe(8)
ConditionCapability=CAP_SYS_MODULE
+ConditionPathExists=!/sys/module/%I
[Service]
Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=Kernel Trace File System
+Documentation=https://www.kernel.org/doc/Documentation/trace/ftrace.txt
+Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems
+DefaultDependencies=no
+ConditionPathExists=/sys/kernel/tracing
+ConditionCapability=CAP_SYS_RAWIO
+Before=sysinit.target
+
+[Mount]
+What=tracefs
+Where=/sys/kernel/tracing
+Type=tracefs
+Options=nosuid,nodev,noexec
ConditionPathExists=!/run/plymouth/pid
[Service]
-ExecStart=@rootbindir@/systemd-tty-ask-password-agent --watch --console
+ExecStart=systemd-tty-ask-password-agent --watch --console
SystemCallArchitectures=native
After=systemd-user-sessions.service
[Service]
-ExecStartPre=-@SYSTEMCTL@ stop systemd-ask-password-console.path systemd-ask-password-console.service systemd-ask-password-plymouth.path systemd-ask-password-plymouth.service
-ExecStart=@rootbindir@/systemd-tty-ask-password-agent --wall
+ExecStartPre=-systemctl stop systemd-ask-password-console.path systemd-ask-password-console.service systemd-ask-password-plymouth.path systemd-ask-password-plymouth.service
+ExecStart=systemd-tty-ask-password-agent --wall
SystemCallArchitectures=native
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@bindir@/bootctl random-seed --graceful
+ExecStart=bootctl random-seed --graceful
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-firstboot --prompt-locale --prompt-timezone --prompt-root-password
+ExecStart=systemd-firstboot --prompt-locale --prompt-timezone --prompt-root-password
StandardOutput=tty
StandardInput=tty
StandardError=tty
[Service]
Type=oneshot
-ExecStart=@SYSTEMCTL@ --force halt
+ExecStart=systemctl --force halt
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=Home Manager
+Documentation=man:systemd-homed.service(8)
+RequiresMountsFor=/home
+
+[Service]
+BusName=org.freedesktop.home1
+CapabilityBoundingSet=CAP_SYS_ADMIN CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_SETGID CAP_SETUID
+DeviceAllow=/dev/loop-control rw
+DeviceAllow=/dev/mapper/control rw
+DeviceAllow=block-* rw
+ExecStart=@rootlibexecdir@/systemd-homed
+IPAddressDeny=any
+KillMode=mixed
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateNetwork=yes
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_ALG
+RestrictNamespaces=mnt
+RestrictRealtime=yes
+StateDirectory=systemd/home
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service @mount
+@SERVICE_WATCHDOG@
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-hwdb update
+ExecStart=systemd-hwdb update
TimeoutSec=90s
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/journalctl --update-catalog
+ExecStart=journalctl --update-catalog
TimeoutSec=90s
RequiresMountsFor=/var/log/journal
[Service]
-ExecStart=@rootbindir@/journalctl --flush
-ExecStop=@rootbindir@/journalctl --smart-relinquish-var
+ExecStart=journalctl --flush
+ExecStop=journalctl --smart-relinquish-var
Type=oneshot
RemainAfterExit=yes
TimeoutSec=90s
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=Journal Varlink Socket for Namespace %i
+Documentation=man:systemd-journald.service(8) man:journald.conf(5)
+StopWhenUnneeded=yes
+
+[Socket]
+Service=systemd-journald@%i.service
+ListenStream=/run/systemd/journal.%i/io.systemd.journal
+SocketMode=0600
Before=sysinit.target
[Service]
-OOMScoreAdjust=-250
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
DeviceAllow=char-* rw
ExecStart=@rootlibexecdir@/systemd-journald
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
+OOMScoreAdjust=-250
Restart=always
RestartSec=0
RestrictAddressFamilies=AF_UNIX AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
+RuntimeDirectory=systemd/journal
+RuntimeDirectoryPreserve=yes
Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket
StandardOutput=null
SystemCallArchitectures=native
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=Journal Service for Namespace %i
+Documentation=man:systemd-journald.service(8) man:journald.conf(5)
+Requires=systemd-journald@%i.socket systemd-journald-varlink@%i.socket
+After=systemd-journald@%i.socket systemd-journald-varlink@%i.socket
+
+[Service]
+CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
+DevicePolicy=closed
+ExecStart=@rootlibexecdir@/systemd-journald %i
+FileDescriptorStoreMax=4224
+Group=systemd-journal
+IPAddressDeny=any
+LockPersonality=yes
+LogsDirectory=journal/%m.%i
+LogsDirectoryMode=02755
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+RestrictAddressFamilies=AF_UNIX AF_NETLINK
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+RuntimeDirectory=systemd/journal.%i
+RuntimeDirectoryPreserve=yes
+Sockets=systemd-journald@%i.socket
+StandardOutput=null
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+Type=notify
+@SERVICE_WATCHDOG@
+
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=Journal Socket for Namespace %i
+Documentation=man:systemd-journald.service(8) man:journald.conf(5)
+StopWhenUnneeded=yes
+
+[Socket]
+Service=systemd-journald@%i.service
+ListenStream=/run/systemd/journal.%i/stdout
+ListenDatagram=/run/systemd/journal.%i/socket
+ListenDatagram=/run/systemd/journal.%i/dev-log
+SocketMode=0666
+PassCredentials=yes
+PassSecurity=yes
+ReceiveBuffer=8M
+SendBuffer=8M
[Service]
Type=oneshot
-ExecStart=@SYSTEMCTL@ --force kexec
+ExecStart=systemctl --force kexec
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-machine-id-setup --commit
+ExecStart=systemd-machine-id-setup --commit
TimeoutSec=30s
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
ProtectHostname=yes
-ProtectKernelLogs=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
RestrictRealtime=yes
SystemCallArchitectures=native
[Unit]
Description=Generate network units from Kernel command line
+Documentation=man:systemd-network-generator.service(8)
DefaultDependencies=no
Before=network-pre.target
[Service]
Type=oneshot
+RemainAfterExit=yes
ExecStart=@rootlibexecdir@/systemd-network-generator
[Install]
ProtectSystem=strict
Restart=on-failure
RestartSec=0
-RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET AF_ALG
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
PartOf=machines.target
Before=machines.target
After=network.target systemd-resolved.service modprobe@tun.service modprobe@loop.service modprobe@dm-mod.service
-RequiresMountsFor=/var/lib/machines
+RequiresMountsFor=/var/lib/machines/%i
[Service]
# Make sure the DeviceAllow= lines below can properly resolve the 'block-loop' expression (and others)
-ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i
+ExecStart=systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i
KillMode=mixed
Type=notify
RestartForceExitStatus=133
Description=Platform Persistent Storage Archival
Documentation=man:systemd-pstore(8)
ConditionDirectoryNotEmpty=/sys/fs/pstore
+ConditionVirtualization=!container
DefaultDependencies=no
Wants=systemd-remount-fs.service
After=systemd-remount-fs.service
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=Repartition Root Disk
+Documentation=man:systemd-repart.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=sysroot.mount
+Before=initrd-root-fs.target shutdown.target
+ConditionVirtualization=!container
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=@rootbindir@/systemd-repart --dry-run=no
+
+# The tool returns 77 if there's no GPT partition table pre-existing
+SuccessExitStatus=77
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-sysusers
+ExecStart=systemd-sysusers
TimeoutSec=90s
[Service]
Type=oneshot
-ExecStart=@rootbindir@/systemd-tmpfiles --clean
+ExecStart=systemd-tmpfiles --clean
SuccessExitStatus=DATAERR
IOSchedulingClass=idle
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot
+ExecStart=systemd-tmpfiles --prefix=/dev --create --boot
SuccessExitStatus=DATAERR CANTCREAT
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
+ExecStart=systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
SuccessExitStatus=DATAERR CANTCREAT
Type=oneshot
TimeoutSec=180
RemainAfterExit=yes
-ExecStart=@rootbindir@/udevadm settle
+ExecStart=udevadm settle
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/udevadm trigger --type=subsystems --action=add
-ExecStart=@rootbindir@/udevadm trigger --type=devices --action=add
+ExecStart=udevadm trigger --type=subsystems --action=add
+ExecStart=udevadm trigger --type=devices --action=add
Restart=always
RestartSec=0
ExecStart=@rootlibexecdir@/systemd-udevd
-ExecReload=@rootbindir@/udevadm control --reload --timeout 0
+ExecReload=udevadm control --reload --timeout 0
KillMode=mixed
TasksMax=infinity
PrivateMounts=yes
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=User Database Manager
+Documentation=man:systemd-userdbd.service(8)
+Requires=systemd-userdbd.socket
+After=systemd-userdbd.socket
+Before=sysinit.target
+DefaultDependencies=no
+
+[Service]
+CapabilityBoundingSet=CAP_DAC_READ_SEARCH
+ExecStart=@rootlibexecdir@/systemd-userdbd
+IPAddressDeny=any
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectSystem=strict
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+Type=notify
+@SERVICE_WATCHDOG@
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# 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.
+
+[Unit]
+Description=User Database Manager Socket
+Documentation=man:systemd-userdbd.service(8)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/userdb/io.systemd.Multiplexer
+Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch
+SocketMode=0666
[Service]
Type=oneshot
+RemainAfterExit=yes
ExecStart=@rootlibexecdir@/systemd-vconsole-setup
'smartcard.target',
'sockets.target',
'sound.target',
- 'timers.target',
'systemd-exit.service',
+ 'systemd-tmpfiles-clean.service',
'systemd-tmpfiles-clean.timer',
+ 'systemd-tmpfiles-setup.service',
+ 'timers.target',
]
foreach file : units
install_data(file,
install_dir : userunitdir)
endforeach
-
-in_units = [
- 'systemd-tmpfiles-clean.service',
- 'systemd-tmpfiles-setup.service',
-]
-
-foreach file : in_units
- gen = configure_file(
- input : file + '.in',
- output : file,
- configuration : substs)
- install_data(gen,
- install_dir : userunitdir)
-endforeach
[Service]
Type=oneshot
-ExecStart=@rootbindir@/systemd-tmpfiles --user --clean
+ExecStart=systemd-tmpfiles --user --clean
SuccessExitStatus=DATAERR
IOSchedulingClass=idle
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-tmpfiles --user --create --remove --boot
+ExecStart=systemd-tmpfiles --user --create --remove --boot
SuccessExitStatus=DATAERR
[Install]
User=%i
PAMName=systemd-user
Type=notify
-ExecStart=-@rootlibexecdir@/systemd --user
+ExecStart=@rootlibexecdir@/systemd --user
Slice=user-%i.slice
KillMode=mixed
Delegate=pids memory