1
0
mirror of https://github.com/systemd/systemd synced 2026-03-14 17:14:49 +01:00

Compare commits

..

40 Commits

Author SHA1 Message Date
Lennart Poettering
37ef2fc9f7
Merge pull request #18863 from keszybz/cmdline-escaping
Escape command lines properly
2021-05-07 17:29:39 +02:00
Zbigniew Jędrzejewski-Szmek
d0f14a6cf4
Merge pull request #19134 from poettering/outbound-special-hostname
introduce a new synthetic hostname "_outbound" that maps to "the" local IP address
2021-05-07 17:15:22 +02:00
Lennart Poettering
2d882d3581 nss-systemd: make llvm work-around for used _cleanup_ explicit 2021-05-07 16:44:03 +02:00
Lennart Poettering
bbfb8c878c userdbd: reverse which path is a socket and which a symlink
userdbd listens on "two" sockets, that are actually the same: one is a
real AF_UNIX socket in the fs, and the other is a symlink to it.

So far, when userdbd was started from the command line it would make one
a symlink and the other a real socket, but when invoked via unit files
they'd be swapped, i.e. the other would be a symlink and the one a real
socket.

Let's bring this in line.

Since the "io.systemd.Multiplexer" is our main interface, let's make it
the one exposed as socket, and then make "io.systemd.NameServiceSwitch"
a symlink to it. Or in other words, let's adjust the C code to match the
unit file.
2021-05-07 16:43:43 +02:00
Lennart Poettering
2708160ccd fileio: optionally, return discovered path of file in search_and_fopen() 2021-05-07 16:43:26 +02:00
Zbigniew Jędrzejewski-Szmek
ac2c088939
Merge pull request #19391 from poettering/dissect-grow
optionally, grow file systems to partition size when mounting them via GPT auto-discovery
2021-05-07 15:04:55 +02:00
Zbigniew Jędrzejewski-Szmek
2f960b3858 core,journald: use quoted commandlines
I think quoting is more useful than not quoting. Without, arguments with
whitespace cannot be split correctly.

Unlike in coredump, "normal" quoting is used in those two cases. This output is
mostly for informational purposes, so the more readable quoting seems apropriate.

dbus GetProcesses:
$ busctl --user call org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/run_2dr4450e1ae73944194bb6593fcfd255fbe_2eservice org.freedesktop.systemd1.Service GetProcesses
a(sus) 2
"/user.slice/user-1000.slice/user@1000.service/app.slice/run-r4450e1ae73944194bb6593fcfd255fbe.service" 131494 "/usr/bin/bash -c \"sleep 100; sleep 20\""
"/user.slice/user-1000.slice/user@1000.service/app.slice/run-r4450e1ae73944194bb6593fcfd255fbe.service" 131496 "sleep 100"
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
5dd55303f4 coredump: use "POSIX quotes" for cmdline
$ coredumpctl info |grep Command
  Command Line: bash -c kill -SEGV $$      (before)
  Command Line: bash -c "kill -SEGV \$\$"  (road not taken, C quotes)
  Command Line: bash -c $'kill -SEGV $$'   (now, POSIX quotes)

Before we wouldn't use any quoting, making it impossible to figure how the
command line was split into arguments. We could use "normal" quotes, but this
has the disadvantage that the commandline *looks* like it could be pasted into
the terminal and executed, but this is not true: various non-printable
characters cannot be expressed in this quoting style. (This is not visible in
this example). Thus, "POSIX quotes" are used, which should allow any command
line to be expressed acurrately and pasted directly into a shell prompt to
reexecute.

