This is useful to check if how fast the code is (painfully slow) and whether
worlds indeed are selected with a flat distribution probability (they are).
The code is slow because the hashing and because chaseat is exteremely
inefficient :(
Building the result one char at a time via strextendn() is O(N^2)
because each call rescans and reallocs the buffer. With lines up to
LONG_LINE_MAX this caused a timeout in fuzz-hostname-setup. Use
GREEDY_REALLOC_APPEND to make it linear.
Fixes https://github.com/systemd/systemd/issues/42713
Lennart suggested to use a more uniform algorithm for
the picking of the hostname words that is not biased
for long words by just (predictably) randomly going over
the offsets until we land on a word boundary. This is a
very nice suggestion so this commit implements it with
a fallback to the "old" behavior if we do not find a
word boundary within a reasonable amount of attempts.
A small python script shows that 64 iterations plus
fallback is a good number:
```
$ python3 simulate-hostname-pick.py 64
hostname-wordlist/adverbs
words=261 p_accept=0.1119 avg_bytes/word=1/p=8.94
max_iterations=64, n_trials=1000000
fallback rate : 0.051000% (510/1_000_000)
mean seeks per word : 8.93
hostname-wordlist/adjectives
words=449 p_accept=0.1380 avg_bytes/word=1/p=7.24
max_iterations=64, n_trials=1000000
fallback rate : 0.007500% (75/1_000_000)
mean seeks per word : 7.25
hostname-wordlist/nouns
words=449 p_accept=0.1472 avg_bytes/word=1/p=6.79
max_iterations=64, n_trials=1000000
fallback rate : 0.002700% (27/1_000_000)
mean seeks per word : 6.79
```
Combined with the fallback to the previous method if
we can't find anything within the 64 attemps this seems
to be the best tradeoff and give us very good uniformity.
Similar to the support for ?/$ in /etc/hostname and the credentials
we now add this to the kernel commandline systemd.hostname= option.
If the expansion fails, e.g. in the initrd where the word lists (and
possibly the machine ID) are not available yet, the option is ignored
and the usual default hostname logic applies. Once the host system is
up the expansion succeeds and the intended name is applied.
Now that we support the `$` we want to also make this available
inside the system.hostname and firstboot.hostname credentials and
the firstboot --hostname option. This commit adds it (and also `?`).
This commit adds support to /etc/hostname for substitution
of $ wordlists from {/etc,/run,/usr/lib}/systemd/hostname-wordlist.
The first $ will lookup hostname-wordlist/1, the next
hostname-wordlist/2 and so on.
With that we can do a petname [1] style hostname in systemd, e.g.
below a possible expansion for a hostname template:
$-$-$-???? -> wildly-happy-octopus-92a9
The substitution of words is stable (based on machine-id) but
not persisted, it is picked on every boot via a stable file
offset so the operation is cheap. But this means that if the
wordlist changes the hostname would change. The next commit
will add the pattern to the firstboot.hostname credential which
is persistet with the resolved names to avoid this issue.
This also includes a wordlist from the "petname" project
that can be optionally installed.
Thanks to Dustin Kirkland for this wonderful project.
[1] https://github.com/dustinkirkland/petname
glibc defines HOST_NAME_MAX as 64 and our code rely on that, but musl
defines the constant as 255. Let's provide our own definition for the
maximum length.
gethostname_full() is used in nss-myhostname, and hence random
application may indirectly call it. When an application with a too strict
seccomp filter loads the nss module, the application may trigger the
assertion.
Partially fixes#38582.
Some configuration files that need updates are directly under in /etc. To
update them atomically, we need write access to /etc. For Ubuntu Core this is
an issue as /etc is not writable. Only a selection of subdirectories can be
writable. The general solution is symlinks or bind mounts to writable places.
But for atomic writes in /etc, that does not work. So Ubuntu has had a patch
for that that did not age well.
Instead we would like to introduce some environment variables for alternate
paths.
* SYSTEMD_ETC_HOSTNAME: /etc/hostname
* SYSTEMD_ETC_MACHINE_INFO: /etc/machine-info
* SYSTEMD_ETC_LOCALTIME: /etc/localtime
* SYSTEMD_ETC_LOCALE_CONF: /etc/locale.conf
* SYSTEMD_ETC_VCONSOLE_CONF: /etc/vconsole.conf
* SYSTEMD_ETC_ADJTIME: /etc/adjtime
While it is for now expected that there is a symlink from the standard, we
still try to read them from that alternate path. This is important for
`/etc/localtime`, which is a symlink, so we cannot have an indirect symlink or
bind mount for it.
Since machine-id is typically written only once and not updated. This commit
does not cover it. An initrd can properly create it and bind mount it.
In one of the next commits we'd like to introduce a concept of
optionally hashing the hostname from the machine ID. For that we we need
to optionally back gethostname_full() by code involving sd-id128, hence
let's move it from src/basic/ to src/shared/, since only there we are
allowed to use our public APIs.
once we decided on a hostname, let's tell the supervisor about it. This
is useful for example in order to recognize the system via mDNS/LLMNR or
in a DHCP lease.
`system.hostname` credential is treated similarly to the pre-existing
`system.machine_id` credential. It is considered after /etc/hostname,
but prior to the kernel defaults or os-release defaults.
Fixes#30667.
Signed-off-by: Ivan Shapovalov <intelfx@intelfx.name>
Previously, sethostname_idempotent_full() calls gethostname_full() with
GET_HOSTNAME_ALLOW_NONE and GET_HOSTNAME_ALLOW_LOCALHOST flags. That
intended to get any values set by kernel. But, that does not work, as
the hostname may be empty.
Let's simplify the logic. The function sethostname_idempotent_full()
intends to set the requested hostname only when the current hostname
is different from the requested one. So, no check in getostname_full()
is required. Hence, simply use the result of uname() here.
Fixes#21896.
This follows the addition of DEFAULT_HOSTNAME= in os-release.
The distinction between the value from os-release or the env var and
the compile-time setting is not made in the api: HostnameSource is
"default" is all cases. I think that this level of detail is not needed,
because the users of this mostly care whether the hostname was set by
user configuration or not.
See https://bugzilla.redhat.com/show_bug.cgi?id=1893417 for the back story:
the fallback hostname matters a lot in certain environments. Right now the only
way to configure the fallback hostname is by recompiling systemd, which is
obviously problematic in case when the fallback hostname shall differ between
different editions of the same distro that share a single compiled rpm.
By making this overridable through an envvar, we're providing an escape hatch
without making this a top-level api. Later on a way to set this through
os-release is added, but I think the approach with the variable is still
useful. It it very convenient for testing, or to override settings only in a
particular service, etc.
In hostnamed this is exposed as a dbus property, and in the logs in both
places.
This is of interest to network management software and such: if the fallback
hostname is used, it's not as useful as the real configured thing. Right now
various programs try to guess the source of hostname by looking at the string.
E.g. "localhost" is assumed to be not the real hostname, but "fedora" is. Any
such attempts are bound to fail, because we cannot distinguish "fedora" (a
fallback value set by a distro), from "fedora" (received from reverse dns),
from "fedora" read from /etc/hostname.
/run/systemd/fallback-hostname is written with the fallback hostname when
either pid1 or hostnamed sets the kernel hostname to the fallback value. Why
remember the fallback value and not the transient hostname in /run/hostname
instead?
We have three hostname types: "static", "transient", fallback".
– Distinguishing "static" is easy: the hostname that is set matches what
is in /etc/hostname.
– Distingiushing "transient" and "fallback" is not easy. And the
"transient" hostname may be set outside of pid1+hostnamed. In particular,
it may be set by container manager, some non-systemd tool in the initramfs,
or even by a direct call. All those mechanisms count as "transient". Trying
to get those cases to write /run/hostname is futile. It is much easier to
isolate the "fallback" case which is mostly under our control.
And since the file is only used as a flag to mark the hostname as fallback,
it can be hidden inside of our /run/systemd directory.
For https://bugzilla.redhat.com/show_bug.cgi?id=1892235.
gethostname(3) says it's unspecified whether the string is properly terminated
when the hostname is too long. We created a buffer with one extra byte, and it
seems the intent was to let that byte serve as terminator even if we get an
unterminated string from gethostname().
No functional change, just moving a bunch of things around. Before
we needed a rather complicated setup to test hostname_setup(), because
the code was in src/core/. When things are moved to src/shared/
we can just test it as any function.
The test is still "unsafe" because hostname_setup() may modify the
hostname.