diff options
author | Thomas Haller <thaller@redhat.com> | 2015-03-03 12:28:49 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2015-03-03 12:37:17 +0100 |
commit | 4e07f6117391ee4d112f8035039ef237c160e828 (patch) | |
tree | 8b4b5a304dc7adb0b6d491402134d7f4d38cfb18 | |
parent | 5599a82d0dc7b69497cb29f67bb0fb8cd618d32c (diff) | |
parent | 117cb022b13cedf4035e2a778b8d61528075a8b4 (diff) | |
download | NetworkManager-4e07f6117391ee4d112f8035039ef237c160e828.tar.gz |
dhcp: merge branch 'th/systemd-dhcp-integration' (bgo #742719)
Update internal dhcp library with new code from upstream systemd.
HEAD=117cb022b13cedf4035e2a778b8d61528075a8b4
MERGE=$(git rev-list --merges -n1 $HEAD)
# how did we modify the systemd code?
git diffs $MERGE^1 $HEAD -- :/src/dhcp-manager/systemd-dhcp/
# what changed in systemd since last merge:
git diffs $MERGE^2 $HEAD -- :/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/
https://bugzilla.gnome.org/show_bug.cgi?id=742719
37 files changed, 3496 insertions, 825 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index eb8984f538..ead105de4a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,6 +65,9 @@ SYSTEMD_DHCP_CFLAGS = \ -I$(top_srcdir)/src/dhcp-manager/systemd-dhcp libsystemd_dhcp_la_SOURCES = \ + dhcp-manager/systemd-dhcp/src/libsystemd/sd-id128/sd-id128.c \ + dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.c \ + dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.h \ dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c \ dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c \ dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h \ @@ -96,8 +99,11 @@ libsystemd_dhcp_la_SOURCES = \ dhcp-manager/systemd-dhcp/src/shared/util.h \ dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h \ dhcp-manager/systemd-dhcp/src/shared/list.h \ + dhcp-manager/systemd-dhcp/src/shared/log.h \ dhcp-manager/systemd-dhcp/src/shared/fileio.h \ dhcp-manager/systemd-dhcp/src/shared/fileio.c \ + dhcp-manager/systemd-dhcp/src/shared/path-util.c \ + dhcp-manager/systemd-dhcp/src/shared/path-util.h \ dhcp-manager/systemd-dhcp/src/shared/strv.h \ dhcp-manager/systemd-dhcp/src/shared/strv.c \ dhcp-manager/systemd-dhcp/src/shared/unaligned.h \ diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index 1ea836e586..de5bf2a9be 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -746,12 +746,6 @@ ip6_start (NMDhcpClient *client, goto error; } - r = sd_dhcp6_client_set_ifname (priv->client6, iface); - if (r < 0) { - nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP ifname (%d)", iface, r); - goto error; - } - r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client); if (r < 0) { nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP callback (%d)", iface, r); diff --git a/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h index a95f5bfb42..06ebcd1b23 100644 --- a/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h +++ b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h @@ -36,14 +36,20 @@ #include <unistd.h> #include <sys/syscall.h> +#include <net/if_arp.h> +#include <sys/resource.h> + #include "nm-logging.h" -static inline guint32 +/*****************************************************************************/ + +static inline NMLogLevel _slog_level_to_nm (int slevel) { switch (slevel) { case LOG_DEBUG: return LOGL_DEBUG; case LOG_WARNING: return LOGL_WARN; + case LOG_CRIT: case LOG_ERR: return LOGL_ERR; case LOG_INFO: case LOG_NOTICE: @@ -51,39 +57,37 @@ _slog_level_to_nm (int slevel) } } -#define log_meta(level, file, line, func, format, ...) \ -G_STMT_START { \ - guint32 _l = _slog_level_to_nm ((level)); \ - if (nm_logging_enabled (_l, LOGD_DHCP)) { \ - const char *_location = strrchr (file "", '/'); \ +#define log_internal(level, error, file, line, func, format, ...) \ +({ \ + int _nm_e = (error); \ + NMLogLevel _nm_l = _slog_level_to_nm ((level)); \ + if (nm_logging_enabled (_nm_l, LOGD_DHCP)) { \ + const char *_nm_location = strrchr ((""file), '/'); \ \ - _nm_log (_location ? _location + 1 : file, line, func, _l, LOGD_DHCP, 0, format, ## __VA_ARGS__); \ + _nm_log (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, ("%s"format), "sd-dhcp: ", ## __VA_ARGS__); \ } \ -} G_STMT_END + (_nm_e > 0 ? -_nm_e : _nm_e); \ +}) -#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) -#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_full(level, ...) log_meta((level), __FILE__, __LINE__, __func__, __VA_ARGS__); +#define log_full_errno(level, error, ...) \ +({ \ + log_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__); \ +}) -#define log_dhcp_client(client, fmt, ...) \ - log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) - -#define log_assert_failed(e, file, line, func) \ +#define log_assert_failed(text, file, line, func) \ G_STMT_START { \ - nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assertion failed: " # e); \ - g_assert (FALSE); \ + log_internal (LOG_CRIT, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", text, file, line, func); \ + g_assert_not_reached (); \ } G_STMT_END -#define log_assert_failed_unreachable(t, file, line, func) \ +#define log_assert_failed_return(text, file, line, func) \ G_STMT_START { \ - nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assert unreachable: " # t); \ - g_assert_not_reached (); \ + log_internal (LOG_DEBUG, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", text, file, line, func); \ + g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, text); \ } G_STMT_END -#define log_assert_failed_return(e, file, line, func) \ - nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assert return: " # e); \ -#define log_oom nm_log_err(LOGD_CORE, "%s:%s/%s: OOM", __FILE__, __LINE__, __func__) +/*****************************************************************************/ /* Can't include both net/if.h and linux/if.h; so have to define this here */ #ifndef IFNAMSIZ diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.c new file mode 100644 index 0000000000..c33761bc13 --- /dev/null +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.c @@ -0,0 +1,111 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2015 Tom Gundersen <teg@jklmen> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "nm-sd-adapt.h" + +#include "sd-id128.h" +#if 0 /* NM_IGNORED a*/ +#include "libudev.h" +#include "udev-util.h" + +#include "virt.h" +#include "sparse-endian.h" +#else /* NM_IGNORED */ +#include <net/if.h> +#endif /* NM_IGNORED */ +#include "siphash24.h" + +#include "dhcp6-protocol.h" +#include "dhcp-identifier.h" +#include "network-internal.h" + +#define SYSTEMD_PEN 43793 +#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) + +int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { + sd_id128_t machine_id; + int r; + + assert(duid); + assert(len); + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + duid->type = htobe16(DHCP6_DUID_EN); + duid->en.pen = htobe32(SYSTEMD_PEN); + *len = sizeof(duid->type) + sizeof(duid->en); + + /* a bit of snake-oil perhaps, but no need to expose the machine-id + directly */ + siphash24(duid->en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes); + + return 0; +} + + +int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id) { +#if 0 /* NM_IGNORED */ + /* name is a pointer to memory in the udev_device struct, so must + have the same scope */ + _cleanup_udev_device_unref_ struct udev_device *device = NULL; +#else /* NM_IGNORED */ + char name_buf[IF_NAMESIZE]; +#endif /* NM_IGNORED */ + const char *name = NULL; + uint64_t id; + +#if 0 /* NM_IGNORED */ + if (detect_container(NULL) <= 0) { + /* not in a container, udev will be around */ + _cleanup_udev_unref_ struct udev *udev; + char ifindex_str[2 + DECIMAL_STR_MAX(int)]; + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + sprintf(ifindex_str, "n%d", ifindex); + device = udev_device_new_from_device_id(udev, ifindex_str); + if (device) { + if (udev_device_get_is_initialized(device) <= 0) + /* not yet ready */ + return -EBUSY; + + name = net_get_name(device); + } + } +#else /* NM_IGNORED */ + name = if_indextoname(ifindex, name_buf); +#endif /* NM_IGNORED */ + + if (name) + siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes); + else + /* fall back to MAC address if no predictable name available */ + siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes); + + /* fold into 32 bits */ + *_id = (id & 0xffffffff) ^ (id >> 32); + + return 0; +} diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.h new file mode 100644 index 0000000000..5d4da21cb0 --- /dev/null +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.h @@ -0,0 +1,65 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2015 Tom Gundersen <teg@jklmen> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "nm-sd-adapt.h" + +#include "macro.h" +#include "sparse-endian.h" +#include "sd-id128.h" + +/* RFC 3315 section 9.1: + * A DUID can be no more than 128 octets long (not including the type code). + */ +#define MAX_DUID_LEN 128 + +struct duid { + uint16_t type; + union { + struct { + /* DHCP6_DUID_LLT */ + uint16_t htype; + uint32_t time; + uint8_t haddr[0]; + } _packed_ llt; + struct { + /* DHCP6_DUID_EN */ + uint32_t pen; + uint8_t id[8]; + } _packed_ en; + struct { + /* DHCP6_DUID_LL */ + int16_t htype; + uint8_t haddr[0]; + } _packed_ ll; + struct { + /* DHCP6_DUID_UUID */ + sd_id128_t uuid; + } _packed_ uuid; + struct { + uint8_t data[MAX_DUID_LEN]; + } _packed_ raw; + }; +} _packed_; + +int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); +int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id); diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h index 28c0e63454..b94c43f3b8 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h @@ -73,4 +73,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref); #define DHCP_CLIENT_DONT_DESTROY(client) \ _cleanup_dhcp_client_unref_ _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client) -#define log_dhcp_client(client, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) +#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c index 26e6e9f3df..0b63028b46 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c @@ -20,7 +20,6 @@ #include "nm-sd-adapt.h" #include <errno.h> -#include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <linux/if_packet.h> @@ -28,7 +27,6 @@ #include <net/ethernet.h> #include <net/if_arp.h> #include <stdio.h> -#include <unistd.h> #include <linux/filter.h> #include "socket-util.h" @@ -65,7 +63,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- mac address length */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c index e6ebf86ae1..bb9bd90491 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c @@ -20,22 +20,14 @@ #include "nm-sd-adapt.h" -#include <stdlib.h> #include <errno.h> #include <string.h> -#include <stdio.h> #include <net/ethernet.h> #include <net/if_arp.h> -#include <sys/param.h> -#include "util.h" -#include "list.h" #include "dhcp-protocol.h" -#include "dhcp-lease-internal.h" #include "dhcp-internal.h" -#include "sd-dhcp-lease.h" -#include "sd-dhcp-client.h" #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h index e29e2f0ee0..ee4014faab 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-internal.h @@ -58,7 +58,7 @@ struct DHCP6IA { typedef struct DHCP6IA DHCP6IA; -#define log_dhcp6_client(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) +#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) int dhcp_network_icmp6_bind_router_solicitation(int index); int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr); diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h index e46470d20e..6598d86074 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp6-protocol.h @@ -53,6 +53,8 @@ enum { DHCP6_PORT_CLIENT = 546, }; +#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC +#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC #define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC #define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC #define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC @@ -73,6 +75,7 @@ enum { enum DHCP6State { DHCP6_STATE_STOPPED = 0, + DHCP6_STATE_INFORMATION_REQUEST = 1, DHCP6_STATE_SOLICITATION = 2, DHCP6_STATE_REQUEST = 3, DHCP6_STATE_BOUND = 4, diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c index 8251269a0a..bc691abf26 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.c @@ -24,12 +24,10 @@ #include <netinet/ether.h> #include <linux/if.h> #include <arpa/inet.h> -#include <fnmatch.h> #if 0 /* NM_IGNORED */ #include "strv.h" #include "siphash24.h" -#include "libudev-private.h" #endif /* NM_IGNORED */ #include "dhcp-lease-internal.h" #if 0 /* NM_IGNORED */ @@ -92,10 +90,10 @@ int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8 } bool net_match_config(const struct ether_addr *match_mac, - const char *match_path, - const char *match_driver, - const char *match_type, - const char *match_name, + char * const *match_paths, + char * const *match_drivers, + char * const *match_types, + char * const *match_names, Condition *match_host, Condition *match_virt, Condition *match_kernel, @@ -108,37 +106,37 @@ bool net_match_config(const struct ether_addr *match_mac, const char *dev_name) { if (match_host && !condition_test(match_host)) - return 0; + return false; if (match_virt && !condition_test(match_virt)) - return 0; + return false; if (match_kernel && !condition_test(match_kernel)) - return 0; + return false; if (match_arch && !condition_test(match_arch)) - return 0; + return false; if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) - return 0; + return false; - if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0))) - return 0; + if (!strv_isempty(match_paths) && + (!dev_path || !strv_fnmatch(match_paths, dev_path, 0))) + return false; - if (match_driver) { - if (dev_parent_driver && !streq(match_driver, dev_parent_driver)) - return 0; - else if (!streq_ptr(match_driver, dev_driver)) - return 0; - } + if (!strv_isempty(match_drivers) && + (!dev_driver || !strv_fnmatch(match_drivers, dev_driver, 0))) + return false; - if (match_type && !streq_ptr(match_type, dev_type)) - return 0; + if (!strv_isempty(match_types) && + (!dev_type || !strv_fnmatch_or_empty(match_types, dev_type, 0))) + return false; - if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0))) - return 0; + if (!strv_isempty(match_names) && + (!dev_name || !strv_fnmatch_or_empty(match_names, dev_name, 0))) + return false; - return 1; + return true; } int config_parse_net_condition(const char *unit, @@ -221,6 +219,49 @@ int config_parse_ifname(const char *unit, return 0; } +int config_parse_ifnames(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 ***sv = data; + const char *word, *state; + size_t l; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + FOREACH_WORD(word, l, rvalue, state) { + char *n; + + n = strndup(word, l); + if (!n) + return log_oom(); + + if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); + free(n); + return 0; + } + + r = strv_consume(sv, n); + if (r < 0) + return log_oom(); + } + + return 0; +} + int config_parse_ifalias(const char *unit, const char *filename, unsigned line, @@ -233,7 +274,7 @@ int config_parse_ifalias(const char *unit, void *userdata) { char **s = data; - char *n; + _cleanup_free_ char *n = NULL; assert(filename); assert(lvalue); @@ -247,17 +288,15 @@ int config_parse_ifalias(const char *unit, if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - free(n); return 0; } free(*s); - if (*n) + if (*n) { *s = n; - else { - free(n); + n = NULL; + } else *s = NULL; - } return 0; } diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h index 7a482f2043..f94099f57a 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/network-internal.h @@ -23,8 +23,6 @@ #include "nm-sd-adapt.h" -#include <netinet/ether.h> -#include <netinet/in.h> #include <stdbool.h> #if 0 /* NM_IGNORED */ @@ -32,10 +30,10 @@ #include "condition.h" bool net_match_config(const struct ether_addr *match_mac, - const char *match_path, - const char *match_driver, - const char *match_type, - const char *match_name, + char * const *match_path, + char * const *match_driver, + char * const *match_type, + char * const *match_name, Condition *match_host, Condition *match_virt, Condition *match_kernel, @@ -59,6 +57,10 @@ int config_parse_ifname(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_ifnames(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_ifalias(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/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c index d5ada92375..d3cc1b8cd4 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-client.c @@ -26,21 +26,19 @@ #include <net/ethernet.h> #include <net/if_arp.h> #include <linux/if_infiniband.h> -#include <netinet/ether.h> -#include <sys/param.h> #include <sys/ioctl.h> #include "util.h" -#include "list.h" #include "refcnt.h" #include "async.h" #include "dhcp-protocol.h" #include "dhcp-internal.h" #include "dhcp-lease-internal.h" +#include "dhcp-identifier.h" #include "sd-dhcp-client.h" -#define MAX_CLIENT_ID_LEN 64 /* Arbitrary limit */ +#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN struct sd_dhcp_client { @@ -62,29 +60,31 @@ struct sd_dhcp_client { uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; - union { - struct { - uint8_t type; /* 0: Generic (non-LL) (RFC 2132) */ - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ gen; - struct { - uint8_t type; /* 1: Ethernet Link-Layer (RFC 2132) */ - uint8_t haddr[ETH_ALEN]; - } _packed_ eth; - struct { - uint8_t type; /* 2 - 254: ARP/Link-Layer (RFC 2132) */ - uint8_t haddr[0]; - } _packed_ ll; - struct { - uint8_t type; /* 255: Node-specific (RFC 4361) */ - uint8_t iaid[4]; - uint8_t duid[MAX_CLIENT_ID_LEN - 4]; - } _packed_ ns; - struct { - uint8_t type; - uint8_t data[MAX_CLIENT_ID_LEN]; - } _packed_ raw; - } client_id; + struct { + uint8_t type; + union { + struct { + /* 0: Generic (non-LL) (RFC 2132) */ + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ gen; + struct { + /* 1: Ethernet Link-Layer (RFC 2132) */ + uint8_t haddr[ETH_ALEN]; + } _packed_ eth; + struct { + /* 2 - 254: ARP/Link-Layer (RFC 2132) */ + uint8_t haddr[0]; + } _packed_ ll; + struct { + /* 255: Node-specific (RFC 4361) */ + uint32_t iaid; + struct duid duid; + } _packed_ ns; + struct { + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ raw; + }; + } _packed_ client_id; size_t client_id_len; char *hostname; char *vendor_class_identifier; @@ -241,10 +241,9 @@ int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type, *data = NULL; *data_len = 0; if (client->client_id_len) { - *type = client->client_id.raw.type; + *type = client->client_id.type; *data = client->client_id.raw.data; - *data_len = client->client_id_len - - sizeof (client->client_id.raw.type); + *data_len = client->client_id_len - sizeof(client->client_id.type); } return 0; @@ -272,8 +271,8 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type, break; } - if (client->client_id_len == data_len + sizeof (client->client_id.raw.type) && - client->client_id.raw.type == type && + if (client->client_id_len == data_len + sizeof(client->client_id.type) && + client->client_id.type == type && memcmp(&client->client_id.raw.data, data, data_len) == 0) return 0; @@ -284,9 +283,9 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type, client_stop(client, DHCP_EVENT_STOP); } - client->client_id.raw.type = type; + client->client_id.type = type; memcpy(&client->client_id.raw.data, data, data_len); - client->client_id_len = data_len + sizeof (client->client_id.raw.type); + client->client_id_len = data_len + sizeof (client->client_id.type); if (need_restart && client->state != DHCP_STATE_STOPPED) sd_dhcp_client_start(client); @@ -402,7 +401,7 @@ static void client_stop(sd_dhcp_client *client, int error) { static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, uint8_t type, size_t *_optlen, size_t *_optoffset) { - _cleanup_free_ DHCPPacket *packet = NULL; + _cleanup_free_ DHCPPacket *packet; size_t optlen, optoffset, size; be16_t max_size; usec_t time_now; @@ -416,8 +415,6 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, assert(_optoffset); assert(type == DHCP_DISCOVER || type == DHCP_REQUEST); - /* See RFC2131 section 4.4.1 */ - optlen = DHCP_MIN_OPTIONS_SIZE; size = sizeof(DHCPPacket) + optlen; @@ -465,12 +462,21 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, if (client->arp_type == ARPHRD_ETHER) memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); - /* If no client identifier exists, construct one from an ethernet - address if present */ - if (client->client_id_len == 0 && client->arp_type == ARPHRD_ETHER) { - client->client_id.eth.type = ARPHRD_ETHER; - memcpy(&client->client_id.eth.haddr, &client->mac_addr, ETH_ALEN); - client->client_id_len = sizeof (client->client_id.eth); + /* If no client identifier exists, construct an RFC 4361-compliant one */ + if (client->client_id_len == 0) { + size_t duid_len; + + client->client_id.type = 255; + + r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid); + if (r < 0) + return r; + + r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len); + if (r < 0) + return r; + + client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len; } /* Some DHCP servers will refuse to issue an DHCP lease if the Client @@ -479,7 +485,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, DHCP_OPTION_CLIENT_IDENTIFIER, client->client_id_len, - &client->client_id.raw); + &client->client_id); if (r < 0) return r; } @@ -504,7 +510,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, Note (from ConnMan): Some DHCP servers will send bigger DHCP packets than the defined default size unless the Maximum Messge Size option - is explicitely set + is explicitly set RFC3442 "Requirements to Avoid Sizing Constraints": Because a full routing table can be quite large, the standard 576 @@ -1035,7 +1041,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, if (client->client_id_len) { r = dhcp_lease_set_client_id(lease, - (uint8_t *) &client->client_id.raw, + (uint8_t *) &client->client_id, client->client_id_len); if (r < 0) return r; @@ -1102,7 +1108,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, if (client->client_id_len) { r = dhcp_lease_set_client_id(lease, - (uint8_t *) &client->client_id.raw, + (uint8_t *) &client->client_id, client->client_id_len); if (r < 0) return r; @@ -1386,8 +1392,10 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, client->last_addr = client->lease->address; r = client_set_lease_timeouts(client); - if (r < 0) + if (r < 0) { + log_dhcp_client(client, "could not set lease timeouts"); goto error; + } r = dhcp_network_bind_udp_socket(client->lease->address, DHCP_PORT_CLIENT); @@ -1615,7 +1623,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { r = client_start(client); if (r >= 0) - log_dhcp_client(client, "STARTED on ifindex %u", client->index); + log_dhcp_client(client, "STARTED on ifindex %i", client->index); return r; } @@ -1674,7 +1682,7 @@ sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) { } sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { - if (client && REFCNT_DEC(client->n_ref) <= 0) { + if (client && REFCNT_DEC(client->n_ref) == 0) { log_dhcp_client(client, "FREE"); client_initialize(client); diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c index 6a28f9b01f..14658742c7 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c @@ -24,24 +24,15 @@ #include <errno.h> #include <string.h> #include <stdio.h> -#include <net/ethernet.h> #include <arpa/inet.h> -#include <sys/param.h> -#include "util.h" -#include "list.h" -#if 0 /* NM_IGNORED */ -#include "mkdir.h" -#endif /* NM_IGNORED */ #include "fileio.h" #include "unaligned.h" #include "in-addr-util.h" #include "dhcp-protocol.h" -#include "dhcp-internal.h" #include "dhcp-lease-internal.h" #include "sd-dhcp-lease.h" -#include "sd-dhcp-client.h" #include "network-internal.h" int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { @@ -55,7 +46,7 @@ int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) { assert_return(lease, -EINVAL); - assert_return(lease, -EINVAL); + assert_return(lifetime, -EINVAL); *lifetime = lease->lifetime; @@ -197,7 +188,7 @@ sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { } sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { - if (lease && REFCNT_DEC(lease->n_ref) <= 0) { + if (lease && REFCNT_DEC(lease->n_ref) == 0) { free(lease->hostname); free(lease->domainname); free(lease->dns); @@ -501,11 +492,20 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, case DHCP_OPTION_DOMAIN_NAME: { _cleanup_free_ char *domainname = NULL; + char *e; r = lease_parse_string(option, len, &domainname); if (r < 0) return r; + /* Chop off trailing dot of domain name that some DHCP + * servers send us back. Internally we want to store + * host names without trailing dots and + * host_name_is_valid() doesn't accept them. */ + e = endswith(domainname, "."); + if (e) + *e = 0; + if (!hostname_is_valid(domainname) || is_localhost(domainname)) break; @@ -518,11 +518,16 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, case DHCP_OPTION_HOST_NAME: { _cleanup_free_ char *hostname = NULL; + char *e; r = lease_parse_string(option, len, &hostname); if (r < 0) return r; + e = endswith(hostname, "."); + if (e) + *e = 0; + if (!hostname_is_valid(hostname) || is_localhost(hostname)) break; @@ -667,7 +672,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); if (r >= 0) { - _cleanup_free_ char *client_id_hex = NULL; + _cleanup_free_ char *client_id_hex; client_id_hex = hexmem (client_id, client_id_len); if (!client_id_hex) { @@ -689,7 +694,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { finish: if (r < 0) - log_error("Failed to save lease data %s: %s", lease_file, strerror(-r)); + log_error_errno(r, "Failed to save lease data %s: %m", lease_file); return r; } @@ -729,8 +734,7 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (r == -ENOENT) return 0; - log_error("Failed to read %s: %s", lease_file, strerror(-r)); - return r; + return log_error_errno(r, "Failed to read %s: %m", lease_file); } r = inet_pton(AF_INET, address, &addr); diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c index 792e46d57a..3687abcbef 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-client.c @@ -29,9 +29,7 @@ #if 0 /* NM_IGNORED */ #include "udev.h" #include "udev-util.h" -#include "virt.h" #endif /* NM_IGNORED */ -#include "siphash24.h" #include "util.h" #include "refcnt.h" @@ -40,14 +38,7 @@ #include "dhcp6-protocol.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" - -#define SYSTEMD_PEN 43793 -#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) - -/* RFC 3315 section 9.1: - * A DUID can be no more than 128 octets long (not including the type code). - */ -#define MAX_DUID_LEN 128 +#include "dhcp-identifier.h" #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN @@ -61,12 +52,12 @@ struct sd_dhcp6_client { uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; - char ifname[IFNAMSIZ]; DHCP6IA ia_na; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; int fd; + bool information_request; be16_t *req_opts; size_t req_opts_allocated; size_t req_opts_len; @@ -77,32 +68,7 @@ struct sd_dhcp6_client { sd_event_source *timeout_resend_expire; sd_dhcp6_client_cb_t cb; void *userdata; - union { - struct { - uint16_t type; /* DHCP6_DUID_LLT */ - uint16_t htype; - uint32_t time; - uint8_t haddr[0]; - } _packed_ llt; - struct { - uint16_t type; /* DHCP6_DUID_EN */ - uint32_t pen; - uint8_t id[8]; - } _packed_ en; - struct { - uint16_t type; /* DHCP6_DUID_LL */ - uint16_t htype; - uint8_t haddr[0]; - } _packed_ ll; - struct { - uint16_t type; /* DHCP6_DUID_UUID */ - sd_id128_t uuid; - } _packed_ uuid; - struct { - uint16_t type; - uint8_t data[MAX_DUID_LEN]; - } _packed_ raw; - } duid; + struct duid duid; size_t duid_len; }; @@ -205,19 +171,19 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du switch (type) { case DHCP6_DUID_LLT: - if (duid_len <= sizeof(client->duid.llt) - 2) + if (duid_len <= sizeof(client->duid.llt)) return -EINVAL; break; case DHCP6_DUID_EN: - if (duid_len != sizeof(client->duid.en) - 2) + if (duid_len != sizeof(client->duid.en)) return -EINVAL; break; case DHCP6_DUID_LL: - if (duid_len <= sizeof(client->duid.ll) - 2) + if (duid_len <= sizeof(client->duid.ll)) return -EINVAL; break; case DHCP6_DUID_UUID: - if (duid_len != sizeof(client->duid.uuid) - 2) + if (duid_len != sizeof(client->duid.uuid)) return -EINVAL; break; default: @@ -225,9 +191,28 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du break; } - client->duid.raw.type = htobe16(type); + client->duid.type = htobe16(type); memcpy(&client->duid.raw.data, duid, duid_len); - client->duid_len = duid_len + 2; /* +2 for sizeof(type) */ + client->duid_len = duid_len + sizeof(client->duid.type); + + return 0; +} + +int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, + bool enabled) { + assert_return(client, -EINVAL); + + client->information_request = enabled; + + return 0; +} + +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, + bool *enabled) { + assert_return(client, -EINVAL); + assert_return(enabled, -EINVAL); + + *enabled = client->information_request; return 0; } @@ -338,6 +323,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { message->transaction_id = client->transaction_id; switch(client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + message->type = DHCP6_INFORMATION_REQUEST; + + break; + case DHCP6_STATE_SOLICITATION: message->type = DHCP6_SOLICIT; @@ -499,6 +489,12 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, client->timeout_resend = sd_event_source_unref(client->timeout_resend); switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + init_retransmit_time = DHCP6_INF_TIMEOUT; + max_retransmit_time = DHCP6_INF_MAX_RT; + + break; + case DHCP6_STATE_SOLICITATION: if (client->retransmit_count && client->lease) { @@ -631,24 +627,16 @@ error: } static int client_ensure_iaid(sd_dhcp6_client *client) { - const char *name; - uint64_t id; + int r; assert(client); if (client->ia_na.id) return 0; - name = client->ifname; - if (name) - siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes); - else - /* fall back to mac address if no predictable name available */ - siphash24((uint8_t*)&id, &client->mac_addr, - client->mac_addr_len, HASH_KEY.bytes); - - /* fold into 32 bits */ - client->ia_na.id = (id & 0xffffffff) ^ (id >> 32); + r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id); + if (r < 0) + return r; return 0; } @@ -726,6 +714,12 @@ static int client_parse_message(sd_dhcp6_client *client, break; case DHCP6_OPTION_IA_NA: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Information request ignoring IA NA option"); + + break; + } + r = dhcp6_option_parse_ia(&optval, &optlen, optcode, &lease->ia); if (r < 0 && r != -ENOMSG) @@ -752,16 +746,21 @@ static int client_parse_message(sd_dhcp6_client *client, } } - if ((r < 0 && r != -ENOMSG) || !clientid) { + if (r == -ENOMSG) + r = 0; + + if (r < 0 || !clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); return -EINVAL; } - r = dhcp6_lease_get_serverid(lease, &id, &id_len); - if (r < 0) - log_dhcp6_client(client, "%s has no server id", - dhcp6_message_type_to_string(message->type)); + if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { + r = dhcp6_lease_get_serverid(lease, &id, &id_len); + if (r < 0) + log_dhcp6_client(client, "%s has no server id", + dhcp6_message_type_to_string(message->type)); + } return r; } @@ -793,12 +792,15 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, return 0; } - if (client->lease) + if (client->lease) { dhcp6_lease_clear_timers(&client->lease->ia); + client->lease = sd_dhcp6_lease_unref(client->lease); + } - client->lease = sd_dhcp6_lease_unref(client->lease); - client->lease = lease; - lease = NULL; + if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { + client->lease = lease; + lease = NULL; + } return DHCP6_STATE_BOUND; } @@ -825,7 +827,8 @@ static int client_receive_advertise(sd_dhcp6_client *client, return r; r = dhcp6_lease_get_preference(client->lease, &pref_lease); - if (!client->lease || r < 0 || pref_advertise > pref_lease) { + + if (r < 0 || pref_advertise > pref_lease) { sd_dhcp6_lease_unref(client->lease); client->lease = lease; lease = NULL; @@ -842,7 +845,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); - _cleanup_free_ DHCP6Message *message = NULL; + _cleanup_free_ DHCP6Message *message; int r, buflen, len; assert(s); @@ -892,6 +895,17 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, return 0; switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + r = client_receive_reply(client, message, len); + if (r < 0) + return 0; + + client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST); + + client_start(client, DHCP6_STATE_STOPPED); + + break; + case DHCP6_STATE_SOLICITATION: r = client_receive_advertise(client, message, len); @@ -967,37 +981,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) switch (state) { case DHCP6_STATE_STOPPED: - case DHCP6_STATE_SOLICITATION: - - r = client_ensure_iaid(client); - if (r < 0) - return r; - - r = dhcp6_network_bind_udp_socket(client->index, NULL); - if (r < 0) - return r; - - client->fd = r; - - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - return r; + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + client->state = DHCP6_STATE_STOPPED; - r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message"); - if (r < 0) - return r; + return 0; + } + /* fall through */ + case DHCP6_STATE_SOLICITATION: client->state = DHCP6_STATE_SOLICITATION; break; + case DHCP6_STATE_INFORMATION_REQUEST: case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: @@ -1102,6 +1098,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) int sd_dhcp6_client_start(sd_dhcp6_client *client) { int r = 0; + enum DHCP6State state = DHCP6_STATE_SOLICITATION; assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); @@ -1111,7 +1108,44 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) if (r < 0) return r; - return client_start(client, DHCP6_STATE_SOLICITATION); + r = client_ensure_iaid(client); + if (r < 0) + return r; + + r = dhcp6_network_bind_udp_socket(client->index, NULL); + if (r < 0) + return r; + + client->fd = r; + + r = sd_event_add_io(client->event, &client->receive_message, + client->fd, EPOLLIN, client_receive_message, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->receive_message, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->receive_message, + "dhcp6-receive-message"); + if (r < 0) + goto error; + + if (client->information_request) + state = DHCP6_STATE_INFORMATION_REQUEST; + + log_dhcp6_client(client, "Started in %s mode", + client->information_request? "Information request": + "Managed"); + + return client_start(client, state); + +error: + client_reset(client); + return r; } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, @@ -1158,7 +1192,7 @@ sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) { } sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { - if (client && REFCNT_DEC(client->n_ref) <= 0) { + if (client && REFCNT_DEC(client->n_ref) == 0) { client_reset(client); sd_dhcp6_client_detach_event(client); @@ -1176,7 +1210,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL; #if 0 /* NM_IGNORED */ - sd_id128_t machine_id; int r; #endif /* NM_IGNORED */ size_t t; @@ -1197,17 +1230,9 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) #if 0 /* NM_IGNORED */ /* initialize DUID */ - client->duid.en.type = htobe16(DHCP6_DUID_EN); - client->duid.en.pen = htobe32(SYSTEMD_PEN); - client->duid_len = sizeof(client->duid.en); - - r = sd_id128_get_machine(&machine_id); + r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); if (r < 0) return r; - - /* a bit of snake-oil perhaps, but no need to expose the machine-id - directly */ - siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes); #endif /* NM_IGNORED */ client->req_opts_len = ELEMENTSOF(default_req_opts); @@ -1225,16 +1250,3 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) return 0; } -/*******************************************/ -/* NetworkManager additions */ - -int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) -{ - assert_return(client, -EINVAL); - assert_return(ifname, -EINVAL); - assert_return(strlen (ifname) < sizeof (client->ifname), -EINVAL); - - strcpy(client->ifname, ifname); - return 0; -} - diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c index 6a1cb22daa..8ac18a7566 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp6-lease.c @@ -112,9 +112,11 @@ int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { } int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { - assert_return(lease, -EINVAL); assert_return(preference, -EINVAL); + if (!lease) + return -EINVAL; + *preference = lease->preference; return 0; @@ -146,10 +148,9 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { return 0; } -int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease, - struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); assert_return(lifetime_preferred, -EINVAL); @@ -169,22 +170,9 @@ int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease, return 0; } -int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease, - struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { - assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); - - if (!lease->ia.addresses) - return -ENOMSG; - - lease->addr_iter = lease->ia.addresses; - - return sd_dhcp6_lease_get_next_address(lease, addr, lifetime_preferred, - lifetime_valid); +void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { + if (lease) + lease->addr_iter = lease->ia.addresses; } sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { @@ -195,7 +183,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { } sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { - if (lease && REFCNT_DEC(lease->n_ref) <= 0) { + if (lease && REFCNT_DEC(lease->n_ref) == 0) { free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd/sd-id128/sd-id128.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd/sd-id128/sd-id128.c new file mode 100644 index 0000000000..850461651a --- /dev/null +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd/sd-id128/sd-id128.c @@ -0,0 +1,235 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "nm-sd-adapt.h" + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include "util.h" +#include "macro.h" +#include "sd-id128.h" + +#if 0 /* NM_IGNORED */ +_public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) { + unsigned n; + + assert_return(s, NULL); + + for (n = 0; n < 16; n++) { + s[n*2] = hexchar(id.bytes[n] >> 4); + s[n*2+1] = hexchar(id.bytes[n] & 0xF); + } + + s[32] = 0; + + return s; +} + +_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { + unsigned n, i; + sd_id128_t t; + bool is_guid = false; + + assert_return(s, -EINVAL); + assert_return(ret, -EINVAL); + + for (n = 0, i = 0; n < 16;) { + int a, b; + + if (s[i] == '-') { + /* Is this a GUID? Then be nice, and skip over + * the dashes */ + + if (i == 8) + is_guid = true; + else if (i == 13 || i == 18 || i == 23) { + if (!is_guid) + return -EINVAL; + } else + return -EINVAL; + + i++; + continue; + } + + a = unhexchar(s[i++]); + if (a < 0) + return -EINVAL; + + b = unhexchar(s[i++]); + if (b < 0) + return -EINVAL; + + t.bytes[n++] = (a << 4) | b; + } + + if (i != (is_guid ? 36 : 32)) + return -EINVAL; + + if (s[i] != 0) + return -EINVAL; + + *ret = t; + return 0; +} + +static sd_id128_t make_v4_uuid(sd_id128_t id) { + /* Stolen from generate_random_uuid() of drivers/char/random.c + * in the kernel sources */ + + /* Set UUID version to 4 --- truly random generation */ + id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; + + /* Set the UUID variant to DCE */ + id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; + + return id; +} +#endif + +_public_ int sd_id128_get_machine(sd_id128_t *ret) { + static thread_local sd_id128_t saved_machine_id; + static thread_local bool saved_machine_id_valid = false; + _cleanup_close_ int fd = -1; + char buf[33]; + ssize_t k; + unsigned j; + sd_id128_t t; + + assert_return(ret, -EINVAL); + + if (saved_machine_id_valid) { + *ret = saved_machine_id; + return 0; + } + + fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, buf, 33, false); + if (k < 0) + return (int) k; + + if (k != 33) + return -EIO; + + if (buf[32] !='\n') + return -EIO; + + for (j = 0; j < 16; j++) { + int a, b; + + a = unhexchar(buf[j*2]); + b = unhexchar(buf[j*2+1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + } + + saved_machine_id = t; + saved_machine_id_valid = true; + + *ret = t; + return 0; +} + +#if 0 /* NM_IGNORED */ +_public_ int sd_id128_get_boot(sd_id128_t *ret) { + static thread_local sd_id128_t saved_boot_id; + static thread_local bool saved_boot_id_valid = false; + _cleanup_close_ int fd = -1; + char buf[36]; + ssize_t k; + unsigned j; + sd_id128_t t; + char *p; + + assert_return(ret, -EINVAL); + + if (saved_boot_id_valid) { + *ret = saved_boot_id; + return 0; + } + + fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, buf, 36, false); + if (k < 0) + return (int) k; + + if (k != 36) + return -EIO; + + for (j = 0, p = buf; j < 16; j++) { + int a, b; + + if (p >= buf + k - 1) + return -EIO; + + if (*p == '-') { + p++; + if (p >= buf + k - 1) + return -EIO; + } + + a = unhexchar(p[0]); + b = unhexchar(p[1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + + p += 2; + } + + saved_boot_id = t; + saved_boot_id_valid = true; + + *ret = t; + return 0; +} + +_public_ int sd_id128_randomize(sd_id128_t *ret) { + sd_id128_t t; + int r; + + assert_return(ret, -EINVAL); + + r = dev_urandom(&t, sizeof(t)); + if (r < 0) + return r; + + /* Turn this into a valid v4 UUID, to be nice. Note that we + * only guarantee this for newly generated UUIDs, not for + * pre-existing ones. */ + + *ret = make_v4_uuid(t); + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/fileio.c b/src/dhcp-manager/systemd-dhcp/src/shared/fileio.c index 84fd50495f..88b15fb4af 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/fileio.c +++ b/src/dhcp-manager/systemd-dhcp/src/shared/fileio.c @@ -510,15 +510,17 @@ static int parse_env_file_push( va_list aq, *ap = userdata; if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = utf8_escape_invalid(key); + _cleanup_free_ char *p; + p = utf8_escape_invalid(key); log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); return -EINVAL; } if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = utf8_escape_invalid(value); + _cleanup_free_ char *p; + p = utf8_escape_invalid(value); log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); return -EINVAL; } diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c index 530be45e14..488f6d1ebd 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c +++ b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.c @@ -245,12 +245,25 @@ int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *re return -EINVAL; } -unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr) { +unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { assert(addr); return 32 - u32ctz(be32toh(addr->s_addr)); } +struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { + assert(addr); + assert(prefixlen <= 32); + + /* Shifting beyond 32 is not defined, handle this specially. */ + if (prefixlen == 0) + addr->s_addr = 0; + else + addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); + + return addr; +} + int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { uint8_t msb_octet = *(uint8_t*) addr; @@ -286,9 +299,42 @@ int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask if (r < 0) return r; - assert(prefixlen > 0 && prefixlen < 32); + in_addr_prefixlen_to_netmask(mask, prefixlen); + return 0; +} + +int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { + assert(addr); - mask->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); + if (family == AF_INET) { + struct in_addr mask; - return 0; + if (!in_addr_prefixlen_to_netmask(&mask, prefixlen)) + return -EINVAL; + + addr->in.s_addr &= mask.s_addr; + return 0; + } + + if (family == AF_INET6) { + unsigned i; + + for (i = 0; i < 16; i++) { + uint8_t mask; + + if (prefixlen >= 8) { + mask = 0xFF; + prefixlen -= 8; + } else { + mask = 0xFF << (8 - prefixlen); + prefixlen = 0; + } + + addr->in6.s6_addr[i] &= mask; + } + + return 0; + } + + return -EAFNOSUPPORT; } diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h index 36afee2c42..715ded330f 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h @@ -41,11 +41,15 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); -unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr); +unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); +struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); +int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); static inline size_t FAMILY_ADDRESS_SIZE(int family) { assert(family == AF_INET || family == AF_INET6); return family == AF_INET6 ? 16 : 4; } + +#define IN_ADDR_NULL ((union in_addr_union) {}) diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/list.h b/src/dhcp-manager/systemd-dhcp/src/shared/list.h index dcb3e0d94c..af121d7009 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/list.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/list.h @@ -57,6 +57,14 @@ *_head = _item; \ } while(false) +/* Append an item to the list */ +#define LIST_APPEND(name,head,item) \ + do { \ + typeof(*(head)) *_tail; \ + LIST_FIND_TAIL(name,head,_tail); \ + LIST_INSERT_AFTER(name,head,_tail,item); \ + } while(false) + /* Remove an item from the list */ #define LIST_REMOVE(name,head,item) \ do { \ @@ -132,6 +140,18 @@ #define LIST_FOREACH_AFTER(name,i,p) \ for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) +/* Iterate through all the members of the list p is included in, but skip over p */ +#define LIST_FOREACH_OTHERS(name,i,p) \ + for (({ \ + (i) = (p); \ + while ((i) && (i)->name##_prev) \ + (i) = (i)->name##_prev; \ + if ((i) == (p)) \ + (i) = (p)->name##_next; \ + }); \ + (i); \ + (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) + /* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */ #define LIST_LOOP_BUT_ONE(name,i,head,p) \ diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/log.h b/src/dhcp-manager/systemd-dhcp/src/shared/log.h new file mode 100644 index 0000000000..ee276922a2 --- /dev/null +++ b/src/dhcp-manager/systemd-dhcp/src/shared/log.h @@ -0,0 +1,214 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "nm-sd-adapt.h" + +#include <stdbool.h> +#include <stdarg.h> +#include <syslog.h> +#include <sys/signalfd.h> +#include <errno.h> + +#include "macro.h" +#include "sd-id128.h" + +typedef enum LogTarget{ + LOG_TARGET_CONSOLE, + LOG_TARGET_CONSOLE_PREFIXED, + LOG_TARGET_KMSG, + LOG_TARGET_JOURNAL, + LOG_TARGET_JOURNAL_OR_KMSG, + LOG_TARGET_SYSLOG, + LOG_TARGET_SYSLOG_OR_KMSG, + LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ + LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ + LOG_TARGET_NULL, + _LOG_TARGET_MAX, + _LOG_TARGET_INVALID = -1 +} LogTarget; + +void log_set_target(LogTarget target); +void log_set_max_level(int level); +void log_set_facility(int facility); + +int log_set_target_from_string(const char *e); +int log_set_max_level_from_string(const char *e); + +void log_show_color(bool b); +bool log_get_show_color(void) _pure_; +void log_show_location(bool b); +bool log_get_show_location(void) _pure_; + +int log_show_color_from_string(const char *e); +int log_show_location_from_string(const char *e); + +LogTarget log_get_target(void) _pure_; +int log_get_max_level(void) _pure_; + +int log_open(void); +void log_close(void); +void log_forget_fds(void); + +void log_close_syslog(void); +void log_close_journal(void); +void log_close_kmsg(void); +void log_close_console(void); + +void log_parse_environment(void); + +#if 0 /* NM_IGNORED */ +int log_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) _printf_(6,7); + +int log_internalv( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, + va_list ap) _printf_(6,0); + +int log_object_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, ...) _printf_(8,9); + +int log_object_internalv( + int level, + int error, + const char*file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, + va_list ap) _printf_(8,0); + +int log_struct_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *format, ...) _printf_(6,0) _sentinel_; + +int log_oom_internal( + const char *file, + int line, + const char *func); + +/* This modifies the buffer passed! */ +int log_dump_internal( + int level, + int error, + const char *file, + int line, + const char *func, + char *buffer); + +/* Logging for various assertions */ +noreturn void log_assert_failed( + const char *text, + const char *file, + int line, + const char *func); + +noreturn void log_assert_failed_unreachable( + const char *text, + const char *file, + int line, + const char *func); + +void log_assert_failed_return( + const char *text, + const char *file, + int line, + const char *func); + +/* Logging with level */ +#define log_full_errno(level, error, ...) \ + ({ \ + int _level = (level), _e = (error); \ + (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ + : -abs(_e); \ + }) +#endif /* NM_IGNORED */ + +#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) + +/* Normal logging */ +#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) +#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) +#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) +#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) +#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) + +/* Logging triggered by an errno-like error */ +#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) +#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) +#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) +#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) +#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) +#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) + +#ifdef LOG_TRACE +# define log_trace(...) log_debug(__VA_ARGS__) +#else +# define log_trace(...) do {} while(0) +#endif + +/* Structured logging */ +#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) + +/* This modifies the buffer passed! */ +#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) + +#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) + +bool log_on_console(void) _pure_; + +const char *log_target_to_string(LogTarget target) _const_; +LogTarget log_target_from_string(const char *s) _pure_; + +/* Helpers to prepare various fields for structured logging */ +#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ +#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) +#define LOG_ERRNO(error) "ERRNO=%i", abs(error) + +void log_received_signal(int level, const struct signalfd_siginfo *si); + +void log_set_upgrade_syslog_to_journal(bool b); diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/macro.h b/src/dhcp-manager/systemd-dhcp/src/shared/macro.h index 82469d81f0..d5cb240b3b 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/macro.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/macro.h @@ -69,6 +69,10 @@ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wshadow\"") +#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") + #define REENABLE_WARNING \ _Pragma("GCC diagnostic pop") @@ -97,15 +101,15 @@ #error "Wut? Pointers are neither 4 nor 8 bytes long?" #endif -#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p)) -#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p)) -#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p)) +#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) +#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) +#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) static inline size_t ALIGN_TO(size_t l, size_t ali) { return ((l + ali - 1) & ~(ali - 1)); } -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p, ali)) +#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ static inline unsigned long ALIGN_POWER2(unsigned long u) { @@ -199,6 +203,17 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { UNIQ_T(X,xq); \ }) +/* [(x + y - 1) / y] suffers from an integer overflow, even though the + * computation should be possible in the given type. Therefore, we use + * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the + * quotient and the remainder, so both should be equally fast. */ +#define DIV_ROUND_UP(_x, _y) \ + __extension__ ({ \ + const typeof(_x) __x = (_x); \ + const typeof(_y) __y = (_y); \ + (__x / __y + !!(__x % __y)); \ + }) + #define assert_se(expr) \ do { \ if (_unlikely_(!(expr))) \ @@ -229,7 +244,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { #else #define assert_cc(expr) \ DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - struct CONCATENATE(_assert_struct_, __LINE__) { \ + struct CONCATENATE(_assert_struct_, __COUNTER__) { \ char x[(expr) ? 0 : -1]; \ }; \ REENABLE_WARNING @@ -266,6 +281,14 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { #define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) #define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) +/* The following macros add 1 when converting things, since UID 0 is a + * valid UID, while the pointer NULL is special */ +#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1)) +#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) + +#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1)) +#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) + #define memzero(x,l) (memset((x), 0, (l))) #define zero(x) (memzero(&(x), sizeof(x))) @@ -362,7 +385,8 @@ do { \ /* Returns the number of chars needed to format variables of the * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix. */ + * negative '-' prefix (hence works correctly on signed + * types). Includes space for the trailing NUL. */ #define DECIMAL_STR_MAX(type) \ (2+(sizeof(type) <= 1 ? 3 : \ sizeof(type) <= 2 ? 5 : \ @@ -386,7 +410,21 @@ do { \ _found; \ }) -#if 0 /* NM_IGNORED */ +/* Return a nulstr for a standard cascade of configuration directories, + * suitable to pass to conf_files_list_nulstr or config_parse_many. */ +#define CONF_DIRS_NULSTR(n) \ + "/etc/" n ".d\0" \ + "/run/" n ".d\0" \ + "/usr/local/lib/" n ".d\0" \ + "/usr/lib/" n ".d\0" \ + CONF_DIR_SPLIT_USR(n) + +#ifdef HAVE_SPLIT_USR +#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0" +#else +#define CONF_DIR_SPLIT_USR(n) +#endif + /* Define C11 thread_local attribute even on older gcc compiler * version */ #ifndef thread_local @@ -394,13 +432,14 @@ do { \ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 */ -#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) #define thread_local _Thread_local #else #define thread_local __thread #endif #endif +#if 0 /* NM_IGNORED */ /* Define C11 noreturn without <stdnoreturn.h> and even on older gcc * compiler versions */ #ifndef noreturn @@ -411,5 +450,16 @@ do { \ #endif #endif -#include "log.h" +#define UID_INVALID ((uid_t) -1) +#define GID_INVALID ((gid_t) -1) +#define MODE_INVALID ((mode_t) -1) #endif /* NM_IGNORED */ + +#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ + static inline void func##p(type *p) { \ + if (*p) \ + func(*p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#include "log.h" diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/path-util.c b/src/dhcp-manager/systemd-dhcp/src/shared/path-util.c new file mode 100644 index 0000000000..003a790db3 --- /dev/null +++ b/src/dhcp-manager/systemd-dhcp/src/shared/path-util.c @@ -0,0 +1,694 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010-2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "nm-sd-adapt.h" + +#if 0 /* NM_IGNORED */ +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/statvfs.h> + +#include "macro.h" +#include "util.h" +#include "log.h" +#include "strv.h" +#endif /* NM_IGNORED */ +#include "path-util.h" +#if 0 /* NM_IGNORED */ +#include "missing.h" + +bool path_is_absolute(const char *p) { + return p[0] == '/'; +} + +bool is_path(const char *p) { + return !!strchr(p, '/'); +} + +int path_get_parent(const char *path, char **_r) { + const char *e, *a = NULL, *b = NULL, *p; + char *r; + bool slash = false; + + assert(path); + assert(_r); + + if (!*path) + return -EINVAL; + + for (e = path; *e; e++) { + + if (!slash && *e == '/') { + a = b; + b = e; + slash = true; + } else if (slash && *e != '/') + slash = false; + } + + if (*(e-1) == '/') + p = a; + else + p = b; + + if (!p) + return -EINVAL; + + if (p == path) + r = strdup("/"); + else + r = strndup(path, p-path); + + if (!r) + return -ENOMEM; + + *_r = r; + return 0; +} + +char **path_split_and_make_absolute(const char *p) { + char **l; + assert(p); + + l = strv_split(p, ":"); + if (!l) + return NULL; + + if (!path_strv_make_absolute_cwd(l)) { + strv_free(l); + return NULL; + } + + return l; +} + +char *path_make_absolute(const char *p, const char *prefix) { + assert(p); + + /* Makes every item in the list an absolute path by prepending + * the prefix, if specified and necessary */ + + if (path_is_absolute(p) || !prefix) + return strdup(p); + + return strjoin(prefix, "/", p, NULL); +} + +char *path_make_absolute_cwd(const char *p) { + _cleanup_free_ char *cwd = NULL; + + assert(p); + + /* Similar to path_make_absolute(), but prefixes with the + * current working directory. */ + + if (path_is_absolute(p)) + return strdup(p); + + cwd = get_current_dir_name(); + if (!cwd) + return NULL; + + return strjoin(cwd, "/", p, NULL); +} + +int path_make_relative(const char *from_dir, const char *to_path, char **_r) { + char *r, *p; + unsigned n_parents; + + assert(from_dir); + assert(to_path); + assert(_r); + + /* Strips the common part, and adds ".." elements as necessary. */ + + if (!path_is_absolute(from_dir)) + return -EINVAL; + + if (!path_is_absolute(to_path)) + return -EINVAL; + + /* Skip the common part. */ + for (;;) { + size_t a; + size_t b; + + from_dir += strspn(from_dir, "/"); + to_path += strspn(to_path, "/"); + + if (!*from_dir) { + if (!*to_path) + /* from_dir equals to_path. */ + r = strdup("."); + else + /* from_dir is a parent directory of to_path. */ + r = strdup(to_path); + + if (!r) + return -ENOMEM; + + path_kill_slashes(r); + + *_r = r; + return 0; + } + + if (!*to_path) + break; + + a = strcspn(from_dir, "/"); + b = strcspn(to_path, "/"); + + if (a != b) + break; + + if (memcmp(from_dir, to_path, a) != 0) + break; + + from_dir += a; + to_path += b; + } + + /* If we're here, then "from_dir" has one or more elements that need to + * be replaced with "..". */ + + /* Count the number of necessary ".." elements. */ + for (n_parents = 0;;) { + from_dir += strspn(from_dir, "/"); + + if (!*from_dir) + break; + + from_dir += strcspn(from_dir, "/"); + n_parents++; + } + + r = malloc(n_parents * 3 + strlen(to_path) + 1); + if (!r) + return -ENOMEM; + + for (p = r; n_parents > 0; n_parents--, p += 3) + memcpy(p, "../", 3); + + strcpy(p, to_path); + path_kill_slashes(r); + + *_r = r; + return 0; +} + +char **path_strv_make_absolute_cwd(char **l) { + char **s; + + /* Goes through every item in the string list and makes it + * absolute. This works in place and won't rollback any + * changes on failure. */ + + STRV_FOREACH(s, l) { + char *t; + + t = path_make_absolute_cwd(*s); + if (!t) + return NULL; + + free(*s); + *s = t; + } + + return l; +} + +char **path_strv_resolve(char **l, const char *prefix) { + char **s; + unsigned k = 0; + bool enomem = false; + + if (strv_isempty(l)) + return l; + + /* Goes through every item in the string list and canonicalize + * the path. This works in place and won't rollback any + * changes on failure. */ + + STRV_FOREACH(s, l) { + char *t, *u; + _cleanup_free_ char *orig = NULL; + + if (!path_is_absolute(*s)) { + free(*s); + continue; + } + + if (prefix) { + orig = *s; + t = strappend(prefix, orig); + if (!t) { + enomem = true; + continue; + } + } else + t = *s; + + errno = 0; + u = canonicalize_file_name(t); + if (!u) { + if (errno == ENOENT) { + if (prefix) { + u = orig; + orig = NULL; + free(t); + } else + u = t; + } else { + free(t); + if (errno == ENOMEM || errno == 0) + enomem = true; + + continue; + } + } else if (prefix) { + char *x; + + free(t); + x = path_startswith(u, prefix); + if (x) { + /* restore the slash if it was lost */ + if (!startswith(x, "/")) + *(--x) = '/'; + + t = strdup(x); + free(u); + if (!t) { + enomem = true; + continue; + } + u = t; + } else { + /* canonicalized path goes outside of + * prefix, keep the original path instead */ + free(u); + u = orig; + orig = NULL; + } + } else + free(t); + + l[k++] = u; + } + + l[k] = NULL; + + if (enomem) + return NULL; + + return l; +} + +char **path_strv_resolve_uniq(char **l, const char *prefix) { + + if (strv_isempty(l)) + return l; + + if (!path_strv_resolve(l, prefix)) + return NULL; + + return strv_uniq(l); +} +#endif /* NM_IGNORED */ + +char *path_kill_slashes(char *path) { + char *f, *t; + bool slash = false; + + /* Removes redundant inner and trailing slashes. Modifies the + * passed string in-place. + * + * ///foo///bar/ becomes /foo/bar + */ + + for (f = path, t = path; *f; f++) { + + if (*f == '/') { + slash = true; + continue; + } + + if (slash) { + slash = false; + *(t++) = '/'; + } + + *(t++) = *f; + } + + /* Special rule, if we are talking of the root directory, a + trailing slash is good */ + + if (t == path && slash) + *(t++) = '/'; + + *t = 0; + return path; +} + +#if 0 /* NM_IGNORED */ +char* path_startswith(const char *path, const char *prefix) { + assert(path); + assert(prefix); + + if ((path[0] == '/') != (prefix[0] == '/')) + return NULL; + + for (;;) { + size_t a, b; + + path += strspn(path, "/"); + prefix += strspn(prefix, "/"); + + if (*prefix == 0) + return (char*) path; + + if (*path == 0) + return NULL; + + a = strcspn(path, "/"); + b = strcspn(prefix, "/"); + + if (a != b) + return NULL; + + if (memcmp(path, prefix, a) != 0) + return NULL; + + path += a; + prefix += b; + } +} + +bool path_equal(const char *a, const char *b) { + assert(a); + assert(b); + + if ((a[0] == '/') != (b[0] == '/')) + return false; + + for (;;) { + size_t j, k; + + a += strspn(a, "/"); + b += strspn(b, "/"); + + if (*a == 0 && *b == 0) + return true; + + if (*a == 0 || *b == 0) + return false; + + j = strcspn(a, "/"); + k = strcspn(b, "/"); + + if (j != k) + return false; + + if (memcmp(a, b, j) != 0) + return false; + + a += j; + b += k; + } +} + +bool path_equal_or_files_same(const char *a, const char *b) { + return path_equal(a, b) || files_same(a, b) > 0; +} + +char* path_join(const char *root, const char *path, const char *rest) { + assert(path); + + if (!isempty(root)) + return strjoin(root, endswith(root, "/") ? "" : "/", + path[0] == '/' ? path+1 : path, + rest ? (endswith(path, "/") ? "" : "/") : NULL, + rest && rest[0] == '/' ? rest+1 : rest, + NULL); + else + return strjoin(path, + rest ? (endswith(path, "/") ? "" : "/") : NULL, + rest && rest[0] == '/' ? rest+1 : rest, + NULL); +} + +int path_is_mount_point(const char *t, bool allow_symlink) { + + union file_handle_union h = FILE_HANDLE_INIT; + int mount_id = -1, mount_id_parent = -1; + _cleanup_free_ char *parent = NULL; + struct stat a, b; + int r; + bool nosupp = false; + + /* We are not actually interested in the file handles, but + * name_to_handle_at() also passes us the mount ID, hence use + * it but throw the handle away */ + + if (path_equal(t, "/")) + return 1; + + r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0); + if (r < 0) { + if (errno == ENOSYS) + /* This kernel does not support name_to_handle_at() + * fall back to the traditional stat() logic. */ + goto fallback; + else if (errno == EOPNOTSUPP) + /* This kernel or file system does not support + * name_to_handle_at(), hence fallback to the + * traditional stat() logic */ + nosupp = true; + else if (errno == ENOENT) + return 0; + else + return -errno; + } + + r = path_get_parent(t, &parent); + if (r < 0) + return r; + + h.handle.handle_bytes = MAX_HANDLE_SZ; + r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW); + if (r < 0) + if (errno == EOPNOTSUPP) + if (nosupp) + /* Neither parent nor child do name_to_handle_at()? + We have no choice but to fall back. */ + goto fallback; + else + /* The parent can't do name_to_handle_at() but + * the directory we are interested in can? + * Or the other way around? + * If so, it must be a mount point. */ + return 1; + else + return -errno; + else + return mount_id != mount_id_parent; + +fallback: + if (allow_symlink) + r = stat(t, &a); + else + r = lstat(t, &a); + + if (r < 0) { + if (errno == ENOENT) + return 0; + + return -errno; + } + + free(parent); + parent = NULL; + + r = path_get_parent(t, &parent); + if (r < 0) + return r; + + r = stat(parent, &b); + if (r < 0) + return -errno; + + return a.st_dev != b.st_dev; +} + +int path_is_read_only_fs(const char *path) { + struct statvfs st; + + assert(path); + + if (statvfs(path, &st) < 0) + return -errno; + + if (st.f_flag & ST_RDONLY) + return true; + + /* On NFS, statvfs() might not reflect whether we can actually + * write to the remote share. Let's try again with + * access(W_OK) which is more reliable, at least sometimes. */ + if (access(path, W_OK) < 0 && errno == EROFS) + return true; + + return false; +} + +int path_is_os_tree(const char *path) { + char *p; + int r; + + /* We use /usr/lib/os-release as flag file if something is an OS */ + p = strjoina(path, "/usr/lib/os-release"); + r = access(p, F_OK); + + if (r >= 0) + return 1; + + /* Also check for the old location in /etc, just in case. */ + p = strjoina(path, "/etc/os-release"); + r = access(p, F_OK); + + return r >= 0; +} + +int find_binary(const char *name, bool local, char **filename) { + assert(name); + + if (is_path(name)) { + if (local && access(name, X_OK) < 0) + return -errno; + + if (filename) { + char *p; + + p = path_make_absolute_cwd(name); + if (!p) + return -ENOMEM; + + *filename = p; + } + + return 0; + } else { + const char *path; + const char *word, *state; + size_t l; + + /** + * Plain getenv, not secure_getenv, because we want + * to actually allow the user to pick the binary. + */ + path = getenv("PATH"); + if (!path) + path = DEFAULT_PATH; + + FOREACH_WORD_SEPARATOR(word, l, path, ":", state) { + _cleanup_free_ char *p = NULL; + + if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0) + return -ENOMEM; + + if (access(p, X_OK) < 0) + continue; + + if (filename) { + *filename = path_kill_slashes(p); + p = NULL; + } + + return 0; + } + + return -ENOENT; + } +} + +bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { + bool changed = false; + const char* const* i; + + assert(timestamp); + + if (paths == NULL) + return false; + + STRV_FOREACH(i, paths) { + struct stat stats; + usec_t u; + + if (stat(*i, &stats) < 0) + continue; + + u = timespec_load(&stats.st_mtim); + + /* first check */ + if (*timestamp >= u) + continue; + + log_debug("timestamp of '%s' changed", *i); + + /* update timestamp */ + if (update) { + *timestamp = u; + changed = true; + } else + return true; + } + + return changed; +} + +int fsck_exists(const char *fstype) { + _cleanup_free_ char *p = NULL, *d = NULL; + const char *checker; + int r; + + checker = strjoina("fsck.", fstype); + + r = find_binary(checker, true, &p); + if (r < 0) + return r; + + /* An fsck that is linked to /bin/true is a non-existent + * fsck */ + + r = readlink_malloc(p, &d); + if (r >= 0 && + (path_equal(d, "/bin/true") || + path_equal(d, "/usr/bin/true") || + path_equal(d, "/dev/null"))) + return -ENOENT; + + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/path-util.h b/src/dhcp-manager/systemd-dhcp/src/shared/path-util.h new file mode 100644 index 0000000000..c754c60b7f --- /dev/null +++ b/src/dhcp-manager/systemd-dhcp/src/shared/path-util.h @@ -0,0 +1,75 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010-2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "nm-sd-adapt.h" + +#include <stdbool.h> + +#include "macro.h" +#include "time-util.h" + +#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" +#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" + +#ifdef HAVE_SPLIT_USR +# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR +#else +# define DEFAULT_PATH DEFAULT_PATH_NORMAL +#endif + +bool is_path(const char *p) _pure_; +char** path_split_and_make_absolute(const char *p); +int path_get_parent(const char *path, char **parent); +bool path_is_absolute(const char *p) _pure_; +char* path_make_absolute(const char *p, const char *prefix); +char* path_make_absolute_cwd(const char *p); +int path_make_relative(const char *from_dir, const char *to_path, char **_r); +char* path_kill_slashes(char *path); +char* path_startswith(const char *path, const char *prefix) _pure_; +bool path_equal(const char *a, const char *b) _pure_; +bool path_equal_or_files_same(const char *a, const char *b); +char* path_join(const char *root, const char *path, const char *rest); + +char** path_strv_make_absolute_cwd(char **l); +char** path_strv_resolve(char **l, const char *prefix); +char** path_strv_resolve_uniq(char **l, const char *prefix); + +int path_is_mount_point(const char *path, bool allow_symlink); +int path_is_read_only_fs(const char *path); +int path_is_os_tree(const char *path); + +int find_binary(const char *name, bool local, char **filename); + +bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); + +int fsck_exists(const char *fstype); + +/* Iterates through the path prefixes of the specified path, going up + * the tree, to root. Also returns "" (and not "/"!) for the root + * directory. Excludes the specified directory itself */ +#define PATH_FOREACH_PREFIX(prefix, path) \ + for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) + +/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ +#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ + for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h b/src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h index 550ed9853b..84d57ab5e6 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/socket-util.h @@ -27,7 +27,6 @@ #include <netinet/in.h> #include <netinet/ether.h> #include <sys/un.h> -#include <asm/types.h> #include <linux/netlink.h> #include <linux/if_packet.h> diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/strv.c b/src/dhcp-manager/systemd-dhcp/src/shared/strv.c index 74a080ff16..bd68f3fc5e 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/strv.c +++ b/src/dhcp-manager/systemd-dhcp/src/shared/strv.c @@ -21,7 +21,6 @@ #include "nm-sd-adapt.h" -#include <assert.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> @@ -71,7 +70,7 @@ char *strv_find_startswith(char **l, const char *name) { return NULL; } -void strv_free(char **l) { +void strv_clear(char **l) { char **k; if (!l) @@ -80,6 +79,11 @@ void strv_free(char **l) { for (k = l; *k; k++) free(*k); + *l = NULL; +} + +void strv_free(char **l) { + strv_clear(l); free(l); } @@ -392,7 +396,7 @@ int strv_push(char ***l, char *value) { n = strv_length(*l); - /* increase and check for overflow */ + /* Increase and check for overflow */ m = n + 2; if (m < n) return -ENOMEM; @@ -408,6 +412,34 @@ int strv_push(char ***l, char *value) { return 0; } +int strv_push_pair(char ***l, char *a, char *b) { + char **c; + unsigned n, m; + + if (!a && !b) + return 0; + + n = strv_length(*l); + + /* increase and check for overflow */ + m = n + !!a + !!b + 1; + if (m < n) + return -ENOMEM; + + c = realloc_multiply(*l, sizeof(char*), m); + if (!c) + return -ENOMEM; + + if (a) + c[n++] = a; + if (b) + c[n++] = b; + c[n] = NULL; + + *l = c; + return 0; +} + int strv_push_prepend(char ***l, char *value) { char **c; unsigned n, m, i; @@ -448,6 +480,18 @@ int strv_consume(char ***l, char *value) { return r; } +int strv_consume_pair(char ***l, char *a, char *b) { + int r; + + r = strv_push_pair(l, a, b); + if (r < 0) { + free(a); + free(b); + } + + return r; +} + int strv_consume_prepend(char ***l, char *value) { int r; @@ -483,6 +527,16 @@ char **strv_uniq(char **l) { return l; } +bool strv_is_uniq(char **l) { + char **i; + + STRV_FOREACH(i, l) + if (strv_find(i+1, *i)) + return false; + + return true; +} + char **strv_remove(char **l, const char *s) { char **f, **t; @@ -591,6 +645,17 @@ char **strv_sort(char **l) { return l; } +bool strv_equal(char **a, char **b) { + if (!a || !b) + return a == b; + + for ( ; *a || *b; ++a, ++b) + if (!streq_ptr(*a, *b)) + return false; + + return true; +} + void strv_print(char **l) { char **s; @@ -612,3 +677,31 @@ int strv_extendf(char ***l, const char *format, ...) { return strv_consume(l, x); } + +char **strv_reverse(char **l) { + unsigned n, i; + + n = strv_length(l); + if (n <= 1) + return l; + + for (i = 0; i < n / 2; i++) { + char *t; + + t = l[i]; + l[i] = l[n-1-i]; + l[n-1-i] = t; + } + + return l; +} + +bool strv_fnmatch(char* const* patterns, const char *s, int flags) { + char* const* p; + + STRV_FOREACH(p, patterns) + if (fnmatch(*p, s, 0) == 0) + return true; + + return false; +} diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/strv.h b/src/dhcp-manager/systemd-dhcp/src/shared/strv.h index 15ef0f1740..13c0e3bc89 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/strv.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/strv.h @@ -25,6 +25,7 @@ #include <stdarg.h> #include <stdbool.h> +#include <fnmatch.h> #include "util.h" @@ -36,6 +37,8 @@ void strv_free(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); #define _cleanup_strv_free_ _cleanup_(strv_freep) +void strv_clear(char **l); + char **strv_copy(char * const *l); unsigned strv_length(char * const *l) _pure_; @@ -44,12 +47,17 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); int strv_push(char ***l, char *value); +int strv_push_pair(char ***l, char *a, char *b); int strv_push_prepend(char ***l, char *value); int strv_consume(char ***l, char *value); +int strv_consume_pair(char ***l, char *a, char *b); int strv_consume_prepend(char ***l, char *value); char **strv_remove(char **l, const char *s); char **strv_uniq(char **l); +bool strv_is_uniq(char **l); + +bool strv_equal(char **a, char **b); #define strv_contains(l, s) (!!strv_find((l), (s))) @@ -137,3 +145,13 @@ void strv_print(char **l); _l ++; \ _l[0]; \ })) + +char **strv_reverse(char **l); + +bool strv_fnmatch(char* const* patterns, const char *s, int flags); + +static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { + assert(s); + return strv_isempty(patterns) || + strv_fnmatch(patterns, s, flags); +} diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/time-util.c b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.c index 5d79f15dc0..f1e56b6e47 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/time-util.c +++ b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.c @@ -507,8 +507,9 @@ int parse_timestamp(const char *t, usec_t *usec) { return parse_sec(t + 1, usec); else if (endswith(t, " ago")) { - _cleanup_free_ char *z = strndup(t, strlen(t) - 4); + _cleanup_free_ char *z; + z = strndup(t, strlen(t) - 4); if (!z) return -ENOMEM; @@ -518,8 +519,9 @@ int parse_timestamp(const char *t, usec_t *usec) { goto finish; } else if (endswith(t, " left")) { - _cleanup_free_ char *z = strndup(t, strlen(t) - 4); + _cleanup_free_ char *z; + z = strndup(t, strlen(t) - 4); if (!z) return -ENOMEM; @@ -791,7 +793,7 @@ int parse_nsec(const char *t, nsec_t *nsec) { s = startswith(p, "infinity"); if (s) { s += strspn(s, WHITESPACE); - if (!*s != 0) + if (*s != 0) return -EINVAL; *nsec = NSEC_INFINITY; @@ -970,7 +972,7 @@ bool timezone_is_valid(const char *name) { if (slash) return false; - t = strappenda("/usr/share/zoneinfo/", name); + t = strjoina("/usr/share/zoneinfo/", name); if (stat(t, &st) < 0) return false; @@ -998,4 +1000,3 @@ clockid_t clock_boottime_or_monotonic(void) { return clock; } - diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/time-util.h b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.h index e3e84acbae..5d5aa10db4 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/time-util.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/time-util.h @@ -69,7 +69,7 @@ typedef struct dual_timestamp { #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) -#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0, 0 }) +#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL }) usec_t now(clockid_t clock); @@ -109,3 +109,5 @@ int get_timezones(char ***l); bool timezone_is_valid(const char *name); clockid_t clock_boottime_or_monotonic(void); + +#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0) diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/utf8.c b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.c index 261f39db84..7433f9b059 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/utf8.c +++ b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.c @@ -144,19 +144,19 @@ int utf8_encoded_to_unichar(const char *str) { } bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { - const uint8_t *p; + const char *p; assert(str); - for (p = (const uint8_t*) str; length;) { + for (p = str; length;) { int encoded_len, val; - encoded_len = utf8_encoded_valid_unichar((const char *) p); + encoded_len = utf8_encoded_valid_unichar(p); if (encoded_len < 0 || (size_t) encoded_len > length) return false; - val = utf8_encoded_to_unichar((const char*) p); + val = utf8_encoded_to_unichar(p); if (val < 0 || is_unicode_control(val) || (!newline && val == '\n')) @@ -204,7 +204,46 @@ char *utf8_escape_invalid(const char *str) { s = mempcpy(s, str, len); str += len; } else { - s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER)); + s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); + str += 1; + } + } + + *s = '\0'; + + return p; +} + +char *utf8_escape_non_printable(const char *str) { + char *p, *s; + + assert(str); + + p = s = malloc(strlen(str) * 4 + 1); + if (!p) + return NULL; + + while (*str) { + int len; + + len = utf8_encoded_valid_unichar(str); + if (len > 0) { + if (utf8_is_printable(str, len)) { + s = mempcpy(s, str, len); + str += len; + } else { + while (len > 0) { + *(s++) = '\\'; + *(s++) = 'x'; + *(s++) = hexchar((int) *str >> 4); + *(s++) = hexchar((int) *str); + + str += 1; + len --; + } + } + } else { + s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); str += 1; } } @@ -226,39 +265,91 @@ char *ascii_is_valid(const char *str) { return (char*) str; } +/** + * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 + * @out_utf8: output buffer of at least 4 bytes or NULL + * @g: UCS-4 character to encode + * + * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8. + * The length of the character is returned. It is not zero-terminated! If the + * output buffer is NULL, only the length is returned. + * + * Returns: The length in bytes that the UTF-8 representation does or would + * occupy. + */ +size_t utf8_encode_unichar(char *out_utf8, uint32_t g) { + if (g < (1 << 7)) { + if (out_utf8) + out_utf8[0] = g & 0x7f; + return 1; + } else if (g < (1 << 11)) { + if (out_utf8) { + out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f); + out_utf8[1] = 0x80 | (g & 0x3f); + } + return 2; + } else if (g < (1 << 16)) { + if (out_utf8) { + out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f); + out_utf8[1] = 0x80 | ((g >> 6) & 0x3f); + out_utf8[2] = 0x80 | (g & 0x3f); + } + return 3; + } else if (g < (1 << 21)) { + if (out_utf8) { + out_utf8[0] = 0xf0 | ((g >> 18) & 0x07); + out_utf8[1] = 0x80 | ((g >> 12) & 0x3f); + out_utf8[2] = 0x80 | ((g >> 6) & 0x3f); + out_utf8[3] = 0x80 | (g & 0x3f); + } + return 4; + } else { + return 0; + } +} + char *utf16_to_utf8(const void *s, size_t length) { - char *r; const uint8_t *f; - uint8_t *t; + char *r, *t; - r = new(char, (length*3+1)/2 + 1); + r = new(char, (length * 4 + 1) / 2 + 1); if (!r) return NULL; - t = (uint8_t*) r; + f = s; + t = r; - for (f = s; f < (const uint8_t*) s + length; f += 2) { - uint16_t c; + while (f < (const uint8_t*) s + length) { + uint16_t w1, w2; - c = (f[1] << 8) | f[0]; + /* see RFC 2781 section 2.2 */ - if (c == 0) { - *t = 0; - return r; - } else if (c < 0x80) { - *(t++) = (uint8_t) c; - } else if (c < 0x800) { - *(t++) = (uint8_t) (0xc0 | (c >> 6)); - *(t++) = (uint8_t) (0x80 | (c & 0x3f)); - } else { - *(t++) = (uint8_t) (0xe0 | (c >> 12)); - *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f)); - *(t++) = (uint8_t) (0x80 | (c & 0x3f)); + w1 = f[1] << 8 | f[0]; + f += 2; + + if (!utf16_is_surrogate(w1)) { + t += utf8_encode_unichar(t, w1); + + continue; } + + if (utf16_is_trailing_surrogate(w1)) + continue; + else if (f >= (const uint8_t*) s + length) + break; + + w2 = f[1] << 8 | f[0]; + f += 2; + + if (!utf16_is_trailing_surrogate(w2)) { + f -= 2; + continue; + } + + t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2)); } *t = 0; - return r; } diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/utf8.h b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.h index d7728a162f..1fb132041e 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/utf8.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/utf8.h @@ -31,14 +31,27 @@ const char *utf8_is_valid(const char *s) _pure_; char *ascii_is_valid(const char *s) _pure_; -char *utf8_escape_invalid(const char *s); bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; -_pure_ static inline bool utf8_is_printable(const char* str, size_t length) { - return utf8_is_printable_newline(str, length, true); -} +#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) +char *utf8_escape_invalid(const char *s); +char *utf8_escape_non_printable(const char *str); + +size_t utf8_encode_unichar(char *out_utf8, uint32_t g); char *utf16_to_utf8(const void *s, size_t length); int utf8_encoded_valid_unichar(const char *str); int utf8_encoded_to_unichar(const char *str); + +static inline bool utf16_is_surrogate(uint16_t c) { + return (0xd800 <= c && c <= 0xdfff); +} + +static inline bool utf16_is_trailing_surrogate(uint16_t c) { + return (0xdc00 <= c && c <= 0xdfff); +} + +static inline uint32_t utf16_surrogate_pair_to_unichar(uint16_t lead, uint16_t trail) { + return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; +} diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/util.c b/src/dhcp-manager/systemd-dhcp/src/shared/util.c index 010243b219..d1dc229689 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/util.c +++ b/src/dhcp-manager/systemd-dhcp/src/shared/util.c @@ -21,12 +21,13 @@ #include "nm-sd-adapt.h" -#include <assert.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <stdlib.h> #include <signal.h> +#include <libintl.h> +#include <locale.h> #include <stdio.h> #include <syslog.h> #include <sched.h> @@ -41,15 +42,13 @@ #include <linux/tiocl.h> #include <termios.h> #include <stdarg.h> -#include <sys/inotify.h> -#include <sys/poll.h> +#include <poll.h> #include <ctype.h> #include <sys/prctl.h> #include <sys/utsname.h> #include <pwd.h> #include <netinet/ip.h> #include <linux/kd.h> -#include <dlfcn.h> #include <sys/wait.h> #include <sys/time.h> #include <glob.h> @@ -62,6 +61,14 @@ #include <langinfo.h> #include <locale.h> #include <sys/personality.h> +#include <sys/xattr.h> +#include <sys/statvfs.h> +#include <sys/file.h> +#include <linux/fs.h> + +/* When we include libgen.h because we need dirname() we immediately + * undefine basename() since libgen.h defines it as a macro to the XDG + * version which is really broken. */ #include <libgen.h> #undef basename @@ -69,6 +76,7 @@ #include <sys/auxv.h> #endif +#include "config.h" #include "macro.h" #include "util.h" #if 0 /* NM_IGNORED */ @@ -76,9 +84,10 @@ #include "missing.h" #include "log.h" #include "strv.h" -#include "label.h" #include "mkdir.h" +#endif /* NM_IGNORED */ #include "path-util.h" +#if 0 /* NM_IGNORED */ #include "exit-status.h" #include "hashmap.h" #include "env-util.h" @@ -88,6 +97,7 @@ #include "gunicode.h" #include "virt.h" #include "def.h" +#include "sparse-endian.h" #endif /* NM_IGNORED */ #if 0 /* NM_IGNORED */ @@ -180,6 +190,69 @@ char* first_word(const char *s, const char *word) { return (char*) p; } +static size_t cescape_char(char c, char *buf) { + char * buf_old = buf; + + switch (c) { + + case '\a': + *(buf++) = '\\'; + *(buf++) = 'a'; + break; + case '\b': + *(buf++) = '\\'; + *(buf++) = 'b'; + break; + case '\f': + *(buf++) = '\\'; + *(buf++) = 'f'; + break; + case '\n': + *(buf++) = '\\'; + *(buf++) = 'n'; + break; + case '\r': + *(buf++) = '\\'; + *(buf++) = 'r'; + break; + case '\t': + *(buf++) = '\\'; + *(buf++) = 't'; + break; + case '\v': + *(buf++) = '\\'; + *(buf++) = 'v'; + break; + case '\\': + *(buf++) = '\\'; + *(buf++) = '\\'; + break; + case '"': + *(buf++) = '\\'; + *(buf++) = '"'; + break; + case '\'': + *(buf++) = '\\'; + *(buf++) = '\''; + break; + + default: + /* For special chars we prefer octal over + * hexadecimal encoding, simply because glib's + * g_strescape() does the same */ + if ((c < ' ') || (c >= 127)) { + *(buf++) = '\\'; + *(buf++) = octchar((unsigned char) c >> 6); + *(buf++) = octchar((unsigned char) c >> 3); + *(buf++) = octchar((unsigned char) c); + } else + *(buf++) = c; + break; + } + + return buf - buf_old; +} + int close_nointr(int fd) { assert(fd >= 0); @@ -298,7 +371,7 @@ int parse_uid(const char *s, uid_t* ret_uid) { if ((unsigned long) uid != ul) return -ERANGE; - /* Some libc APIs use (uid_t) -1 as special placeholder */ + /* Some libc APIs use UID_INVALID as special placeholder */ if (uid == (uid_t) 0xFFFFFFFF) return -ENXIO; @@ -449,18 +522,24 @@ int safe_atolli(const char *s, long long int *ret_lli) { int safe_atod(const char *s, double *ret_d) { char *x = NULL; double d = 0; + locale_t loc; assert(s); assert(ret_d); - RUN_WITH_LOCALE(LC_NUMERIC_MASK, "C") { - errno = 0; - d = strtod(s, &x); - } + loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); + if (loc == (locale_t) 0) + return -errno; - if (!x || x == s || *x || errno) + errno = 0; + d = strtod_l(s, &x, loc); + + if (!x || x == s || *x || errno) { + freelocale(loc); return errno ? -errno : -EINVAL; + } + freelocale(loc); *ret_d = (double) d; return 0; } @@ -468,7 +547,7 @@ int safe_atod(const char *s, double *ret_d) { static size_t strcspn_escaped(const char *s, const char *reject) { bool escaped = false; - size_t n; + int n; for (n=0; s[n]; n++) { if (escaped) @@ -478,6 +557,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) { else if (strchr(reject, s[n])) break; } + /* if s ends in \, return index of previous char */ return n - escaped; } @@ -505,7 +585,7 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo *l = strcspn_escaped(current + 1, quotechars); if (current[*l + 1] == '\0' || (current[*l + 2] && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end*/ + /* right quote missing or garbage at the end */ *state = current; return NULL; } @@ -513,6 +593,11 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo *state = current++ + *l + 2; } else if (quoted) { *l = strcspn_escaped(current, separator); + if (current[*l] && !strchr(separator, current[*l])) { + /* unfinished escape */ + *state = current; + return NULL; + } *state = current + *l; } else { *l = strcspn(current, separator); @@ -565,56 +650,6 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { return 0; } - -int get_starttime_of_pid(pid_t pid, unsigned long long *st) { - int r; - _cleanup_free_ char *line = NULL; - const char *p; - - assert(pid >= 0); - assert(st); - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r < 0) - return r; - - /* Let's skip the pid and comm fields. The latter is enclosed - * in () but does not escape any () in its value, so let's - * skip over it manually */ - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%*d " /* ppid */ - "%*d " /* pgrp */ - "%*d " /* session */ - "%*d " /* tty_nr */ - "%*d " /* tpgid */ - "%*u " /* flags */ - "%*u " /* minflt */ - "%*u " /* cminflt */ - "%*u " /* majflt */ - "%*u " /* cmajflt */ - "%*u " /* utime */ - "%*u " /* stime */ - "%*d " /* cutime */ - "%*d " /* cstime */ - "%*d " /* priority */ - "%*d " /* nice */ - "%*d " /* num_threads */ - "%*d " /* itrealvalue */ - "%llu " /* starttime */, - st) != 1) - return -EIO; - - return 0; -} #endif /* NM_IGNORED */ int fchmod_umask(int fd, mode_t m) { @@ -810,19 +845,30 @@ int get_process_capeff(pid_t pid, char **capeff) { return get_status_field(p, "\nCapEff:", capeff); } +static int get_process_link_contents(const char *proc_file, char **name) { + int r; + + assert(proc_file); + assert(name); + + r = readlink_malloc(proc_file, name); + if (r < 0) + return r == -ENOENT ? -ESRCH : r; + + return 0; +} + int get_process_exe(pid_t pid, char **name) { const char *p; char *d; int r; assert(pid >= 0); - assert(name); p = procfs_file_alloca(pid, "exe"); - - r = readlink_malloc(p, name); + r = get_process_link_contents(p, name); if (r < 0) - return r == -ENOENT ? -ESRCH : r; + return r; d = endswith(*name, " (deleted)"); if (d) @@ -873,6 +919,59 @@ int get_process_gid(pid_t pid, gid_t *gid) { assert_cc(sizeof(uid_t) == sizeof(gid_t)); return get_process_id(pid, "Gid:", gid); } + +int get_process_cwd(pid_t pid, char **cwd) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "cwd"); + + return get_process_link_contents(p, cwd); +} + +int get_process_root(pid_t pid, char **root) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "root"); + + return get_process_link_contents(p, root); +} + +int get_process_environ(pid_t pid, char **env) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *outcome = NULL; + int c; + const char *p; + size_t allocated = 0, sz = 0; + + assert(pid >= 0); + assert(env); + + p = procfs_file_alloca(pid, "environ"); + + f = fopen(p, "re"); + if (!f) + return -errno; + + while ((c = fgetc(f)) != EOF) { + if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) + return -ENOMEM; + + if (c == '\0') + outcome[sz++] = '\n'; + else + sz += cescape_char(c, outcome + sz); + } + + outcome[sz] = '\0'; + *env = outcome; + outcome = NULL; + + return 0; +} #endif /* NM_IGNORED */ char *strnappend(const char *s, const char *suffix, size_t b) { @@ -1085,7 +1184,7 @@ char *delete_chars(char *s, const char *bad) { } char *file_in_same_dir(const char *path, const char *filename) { - char *e, *r; + char *e, *ret; size_t k; assert(path); @@ -1098,17 +1197,17 @@ char *file_in_same_dir(const char *path, const char *filename) { if (path_is_absolute(filename)) return strdup(filename); - if (!(e = strrchr(path, '/'))) + e = strrchr(path, '/'); + if (!e) return strdup(filename); k = strlen(filename); - if (!(r = new(char, e-path+1+k+1))) + ret = new(char, (e + 1 - path) + k + 1); + if (!ret) return NULL; - memcpy(r, path, e-path+1); - memcpy(r+(e-path)+1, filename, k+1); - - return r; + memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); + return ret; } int rmdir_parents(const char *path, const char *stop) { @@ -1258,63 +1357,7 @@ char *cescape(const char *s) { return NULL; for (f = s, t = r; *f; f++) - - switch (*f) { - - case '\a': - *(t++) = '\\'; - *(t++) = 'a'; - break; - case '\b': - *(t++) = '\\'; - *(t++) = 'b'; - break; - case '\f': - *(t++) = '\\'; - *(t++) = 'f'; - break; - case '\n': - *(t++) = '\\'; - *(t++) = 'n'; - break; - case '\r': - *(t++) = '\\'; - *(t++) = 'r'; - break; - case '\t': - *(t++) = '\\'; - *(t++) = 't'; - break; - case '\v': - *(t++) = '\\'; - *(t++) = 'v'; - break; - case '\\': - *(t++) = '\\'; - *(t++) = '\\'; - break; - case '"': - *(t++) = '\\'; - *(t++) = '"'; - break; - case '\'': - *(t++) = '\\'; - *(t++) = '\''; - break; - - default: - /* For special chars we prefer octal over - * hexadecimal encoding, simply because glib's - * g_strescape() does the same */ - if ((*f < ' ') || (*f >= 127)) { - *(t++) = '\\'; - *(t++) = octchar((unsigned char) *f >> 6); - *(t++) = octchar((unsigned char) *f >> 3); - *(t++) = octchar((unsigned char) *f); - } else - *(t++) = *f; - break; - } + t += cescape_char(*f, t); *t = 0; @@ -1340,12 +1383,19 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre memcpy(r, prefix, pl); for (f = s, t = r + pl; f < s + length; f++) { + size_t remaining = s + length - f; + assert(remaining > 0); - if (*f != '\\') { + if (*f != '\\') { /* a literal literal */ *(t++) = *f; continue; } + if (--remaining == 0) { /* copy trailing backslash verbatim */ + *(t++) = *f; + break; + } + f++; switch (*f) { @@ -1388,10 +1438,12 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre case 'x': { /* hexadecimal encoding */ - int a, b; + int a = -1, b = -1; - a = unhexchar(f[1]); - b = unhexchar(f[2]); + if (remaining >= 2) { + a = unhexchar(f[1]); + b = unhexchar(f[2]); + } if (a < 0 || b < 0 || (a == 0 && b == 0)) { /* Invalid escape code, let's take it literal then */ @@ -1414,11 +1466,13 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre case '6': case '7': { /* octal encoding */ - int a, b, c; + int a = -1, b = -1, c = -1; - a = unoctchar(f[0]); - b = unoctchar(f[1]); - c = unoctchar(f[2]); + if (remaining >= 3) { + a = unoctchar(f[0]); + b = unoctchar(f[1]); + c = unoctchar(f[2]); + } if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) { /* Invalid escape code, let's take it literal then */ @@ -1432,11 +1486,6 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre break; } - case 0: - /* premature end of string.*/ - *(t++) = '\\'; - goto finish; - default: /* Invalid escape code, let's take it literal then */ *(t++) = '\\'; @@ -1445,7 +1494,6 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre } } -finish: *t = 0; return r; } @@ -1502,7 +1550,7 @@ char *ascii_strlower(char *t) { return t; } -_pure_ static bool ignore_file_allow_backup(const char *filename) { +_pure_ static bool hidden_file_allow_backup(const char *filename) { assert(filename); return @@ -1516,16 +1564,20 @@ _pure_ static bool ignore_file_allow_backup(const char *filename) { endswith(filename, ".dpkg-old") || endswith(filename, ".dpkg-new") || endswith(filename, ".dpkg-tmp") || + endswith(filename, ".dpkg-dist") || + endswith(filename, ".dpkg-bak") || + endswith(filename, ".dpkg-backup") || + endswith(filename, ".dpkg-remove") || endswith(filename, ".swp"); } -bool ignore_file(const char *filename) { +bool hidden_file(const char *filename) { assert(filename); if (endswith(filename, "~")) return true; - return ignore_file_allow_backup(filename); + return hidden_file_allow_backup(filename); } int fd_nonblock(int fd, bool nonblock) { @@ -1619,7 +1671,7 @@ int close_all_fds(const int except[], unsigned n_except) { while ((de = readdir(d))) { int fd = -1; - if (ignore_file(de->d_name)) + if (hidden_file(de->d_name)) continue; if (safe_atoi(de->d_name, &fd) < 0) @@ -1660,6 +1712,7 @@ bool chars_intersect(const char *a, const char *b) { #if 0 /* NM_IGNORED */ bool fstype_is_network(const char *fstype) { static const char table[] = + "afs\0" "cifs\0" "smbfs\0" "sshfs\0" @@ -1681,7 +1734,7 @@ bool fstype_is_network(const char *fstype) { } int chvt(int vt) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd; fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) @@ -2100,9 +2153,9 @@ int acquire_terminal( assert(notify >= 0); for (;;) { - uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX]; - ssize_t l; + union inotify_event_buffer buffer; struct inotify_event *e; + ssize_t l; if (timeout != USEC_INFINITY) { usec_t n; @@ -2123,9 +2176,8 @@ int acquire_terminal( } } - l = read(notify, inotify_buffer, sizeof(inotify_buffer)); + l = read(notify, &buffer, sizeof(buffer)); if (l < 0) { - if (errno == EINTR || errno == EAGAIN) continue; @@ -2133,21 +2185,11 @@ int acquire_terminal( goto fail; } - e = (struct inotify_event*) inotify_buffer; - - while (l > 0) { - size_t step; - + FOREACH_INOTIFY_EVENT(e, buffer, l) { if (e->wd != wd || !(e->mask & IN_CLOSE)) { r = -EIO; goto fail; } - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) l); - - e = (struct inotify_event*) ((uint8_t*) e + step); - l -= step; } break; @@ -2164,7 +2206,7 @@ int acquire_terminal( r = reset_terminal_fd(fd, true); if (r < 0) - log_warning("Failed to reset terminal: %s", strerror(-r)); + log_warning_errno(r, "Failed to reset terminal: %m"); return fd; @@ -2280,21 +2322,25 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { ssize_t k; k = read(fd, p, nbytes); - if (k < 0 && errno == EINTR) - continue; + if (k < 0) { + if (errno == EINTR) + continue; - if (k < 0 && errno == EAGAIN && do_poll) { + if (errno == EAGAIN && do_poll) { - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via read() */ - fd_wait_for_event(fd, POLLIN, USEC_INFINITY); - continue; + fd_wait_for_event(fd, POLLIN, USEC_INFINITY); + continue; + } + + return n > 0 ? n : -errno; } - if (k <= 0) - return n > 0 ? n : (k < 0 ? -errno : 0); + if (k == 0) + return n; p += k; nbytes -= k; @@ -2305,39 +2351,42 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { } #if 0 /* NM_IGNORED */ -ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { +int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { const uint8_t *p = buf; - ssize_t n = 0; assert(fd >= 0); assert(buf); + errno = 0; + while (nbytes > 0) { ssize_t k; k = write(fd, p, nbytes); - if (k < 0 && errno == EINTR) - continue; + if (k < 0) { + if (errno == EINTR) + continue; - if (k < 0 && errno == EAGAIN && do_poll) { + if (errno == EAGAIN && do_poll) { + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via write() */ - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via write() */ + fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); + continue; + } - fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); - continue; + return -errno; } - if (k <= 0) - return n > 0 ? n : (k < 0 ? -errno : 0); + if (k == 0) /* Can't really happen */ + return -EIO; p += k; nbytes -= k; - n += k; } - return n; + return 0; } int parse_size(const char *t, off_t base, off_t *size) { @@ -2474,9 +2523,9 @@ int make_stdio(int fd) { assert(fd >= 0); - r = dup3(fd, STDIN_FILENO, 0); - s = dup3(fd, STDOUT_FILENO, 0); - t = dup3(fd, STDERR_FILENO, 0); + r = dup2(fd, STDIN_FILENO); + s = dup2(fd, STDOUT_FILENO); + t = dup2(fd, STDERR_FILENO); if (fd >= 3) safe_close(fd); @@ -2484,7 +2533,11 @@ int make_stdio(int fd) { if (r < 0 || s < 0 || t < 0) return -errno; - /* We rely here that the new fd has O_CLOEXEC not set */ + /* Explicitly unset O_CLOEXEC, since if fd was < 3, then + * dup2() was a NOP and the bit hence possibly set. */ + fd_cloexec(STDIN_FILENO, false); + fd_cloexec(STDOUT_FILENO, false); + fd_cloexec(STDERR_FILENO, false); return 0; } @@ -2527,7 +2580,7 @@ int dir_is_empty(const char *path) { if (!de) return 1; - if (!ignore_file(de->d_name)) + if (!hidden_file(de->d_name)) return 0; } } @@ -2788,23 +2841,36 @@ char *getusername_malloc(void) { return lookup_uid(getuid()); } -int getttyname_malloc(int fd, char **r) { - char path[PATH_MAX], *c; - int k; +int getttyname_malloc(int fd, char **ret) { + size_t l = 100; + int r; - assert(r); + assert(fd >= 0); + assert(ret); - k = ttyname_r(fd, path, sizeof(path)); - if (k > 0) - return -k; + for (;;) { + char path[l]; - char_array_0(path); + r = ttyname_r(fd, path, sizeof(path)); + if (r == 0) { + const char *p; + char *c; - c = strdup(startswith(path, "/dev/") ? path + 5 : path); - if (!c) - return -ENOMEM; + p = startswith(path, "/dev/"); + c = strdup(p ?: path); + if (!c) + return -ENOMEM; + + *ret = c; + return 0; + } + + if (r != ERANGE) + return -r; + + l *= 2; + } - *r = c; return 0; } @@ -3011,6 +3077,15 @@ _pure_ static int is_temporary_fs(struct statfs *s) { F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC); } +int is_fd_on_temporary_fs(int fd) { + struct statfs s; + + if (fstatfs(fd, &s) < 0) + return -errno; + + return is_temporary_fs(&s); +} + int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { struct statfs s; @@ -3063,7 +3138,7 @@ static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bo fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); if (fd < 0) { - if (errno != ENOTDIR) + if (errno != ENOTDIR && errno != ELOOP) return -errno; if (!dangerous) { @@ -3126,11 +3201,11 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { * first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ - if (mode != (mode_t) -1) + if (mode != MODE_INVALID) if (chmod(path, mode) < 0) return -errno; - if (uid != (uid_t) -1 || gid != (gid_t) -1) + if (uid != UID_INVALID || gid != GID_INVALID) if (chown(path, uid, gid) < 0) return -errno; @@ -3144,11 +3219,11 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { * first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ - if (mode != (mode_t) -1) + if (mode != MODE_INVALID) if (fchmod(fd, mode) < 0) return -errno; - if (uid != (uid_t) -1 || gid != (gid_t) -1) + if (uid != UID_INVALID || gid != GID_INVALID) if (fchown(fd, uid, gid) < 0) return -errno; @@ -3436,7 +3511,7 @@ unsigned columns(void) { c = 80; cached_columns = c; - return c; + return cached_columns; } int fd_lines(int fd) { @@ -3453,7 +3528,7 @@ int fd_lines(int fd) { unsigned lines(void) { const char *e; - unsigned l; + int l; if (_likely_(cached_lines > 0)) return cached_lines; @@ -3461,7 +3536,7 @@ unsigned lines(void) { l = 0; e = getenv("LINES"); if (e) - (void) safe_atou(e, &l); + (void) safe_atoi(e, &l); if (l <= 0) l = fd_lines(STDOUT_FILENO); @@ -3621,7 +3696,7 @@ char *ellipsize(const char *s, size_t length, unsigned percent) { } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd; int r; assert(path); @@ -3639,7 +3714,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi return -errno; } - if (uid != (uid_t) -1 || gid != (gid_t) -1) { + if (uid != UID_INVALID || gid != GID_INVALID) { r = fchown(fd, uid, gid); if (r < 0) return -errno; @@ -3660,7 +3735,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi } int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, (uid_t) -1, (gid_t) -1, 0); + return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0); } char *unquote(const char *s, const char* quotes) { @@ -3670,7 +3745,7 @@ char *unquote(const char *s, const char* quotes) { /* This is rather stupid, simply removes the heading and * trailing quotes if there is one. Doesn't care about * escaping or anything. We should make this smarter one - * day...*/ + * day... */ l = strlen(s); if (l < 2) @@ -3745,8 +3820,11 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) { * * That is, success is indicated by a return value of zero, and an * error is indicated by a non-zero value. + * + * A warning is emitted if the process terminates abnormally, + * and also if it returns non-zero unless check_exit_code is true. */ -int wait_for_terminate_and_warn(const char *name, pid_t pid) { +int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { int r; siginfo_t status; @@ -3754,20 +3832,17 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) { assert(pid > 1); r = wait_for_terminate(pid, &status); - if (r < 0) { - log_warning("Failed to wait for %s: %s", name, strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "Failed to wait for %s: %m", name); if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) { - log_warning("%s failed with error code %i.", name, status.si_status); - return status.si_status; - } - - log_debug("%s succeeded.", name); - return 0; + if (status.si_status != 0) + log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, + "%s failed with error code %i.", name, status.si_status); + else + log_debug("%s succeeded.", name); + return status.si_status; } else if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) { @@ -3984,13 +4059,13 @@ bool tty_is_vc_resolve(const char *tty) { const char *default_term_for_tty(const char *tty) { assert(tty); - return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt102"; + return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220"; } bool dirent_is_file(const struct dirent *de) { assert(de); - if (ignore_file(de->d_name)) + if (hidden_file(de->d_name)) return false; if (de->d_type != DT_REG && @@ -4009,73 +4084,75 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { de->d_type != DT_UNKNOWN) return false; - if (ignore_file_allow_backup(de->d_name)) + if (hidden_file_allow_backup(de->d_name)) return false; return endswith(de->d_name, suffix); } -void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv[]) { - pid_t executor_pid; - int r; - - assert(directory); +static int do_execute(char **directories, usec_t timeout, char *argv[]) { + _cleanup_hashmap_free_free_ Hashmap *pids = NULL; + _cleanup_set_free_free_ Set *seen = NULL; + char **directory; - /* Executes all binaries in a directory in parallel and waits - * for them to finish. Optionally a timeout is applied. */ + /* We fork this all off from a child process so that we can + * somewhat cleanly make use of SIGALRM to set a time limit */ - executor_pid = fork(); - if (executor_pid < 0) { - log_error("Failed to fork: %m"); - return; + reset_all_signal_handlers(); + reset_signal_mask(); - } else if (executor_pid == 0) { - _cleanup_hashmap_free_free_ Hashmap *pids = NULL; - _cleanup_closedir_ DIR *_d = NULL; - struct dirent *de; + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - /* We fork this all off from a child process so that - * we can somewhat cleanly make use of SIGALRM to set - * a time limit */ + pids = hashmap_new(NULL); + if (!pids) + return log_oom(); - reset_all_signal_handlers(); - reset_signal_mask(); + seen = set_new(&string_hash_ops); + if (!seen) + return log_oom(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + STRV_FOREACH(directory, directories) { + _cleanup_closedir_ DIR *d; + struct dirent *de; + d = opendir(*directory); if (!d) { - d = _d = opendir(directory); - if (!d) { - if (errno == ENOENT) - _exit(EXIT_SUCCESS); - - log_error("Failed to enumerate directory %s: %m", directory); - _exit(EXIT_FAILURE); - } - } + if (errno == ENOENT) + continue; - pids = hashmap_new(NULL); - if (!pids) { - log_oom(); - _exit(EXIT_FAILURE); + return log_error_errno(errno, "Failed to open directory %s: %m", *directory); } FOREACH_DIRENT(de, d, break) { _cleanup_free_ char *path = NULL; pid_t pid; + int r; if (!dirent_is_file(de)) continue; - path = strjoin(directory, "/", de->d_name, NULL); - if (!path) { - log_oom(); - _exit(EXIT_FAILURE); + if (set_contains(seen, de->d_name)) { + log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); + continue; } + r = set_put_strdup(seen, de->d_name); + if (r < 0) + return log_oom(); + + path = strjoin(*directory, "/", de->d_name, NULL); + if (!path) + return log_oom(); + + if (null_or_empty_path(path)) { + log_debug("%s is empty (a mask).", path); + continue; + } else + log_debug("%s will be executed.", path); + pid = fork(); if (pid < 0) { - log_error("Failed to fork: %m"); + log_error_errno(errno, "Failed to fork: %m"); continue; } else if (pid == 0) { char *_argv[2]; @@ -4090,45 +4167,68 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv argv[0] = path; execv(path, argv); - log_error("Failed to execute %s: %m", path); - _exit(EXIT_FAILURE); + return log_error_errno(errno, "Failed to execute %s: %m", path); } log_debug("Spawned %s as " PID_FMT ".", path, pid); r = hashmap_put(pids, UINT_TO_PTR(pid), path); - if (r < 0) { - log_oom(); - _exit(EXIT_FAILURE); - } - + if (r < 0) + return log_oom(); path = NULL; } + } - /* Abort execution of this process after the - * timout. We simply rely on SIGALRM as default action - * terminating the process, and turn on alarm(). */ + /* Abort execution of this process after the timout. We simply + * rely on SIGALRM as default action terminating the process, + * and turn on alarm(). */ - if (timeout != USEC_INFINITY) - alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + if (timeout != USEC_INFINITY) + alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); - while (!hashmap_isempty(pids)) { - _cleanup_free_ char *path = NULL; - pid_t pid; + while (!hashmap_isempty(pids)) { + _cleanup_free_ char *path = NULL; + pid_t pid; - pid = PTR_TO_UINT(hashmap_first_key(pids)); - assert(pid > 0); + pid = PTR_TO_UINT(hashmap_first_key(pids)); + assert(pid > 0); - path = hashmap_remove(pids, UINT_TO_PTR(pid)); - assert(path); + path = hashmap_remove(pids, UINT_TO_PTR(pid)); + assert(path); - wait_for_terminate_and_warn(path, pid); - } + wait_for_terminate_and_warn(path, pid, true); + } - _exit(EXIT_SUCCESS); + return 0; +} + +void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) { + pid_t executor_pid; + int r; + char *name; + char **dirs = (char**) directories; + + assert(!strv_isempty(dirs)); + + name = basename(dirs[0]); + assert(!isempty(name)); + + /* Executes all binaries in the directories in parallel and waits + * for them to finish. Optionally a timeout is applied. If a file + * with the same name exists in more than one directory, the + * earliest one wins. */ + + executor_pid = fork(); + if (executor_pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + return; + + } else if (executor_pid == 0) { + r = do_execute(dirs, timeout, argv); + _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } - wait_for_terminate_and_warn(directory, executor_pid); + wait_for_terminate_and_warn(name, executor_pid, true); } int kill_and_sigcont(pid_t pid, int sig) { @@ -4186,6 +4286,11 @@ bool hostname_is_valid(const char *s) { if (isempty(s)) return false; + /* Doesn't accept empty hostnames, hostnames with trailing or + * leading dots, and hostnames with multiple dots in a + * sequence. Also ensures that the length stays below + * HOST_NAME_MAX. */ + for (p = s, dot = true; *p; p++) { if (*p == '.') { if (dot) @@ -4295,15 +4400,15 @@ int fd_wait_for_event(int fd, int event, usec_t t) { int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { FILE *f; char *t; - int fd; + int r, fd; assert(path); assert(_f); assert(_temp_path); - t = tempfn_xxxxxx(path); - if (!t) - return -ENOMEM; + r = tempfn_xxxxxx(path, &t); + if (r < 0) + return r; fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC); if (fd < 0) { @@ -4335,7 +4440,7 @@ int terminal_vhangup_fd(int fd) { } int terminal_vhangup(const char *name) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd; fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) @@ -4415,13 +4520,14 @@ int vt_disallocate(const char *name) { int symlink_atomic(const char *from, const char *to) { _cleanup_free_ char *t = NULL; + int r; assert(from); assert(to); - t = tempfn_random(to); - if (!t) - return -ENOMEM; + r = tempfn_random(to, &t); + if (r < 0) + return r; if (symlink(from, t) < 0) return -errno; @@ -4436,12 +4542,13 @@ int symlink_atomic(const char *from, const char *to) { int mknod_atomic(const char *path, mode_t mode, dev_t dev) { _cleanup_free_ char *t = NULL; + int r; assert(path); - t = tempfn_random(path); - if (!t) - return -ENOMEM; + r = tempfn_random(path, &t); + if (r < 0) + return r; if (mknod(t, mode, dev) < 0) return -errno; @@ -4456,12 +4563,13 @@ int mknod_atomic(const char *path, mode_t mode, dev_t dev) { int mkfifo_atomic(const char *path, mode_t mode) { _cleanup_free_ char *t = NULL; + int r; assert(path); - t = tempfn_random(path); - if (!t) - return -ENOMEM; + r = tempfn_random(path, &t); + if (r < 0) + return r; if (mkfifo(t, mode) < 0) return -errno; @@ -5150,6 +5258,9 @@ char *format_bytes(char *buf, size_t l, off_t t) { { "K", 1024ULL }, }; + if (t == (off_t) -1) + return NULL; + for (i = 0; i < ELEMENTSOF(table); i++) { if (t >= table[i].factor) { @@ -5290,7 +5401,7 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa * keep an unused copy of stdin around. */ fd = open("/dev/tty", O_WRONLY); if (fd < 0) { - log_error("Failed to open /dev/tty: %m"); + log_error_errno(errno, "Failed to open /dev/tty: %m"); _exit(EXIT_FAILURE); } @@ -5401,25 +5512,56 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) { return r; } -bool is_valid_documentation_url(const char *url) { - assert(url); +bool http_etag_is_valid(const char *etag) { + if (isempty(etag)) + return false; - if (startswith(url, "http://") && url[7]) - return true; + if (!endswith(etag, "\"")) + return false; - if (startswith(url, "https://") && url[8]) - return true; + if (!startswith(etag, "\"") && !startswith(etag, "W/\"")) + return false; - if (startswith(url, "file:") && url[5]) - return true; + return true; +} - if (startswith(url, "info:") && url[5]) - return true; +bool http_url_is_valid(const char *url) { + const char *p; - if (startswith(url, "man:") && url[4]) + if (isempty(url)) + return false; + + p = startswith(url, "http://"); + if (!p) + p = startswith(url, "https://"); + if (!p) + return false; + + if (isempty(p)) + return false; + + return ascii_is_valid(p); +} + +bool documentation_url_is_valid(const char *url) { + const char *p; + + if (isempty(url)) + return false; + + if (http_url_is_valid(url)) return true; - return false; + p = startswith(url, "file:/"); + if (!p) + p = startswith(url, "info:"); + if (!p) + p = startswith(url, "man:"); + + if (isempty(p)) + return false; + + return ascii_is_valid(p); } bool in_initrd(void) { @@ -5473,16 +5615,12 @@ int make_console_stdio(void) { /* Make /dev/console the controlling terminal and stdin/stdout/stderr */ fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY); - if (fd < 0) { - log_error("Failed to acquire terminal: %s", strerror(-fd)); - return fd; - } + if (fd < 0) + return log_error_errno(fd, "Failed to acquire terminal: %m"); r = make_stdio(fd); - if (r < 0) { - log_error("Failed to duplicate terminal fd: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to duplicate terminal fd: %m"); return 0; } @@ -5580,8 +5718,9 @@ int get_shell(char **_s) { *_s = s; return 0; } +#endif /* NM_IGNORED */ -bool filename_is_safe(const char *p) { +bool filename_is_valid(const char *p) { if (isempty(p)) return false; @@ -5601,6 +5740,7 @@ bool filename_is_safe(const char *p) { return true; } +#if 0 /* NM_IGNORED */ bool string_is_safe(const char *p) { const char *t; @@ -5687,6 +5827,11 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, return NULL; } +void init_gettext(void) { + setlocale(LC_ALL, ""); + textdomain(GETTEXT_PACKAGE); +} + bool is_locale_utf8(void) { const char *set; static int cached_answer = -1; @@ -5914,7 +6059,7 @@ int on_ac_power(void) { if (!de) break; - if (ignore_file(de->d_name)) + if (hidden_file(de->d_name)) continue; device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); @@ -6241,38 +6386,16 @@ int split_pair(const char *s, const char *sep, char **l, char **r) { } int shall_restore_state(void) { - _cleanup_free_ char *line = NULL; - const char *p; + _cleanup_free_ char *value = NULL; int r; - r = proc_cmdline(&line); + r = get_proc_cmdline_key("systemd.restore_state=", &value); if (r < 0) return r; + if (r == 0) + return true; - r = 1; - p = line; - - for (;;) { - _cleanup_free_ char *word = NULL; - const char *e; - int k; - - k = unquote_first_word(&p, &word, true); - if (k < 0) - return k; - if (k == 0) - break; - - e = startswith(word, "systemd.restore_state="); - if (!e) - continue; - - k = parse_boolean(e); - if (k >= 0) - r = k; - } - - return r; + return parse_boolean(value) != 0; } int proc_cmdline(char **ret) { @@ -6323,6 +6446,59 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { return 0; } +int get_proc_cmdline_key(const char *key, char **value) { + _cleanup_free_ char *line = NULL, *ret = NULL; + bool found = false; + const char *p; + int r; + + assert(key); + + r = proc_cmdline(&line); + if (r < 0) + return r; + + p = line; + for (;;) { + _cleanup_free_ char *word = NULL; + const char *e; + + r = unquote_first_word(&p, &word, true); + if (r < 0) + return r; + if (r == 0) + break; + + /* Filter out arguments that are intended only for the + * initrd */ + if (!in_initrd() && startswith(word, "rd.")) + continue; + + if (value) { + e = startswith(word, key); + if (!e) + continue; + + r = free_and_strdup(&ret, e); + if (r < 0) + return r; + + found = true; + } else { + if (streq(word, key)) + found = true; + } + } + + if (value) { + *value = ret; + ret = NULL; + } + + return found; + +} + int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; const char *p; @@ -6332,7 +6508,7 @@ int container_get_leader(const char *machine, pid_t *pid) { assert(machine); assert(pid); - p = strappenda("/run/systemd/machines/", machine); + p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); if (r == -ENOENT) return -EHOSTDOWN; @@ -6493,6 +6669,10 @@ int getpeercred(int fd, struct ucred *ucred) { * to namespacing issues */ if (u.pid <= 0) return -ENODATA; + if (u.uid == UID_INVALID) + return -ENODATA; + if (u.gid == GID_INVALID) + return -ENODATA; *ucred = u; return 0; @@ -6569,7 +6749,7 @@ int open_tmpfile(const char *path, int flags) { #endif /* Fall back to unguessable name + unlinking */ - p = strappenda(path, "/systemd-tmp-XXXXXX"); + p = strjoina(path, "/systemd-tmp-XXXXXX"); fd = mkostemp_safe(p, flags); if (fd < 0) @@ -6657,23 +6837,6 @@ uint64_t physical_memory(void) { return (uint64_t) mem * (uint64_t) page_size(); } -char* mount_test_option(const char *haystack, const char *needle) { - - struct mntent me = { - .mnt_opts = (char*) haystack - }; - - assert(needle); - - /* Like glibc's hasmntopt(), but works on a string, not a - * struct mntent */ - - if (!haystack) - return NULL; - - return hasmntopt(&me, needle); -} - void hexdump(FILE *f, const void *p, size_t s) { const uint8_t *b = p; unsigned n = 0; @@ -6798,6 +6961,15 @@ int umount_recursive(const char *prefix, int flags) { return r ? r : n; } +static int get_mount_flags(const char *path, unsigned long *flags) { + struct statvfs buf; + + if (statvfs(path, &buf) < 0) + return -errno; + *flags = buf.f_flag; + return 0; +} + int bind_remount_recursive(const char *prefix, bool ro) { _cleanup_set_free_free_ Set *done = NULL; _cleanup_free_ char *cleaned = NULL; @@ -6832,6 +7004,7 @@ int bind_remount_recursive(const char *prefix, bool ro) { _cleanup_set_free_free_ Set *todo = NULL; bool top_autofs = false; char *x; + unsigned long orig_flags; todo = set_new(&string_hash_ops); if (!todo) @@ -6909,7 +7082,11 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) return -errno; - if (mount(NULL, prefix, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) + orig_flags = 0; + (void) get_mount_flags(cleaned, &orig_flags); + orig_flags &= ~MS_RDONLY; + + if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) return -errno; x = strdup(cleaned); @@ -6929,7 +7106,14 @@ int bind_remount_recursive(const char *prefix, bool ro) { if (r < 0) return r; - if (mount(NULL, x, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) { + /* Try to reuse the original flag set, but + * don't care for errors, in case of + * obstructed mounts */ + orig_flags = 0; + (void) get_mount_flags(x, &orig_flags); + orig_flags &= ~MS_RDONLY; + + if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) { /* Deal with mount points that are * obstructed by a later mount */ @@ -6955,43 +7139,62 @@ int fflush_and_check(FILE *f) { return 0; } -char *tempfn_xxxxxx(const char *p) { +int tempfn_xxxxxx(const char *p, char **ret) { const char *fn; char *t; - size_t k; assert(p); + assert(ret); - t = new(char, strlen(p) + 1 + 6 + 1); - if (!t) - return NULL; + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#waldoXXXXXX + */ fn = basename(p); - k = fn - p; + if (!filename_is_valid(fn)) + return -EINVAL; + + t = new(char, strlen(p) + 2 + 6 + 1); + if (!t) + return -ENOMEM; - strcpy(stpcpy(stpcpy(mempcpy(t, p, k), "."), fn), "XXXXXX"); + strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX"); - return t; + *ret = path_kill_slashes(t); + return 0; } #if 0 /* NM_IGNORED */ -char *tempfn_random(const char *p) { +int tempfn_random(const char *p, char **ret) { const char *fn; char *t, *x; uint64_t u; - size_t k; unsigned i; assert(p); + assert(ret); - t = new(char, strlen(p) + 1 + 16 + 1); - if (!t) - return NULL; + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#waldobaa2a261115984a9 + */ fn = basename(p); - k = fn - p; + if (!filename_is_valid(fn)) + return -EINVAL; + + t = new(char, strlen(p) + 2 + 16 + 1); + if (!t) + return -ENOMEM; - x = stpcpy(stpcpy(mempcpy(t, p, k), "."), fn); + x = stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn); u = random_u64(); for (i = 0; i < 16; i++) { @@ -7001,7 +7204,40 @@ char *tempfn_random(const char *p) { *x = 0; - return t; + *ret = path_kill_slashes(t); + return 0; +} + +int tempfn_random_child(const char *p, char **ret) { + char *t, *x; + uint64_t u; + unsigned i; + + assert(p); + assert(ret); + + /* Turns this: + * /foo/bar/waldo + * Into this: + * /foo/bar/waldo/.#3c2b6219aa75d7d0 + */ + + t = new(char, strlen(p) + 3 + 16 + 1); + if (!t) + return -ENOMEM; + + x = stpcpy(stpcpy(t, p), "/.#"); + + u = random_u64(); + for (i = 0; i < 16; i++) { + *(x++) = hexchar(u & 0xF); + u >>= 4; + } + + *x = 0; + + *ret = path_kill_slashes(t); + return 0; } #endif /* NM_IGNORED */ @@ -7046,7 +7282,7 @@ int take_password_lock(const char *root) { * awfully racy, and thus we just won't do them. */ if (root) - path = strappenda(root, "/etc/.pwd.lock"); + path = strjoina(root, "/etc/.pwd.lock"); else path = "/etc/.pwd.lock"; @@ -7342,5 +7578,598 @@ int sethostname_idempotent(const char *s) { return 1; } + +int ptsname_malloc(int fd, char **ret) { + size_t l = 100; + + assert(fd >= 0); + assert(ret); + + for (;;) { + char *c; + + c = new(char, l); + if (!c) + return -ENOMEM; + + if (ptsname_r(fd, c, l) == 0) { + *ret = c; + return 0; + } + if (errno != ERANGE) { + free(c); + return -errno; + } + + free(c); + l *= 2; + } +} + +int openpt_in_namespace(pid_t pid, int flags) { + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + siginfo_t si; + pid_t child; + int r; + + assert(pid > 0); + + r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + int master; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd); + if (r < 0) + _exit(EXIT_FAILURE); + + master = posix_openpt(flags); + if (master < 0) + _exit(EXIT_FAILURE); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &master, sizeof(int)); + + mh.msg_controllen = cmsg->cmsg_len; + + if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return -EIO; + + if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) + return -errno; + + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int *fds; + unsigned n_fds; + + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + if (n_fds != 1) { + close_many(fds, n_fds); + return -EIO; + } + + return fds[0]; + } + + return -EIO; +} + +ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) { + _cleanup_close_ int fd = -1; + ssize_t l; + + /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */ + + fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); + if (fd < 0) + return -errno; + + l = fgetxattr(fd, attribute, value, size); + if (l < 0) + return -errno; + + return l; +} + +static int parse_crtime(le64_t le, usec_t *usec) { + uint64_t u; + + assert(usec); + + u = le64toh(le); + if (u == 0 || u == (uint64_t) -1) + return -EIO; + + *usec = (usec_t) u; + return 0; +} + +int fd_getcrtime(int fd, usec_t *usec) { + le64_t le; + ssize_t n; + + assert(fd >= 0); + assert(usec); + + /* Until Linux gets a real concept of birthtime/creation time, + * let's fake one with xattrs */ + + n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le)); + if (n < 0) + return -errno; + if (n != sizeof(le)) + return -EIO; + + return parse_crtime(le, usec); +} + +int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) { + le64_t le; + ssize_t n; + + n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags); + if (n < 0) + return -errno; + if (n != sizeof(le)) + return -EIO; + + return parse_crtime(le, usec); +} + +int path_getcrtime(const char *p, usec_t *usec) { + le64_t le; + ssize_t n; + + assert(p); + assert(usec); + + n = getxattr(p, "user.crtime_usec", &le, sizeof(le)); + if (n < 0) + return -errno; + if (n != sizeof(le)) + return -EIO; + + return parse_crtime(le, usec); +} + +int fd_setcrtime(int fd, usec_t usec) { + le64_t le; + + assert(fd >= 0); + + if (usec <= 0) + usec = now(CLOCK_REALTIME); + + le = htole64((uint64_t) usec); + if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0) + return -errno; + + return 0; +} + +int same_fd(int a, int b) { + struct stat sta, stb; + pid_t pid; + int r, fa, fb; + + assert(a >= 0); + assert(b >= 0); + + /* Compares two file descriptors. Note that semantics are + * quite different depending on whether we have kcmp() or we + * don't. If we have kcmp() this will only return true for + * dup()ed file descriptors, but not otherwise. If we don't + * have kcmp() this will also return true for two fds of the same + * file, created by separate open() calls. Since we use this + * call mostly for filtering out duplicates in the fd store + * this difference hopefully doesn't matter too much. */ + + if (a == b) + return true; + + /* Try to use kcmp() if we have it. */ + pid = getpid(); + r = kcmp(pid, pid, KCMP_FILE, a, b); + if (r == 0) + return true; + if (r > 0) + return false; + if (errno != ENOSYS) + return -errno; + + /* We don't have kcmp(), use fstat() instead. */ + if (fstat(a, &sta) < 0) + return -errno; + + if (fstat(b, &stb) < 0) + return -errno; + + if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT)) + return false; + + /* We consider all device fds different, since two device fds + * might refer to quite different device contexts even though + * they share the same inode and backing dev_t. */ + + if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode)) + return false; + + if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) + return false; + + /* The fds refer to the same inode on disk, let's also check + * if they have the same fd flags. This is useful to + * distuingish the read and write side of a pipe created with + * pipe(). */ + fa = fcntl(a, F_GETFL); + if (fa < 0) + return -errno; + + fb = fcntl(b, F_GETFL); + if (fb < 0) + return -errno; + + return fa == fb; +} + +int chattr_fd(int fd, bool b, unsigned mask) { + unsigned old_attr, new_attr; + + assert(fd >= 0); + + if (mask == 0) + return 0; + + if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0) + return -errno; + + if (b) + new_attr = old_attr | mask; + else + new_attr = old_attr & ~mask; + + if (new_attr == old_attr) + return 0; + + if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0) + return -errno; + + return 0; +} + +int chattr_path(const char *p, bool b, unsigned mask) { + _cleanup_close_ int fd = -1; + + assert(p); + + if (mask == 0) + return 0; + + fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + return -errno; + + return chattr_fd(fd, b, mask); +} + +int read_attr_fd(int fd, unsigned *ret) { + assert(fd >= 0); + + if (ioctl(fd, FS_IOC_GETFLAGS, ret) < 0) + return -errno; + + return 0; +} + +int read_attr_path(const char *p, unsigned *ret) { + _cleanup_close_ int fd = -1; + + assert(p); + assert(ret); + + fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + return -errno; + + return read_attr_fd(fd, ret); +} + +int make_lock_file(const char *p, int operation, LockFile *ret) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *t = NULL; + int r; + + /* + * We use UNPOSIX locks if they are available. They have nice + * semantics, and are mostly compatible with NFS. However, + * they are only available on new kernels. When we detect we + * are running on an older kernel, then we fall back to good + * old BSD locks. They also have nice semantics, but are + * slightly problematic on NFS, where they are upgraded to + * POSIX locks, even though locally they are orthogonal to + * POSIX locks. + */ + + t = strdup(p); + if (!t) + return -ENOMEM; + + for (;;) { + struct flock fl = { + .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, + .l_whence = SEEK_SET, + }; + struct stat st; + + fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) + return -errno; + + r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); + if (r < 0) { + + /* If the kernel is too old, use good old BSD locks */ + if (errno == EINVAL) + r = flock(fd, operation); + + if (r < 0) + return errno == EAGAIN ? -EBUSY : -errno; + } + + /* If we acquired the lock, let's check if the file + * still exists in the file system. If not, then the + * previous exclusive owner removed it and then closed + * it. In such a case our acquired lock is worthless, + * hence try again. */ + + r = fstat(fd, &st); + if (r < 0) + return -errno; + if (st.st_nlink > 0) + break; + + fd = safe_close(fd); + } + + ret->path = t; + ret->fd = fd; + ret->operation = operation; + + fd = -1; + t = NULL; + + return r; +} + +int make_lock_file_for(const char *p, int operation, LockFile *ret) { + const char *fn; + char *t; + + assert(p); + assert(ret); + + fn = basename(p); + if (!filename_is_valid(fn)) + return -EINVAL; + + t = newa(char, strlen(p) + 2 + 4 + 1); + stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck"); + + return make_lock_file(t, operation, ret); +} + +void release_lock_file(LockFile *f) { + int r; + + if (!f) + return; + + if (f->path) { + + /* If we are the exclusive owner we can safely delete + * the lock file itself. If we are not the exclusive + * owner, we can try becoming it. */ + + if (f->fd >= 0 && + (f->operation & ~LOCK_NB) == LOCK_SH) { + static const struct flock fl = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + + r = fcntl(f->fd, F_OFD_SETLK, &fl); + if (r < 0 && errno == EINVAL) + r = flock(f->fd, LOCK_EX|LOCK_NB); + + if (r >= 0) + f->operation = LOCK_EX|LOCK_NB; + } + + if ((f->operation & ~LOCK_NB) == LOCK_EX) + unlink_noerrno(f->path); + + free(f->path); + f->path = NULL; + } + + f->fd = safe_close(f->fd); + f->operation = 0; +} + +static size_t nul_length(const uint8_t *p, size_t sz) { + size_t n = 0; + + while (sz > 0) { + if (*p != 0) + break; + + n++; + p++; + sz--; + } + + return n; +} + +ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) { + const uint8_t *q, *w, *e; + ssize_t l; + + q = w = p; + e = q + sz; + while (q < e) { + size_t n; + + n = nul_length(q, e - q); + + /* If there are more than the specified run length of + * NUL bytes, or if this is the beginning or the end + * of the buffer, then seek instead of write */ + if ((n > run_length) || + (n > 0 && q == p) || + (n > 0 && q + n >= e)) { + if (q > w) { + l = write(fd, w, q - w); + if (l < 0) + return -errno; + if (l != q -w) + return -EIO; + } + + if (lseek(fd, n, SEEK_CUR) == (off_t) -1) + return -errno; + + q += n; + w = q; + } else if (n > 0) + q += n; + else + q ++; + } + + if (q > w) { + l = write(fd, w, q - w); + if (l < 0) + return -errno; + if (l != q - w) + return -EIO; + } + + return q - (const uint8_t*) p; +} + +void sigkill_wait(pid_t *pid) { + if (!pid) + return; + if (*pid <= 1) + return; + + if (kill(*pid, SIGKILL) > 0) + (void) wait_for_terminate(*pid, NULL); +} + +int syslog_parse_priority(const char **p, int *priority, bool with_facility) { + int a = 0, b = 0, c = 0; + int k; + + assert(p); + assert(*p); + assert(priority); + + if ((*p)[0] != '<') + return 0; + + if (!strchr(*p, '>')) + return 0; + + if ((*p)[2] == '>') { + c = undecchar((*p)[1]); + k = 3; + } else if ((*p)[3] == '>') { + b = undecchar((*p)[1]); + c = undecchar((*p)[2]); + k = 4; + } else if ((*p)[4] == '>') { + a = undecchar((*p)[1]); + b = undecchar((*p)[2]); + c = undecchar((*p)[3]); + k = 5; + } else + return 0; + + if (a < 0 || b < 0 || c < 0 || + (!with_facility && (a || b || c > 7))) + return 0; + + if (with_facility) + *priority = a*100 + b*10 + c; + else + *priority = (*priority & LOG_FACMASK) | c; + + *p += k; + return 1; +} #endif /* NM_IGNORED */ +ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { + size_t i; + + if (!key) + return -1; + + for (i = 0; i < len; ++i) + if (streq_ptr(table[i], key)) + return (ssize_t)i; + + return -1; +} + +#if 0 /* NM_IGNORED */ +void cmsg_close_all(struct msghdr *mh) { + struct cmsghdr *cmsg; + + assert(mh); + + for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); +} +#endif /* NM_IGNORED */ diff --git a/src/dhcp-manager/systemd-dhcp/src/shared/util.h b/src/dhcp-manager/systemd-dhcp/src/shared/util.h index 68b08c4016..573852f754 100644 --- a/src/dhcp-manager/systemd-dhcp/src/shared/util.h +++ b/src/dhcp-manager/systemd-dhcp/src/shared/util.h @@ -27,7 +27,6 @@ #include <fcntl.h> #include <inttypes.h> #include <time.h> -#include <sys/time.h> #include <stdarg.h> #include <stdbool.h> #include <stdlib.h> @@ -36,23 +35,24 @@ #include <sched.h> #include <limits.h> #include <sys/types.h> +#include <sys/socket.h> #include <sys/stat.h> #include <dirent.h> -#include <sys/resource.h> #include <stddef.h> #include <unistd.h> #include <locale.h> #include <mntent.h> -#include <sys/socket.h> +#include <sys/inotify.h> #if 0 /* NM_IGNORED */ #if SIZEOF_PID_T == 4 -# define PID_FMT "%" PRIu32 +# define PID_PRI PRIi32 #elif SIZEOF_PID_T == 2 -# define PID_FMT "%" PRIu16 +# define PID_PRI PRIi16 #else # error Unknown pid_t size #endif +#define PID_FMT "%" PID_PRI #if SIZEOF_UID_T == 4 # define UID_FMT "%" PRIu32 @@ -71,7 +71,7 @@ #endif #if SIZEOF_TIME_T == 8 -# define PRI_TIME PRIu64 +# define PRI_TIME PRIi64 #elif SIZEOF_TIME_T == 4 # define PRI_TIME PRIu32 #else @@ -118,7 +118,7 @@ #define ANSI_HIGHLIGHT_OFF "\x1B[0m" #define ANSI_ERASE_TO_END_OF_LINE "\x1B[K" -size_t page_size(void); +size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) #define streq(a,b) (strcmp((a),(b)) == 0) @@ -148,6 +148,10 @@ static inline const char* true_false(bool b) { return b ? "true" : "false"; } +static inline const char* one_zero(bool b) { + return b ? "1" : "0"; +} + static inline const char* strempty(const char *s) { return s ? s : ""; } @@ -269,7 +273,6 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); -int get_starttime_of_pid(pid_t pid, unsigned long long *st); char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); @@ -301,6 +304,9 @@ int get_process_exe(pid_t pid, char **name); int get_process_uid(pid_t pid, uid_t *uid); int get_process_gid(pid_t pid, gid_t *gid); int get_process_capeff(pid_t pid, char **capeff); +int get_process_cwd(pid_t pid, char **cwd); +int get_process_root(pid_t pid, char **root); +int get_process_environ(pid_t pid, char **environ); char hexchar(int x) _const_; int unhexchar(char c) _const_; @@ -321,7 +327,7 @@ char *ascii_strlower(char *path); bool dirent_is_file(const struct dirent *de) _pure_; bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_; -bool ignore_file(const char *filename) _pure_; +bool hidden_file(const char *filename) _pure_; bool chars_intersect(const char *a, const char *b) _pure_; @@ -346,26 +352,29 @@ static inline uint32_t random_u32(void) { } /* For basic lookup tables with strictly enumerated entries */ -#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ +#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ scope const char *name##_to_string(type i) { \ if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ return NULL; \ return name##_table[i]; \ - } \ - scope type name##_from_string(const char *s) { \ - type i; \ - if (!s) \ - return (type) -1; \ - for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \ - if (name##_table[i] && \ - streq(name##_table[i], s)) \ - return i; \ - return (type) -1; \ - } \ + } + +ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); + +#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ + scope inline type name##_from_string(const char *s) { \ + return (type)string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ + } + +#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ struct __useless_struct_to_allow_trailing_semicolon__ -#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static) +#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) /* For string conversions where numbers are also acceptable */ #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ @@ -379,7 +388,7 @@ static inline uint32_t random_u32(void) { if (!s) \ return log_oom(); \ } else { \ - r = asprintf(&s, "%u", i); \ + r = asprintf(&s, "%i", i); \ if (r < 0) \ return log_oom(); \ } \ @@ -429,7 +438,7 @@ int sigaction_many(const struct sigaction *sa, ...); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); -ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); +int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); bool is_device_path(const char *path); @@ -457,6 +466,8 @@ int get_ctty(pid_t, dev_t *_devnr, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); +int is_fd_on_temporary_fs(int fd); + int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev); int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev); int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); @@ -469,6 +480,8 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus); int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0); int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5); +#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf)) + int fd_columns(int fd); unsigned columns(void); int fd_lines(int fd); @@ -516,7 +529,7 @@ char *unquote(const char *s, const char *quotes); char *normalize_env_assignment(const char *s); int wait_for_terminate(pid_t pid, siginfo_t *status); -int wait_for_terminate_and_warn(const char *name, pid_t pid); +int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); noreturn void freeze(void); @@ -535,7 +548,7 @@ bool tty_is_console(const char *tty) _pure_; int vtnr_from_tty(const char *tty); const char *default_term_for_tty(const char *tty); -void execute_directory(const char *directory, DIR *_d, usec_t timeout, char *argv[]); +void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); int kill_and_sigcont(pid_t pid, int sig); @@ -648,7 +661,10 @@ int setrlimit_closest(int resource, const struct rlimit *rlim); int getenv_for_pid(pid_t pid, const char *field, char **_value); -bool is_valid_documentation_url(const char *url) _pure_; +bool http_url_is_valid(const char *url) _pure_; +bool documentation_url_is_valid(const char *url) _pure_; + +bool http_etag_is_valid(const char *etag); bool in_initrd(void); @@ -661,13 +677,6 @@ static inline void freep(void *p) { free(*(void**) p); } -#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ - static inline void func##p(type *p) { \ - if (*p) \ - func(*p); \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - static inline void closep(int *fd) { safe_close(*fd); } @@ -716,7 +725,7 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_ return memdup(p, a * b); } -bool filename_is_safe(const char *p) _pure_; +bool filename_is_valid(const char *p) _pure_; bool path_is_safe(const char *p) _pure_; bool string_is_safe(const char *p) _pure_; bool string_has_cc(const char *p, const char *ok) _pure_; @@ -732,6 +741,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, int (*compar) (const void *, const void *, void *), void *arg); +#define _(String) gettext (String) +void init_gettext(void); bool is_locale_utf8(void); typedef enum DrawSpecialChar { @@ -773,10 +784,19 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root on_error; \ } \ break; \ - } else if (ignore_file((de)->d_name)) \ + } else if (hidden_file((de)->d_name)) \ continue; \ else +#define FOREACH_DIRENT_ALL(de, d, on_error) \ + for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \ + if (!de) { \ + if (errno > 0) { \ + on_error; \ + } \ + break; \ + } else + static inline void *mempset(void *s, int c, size_t n) { memset(s, c, n); return (uint8_t*)s + n; @@ -841,7 +861,7 @@ static inline unsigned u32ctz(uint32_t n) { #endif } -static inline int log2i(int x) { +static inline unsigned log2i(int x) { assert(x > 0); return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; @@ -885,6 +905,7 @@ int unlink_noerrno(const char *path); (void *) memset(_new_, 0, _len_); \ }) +/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */ #define alloca_align(size, align) \ ({ \ void *_ptr_; \ @@ -901,19 +922,19 @@ int unlink_noerrno(const char *path); (void*)memset(_new_, 0, _size_); \ }) -#define strappenda(a, ...) \ - ({ \ - int _len = strlen(a); \ - unsigned _i; \ - char *_d_, *_p_; \ - const char *_appendees_[] = { __VA_ARGS__ }; \ - for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \ - _len += strlen(_appendees_[_i]); \ - _d_ = alloca(_len + 1); \ - _p_ = stpcpy(_d_, a); \ - for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \ - _p_ = stpcpy(_p_, _appendees_[_i]); \ - _d_; \ +#define strjoina(a, ...) \ + ({ \ + const char *_appendees_[] = { a, __VA_ARGS__ }; \ + char *_d_, *_p_; \ + int _len_ = 0; \ + unsigned _i_; \ + for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ + _len_ += strlen(_appendees_[_i_]); \ + _p_ = _d_ = alloca(_len_ + 1); \ + for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ + _p_ = stpcpy(_p_, _appendees_[_i_]); \ + *_p_ = 0; \ + _d_; \ }) #define procfs_file_alloca(pid, field) \ @@ -929,32 +950,6 @@ int unlink_noerrno(const char *path); _r_; \ }) -struct _locale_struct_ { - locale_t saved_locale; - locale_t new_locale; - bool quit; -}; - -static inline void _reset_locale_(struct _locale_struct_ *s) { - PROTECT_ERRNO; - if (s->saved_locale != (locale_t) 0) - uselocale(s->saved_locale); - if (s->new_locale != (locale_t) 0) - freelocale(s->new_locale); -} - -#define RUN_WITH_LOCALE(mask, loc) \ - for (_cleanup_(_reset_locale_) struct _locale_struct_ _saved_locale_ = { (locale_t) 0, (locale_t) 0, false }; \ - ({ \ - if (!_saved_locale_.quit) { \ - PROTECT_ERRNO; \ - _saved_locale_.new_locale = newlocale((mask), (loc), (locale_t) 0); \ - if (_saved_locale_.new_locale != (locale_t) 0) \ - _saved_locale_.saved_locale = uselocale(_saved_locale_.new_locale); \ - } \ - !_saved_locale_.quit; }) ; \ - _saved_locale_.quit = true) - bool id128_is_valid(const char *s) _pure_; int split_pair(const char *s, const char *sep, char **l, char **r); @@ -975,6 +970,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, int proc_cmdline(char **ret); int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value)); +int get_proc_cmdline_key(const char *parameter, char **value); int container_get_leader(const char *machine, pid_t *pid); @@ -1001,8 +997,6 @@ const char *personality_to_string(unsigned long); uint64_t physical_memory(void); -char* mount_test_option(const char *haystack, const char *needle); - void hexdump(FILE *f, const void *p, size_t s); #if 0 /* NM_IGNORED */ @@ -1010,6 +1004,7 @@ union file_handle_union { struct file_handle handle; char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ]; }; +#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ } #endif /* NM_IGNORED */ int update_reboot_param_file(const char *param); @@ -1020,8 +1015,9 @@ int bind_remount_recursive(const char *prefix, bool ro); int fflush_and_check(FILE *f); -char *tempfn_xxxxxx(const char *p); -char *tempfn_random(const char *p); +int tempfn_xxxxxx(const char *p, char **ret); +int tempfn_random(const char *p, char **ret); +int tempfn_random_child(const char *p, char **ret); bool is_localhost(const char *hostname); @@ -1036,3 +1032,61 @@ int unquote_many_words(const char **p, ...) _sentinel_; int free_and_strdup(char **p, const char *s); int sethostname_idempotent(const char *s); + +#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) + +#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ + for ((e) = &buffer.ev; \ + (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ + (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) + +union inotify_event_buffer { + struct inotify_event ev; + uint8_t raw[INOTIFY_EVENT_MAX]; +}; + +#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) + +int ptsname_malloc(int fd, char **ret); + +int openpt_in_namespace(pid_t pid, int flags); + +ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags); + +int fd_setcrtime(int fd, usec_t usec); +int fd_getcrtime(int fd, usec_t *usec); +int path_getcrtime(const char *p, usec_t *usec); +int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags); + +int same_fd(int a, int b); + +int chattr_fd(int fd, bool b, unsigned mask); +int chattr_path(const char *p, bool b, unsigned mask); + +int read_attr_fd(int fd, unsigned *ret); +int read_attr_path(const char *p, unsigned *ret); + +typedef struct LockFile { + char *path; + int fd; + int operation; +} LockFile; + +int make_lock_file(const char *p, int operation, LockFile *ret); +int make_lock_file_for(const char *p, int operation, LockFile *ret); +void release_lock_file(LockFile *f); + +#define _cleanup_release_lock_file_ _cleanup_(release_lock_file) + +#define LOCK_FILE_INIT { .fd = -1, .path = NULL } + +#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) + +ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); + +void sigkill_wait(pid_t *pid); +#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait) + +int syslog_parse_priority(const char **p, int *priority, bool with_facility); + +void cmsg_close_all(struct msghdr *mh); diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h index 301f306705..e2646cbbc4 100644 --- a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h +++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-client.h @@ -35,6 +35,7 @@ enum { DHCP6_EVENT_RESEND_EXPIRE = 10, DHCP6_EVENT_RETRANS_MAX = 11, DHCP6_EVENT_IP_ACQUIRE = 12, + DHCP6_EVENT_INFORMATION_REQUEST = 13, }; typedef struct sd_dhcp6_client sd_dhcp6_client; @@ -49,7 +50,10 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, uint16_t arp_type); int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, size_t duid_len); -int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname); +int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, + bool enabled); +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, + bool *enabled); int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option); diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h index e72eb8be05..84008cf590 100644 --- a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h +++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp6-lease.h @@ -29,14 +29,11 @@ typedef struct sd_dhcp6_lease sd_dhcp6_lease; -int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease, - struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid); -int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease, - struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid); +void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, + struct in6_addr *addr, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); diff --git a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h index df9b83c4c0..da1bb2fa24 100644 --- a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h +++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-id128.h @@ -62,7 +62,7 @@ int sd_id128_get_boot(sd_id128_t *ret); /* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 * times. It is hence not a good idea to call this macro with an - * expensive function as paramater or an expression with side + * expensive function as parameter or an expression with side * effects */ #define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" @@ -108,6 +108,10 @@ _sd_pure_ static inline int sd_id128_equal(sd_id128_t a, sd_id128_t b) { return memcmp(&a, &b, 16) == 0; } +_sd_pure_ static inline int sd_id128_is_null(sd_id128_t a) { + return a.qwords[0] == 0 && a.qwords[1] == 0; +} + #define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) _SD_END_DECLARATIONS; |