I wonder if we should another field in the coredump entry that simply shows the
original cmdline with embedded NULs, in the original /proc/*/cmdline
format. This would allow clients to format the data as they see fit. But I
think we'd want to keep the serialized form anyway, for backwards compatibility.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
4e3fbc133e man: add an example of coredumpctl output
People like examples. Also shows off the new quoted command line.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
510c7a953e test-process-util: add test that prints all cmdlines 2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
99009ed0f4 basic/process-util: add mode where posix shell escape is used for quoting
The new flag is not used, except in tests, so no functional change yet.

This way, the command as shown can be copied-and-pasted into the shell
in more cases. For simple cases, shell quoting with "" is enough. But
$'' is needed when there are control characters in the command.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
07468a16e4 test-process-util: run fewer getpid() tests
Significant time was spent in the getpid() measurement code, which is not very
important.  So let's optimize this a bit by running the slower version less
times, and only running both tests a lesser amount of times unless slow tests
are enabled.

This gives the better accuracy then before in slow mode, and still reasonable
accuracy in fast mode without a noticable slowdown.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
daceaabe1f test-process-util: add more debug logging but hide most of it by default
It makes little sense to always print the stuff that is fully deterministic
and verified by asserts. It can be opted-in with $SYSTEMD_LOG_LEVEL when
developing the tests or debugging a failure.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
61977664e9 basic/process-util: allow quoting of commandlines
Since the new functionality is controlled by an option, this causes no change
in output yet, except tests.

The login in the old branch of !(flags & PROCESS_CMDLINE_QUOTE) is essentially
unmodified. But there is an important difference in behaviour: instead of
unconditionally reading the whole virtual file, we now read only 'max_columns'
bytes. This makes out code to write process lists quite a bit more efficient
when there are processes with long command lines.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
82208a9949 test-utf8: hide most output by default
Unless one is working on the code, there is little reason to write most
of the output. So let's hide it unless requested with SYSTEMD_LOG_LEVEL=debug.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
fc96e5c053 basic/escape: allow truncation mode where "…" is always appended
So far we would append "…" or "..." when the string was wider than the specified
output width. But let's add a mode where the caller knows that the string being
passed is already truncated.

The condition for jumping back in utf8_escape_non_printable_full() was
off-by-one. But we only jumped to that label after doing a check with a
stronger condition, so I think it didn't matter. Now it matters because we'd
output the forced ellipsis one column too early.
2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
b19f211698 basic/escape: flagsify xescape_full() 2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
d12ccbc302 test-fileio: modernization 2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
ad0e687c07 basic/fileio: add a mode to read_full_virtual_file() where not the whole file is read 2021-05-05 13:59:23 +02:00
Zbigniew Jędrzejewski-Szmek
0089ab0800 basic/escape: escape control characters, but not utf-8, in shell quoting
The comment in the code said that so far this didn't matter, but I want to use
shell quoting in more places where this will make a difference. So control
characters are now escaped. Normal utf-8 characters are passed through, it
is 2021 after all and pretty much everyone is (or should be) using utf-8.

While touching the code, change 'char *r' → 'char *buf', in line with modern
style.
2021-05-05 12:12:42 +02:00
Zbigniew Jędrzejewski-Szmek
523e1b14a1 basic/string-util: simplify how str_realloc() is used
All callers ignore failure anyway, so let's do that internally.
2021-05-05 12:12:42 +02:00
Zbigniew Jędrzejewski-Szmek
a01080ceb3 basic/string-util: inline iterator variable declarations 2021-05-05 12:12:42 +02:00
Zbigniew Jędrzejewski-Szmek
6302d38609 basic/string-util: split out helper function 2021-05-05 12:12:42 +02:00
Zbigniew Jędrzejewski-Szmek
566d06ae50 basic/escape: always escape newlines in shell_escape()
shell_escape() is mostly used for mount paths and similar, where we assume
no newlines are present in the string. But if any were ever present, we
should escape them. So let's simplify the code by making this unconditional.
2021-05-05 12:12:42 +02:00
Zbigniew Jędrzejewski-Szmek
1129cd8a71 basic/escape: add mode where empty arguments are still shown as ""
For variables, FOO= is OK. But when quoting positional arguments, we want to
use something with quotes ("", '', or even $'') for an empty string.
2021-05-05 12:12:10 +02:00
Lennart Poettering
cd4d2b1777 update TODO 2021-04-23 17:57:26 +02:00
Lennart Poettering
400c1e8f98 gpt-auto-generator: pull in systemd-growfs@.service if new GPT growfs partition flag is set 2021-04-23 17:57:22 +02:00
Lennart Poettering
66e482cbdb man: document the new grow-file-system flag 2021-04-23 17:57:19 +02:00
Lennart Poettering
1c41c1dc34 repart: add GrowFileSystem= setting to set new GPT partition flag for newly created partitions
And set it to on by default, except if partition is marked read-only.
2021-04-23 17:56:55 +02:00
Lennart Poettering
c65f854af6 tree-wide: enable automatic growing of file systems in images in various tools that deal with OS images
Let's enable this in all tools that intend to write to the OS images.
It's not conditionalized for now, as there already is conditionalization
in the existance or absence of the flag in the GPT partition table (and
it's opt-in), hence it should be OK to just enable this by default for
now if the flag is set.
2021-04-23 17:56:51 +02:00
Lennart Poettering
74a54baeec dissect: enable growfs by default, but make it configurable
This adds a new --growfs=yes|no switch to systemd-dissect, defaulting to
on.
2021-04-23 17:56:34 +02:00
Lennart Poettering
81939d9d5e dissect-image: optionally, grow file systems on mount
The new GPT partition flag the previous commits added is now honoured on
mount.
2021-04-23 17:56:23 +02:00
Lennart Poettering
ee8e497d24 dissect: show growfs flag in systemd-dissect table output 2021-04-23 17:56:19 +02:00
Lennart Poettering
de98f63140 dissect: look for new GPT partition flag marking partitions for growing
systemd-repart can grow partitions dynamically at boot, but it won't
grow the file systems inside them. In /etc/fstab you can request that
via x-systemd.growfs. So far we didn't have a nice scheme for images
with GPT auto-discovery however, and that meant in particular in tools
such as systemd-nspawn the file systems couldn't be grown automatically.

Let's address this: let's define a new GPT partition flag that can be
set for our partition types. If set it indicates that the file system
should be grown to the partition size on mount.

This commit adds the flag and adds code to discover it when dissecting
images. There's no code yet to actually do something about it.
2021-04-23 17:55:45 +02:00
Lennart Poettering
2f166bb79b man: document _outbound 2021-04-23 12:02:24 +02:00
Lennart Poettering
ee18f107d3 resolved: synthesize _outbound magic hostname here too 2021-04-23 12:02:20 +02:00
Lennart Poettering
a1fdbcbe3d nss-myhostname: expose the "outbound" IP addresses under the synthetic "_outbound" hostname
I found myself often looking for a quick way to determine "the local IP
address", and then being lost in the "ip addr" output to find for the
right one to use. This is supposed to help a bit with that. Let's
introduce a new special hostname "_outbound" with semantics similar to
"_gateway" that resolves to addresses that are the closest I could come
up with that maps to "the" local IP address.
2021-04-23 12:02:11 +02:00
Lennart Poettering
54e6f97bc9 local-addresses: add helper for determining local "outbound" IP addresses
This adds a small helper, similar in style to local_addresses() and
local_gateways() that determines the local "outbound" addresses.

What's an "outbound" address supposed to be? The local IP addresses that
are the most likely used for outbound communication. It's determined
by using connect() towards the default gws on an UDP socket, and then
reading the address of the socket this caused it to be bound to.

This is not the "public" or "external" IP address of the local system,
and is not supposed to be. It's just the local IP addresses that are
likely the ones going to be used by the local IP stack for
communication with other hosts.
2021-04-23 12:01:41 +02:00
Zbigniew Jędrzejewski-Szmek
9e53c10a0f Flagsify EscapeStyle and make ESCAPE_BACKSLASH_ONELINE implicit
I want to tweak behaviour further, and that'll be easier when "style"
is converted to a bitfield.

Some callers used ESCAPE_BACKSLASH_ONELINE, and others not. But the
ones that didn't, simply didn't care, because the argument was assumed to
be one-line anyway (e.g. a service name). In environment-generator, this
could make a difference. But I think it's better to escape the newlines
there too. So newlines are now always escaped, to simplify the code and
the test matrix.
2021-04-01 12:46:24 +02:00
Zbigniew Jędrzejewski-Szmek
679b0b0a21 test-process-util: getpid_cached() → 0
This has the same effect and is less verbose.
2021-04-01 12:46:24 +02:00
61 changed files with 1355 additions and 522 deletions

9
TODO
View File

@ -50,12 +50,6 @@ Features:
* systemd-sysext: optionally, run it in initrd already, before transitioning
into host, to open up possibility for services shipped like that.
* add a flag to the GPT spec that says "grow my fs to partition size", and make
it settable via systemd-repart. Add in growfs jobs in
systemd-gpt-auto-generator when it is set, and issue the ioctls while
mounting in systemd-npsawn --image=. That way systemd-repart suffices to
enlarge an image.
* add a new switch --auto-definitions=yes/no or so to systemd-repart. If
specified, synthesize a definition automatically if we can: enlarge last
partition on disk, but only if it is marked for growing and not read-only.
@ -355,9 +349,6 @@ Features:
* busctl: maybe expose a verb "ping" for pinging a dbus service to see if it
exists and responds.
* systemd-gpt-auto should probably set x-systemd.growfs on the mounts it
creates
* 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

View File

@ -94,24 +94,48 @@ localized.
## Partition Flags
For the root, `/usr/`, 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.
This specification defines three GPT partition flags that may be set for the
partition types defined above:
For the root, `/usr/`, 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.
1. For the root, `/usr/`, Verity, home, server data, variable data, temporary data,
swap and extended boot loader 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.
Note that these two flag definitions happen to map nicely to the ones used by
Microsoft Basic Data Partitions.
2. For the root, `/usr/`, Verity, home, server data, variable data, temporary
data and extended boot loader 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. Also note that Verity partitions are
by their semantics always read-only. The flag is hence of little effect for
them, and it is recommended to set it unconditionally for the Verity
partition types.
3. For the root, `/usr/`, home, server data, variable data, temporary data and
extended boot loader partitions, the partition flag bit 59
("*grow-file-system*") may be used to mark a partition for automatic growing
of the contained file system to the size of the partition when
mounted. Tools that automatically mount disk image with a GPT partition
table are suggested to implicitly grow the contained file system to the
partition size they are contained in. This flag is without effect on
partitions marked read-only.
Note that the first two flag definitions happen to map nicely to the ones used
by Microsoft Basic Data Partitions.
All three of these flags generally affect only auto-discovery and automatic
mounting of disk images. If partitions marked with these flags are mounted
using low-level commands like
[mount(8)](https://man7.org/linux/man-pages/man2/mount.8.html) or directly with
[mount(2)](https://man7.org/linux/man-pages/man2/mount.2.html), they typically
have no effect.
## Suggested Mode of Operation

View File

@ -354,10 +354,40 @@ Fri … 552351 1000 1000 SIGSEGV present /usr/lib64/firefox/firefox 28.7M
</example>
<example>
<title>Show information about a process that dumped core,
matching by its PID 6654</title>
<title>Show information about a core dump matched by PID</title>
<programlisting>$ coredumpctl info 6654</programlisting>
<programlisting>$ coredumpctl info 6654
PID: 6654 (bash)
UID: 1000 (user)
GID: 1000 (user)
Signal: 11 (SEGV)
Timestamp: Mon 2021-01-01 00:00:01 CET (20s ago)
Command Line: bash -c $'kill -SEGV $$'
Executable: /usr/bin/bash
Control Group: /user.slice/user-1000.slice/…
Unit: user@1000.service
User Unit: vte-spawn-….scope
Slice: user-1000.slice
Owner UID: 1000 (user)
Boot ID: …
Machine ID: …
Hostname: …
Storage: /var/lib/systemd/coredump/core.bash.1000.….zst (present)
Disk Size: 51.7K
Message: Process 130414 (bash) of user 1000 dumped core.
Stack trace of thread 130414:
#0 0x00007f398142358b kill (libc.so.6 + 0x3d58b)
#1 0x0000558c2c7fda09 kill_builtin (bash + 0xb1a09)
#2 0x0000558c2c79dc59 execute_builtin.lto_priv.0 (bash + 0x51c59)
#3 0x0000558c2c79709c execute_simple_command (bash + 0x4b09c)
#4 0x0000558c2c798408 execute_command_internal (bash + 0x4c408)
#5 0x0000558c2c7f6bdc parse_and_execute (bash + 0xaabdc)
#6 0x0000558c2c85415c run_one_command.isra.0 (bash + 0x10815c)
#7 0x0000558c2c77d040 main (bash + 0x31040)
#8 0x00007f398140db75 __libc_start_main (libc.so.6 + 0x27b75)
#9 0x0000558c2c77dd1e _start (bash + 0x31d1e)
</programlisting>
</example>
<example>

View File

@ -51,6 +51,13 @@
ordered by their metric. This assigns a stable hostname to the
current gateway, useful for referencing it independently of the
current network configuration state.</para></listitem>
<listitem><para>The hostname <literal>_outbound</literal> is resolved to the local IPv4 and IPv6
addresses that are most likely used for communication with other hosts. This is determined by
requesting a routing decision to the configured default gateways from the kernel and then using the
local IP addresses selected by this decision. This hostname is only available if there is at least one
local default gateway configured. This assigns a stable hostname to the local outbound IP addresses,
useful for referencing them independently of the current network configuration state.</para></listitem>
</itemizedlist>
<para>Various software relies on an always-resolvable local

View File

@ -55,11 +55,13 @@
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 definitions may only be used to created and initialize new partitions or grow
<para>Note that these definitions may only be used to create and initialize new partitions or to grow
existing ones. In the latter case it will not grow the contained files systems however; separate
mechanisms, such as
<citerefentry><refentrytitle>systemd-growfs</refentrytitle><manvolnum>8</manvolnum></citerefentry> may be
used to grow the file systems inside of these partitions.</para>
used to grow the file systems inside of these partitions. Partitions may also be marked for automatic
growing via the <varname>GrowFileSystem=</varname> setting, in which case the file system is grown on
first mount by tools that respect this flag. See below for details.</para>
</refsect1>
<refsect1>
@ -580,13 +582,39 @@
<varlistentry>
<term><varname>ReadOnly=</varname></term>
<term><varname>GrowFileSystem=</varname></term>
<listitem><para>Configures the Read-Only partition flags (bit 60) of the partition table entry. This
option is a friendly way to set bit 60 of the partition flags value without setting any of the other
bits, and may be set via <varname>Flags=</varname> too, see above.</para>
<listitem><para>Configures the Read-Only and Grow-File-System partition flags (bit 60 and 59) of the
partition table entry, as defined by the <ulink
url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>. Only
available for partition types supported by the specification. This option is a friendly way to set bit
60 and 59 of the partition flags value without setting any of the other bits, and may be set via
<varname>Flags=</varname> too, see above.</para>
<para>If both <varname>Flags=</varname> and <varname>ReadOnly=</varname> are set the latter controls
the value of the flag.</para></listitem>
<para>If <varname>Flags=</varname> is used in conjunction with one or both of
<varname>ReadOnly=</varname>/<varname>GrowFileSystem=</varname> the latter control the value of the
relevant flags, i.e. the high-level settings
<varname>ReadOnly=</varname>/<varname>GrowFileSystem=</varname> override the low-level setting
<varname>Flags=</varname>.</para>
<para>Note that the two flags affect only automatic partition mounting, as implemented by
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
or the <option>--image=</option> option of various commands (such as
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>). It
has no effect on explicit mounts, such as those done via <citerefentry
project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
<citerefentry
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
<para>If both bit 50 and 59 are set for a partition (i.e. the partition is marked both read-only and
marked for file system growing) the latter is typically without effect: the read-only flag takes
precedence in most tools reading these flags, and since growing the file system involves writing to
the partition it is consequently ignored.</para>
<para><varname>ReadOnly=</varname> defaults to on for Verity partition
types. <varname>GrowFileSystem=</varname> defaults to on for all partition types that support it,
except if the partition is marked read-only (and thus effectively, defaults to off for Verity
partitions).</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -299,11 +299,11 @@
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), select domains are resolved on the local system, among them
<literal>localhost</literal> and <literal>_gateway</literal> or entries from
<filename>/etc/hosts</filename>. If false these domains are not resolved locally, and either fail (in
case of <literal>localhost</literal> or <literal>_gateway</literal> and suchlike) or go to the
network via regular DNS/mDNS/LLMNR lookups (in case of <filename>/etc/hosts</filename>
entries).</para></listitem>
<literal>localhost</literal>, <literal>_gateway</literal> and <literal>_outbound</literal>, or
entries from <filename>/etc/hosts</filename>. If false these domains are not resolved locally, and
either fail (in case of <literal>localhost</literal>, <literal>_gateway</literal> or
<literal>_outbound</literal> and suchlike) or go to the network via regular DNS/mDNS/LLMNR lookups
(in case of <filename>/etc/hosts</filename> entries).</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -194,6 +194,28 @@
<option>--fsck=no</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--growfs=no</option></term>
<listitem><para>Turn off automatic growing of accessed file systems to their partition size, if
marked for that in the GPT partition table. By default when an image is accessed for writing (by
<option>--mount</option> or <option>--copy-to</option>) the file systems contained in the OS image
are automatically grown to their partition sizes, if bit 59 in the GPT partition flags is set for
partition types that are defined by the <ulink
url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>. This
behavior may be switched off using <option>--growfs=no</option>. File systems are grown automatically
on access if all of the following conditions are met:</para>
<orderedlist>
<listitem><para>The file system is mounted writable</para></listitem>
<listitem><para>The file system currently is smaller than the partition it is contained in (and thus can be grown)</para></listitem>
<listitem><para>The image contains a GPT partition table</para></listitem>
<listitem><para>The file system is stored on a partition defined by the Discoverable Partitions Specification</para></listitem>
<listitem><para>Bit 59 of the GPT partition flags for this partition is set, as per specification</para></listitem>
<listitem><para>The <option>--growfs=no</option> option is not passed.</para></listitem>
</orderedlist>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--mkdir</option></term>

View File

@ -104,6 +104,13 @@
gateway addresses, ordered by their metric. This assigns a stable hostname to the current gateway,
useful for referencing it independently of the current network configuration state.</para></listitem>
<listitem><para>The hostname <literal>_outbound</literal> is resolved to the local IPv4 and IPv6
addresses that are most likely used for communication with other hosts. This is determined by
requesting a routing decision to the configured default gateways from the kernel and then using the
local IP addresses selected by this decision. This hostname is only available if there is at least one
local default gateway configured. This assigns a stable hostname to the local outbound IP addresses,
useful for referencing them independently of the current network configuration state.</para></listitem>
<listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved to their
configured addresses and back, but they will not affect lookups for non-address types (like MX).
Support for <filename>/etc/hosts</filename> may be disabled with <varname>ReadEtcHosts=no</varname>,

View File

@ -360,15 +360,16 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
return t - r;
}
char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) {
char *ans, *t, *prev, *prev2;
const char *f;
/* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
* reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
* This corresponds to non-ASCII printable characters in pre-unicode encodings.
* reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
* unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
*
* If console_width is reached, output is truncated and "..." is appended. */
* If console_width is reached, or XESCAPE_FORCE_ELLIPSIS is set, output is truncated and "..." is
* appended. */
if (console_width == 0)
return strdup("");
@ -380,17 +381,23 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei
memset(ans, '_', MIN(strlen(s), console_width) * 4);
ans[MIN(strlen(s), console_width) * 4] = 0;
bool force_ellipsis = FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS);
for (f = s, t = prev = prev2 = ans; ; f++) {
char *tmp_t = t;
if (!*f) {
if (force_ellipsis)
break;
*t = 0;
return ans;
}
if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) ||
if ((unsigned char) *f < ' ' ||
(!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
*f == '\\' || strchr(bad, *f)) {
if ((size_t) (t - ans) + 4 > console_width)
if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
break;
*(t++) = '\\';
@ -398,7 +405,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
} else {
if ((size_t) (t - ans) + 1 > console_width)
if ((size_t) (t - ans) + 1 + 3 * force_ellipsis > console_width)
break;
*(t++) = *f;
@ -427,11 +434,13 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei
return ans;
}
char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
if (eight_bit)
return xescape_full(str, "", console_width, true);
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) {
if (FLAGS_SET(flags, XESCAPE_8_BIT))
return xescape_full(str, "", console_width, flags);
else
return utf8_escape_non_printable_full(str, console_width);
return utf8_escape_non_printable_full(str,
console_width,
FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS));
}
char* octescape(const char *s, size_t len) {
@ -462,19 +471,15 @@ char* octescape(const char *s, size_t len) {
}
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);
for (; *s; s++) {
if (escape_tab_nl && IN_SET(*s, '\n', '\t')) {
*(t++) = '\\';
*(t++) = *s == '\n' ? 'n' : 't';
continue;
}
for (; *s; s++)
if (char_is_cc(*s))
t += cescape_char(*s, t);
else {
if (*s == '\\' || strchr(bad, *s))
*(t++) = '\\';
*(t++) = *s;
}
@ -482,68 +487,58 @@ static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad, b
}
char* shell_escape(const char *s, const char *bad) {
char *r, *t;
char *buf, *t;
r = new(char, strlen(s)*2+1);
if (!r)
buf = new(char, strlen(s)*4+1);
if (!buf)
return NULL;
t = strcpy_backslash_escaped(r, s, bad, false);
t = strcpy_backslash_escaped(buf, s, bad);
*t = 0;
return r;
return buf;
}
char* shell_maybe_quote(const char *s, EscapeStyle style) {
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
const char *p;
char *r, *t;
char *buf, *t;
assert(s);
/* Encloses a string in quotes if necessary to make it OK as a shell
* string. Note that we treat benign UTF-8 characters as needing
* escaping too, but that should be OK. */
/* Encloses a string in quotes if necessary to make it OK as a shell string. */
if (FLAGS_SET(flags, SHELL_ESCAPE_EMPTY) && isempty(s))
return strdup("\"\""); /* We don't use $'' here in the POSIX mode. "" is fine too. */
for (p = s; *p; p++)
if (*p <= ' ' ||
*p >= 127 ||
strchr(SHELL_NEED_QUOTES, *p))
if (char_is_cc(*p) ||
strchr(WHITESPACE SHELL_NEED_QUOTES, *p))
break;
if (!*p)
return strdup(s);
r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1);
if (!r)
buf = new(char, FLAGS_SET(flags, SHELL_ESCAPE_POSIX) + 1 + strlen(s)*4 + 1 + 1);
if (!buf)
return NULL;
t = r;
switch (style) {
case ESCAPE_BACKSLASH:
case ESCAPE_BACKSLASH_ONELINE:
*(t++) = '"';
break;
case ESCAPE_POSIX:
t = buf;
if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) {
*(t++) = '$';
*(t++) = '\'';
break;
default:
assert_not_reached("Bad EscapeStyle");
}
} else
*(t++) = '"';
t = mempcpy(t, s, p - s);
if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE,
style == ESCAPE_BACKSLASH_ONELINE);
else
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
t = strcpy_backslash_escaped(t, p,
FLAGS_SET(flags, SHELL_ESCAPE_POSIX) ? SHELL_NEED_ESCAPE_POSIX : SHELL_NEED_ESCAPE);
if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
*(t++) = '"';
else
if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX))
*(t++) = '\'';
else
*(t++) = '"';
*t = 0;
return r;
return str_realloc(buf);
}

View File

@ -33,15 +33,13 @@ typedef enum UnescapeFlags {
UNESCAPE_ACCEPT_NUL = 1 << 1,
} UnescapeFlags;
typedef enum EscapeStyle {
ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single
argument, possibly multiline. Tabs and newlines are not escaped. */
ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line
string instead. Shell escape sequences are produced for tabs and
newlines. */
ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape
* syntax (a string enclosed in $'') instead of plain quotes. */
} EscapeStyle;
typedef enum ShellEscapeFlags {
/* The default is to add shell quotes ("") so the shell will consider this a single argument.
* Tabs and newlines are escaped. */
SHELL_ESCAPE_POSIX = 1 << 1, /* Use POSIX shell escape syntax (a string enclosed in $'') instead of plain quotes. */
SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */
} ShellEscapeFlags;
char* cescape(const char *s);
char* cescape_length(const char *s, size_t n);
@ -56,12 +54,17 @@ static inline int cunescape(const char *s, UnescapeFlags flags, char **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);
typedef enum XEscapeFlags {
XESCAPE_8_BIT = 1 << 0,
XESCAPE_FORCE_ELLIPSIS = 1 << 1,
} XEscapeFlags;
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags);
static inline char* xescape(const char *s, const char *bad) {
return xescape_full(s, bad, SIZE_MAX, false);
return xescape_full(s, bad, SIZE_MAX, 0);
}
char* octescape(const char *s, size_t len);
char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
char* shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, EscapeStyle style);
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);

View File

