diff options
28 files changed, 501 insertions, 245 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8a6db1f629..abee9cc740 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -18,7 +18,7 @@ Following these guidelines makes it easier for us to process your issue, and ens * Please make sure to test your change before submitting the PR. See [HACKING](https://raw.githubusercontent.com/systemd/systemd/master/HACKING) for details how to do this. * Make sure to run "make check" locally, before posting your PR. We use a CI system, meaning we don't even look at your PR, if the build and tests don't pass. * If you need to update the code in an existing PR, force-push into the same branch, overriding old commits with new versions. -* After you have pushed a new version, try to remove the `reviewed/needs-rework` label. Also add a comment about the new version (no notification is sent just for the commits, so it's easy to miss the update without an explicit comment). +* After you have pushed a new version, add a comment about the new version (no notification is sent just for the commits, so it's easy to miss the update without an explicit comment). If you are a member of the systemd project on github, remove the `reviewed/needs-rework` label. ## Final Words diff --git a/configure.ac b/configure.ac index 65eaae1ae0..5979de4dc6 100644 --- a/configure.ac +++ b/configure.ac @@ -621,10 +621,13 @@ AM_CONDITIONAL(HAVE_BZIP2, [test "$have_bzip2" = "yes"]) have_lz4=no AC_ARG_ENABLE(lz4, AS_HELP_STRING([--disable-lz4], [disable optional LZ4 support])) AS_IF([test "x$enable_lz4" != "xno"], [ - PKG_CHECK_MODULES(LZ4, [ liblz4 >= 125 ], - [AC_DEFINE(HAVE_LZ4, 1, [Define in LZ4 is available]) + PKG_CHECK_MODULES(LZ4, [ liblz4 < 10 ], + [AC_DEFINE(HAVE_LZ4, 1, [Define if LZ4 is available]) have_lz4=yes], - have_lz4=no) + [PKG_CHECK_MODULES(LZ4, [ liblz4 >= 125 ], + [AC_DEFINE(HAVE_LZ4, 1, [Define if LZ4 is available]) + have_lz4=yes], + have_lz4=no)]) AS_IF([test "x$have_lz4" = xno -a "x$enable_lz4" = xyes], [AC_MSG_ERROR([*** LZ4 support requested but libraries not found])]) ]) diff --git a/man/bootup.xml b/man/bootup.xml index 986996398c..b92c60f43a 100644 --- a/man/bootup.xml +++ b/man/bootup.xml @@ -179,6 +179,13 @@ identical to the system manager bootup (see above) until it reaches <filename>basic.target</filename>. From there, systemd approaches the special target <filename>initrd.target</filename>. + + Before any file systems are mounted, it must be determined whether + the system will resume from hibernation or proceed with normal boot. + This is accomplished by <filename>systemd-hibernate-resume@.service</filename> + which must be finished before <filename>local-fs-pre.target</filename>, + so no filesystems can be mounted before the check is complete. + When the root device becomes available, <filename>initd-root-device.target</filename> is reached. If the root device can be mounted at diff --git a/man/machinectl.xml b/man/machinectl.xml index 5a6ec294d2..81192417d8 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -599,8 +599,8 @@ <listitem><para>Clones a container or VM image. The arguments specify the name of the image to clone and the name of the newly cloned image. Note that plain directory container images are cloned into btrfs subvolume images with this command, if the underlying file system supports this. Note that cloning a container or VM - image is optimized for btrfs file systems, and might not be efficient on others, due to file system - limitations.</para> + image is optimized for file systems that support copy-on-write, and might not be efficient on others, due to + file system limitations.</para> <para>Note that this command leaves host name, machine ID and all other settings that could identify the instance @@ -910,7 +910,7 @@ <filename>/var/lib/machines/</filename> to make them available for control with <command>machinectl</command>.</para> - <para>Note that many image operations are only supported, + <para>Note that some image operations are only supported, efficient or atomic on btrfs file systems. Due to this, if the <command>pull-tar</command>, <command>pull-raw</command>, <command>import-tar</command>, <command>import-raw</command> and diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml index 9f24f65019..4e102cec26 100644 --- a/man/nss-resolve.xml +++ b/man/nss-resolve.xml @@ -63,16 +63,13 @@ hostnames via DNS.</para> <para>To activate the NSS module, add <literal>resolve</literal> to the line starting with - <literal>hosts:</literal> in <filename>/etc/nsswitch.conf</filename>.</para> - - <para>It is recommended to place <literal>resolve</literal> early in <filename>/etc/nsswitch.conf</filename>' - <literal>hosts:</literal> line (but after the <literal>files</literal> or <literal>mymachines</literal> entries), - replacing the <literal>dns</literal> entry if it exists, to ensure DNS queries are always routed via - <citerefentry><refentrytitle>systemd-resolved</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> - - <para>Note that <command>nss-resolve</command> will chain-load <command>nss-dns</command> if - <filename>systemd-resolved.service</filename> is not running, ensuring that basic DNS resolution continues to work - if the service is down.</para> + <literal>hosts:</literal> in <filename>/etc/nsswitch.conf</filename>. Specifcally, it is recommended to place + <literal>resolve</literal> early in <filename>/etc/nsswitch.conf</filename>' <literal>hosts:</literal> line (but + after the <literal>files</literal> or <literal>mymachines</literal> entries), right before the + <literal>dns</literal> entry if it exists, followed by <literal>[!UNAVAIL=return]</literal>, to ensure DNS queries + are always routed via + <citerefentry><refentrytitle>systemd-resolved</refentrytitle><manvolnum>8</manvolnum></citerefentry> if it is + running, but are routed to <command>nss-dns</command> if this service is not available.</para> </refsect1> <refsect1> @@ -94,9 +91,6 @@ ethers: db files rpc: db files netgroup: nis</programlisting> - - <para>This keeps the <command>dns</command> module as a fallback for cases where the <command>nss-resolve</command> - module is not installed.</para> </refsect1> <refsect1> diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index f153034296..dbbf9890c8 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -181,25 +181,15 @@ <varlistentry> <term><option>--template=</option></term> - <listitem><para>Directory or <literal>btrfs</literal> - subvolume to use as template for the container's root - directory. If this is specified and the container's root - directory (as configured by <option>--directory=</option>) - does not yet exist it is created as <literal>btrfs</literal> - subvolume and populated from this template tree. Ideally, the - specified template path refers to the root of a - <literal>btrfs</literal> subvolume, in which case a simple - copy-on-write snapshot is taken, and populating the root - directory is instant. If the specified template path does not - refer to the root of a <literal>btrfs</literal> subvolume (or - not even to a <literal>btrfs</literal> file system at all), - the tree is copied, which can be substantially more - time-consuming. Note that if this option is used the - container's root directory (in contrast to the template - directory!) must be located on a <literal>btrfs</literal> file - system, so that the <literal>btrfs</literal> subvolume may be - created. May not be specified together with - <option>--image=</option> or + <listitem><para>Directory or <literal>btrfs</literal> subvolume to use as template for the container's root + directory. If this is specified and the container's root directory (as configured by + <option>--directory=</option>) does not yet exist it is created as <literal>btrfs</literal> snapshot (if + supported) or plain directory (otherwise) and populated from this template tree. Ideally, the specified + template path refers to the root of a <literal>btrfs</literal> subvolume, in which case a simple copy-on-write + snapshot is taken, and populating the root directory is instant. If the specified template path does not refer + to the root of a <literal>btrfs</literal> subvolume (or not even to a <literal>btrfs</literal> file system at + all), the tree is copied (though possibly in a copy-on-write scheme — if the file system supports that), which + can be substantially more time-consuming. May not be specified together with <option>--image=</option> or <option>--ephemeral</option>.</para> <para>Note that this switch leaves host name, machine ID and @@ -211,13 +201,8 @@ <term><option>-x</option></term> <term><option>--ephemeral</option></term> - <listitem><para>If specified, the container is run with a - temporary <literal>btrfs</literal> snapshot of its root - directory (as configured with <option>--directory=</option>), - that is removed immediately when the container terminates. - This option is only supported if the root file system is - <literal>btrfs</literal>. May not be specified together with - <option>--image=</option> or + <listitem><para>If specified, the container is run with a temporary snapshot of its file system that is removed + immediately when the container terminates. May not be specified together with <option>--template=</option>.</para> <para>Note that this switch leaves host name, machine ID and all other settings that could identify the instance @@ -252,11 +237,12 @@ Partitions Specification</ulink>.</para></listitem> </itemizedlist> - <para>Any other partitions, such as foreign partitions, swap - partitions or EFI system partitions are not mounted. May not - be specified together with <option>--directory=</option>, - <option>--template=</option> or - <option>--ephemeral</option>.</para></listitem> + <para>On GPT images, if an EFI System Partition (ESP) is discovered, it is automatically mounted to + <filename>/efi</filename> (or <filename>/boot</filename> as fallback) in case a directory by this name exists + and is empty.</para> + + <para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified + together with <option>--directory=</option>, <option>--template=</option>.</para></listitem> </varlistentry> <varlistentry> @@ -1056,14 +1042,12 @@ </example> <example> - <title>Boot into an ephemeral <literal>btrfs</literal> snapshot of the host system</title> + <title>Boot into an ephemeral snapshot of the host system</title> <programlisting># systemd-nspawn -D / -xb</programlisting> - <para>This runs a copy of the host system in a - <literal>btrfs</literal> snapshot which is removed immediately - when the container exits. All file system changes made during - runtime will be lost on shutdown, hence.</para> + <para>This runs a copy of the host system in a snapshot which is removed immediately when the container + exits. All file system changes made during runtime will be lost on shutdown, hence.</para> </example> <example> diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 656bb13719..5f9e21dcba 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -20,6 +20,7 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <linux/fs.h> #include <linux/loop.h> #include <stddef.h> #include <stdio.h> @@ -38,6 +39,7 @@ #include "alloc-util.h" #include "btrfs-ctree.h" #include "btrfs-util.h" +#include "chattr-util.h" #include "copy.h" #include "fd-util.h" #include "fileio.h" @@ -45,6 +47,7 @@ #include "macro.h" #include "missing.h" #include "path-util.h" +#include "rm-rf.h" #include "selinux-util.h" #include "smack-util.h" #include "sparse-endian.h" @@ -1718,28 +1721,46 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag if (r < 0) return r; if (r == 0) { + bool plain_directory = false; + + /* If the source isn't a proper subvolume, fail unless fallback is requested */ if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY)) return -EISDIR; r = btrfs_subvol_make(new_path); - if (r < 0) + if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) { + /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */ + if (mkdir(new_path, 0755) < 0) + return r; + + plain_directory = true; + } else if (r < 0) return r; r = copy_directory_fd(old_fd, new_path, true); - if (r < 0) { - (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); - return r; - } + if (r < 0) + goto fallback_fail; if (flags & BTRFS_SNAPSHOT_READ_ONLY) { - r = btrfs_subvol_set_read_only(new_path, true); - if (r < 0) { - (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); - return r; + + if (plain_directory) { + /* Plain directories have no recursive read-only flag, but something pretty close to + * it: the IMMUTABLE bit. Let's use this here, if this is requested. */ + + if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE) + (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); + } else { + r = btrfs_subvol_set_read_only(new_path, true); + if (r < 0) + goto fallback_fail; } } return 0; + + fallback_fail: + (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + return r; } r = extract_subvolume_name(new_path, &subvolume); diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index 1d852d502c..04a2e1274b 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -45,10 +45,12 @@ typedef struct BtrfsQuotaInfo { } BtrfsQuotaInfo; typedef enum BtrfsSnapshotFlags { - BTRFS_SNAPSHOT_FALLBACK_COPY = 1, + BTRFS_SNAPSHOT_FALLBACK_COPY = 1, /* If the source isn't a subvolume, reflink everything */ BTRFS_SNAPSHOT_READ_ONLY = 2, BTRFS_SNAPSHOT_RECURSIVE = 4, BTRFS_SNAPSHOT_QUOTA = 8, + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 16, /* If the destination doesn't support subvolumes, reflink/copy instead */ + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 32, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */ } BtrfsSnapshotFlags; typedef enum BtrfsRemoveFlags { diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index aa7ccd1afd..3b06cb00ad 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -31,15 +31,9 @@ #include "util.h" bool in4_addr_is_null(const struct in_addr *a) { - return a->s_addr == 0; -} + assert(a); -bool in6_addr_is_null(const struct in6_addr *a) { - return - a->s6_addr32[0] == 0 && - a->s6_addr32[1] == 0 && - a->s6_addr32[2] == 0 && - a->s6_addr32[3] == 0; + return a->s_addr == 0; } int in_addr_is_null(int family, const union in_addr_union *u) { @@ -49,16 +43,22 @@ int in_addr_is_null(int family, const union in_addr_union *u) { return in4_addr_is_null(&u->in); if (family == AF_INET6) - return in6_addr_is_null(&u->in6); + return IN6_IS_ADDR_UNSPECIFIED(&u->in6); return -EAFNOSUPPORT; } +bool in4_addr_is_link_local(const struct in_addr *a) { + assert(a); + + return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); +} + int in_addr_is_link_local(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) - return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); + return in4_addr_is_link_local(&u->in); if (family == AF_INET6) return IN6_IS_ADDR_LINKLOCAL(&u->in6); @@ -66,12 +66,18 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +bool in4_addr_is_localhost(const struct in_addr *a) { + assert(a); + + /* All of 127.x.x.x is localhost. */ + return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; +} + int in_addr_is_localhost(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) - /* All of 127.x.x.x is localhost. */ - return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; + return in4_addr_is_localhost(&u->in); if (family == AF_INET6) return IN6_IS_ADDR_LOOPBACK(&u->in6); @@ -277,15 +283,14 @@ fallback: } int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { - + union in_addr_union buffer; assert(s); - assert(ret); if (!IN_SET(family, AF_INET, AF_INET6)) return -EAFNOSUPPORT; errno = 0; - if (inet_pton(family, s, ret) <= 0) + if (inet_pton(family, s, ret ?: &buffer) <= 0) return errno > 0 ? -errno : -EINVAL; return 0; @@ -295,18 +300,18 @@ int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *re int r; assert(s); - assert(family); - assert(ret); r = in_addr_from_string(AF_INET, s, ret); if (r >= 0) { - *family = AF_INET; + if (family) + *family = AF_INET; return 0; } r = in_addr_from_string(AF_INET6, s, ret); if (r >= 0) { - *family = AF_INET6; + if (family) + *family = AF_INET6; return 0; } diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index d60064aef8..64a812c322 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -37,11 +37,14 @@ struct in_addr_data { }; bool in4_addr_is_null(const struct in_addr *a); -bool in6_addr_is_null(const struct in6_addr *a); - int in_addr_is_null(int family, const union in_addr_union *u); + +bool in4_addr_is_link_local(const struct in_addr *a); int in_addr_is_link_local(int family, const union in_addr_union *u); + +bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); + int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); diff --git a/src/basic/missing.h b/src/basic/missing.h index a5ae5d9e79..8833617dc6 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -143,6 +143,10 @@ #define GRND_RANDOM 0x0002 #endif +#ifndef FS_NOCOW_FL +#define FS_NOCOW_FL 0x00800000 +#endif + #ifndef BTRFS_IOCTL_MAGIC #define BTRFS_IOCTL_MAGIC 0x94 #endif diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 2ae2a4174c..5ddc0c56f4 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -144,12 +144,12 @@ int pull_make_local_copy(const char *final, const char *image_root, const char * if (force_local) (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA); - if (r == -ENOTTY) { - r = copy_tree(final, p, false); - if (r < 0) - return log_error_errno(r, "Failed to copy image: %m"); - } else if (r < 0) + r = btrfs_subvol_snapshot(final, p, + BTRFS_SNAPSHOT_QUOTA| + BTRFS_SNAPSHOT_FALLBACK_COPY| + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY| + BTRFS_SNAPSHOT_RECURSIVE); + if (r < 0) return log_error_errno(r, "Failed to create local image: %m"); log_info("Created new local image '%s'.", local); diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c index 41ff2b353a..cf56c89d76 100644 --- a/src/libsystemd-network/ndisc-router.c +++ b/src/libsystemd-network/ndisc-router.c @@ -91,7 +91,7 @@ _public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *r assert_return(rt, -EINVAL); assert_return(ret_addr, -EINVAL); - if (in6_addr_is_null(&rt->address)) + if (IN6_IS_ADDR_UNSPECIFIED(&rt->address)) return -ENODATA; *ret_addr = rt->address; diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 6475da2c2a..1423264806 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -386,45 +386,23 @@ int sd_dhcp_client_set_hostname( sd_dhcp_client *client, const char *hostname) { - char *new_hostname = NULL; - assert_return(client, -EINVAL); - if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname)) + /* Refuse hostnames that neither qualify as DNS nor as Linux hosntames */ + if (hostname && + !(hostname_is_valid(hostname, false) || dns_name_is_valid(hostname) > 0)) return -EINVAL; - if (streq_ptr(client->hostname, hostname)) - return 0; - - if (hostname) { - new_hostname = strdup(hostname); - if (!new_hostname) - return -ENOMEM; - } - - free(client->hostname); - client->hostname = new_hostname; - - return 0; + return free_and_strdup(&client->hostname, hostname); } int sd_dhcp_client_set_vendor_class_identifier( sd_dhcp_client *client, const char *vci) { - char *new_vci = NULL; - assert_return(client, -EINVAL); - new_vci = strdup(vci); - if (!new_vci) - return -ENOMEM; - - free(client->vendor_class_identifier); - - client->vendor_class_identifier = new_vci; - - return 0; + return free_and_strdup(&client->vendor_class_identifier, vci); } int sd_dhcp_client_set_client_port( diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 8387b185c0..7fed55c5fc 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -383,6 +383,23 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { return 0; } +static void filter_bogus_addresses(struct in_addr *addresses, size_t *n) { + size_t i, j; + + /* Silently filter DNS/NTP servers supplied to us that do not make outside of the local scope. */ + + for (i = 0, j = 0; i < *n; i ++) { + + if (in4_addr_is_null(addresses+i) || + in4_addr_is_localhost(addresses+i)) + continue; + + addresses[j++] = addresses[i]; + } + + *n = j; +} + static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { assert(option); assert(ret); @@ -404,6 +421,8 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add if (!addresses) return -ENOMEM; + filter_bogus_addresses(addresses, &n_addresses); + free(*ret); *ret = addresses; *n_ret = n_addresses; diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 614bceefab..ca23c1c2a7 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -536,6 +536,28 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { return; } +static int dhcp4_set_hostname(Link *link) { + _cleanup_free_ char *hostname = NULL; + const char *hn; + int r; + + assert(link); + + if (!link->network->dhcp_send_hostname) + hn = NULL; + else if (link->network->dhcp_hostname) + hn = link->network->dhcp_hostname; + else { + r = gethostname_strict(&hostname); + if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */ + return r; + + hn = hostname; + } + + return sd_dhcp_client_set_hostname(link->dhcp_client, hn); +} + int dhcp4_configure(Link *link) { int r; @@ -605,25 +627,9 @@ int dhcp4_configure(Link *link) { if (r < 0) return r; - if (link->network->dhcp_send_hostname) { - _cleanup_free_ char *hostname = NULL; - const char *hn = NULL; - - if (!link->network->dhcp_hostname) { - hostname = gethostname_malloc(); - if (!hostname) - return -ENOMEM; - - hn = hostname; - } else - hn = link->network->dhcp_hostname; - - if (!is_localhost(hn)) { - r = sd_dhcp_client_set_hostname(link->dhcp_client, hn); - if (r < 0) - return r; - } - } + r = dhcp4_set_hostname(link); + if (r < 0) + return r; if (link->network->dhcp_vendor_class_identifier) { r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client, diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 0b634572a9..b38eec1ba7 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -851,21 +851,27 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda return 1; } -static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { +static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { _cleanup_free_ struct in_addr *addresses = NULL; size_t n_addresses = 0, n_allocated = 0; - char **a; + unsigned i; log_debug("Copying DNS server information from %s", link->ifname); if (!link->network) return 0; - STRV_FOREACH(a, link->network->dns) { + for (i = 0; i < link->network->n_dns; i++) { struct in_addr ia; /* Only look for IPv4 addresses */ - if (inet_pton(AF_INET, *a, &ia) <= 0) + if (link->network->dns[i].family != AF_INET) + continue; + + ia = link->network->dns[i].address.in; + + /* Never propagate obviously borked data */ + if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia)) continue; if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) @@ -874,8 +880,7 @@ static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { addresses[n_addresses++] = ia; } - if (link->network->dhcp_use_dns && - link->dhcp_lease) { + if (link->network->dhcp_use_dns && link->dhcp_lease) { const struct in_addr *da = NULL; int n; @@ -896,7 +901,7 @@ static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { return sd_dhcp_server_set_dns(s, addresses, n_addresses); } -static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { +static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { _cleanup_free_ struct in_addr *addresses = NULL; size_t n_addresses = 0, n_allocated = 0; char **a; @@ -913,14 +918,17 @@ static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { if (inet_pton(AF_INET, *a, &ia) <= 0) continue; + /* Never propagate obviously borked data */ + if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia)) + continue; + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) return log_oom(); addresses[n_addresses++] = ia; } - if (link->network->dhcp_use_ntp && - link->dhcp_lease) { + if (link->network->dhcp_use_ntp && link->dhcp_lease) { const struct in_addr *da = NULL; int n; @@ -1034,7 +1042,7 @@ static int link_enter_set_addresses(Link *link) { log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink."); r = 0; } else - r = link_push_dns_to_dhcp_server(uplink, link->dhcp_server); + r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server); } if (r < 0) log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m"); @@ -1053,7 +1061,7 @@ static int link_enter_set_addresses(Link *link) { log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink."); r = 0; } else - r = link_push_ntp_to_dhcp_server(uplink, link->dhcp_server); + r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server); } if (r < 0) @@ -3235,7 +3243,7 @@ int link_save(Link *link) { if (r < 0) goto fail; - fchmod(fileno(f), 0644); + (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n" @@ -3248,6 +3256,7 @@ int link_save(Link *link) { sd_dhcp6_lease *dhcp6_lease = NULL; const char *dhcp_domainname = NULL; char **dhcp6_domains = NULL; + unsigned j; if (link->dhcp6_client) { r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); @@ -3259,7 +3268,22 @@ int link_save(Link *link) { fputs("DNS=", f); space = false; - fputstrv(f, link->network->dns, NULL, &space); + + for (j = 0; j < link->network->n_dns; j++) { + _cleanup_free_ char *b = NULL; + + r = in_addr_to_string(link->network->dns[j].family, + &link->network->dns[j].address, &b); + if (r < 0) { + log_debug_errno(r, "Failed to format address, ignoring: %m"); + continue; + } + + if (space) + fputc(' ', f); + fputs(b, f); + space = true; + } if (link->network->dhcp_use_dns && link->dhcp_lease) { diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index a1252c9b51..c3d3f48a3f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -774,11 +774,48 @@ static int manager_connect_rtnl(Manager *m) { return 0; } -static int ordered_set_put_in_addr(OrderedSet *s, const struct in_addr *address) { +static int ordered_set_put_in_addr_data(OrderedSet *s, const struct in_addr_data *address) { char *p; int r; assert(s); + assert(address); + + r = in_addr_to_string(address->family, &address->address, &p); + if (r < 0) + return r; + + r = ordered_set_consume(s, p); + if (r == -EEXIST) + return 0; + + return r; +} + +static int ordered_set_put_in_addr_datav(OrderedSet *s, const struct in_addr_data *addresses, unsigned n) { + int r, c = 0; + unsigned i; + + assert(s); + assert(addresses || n == 0); + + for (i = 0; i < n; i++) { + r = ordered_set_put_in_addr_data(s, addresses+i); + if (r < 0) + return r; + + c += r; + } + + return c; +} + +static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address) { + char *p; + int r; + + assert(s); + assert(address); r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p); if (r < 0) @@ -791,14 +828,15 @@ static int ordered_set_put_in_addr(OrderedSet *s, const struct in_addr *address) return r; } -static int ordered_set_put_in_addrv(OrderedSet *s, const struct in_addr *addresses, int n) { - int r, i, c = 0; +static int ordered_set_put_in4_addrv(OrderedSet *s, const struct in_addr *addresses, unsigned n) { + int r, c = 0; + unsigned i; assert(s); - assert(n <= 0 || addresses); + assert(n == 0 || addresses); for (i = 0; i < n; i++) { - r = ordered_set_put_in_addr(s, addresses+i); + r = ordered_set_put_in4_addr(s, addresses+i); if (r < 0) return r; @@ -865,7 +903,7 @@ static int manager_save(Manager *m) { continue; /* First add the static configured entries */ - r = ordered_set_put_strdupv(dns, link->network->dns); + r = ordered_set_put_in_addr_datav(dns, link->network->dns, link->network->n_dns); if (r < 0) return r; @@ -890,7 +928,7 @@ static int manager_save(Manager *m) { r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); if (r > 0) { - r = ordered_set_put_in_addrv(dns, addresses, r); + r = ordered_set_put_in4_addrv(dns, addresses, r); if (r < 0) return r; } else if (r < 0 && r != -ENODATA) @@ -902,7 +940,7 @@ static int manager_save(Manager *m) { r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); if (r > 0) { - r = ordered_set_put_in_addrv(ntp, addresses, r); + r = ordered_set_put_in4_addrv(ntp, addresses, r); if (r < 0) return r; } else if (r < 0 && r != -ENODATA) @@ -934,7 +972,7 @@ static int manager_save(Manager *m) { if (r < 0) return r; - fchmod(fileno(f), 0644); + (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n" diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index efd3176ac3..463f4595c1 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -54,7 +54,7 @@ Network.LLMNR, config_parse_resolve_support, Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns) Network.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Network, dnssec_mode) Network.DNSSECNegativeTrustAnchors, config_parse_dnssec_negative_trust_anchors, 0, 0 -Network.NTP, config_parse_strv, 0, offsetof(Network, ntp) +Network.NTP, config_parse_ntp, 0, offsetof(Network, ntp) Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward) Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade) Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Network, ipv6_privacy_extensions) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 31e899eecd..bc4dc95ff9 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -244,7 +244,7 @@ void network_free(Network *network) { free(network->mac); strv_free(network->ntp); - strv_free(network->dns); + free(network->dns); strv_free(network->search_domains); strv_free(network->route_domains); strv_free(network->bind_carrier); @@ -396,7 +396,7 @@ int network_apply(Network *network, Link *link) { route->protocol = RTPROT_STATIC; } - if (!strv_isempty(network->dns) || + if (network->n_dns > 0 || !strv_isempty(network->ntp) || !strv_isempty(network->search_domains) || !strv_isempty(network->route_domains)) @@ -909,13 +909,14 @@ int config_parse_dhcp_server_dns( struct in_addr a, *m; r = extract_first_word(&p, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); return 0; } - if (r == 0) - return 0; + break; if (inet_pton(AF_INET, w, &a) <= 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); @@ -929,6 +930,8 @@ int config_parse_dhcp_server_dns( m[n->n_dhcp_server_dns++] = a; n->dhcp_server_dns = m; } + + return 0; } int config_parse_dhcp_server_ntp( @@ -956,11 +959,12 @@ int config_parse_dhcp_server_ntp( struct in_addr a, *m; r = extract_first_word(&p, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); return 0; } - if (r == 0) return 0; @@ -1000,29 +1004,35 @@ int config_parse_dns( for (;;) { _cleanup_free_ char *w = NULL; union in_addr_union a; + struct in_addr_data *m; int family; - r = extract_first_word(&rvalue, &w, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE); - if (r == 0) - break; + r = extract_first_word(&rvalue, &w, NULL, 0); if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); break; } + if (r == 0) + break; r = in_addr_from_string_auto(w, &family, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse dns server address, ignoring: %s", w); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse dns server address, ignoring: %s", w); continue; } - r = strv_consume(&n->dns, w); - if (r < 0) + m = realloc(n->dns, (n->n_dns + 1) * sizeof(struct in_addr_data)); + if (!m) return log_oom(); - w = NULL; + m[n->n_dns++] = (struct in_addr_data) { + .family = family, + .address = a, + }; + + n->dns = m; } return 0; @@ -1084,6 +1094,59 @@ int config_parse_dnssec_negative_trust_anchors( return 0; } +int config_parse_ntp( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char ***l = data; + int r; + + assert(l); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + *l = strv_free(*l); + return 0; + } + + for (;;) { + _cleanup_free_ char *w = NULL; + + r = extract_first_word(&rvalue, &w, NULL, 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract NTP server name, ignoring: %s", rvalue); + break; + } + if (r == 0) + break; + + r = dns_name_is_valid_or_address(w); + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name or IP address, ignoring.", w); + continue; + } + + r = strv_push(l, w); + if (r < 0) + return log_oom(); + + w = NULL; + } + + return 0; +} + int config_parse_dhcp_route_table(const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index e956a59fe3..4dbc19fc3b 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -112,19 +112,19 @@ struct Network { DCHPClientIdentifier dhcp_client_identifier; char *dhcp_vendor_class_identifier; char *dhcp_hostname; - bool dhcp_use_dns; - bool dhcp_use_ntp; - bool dhcp_use_mtu; - bool dhcp_use_hostname; - DHCPUseDomains dhcp_use_domains; + unsigned dhcp_route_metric; + uint32_t dhcp_route_table; + uint32_t dhcp_client_port; bool dhcp_send_hostname; bool dhcp_broadcast; bool dhcp_critical; + bool dhcp_use_dns; + bool dhcp_use_ntp; + bool dhcp_use_mtu; bool dhcp_use_routes; bool dhcp_use_timezone; - unsigned dhcp_route_metric; - uint32_t dhcp_route_table; - uint32_t dhcp_client_port; + bool dhcp_use_hostname; + DHCPUseDomains dhcp_use_domains; /* DHCP Server Support */ bool dhcp_server; @@ -174,7 +174,7 @@ struct Network { IPv6PrivacyExtensions ipv6_privacy_extensions; struct ether_addr *mac; - unsigned mtu; + size_t mtu; int arp; uint32_t iaid; DUID duid; @@ -194,7 +194,10 @@ struct Network { Hashmap *routes_by_section; Hashmap *fdb_entries_by_section; - char **search_domains, **route_domains, **dns, **ntp, **bind_carrier; + struct in_addr_data *dns; + unsigned n_dns; + + char **search_domains, **route_domains, **ntp, **bind_carrier; ResolveSupport llmnr; ResolveSupport mdns; @@ -233,6 +236,7 @@ int config_parse_dnssec_negative_trust_anchors(const char *unit, const char *fil int config_parse_dhcp_use_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_lldp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_dhcp_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* Legacy IPv4LL support */ int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 95bb3c09b0..91cb0861d3 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -298,7 +298,7 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) { MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL); } -static int mkdir_userns(const char *path, mode_t mode, bool in_userns, uid_t uid_shift) { +static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) { int r; assert(path); @@ -307,16 +307,20 @@ static int mkdir_userns(const char *path, mode_t mode, bool in_userns, uid_t uid if (r < 0 && errno != EEXIST) return -errno; - if (!in_userns) { - r = lchown(path, uid_shift, uid_shift); - if (r < 0) - return -errno; - } + if ((mask & MOUNT_USE_USERNS) == 0) + return 0; + + if (mask & MOUNT_IN_USERNS) + return 0; + + r = lchown(path, uid_shift, uid_shift); + if (r < 0) + return -errno; return 0; } -static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, bool in_userns, uid_t uid_shift) { +static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) { const char *p, *e; int r; @@ -343,12 +347,12 @@ static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, boo if (prefix && path_startswith(prefix, t)) continue; - r = mkdir_userns(t, mode, in_userns, uid_shift); + r = mkdir_userns(t, mode, mask, uid_shift); if (r < 0) return r; } - return mkdir_userns(path, mode, in_userns, uid_shift); + return mkdir_userns(path, mode, mask, uid_shift); } int mount_all(const char *dest, @@ -422,7 +426,7 @@ int mount_all(const char *dest, if (mount_table[k].what && r > 0) continue; - r = mkdir_userns_p(dest, where, 0755, in_userns, uid_shift); + r = mkdir_userns_p(dest, where, 0755, mount_settings, uid_shift); if (r < 0 && r != -EEXIST) { if (fatal) return log_error_errno(r, "Failed to create directory %s: %m", where); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index a6adbbe879..2770770cd0 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1143,11 +1143,6 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_ephemeral && arg_image) { - log_error("--ephemeral and --image= may not be combined."); - return -EINVAL; - } - if (arg_ephemeral && !IN_SET(arg_link_journal, LINK_NO, LINK_AUTO)) { log_error("--ephemeral and --link-journal= may not be combined."); return -EINVAL; @@ -2605,7 +2600,7 @@ static int determine_names(void) { r = image_find(arg_machine, &i); if (r < 0) return log_error_errno(r, "Failed to find image for machine '%s': %m", arg_machine); - else if (r == 0) { + if (r == 0) { log_error("No image for machine '%s': %m", arg_machine); return -ENOENT; } @@ -2615,14 +2610,14 @@ static int determine_names(void) { else r = free_and_strdup(&arg_directory, i->path); if (r < 0) - return log_error_errno(r, "Invalid image directory: %m"); + return log_oom(); if (!arg_ephemeral) arg_read_only = arg_read_only || i->read_only; } else arg_directory = get_current_dir_name(); - if (!arg_directory && !arg_machine) { + if (!arg_directory && !arg_image) { log_error("Failed to determine path, please use -D or -i."); return -EINVAL; } @@ -2633,7 +2628,6 @@ static int determine_names(void) { arg_machine = gethostname_malloc(); else arg_machine = strdup(basename(arg_image ?: arg_directory)); - if (!arg_machine) return log_oom(); @@ -3795,7 +3789,6 @@ static int run(int master, l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0); if (l < 0) return log_error_errno(errno, "Failed to read UID shift: %m"); - if (l != sizeof arg_uid_shift) { log_error("Short read while reading UID shift."); return -EIO; @@ -4029,7 +4022,7 @@ static int run(int master, terminate_machine(*pid); /* Normally redundant, but better safe than sorry */ - kill(*pid, SIGKILL); + (void) kill(*pid, SIGKILL); r = wait_for_container(*pid, &container_status); *pid = 0; @@ -4077,11 +4070,12 @@ int main(int argc, char *argv[]) { _cleanup_fdset_free_ FDSet *fds = NULL; int r, n_fd_passed, loop_nr = -1, ret = EXIT_SUCCESS; char veth_name[IFNAMSIZ] = ""; - bool secondary = false, remove_subvol = false; + bool secondary = false, remove_directory = false, remove_image = false; pid_t pid = 0; union in_addr_union exposed = {}; _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; - bool interactive, veth_created = false; + bool interactive, veth_created = false, remove_tmprootdir = false; + char tmprootdir[] = "/tmp/nspawn-root-XXXXXX"; log_parse_environment(); log_open(); @@ -4148,7 +4142,7 @@ int main(int argc, char *argv[]) { else r = tempfn_random(arg_directory, "machine.", &np); if (r < 0) { - log_error_errno(r, "Failed to generate name for snapshot: %m"); + log_error_errno(r, "Failed to generate name for directory snapshot: %m"); goto finish; } @@ -4158,7 +4152,12 @@ int main(int argc, char *argv[]) { goto finish; } - r = btrfs_subvol_snapshot(arg_directory, np, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + r = btrfs_subvol_snapshot(arg_directory, np, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA); if (r < 0) { log_error_errno(r, "Failed to create snapshot %s from %s: %m", np, arg_directory); goto finish; @@ -4168,7 +4167,7 @@ int main(int argc, char *argv[]) { arg_directory = np; np = NULL; - remove_subvol = true; + remove_directory = true; } else { r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); @@ -4182,7 +4181,13 @@ int main(int argc, char *argv[]) { } if (arg_template) { - r = btrfs_subvol_snapshot(arg_template, arg_directory, (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + r = btrfs_subvol_snapshot(arg_template, arg_directory, + (arg_read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA); if (r == -EEXIST) { if (!arg_quiet) log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template); @@ -4214,28 +4219,55 @@ int main(int argc, char *argv[]) { } } else { - char template[] = "/tmp/nspawn-root-XXXXXX"; - assert(arg_image); assert(!arg_template); - r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); - if (r == -EBUSY) { - r = log_error_errno(r, "Disk image %s is currently busy.", arg_image); - goto finish; - } - if (r < 0) { - r = log_error_errno(r, "Failed to create image lock: %m"); - goto finish; + if (arg_ephemeral) { + _cleanup_free_ char *np = NULL; + + r = tempfn_random(arg_image, "machine.", &np); + if (r < 0) { + log_error_errno(r, "Failed to generate name for image snapshot: %m"); + goto finish; + } + + r = image_path_lock(np, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r < 0) { + r = log_error_errno(r, "Failed to create image lock: %m"); + goto finish; + } + + r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL); + if (r < 0) { + r = log_error_errno(r, "Failed to copy image file: %m"); + goto finish; + } + + free(arg_image); + arg_image = np; + np = NULL; + + remove_image = true; + } else { + r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock); + if (r == -EBUSY) { + r = log_error_errno(r, "Disk image %s is currently busy.", arg_image); + goto finish; + } + if (r < 0) { + r = log_error_errno(r, "Failed to create image lock: %m"); + goto finish; + } } - if (!mkdtemp(template)) { - log_error_errno(errno, "Failed to create temporary directory: %m"); - r = -errno; + if (!mkdtemp(tmprootdir)) { + r = log_error_errno(errno, "Failed to create temporary directory: %m"); goto finish; } - arg_directory = strdup(template); + remove_tmprootdir = true; + + arg_directory = strdup(tmprootdir); if (!arg_directory) { r = log_oom(); goto finish; @@ -4255,6 +4287,10 @@ int main(int argc, char *argv[]) { &secondary); if (r < 0) goto finish; + + /* Now that we mounted the image, let's try to remove it again, if it is ephemeral */ + if (remove_image && unlink(arg_image) >= 0) + remove_image = false; } r = custom_mounts_prepare(); @@ -4321,20 +4357,35 @@ finish: "STOPPING=1\nSTATUS=Terminating..."); if (pid > 0) - kill(pid, SIGKILL); + (void) kill(pid, SIGKILL); /* Try to flush whatever is still queued in the pty */ - if (master >= 0) + if (master >= 0) { (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false); + master = safe_close(master); + } + + if (pid > 0) + (void) wait_for_terminate(pid, NULL); loop_remove(loop_nr, &image_fd); - if (remove_subvol && arg_directory) { + if (remove_directory && arg_directory) { int k; - k = btrfs_subvol_remove(arg_directory, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + k = rm_rf(arg_directory, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); if (k < 0) - log_warning_errno(k, "Cannot remove subvolume '%s', ignoring: %m", arg_directory); + log_warning_errno(k, "Cannot remove '%s', ignoring: %m", arg_directory); + } + + if (remove_image && arg_image) { + if (unlink(arg_image) < 0) + log_warning_errno(errno, "Can't remove image file '%s', ignoring: %m", arg_image); + } + + if (remove_tmprootdir) { + if (rmdir(tmprootdir) < 0) + log_debug_errno(errno, "Can't remove temporary root directory '%s', ignoring: %m", tmprootdir); } if (arg_machine) { diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index f9a6fd5f03..33debadb15 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -1324,3 +1324,15 @@ int dns_name_apply_idna(const char *name, char **ret) { return (int) n; } + +int dns_name_is_valid_or_address(const char *name) { + /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */ + + if (isempty(name)) + return 0; + + if (in_addr_from_string_auto(name, NULL, NULL) >= 0) + return 1; + + return dns_name_is_valid(name); +} diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index af780f0b8b..03f160369c 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -107,3 +107,5 @@ int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b); int dns_name_common_suffix(const char *a, const char *b, const char **ret); int dns_name_apply_idna(const char *name, char **ret); + +int dns_name_is_valid_or_address(const char *name); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 6414ba5246..712aff65b9 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -27,18 +27,20 @@ #include <sys/stat.h> #include <unistd.h> #include <linux/fs.h> + #include "alloc-util.h" #include "btrfs-util.h" #include "chattr-util.h" #include "copy.h" #include "dirent-util.h" +#include "env-util.h" #include "fd-util.h" #include "fs-util.h" #include "hashmap.h" #include "lockfile-util.h" #include "log.h" -#include "macro.h" #include "machine-image.h" +#include "macro.h" #include "mkdir.h" #include "path-util.h" #include "rm-rf.h" @@ -607,14 +609,14 @@ int image_clone(Image *i, const char *new_name, bool read_only) { new_path = strjoina("/var/lib/machines/", new_name); - r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); - if (r == -EOPNOTSUPP) { - /* No btrfs snapshots supported, create a normal directory then. */ - - r = copy_directory(i->path, new_path, false); - if (r >= 0) - (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); - } else if (r >= 0) + r = btrfs_subvol_snapshot(i->path, new_path, + (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | + BTRFS_SNAPSHOT_FALLBACK_COPY | + BTRFS_SNAPSHOT_FALLBACK_DIRECTORY | + BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE | + BTRFS_SNAPSHOT_RECURSIVE | + BTRFS_SNAPSHOT_QUOTA); + if (r >= 0) /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ (void) btrfs_subvol_auto_qgroup(new_path, 0, true); @@ -723,12 +725,17 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile * uses the device/inode number. This has the benefit that we * can even lock a tree that is a mount point, correctly. */ - if (path_equal(path, "/")) - return -EBUSY; - if (!path_is_absolute(path)) return -EINVAL; + if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) { + *local = *global = (LockFile) LOCK_FILE_INIT; + return 0; + } + + if (path_equal(path, "/")) + return -EBUSY; + if (stat(path, &st) >= 0) { if (asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) return -ENOMEM; @@ -746,7 +753,8 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile release_lock_file(&t); return r; } - } + } else + *global = (LockFile) LOCK_FILE_INIT; *local = t; return 0; @@ -782,6 +790,11 @@ int image_name_lock(const char *name, int operation, LockFile *ret) { if (!image_name_is_valid(name)) return -EINVAL; + if (getenv_bool("SYSTEMD_NSPAWN_LOCK") == 0) { + *ret = (LockFile) LOCK_FILE_INIT; + return 0; + } + if (streq(name, ".host")) return -EBUSY; diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index e2f097c95e..b4db4a6702 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -627,6 +627,18 @@ static void test_dns_name_apply_idna(void) { test_dns_name_apply_idna_one("föö.bär.", "xn--f-1gaa.xn--br-via"); } +static void test_dns_name_is_valid_or_address(void) { + assert_se(dns_name_is_valid_or_address(NULL) == 0); + assert_se(dns_name_is_valid_or_address("") == 0); + assert_se(dns_name_is_valid_or_address("foobar") > 0); + assert_se(dns_name_is_valid_or_address("foobar.com") > 0); + assert_se(dns_name_is_valid_or_address("foobar..com") == 0); + assert_se(dns_name_is_valid_or_address("foobar.com.") > 0); + assert_se(dns_name_is_valid_or_address("127.0.0.1") > 0); + assert_se(dns_name_is_valid_or_address("::") > 0); + assert_se(dns_name_is_valid_or_address("::1") > 0); +} + int main(int argc, char *argv[]) { test_dns_label_unescape(); @@ -654,6 +666,7 @@ int main(int argc, char *argv[]) { test_dns_name_compare_func(); test_dns_name_common_suffix(); test_dns_name_apply_idna(); + test_dns_name_is_valid_or_address(); return 0; } diff --git a/units/initrd-switch-root.service.in b/units/initrd-switch-root.service.in index 82893dafb1..b89f2348c7 100644 --- a/units/initrd-switch-root.service.in +++ b/units/initrd-switch-root.service.in @@ -17,4 +17,10 @@ AllowIsolate=yes Type=oneshot # we have to use "--force" here, otherwise systemd would umount /run ExecStart=@rootbindir@/systemctl --no-block --force switch-root /sysroot -KillMode=none + +# Just before switching to the new rootfs, systemd might send us a TERM signal +# depending on how fast we are to execute the main command and exit. If we get +# the SIGTERM signal that simply means that we succeed but haven't got enough +# time to exit properly. Since systemd considers SIGTERM as a failure for +# short-running process (aka Type=oneshot), instruct it to ignore this case. +SuccessExitStatus=SIGTERM |