@ -364,32 +364,40 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
return 1;
}
int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
_cleanup_close_ int fd = -1;
struct stat st;
size_t n, size;
int n_retries;
bool truncated = false;
assert(ret_contents);
/* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work
* with two sorts of virtual files. One sort uses "seq_file", and the results of
* the first read are buffered for the second read. The other sort uses "raw"
* reads which always go direct to the device. In the latter case, the content of
* the virtual file must be retrieved with a single read otherwise a second read
* might get the new value instead of finding EOF immediately. That's the reason
* why the usage of fread(3) is prohibited in this case as it always performs a
* second call to read(2) looking for EOF. See issue 13585. */
/* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work with two sorts of
* virtual files. One sort uses "seq_file", and the results of the first read are buffered for the
* second read. The other sort uses "raw" reads which always go direct to the device. In the latter
* case, the content of the virtual file must be retrieved with a single read otherwise a second read
* might get the new value instead of finding EOF immediately. That's the reason why the usage of
* fread(3) is prohibited in this case as it always performs a second call to read(2) looking for
* EOF. See issue #13585.
*
* max_size specifies a limit on the bytes read. If max_size is SIZE_MAX, the full file is read. If
* the the full file is too large to read, an error is returned. For other values of max_size,
* *partial contents* may be returned. (Though the read is still done using one syscall.)
* Returns 0 on partial success, 1 if untruncated contents were read. */
fd = open(filename, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
assert(max_size <= READ_FULL_BYTES_MAX || max_size == SIZE_MAX);
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
n_retries = 3;
for (;;) {
struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
@ -399,13 +407,16 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
/* Be prepared for files from /proc which generally report a file size of 0. */
assert_cc(READ_FULL_BYTES_MAX < SSIZE_MAX);
if (st.st_size > 0) {
if (st.st_size > READ_FULL_BYTES_MAX)
if (st.st_size > SSIZE_MAX) /* Avoid overflow with 32-bit size_t and 64-bit off_t. */
return -EFBIG;
size = MIN((size_t) st.st_size, max_size);
if (size > READ_FULL_BYTES_MAX)
return -EFBIG;
size = st.st_size;
n_retries--;
} else {
size = READ_FULL_BYTES_MAX;
size = MIN(READ_FULL_BYTES_MAX, max_size);
n_retries = 0;
}
@ -413,7 +424,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
if (!buf)
return -ENOMEM;
/* Use a bigger allocation if we got it anyway, but not more than the limit. */
size = MIN(malloc_usable_size(buf) - 1, READ_FULL_BYTES_MAX);
size = MIN3(malloc_usable_size(buf) - 1, max_size, READ_FULL_BYTES_MAX);
for (;;) {
ssize_t k;
@ -440,9 +451,16 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
* processing, let's try again either with a bigger guessed size or the new
* file size. */
if (n_retries <= 0)
if (n_retries <= 0) {
if (max_size == SIZE_MAX)
return st.st_size > 0 ? -EIO : -EFBIG;
/* Accept a short read, but truncate it appropropriately. */
n = MIN(n, max_size);
truncated = true;
break;
}
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
@ -470,7 +488,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
buf[n] = 0;
*ret_contents = TAKE_PTR(buf);
return 0;
return !truncated;
}
int read_full_stream_full(
@ -914,12 +932,19 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
return 0;
}
static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
static int search_and_fopen_internal(
const char *path,
const char *mode,
const char *root,
char **search,
FILE **ret,
char **ret_path) {
char **i;
assert(path);
assert(mode);
assert(_f);
assert(ret);
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
@ -934,7 +959,10 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
f = fopen(p, mode);
if (f) {
*_f = f;
if (ret_path)
*ret_path = path_simplify(TAKE_PTR(p), true);
*ret = f;
return 0;
}
@ -945,52 +973,84 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
return -ENOENT;
}
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
int search_and_fopen(
const char *filename,
const char *mode,
const char *root,
const char **search,
FILE **ret,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
assert(path);
assert(filename);
assert(mode);
assert(_f);
assert(ret);
if (path_is_absolute(path)) {
FILE *f;
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
f = fopen(path, mode);
if (f) {
*_f = f;
return 0;
f = fopen(filename, mode);
if (!f)
return -errno;
if (ret_path) {
char *p;
p = strdup(filename);
if (!p)
return -ENOMEM;
*ret_path = path_simplify(p, true);
}
return -errno;
*ret = TAKE_PTR(f);
return 0;
}
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(path, mode, root, copy, _f);
return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
}
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
int search_and_fopen_nulstr(
const char *filename,
const char *mode,
const char *root,
const char *search,
FILE **ret,
char **ret_path) {
_cleanup_strv_free_ char **s = NULL;
if (path_is_absolute(path)) {
FILE *f;
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
f = fopen(path, mode);
if (f) {
*_f = f;
return 0;
f = fopen(filename, mode);
if (!f)
return -errno;
if (ret_path) {
char *p;
p = strdup(filename);
if (!p)
return -ENOMEM;
*ret_path = path_simplify(p, true);
}
return -errno;
*ret = TAKE_PTR(f);
return 0;
}
s = strv_split_nulstr(search);
if (!s)
return -ENOMEM;
return search_and_fopen_internal(path, mode, root, s, _f);
return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
}
int chase_symlinks_and_fopen_unlocked(

View File

@ -65,7 +65,12 @@ int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_
static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
}
int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size);
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
}
int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size);
static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) {
return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size);
@ -80,8 +85,8 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
DIR *xopendirat(int dirfd, const char *name, int flags);
int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret);
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
int chase_symlinks_and_fopen_unlocked(
const char *path,

View File

@ -28,3 +28,8 @@ static inline bool is_gateway_hostname(const char *hostname) {
/* This tries to identify the valid syntaxes for the our synthetic "gateway" host. */
return STRCASE_IN_SET(hostname, "_gateway", "_gateway.");
}
static inline bool is_outbound_hostname(const char *hostname) {
/* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */
return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
}

View File

@ -123,64 +123,136 @@ int get_process_comm(pid_t pid, char **ret) {
return 0;
}
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
_cleanup_free_ char *t = NULL, *ans = NULL;
static int get_process_cmdline_nulstr(
pid_t pid,
size_t max_size,
ProcessCmdlineFlags flags,
char **ret,
size_t *ret_size) {
const char *p;
char *t;
size_t k;
int r;
assert(line);
assert(pid >= 0);
/* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (<28>). If
* max_columns is != -1 will return a string of the specified console width at most, abbreviated with
* an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command
* line set (the case for kernel threads), or has a command line that resolves to the empty string
* will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of
* input data.
/* Retrieves a process' command line as a "sized nulstr", i.e. possibly without the last NUL, but
* with a specified size.
*
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* comm_fallback is false). Returns 0 and sets *line otherwise. */
* If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command line set
* (the case for kernel threads), or has a command line that resolves to the empty string, will
* return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of input
* data.
*
* Returns an error, 0 if output was read but is truncated, 1 otherwise.
*/
p = procfs_file_alloca(pid, "cmdline");
r = read_full_virtual_file(p, &t, &k);
r = read_virtual_file(p, max_size, &t, &k); /* Let's assume that each input byte results in >= 1
* columns of output. We ignore zero-width codepoints. */
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
if (k > 0) {
/* Arguments are separated by NULs. Let's replace those with spaces. */
for (size_t i = 0; i < k - 1; i++)
if (t[i] == '\0')
t[i] = ' ';
} else {
if (k == 0) {
t = mfree(t);
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
/* Kernel threads have no argv[] */
_cleanup_free_ char *t2 = NULL;
_cleanup_free_ char *comm = NULL;
r = get_process_comm(pid, &t2);
r = get_process_comm(pid, &comm);
if (r < 0)
return r;
free(t);
t = strjoin("[", t2, "]");
t = strjoin("[", comm, "]");
if (!t)
return -ENOMEM;
k = strlen(t);
r = k <= max_size;
if (r == 0) /* truncation */
t[max_size] = '\0';
}
*ret = t;
*ret_size = k;
return r;
}
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
_cleanup_free_ char *t = NULL;
size_t k;
char *ans;
assert(line);
assert(pid >= 0);
/* Retrieve adn format a commandline. See above for discussion of retrieval options.
*
* There are two main formatting modes:
*
* - when PROCESS_CMDLINE_QUOTE is specified, output is quoted in C/Python style. If no shell special
* characters are present, this output can be copy-pasted into the terminal to execute. UTF-8
* output is assumed.
*
* - otherwise, a compact non-roundtrippable form is returned. Non-UTF8 bytes are replaced by <EFBFBD>. The
* returned string is of the specified console width at most, abbreviated with an ellipsis.
*
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */
int full = get_process_cmdline_nulstr(pid, max_columns, flags, &t, &k);
if (full < 0)
return full;
if (flags & (PROCESS_CMDLINE_QUOTE | PROCESS_CMDLINE_QUOTE_POSIX)) {
ShellEscapeFlags shflags = SHELL_ESCAPE_EMPTY |
FLAGS_SET(flags, PROCESS_CMDLINE_QUOTE_POSIX) * SHELL_ESCAPE_POSIX;
assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
_cleanup_strv_free_ char **args = NULL;
args = strv_parse_nulstr(t, k);
if (!args)
return -ENOMEM;
for (size_t i = 0; args[i]; i++) {
char *e;
e = shell_maybe_quote(args[i], shflags);
if (!e)
return -ENOMEM;
free_and_replace(args[i], e);
}
ans = strv_join(args, " ");
if (!ans)
return -ENOMEM;
} else {
/* Arguments are separated by NULs. Let's replace those with spaces. */
for (size_t i = 0; i < k - 1; i++)
if (t[i] == '\0')
t[i] = ' ';
delete_trailing_chars(t, WHITESPACE);
bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
ans = escape_non_printable_full(t, max_columns, eight_bit);
ans = escape_non_printable_full(t, max_columns,
eight_bit * XESCAPE_8_BIT | !full * XESCAPE_FORCE_ELLIPSIS);
if (!ans)
return -ENOMEM;
(void) str_realloc(&ans);
*line = TAKE_PTR(ans);
ans = str_realloc(ans);
}
*line = ans;
return 0;
}

View File

@ -35,6 +35,8 @@
typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
PROCESS_CMDLINE_USE_LOCALE = 1 << 1,
PROCESS_CMDLINE_QUOTE = 1 << 2,
PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3,
} ProcessCmdlineFlags;
int get_process_comm(pid_t pid, char **name);

View File

@ -150,7 +150,7 @@ char *delete_chars(char *s, const char *bad) {
}
char *delete_trailing_chars(char *s, const char *bad) {
char *p, *c = s;
char *c = s;
/* Drops all specified bad characters, at the end of the string */
@ -160,7 +160,7 @@ char *delete_trailing_chars(char *s, const char *bad) {
if (!bad)
bad = WHITESPACE;
for (p = s; *p; p++)
for (char *p = s; *p; p++)
if (!strchr(bad, *p))
c = p + 1;
@ -193,34 +193,28 @@ char ascii_toupper(char x) {
}
char *ascii_strlower(char *t) {
char *p;
assert(t);
for (p = t; *p; p++)
for (char *p = t; *p; p++)
*p = ascii_tolower(*p);
return t;
}
char *ascii_strupper(char *t) {
char *p;
assert(t);
for (p = t; *p; p++)
for (char *p = t; *p; p++)
*p = ascii_toupper(*p);
return t;
}
char *ascii_strlower_n(char *t, size_t n) {
size_t i;
if (n <= 0)
return t;
for (i = 0; i < n; i++)
for (size_t i = 0; i < n; i++)
t[i] = ascii_tolower(t[i]);
return t;
@ -252,10 +246,8 @@ int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
}
bool chars_intersect(const char *a, const char *b) {
const char *p;
/* Returns true if any of the chars in a are in b. */
for (p = a; *p; p++)
for (const char *p = a; *p; p++)
if (strchr(b, *p))
return true;
@ -263,8 +255,6 @@ bool chars_intersect(const char *a, const char *b) {
}
bool string_has_cc(const char *p, const char *ok) {
const char *t;
assert(p);
/*
@ -273,14 +263,11 @@ bool string_has_cc(const char *p, const char *ok) {
* considered OK.
*/
for (t = p; *t; t++) {
for (const char *t = p; *t; t++) {
if (ok && strchr(ok, *t))
continue;
if (*t > 0 && *t < ' ')
return true;
if (*t == 127)
if (char_is_cc(*t))
return true;
}
@ -468,7 +455,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
* very end.
*/
size_t i = 0, last_char_width[4] = {}, k = 0, j;
size_t i = 0, last_char_width[4] = {}, k = 0;
assert(len > 0); /* at least a terminating NUL */
@ -497,7 +484,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
/* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
* characters ideally, but the buffer is shorter than that in the first place take what we can get */
for (j = 0; j < ELEMENTSOF(last_char_width); j++) {
for (size_t j = 0; j < ELEMENTSOF(last_char_width); j++) {
if (i + 4 <= len) /* nice, we reached our space goal */
break;
@ -984,14 +971,12 @@ int free_and_strndup(char **p, const char *s, size_t l) {
}
bool string_is_safe(const char *p) {
const char *t;
if (!p)
return false;
/* Checks if the specified string contains no quotes or control characters */
for (t = p; *t; t++) {
for (const char *t = p; *t; t++) {
if (*t > 0 && *t < ' ') /* no control characters */
return false;

View File

@ -129,6 +129,9 @@ static inline bool _pure_ in_charset(const char *s, const char* charset) {
return s[strspn(s, charset)] == '\0';
}
static inline bool char_is_cc(char p) {
return (p >= 0 && p < ' ') || p == 127;
}
bool string_has_cc(const char *p, const char *ok) _pure_;
char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent);
@ -216,17 +219,13 @@ static inline void *memory_startswith_no_case(const void *p, size_t sz, const ch
return (uint8_t*) p + n;
}
static inline char* str_realloc(char **p) {
/* Reallocate *p to actual size */
static inline char* str_realloc(char *p) {
/* Reallocate *p to actual size. Ignore failure, and return the original string on error. */
if (!*p)
if (!p)
return NULL;
char *t = realloc(*p, strlen(*p) + 1);
if (!t)
return NULL;
return (*p = t);
return realloc(p, strlen(p) + 1) ?: p;
}
char* string_erase(char *x);

View File

@ -196,8 +196,7 @@ char *utf8_escape_invalid(const char *str) {
}
*s = '\0';
(void) str_realloc(&p);
return p;
return str_realloc(p);
}
static int utf8_char_console_width(const char *str) {
@ -213,7 +212,7 @@ static int utf8_char_console_width(const char *str) {
return unichar_iswide(c) ? 2 : 1;
}
char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis) {
char *p, *s, *prev_s;
size_t n = 0; /* estimated print width */
@ -230,8 +229,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
int len;
char *saved_s = s;
if (!*str) /* done! */
if (!*str) { /* done! */
if (force_ellipsis)
goto truncation;
else
goto finish;
}
len = utf8_encoded_valid_unichar(str, SIZE_MAX);
if (len > 0) {
@ -275,15 +278,14 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
truncation:
/* Try to go back one if we don't have enough space for the ellipsis */
if (n + 1 >= console_width)
if (n + 1 > console_width)
s = prev_s;
s = mempcpy(s, "", strlen(""));
finish:
*s = '\0';
(void) str_realloc(&p);
return p;
return str_realloc(p);
}
char *ascii_is_valid(const char *str) {

View File

@ -25,9 +25,9 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
char *utf8_escape_invalid(const char *s);
char *utf8_escape_non_printable_full(const char *str, size_t console_width);
char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis);
static inline char *utf8_escape_non_printable(const char *str) {
return utf8_escape_non_printable_full(str, SIZE_MAX);
return utf8_escape_non_printable_full(str, SIZE_MAX, false);
}
size_t utf8_encode_unichar(char *out_utf8, char32_t g);

View File

@ -65,11 +65,12 @@ static int apply_rule(const char *rule) {
static int apply_file(const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *pp = NULL;
int r;
assert(path);
r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("binfmt.d"), &f);
r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("binfmt.d"), &f, &pp);
if (r < 0) {
if (ignore_enoent && r == -ENOENT)
return 0;
@ -77,7 +78,7 @@ static int apply_file(const char *path, bool ignore_enoent) {
return log_error_errno(r, "Failed to open file '%s': %m", path);
}
log_debug("apply: %s", path);
log_debug("apply: %s", pp);
for (;;) {
_cleanup_free_ char *line = NULL;
char *p;
@ -85,7 +86,7 @@ static int apply_file(const char *path, bool ignore_enoent) {
k = read_line(f, LONG_LINE_MAX, &line);
if (k < 0)
return log_error_errno(k, "Failed to read file '%s': %m", path);
return log_error_errno(k, "Failed to read file '%s': %m", pp);
if (k == 0)
break;

View File

@ -1229,7 +1229,9 @@ static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *
p = buf;
}
(void) get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &cmdline);
(void) get_process_cmdline(pid, SIZE_MAX,
PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE,
&cmdline);
return sd_bus_message_append(reply,
"(sus)",

View File

@ -848,7 +848,7 @@ static void job_print_done_status_message(Unit *u, JobType t, JobResult result)
if (t == JOB_START && result == JOB_FAILED) {
_cleanup_free_ char *quoted = NULL;
quoted = shell_maybe_quote(u->id, ESCAPE_BACKSLASH);
quoted = shell_maybe_quote(u->id, 0);
manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
}
}

View File

@ -1818,7 +1818,8 @@ int setup_namespace(
DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_USR_NO_ROOT;
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_GROWFS;
size_t n_mounts;
int r;

View File

@ -665,7 +665,7 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
if (r < 0)
return r;
r = get_process_cmdline(container_pid, SIZE_MAX, 0, cmdline);
r = get_process_cmdline(container_pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, cmdline);
if (r < 0)
return r;
@ -1145,7 +1145,7 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
if (sd_pid_get_slice(pid, &t) >= 0)
(void) iovw_put_string_field_free(iovw, "COREDUMP_SLICE=", t);
if (get_process_cmdline(pid, SIZE_MAX, 0, &t) >= 0)
if (get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &t) >= 0)
(void) iovw_put_string_field_free(iovw, "COREDUMP_CMDLINE=", t);
if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)

View File

@ -49,7 +49,8 @@ static DissectImageFlags arg_flags =
DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_USR_NO_ROOT;
DISSECT_IMAGE_USR_NO_ROOT |
DISSECT_IMAGE_GROWFS;
static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
@ -75,6 +76,7 @@ static int help(void) {
" --no-legend Do not show the headers and footers\n"
" -r --read-only Mount read-only\n"
" --fsck=BOOL Run fsck before mounting\n"
" --growfs=BOOL Grow file system to partition size, if marked\n"
" --mkdir Make mount directory before mounting, if missing\n"
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
" --root-hash=HASH Specify root hash for verity\n"
@ -112,6 +114,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_LEGEND,
ARG_DISCARD,
ARG_FSCK,
ARG_GROWFS,
ARG_ROOT_HASH,
ARG_ROOT_HASH_SIG,
ARG_VERITY_DATA,
@ -128,6 +131,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD },
{ "fsck", required_argument, NULL, ARG_FSCK },
{ "growfs", required_argument, NULL, ARG_GROWFS },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
@ -264,6 +268,14 @@ static int parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r);
break;
case ARG_GROWFS:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --growfs= parameter: %s", optarg);
SET_FLAG(arg_flags, DISSECT_IMAGE_GROWFS, r);
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
if (r <= 0)
@ -467,7 +479,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
return log_oom();
}
t = table_new("rw", "designator", "partition uuid", "partition label", "fstype", "architecture", "verity", "node", "partno");
t = table_new("rw", "designator", "partition uuid", "partition label", "fstype", "architecture", "verity", "growfs", "node", "partno");
if (!t)
return log_oom();
@ -511,6 +523,10 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
if (r < 0)
return table_log_add_error(r);
r = table_add_many(t, TABLE_BOOLEAN, (int) p->growfs);
if (r < 0)
return table_log_add_error(r);
if (p->partno < 0) /* no partition table, naked file system */ {
r = table_add_cell(t, NULL, TABLE_STRING, arg_image);
if (r < 0)

View File

@ -70,7 +70,7 @@ static int load_and_print(void) {
t = strchr(*i, '=');
assert(t);
q = shell_maybe_quote(t + 1, ESCAPE_BACKSLASH);
q = shell_maybe_quote(t + 1, 0);
if (!q)
return log_oom();

View File

@ -1357,7 +1357,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK,
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
&unlink_dir,
&loop_device,
&decrypted_image);

View File

@ -194,6 +194,7 @@ static int add_mount(
const char *where,
const char *fstype,
bool rw,
bool growfs,
const char *options,
const char *description,
const char *post) {
@ -271,8 +272,18 @@ static int add_mount(
if (r < 0)
return log_error_errno(r, "Failed to write unit file %s: %m", p);
if (post)
return generator_add_symlink(arg_dest, post, "requires", unit);
if (growfs) {
r = generator_hook_up_growfs(arg_dest, where, post);
if (r < 0)
return r;
}
if (post) {
r = generator_add_symlink(arg_dest, post, "requires", unit);
if (r < 0)
return r;
}
return 0;
}
@ -321,6 +332,7 @@ static int add_partition_mount(
where,
p->fstype,
p->rw,
p->growfs,
NULL,
description,
SPECIAL_LOCAL_FS_TARGET);
@ -385,6 +397,7 @@ static int add_automount(
const char *where,
const char *fstype,
bool rw,
bool growfs,
const char *options,
const char *description,
usec_t timeout) {
@ -406,6 +419,7 @@ static int add_automount(
where,
fstype,
rw,
growfs,
opt,
description,
NULL);
@ -481,7 +495,8 @@ static int add_xbootldr(DissectedPartition *p) {
p->node,
"/boot",
p->fstype,
true,
/* rw= */ true,
/* growfs= */ false,
esp_or_xbootldr_options(p),
"Boot Loader Partition",
120 * USEC_PER_SEC);
@ -555,7 +570,8 @@ static int add_esp(DissectedPartition *p, bool has_xbootldr) {
p->node,
esp_path,
p->fstype,
true,
/* rw= */ true,
/* growfs= */ false,
esp_or_xbootldr_options(p),
"EFI System Partition Automount",
120 * USEC_PER_SEC);
@ -651,7 +667,8 @@ static int add_root_mount(void) {
"/dev/gpt-auto-root",
in_initrd() ? "/sysroot" : "/",
NULL,
arg_root_rw > 0,
/* rw= */ arg_root_rw > 0,
/* growfs= */ false,
NULL,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);

View File

@ -1327,7 +1327,7 @@ static int manager_load_key_pair(Manager *m) {
m->private_key = NULL;
}
r = search_and_fopen_nulstr("local.private", "re", NULL, KEY_PATHS_NULSTR, &f);
r = search_and_fopen_nulstr("local.private", "re", NULL, KEY_PATHS_NULSTR, &f, NULL);
if (r == -ENOENT)
return 0;
if (r < 0)

View File

@ -2154,7 +2154,7 @@ int main(int argc, char *argv[]) {
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
(arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK : DISSECT_IMAGE_READ_ONLY),
(arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS : DISSECT_IMAGE_READ_ONLY),
&unlink_dir,
&loop_device,
&decrypted_image);

View File

@ -225,7 +225,7 @@ static void client_context_read_basic(ClientContext *c) {
if (get_process_exe(c->pid, &t) >= 0)
free_and_replace(c->exe, t);
if (get_process_cmdline(c->pid, SIZE_MAX, 0, &t) >= 0)
if (get_process_cmdline(c->pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &t) >= 0)
free_and_replace(c->cmdline, t);
if (get_process_capeff(c->pid, &t) >= 0)

View File

@ -146,7 +146,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK,
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
&unlink_dir,
&loop_device,
&decrypted_image);

View File

@ -62,12 +62,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *pp = NULL;
int r;
assert(ctx);
assert(path);
r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f, &pp);
if (r < 0) {
if (ignore_enoent && r == -ENOENT)
return 0;
@ -75,7 +76,7 @@ static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent
return log_error_errno(r, "Failed to open %s: %m", path);
}
log_debug("apply: %s", path);
log_debug("apply: %s", pp);
for (;;) {
_cleanup_free_ char *line = NULL;
char *l;
@ -83,7 +84,7 @@ static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent
k = read_line(f, LONG_LINE_MAX, &line);
if (k < 0)
return log_error_errno(k, "Failed to read file '%s': %m", path);
return log_error_errno(k, "Failed to read file '%s': %m", pp);
if (k == 0)
break;

View File

@ -3577,7 +3577,7 @@ static int outer_child(
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
DISSECT_IMAGE_DISCARD_ON_LOOP|
DISSECT_IMAGE_USR_NO_ROOT|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS)|
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
if (r < 0)
return r;
@ -3670,7 +3670,7 @@ static int outer_child(
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
DISSECT_IMAGE_DISCARD_ON_LOOP|
DISSECT_IMAGE_USR_NO_ROOT|
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK));
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK|DISSECT_IMAGE_GROWFS));
if (r == -EUCLEAN)
return log_error_errno(r, "File system check for image failed: %m");
if (r < 0)

View File

@ -54,8 +54,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
assert(h_errnop);
if (is_localhost(name)) {
/* We respond to 'localhost', so that /etc/hosts
* is optional */
/* We respond to 'localhost', so that /etc/hosts is optional */
canonical = "localhost";
local_address_ipv4 = htobe32(INADDR_LOOPBACK);
@ -68,6 +67,14 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
canonical = "_gateway";
} else if (is_outbound_hostname(name)) {
n_addresses = local_outbounds(NULL, 0, AF_UNSPEC, &addresses);
if (n_addresses <= 0)
goto not_found;
canonical = "_outbound";
} else {
hn = gethostname_malloc();
if (!hn) {
@ -343,6 +350,14 @@ enum nss_status _nss_myhostname_gethostbyname3_r(
canonical = "_gateway";
} else if (is_outbound_hostname(name)) {
n_addresses = local_outbounds(NULL, 0, af, &addresses);
if (n_addresses <= 0)
goto not_found;
canonical = "_outbound";
} else {
hn = gethostname_malloc();
if (!hn) {

View File

@ -294,8 +294,8 @@ static enum nss_status nss_systemd_endent(GetentData *p) {
assert(p);
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
_l = pthread_mutex_lock_assert(&p->mutex);
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&p->mutex);
(void) _l; /* make llvm shut up about _l not being used. */
p->iterator = userdb_iterator_free(p->iterator);
p->by_membership = false;
@ -312,16 +312,16 @@ enum nss_status _nss_systemd_endgrent(void) {
}
enum nss_status _nss_systemd_setpwent(int stayopen) {
int r;
PROTECT_ERRNO;
NSS_ENTRYPOINT_BEGIN;
if (_nss_systemd_is_blocked())
return NSS_STATUS_NOTFOUND;
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
int r;
_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
(void) _l; /* make llvm shut up about _l not being used. */
getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
getpwent_data.by_membership = false;
@ -336,16 +336,16 @@ enum nss_status _nss_systemd_setpwent(int stayopen) {
}
enum nss_status _nss_systemd_setgrent(int stayopen) {
int r;
PROTECT_ERRNO;
NSS_ENTRYPOINT_BEGIN;
if (_nss_systemd_is_blocked())
return NSS_STATUS_NOTFOUND;
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
int r;
_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
(void) _l; /* make llvm shut up about _l not being used. */
getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
getgrent_data.by_membership = false;
@ -372,9 +372,8 @@ enum nss_status _nss_systemd_getpwent_r(
if (_nss_systemd_is_blocked())
return NSS_STATUS_NOTFOUND;
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
(void) _l; /* make llvm shut up about _l not being used. */
if (!getpwent_data.iterator) {
UNPROTECT_ERRNO;
@ -419,9 +418,8 @@ enum nss_status _nss_systemd_getgrent_r(
if (_nss_systemd_is_blocked())
return NSS_STATUS_NOTFOUND;
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
(void) _l; /* make llvm shut up about _l not being used. */
if (!getgrent_data.iterator) {
UNPROTECT_ERRNO;

View File

@ -170,6 +170,7 @@ struct Partition {
uint64_t gpt_flags;
int read_only;
int growfs;
LIST_FIELDS(Partition, partitions);
};
@ -243,6 +244,7 @@ static Partition *partition_new(void) {
.copy_blocks_fd = -1,
.copy_blocks_size = UINT64_MAX,
.read_only = -1,
.growfs = -1,
};
return p;
@ -1316,6 +1318,7 @@ static int partition_read_definition(Partition *p, const char *path) {
{ "Partition", "Encrypt", config_parse_encrypt, 0, &p->encrypt },
{ "Partition", "Flags", config_parse_gpt_flags, 0, &p->gpt_flags },
{ "Partition", "ReadOnly", config_parse_tristate, 0, &p->read_only },
{ "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
{}
};
int r;
@ -1363,6 +1366,11 @@ static int partition_read_definition(Partition *p, const char *path) {
p->read_only < 0)
p->read_only = true;
/* Default to "growfs" on, unless read-only */
if (gpt_partition_type_knows_growfs(p->type_uuid) &&
p->read_only <= 0)
p->growfs = true;
return 0;
}
@ -3255,6 +3263,38 @@ static int set_gpt_flags(struct fdisk_partition *q, uint64_t flags) {
return fdisk_partition_set_attrs(q, a);
}
static uint64_t partition_merge_flags(Partition *p) {
uint64_t f;
assert(p);
f = p->gpt_flags;
if (p->read_only >= 0) {
if (gpt_partition_type_knows_read_only(p->type_uuid))
SET_FLAG(f, GPT_FLAG_READ_ONLY, p->read_only);
else {
char buffer[ID128_UUID_STRING_MAX];
log_warning("Configured ReadOnly=%s for partition type '%s' that doesn't support it, ignoring.",
yes_no(p->read_only),
gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
}
}
if (p->growfs >= 0) {
if (gpt_partition_type_knows_growfs(p->type_uuid))
SET_FLAG(f, GPT_FLAG_GROWFS, p->growfs);
else {
char buffer[ID128_UUID_STRING_MAX];
log_warning("Configured GrowFileSystem=%s for partition type '%s' that doesn't support it, ignoring.",
yes_no(p->growfs),
gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
}
}
return f;
}
static int context_mangle_partitions(Context *context) {
Partition *p;
int r;
@ -3323,7 +3363,6 @@ static int context_mangle_partitions(Context *context) {
_cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
_cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
char ids[ID128_UUID_STRING_MAX];
uint64_t f;
assert(!p->new_partition);
assert(p->offset % 512 == 0);
@ -3371,19 +3410,8 @@ static int context_mangle_partitions(Context *context) {
if (r < 0)
return log_error_errno(r, "Failed to set partition label: %m");
/* Merge the read only setting with the literal flags */
f = p->gpt_flags;
if (p->read_only >= 0) {
if (gpt_partition_type_knows_read_only(p->type_uuid))
SET_FLAG(f, GPT_FLAG_READ_ONLY, p->read_only);
else {
char buffer[ID128_UUID_STRING_MAX];
log_warning("Configured ReadOnly=yes for partition type '%s' that doesn't support it, ignoring.",
gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer));
}
}
r = set_gpt_flags(q, f);
/* Merge the read only + growfs setting with the literal flags, and set them for the partition */
r = set_gpt_flags(q, partition_merge_flags(p));
if (r < 0)
return log_error_errno(r, "Failed to set GPT partition flags: %m");

View File

@ -65,9 +65,9 @@ device: $D/zzz
unit: sectors
first-lba: 2048
last-lba: 2097118
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59"
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
EOF
@ -100,9 +100,9 @@ device: $D/zzz
unit: sectors
first-lba: 2048
last-lba: 2097118
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59"
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
EOF
@ -120,9 +120,9 @@ device: $D/zzz
unit: sectors
first-lba: 2048
last-lba: 4194270
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59"
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
EOF
@ -150,9 +150,9 @@ device: $D/zzz
unit: sectors
first-lba: 2048
last-lba: 6291422
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59"
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
$D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy"
@ -187,9 +187,9 @@ device: $D/zzz
unit: sectors
first-lba: 2048
last-lba: 6389726
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
$D/zzz1 : start= 2048, size= 591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first", attrs="GUID:59"
$D/zzz2 : start= 593904, size= 591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64", attrs="GUID:59"
$D/zzz3 : start= 1185760, size= 591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2", attrs="GUID:59"
$D/zzz4 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
$D/zzz5 : start= 1908696, size= 2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
$D/zzz6 : start= 4194264, size= 2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy"

View File

@ -630,8 +630,8 @@ DnsScopeMatch dns_scope_good_domain(
if (dns_name_endswith(domain, "invalid") > 0)
return DNS_SCOPE_NO;
/* Never go to network for the _gateway domain, it's something special, synthesized locally. */
if (is_gateway_hostname(domain))
/* Never go to network for the _gateway or _outbound domain — they're something special, synthesized locally. */
if (is_gateway_hostname(domain) || is_outbound_hostname(domain))
return DNS_SCOPE_NO;
switch (s->protocol) {
@ -739,6 +739,7 @@ DnsScopeMatch dns_scope_good_domain(
if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */
!is_gateway_hostname(domain) && /* don't resolve "_gateway" with LLMNR, let local synthesizing logic handle that */
!is_outbound_hostname(domain) && /* similar for "_outbound" */
dns_name_equal(domain, "local") == 0 && /* don't resolve "local" with LLMNR, it's the top-level domain of mDNS after all, see above */
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative

View File

@ -311,27 +311,33 @@ static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_add
return added;
}
static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
static int synthesize_gateway_rr(
Manager *m,
const DnsResourceKey *key,
int ifindex,
int (*lookup)(sd_netlink *context, int ifindex, int af, struct local_address **ret), /* either local_gateways() or local_outbound() */
DnsAnswer **answer) {
_cleanup_free_ struct local_address *addresses = NULL;
int n = 0, af, r;
assert(m);
assert(key);
assert(lookup);
assert(answer);
af = dns_type_to_af(key->type);
if (af >= 0) {
n = local_gateways(m->rtnl, ifindex, af, &addresses);
n = lookup(m->rtnl, ifindex, af, &addresses);
if (n < 0) /* < 0 means: error */
return n;
if (n == 0) { /* == 0 means we have no gateway */
/* See if there's a gateway on the other protocol */
if (af == AF_INET)
n = local_gateways(m->rtnl, ifindex, AF_INET6, NULL);
n = lookup(m->rtnl, ifindex, AF_INET6, NULL);
else {
assert(af == AF_INET6);
n = local_gateways(m->rtnl, ifindex, AF_INET, NULL);
n = lookup(m->rtnl, ifindex, AF_INET, NULL);
}
if (n <= 0) /* error (if < 0) or really no gateway at all (if == 0) */
return n;
@ -402,7 +408,7 @@ int dns_synthesize_answer(
} else if (is_gateway_hostname(name)) {
r = synthesize_gateway_rr(m, key, ifindex, &answer);
r = synthesize_gateway_rr(m, key, ifindex, local_gateways, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
if (r == 0) { /* if we have no gateway return NXDOMAIN */
@ -410,6 +416,16 @@ int dns_synthesize_answer(
continue;
}
} else if (is_outbound_hostname(name)) {
r = synthesize_gateway_rr(m, key, ifindex, local_outbounds, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize outbound RRs: %m");
if (r == 0) { /* if we have no gateway return NXDOMAIN */
nxdomain = true;
continue;
}
} else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) ||
dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) {
@ -431,6 +447,10 @@ int dns_synthesize_answer(
if (v == 0 && w == 0) /* This IP address is neither a local one nor a gateway */
continue;
/* Note that we never synthesize reverse PTR for _outbound, since those are local
* addresses and thus mapped to the local hostname anyway, hence they already have a
* mapping. */
} else
continue;

View File

@ -250,7 +250,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) {
_cleanup_free_ char *e = NULL;
e = shell_maybe_quote(str, ESCAPE_BACKSLASH_ONELINE);
e = shell_maybe_quote(str, 0);
if (!e)
return -ENOMEM;

View File

@ -181,7 +181,7 @@ static void log_job_error_with_service_result(const char* service, const char *r
assert(service);
service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
service_shell_quoted = shell_maybe_quote(service, 0);
if (!strv_isempty((char**) extra_args)) {
_cleanup_free_ char *t = NULL;

View File

@ -46,6 +46,7 @@
#include "path-util.h"
#include "process-util.h"
#include "raw-clone.h"
#include "resize-fs.h"
#include "signal-util.h"
#include "stat-util.h"
#include "stdio-util.h"
@ -534,7 +535,8 @@ int dissect_image(
sd_id128_t usr_uuid = SD_ID128_NULL, usr_verity_uuid = SD_ID128_NULL;
#endif
bool is_gpt, is_mbr, multiple_generic = false,
generic_rw = false; /* initialize to appease gcc */
generic_rw = false, /* initialize to appease gcc */
generic_growfs = false;
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
@ -795,7 +797,7 @@ int dissect_image(
int architecture = _ARCHITECTURE_INVALID;
const char *stype, *sid, *fstype = NULL, *label;
sd_id128_t type_id, id;
bool rw = true;
bool rw = true, growfs = false;
sid = blkid_partition_get_uuid(pp);
if (!sid)
@ -813,23 +815,25 @@ int dissect_image(
if (sd_id128_equal(type_id, GPT_HOME)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_HOME;
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
} else if (sd_id128_equal(type_id, GPT_SRV)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_SRV;
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
} else if (sd_id128_equal(type_id, GPT_ESP)) {
@ -846,18 +850,19 @@ int dissect_image(
} else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_XBOOTLDR;
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
}
#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);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
@ -869,6 +874,7 @@ int dissect_image(
designator = PARTITION_ROOT;
architecture = native_architecture();
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
} else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
@ -892,7 +898,7 @@ int dissect_image(
#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);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
@ -904,6 +910,7 @@ int dissect_image(
designator = PARTITION_ROOT_SECONDARY;
architecture = SECONDARY_ARCHITECTURE;
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
} else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
@ -927,7 +934,7 @@ int dissect_image(
#ifdef GPT_USR_NATIVE
else if (sd_id128_equal(type_id, GPT_USR_NATIVE)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
@ -939,6 +946,7 @@ int dissect_image(
designator = PARTITION_USR;
architecture = native_architecture();
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
} else if (sd_id128_equal(type_id, GPT_USR_NATIVE_VERITY)) {
@ -962,7 +970,7 @@ int dissect_image(
#ifdef GPT_USR_SECONDARY
else if (sd_id128_equal(type_id, GPT_USR_SECONDARY)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
@ -974,6 +982,7 @@ int dissect_image(
designator = PARTITION_USR_SECONDARY;
architecture = SECONDARY_ARCHITECTURE;
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
} else if (sd_id128_equal(type_id, GPT_USR_SECONDARY_VERITY)) {
@ -1006,7 +1015,7 @@ int dissect_image(
} else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
@ -1016,6 +1025,7 @@ int dissect_image(
else {
generic_nr = nr;
generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
generic_growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
generic_uuid = id;
generic_node = strdup(node);
if (!generic_node)
@ -1024,17 +1034,18 @@ int dissect_image(
} else if (sd_id128_equal(type_id, GPT_TMP)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
designator = PARTITION_TMP;
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
} else if (sd_id128_equal(type_id, GPT_VAR)) {
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY|GPT_FLAG_GROWFS);
if (pflags & GPT_FLAG_NO_AUTO)
continue;
@ -1062,6 +1073,7 @@ int dissect_image(
designator = PARTITION_VAR;
rw = !(pflags & GPT_FLAG_READ_ONLY);
growfs = FLAGS_SET(pflags, GPT_FLAG_GROWFS);
}
if (designator != _PARTITION_DESIGNATOR_INVALID) {
@ -1108,6 +1120,7 @@ int dissect_image(
.found = true,
.partno = nr,
.rw = rw,
.growfs = growfs,
.architecture = architecture,
.node = TAKE_PTR(n),
.fstype = TAKE_PTR(t),
@ -1131,6 +1144,7 @@ int dissect_image(
else {
generic_nr = nr;
generic_rw = true;
generic_growfs = false;
generic_node = strdup(node);
if (!generic_node)
return -ENOMEM;
@ -1166,6 +1180,7 @@ int dissect_image(
.found = true,
.partno = nr,
.rw = true,
.growfs = false,
.architecture = _ARCHITECTURE_INVALID,
.node = TAKE_PTR(n),
.uuid = id,
@ -1254,6 +1269,7 @@ int dissect_image(
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = generic_rw,
.growfs = generic_growfs,
.partno = generic_nr,
.architecture = _ARCHITECTURE_INVALID,
.node = TAKE_PTR(generic_node),
@ -1316,6 +1332,9 @@ int dissect_image(
if (p->fstype && fstype_is_ro(p->fstype))
p->rw = false;
if (!p->rw)
p->growfs = false;
}
*ret = TAKE_PTR(m);
@ -1411,6 +1430,43 @@ static int run_fsck(const char *node, const char *fstype) {
return 0;
}
static int fs_grow(const char *node_path, const char *mount_path) {
_cleanup_close_ int mount_fd = -1, node_fd = -1;
char fb[FORMAT_BYTES_MAX];
uint64_t size, newsize;
int r;
node_fd = open(node_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (node_fd < 0)
return log_debug_errno(errno, "Failed to open node device %s: %m", node_path);
if (ioctl(node_fd, BLKGETSIZE64, &size) != 0)
return log_debug_errno(errno, "Failed to get block device size of %s: %m", node_path);
mount_fd = open(mount_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (mount_fd < 0)
return log_debug_errno(errno, "Failed to open mountd file system %s: %m", mount_path);
log_debug("Resizing \"%s\" to %"PRIu64" bytes...", mount_path, size);
r = resize_fs(mount_fd, size, &newsize);
if (r < 0)
return log_debug_errno(r, "Failed to resize \"%s\" to %"PRIu64" bytes: %m", mount_path, size);
if (newsize == size)
log_debug("Successfully resized \"%s\" to %s bytes.",
mount_path,
format_bytes(fb, sizeof fb, newsize));
else {
assert(newsize < size);
log_debug("Successfully resized \"%s\" to %s bytes (%"PRIu64" bytes lost due to blocksize).",
mount_path,
format_bytes(fb, sizeof fb, newsize),
size - newsize);
}
return 0;
}
static int mount_partition(
DissectedPartition *m,
const char *where,
@ -1519,6 +1575,9 @@ static int mount_partition(
if (r < 0)
return r;
if (rw && m->growfs && FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))
(void) fs_grow(node, p);
return 1;
}

View File

@ -18,6 +18,7 @@ typedef struct VeritySettings VeritySettings;
struct DissectedPartition {
bool found:1;
bool rw:1;
bool growfs:1;
int partno; /* -1 if there was no partition and the images contains a file system directly */
int architecture; /* Intended architecture: either native, secondary or unset (-1). */
sd_id128_t uuid; /* Partition entry UUID as reported by the GPT */
@ -110,6 +111,7 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_MOUNT_READ_ONLY = 1 << 17, /* Make mounts read-only */
DISSECT_IMAGE_READ_ONLY = DISSECT_IMAGE_DEVICE_READ_ONLY |
DISSECT_IMAGE_MOUNT_READ_ONLY,
DISSECT_IMAGE_GROWFS = 1 << 18, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
} DissectImageFlags;
struct DissectedImage {

View File

@ -503,6 +503,9 @@ int generator_hook_up_growfs(
const char *unit_file;
int r;
assert(dir);
assert(where);
escaped = cescape(where);
if (!escaped)
return log_oom();
@ -534,9 +537,10 @@ int generator_hook_up_growfs(
"BindsTo=%%i.mount\n"
"Conflicts=shutdown.target\n"
"After=%%i.mount\n"
"Before=shutdown.target %s\n",
"Before=shutdown.target%s%s\n",
program_invocation_short_name,
target);
target ? " " : "",
strempty(target));
if (empty_or_root(where)) /* Make sure the root fs is actually writable before we resize it */
fprintf(f,

View File

@ -163,3 +163,14 @@ bool gpt_partition_type_knows_read_only(sd_id128_t id) {
gpt_partition_type_is_root_verity(id) || /* pretty much implied, but let's set the bit to make things really clear */
gpt_partition_type_is_usr_verity(id); /* ditto */
}
bool gpt_partition_type_knows_growfs(sd_id128_t id) {
return gpt_partition_type_is_root(id) ||
gpt_partition_type_is_usr(id) ||
sd_id128_in_set(id,
GPT_HOME,
GPT_SRV,
GPT_VAR,
GPT_TMP,
GPT_XBOOTLDR);
}

View File

@ -113,6 +113,7 @@
* but that's just because we saw no point in defining any other values here. */
#define GPT_FLAG_READ_ONLY (1ULL << 60)
#define GPT_FLAG_NO_AUTO (1ULL << 63)
#define GPT_FLAG_GROWFS (1ULL << 59)
const char *gpt_partition_type_uuid_to_string(sd_id128_t id);
const char *gpt_partition_type_uuid_to_string_harder(
@ -135,3 +136,4 @@ bool gpt_partition_type_is_usr(sd_id128_t id);
bool gpt_partition_type_is_usr_verity(sd_id128_t id);
bool gpt_partition_type_knows_read_only(sd_id128_t id);
bool gpt_partition_type_knows_growfs(sd_id128_t id);

View File

@ -1,8 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <net/if_arp.h>
#include "sd-netlink.h"
#include "alloc-util.h"
#include "fd-util.h"
#include "local-addresses.h"
#include "macro.h"
#include "netlink-util.h"
@ -33,7 +36,34 @@ static int address_compare(const struct local_address *a, const struct local_add
return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family));
}
int local_addresses(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
static void suppress_duplicates(struct local_address *list, size_t *n_list) {
size_t old_size, new_size;
/* Removes duplicate entries, assumes the list of addresses is already sorted. Updates in-place. */
if (*n_list < 2) /* list with less than two entries can't have duplicates */
return;
old_size = *n_list;
new_size = 1;
for (size_t i = 1; i < old_size; i++) {
if (address_compare(list + i, list + new_size - 1) == 0)
continue;
list[new_size++] = list[i];
}
*n_list = new_size;
}
int local_addresses(
sd_netlink *context,
int ifindex,
int af,
struct local_address **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_free_ struct local_address *list = NULL;
@ -135,6 +165,7 @@ int local_addresses(sd_netlink *context, int ifindex, int af, struct local_addre
if (ret) {
typesafe_qsort(list, n_list, address_compare);
suppress_duplicates(list, &n_list);
*ret = TAKE_PTR(list);
}
@ -171,7 +202,12 @@ static int add_local_gateway(
return 0;
}
int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
int local_gateways(
sd_netlink *context,
int ifindex,
int af,
struct local_address **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_free_ struct local_address *list = NULL;
@ -308,6 +344,151 @@ int local_gateways(sd_netlink *context, int ifindex, int af, struct local_addres
if (ret) {
typesafe_qsort(list, n_list, address_compare);
suppress_duplicates(list, &n_list);
*ret = TAKE_PTR(list);
}
return (int) n_list;
}
int local_outbounds(
sd_netlink *context,
int ifindex,
int af,
struct local_address **ret) {
_cleanup_free_ struct local_address *list = NULL, *gateways = NULL;
size_t n_list = 0, n_allocated = 0;
int r, n_gateways;
/* Determines our default outbound addresses, i.e. the "primary" local addresses we use to talk to IP
* addresses behind the default routes. This is still an address of the local host (i.e. this doesn't
* resolve NAT or so), but it's the set of addresses the local IP stack most likely uses to talk to
* other hosts.
*
* This works by connect()ing a SOCK_DGRAM socket to the local gateways, and then reading the IP
* address off the socket that was chosen for the routing decision. */
n_gateways = local_gateways(context, ifindex, af, &gateways);
if (n_gateways < 0)
return n_gateways;
if (n_gateways == 0) {
/* No gateways? Then we have no outbound addresses either. */
if (ret)
*ret = NULL;
return 0;
}
for (int i = 0; i < n_gateways; i++) {
_cleanup_close_ int fd = -1;
union sockaddr_union sa;
socklen_t salen;
fd = socket(gateways[i].family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;
switch (gateways[i].family) {
case AF_INET:
sa.in = (struct sockaddr_in) {
.sin_family = AF_INET,
.sin_addr = gateways[i].address.in,
.sin_port = htobe16(53), /* doesn't really matter which port we pick — we just care about the routing decision */
};
break;
case AF_INET6:
sa.in6 = (struct sockaddr_in6) {
.sin6_family = AF_INET6,
.sin6_addr = gateways[i].address.in6,
.sin6_port = htobe16(53),
.sin6_scope_id = gateways[i].ifindex,
};
break;
default:
assert_not_reached("Unexpected protocol");
}
/* So ideally we'd just use IP_UNICAST_IF here to pass the ifindex info to the kernel before
* connect()ing, sot that it influences the routing decision. However, on current kernels
* IP_UNICAST_IF doesn't actually influence the routing decision for UDP which I think
* should probably just be considered a bug. Once that bug is fixed this is the best API to
* use, since it is the most lightweight. */
r = socket_set_unicast_if(fd, gateways[i].family, gateways[i].ifindex);
if (r < 0)
log_debug_errno(r, "Failed to set unicast interface index %i, ignoring: %m", gateways[i].ifindex);
/* We'll also use SO_BINDTOINDEX. This requires CAP_NET_RAW on old kernels, hence there's a
* good chance this fails. Since 5.7 this restriction was dropped and the first
* SO_BINDTOINDEX on a socket may be done without privileges. This one has the benefit of
* really influencing the routing decision, i.e. this one definitely works for us as long
* as we have the privileges for it.*/
r = socket_bind_to_ifindex(fd, gateways[i].ifindex);
if (r < 0)
log_debug_errno(r, "Failed to bind socket to interface %i, ignoring: %m", gateways[i].ifindex);
/* Let's now connect() to the UDP socket, forcing the kernel to make a routing decision and
* auto-bind the socket. We ignore failures on this, since that failure might happen for a
* multitude of reasons (policy/firewall issues, who knows?) and some of them might be
* *after* the routing decision and the auto-binding already took place. If so we can still
* make use of the binding and return it. Hence, let's not unnecessarily fail early here: we
* can still easily detect if the auto-binding worked or not, by comparing the bound IP
* address with zero which we do below. */
if (connect(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0)
log_debug_errno(errno, "Failed to connect SOCK_DGRAM socket to gateway, ignoring: %m");
/* Let's now read the socket address of the socket. A routing decision should have been
* made. Let's verify that and use the data. */
salen = SOCKADDR_LEN(sa);
if (getsockname(fd, &sa.sa, &salen) < 0)
return -errno;
assert(sa.sa.sa_family == gateways[i].family);
assert(salen == SOCKADDR_LEN(sa));
switch (gateways[i].family) {
case AF_INET:
if (in4_addr_is_null(&sa.in.sin_addr)) /* Auto-binding didn't work. :-( */
continue;
if (!GREEDY_REALLOC(list, n_allocated, n_list+1))
return -ENOMEM;
list[n_list++] = (struct local_address) {
.family = gateways[i].family,
.ifindex = gateways[i].ifindex,
.address.in = sa.in.sin_addr,
};
break;
case AF_INET6:
if (in6_addr_is_null(&sa.in6.sin6_addr))
continue;
if (!GREEDY_REALLOC(list, n_allocated, n_list+1))
return -ENOMEM;
list[n_list++] = (struct local_address) {
.family = gateways[i].family,
.ifindex = gateways[i].ifindex,
.address.in6 = sa.in6.sin6_addr,
};
break;
default:
assert_not_reached("Unexpected protocol");
}
}
if (ret) {
typesafe_qsort(list, n_list, address_compare);
suppress_duplicates(list, &n_list);
*ret = TAKE_PTR(list);
}

View File

@ -15,3 +15,5 @@ struct local_address {
int local_addresses(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret);
int local_gateways(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret);
int local_outbounds(sd_netlink *rtnl, int ifindex, int af, struct local_address **ret);

View File

@ -182,12 +182,13 @@ static int apply_all(OrderedHashmap *sysctl_options) {
static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *pp = NULL;
unsigned c = 0;
int r;
assert(path);
r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("sysctl.d"), &f);
r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("sysctl.d"), &f, &pp);
if (r < 0) {
if (ignore_enoent && r == -ENOENT)
return 0;
@ -195,7 +196,7 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig
return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
}
log_debug("Parsing %s", path);
log_debug("Parsing %s", pp);
for (;;) {
_cleanup_(option_freep) Option *new_option = NULL;
_cleanup_free_ char *l = NULL;
@ -208,7 +209,7 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig
if (k == 0)
break;
if (k < 0)
return log_error_errno(k, "Failed to read file '%s', ignoring: %m", path);
return log_error_errno(k, "Failed to read file '%s', ignoring: %m", pp);
c++;
@ -235,7 +236,7 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig
/* We have a "negative match" option. Let's continue with value==NULL. */
p++;
else {
log_syntax(NULL, LOG_WARNING, path, c, 0,
log_syntax(NULL, LOG_WARNING, pp, c, 0,
"Line is not an assignment, ignoring: %s", p);
if (r == 0)
r = -EINVAL;
@ -261,7 +262,7 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig
continue;
}
log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c);
log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, pp, c);
option_free(ordered_hashmap_remove(*sysctl_options, p));
}

View File

@ -17,7 +17,7 @@ static int print_variable(const char *s) {
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
"Invalid environment block");
esc = shell_maybe_quote(sep + 1, ESCAPE_POSIX);
esc = shell_maybe_quote(sep + 1, SHELL_ESCAPE_POSIX);
if (!esc)
return log_oom();

View File

@ -1728,6 +1728,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
static int read_config_file(const char *fn, bool ignore_enoent) {
_cleanup_fclose_ FILE *rf = NULL;
_cleanup_free_ char *pp = NULL;
FILE *f = NULL;
unsigned v = 0;
int r = 0;
@ -1737,7 +1738,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
if (streq(fn, "-"))
f = stdin;
else {
r = search_and_fopen(fn, "re", arg_root, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf);
r = search_and_fopen(fn, "re", arg_root, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf, &pp);
if (r < 0) {
if (ignore_enoent && r == -ENOENT)
return 0;
@ -1746,6 +1747,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
}
f = rf;
fn = pp;
}
for (;;) {
@ -1997,7 +1999,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK,
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
&unlink_dir,
&loop_device,
&decrypted_image);

View File

@ -24,13 +24,14 @@ static void test_xescape_full(bool eight_bits) {
"a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb" :
"a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\177\234\313";
const unsigned full_fit = !eight_bits ? 55 : 46;
XEscapeFlags flags = eight_bits * XESCAPE_8_BIT;
for (unsigned i = 0; i < 60; i++) {
_cleanup_free_ char *t;
_cleanup_free_ char *t, *q;
assert_se(t = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, eight_bits));
assert_se(t = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, flags));
log_info("%02d: %s", i, t);
log_info("%02d: <%s>", i, t);
if (i >= full_fit)
assert_se(streq(t, escaped));
@ -44,6 +45,15 @@ static void test_xescape_full(bool eight_bits) {
assert_se(strlen(t) == i);
assert_se(strneq(t, "...", i));
}
assert_se(q = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i,
flags | XESCAPE_FORCE_ELLIPSIS));
log_info("%02d: <%s>", i, q);
if (i > 0)
assert_se(endswith(q, "."));
assert(strlen(q) <= i);
assert(strlen(q) + 3 >= strlen(t));
}
}
@ -118,6 +128,7 @@ static void test_shell_escape_one(const char *s, const char *bad, const char *ex
_cleanup_free_ char *r;
assert_se(r = shell_escape(s, bad));
log_debug("%s → %s (expected %s)", s, r, expected);
assert_se(streq_ptr(r, expected));
}
@ -127,58 +138,58 @@ static void test_shell_escape(void) {
test_shell_escape_one("foobar", "", "foobar");
test_shell_escape_one("foobar", "o", "f\\o\\obar");
test_shell_escape_one("foo:bar,baz", ",:", "foo\\:bar\\,baz");
test_shell_escape_one("foo\nbar\nbaz", ",:", "foo\\nbar\\nbaz");
}
static void test_shell_maybe_quote_one(const char *s,
EscapeStyle style,
const char *expected) {
static void test_shell_maybe_quote_one(const char *s, ShellEscapeFlags flags, const char *expected) {
_cleanup_free_ char *ret = NULL;
assert_se(ret = shell_maybe_quote(s, style));
assert_se(ret = shell_maybe_quote(s, flags));
log_debug("[%s] → [%s] (%s)", s, ret, expected);
assert_se(streq(ret, expected));
}
static void test_shell_maybe_quote(void) {
test_shell_maybe_quote_one("", ESCAPE_BACKSLASH, "");
test_shell_maybe_quote_one("", ESCAPE_BACKSLASH_ONELINE, "");
test_shell_maybe_quote_one("", ESCAPE_POSIX, "");
test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH, "\"\\\\\"");
test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH_ONELINE, "\"\\\\\"");
test_shell_maybe_quote_one("\\", ESCAPE_POSIX, "$'\\\\'");
test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH, "\"\\\"\"");
test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH_ONELINE, "\"\\\"\"");
test_shell_maybe_quote_one("\"", ESCAPE_POSIX, "$'\"'");
test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH, "foobar");
test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH_ONELINE, "foobar");
test_shell_maybe_quote_one("foobar", ESCAPE_POSIX, "foobar");
test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH, "\"foo bar\"");
test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH_ONELINE, "\"foo bar\"");
test_shell_maybe_quote_one("foo bar", ESCAPE_POSIX, "$'foo bar'");
test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH, "\"foo\tbar\"");
test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\tbar\"");
test_shell_maybe_quote_one("foo\tbar", ESCAPE_POSIX, "$'foo\\tbar'");
test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH, "\"foo\nbar\"");
test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\nbar\"");
test_shell_maybe_quote_one("foo\nbar", ESCAPE_POSIX, "$'foo\\nbar'");
test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH, "\"foo \\\"bar\\\" waldo\"");
test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH_ONELINE, "\"foo \\\"bar\\\" waldo\"");
test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_POSIX, "$'foo \"bar\" waldo'");
test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", ESCAPE_POSIX, "$'foo$bar'");
test_shell_maybe_quote_one("", 0, "");
test_shell_maybe_quote_one("", SHELL_ESCAPE_EMPTY, "\"\"");
test_shell_maybe_quote_one("", SHELL_ESCAPE_POSIX, "");
test_shell_maybe_quote_one("", SHELL_ESCAPE_POSIX | SHELL_ESCAPE_EMPTY, "\"\"");
test_shell_maybe_quote_one("\\", 0, "\"\\\\\"");
test_shell_maybe_quote_one("\\", SHELL_ESCAPE_POSIX, "$'\\\\'");
test_shell_maybe_quote_one("\"", 0, "\"\\\"\"");
test_shell_maybe_quote_one("\"", SHELL_ESCAPE_POSIX, "$'\"'");
test_shell_maybe_quote_one("foobar", 0, "foobar");
test_shell_maybe_quote_one("foobar", SHELL_ESCAPE_POSIX, "foobar");
test_shell_maybe_quote_one("foo bar", 0, "\"foo bar\"");
test_shell_maybe_quote_one("foo bar", SHELL_ESCAPE_POSIX, "$'foo bar'");
test_shell_maybe_quote_one("foo\tbar", 0, "\"foo\\tbar\"");
test_shell_maybe_quote_one("foo\tbar", SHELL_ESCAPE_POSIX, "$'foo\\tbar'");
test_shell_maybe_quote_one("foo\nbar", 0, "\"foo\\nbar\"");
test_shell_maybe_quote_one("foo\nbar", SHELL_ESCAPE_POSIX, "$'foo\\nbar'");
test_shell_maybe_quote_one("foo \"bar\" waldo", 0, "\"foo \\\"bar\\\" waldo\"");
test_shell_maybe_quote_one("foo \"bar\" waldo", SHELL_ESCAPE_POSIX, "$'foo \"bar\" waldo'");
test_shell_maybe_quote_one("foo$bar", 0, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_EMPTY, "\"foo\\$bar\"");
test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_POSIX, "$'foo$bar'");
test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_POSIX | SHELL_ESCAPE_EMPTY, "$'foo$bar'");
/* Note that current users disallow control characters, so this "test"
* is here merely to establish current behaviour. If control characters
* were allowed, they should be quoted, i.e. \001 should become \\001. */
test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH, "\"a\nb\001\"");
test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH_ONELINE, "\"a\\nb\001\"");
test_shell_maybe_quote_one("a\nb\001", ESCAPE_POSIX, "$'a\\nb\001'");
/* Exclamation mark is special in the interactive shell, but we don't treat it so. */
test_shell_maybe_quote_one("foo!bar", 0, "\"foo!bar\"");
test_shell_maybe_quote_one("foo!bar", SHELL_ESCAPE_POSIX, "$'foo!bar'");
test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH, "\"foo!bar\"");
test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH_ONELINE, "\"foo!bar\"");
test_shell_maybe_quote_one("foo!bar", ESCAPE_POSIX, "$'foo!bar'");
/* Control characters and unicode */
test_shell_maybe_quote_one("a\nb\001", 0, "\"a\\nb\\001\"");
test_shell_maybe_quote_one("a\nb\001", SHELL_ESCAPE_POSIX, "$'a\\nb\\001'");
test_shell_maybe_quote_one("głąb", 0, "głąb");
test_shell_maybe_quote_one("głąb", SHELL_ESCAPE_POSIX, "głąb");
test_shell_maybe_quote_one("głąb\002\003", 0, "\"głąb\\002\\003\"");
test_shell_maybe_quote_one("głąb\002\003", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003'");
test_shell_maybe_quote_one("głąb\002\003rząd", 0, "\"głąb\\002\\003rząd\"");
test_shell_maybe_quote_one("głąb\002\003rząd", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003rząd'");
}
int main(int argc, char *argv[]) {

View File

@ -15,6 +15,7 @@
#include "fs-util.h"
#include "io-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
#include "rm-rf.h"
@ -321,6 +322,8 @@ static void test_executable_is_script(void) {
char *command;
int r;
log_info("/* %s */", __func__);
assert_se(fmkostemp_safe(t, "w", &f) == 0);
fputs("#! /bin/script -a -b \ngoo goo", f);
fflush(f);
@ -346,6 +349,8 @@ static void test_status_field(void) {
unsigned long long total = 0, buffers = 0;
int r;
log_info("/* %s */", __func__);
assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0);
puts(t);
assert_se(streq(t, "1"));
@ -377,11 +382,11 @@ static void test_status_field(void) {
}
static void test_capeff(void) {
int pid, p;
log_info("/* %s */", __func__);
for (pid = 0; pid < 2; pid++) {
for (int pid = 0; pid < 2; pid++) {
_cleanup_free_ char *capeff = NULL;
int r;
int r, p;
r = get_process_capeff(0, &capeff);
log_info("capeff: '%s' (r=%d)", capeff, r);
@ -402,6 +407,8 @@ static void test_write_string_stream(void) {
int fd;
char buf[64];
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@ -436,6 +443,8 @@ static void test_write_string_file(void) {
char buf[64] = {};
_cleanup_close_ int fd;
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@ -450,6 +459,8 @@ static void test_write_string_file_no_create(void) {
_cleanup_close_ int fd;
char buf[64] = {};
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@ -464,6 +475,8 @@ static void test_write_string_file_verify(void) {
_cleanup_free_ char *buf = NULL, *buf2 = NULL;
int r;
log_info("/* %s */", __func__);
r = read_one_line_file("/proc/version", &buf);
if (ERRNO_IS_PRIVILEGE(r))
return;
@ -490,6 +503,8 @@ static void test_load_env_file_pairs(void) {
_cleanup_strv_free_ char **l = NULL;
char **k, **v;
log_info("/* %s */", __func__);
fd = mkostemp_safe(fn);
assert_se(fd >= 0);
@ -525,69 +540,97 @@ static void test_load_env_file_pairs(void) {
}
static void test_search_and_fopen(void) {
const char *dirs[] = {"/tmp/foo/bar", "/tmp", NULL};
static const char* const dirs[] = {
"/tmp/foo/bar",
"/tmp",
NULL
};
char name[] = "/tmp/test-search_and_fopen.XXXXXX";
int fd, r;
FILE *f;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
const char *e;
int r;
log_info("/* %s */", __func__);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
close(fd);
fd = safe_close(fd);
r = search_and_fopen(basename(name), "r", NULL, dirs, &f);
r = search_and_fopen(basename(name), "re", NULL, (const char**) dirs, &f, &p);
assert_se(r >= 0);
fclose(f);
assert_se(e = path_startswith(p, "/tmp/"));
assert_se(streq(basename(name), e));
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen(name, "r", NULL, dirs, &f);
r = search_and_fopen(name, "re", NULL, (const char**) dirs, &f, &p);
assert_se(r >= 0);
fclose(f);
assert_se(path_equal(name, p));
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen(basename(name), "r", "/", dirs, &f);
r = search_and_fopen(basename(name), "re", "/", (const char**) dirs, &f, &p);
assert_se(r >= 0);
fclose(f);
assert_se(e = path_startswith(p, "/tmp/"));
assert_se(streq(basename(name), e));
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f);
assert_se(r < 0);
r = search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f);
assert_se(r < 0);
r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL, (const char**) dirs, &f, &p);
assert_se(r == -ENOENT);
r = search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL, (const char**) dirs, &f, &p);
assert_se(r == -ENOENT);
r = unlink(name);
assert_se(r == 0);
r = search_and_fopen(basename(name), "r", NULL, dirs, &f);
assert_se(r < 0);
r = search_and_fopen(basename(name), "r", NULL, (const char**) dirs, &f, &p);
assert_se(r == -ENOENT);
}
static void test_search_and_fopen_nulstr(void) {
const char dirs[] = "/tmp/foo/bar\0/tmp\0";
static const char dirs[] =
"/tmp/foo/bar\0"
"/tmp\0";
log_info("/* %s */", __func__);
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-search_and_fopen.XXXXXX";
int fd, r;
FILE *f;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
const char *e;
int r;
fd = mkostemp_safe(name);
assert_se(fd >= 0);
close(fd);
fd = safe_close(fd);
r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f);
r = search_and_fopen_nulstr(basename(name), "re", NULL, dirs, &f, &p);
assert_se(r >= 0);
fclose(f);
assert_se(e = path_startswith(p, "/tmp/"));
assert_se(streq(basename(name), e));
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen_nulstr(name, "r", NULL, dirs, &f);
r = search_and_fopen_nulstr(name, "re", NULL, dirs, &f, &p);
assert_se(r >= 0);
fclose(f);
assert_se(path_equal(name, p));
f = safe_fclose(f);
p = mfree(p);
r = search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f);
assert_se(r < 0);
r = search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f);
assert_se(r < 0);
r = search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f, &p);
assert_se(r == -ENOENT);
r = search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f, &p);
assert_se(r == -ENOENT);
r = unlink(name);
assert_se(r == 0);
r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f);
assert_se(r < 0);
r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f, &p);
assert_se(r == -ENOENT);
}
static void test_writing_tmpfile(void) {
@ -595,12 +638,15 @@ static void test_writing_tmpfile(void) {
_cleanup_free_ char *contents = NULL;
size_t size;
_cleanup_close_ int fd = -1;
struct iovec iov[3];
int r;
iov[0] = IOVEC_MAKE_STRING("abc\n");
iov[1] = IOVEC_MAKE_STRING(ALPHANUMERICAL "\n");
iov[2] = IOVEC_MAKE_STRING("");
log_info("/* %s */", __func__);
struct iovec iov[] = {
IOVEC_MAKE_STRING("abc\n"),
IOVEC_MAKE_STRING(ALPHANUMERICAL "\n"),
IOVEC_MAKE_STRING(""),
};
fd = mkostemp_safe(name);
printf("tmpfile: %s", name);
@ -617,6 +663,8 @@ static void test_writing_tmpfile(void) {
static void test_tempfn(void) {
char *ret = NULL, *p;
log_info("/* %s */", __func__);
assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0);
assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX"));
free(ret);
@ -659,8 +707,7 @@ static void test_fgetc(void) {
_cleanup_fclose_ FILE *f = NULL;
char c;
f = fmemopen_unlocked((void*) chars, sizeof(chars), "re");
assert_se(f);
assert_se(f = fmemopen_unlocked((void*) chars, sizeof(chars), "re"));
for (size_t i = 0; i < sizeof(chars); i++) {
assert_se(safe_fgetc(f, &c) == 1);
@ -753,9 +800,9 @@ static void test_read_line_one_file(FILE *f) {
static void test_read_line(void) {
_cleanup_fclose_ FILE *f = NULL;
f = fmemopen_unlocked((void*) buffer, sizeof(buffer), "re");
assert_se(f);
log_info("/* %s */", __func__);
assert_se(f = fmemopen_unlocked((void*) buffer, sizeof(buffer), "re"));
test_read_line_one_file(f);
}
@ -764,6 +811,8 @@ static void test_read_line2(void) {
int fd;
_cleanup_fclose_ FILE *f = NULL;
log_info("/* %s */", __func__);
fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se((size_t) write(fd, buffer, sizeof(buffer)) == sizeof(buffer));
@ -779,6 +828,8 @@ static void test_read_line3(void) {
_cleanup_free_ char *line = NULL;
int r;
log_info("/* %s */", __func__);
f = fopen("/proc/uptime", "re");
if (!f && IN_SET(errno, ENOENT, EPERM))
return;
@ -811,10 +862,9 @@ static void test_read_line4(void) {
{ 6, "foo\n\r\0" },
};
size_t i;
int r;
for (i = 0; i < ELEMENTSOF(eof_endings); i++) {
for (size_t i = 0; i < ELEMENTSOF(eof_endings); i++) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *s = NULL;
@ -839,6 +889,8 @@ static void test_read_nul_string(void) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *s = NULL;
log_info("/* %s */", __func__);
assert_se(f = fmemopen_unlocked((void*) test, sizeof(test)-1, "r"));
assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 13 && streq_ptr(s, "string nr. 1"));
@ -928,6 +980,8 @@ static void test_read_full_file_offset_size(void) {
size_t rbuf_size;
uint8_t buf[4711];
log_info("/* %s */", __func__);
random_bytes(buf, sizeof(buf));
assert_se(tempfn_random_child(NULL, NULL, &fn) >= 0);
@ -965,10 +1019,12 @@ static void test_read_full_file_offset_size(void) {
rbuf = mfree(rbuf);
}
static void test_read_full_virtual_file(void) {
static void test_read_virtual_file(size_t max_size) {
const char *filename;
int r;
log_info("/* %s (max_size=%zu) */", __func__, max_size);
FOREACH_STRING(filename,
"/proc/1/cmdline",
"/etc/nsswitch.conf",
@ -977,8 +1033,8 @@ static void test_read_full_virtual_file(void) {
_cleanup_free_ char *buf = NULL;
size_t size = 0;
r = read_full_virtual_file(filename, &buf, &size);
log_info_errno(r, "read_full_virtual_file(\"%s\"): %m (%zu bytes)", filename, size);
r = read_virtual_file(filename, max_size, &buf, &size);
log_info_errno(r, "read_virtual_file(\"%s\", %zu): %m (%zu bytes)", filename, max_size, size);
assert_se(r == 0 || ERRNO_IS_PRIVILEGE(r) || r == -ENOENT);
}
}
@ -1010,7 +1066,9 @@ int main(int argc, char *argv[]) {
test_read_nul_string();
test_read_full_file_socket();
test_read_full_file_offset_size();
test_read_full_virtual_file();
test_read_virtual_file(20);
test_read_virtual_file(4096);
test_read_virtual_file(SIZE_MAX);
return 0;
}

View File

@ -40,5 +40,12 @@ int main(int argc, char *argv[]) {
print_local_addresses(a, (unsigned) n);
free(a);
n = local_outbounds(NULL, 0, AF_UNSPEC, &a);
assert_se(n >= 0);
printf("Local Outbounds:\n");
print_local_addresses(a, (unsigned) n);
free(a);
return 0;
}

View File

@ -455,8 +455,7 @@ static int parse_argv(int argc, char **argv,
} else {
_cleanup_free_ char *hostname;
assert_se(hostname = gethostname_malloc());
assert_se(names = strv_new("localhost", "_gateway", "foo_no_such_host", hostname));
assert_se(names = strv_new("localhost", "_gateway", "_outbound", "foo_no_such_host", hostname));
n = make_addresses(&addrs);
assert_se(n >= 0);

View File

@ -15,6 +15,8 @@
#include "alloc-util.h"
#include "architecture.h"
#include "errno-util.h"
#include "errno-list.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "log.h"
#include "macro.h"
@ -43,6 +45,8 @@ static void test_get_process_comm(pid_t pid) {
dev_t h;
int r;
log_info("/* %s */", __func__);
xsprintf(path, "/proc/"PID_FMT"/comm", pid);
if (stat(path, &st) == 0) {
@ -88,15 +92,61 @@ static void test_get_process_comm(pid_t pid) {
log_info("PID"PID_FMT" $PATH: '%s'", pid, strna(i));
}
static void test_get_process_cmdline_one(pid_t pid) {
_cleanup_free_ char *c = NULL, *d = NULL, *e = NULL, *f = NULL, *g = NULL, *h = NULL;
int r;
r = get_process_cmdline(pid, SIZE_MAX, 0, &c);
log_info("PID "PID_FMT": %s", pid, r >= 0 ? c : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &d);
log_info(" %s", r >= 0 ? d : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &e);
log_info(" %s", r >= 0 ? e : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE | PROCESS_CMDLINE_COMM_FALLBACK, &f);
log_info(" %s", r >= 0 ? f : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &g);
log_info(" %s", r >= 0 ? g : errno_to_name(r));
r = get_process_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX | PROCESS_CMDLINE_COMM_FALLBACK, &h);
log_info(" %s", r >= 0 ? h : errno_to_name(r));
}
static void test_get_process_cmdline(void) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
log_info("/* %s */", __func__);
assert_se(d = opendir("/proc"));
FOREACH_DIRENT(de, d, return) {
pid_t pid;
dirent_ensure_type(d, de);
if (de->d_type != DT_DIR)
continue;
if (parse_pid(de->d_name, &pid) < 0)
continue;
test_get_process_cmdline_one(pid);
}
}
static void test_get_process_comm_escape_one(const char *input, const char *output) {
_cleanup_free_ char *n = NULL;
log_info("input: <%s> — output: <%s>", input, output);
log_debug("input: <%s> — output: <%s>", input, output);
assert_se(prctl(PR_SET_NAME, input) >= 0);
assert_se(get_process_comm(0, &n) >= 0);
log_info("got: <%s>", n);
log_debug("got: <%s>", n);
assert_se(streq_ptr(n, output));
}
@ -104,6 +154,8 @@ static void test_get_process_comm_escape_one(const char *input, const char *outp
static void test_get_process_comm_escape(void) {
_cleanup_free_ char *saved = NULL;
log_info("/* %s */", __func__);
assert_se(get_process_comm(0, &saved) >= 0);
test_get_process_comm_escape_one("", "");
@ -140,6 +192,8 @@ static void test_pid_is_unwaited(void) {
static void test_pid_is_alive(void) {
pid_t pid;
log_info("/* %s */", __func__);
pid = fork();
assert_se(pid >= 0);
if (pid == 0) {
@ -155,6 +209,7 @@ static void test_pid_is_alive(void) {
}
static void test_personality(void) {
log_info("/* %s */", __func__);
assert_se(personality_to_string(PER_LINUX));
assert_se(!personality_to_string(PERSONALITY_INVALID));
@ -183,6 +238,8 @@ static void test_get_process_cmdline_harder(void) {
_cleanup_free_ char *line = NULL;
pid_t pid;
log_info("/* %s */", __func__);
if (geteuid() != 0) {
log_info("Skipping %s: not root", __func__);
return;
@ -245,151 +302,219 @@ static void test_get_process_cmdline_harder(void) {
assert_se(prctl(PR_SET_NAME, "testa") >= 0);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[testa]"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_info("'%s'", line);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK | PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "\"[testa]\"")); /* quoting is enabled here */
line = mfree(line);
assert_se(get_process_cmdline(0, 0, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, ""));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, ""));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[t…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[te…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[tes…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[test…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[testa]"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "[testa]"));
line = mfree(line);
/* Test with multiple arguments that don't require quoting */
assert_se(write(fd, "foo\0bar", 8) == 8);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
log_info("'%s'", line);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(streq(line, "foo bar"));
line = mfree(line);
assert_se(write(fd, "quux", 4) == 4);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) >= 0);
log_info("'%s'", line);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 1, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, ""));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 2, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "f…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 3, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "fo…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 4, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 5, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo …"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 6, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo b…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 7, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo ba…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 8, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 9, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar …"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar q…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar qu…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 13, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 14, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 1000, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "foo bar quux"));
line = mfree(line);
assert_se(ftruncate(fd, 0) >= 0);
assert_se(prctl(PR_SET_NAME, "aaaa bbbb cccc") >= 0);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(0, SIZE_MAX, 0, &line) == -ENOENT);
assert_se(get_process_cmdline(getpid_cached(), SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbbb cccc]"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 10, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbb…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 11, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbbb…"));
line = mfree(line);
assert_se(get_process_cmdline(getpid_cached(), 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
assert_se(get_process_cmdline(0, 12, PROCESS_CMDLINE_COMM_FALLBACK, &line) >= 0);
log_debug("'%s'", line);
assert_se(streq(line, "[aaaa bbbb …"));
line = mfree(line);
/* Test with multiple arguments that do require quoting */
#define CMDLINE1 "foo\0'bar'\0\"bar$\"\0x y z\0!``\0"
#define EXPECT1 "foo \"'bar'\" \"\\\"bar\\$\\\"\" \"x y z\" \"!\\`\\`\" \"\""
#define EXPECT1p "foo $'\\'bar\\'' $'\"bar$\"' $'x y z' $'!``' \"\""
assert_se(lseek(fd, SEEK_SET, 0) == 0);
assert_se(write(fd, CMDLINE1, sizeof CMDLINE1) == sizeof CMDLINE1);
assert_se(ftruncate(fd, sizeof CMDLINE1) == 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT1);
assert_se(streq(line, EXPECT1));
line = mfree(line);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT1p);
assert_se(streq(line, EXPECT1p));
line = mfree(line);
#define CMDLINE2 "foo\0\1\2\3\0\0"
#define EXPECT2 "foo \"\\001\\002\\003\" \"\" \"\""
#define EXPECT2p "foo $'\\001\\002\\003' \"\" \"\""
assert_se(lseek(fd, SEEK_SET, 0) == 0);
assert_se(write(fd, CMDLINE2, sizeof CMDLINE2) == sizeof CMDLINE2);
assert_se(ftruncate(fd, sizeof CMDLINE2) == 0);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT2);
assert_se(streq(line, EXPECT2));
line = mfree(line);
assert_se(get_process_cmdline(0, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &line) >= 0);
log_debug("got: ==%s==", line);
log_debug("exp: ==%s==", EXPECT2p);
assert_se(streq(line, EXPECT2p));
line = mfree(line);
safe_close(fd);
_exit(EXIT_SUCCESS);
}
@ -398,11 +523,15 @@ static void test_rename_process_now(const char *p, int ret) {
_cleanup_free_ char *comm = NULL, *cmdline = NULL;
int r;
log_info("/* %s */", __func__);
r = rename_process(p);
assert_se(r == ret ||
(ret == 0 && r >= 0) ||
(ret > 0 && r > 0));
log_debug_errno(r, "rename_process(%s): %m", p);
if (r < 0)
return;
@ -413,7 +542,7 @@ static void test_rename_process_now(const char *p, int ret) {
#endif
assert_se(get_process_comm(0, &comm) >= 0);
log_info("comm = <%s>", comm);
log_debug("comm = <%s>", comm);
assert_se(strneq(comm, p, TASK_COMM_LEN-1));
/* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the
* future. We'd only check the initial part, at least until we recompile, but this will still pass. */
@ -425,9 +554,12 @@ static void test_rename_process_now(const char *p, int ret) {
if (r == 0 && detect_container() > 0)
log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline);
else {
log_info("cmdline = <%s>", cmdline);
assert_se(strneq(p, cmdline, STRLEN("test-process-util")));
assert_se(startswith(p, cmdline));
log_info("cmdline = <%s> (expected <%.*s>)", cmdline, (int) strlen("test-process-util"), p);
bool skip = cmdline[0] == '"'; /* A shortcut to check if the string is quoted */
assert_se(strneq(cmdline + skip, p, strlen("test-process-util")));
assert_se(startswith(cmdline + skip, p));
}
} else
log_info("cmdline = <%s> (not verified)", cmdline);
@ -437,6 +569,8 @@ static void test_rename_process_one(const char *p, int ret) {
siginfo_t si;
pid_t pid;
log_info("/* %s */", __func__);
pid = fork();
assert_se(pid >= 0);
@ -491,6 +625,8 @@ static void test_getpid_cached(void) {
siginfo_t si;
pid_t a, b, c, d, e, f, child;
log_info("/* %s */", __func__);
a = raw_getpid();
b = getpid_cached();
c = getpid();
@ -521,25 +657,28 @@ static void test_getpid_cached(void) {
assert_se(si.si_code == CLD_EXITED);
}
#define MEASURE_ITERATIONS (10000000LLU)
static void test_getpid_measure(void) {
unsigned long long i;
usec_t t, q;
unsigned long long iterations = slow_tests_enabled() ? 1000000 : 1000;
log_info("/* %s (%llu iterations) */", __func__, iterations);
t = now(CLOCK_MONOTONIC);
for (i = 0; i < MEASURE_ITERATIONS; i++)
for (unsigned long long i = 0; i < iterations; i++)
(void) getpid();
q = now(CLOCK_MONOTONIC) - t;
log_info(" glibc getpid(): %lf µs each\n", (double) q / MEASURE_ITERATIONS);
log_info(" glibc getpid(): %lf µs each\n", (double) q / iterations);
iterations *= 50; /* _cached() is about 50 times faster, so we need more iterations */
t = now(CLOCK_MONOTONIC);
for (i = 0; i < MEASURE_ITERATIONS; i++)
for (unsigned long long i = 0; i < iterations; i++)
(void) getpid_cached();
q = now(CLOCK_MONOTONIC) - t;
log_info("getpid_cached(): %lf µs each\n", (double) q / MEASURE_ITERATIONS);
log_info("getpid_cached(): %lf µs each\n", (double) q / iterations);
}
static void test_safe_fork(void) {
@ -547,6 +686,8 @@ static void test_safe_fork(void) {
pid_t pid;
int r;
log_info("/* %s */", __func__);
BLOCK_SIGNALS(SIGCHLD);
r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid);
@ -595,6 +736,8 @@ static void test_ioprio_class_from_to_string_one(const char *val, int expected)
}
static void test_ioprio_class_from_to_string(void) {
log_info("/* %s */", __func__);
test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE);
test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT);
test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE);
@ -610,7 +753,10 @@ static void test_ioprio_class_from_to_string(void) {
static void test_setpriority_closest(void) {
int r;
r = safe_fork("(test-setprio)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
log_info("/* %s */", __func__);
r = safe_fork("(test-setprio)",
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
assert_se(r >= 0);
if (r == 0) {
@ -694,7 +840,8 @@ static void test_setpriority_closest(void) {
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
log_show_color(true);
test_setup_logging(LOG_INFO);
save_argc_argv(argc, argv);
@ -709,6 +856,7 @@ int main(int argc, char *argv[]) {
}
test_get_process_comm_escape();
test_get_process_cmdline();
test_pid_is_unwaited();
test_pid_is_alive();
test_personality();

View File

@ -3,6 +3,7 @@
#include "alloc-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
#include "utf8.h"
#include "util.h"
@ -91,15 +92,15 @@ static void test_utf8_escape_invalid(void) {
log_info("/* %s */", __func__);
p1 = utf8_escape_invalid("goo goo goo");
puts(p1);
log_debug("\"%s\"", p1);
assert_se(utf8_is_valid(p1));
p2 = utf8_escape_invalid("\341\204\341\204");
puts(p2);
log_debug("\"%s\"", p2);
assert_se(utf8_is_valid(p2));
p3 = utf8_escape_invalid("\341\204");
puts(p3);
log_debug("\"%s\"", p3);
assert_se(utf8_is_valid(p3));
}
@ -109,58 +110,55 @@ static void test_utf8_escape_non_printable(void) {
log_info("/* %s */", __func__);
p1 = utf8_escape_non_printable("goo goo goo");
puts(p1);
log_debug("\"%s\"", p1);
assert_se(utf8_is_valid(p1));
p2 = utf8_escape_non_printable("\341\204\341\204");
puts(p2);
log_debug("\"%s\"", p2);
assert_se(utf8_is_valid(p2));
p3 = utf8_escape_non_printable("\341\204");
puts(p3);
log_debug("\"%s\"", p3);
assert_se(utf8_is_valid(p3));
p4 = utf8_escape_non_printable("ąę\n가너도루\n1234\n\341\204\341\204\n\001 \019\20\a");
puts(p4);
log_debug("\"%s\"", p4);
assert_se(utf8_is_valid(p4));
p5 = utf8_escape_non_printable("\001 \019\20\a");
puts(p5);
log_debug("\"%s\"", p5);
assert_se(utf8_is_valid(p5));
p6 = utf8_escape_non_printable("\xef\xbf\x30\x13");
puts(p6);
log_debug("\"%s\"", p6);
assert_se(utf8_is_valid(p6));
}
static void test_utf8_escape_non_printable_full(void) {
log_info("/* %s */", __func__);
for (size_t i = 0; i < 20; i++) {
_cleanup_free_ char *p;
const char *s;
FOREACH_STRING(s,
"goo goo goo", /* ASCII */
"\001 \019\20\a", /* control characters */
"\xef\xbf\x30\x13") /* misplaced continuation bytes followed by a digit and cc */
for (size_t cw = 0; cw < 22; cw++) {
_cleanup_free_ char *p, *q;
size_t ew;
p = utf8_escape_non_printable_full("goo goo goo", i);
puts(p);
p = utf8_escape_non_printable_full(s, cw, false);
ew = utf8_console_width(p);
log_debug("%02zu \"%s\" (%zu wasted)", cw, p, cw - ew);
assert_se(utf8_is_valid(p));
assert_se(utf8_console_width(p) <= i);
}
assert_se(ew <= cw);
for (size_t i = 0; i < 20; i++) {
_cleanup_free_ char *p;
p = utf8_escape_non_printable_full("\001 \019\20\a", i);
puts(p);
assert_se(utf8_is_valid(p));
assert_se(utf8_console_width(p) <= i);
}
for (size_t i = 0; i < 20; i++) {
_cleanup_free_ char *p;
p = utf8_escape_non_printable_full("\xef\xbf\x30\x13", i);
puts(p);
assert_se(utf8_is_valid(p));
assert_se(utf8_console_width(p) <= i);
q = utf8_escape_non_printable_full(s, cw, true);
ew = utf8_console_width(q);
log_debug(" \"%s\" (%zu wasted)", q, cw - ew);
assert_se(utf8_is_valid(q));
assert_se(ew <= cw);
if (cw > 0)
assert_se(endswith(q, ""));
}
}
@ -235,6 +233,9 @@ static void test_utf8_to_utf16(void) {
}
int main(int argc, char *argv[]) {
log_show_color(true);
test_setup_logging(LOG_INFO);
test_utf8_n_is_valid();
test_utf8_is_valid();
test_utf8_is_printable();

View File

@ -3185,6 +3185,7 @@ static int parse_argv(int argc, char *argv[]) {
static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
_cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
_cleanup_fclose_ FILE *_f = NULL;
_cleanup_free_ char *pp = NULL;
unsigned v = 0;
FILE *f;
ItemArray *ia;
@ -3197,7 +3198,7 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
fn = "<stdin>";
f = stdin;
} else {
r = search_and_fopen(fn, "re", arg_root, (const char**) config_dirs, &_f);
r = search_and_fopen(fn, "re", arg_root, (const char**) config_dirs, &_f, &pp);
if (r < 0) {
if (ignore_enoent && r == -ENOENT) {
log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
@ -3206,7 +3207,9 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
return log_error_errno(r, "Failed to open '%s': %m", fn);
}
log_debug("Reading config file \"%s\"", fn);
log_debug("Reading config file \"%s\"", pp);
fn = pp;
f = _f;
}
@ -3446,7 +3449,8 @@ static int run(int argc, char *argv[]) {
DISSECT_IMAGE_REQUIRE_ROOT |
DISSECT_IMAGE_VALIDATE_OS |
DISSECT_IMAGE_RELAX_VAR_CHECK |
DISSECT_IMAGE_FSCK,
DISSECT_IMAGE_FSCK |
DISSECT_IMAGE_GROWFS,
&unlink_dir,
&loop_device,
&decrypted_image);

View File

@ -267,7 +267,7 @@ int manager_startup(Manager *m) {
else {
union sockaddr_union sockaddr = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/userdb/io.systemd.NameServiceSwitch",
.un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer",
};
r = mkdir_p("/run/systemd/userdb", 0755);
@ -284,7 +284,8 @@ int manager_startup(Manager *m) {
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);
r = symlink_idempotent("io.systemd.Multiplexer",
"/run/systemd/userdb/io.systemd.NameServiceSwitch", false);
if (r < 0)
return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");

View File

@ -130,8 +130,8 @@ losetup -d "${loop}"
ROOT_UUID="$(systemd-id128 -u show "$(head -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
VERITY_UUID="$(systemd-id128 -u show "$(tail -c 32 "${image}.roothash")" -u | tail -n 1 | cut -b 6-)"
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"yes","node":'
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,"node":'
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'"$ROOT_UUID"'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'"$architecture"'","verity":"yes",'
systemd-dissect --json=short --root-hash "${roothash}" "${image}.gpt" | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'"$VERITY_UUID"'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'"$architecture"'","verity":null,'
systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F "MARKER=1"
systemd-dissect --root-hash "${roothash}" "${image}.gpt" | grep -q -F -f <(sed 's/"//g' "$os_release")