diff options
Diffstat (limited to 'src/systemd')
144 files changed, 1493 insertions, 25889 deletions
diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 1bf1ea41..9dea4fb5 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -1,73 +1,46 @@ -sources = files( - 'sd-adapt/nm-sd-adapt.c', - 'src/basic/alloc-util.c', - 'src/basic/escape.c', - 'src/basic/env-util.c', - 'src/basic/ether-addr-util.c', - 'src/basic/extract-word.c', - 'src/basic/fd-util.c', - 'src/basic/fileio.c', - 'src/basic/fs-util.c', - 'src/basic/hash-funcs.c', - 'src/basic/hashmap.c', - 'src/basic/hexdecoct.c', - 'src/basic/hostname-util.c', - 'src/basic/in-addr-util.c', - 'src/basic/io-util.c', - 'src/basic/mempool.c', - 'src/basic/parse-util.c', - 'src/basic/path-util.c', - 'src/basic/prioq.c', - 'src/basic/process-util.c', - 'src/basic/random-util.c', - 'src/basic/socket-util.c', - 'src/basic/stat-util.c', - 'src/basic/string-table.c', - 'src/basic/string-util.c', - 'src/basic/strv.c', - 'src/basic/time-util.c', - 'src/basic/utf8.c', - 'src/basic/util.c', - 'src/libsystemd-network/arp-util.c', - 'src/libsystemd-network/dhcp-identifier.c', - 'src/libsystemd-network/dhcp-network.c', - 'src/libsystemd-network/dhcp-option.c', - 'src/libsystemd-network/dhcp-packet.c', - 'src/libsystemd-network/dhcp6-network.c', - 'src/libsystemd-network/dhcp6-option.c', - 'src/libsystemd-network/lldp-neighbor.c', - 'src/libsystemd-network/lldp-network.c', - 'src/libsystemd-network/network-internal.c', - 'src/libsystemd-network/sd-dhcp-client.c', - 'src/libsystemd-network/sd-dhcp-lease.c', - 'src/libsystemd-network/sd-dhcp6-client.c', - 'src/libsystemd-network/sd-dhcp6-lease.c', - 'src/libsystemd-network/sd-ipv4acd.c', - 'src/libsystemd-network/sd-ipv4ll.c', - 'src/libsystemd-network/sd-lldp.c', - 'src/libsystemd/sd-event/sd-event.c', - 'src/libsystemd/sd-id128/id128-util.c', - 'src/libsystemd/sd-id128/sd-id128.c', - 'src/shared/dns-domain.c', - 'nm-sd.c', - 'nm-sd-utils.c', -) - -incs = [ - src_inc, - include_directories( - 'sd-adapt', - 'src/basic', - 'src/libsystemd-network', - 'src/shared', - 'src/systemd' - ) -] - -libsystemd_nm = static_library( - 'systemd-nm', - sources: sources, - include_directories: incs, - dependencies: nm_core_dep, - c_args: '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD' +libnm_systemd_core = static_library( + 'nm-systemd-core', + sources: files( + 'sd-adapt-core/nm-sd-adapt-core.c', + 'src/libsystemd-network/arp-util.c', + 'src/libsystemd-network/dhcp-identifier.c', + 'src/libsystemd-network/dhcp-network.c', + 'src/libsystemd-network/dhcp-option.c', + 'src/libsystemd-network/dhcp-packet.c', + 'src/libsystemd-network/dhcp6-network.c', + 'src/libsystemd-network/dhcp6-option.c', + 'src/libsystemd-network/lldp-neighbor.c', + 'src/libsystemd-network/lldp-network.c', + 'src/libsystemd-network/network-internal.c', + 'src/libsystemd-network/sd-dhcp-client.c', + 'src/libsystemd-network/sd-dhcp-lease.c', + 'src/libsystemd-network/sd-dhcp6-client.c', + 'src/libsystemd-network/sd-dhcp6-lease.c', + 'src/libsystemd-network/sd-ipv4acd.c', + 'src/libsystemd-network/sd-ipv4ll.c', + 'src/libsystemd-network/sd-lldp.c', + 'src/libsystemd/sd-event/event-util.c', + 'src/libsystemd/sd-event/sd-event.c', + 'src/libsystemd/sd-id128/id128-util.c', + 'src/libsystemd/sd-id128/sd-id128.c', + 'src/shared/dns-domain.c', + 'nm-sd.c', + 'nm-sd-utils-core.c', + ), + include_directories: [ + src_inc, + include_directories( + 'sd-adapt-core', + 'src/libsystemd-network', + 'src/libsystemd/sd-event', + 'src/shared', + 'src/systemd', + ) + ], + dependencies: [ + nm_core_dep, + ], + c_args: [ + '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD', + ], ) diff --git a/src/systemd/nm-sd-utils.c b/src/systemd/nm-sd-utils-core.c index c6c4c123..42560789 100644 --- a/src/systemd/nm-sd-utils.c +++ b/src/systemd/nm-sd-utils-core.c @@ -18,37 +18,16 @@ #include "nm-default.h" -#include "nm-sd-utils.h" +#include "nm-sd-utils-core.h" #include "nm-core-internal.h" -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include "path-util.h" #include "sd-id128.h" /*****************************************************************************/ -gboolean -nm_sd_utils_path_equal (const char *a, const char *b) -{ - return path_equal (a, b); -} - -char * -nm_sd_utils_path_simplify (char *path, gboolean kill_dots) -{ - return path_simplify (path, kill_dots); -} - -const char * -nm_sd_utils_path_startswith (const char *path, const char *prefix) -{ - return path_startswith (path, prefix); -} - -/*****************************************************************************/ - NMUuid * nm_sd_utils_id128_get_machine (NMUuid *out_uuid) { diff --git a/src/systemd/nm-sd-utils.h b/src/systemd/nm-sd-utils-core.h index 0af514eb..a7b092b3 100644 --- a/src/systemd/nm-sd-utils.h +++ b/src/systemd/nm-sd-utils-core.h @@ -16,16 +16,8 @@ * Copyright (C) 2018 Red Hat, Inc. */ -#ifndef __NM_SD_UTILS_H__ -#define __NM_SD_UTILS_H__ - -/*****************************************************************************/ - -gboolean nm_sd_utils_path_equal (const char *a, const char *b); - -char *nm_sd_utils_path_simplify (char *path, gboolean kill_dots); - -const char *nm_sd_utils_path_startswith (const char *path, const char *prefix); +#ifndef __NM_SD_UTILS_CORE_H__ +#define __NM_SD_UTILS_CORE_H__ /*****************************************************************************/ @@ -35,4 +27,4 @@ struct _NMUuid *nm_sd_utils_id128_get_machine (struct _NMUuid *out_uuid); /*****************************************************************************/ -#endif /* __NM_SD_UTILS_H__ */ +#endif /* __NM_SD_UTILS_CORE_H__ */ diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c index cbef91d1..4009c617 100644 --- a/src/systemd/nm-sd.c +++ b/src/systemd/nm-sd.c @@ -133,9 +133,13 @@ nm_sd_event_attach_default (void) /*****************************************************************************/ +const bool mempool_use_allowed = true; + +/*****************************************************************************/ + /* ensure that defines in nm-sd.h correspond to the internal defines. */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include "dhcp-lease-internal.h" /*****************************************************************************/ diff --git a/src/systemd/sd-adapt/condition.h b/src/systemd/sd-adapt-core/condition.h index d3a6812a..d3a6812a 100644 --- a/src/systemd/sd-adapt/condition.h +++ b/src/systemd/sd-adapt-core/condition.h diff --git a/src/systemd/sd-adapt/conf-parser.h b/src/systemd/sd-adapt-core/conf-parser.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/conf-parser.h +++ b/src/systemd/sd-adapt-core/conf-parser.h diff --git a/src/systemd/sd-adapt/architecture.h b/src/systemd/sd-adapt-core/device-util.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/architecture.h +++ b/src/systemd/sd-adapt-core/device-util.h diff --git a/src/systemd/sd-adapt/khash.h b/src/systemd/sd-adapt-core/khash.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/khash.h +++ b/src/systemd/sd-adapt-core/khash.h diff --git a/src/systemd/sd-adapt/nm-sd-adapt.c b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c index 4e308276..d7ed687e 100644 --- a/src/systemd/sd-adapt/nm-sd-adapt.c +++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c @@ -18,7 +18,7 @@ #include "nm-default.h" -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include "fd-util.h" diff --git a/src/systemd/sd-adapt-core/nm-sd-adapt-core.h b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h new file mode 100644 index 00000000..8c07c53a --- /dev/null +++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h @@ -0,0 +1,101 @@ +/* This program 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 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2014 - 2018 Red Hat, Inc. + */ + +#ifndef __NM_SD_ADAPT_CORE_H__ +#define __NM_SD_ADAPT_CORE_H__ + +#include "nm-default.h" + +#include <stdbool.h> +#include <sys/resource.h> +#include <time.h> + +#include "systemd/sd-adapt-shared/nm-sd-adapt-shared.h" + +#ifndef HAVE_SYS_AUXV_H +#define HAVE_SYS_AUXV_H 0 +#endif + +/***************************************************************************** + * The remainder of the header is only enabled when building the systemd code + * itself. + *****************************************************************************/ + +#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD + +#include <netinet/in.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <elf.h> +#ifdef HAVE_SYS_AUXV_H +#include <sys/auxv.h> +#endif +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/ioctl.h> + +#include <net/if_arp.h> + +/* Missing in Linux 3.2.0, in Ubuntu 12.04 */ +#ifndef BPF_XOR +#define BPF_XOR 0xa0 +#endif + +#ifndef ETHERTYPE_LLDP +#define ETHERTYPE_LLDP 0x88cc +#endif + +#ifndef HAVE_SECURE_GETENV +# ifdef HAVE___SECURE_GETENV +# define secure_getenv __secure_getenv +# else +# error neither secure_getenv nor __secure_getenv is available +# endif +#endif + +/*****************************************************************************/ + +static inline int +sd_notify (int unset_environment, const char *state) +{ + return 0; +} + +/* Can't include both net/if.h and linux/if.h; so have to define this here */ +#ifndef IF_NAMESIZE +#define IF_NAMESIZE 16 +#endif + +#ifndef IFNAMSIZ +#define IFNAMSIZ IF_NAMESIZE +#endif + +#ifndef MAX_HANDLE_SZ +#define MAX_HANDLE_SZ 128 +#endif + +#include "sd-id128.h" +#include "sparse-endian.h" +#include "async.h" +#include "util.h" + +#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ + +#endif /* __NM_SD_ADAPT_CORE_H__ */ + diff --git a/src/systemd/sd-adapt/sd-daemon.h b/src/systemd/sd-adapt-core/sd-daemon.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/sd-daemon.h +++ b/src/systemd/sd-adapt-core/sd-daemon.h diff --git a/src/systemd/sd-adapt/sd-device.h b/src/systemd/sd-adapt-core/sd-device.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/sd-device.h +++ b/src/systemd/sd-adapt-core/sd-device.h diff --git a/src/systemd/sd-adapt/stat-util.h b/src/systemd/sd-adapt-core/stat-util.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/stat-util.h +++ b/src/systemd/sd-adapt-core/stat-util.h diff --git a/src/systemd/sd-adapt/btrfs-util.h b/src/systemd/sd-adapt/btrfs-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/btrfs-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/build.h b/src/systemd/sd-adapt/build.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/build.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/cgroup-util.h b/src/systemd/sd-adapt/cgroup-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/cgroup-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/copy.h b/src/systemd/sd-adapt/copy.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/copy.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/def.h b/src/systemd/sd-adapt/def.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/def.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/device-nodes.h b/src/systemd/sd-adapt/device-nodes.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/device-nodes.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/dirent-util.h b/src/systemd/sd-adapt/dirent-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/dirent-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/errno-list.h b/src/systemd/sd-adapt/errno-list.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/errno-list.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/format-util.h b/src/systemd/sd-adapt/format-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/format-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/glob-util.h b/src/systemd/sd-adapt/glob-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/glob-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/gunicode.h b/src/systemd/sd-adapt/gunicode.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/gunicode.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/ioprio.h b/src/systemd/sd-adapt/ioprio.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/ioprio.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/locale-util.h b/src/systemd/sd-adapt/locale-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/locale-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/memfd-util.h b/src/systemd/sd-adapt/memfd-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/memfd-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/missing.h b/src/systemd/sd-adapt/missing.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/missing.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/mkdir.h b/src/systemd/sd-adapt/mkdir.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/mkdir.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h deleted file mode 100644 index e163eeda..00000000 --- a/src/systemd/sd-adapt/nm-sd-adapt.h +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2014 - 2015 Red Hat, Inc. - */ - -#ifndef NM_SD_ADAPT_H -#define NM_SD_ADAPT_H - -#include "nm-default.h" - -#include <stdbool.h> -#include <syslog.h> -#include <sys/resource.h> -#include <time.h> - -#if defined(HAVE_DECL_REALLOCARRAY) && HAVE_DECL_REALLOCARRAY == 1 -#define HAVE_REALLOCARRAY 1 -#else -#define HAVE_REALLOCARRAY 0 -#endif - -#if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1 -#define HAVE_EXPLICIT_BZERO 1 -#else -#define HAVE_EXPLICIT_BZERO 0 -#endif - -#define ENABLE_DEBUG_HASHMAP 0 - -#ifndef HAVE_SYS_AUXV_H -#define HAVE_SYS_AUXV_H 0 -#endif - -/*****************************************************************************/ - -static inline NMLogLevel -_slog_level_to_nm (int slevel) -{ - switch (LOG_PRI (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: - default: return LOGL_INFO; - } -} - -static inline int -_nm_log_get_max_level_realm (void) -{ - /* inline function, to avoid coverity warning about constant expression. */ - return LOG_DEBUG; -} -#define log_get_max_level_realm(realm) _nm_log_get_max_level_realm () - -#define log_internal_realm(level, error, file, line, func, format, ...) \ -({ \ - const int _nm_e = (error); \ - const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \ - \ - if (nm_logging_enabled (_nm_l, LOGD_SYSTEMD)) { \ - const char *_nm_location = strrchr ((""file), '/'); \ - \ - _nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, NULL, NULL, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \ - } \ - (_nm_e > 0 ? -_nm_e : _nm_e); \ -}) - -#define log_assert_failed(text, file, line, func) \ -G_STMT_START { \ - 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(text, file, line, func) \ -G_STMT_START { \ - log_internal (LOG_CRIT, 0, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", text, file, line, func); \ - g_assert_not_reached (); \ -} G_STMT_END - -#define log_assert_failed_return(text, file, line, func) \ -({ \ - 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); \ - (void) 0; \ -}) - -/***************************************************************************** - * The remainder of the header is only enabled when building the systemd code - * itself. - *****************************************************************************/ - -#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD - -#include <netinet/in.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <elf.h> -#ifdef HAVE_SYS_AUXV_H -#include <sys/auxv.h> -#endif -#include <unistd.h> -#include <sys/syscall.h> -#include <sys/ioctl.h> - -#include <net/if_arp.h> - -/* Missing in Linux 3.2.0, in Ubuntu 12.04 */ -#ifndef BPF_XOR -#define BPF_XOR 0xa0 -#endif - -#ifndef ETHERTYPE_LLDP -#define ETHERTYPE_LLDP 0x88cc -#endif - -#ifndef HAVE_SECURE_GETENV -# ifdef HAVE___SECURE_GETENV -# define secure_getenv __secure_getenv -# else -# error neither secure_getenv nor __secure_getenv is available -# endif -#endif - -#define VALGRIND 0 - -static inline pid_t -raw_getpid (void) { -#if defined(__alpha__) - return (pid_t) syscall (__NR_getxpid); -#else - return (pid_t) syscall (__NR_getpid); -#endif -} - -/*****************************************************************************/ - -/* work around missing uchar.h */ -typedef guint16 char16_t; -typedef guint32 char32_t; - -/*****************************************************************************/ - -static inline int -sd_notify (int unset_environment, const char *state) -{ - return 0; -} - -/* Can't include both net/if.h and linux/if.h; so have to define this here */ -#ifndef IF_NAMESIZE -#define IF_NAMESIZE 16 -#endif - -#ifndef IFNAMSIZ -#define IFNAMSIZ IF_NAMESIZE -#endif - -#ifndef MAX_HANDLE_SZ -#define MAX_HANDLE_SZ 128 -#endif - -#include "sd-id128.h" -#include "sparse-endian.h" -#include "async.h" -#include "util.h" - -static inline pid_t gettid(void) { - return (pid_t) syscall(SYS_gettid); -} - -#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ - -#endif /* NM_SD_ADAPT_H */ - diff --git a/src/systemd/sd-adapt/procfs-util.h b/src/systemd/sd-adapt/procfs-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/procfs-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/raw-clone.h b/src/systemd/sd-adapt/raw-clone.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/raw-clone.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/terminal-util.h b/src/systemd/sd-adapt/terminal-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/terminal-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/unaligned.h b/src/systemd/sd-adapt/unaligned.h deleted file mode 100644 index 17dc0444..00000000 --- a/src/systemd/sd-adapt/unaligned.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "nm-utils/unaligned.h" diff --git a/src/systemd/sd-adapt/user-util.h b/src/systemd/sd-adapt/user-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/user-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/virt.h b/src/systemd/sd-adapt/virt.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/virt.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c deleted file mode 100644 index ef405098..00000000 --- a/src/systemd/src/basic/alloc-util.c +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <stdint.h> -#include <string.h> - -#include "alloc-util.h" -#include "macro.h" -#include "util.h" - -void* memdup(const void *p, size_t l) { - void *ret; - - assert(l == 0 || p); - - ret = malloc(l); - if (!ret) - return NULL; - - memcpy(ret, p, l); - return ret; -} - -void* memdup_suffix0(const void *p, size_t l) { - void *ret; - - assert(l == 0 || p); - - /* The same as memdup() but place a safety NUL byte after the allocated memory */ - - ret = malloc(l + 1); - if (!ret) - return NULL; - - *((uint8_t*) mempcpy(ret, p, l)) = 0; - return ret; -} - -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { - size_t a, newalloc; - void *q; - - assert(p); - assert(allocated); - - if (*allocated >= need) - return *p; - - newalloc = MAX(need * 2, 64u / size); - a = newalloc * size; - - /* check for overflows */ - if (a < size * need) - return NULL; - - q = realloc(*p, a); - if (!q) - return NULL; - - *p = q; - *allocated = newalloc; - return q; -} - -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) { - size_t prev; - uint8_t *q; - - assert(p); - assert(allocated); - - prev = *allocated; - - q = greedy_realloc(p, allocated, need, size); - if (!q) - return NULL; - - if (*allocated > prev) - memzero(q + prev * size, (*allocated - prev) * size); - - return q; -} diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h deleted file mode 100644 index ebe42889..00000000 --- a/src/systemd/src/basic/alloc-util.h +++ /dev/null @@ -1,130 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#include "macro.h" - -#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) - -#define new0(t, n) ((t*) calloc((n), sizeof(t))) - -#define newa(t, n) \ - ({ \ - assert(!size_multiply_overflow(sizeof(t), n)); \ - (t*) alloca(sizeof(t)*(n)); \ - }) - -#define newa0(t, n) \ - ({ \ - assert(!size_multiply_overflow(sizeof(t), n)); \ - (t*) alloca0(sizeof(t)*(n)); \ - }) - -#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) - -#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) - -#define malloc0(n) (calloc(1, (n))) - -static inline void *mfree(void *memory) { - free(memory); - return NULL; -} - -#define free_and_replace(a, b) \ - ({ \ - free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) - -void* memdup(const void *p, size_t l) _alloc_(2); -void* memdup_suffix0(const void *p, size_t l) _alloc_(2); - -static inline void freep(void *p) { - free(*(void**) p); -} - -#define _cleanup_free_ _cleanup_(freep) - -static inline bool size_multiply_overflow(size_t size, size_t need) { - return _unlikely_(need != 0 && size > (SIZE_MAX / need)); -} - -_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return malloc(size * need); -} - -#if !HAVE_REALLOCARRAY -_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) { - if (size_multiply_overflow(size, need)) - return NULL; - - return realloc(p, size * need); -} -#endif - -_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup(p, size * need); -} - -_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup_suffix0(p, size * need); -} - -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); - -#define GREEDY_REALLOC(array, allocated, need) \ - greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0])) - -#define GREEDY_REALLOC0(array, allocated, need) \ - greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0])) - -#define alloca0(n) \ - ({ \ - char *_new_; \ - size_t _len_ = n; \ - _new_ = alloca(_len_); \ - (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_; \ - size_t _mask_ = (align) - 1; \ - _ptr_ = alloca((size) + _mask_); \ - (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ - }) - -#define alloca0_align(size, align) \ - ({ \ - void *_new_; \ - size_t _size_ = (size); \ - _new_ = alloca_align(_size_, (align)); \ - (void*)memset(_new_, 0, _size_); \ - }) - -/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to - * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ -#define TAKE_PTR(ptr) \ - ({ \ - typeof(ptr) _ptr_ = (ptr); \ - (ptr) = NULL; \ - _ptr_; \ - }) diff --git a/src/systemd/src/basic/async.h b/src/systemd/src/basic/async.h deleted file mode 100644 index 31606131..00000000 --- a/src/systemd/src/basic/async.h +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -int asynchronous_job(void* (*func)(void *p), void *arg); - -int asynchronous_sync(pid_t *ret_pid); -int asynchronous_close(int fd); diff --git a/src/systemd/src/basic/env-util.c b/src/systemd/src/basic/env-util.c deleted file mode 100644 index 52d12a8a..00000000 --- a/src/systemd/src/basic/env-util.c +++ /dev/null @@ -1,791 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "env-util.h" -#include "escape.h" -#include "extract-word.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -#if 0 /* NM_IGNORED */ -#define VALID_CHARS_ENV_NAME \ - DIGITS LETTERS \ - "_" - -#ifndef ARG_MAX -#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX)) -#endif - -static bool env_name_is_valid_n(const char *e, size_t n) { - const char *p; - - if (!e) - return false; - - if (n <= 0) - return false; - - if (e[0] >= '0' && e[0] <= '9') - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the equal sign and trailing NUL this - * hence leaves ARG_MAX-2 as longest possible variable - * name. */ - if (n > ARG_MAX - 2) - return false; - - for (p = e; p < e + n; p++) - if (!strchr(VALID_CHARS_ENV_NAME, *p)) - return false; - - return true; -} - -bool env_name_is_valid(const char *e) { - if (!e) - return false; - - return env_name_is_valid_n(e, strlen(e)); -} - -bool env_value_is_valid(const char *e) { - if (!e) - return false; - - if (!utf8_is_valid(e)) - return false; - - /* bash allows tabs and newlines in environment variables, and so - * should we */ - if (string_has_cc(e, "\t\n")) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the shortest possible variable name of - * length 1, the equal sign and trailing NUL this hence leaves - * ARG_MAX-3 as longest possible variable value. */ - if (strlen(e) > ARG_MAX - 3) - return false; - - return true; -} - -bool env_assignment_is_valid(const char *e) { - const char *eq; - - eq = strchr(e, '='); - if (!eq) - return false; - - if (!env_name_is_valid_n(e, eq - e)) - return false; - - if (!env_value_is_valid(eq + 1)) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, hence the individual variable assignments - * cannot be either, but let's leave room for one trailing NUL - * byte. */ - if (strlen(e) > ARG_MAX - 1) - return false; - - return true; -} - -bool strv_env_is_valid(char **e) { - char **p, **q; - - STRV_FOREACH(p, e) { - size_t k; - - if (!env_assignment_is_valid(*p)) - return false; - - /* Check if there are duplicate assginments */ - k = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, k) && (*q)[k] == '=') - return false; - } - - return true; -} - -bool strv_env_name_is_valid(char **l) { - char **p, **q; - - STRV_FOREACH(p, l) { - if (!env_name_is_valid(*p)) - return false; - - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; - } - - return true; -} - -bool strv_env_name_or_assignment_is_valid(char **l) { - char **p, **q; - - STRV_FOREACH(p, l) { - if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) - return false; - - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; - } - - return true; -} - -static int env_append(char **r, char ***k, char **a) { - assert(r); - assert(k); - - if (!a) - return 0; - - /* Add the entries of a to *k unless they already exist in *r - * in which case they are overridden instead. This assumes - * there is enough space in the r array. */ - - for (; *a; a++) { - char **j; - size_t n; - - n = strcspn(*a, "="); - - if ((*a)[n] == '=') - n++; - - for (j = r; j < *k; j++) - if (strneq(*j, *a, n)) - break; - - if (j >= *k) - (*k)++; - else - free(*j); - - *j = strdup(*a); - if (!*j) - return -ENOMEM; - } - - return 0; -} - -char **strv_env_merge(size_t n_lists, ...) { - size_t n = 0; - char **l, **k, **r; - va_list ap; - size_t i; - - /* Merges an arbitrary number of environment sets */ - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - n += strv_length(l); - } - va_end(ap); - - r = new(char*, n+1); - if (!r) - return NULL; - - k = r; - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - if (env_append(r, &k, l) < 0) - goto fail; - } - va_end(ap); - - *k = NULL; - - return r; - -fail: - va_end(ap); - strv_free(r); - - return NULL; -} - -static bool env_match(const char *t, const char *pattern) { - assert(t); - assert(pattern); - - /* pattern a matches string a - * a matches a= - * a matches a=b - * a= matches a= - * a=b matches a=b - * a= does not match a - * a=b does not match a= - * a=b does not match a - * a=b does not match a=c */ - - if (streq(t, pattern)) - return true; - - if (!strchr(pattern, '=')) { - size_t l = strlen(pattern); - - return strneq(t, pattern, l) && t[l] == '='; - } - - return false; -} - -static bool env_entry_has_name(const char *entry, const char *name) { - const char *t; - - assert(entry); - assert(name); - - t = startswith(entry, name); - if (!t) - return false; - - return *t == '='; -} - -char **strv_env_delete(char **x, size_t n_lists, ...) { - size_t n, i = 0; - char **k, **r; - va_list ap; - - /* Deletes every entry from x that is mentioned in the other - * string lists */ - - n = strv_length(x); - - r = new(char*, n+1); - if (!r) - return NULL; - - STRV_FOREACH(k, x) { - size_t v; - - va_start(ap, n_lists); - for (v = 0; v < n_lists; v++) { - char **l, **j; - - l = va_arg(ap, char**); - STRV_FOREACH(j, l) - if (env_match(*k, *j)) - goto skip; - } - va_end(ap); - - r[i] = strdup(*k); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - continue; - - skip: - va_end(ap); - } - - r[i] = NULL; - - assert(i <= n); - - return r; -} - -char **strv_env_unset(char **l, const char *p) { - - char **f, **t; - - if (!l) - return NULL; - - assert(p); - - /* Drops every occurrence of the env var setting p in the - * string list. Edits in-place. */ - - for (f = t = l; *f; f++) { - - if (env_match(*f, p)) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -char **strv_env_unset_many(char **l, ...) { - - char **f, **t; - - if (!l) - return NULL; - - /* Like strv_env_unset() but applies many at once. Edits in-place. */ - - for (f = t = l; *f; f++) { - bool found = false; - const char *p; - va_list ap; - - va_start(ap, l); - - while ((p = va_arg(ap, const char*))) { - if (env_match(*f, p)) { - found = true; - break; - } - } - - va_end(ap); - - if (found) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -int strv_env_replace(char ***l, char *p) { - char **f; - const char *t, *name; - - assert(p); - - /* Replace first occurrence of the env var or add a new one in the - * string list. Drop other occurences. Edits in-place. Does not copy p. - * p must be a valid key=value assignment. - */ - - t = strchr(p, '='); - assert(t); - - name = strndupa(p, t - p); - - for (f = *l; f && *f; f++) - if (env_entry_has_name(*f, name)) { - free_and_replace(*f, p); - strv_env_unset(f + 1, *f); - return 0; - } - - /* We didn't find a match, we need to append p or create a new strv */ - if (strv_push(l, p) < 0) - return -ENOMEM; - return 1; -} - -char **strv_env_set(char **x, const char *p) { - - char **k; - _cleanup_strv_free_ char **r = NULL; - char* m[2] = { (char*) p, NULL }; - - /* Overrides the env var setting of p, returns a new copy */ - - r = new(char*, strv_length(x)+2); - if (!r) - return NULL; - - k = r; - if (env_append(r, &k, x) < 0) - return NULL; - - if (env_append(r, &k, m) < 0) - return NULL; - - *k = NULL; - - return TAKE_PTR(r); -} - -char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { - char **i; - - assert(name); - - if (k <= 0) - return NULL; - - STRV_FOREACH_BACKWARDS(i, l) - if (strneq(*i, name, k) && - (*i)[k] == '=') - return *i + k + 1; - - if (flags & REPLACE_ENV_USE_ENVIRONMENT) { - const char *t; - - t = strndupa(name, k); - return getenv(t); - }; - - return NULL; -} - -char *strv_env_get(char **l, const char *name) { - assert(name); - - return strv_env_get_n(l, name, strlen(name), 0); -} - -char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; - int k = 0; - - STRV_FOREACH(p, e) { - size_t n; - bool duplicate = false; - - if (!env_assignment_is_valid(*p)) { - if (invalid_callback) - invalid_callback(*p, userdata); - free(*p); - continue; - } - - n = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, n) && (*q)[n] == '=') { - duplicate = true; - break; - } - - if (duplicate) { - free(*p); - continue; - } - - e[k++] = *p; - } - - if (e) - e[k] = NULL; - - return e; -} - -char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { - enum { - WORD, - CURLY, - VARIABLE, - VARIABLE_RAW, - TEST, - DEFAULT_VALUE, - ALTERNATE_VALUE, - } state = WORD; - - const char *e, *word = format, *test_value; - char *k; - _cleanup_free_ char *r = NULL; - size_t i, len; - int nest = 0; - - assert(format); - - for (e = format, i = 0; *e && i < n; e ++, i ++) - switch (state) { - - case WORD: - if (*e == '$') - state = CURLY; - break; - - case CURLY: - if (*e == '{') { - k = strnappend(r, word, e-word-1); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e-1; - state = VARIABLE; - nest++; - } else if (*e == '$') { - k = strnappend(r, word, e-word); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - - } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) { - k = strnappend(r, word, e-word-1); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e-1; - state = VARIABLE_RAW; - - } else - state = WORD; - break; - - case VARIABLE: - if (*e == '}') { - const char *t; - - t = strv_env_get_n(env, word+2, e-word-2, flags); - - k = strappend(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - } else if (*e == ':') { - if (!(flags & REPLACE_ENV_ALLOW_EXTENDED)) - /* Treat this as unsupported syntax, i.e. do no replacement */ - state = WORD; - else { - len = e-word-2; - state = TEST; - } - } - break; - - case TEST: - if (*e == '-') - state = DEFAULT_VALUE; - else if (*e == '+') - state = ALTERNATE_VALUE; - else { - state = WORD; - break; - } - - test_value = e+1; - break; - - case DEFAULT_VALUE: /* fall through */ - case ALTERNATE_VALUE: - assert(flags & REPLACE_ENV_ALLOW_EXTENDED); - - if (*e == '{') { - nest++; - break; - } - - if (*e != '}') - break; - - nest--; - if (nest == 0) { - const char *t; - _cleanup_free_ char *v = NULL; - - t = strv_env_get_n(env, word+2, len, flags); - - if (t && state == ALTERNATE_VALUE) - t = v = replace_env_n(test_value, e-test_value, env, flags); - else if (!t && state == DEFAULT_VALUE) - t = v = replace_env_n(test_value, e-test_value, env, flags); - - k = strappend(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - } - break; - - case VARIABLE_RAW: - assert(flags & REPLACE_ENV_ALLOW_BRACELESS); - - if (!strchr(VALID_CHARS_ENV_NAME, *e)) { - const char *t; - - t = strv_env_get_n(env, word+1, e-word-1, flags); - - k = strappend(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e--; - i--; - state = WORD; - } - break; - } - - if (state == VARIABLE_RAW) { - const char *t; - - assert(flags & REPLACE_ENV_ALLOW_BRACELESS); - - t = strv_env_get_n(env, word+1, e-word-1, flags); - return strappend(r, t); - } else - return strnappend(r, word, e-word); -} - -char **replace_env_argv(char **argv, char **env) { - char **ret, **i; - size_t k = 0, l = 0; - - l = strv_length(argv); - - ret = new(char*, l+1); - if (!ret) - return NULL; - - STRV_FOREACH(i, argv) { - - /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) { - char *e; - char **w, **m = NULL; - size_t q; - - e = strv_env_get(env, *i+1); - if (e) { - int r; - - r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES); - if (r < 0) { - ret[k] = NULL; - strv_free(ret); - return NULL; - } - } else - m = NULL; - - q = strv_length(m); - l = l + q - 1; - - w = reallocarray(ret, l + 1, sizeof(char *)); - if (!w) { - ret[k] = NULL; - strv_free(ret); - strv_free(m); - return NULL; - } - - ret = w; - if (m) { - memcpy(ret + k, m, q * sizeof(char*)); - free(m); - } - - k += q; - continue; - } - - /* If ${FOO} appears as part of a word, replace it by the variable as-is */ - ret[k] = replace_env(*i, env, 0); - if (!ret[k]) { - strv_free(ret); - return NULL; - } - k++; - } - - ret[k] = NULL; - return ret; -} -#endif /* NM_IGNORED */ - -int getenv_bool(const char *p) { - const char *e; - - e = getenv(p); - if (!e) - return -ENXIO; - - return parse_boolean(e); -} - -#if 0 /* NM_IGNORED */ -int getenv_bool_secure(const char *p) { - const char *e; - - e = secure_getenv(p); - if (!e) - return -ENXIO; - - return parse_boolean(e); -} - -int serialize_environment(FILE *f, char **environment) { - char **e; - - STRV_FOREACH(e, environment) { - _cleanup_free_ char *ce; - - ce = cescape(*e); - if (!ce) - return -ENOMEM; - - fprintf(f, "env=%s\n", ce); - } - - /* caller should call ferror() */ - - return 0; -} - -int deserialize_environment(char ***environment, const char *line) { - char *uce; - int r; - - assert(line); - assert(environment); - - assert(startswith(line, "env=")); - r = cunescape(line + 4, 0, &uce); - if (r < 0) - return r; - - return strv_env_replace(environment, uce); -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/env-util.h b/src/systemd/src/basic/env-util.h deleted file mode 100644 index 174433ea..00000000 --- a/src/systemd/src/basic/env-util.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> - -#include "macro.h" -#include "string.h" - -bool env_name_is_valid(const char *e); -bool env_value_is_valid(const char *e); -bool env_assignment_is_valid(const char *e); - -enum { - REPLACE_ENV_USE_ENVIRONMENT = 1u, - REPLACE_ENV_ALLOW_BRACELESS = 2u, - REPLACE_ENV_ALLOW_EXTENDED = 4u, -}; - -char *replace_env_n(const char *format, size_t n, char **env, unsigned flags); -char **replace_env_argv(char **argv, char **env); - -static inline char *replace_env(const char *format, char **env, unsigned flags) { - return replace_env_n(format, strlen(format), env, flags); -} - -bool strv_env_is_valid(char **e); -#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) -char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); - -bool strv_env_name_is_valid(char **l); -bool strv_env_name_or_assignment_is_valid(char **l); - -char **strv_env_merge(size_t n_lists, ...); -char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ - -char **strv_env_set(char **x, const char *p); /* New copy ... */ -char **strv_env_unset(char **l, const char *p); /* In place ... */ -char **strv_env_unset_many(char **l, ...) _sentinel_; -int strv_env_replace(char ***l, char *p); /* In place ... */ - -char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_; -char *strv_env_get(char **x, const char *n) _pure_; - -int getenv_bool(const char *p); -int getenv_bool_secure(const char *p); - -int serialize_environment(FILE *f, char **environment); -int deserialize_environment(char ***environment, const char *line); diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c deleted file mode 100644 index 5c82a545..00000000 --- a/src/systemd/src/basic/escape.c +++ /dev/null @@ -1,509 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.h" - -int cescape_char(char c, char *buf) { - char *buf_old = buf; - - /* Needs space for 4 characters in the buffer */ - - 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; -} - -char *cescape_length(const char *s, size_t n) { - const char *f; - char *r, *t; - - assert(s || n == 0); - - /* Does C style string escaping. May be reversed with - * cunescape(). */ - - r = new(char, n*4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + n; f++) - t += cescape_char(*f, t); - - *t = 0; - - return r; -} - -char *cescape(const char *s) { - assert(s); - - return cescape_length(s, strlen(s)); -} - -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) { - int r = 1; - - assert(p); - assert(*p); - assert(ret); - - /* Unescapes C style. Returns the unescaped character in ret. - * Sets *eight_bit to true if the escaped sequence either fits in - * one byte in UTF-8 or is a non-unicode literal byte and should - * instead be copied directly. - */ - - if (length != (size_t) -1 && length < 1) - return -EINVAL; - - switch (p[0]) { - - case 'a': - *ret = '\a'; - break; - case 'b': - *ret = '\b'; - break; - case 'f': - *ret = '\f'; - break; - case 'n': - *ret = '\n'; - break; - case 'r': - *ret = '\r'; - break; - case 't': - *ret = '\t'; - break; - case 'v': - *ret = '\v'; - break; - case '\\': - *ret = '\\'; - break; - case '"': - *ret = '"'; - break; - case '\'': - *ret = '\''; - break; - - case 's': - /* This is an extension of the XDG syntax files */ - *ret = ' '; - break; - - case 'x': { - /* hexadecimal encoding */ - int a, b; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unhexchar(p[1]); - if (a < 0) - return -EINVAL; - - b = unhexchar(p[2]); - if (b < 0) - return -EINVAL; - - /* Don't allow NUL bytes */ - if (a == 0 && b == 0) - return -EINVAL; - - *ret = (a << 4U) | b; - *eight_bit = true; - r = 3; - break; - } - - case 'u': { - /* C++11 style 16bit unicode */ - - int a[4]; - size_t i; - uint32_t c; - - if (length != (size_t) -1 && length < 5) - return -EINVAL; - - for (i = 0; i < 4; i++) { - a[i] = unhexchar(p[1 + i]); - if (a[i] < 0) - return a[i]; - } - - c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3]; - - /* Don't allow 0 chars */ - if (c == 0) - return -EINVAL; - - *ret = c; - r = 5; - break; - } - - case 'U': { - /* C++11 style 32bit unicode */ - - int a[8]; - size_t i; - char32_t c; - - if (length != (size_t) -1 && length < 9) - return -EINVAL; - - for (i = 0; i < 8; i++) { - a[i] = unhexchar(p[1 + i]); - if (a[i] < 0) - return a[i]; - } - - c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) | - ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; - - /* Don't allow 0 chars */ - if (c == 0) - return -EINVAL; - - /* Don't allow invalid code points */ - if (!unichar_is_valid(c)) - return -EINVAL; - - *ret = c; - r = 9; - break; - } - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': { - /* octal encoding */ - int a, b, c; - char32_t m; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unoctchar(p[0]); - if (a < 0) - return -EINVAL; - - b = unoctchar(p[1]); - if (b < 0) - return -EINVAL; - - c = unoctchar(p[2]); - if (c < 0) - return -EINVAL; - - /* don't allow NUL bytes */ - if (a == 0 && b == 0 && c == 0) - return -EINVAL; - - /* Don't allow bytes above 255 */ - m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c; - if (m > 255) - return -EINVAL; - - *ret = m; - *eight_bit = true; - r = 3; - break; - } - - default: - return -EINVAL; - } - - return r; -} - -int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) { - char *r, *t; - const char *f; - size_t pl; - - assert(s); - assert(ret); - - /* Undoes C style string escaping, and optionally prefixes it. */ - - pl = strlen_ptr(prefix); - - r = new(char, pl+length+1); - if (!r) - return -ENOMEM; - - if (prefix) - memcpy(r, prefix, pl); - - for (f = s, t = r + pl; f < s + length; f++) { - size_t remaining; - bool eight_bit = false; - char32_t u; - int k; - - remaining = s + length - f; - assert(remaining > 0); - - if (*f != '\\') { - /* A literal, copy verbatim */ - *(t++) = *f; - continue; - } - - if (remaining == 1) { - if (flags & UNESCAPE_RELAX) { - /* A trailing backslash, copy verbatim */ - *(t++) = *f; - continue; - } - - free(r); - return -EINVAL; - } - - k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit); - if (k < 0) { - if (flags & UNESCAPE_RELAX) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - continue; - } - - free(r); - return k; - } - - f += k; - if (eight_bit) - /* One byte? Set directly as specified */ - *(t++) = u; - else - /* Otherwise encode as multi-byte UTF-8 */ - t += utf8_encode_unichar(t, u); - } - - *t = 0; - - *ret = r; - return t - r; -} - -int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) { - return cunescape_length_with_prefix(s, length, NULL, flags, ret); -} - -int cunescape(const char *s, UnescapeFlags flags, char **ret) { - return cunescape_length(s, strlen(s), flags, ret); -} - -char *xescape(const char *s, const char *bad) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and all special - * chars, in \xFF style escaping. May be reversed with - * cunescape(). */ - - r = new(char, strlen(s) * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; *f; f++) { - - if ((*f < ' ') || (*f >= 127) || - (*f == '\\') || strchr(bad, *f)) { - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(*f >> 4); - *(t++) = hexchar(*f); - } else - *(t++) = *f; - } - - *t = 0; - - return r; -} - -char *octescape(const char *s, size_t len) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and " chars, - * in \nnn style escaping. */ - - r = new(char, len * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + len; f++) { - - if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) { - *(t++) = '\\'; - *(t++) = '0' + (*f >> 6); - *(t++) = '0' + ((*f >> 3) & 8); - *(t++) = '0' + (*f & 8); - } else - *(t++) = *f; - } - - *t = 0; - - return r; - -} - -static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { - assert(bad); - - for (; *s; s++) { - if (escape_tab_nl && IN_SET(*s, '\n', '\t')) { - *(t++) = '\\'; - *(t++) = *s == '\n' ? 'n' : 't'; - continue; - } - - if (*s == '\\' || strchr(bad, *s)) - *(t++) = '\\'; - - *(t++) = *s; - } - - return t; -} - -char *shell_escape(const char *s, const char *bad) { - char *r, *t; - - r = new(char, strlen(s)*2+1); - if (!r) - return NULL; - - t = strcpy_backslash_escaped(r, s, bad, false); - *t = 0; - - return r; -} - -char* shell_maybe_quote(const char *s, EscapeStyle style) { - const char *p; - char *r, *t; - - assert(s); - - /* Encloses a string in quotes if necessary to make it OK as a shell - * string. Note that we treat benign UTF-8 characters as needing - * escaping too, but that should be OK. */ - - for (p = s; *p; p++) - if (*p <= ' ' || - *p >= 127 || - strchr(SHELL_NEED_QUOTES, *p)) - break; - - if (!*p) - return strdup(s); - - r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1); - if (!r) - return NULL; - - t = r; - if (style == ESCAPE_BACKSLASH) - *(t++) = '"'; - else if (style == ESCAPE_POSIX) { - *(t++) = '$'; - *(t++) = '\''; - } else - assert_not_reached("Bad EscapeStyle"); - - t = mempcpy(t, s, p - s); - - if (style == ESCAPE_BACKSLASH) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); - else - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - - if (style == ESCAPE_BACKSLASH) - *(t++) = '"'; - else - *(t++) = '\''; - *t = 0; - - return r; -} diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h deleted file mode 100644 index c612a7c0..00000000 --- a/src/systemd/src/basic/escape.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#if 0 /* NM_IGNORED */ -#include <uchar.h> -#endif /* NM_IGNORED */ - -#include "string-util.h" -#include "missing.h" - -/* What characters are special in the shell? */ -/* must be escaped outside and inside double-quotes */ -#define SHELL_NEED_ESCAPE "\"\\`$" - -/* Those that can be escaped or double-quoted. - * - * Stricly speaking, ! does not need to be escaped, except in interactive - * mode, but let's be extra nice to the user and quote ! in case this - * output is ever used in interactive mode. */ -#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;!" - -/* Note that we assume control characters would need to be escaped too in - * addition to the "special" characters listed here, if they appear in the - * string. Current users disallow control characters. Also '"' shall not - * be escaped. - */ -#define SHELL_NEED_ESCAPE_POSIX "\\\'" - -typedef enum UnescapeFlags { - UNESCAPE_RELAX = 1, -} UnescapeFlags; - -typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, - ESCAPE_POSIX = 2, -} EscapeStyle; - -char *cescape(const char *s); -char *cescape_length(const char *s, size_t n); -int cescape_char(char c, char *buf); - -int cunescape(const char *s, UnescapeFlags flags, char **ret); -int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret); -int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret); -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit); - -char *xescape(const char *s, const char *bad); -char *octescape(const char *s, size_t len); - -char *shell_escape(const char *s, const char *bad); -char* shell_maybe_quote(const char *s, EscapeStyle style); diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c deleted file mode 100644 index ed92bc60..00000000 --- a/src/systemd/src/basic/ether-addr-util.c +++ /dev/null @@ -1,119 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <net/ethernet.h> -#include <stdio.h> -#include <sys/types.h> - -#include "ether-addr-util.h" -#include "macro.h" -#include "string-util.h" - -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { - assert(addr); - assert(buffer); - - /* Like ether_ntoa() but uses %02x instead of %x to print - * ethernet addresses, which makes them look less funny. Also, - * doesn't use a static buffer. */ - - sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", - addr->ether_addr_octet[0], - addr->ether_addr_octet[1], - addr->ether_addr_octet[2], - addr->ether_addr_octet[3], - addr->ether_addr_octet[4], - addr->ether_addr_octet[5]); - - return buffer; -} - -int ether_addr_compare(const void *a, const void *b) { - assert(a); - assert(b); - - return memcmp(a, b, ETH_ALEN); -} - -static void ether_addr_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(struct ether_addr), state); -} - -const struct hash_ops ether_addr_hash_ops = { - .hash = ether_addr_hash_func, - .compare = ether_addr_compare -}; - -int ether_addr_from_string(const char *s, struct ether_addr *ret) { - size_t pos = 0, n, field; - char sep = '\0'; - const char *hex = HEXDIGITS, *hexoff; - size_t x; - bool touched; - -#define parse_fields(v) \ - for (field = 0; field < ELEMENTSOF(v); field++) { \ - touched = false; \ - for (n = 0; n < (2 * sizeof(v[0])); n++) { \ - if (s[pos] == '\0') \ - break; \ - hexoff = strchr(hex, s[pos]); \ - if (!hexoff) \ - break; \ - assert(hexoff >= hex); \ - x = hexoff - hex; \ - if (x >= 16) \ - x -= 6; /* A-F */ \ - assert(x < 16); \ - touched = true; \ - v[field] <<= 4; \ - v[field] += x; \ - pos++; \ - } \ - if (!touched) \ - return -EINVAL; \ - if (field < (ELEMENTSOF(v)-1)) { \ - if (s[pos] != sep) \ - return -EINVAL; \ - else \ - pos++; \ - } \ - } - - assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - sep = s[strspn(s, hex)]; - - if (sep == '.') { - uint16_t shorts[3] = { 0 }; - - parse_fields(shorts); - - if (s[pos] != '\0') - return -EINVAL; - - for (n = 0; n < ELEMENTSOF(shorts); n++) { - ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); - ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); - } - - } else if (IN_SET(sep, ':', '-')) { - struct ether_addr out = ETHER_ADDR_NULL; - - parse_fields(out.ether_addr_octet); - - if (s[pos] != '\0') - return -EINVAL; - - for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) - ret->ether_addr_octet[n] = out.ether_addr_octet[n]; - - } else - return -EINVAL; - - return 0; -} diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h deleted file mode 100644 index 3be03700..00000000 --- a/src/systemd/src/basic/ether-addr-util.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <net/ethernet.h> -#include <stdbool.h> - -#include "hash-funcs.h" - -#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" -#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] - -#define ETHER_ADDR_TO_STRING_MAX (3*6) -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); - -int ether_addr_compare(const void *a, const void *b); -static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { - return ether_addr_compare(a, b) == 0; -} - -#define ETHER_ADDR_NULL ((const struct ether_addr){}) - -static inline bool ether_addr_is_null(const struct ether_addr *addr) { - return ether_addr_equal(addr, ÐER_ADDR_NULL); -} - -int ether_addr_from_string(const char *s, struct ether_addr *ret); - -extern const struct hash_ops ether_addr_hash_ops; diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c deleted file mode 100644 index 404fe615..00000000 --- a/src/systemd/src/basic/extract-word.c +++ /dev/null @@ -1,289 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "utf8.h" - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { - _cleanup_free_ char *s = NULL; - size_t allocated = 0, sz = 0; - char c; - int r; - - char quote = 0; /* 0 or ' or " */ - bool backslash = false; /* whether we've just seen a backslash */ - - assert(p); - assert(ret); - - /* Bail early if called after last value or with no input */ - if (!*p) - goto finish; - c = **p; - - if (!separators) - separators = WHITESPACE; - - /* Parses the first word of a string, and returns it in - * *ret. Removes all quotes in the process. When parsing fails - * (because of an uneven number of quotes or similar), leaves - * the pointer *p at the first invalid character. */ - - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - } else { - /* We found a non-blank character, so we will always - * want to return a string (even if it is empty), - * allocate it here. */ - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - break; - } - } - - for (;; (*p)++, c = **p) { - if (backslash) { - if (!GREEDY_REALLOC(s, allocated, sz+7)) - return -ENOMEM; - - if (c == 0) { - if ((flags & EXTRACT_CUNESCAPE_RELAX) && - (!quote || flags & EXTRACT_RELAX)) { - /* If we find an unquoted trailing backslash and we're in - * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the - * output. - * - * Unbalanced quotes will only be allowed in EXTRACT_RELAX - * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. - */ - s[sz++] = '\\'; - goto finish_force_terminate; - } - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } - - if (flags & EXTRACT_CUNESCAPE) { - bool eight_bit = false; - char32_t u; - - r = cunescape_one(*p, (size_t) -1, &u, &eight_bit); - if (r < 0) { - if (flags & EXTRACT_CUNESCAPE_RELAX) { - s[sz++] = '\\'; - s[sz++] = c; - } else - return -EINVAL; - } else { - (*p) += r - 1; - - if (eight_bit) - s[sz++] = u; - else - sz += utf8_encode_unichar(s + sz, u); - } - } else - s[sz++] = c; - - backslash = false; - - } else if (quote) { /* inside either single or double quotes */ - for (;; (*p)++, c = **p) { - if (c == 0) { - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } else if (c == quote) { /* found the end quote */ - quote = 0; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - - } else { - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) { - quote = c; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - /* Skip additional coalesced separators. */ - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - if (!strchr(separators, c)) - break; - } - goto finish; - - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - } - } - -finish_force_terminate: - *p = NULL; -finish: - if (!s) { - *p = NULL; - *ret = NULL; - return 0; - } - -finish_force_next: - s[sz] = 0; - *ret = TAKE_PTR(s); - - return 1; -} - -#if 0 /* NM_IGNORED */ -int extract_first_word_and_warn( - const char **p, - char **ret, - const char *separators, - ExtractFlags flags, - const char *unit, - const char *filename, - unsigned line, - const char *rvalue) { - - /* Try to unquote it, if it fails, warn about it and try again - * but this time using EXTRACT_CUNESCAPE_RELAX to keep the - * backslashes verbatim in invalid escape sequences. */ - - const char *save; - int r; - - save = *p; - r = extract_first_word(p, ret, separators, flags); - if (r >= 0) - return r; - - if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) { - - /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ - *p = save; - r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); - if (r >= 0) { - /* It worked this time, hence it must have been an invalid escape sequence. */ - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret); - return r; - } - - /* If it's still EINVAL; then it must be unbalanced quoting, report this. */ - if (r == -EINVAL) - return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue); - } - - /* Can be any error, report it */ - return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue); -} - -/* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing - * an object that undergoes default argument promotion as an argument to va_start). - * Let's make sure that ExtractFlags fits into an unsigned int. */ -assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned)); - -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) { - va_list ap; - char **l; - int n = 0, i, c, r; - - /* Parses a number of words from a string, stripping any - * quotes if necessary. */ - - assert(p); - - /* Count how many words are expected */ - va_start(ap, flags); - for (;;) { - if (!va_arg(ap, char **)) - break; - n++; - } - va_end(ap); - - if (n <= 0) - return 0; - - /* Read all words into a temporary array */ - l = newa0(char*, n); - for (c = 0; c < n; c++) { - - r = extract_first_word(p, &l[c], separators, flags); - if (r < 0) { - int j; - - for (j = 0; j < c; j++) - free(l[j]); - - return r; - } - - if (r == 0) - break; - } - - /* If we managed to parse all words, return them in the passed - * in parameters */ - va_start(ap, flags); - for (i = 0; i < n; i++) { - char **v; - - v = va_arg(ap, char **); - assert(v); - - *v = l[i]; - } - va_end(ap); - - return c; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/extract-word.h b/src/systemd/src/basic/extract-word.h deleted file mode 100644 index 8c63b7c3..00000000 --- a/src/systemd/src/basic/extract-word.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include "macro.h" - -typedef enum ExtractFlags { - EXTRACT_RELAX = 1, - EXTRACT_CUNESCAPE = 2, - EXTRACT_CUNESCAPE_RELAX = 4, - EXTRACT_QUOTES = 8, - EXTRACT_DONT_COALESCE_SEPARATORS = 16, - EXTRACT_RETAIN_ESCAPE = 32, -} ExtractFlags; - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); -int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue); -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_; diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c deleted file mode 100644 index 71babe2f..00000000 --- a/src/systemd/src/basic/fd-util.c +++ /dev/null @@ -1,959 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "copy.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "macro.h" -#include "memfd-util.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "stdio-util.h" -#include "util.h" - -int close_nointr(int fd) { - assert(fd >= 0); - - if (close(fd) >= 0) - return 0; - - /* - * Just ignore EINTR; a retry loop is the wrong thing to do on - * Linux. - * - * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html - * https://bugzilla.gnome.org/show_bug.cgi?id=682819 - * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR - * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain - */ - if (errno == EINTR) - return 0; - - return -errno; -} - -int safe_close(int fd) { - - /* - * Like close_nointr() but cannot fail. Guarantees errno is - * unchanged. Is a NOP with negative fds passed, and returns - * -1, so that it can be used in this syntax: - * - * fd = safe_close(fd); - */ - - if (fd >= 0) { - PROTECT_ERRNO; - - /* The kernel might return pretty much any error code - * via close(), but the fd will be closed anyway. The - * only condition we want to check for here is whether - * the fd was invalid at all... */ - - assert_se(close_nointr(fd) != -EBADF); - } - - return -1; -} - -void safe_close_pair(int p[]) { - assert(p); - - if (p[0] == p[1]) { - /* Special case pairs which use the same fd in both - * directions... */ - p[0] = p[1] = safe_close(p[0]); - return; - } - - p[0] = safe_close(p[0]); - p[1] = safe_close(p[1]); -} - -void close_many(const int fds[], size_t n_fd) { - size_t i; - - assert(fds || n_fd <= 0); - - for (i = 0; i < n_fd; i++) - safe_close(fds[i]); -} - -int fclose_nointr(FILE *f) { - assert(f); - - /* Same as close_nointr(), but for fclose() */ - - if (fclose(f) == 0) - return 0; - - if (errno == EINTR) - return 0; - - return -errno; -} - -FILE* safe_fclose(FILE *f) { - - /* Same as safe_close(), but for fclose() */ - - if (f) { - PROTECT_ERRNO; - - assert_se(fclose_nointr(f) != EBADF); - } - - return NULL; -} - -DIR* safe_closedir(DIR *d) { - - if (d) { - PROTECT_ERRNO; - - assert_se(closedir(d) >= 0 || errno != EBADF); - } - - return NULL; -} - -int fd_nonblock(int fd, bool nonblock) { - int flags, nflags; - - assert(fd >= 0); - - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) - return -errno; - - if (nonblock) - nflags = flags | O_NONBLOCK; - else - nflags = flags & ~O_NONBLOCK; - - if (nflags == flags) - return 0; - - if (fcntl(fd, F_SETFL, nflags) < 0) - return -errno; - - return 0; -} - -int fd_cloexec(int fd, bool cloexec) { - int flags, nflags; - - assert(fd >= 0); - - flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) - return -errno; - - if (cloexec) - nflags = flags | FD_CLOEXEC; - else - nflags = flags & ~FD_CLOEXEC; - - if (nflags == flags) - return 0; - - if (fcntl(fd, F_SETFD, nflags) < 0) - return -errno; - - return 0; -} - -#if 0 /* NM_IGNORED */ -_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { - size_t i; - - assert(n_fdset == 0 || fdset); - - for (i = 0; i < n_fdset; i++) - if (fdset[i] == fd) - return true; - - return false; -} - -int close_all_fds(const int except[], size_t n_except) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(n_except == 0 || except); - - d = opendir("/proc/self/fd"); - if (!d) { - struct rlimit rl; - int fd, max_fd; - - /* When /proc isn't available (for example in chroots) the fallback is brute forcing through the fd - * table */ - - assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); - - if (rl.rlim_max == 0) - return -EINVAL; - - /* Let's take special care if the resource limit is set to unlimited, or actually larger than the range - * of 'int'. Let's avoid implicit overflows. */ - max_fd = (rl.rlim_max == RLIM_INFINITY || rl.rlim_max > INT_MAX) ? INT_MAX : (int) (rl.rlim_max - 1); - - for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { - int q; - - if (fd_in_set(fd, except, n_except)) - continue; - - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) - r = q; - } - - return r; - } - - FOREACH_DIRENT(de, d, return -errno) { - int fd = -1, q; - - if (safe_atoi(de->d_name, &fd) < 0) - /* Let's better ignore this, just in case */ - continue; - - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; - - if (fd_in_set(fd, except, n_except)) - continue; - - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ - r = q; - } - - return r; -} - -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_cached(); - 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 - * distinguish 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; -} -#endif /* NM_IGNORED */ - -void cmsg_close_all(struct msghdr *mh) { - struct cmsghdr *cmsg; - - assert(mh); - - CMSG_FOREACH(cmsg, mh) - 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)); -} - -bool fdname_is_valid(const char *s) { - const char *p; - - /* Validates a name for $LISTEN_FDNAMES. We basically allow - * everything ASCII that's not a control character. Also, as - * special exception the ":" character is not allowed, as we - * use that as field separator in $LISTEN_FDNAMES. - * - * Note that the empty string is explicitly allowed - * here. However, we limit the length of the names to 255 - * characters. */ - - if (!s) - return false; - - for (p = s; *p; p++) { - if (*p < ' ') - return false; - if (*p >= 127) - return false; - if (*p == ':') - return false; - } - - return p - s < 256; -} - -int fd_get_path(int fd, char **ret) { - _cleanup_close_ int dir = -1; - char fdname[DECIMAL_STR_MAX(int)]; - int r; - - dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH); - if (dir < 0) - /* /proc is not available or not set up properly, we're most likely - * in some chroot environment. */ - return errno == ENOENT ? -EOPNOTSUPP : -errno; - - xsprintf(fdname, "%i", fd); - - r = readlinkat_malloc(dir, fdname, ret); - if (r == -ENOENT) - /* If the file doesn't exist the fd is invalid */ - return -EBADF; - - return r; -} - -#if 0 /* NM_IGNORED */ -int move_fd(int from, int to, int cloexec) { - int r; - - /* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If - * 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned - * off, if it is > 0 it is turned on. */ - - if (from < 0) - return -EBADF; - if (to < 0) - return -EBADF; - - if (from == to) { - - if (cloexec >= 0) { - r = fd_cloexec(to, cloexec); - if (r < 0) - return r; - } - - return to; - } - - if (cloexec < 0) { - int fl; - - fl = fcntl(from, F_GETFD, 0); - if (fl < 0) - return -errno; - - cloexec = !!(fl & FD_CLOEXEC); - } - - r = dup3(from, to, cloexec ? O_CLOEXEC : 0); - if (r < 0) - return -errno; - - assert(r == to); - - safe_close(from); - - return to; -} - -int acquire_data_fd(const void *data, size_t size, unsigned flags) { - - _cleanup_close_pair_ int pipefds[2] = { -1, -1 }; - char pattern[] = "/dev/shm/data-fd-XXXXXX"; - _cleanup_close_ int fd = -1; - int isz = 0, r; - ssize_t n; - off_t f; - - assert(data || size == 0); - - /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more - * complex than I wish it was. But here's why: - * - * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them - * read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14. - * - * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining - * a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged - * clients can only bump their size to a system-wide limit, which might be quite low. - * - * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from - * earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via - * /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs. - * - * d) Finally, we try creating a regular file in /dev/shm, which we then delete. - * - * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I - * figure. */ - - if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) { - /* As a special case, return /dev/null if we have been called for an empty data block */ - r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (r < 0) - return -errno; - - return r; - } - - if ((flags & ACQUIRE_NO_MEMFD) == 0) { - fd = memfd_new("data-fd"); - if (fd < 0) - goto try_pipe; - - n = write(fd, data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - f = lseek(fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - r = memfd_set_sealed(fd); - if (r < 0) - return r; - - return TAKE_FD(fd); - } - -try_pipe: - if ((flags & ACQUIRE_NO_PIPE) == 0) { - if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) - return -errno; - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - if ((size_t) isz < size) { - isz = (int) size; - if (isz < 0 || (size_t) isz != size) - return -E2BIG; - - /* Try to bump the pipe size */ - (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz); - - /* See if that worked */ - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - if ((size_t) isz < size) - goto try_dev_shm; - } - - n = write(pipefds[1], data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - (void) fd_nonblock(pipefds[0], false); - - return TAKE_FD(pipefds[0]); - } - -try_dev_shm: - if ((flags & ACQUIRE_NO_TMPFILE) == 0) { - fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500); - if (fd < 0) - goto try_dev_shm_without_o_tmpfile; - - n = write(fd, data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ - return fd_reopen(fd, O_RDONLY|O_CLOEXEC); - } - -try_dev_shm_without_o_tmpfile: - if ((flags & ACQUIRE_NO_REGULAR) == 0) { - fd = mkostemp_safe(pattern); - if (fd < 0) - return fd; - - n = write(fd, data, size); - if (n < 0) { - r = -errno; - goto unlink_and_return; - } - if ((size_t) n != size) { - r = -EIO; - goto unlink_and_return; - } - - /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ - r = open(pattern, O_RDONLY|O_CLOEXEC); - if (r < 0) - r = -errno; - - unlink_and_return: - (void) unlink(pattern); - return r; - } - - return -EOPNOTSUPP; -} - -/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */ -#define DATA_FD_MEMORY_LIMIT (64U*1024U) - -/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */ -#define DATA_FD_TMP_LIMIT (1024U*1024U) - -int fd_duplicate_data_fd(int fd) { - - _cleanup_close_ int copy_fd = -1, tmp_fd = -1; - _cleanup_free_ void *remains = NULL; - size_t remains_size = 0; - const char *td; - struct stat st; - int r; - - /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but - * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be - * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported - * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in - * /var/tmp. */ - - if (fstat(fd, &st) < 0) - return -errno; - - /* For now, let's only accept regular files, sockets, pipes and char devices */ - if (S_ISDIR(st.st_mode)) - return -EISDIR; - if (S_ISLNK(st.st_mode)) - return -ELOOP; - if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode)) - return -EBADFD; - - /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note - * that we use the reported regular file size only as a hint, given that there are plenty special files in - * /proc and /sys which report a zero file size but can be read from. */ - - if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) { - - /* Try a memfd first */ - copy_fd = memfd_new("data-fd"); - if (copy_fd >= 0) { - off_t f; - - r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0); - if (r < 0) - return r; - - f = lseek(copy_fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - if (r == 0) { - /* Did it fit into the limit? If so, we are done. */ - r = memfd_set_sealed(copy_fd); - if (r < 0) - return r; - - return TAKE_FD(copy_fd); - } - - /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */ - - } else { - _cleanup_(close_pairp) int pipefds[2] = { -1, -1 }; - int isz; - - /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather - * then block indefinitely when we hit the pipe size limit */ - - if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) - return -errno; - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - /* Try to enlarge the pipe size if necessary */ - if ((size_t) isz < DATA_FD_MEMORY_LIMIT) { - - (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT); - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - } - - if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) { - - r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size); - if (r < 0 && r != -EAGAIN) - return r; /* If we get EAGAIN it could be because of the source or because of - * the destination fd, we can't know, as sendfile() and friends won't - * tell us. Hence, treat this as reason to fall back, just to be - * sure. */ - if (r == 0) { - /* Everything fit in, yay! */ - (void) fd_nonblock(pipefds[0], false); - - return TAKE_FD(pipefds[0]); - } - - /* Things didn't fit in. But we read data into the pipe, let's remember that, so that - * when writing the new file we incorporate this first. */ - copy_fd = TAKE_FD(pipefds[0]); - } - } - } - - /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */ - if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) && - (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) { - off_t f; - - tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC); - if (tmp_fd < 0) - return tmp_fd; - - if (copy_fd >= 0) { - /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the - * temporary file first. */ - - r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0); - if (r < 0) - return r; - - assert(r == 0); - } - - if (remains_size > 0) { - /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the - * failed copy operation, let's flush them out next. */ - - r = loop_write(tmp_fd, remains, remains_size, false); - if (r < 0) - return r; - } - - r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK); - if (r < 0) - return r; - if (r == 0) - goto finish; /* Yay, it fit in */ - - /* It didn't fit in. Let's not forget to use what we already used */ - f = lseek(tmp_fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - safe_close(copy_fd); - copy_fd = TAKE_FD(tmp_fd); - - remains = mfree(remains); - remains_size = 0; - } - - /* As last fallback use /var/tmp */ - r = var_tmp_dir(&td); - if (r < 0) - return r; - - tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC); - if (tmp_fd < 0) - return tmp_fd; - - if (copy_fd >= 0) { - /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this - * into the temporary file first. */ - r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK); - if (r < 0) - return r; - - assert(r == 0); - } - - if (remains_size > 0) { - /* Then, copy in any read but not yet written bytes. */ - r = loop_write(tmp_fd, remains, remains_size, false); - if (r < 0) - return r; - } - - /* Copy in the rest */ - r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK); - if (r < 0) - return r; - - assert(r == 0); - -finish: - /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the - * file again */ - - return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC); -} -#endif /* NM_IGNORED */ - -int fd_move_above_stdio(int fd) { - int flags, copy; - PROTECT_ERRNO; - - /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of - * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is - * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that - * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as - * stdin/stdout/stderr of unrelated code. - * - * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by - * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has - * been closed before. - * - * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an - * error we simply return the original file descriptor, and we do not touch errno. */ - - if (fd < 0 || fd > 2) - return fd; - - flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) - return fd; - - if (flags & FD_CLOEXEC) - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - else - copy = fcntl(fd, F_DUPFD, 3); - if (copy < 0) - return fd; - - assert(copy > 2); - - (void) close(fd); - return copy; -} - -#if 0 /* NM_IGNORED */ -int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) { - - int fd[3] = { /* Put together an array of fds we work on */ - original_input_fd, - original_output_fd, - original_error_fd - }; - - int r, i, - null_fd = -1, /* if we open /dev/null, we store the fd to it here */ - copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */ - bool null_readable, null_writable; - - /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is - * specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as - * itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be - * on. - * - * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on - * failure! Thus, callers should assume that when this function returns the input fds are invalidated. - * - * Note that when this function fails stdin/stdout/stderr might remain half set up! - * - * O_CLOEXEC is turned off for all three file descriptors (which is how it should be for - * stdin/stdout/stderr). */ - - null_readable = original_input_fd < 0; - null_writable = original_output_fd < 0 || original_error_fd < 0; - - /* First step, open /dev/null once, if we need it */ - if (null_readable || null_writable) { - - /* Let's open this with O_CLOEXEC first, and convert it to non-O_CLOEXEC when we move the fd to the final position. */ - null_fd = open("/dev/null", (null_readable && null_writable ? O_RDWR : - null_readable ? O_RDONLY : O_WRONLY) | O_CLOEXEC); - if (null_fd < 0) { - r = -errno; - goto finish; - } - - /* If this fd is in the 0…2 range, let's move it out of it */ - if (null_fd < 3) { - int copy; - - copy = fcntl(null_fd, F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ - if (copy < 0) { - r = -errno; - goto finish; - } - - safe_close(null_fd); - null_fd = copy; - } - } - - /* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */ - for (i = 0; i < 3; i++) { - - if (fd[i] < 0) - fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */ - else if (fd[i] != i && fd[i] < 3) { - /* This fd is in the 0…2 territory, but not at its intended place, move it out of there, so that we can work there. */ - copy_fd[i] = fcntl(fd[i], F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ - if (copy_fd[i] < 0) { - r = -errno; - goto finish; - } - - fd[i] = copy_fd[i]; - } - } - - /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we - * have freedom to move them around. If the fds already were at the right places then the specific fds are - * -1. Let's now move them to the right places. This is the point of no return. */ - for (i = 0; i < 3; i++) { - - if (fd[i] == i) { - - /* fd is already in place, but let's make sure O_CLOEXEC is off */ - r = fd_cloexec(i, false); - if (r < 0) - goto finish; - - } else { - assert(fd[i] > 2); - - if (dup2(fd[i], i) < 0) { /* Turns off O_CLOEXEC on the new fd. */ - r = -errno; - goto finish; - } - } - } - - r = 0; - -finish: - /* Close the original fds, but only if they were outside of the stdio range. Also, properly check for the same - * fd passed in multiple times. */ - safe_close_above_stdio(original_input_fd); - if (original_output_fd != original_input_fd) - safe_close_above_stdio(original_output_fd); - if (original_error_fd != original_input_fd && original_error_fd != original_output_fd) - safe_close_above_stdio(original_error_fd); - - /* Close the copies we moved > 2 */ - for (i = 0; i < 3; i++) - safe_close(copy_fd[i]); - - /* Close our null fd, if it's > 2 */ - safe_close_above_stdio(null_fd); - - return r; -} - -int fd_reopen(int fd, int flags) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - int new_fd; - - /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to - * turn O_RDWR fds into O_RDONLY fds. - * - * This doesn't work on sockets (since they cannot be open()ed, ever). - * - * This implicitly resets the file read index to 0. */ - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - new_fd = open(procfs_path, flags); - if (new_fd < 0) - return -errno; - - return new_fd; -} - -int read_nr_open(void) { - _cleanup_free_ char *nr_open = NULL; - int r; - - /* Returns the kernel's current fd limit, either by reading it of /proc/sys if that works, or using the - * hard-coded default compiled-in value of current kernels (1M) if not. This call will never fail. */ - - r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); - if (r < 0) - log_debug_errno(r, "Failed to read /proc/sys/fs/nr_open, ignoring: %m"); - else { - int v; - - r = safe_atoi(nr_open, &v); - if (r < 0) - log_debug_errno(r, "Failed to parse /proc/sys/fs/nr_open value '%s', ignoring: %m", nr_open); - else - return v; - } - - /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ - return 1024 * 1024; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h deleted file mode 100644 index 00303a7e..00000000 --- a/src/systemd/src/basic/fd-util.h +++ /dev/null @@ -1,110 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <dirent.h> -#include <stdbool.h> -#include <stdio.h> -#include <sys/socket.h> - -#include "macro.h" - -/* Make sure we can distinguish fd 0 and NULL */ -#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) -#define PTR_TO_FD(p) (PTR_TO_INT(p)-1) - -int close_nointr(int fd); -int safe_close(int fd); -void safe_close_pair(int p[]); - -static inline int safe_close_above_stdio(int fd) { - if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */ - return -1; - - return safe_close(fd); -} - -void close_many(const int fds[], size_t n_fd); - -int fclose_nointr(FILE *f); -FILE* safe_fclose(FILE *f); -DIR* safe_closedir(DIR *f); - -static inline void closep(int *fd) { - safe_close(*fd); -} - -static inline void close_pairp(int (*p)[2]) { - safe_close_pair(*p); -} - -static inline void fclosep(FILE **f) { - safe_fclose(*f); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose); -DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); - -#define _cleanup_close_ _cleanup_(closep) -#define _cleanup_fclose_ _cleanup_(fclosep) -#define _cleanup_pclose_ _cleanup_(pclosep) -#define _cleanup_closedir_ _cleanup_(closedirp) -#define _cleanup_close_pair_ _cleanup_(close_pairp) - -int fd_nonblock(int fd, bool nonblock); -int fd_cloexec(int fd, bool cloexec); - -int close_all_fds(const int except[], size_t n_except); - -int same_fd(int a, int b); - -void cmsg_close_all(struct msghdr *mh); - -bool fdname_is_valid(const char *s); - -int fd_get_path(int fd, char **ret); - -int move_fd(int from, int to, int cloexec); - -enum { - ACQUIRE_NO_DEV_NULL = 1 << 0, - ACQUIRE_NO_MEMFD = 1 << 1, - ACQUIRE_NO_PIPE = 1 << 2, - ACQUIRE_NO_TMPFILE = 1 << 3, - ACQUIRE_NO_REGULAR = 1 << 4, -}; - -int acquire_data_fd(const void *data, size_t size, unsigned flags); - -int fd_duplicate_data_fd(int fd); - -/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ -/* The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. - * See the icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */ -#define ERRNO_IS_DISCONNECT(r) \ - IN_SET(r, \ - ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \ - ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, ENONET) - -/* Resource exhaustion, could be our fault or general system trouble */ -#define ERRNO_IS_RESOURCE(r) \ - IN_SET(r, ENOMEM, EMFILE, ENFILE) - -int fd_move_above_stdio(int fd); - -int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd); - -static inline int make_null_stdio(void) { - return rearrange_stdio(-1, -1, -1); -} - -/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ -#define TAKE_FD(fd) \ - ({ \ - int _fd_ = (fd); \ - (fd) = -1; \ - _fd_; \ - }) - -int fd_reopen(int fd, int flags); - -int read_nr_open(void); diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c deleted file mode 100644 index 3978c3a7..00000000 --- a/src/systemd/src/basic/fileio.c +++ /dev/null @@ -1,1671 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "ctype.h" -#include "def.h" -#include "env-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hexdecoct.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "random-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "utf8.h" - -#define READ_FULL_BYTES_MAX (4U*1024U*1024U) - -#if 0 /* NM_IGNORED */ -int write_string_stream_ts( - FILE *f, - const char *line, - WriteStringFileFlags flags, - struct timespec *ts) { - - bool needs_nl; - int r; - - assert(f); - assert(line); - - if (ferror(f)) - return -EIO; - - needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); - - if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { - /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so - * that the write goes out in one go, instead of two */ - - line = strjoina(line, "\n"); - needs_nl = false; - } - - if (fputs(line, f) == EOF) - return -errno; - - if (needs_nl) - if (fputc('\n', f) == EOF) - return -errno; - - if (flags & WRITE_STRING_FILE_SYNC) - r = fflush_sync_and_check(f); - else - r = fflush_and_check(f); - if (r < 0) - return r; - - if (ts) { - struct timespec twice[2] = {*ts, *ts}; - - if (futimens(fileno(f), twice) < 0) - return -errno; - } - - return 0; -} - -static int write_string_file_atomic( - const char *fn, - const char *line, - WriteStringFileFlags flags, - struct timespec *ts) { - - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - int r; - - assert(fn); - assert(line); - - r = fopen_temporary(fn, &f, &p); - if (r < 0) - return r; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - (void) fchmod_umask(fileno(f), 0644); - - r = write_string_stream_ts(f, line, flags, ts); - if (r < 0) - goto fail; - - if (rename(p, fn) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(p); - return r; -} - -int write_string_file_ts( - const char *fn, - const char *line, - WriteStringFileFlags flags, - struct timespec *ts) { - - _cleanup_fclose_ FILE *f = NULL; - int q, r; - - assert(fn); - assert(line); - - /* We don't know how to verify whether the file contents was already on-disk. */ - assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC))); - - if (flags & WRITE_STRING_FILE_ATOMIC) { - assert(flags & WRITE_STRING_FILE_CREATE); - - r = write_string_file_atomic(fn, line, flags, ts); - if (r < 0) - goto fail; - - return r; - } else - assert(!ts); - - if (flags & WRITE_STRING_FILE_CREATE) { - f = fopen(fn, "we"); - if (!f) { - r = -errno; - goto fail; - } - } else { - int fd; - - /* We manually build our own version of fopen(..., "we") that - * works without O_CREAT */ - fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - r = -errno; - goto fail; - } - - f = fdopen(fd, "we"); - if (!f) { - r = -errno; - safe_close(fd); - goto fail; - } - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) - setvbuf(f, NULL, _IONBF, 0); - - r = write_string_stream_ts(f, line, flags, ts); - if (r < 0) - goto fail; - - return 0; - -fail: - if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE)) - return r; - - f = safe_fclose(f); - - /* OK, the operation failed, but let's see if the right - * contents in place already. If so, eat up the error. */ - - q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); - if (q <= 0) - return r; - - return 0; -} - -int write_string_filef( - const char *fn, - WriteStringFileFlags flags, - const char *format, ...) { - - _cleanup_free_ char *p = NULL; - va_list ap; - int r; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return write_string_file(fn, p, flags); -} - -int read_one_line_file(const char *fn, char **line) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(fn); - assert(line); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - r = read_line(f, LONG_LINE_MAX, line); - return r < 0 ? r : 0; -} - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *buf = NULL; - size_t l, k; - - assert(fn); - assert(blob); - - l = strlen(blob); - - if (accept_extra_nl && endswith(blob, "\n")) - accept_extra_nl = false; - - buf = malloc(l + accept_extra_nl + 1); - if (!buf) - return -ENOMEM; - - f = fopen(fn, "re"); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - /* We try to read one byte more than we need, so that we know whether we hit eof */ - errno = 0; - k = fread(buf, 1, l + accept_extra_nl + 1, f); - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - if (k != l && k != l + accept_extra_nl) - return 0; - if (memcmp(buf, blob, l) != 0) - return 0; - if (k > l && buf[l] != '\n') - return 0; - - return 1; -} -#endif /* NM_IGNORED */ - -int read_full_stream(FILE *f, char **contents, size_t *size) { - _cleanup_free_ char *buf = NULL; - struct stat st; - size_t n, l; - int fd; - - assert(f); - assert(contents); - - n = LINE_MAX; - - fd = fileno(f); - if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's - * optimize our buffering) */ - - if (fstat(fileno(f), &st) < 0) - return -errno; - - if (S_ISREG(st.st_mode)) { - - /* Safety check */ - if (st.st_size > READ_FULL_BYTES_MAX) - return -E2BIG; - - /* Start with the right file size, but be prepared for files from /proc which generally report a file - * size of 0. Note that we increase the size to read here by one, so that the first read attempt - * already makes us notice the EOF. */ - if (st.st_size > 0) - n = st.st_size + 1; - } - } - - l = 0; - for (;;) { - char *t; - size_t k; - - t = realloc(buf, n + 1); - if (!t) - return -ENOMEM; - - buf = t; - errno = 0; - k = fread(buf + l, 1, n - l, f); - if (k > 0) - l += k; - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - if (feof(f)) - break; - - /* We aren't expecting fread() to return a short read outside - * of (error && eof), assert buffer is full and enlarge buffer. - */ - assert(l == n); - - /* Safety check */ - if (n >= READ_FULL_BYTES_MAX) - return -E2BIG; - - n = MIN(n * 2, READ_FULL_BYTES_MAX); - } - - buf[l] = 0; - *contents = TAKE_PTR(buf); - - if (size) - *size = l; - - return 0; -} - -int read_full_file(const char *fn, char **contents, size_t *size) { - _cleanup_fclose_ FILE *f = NULL; - - assert(fn); - assert(contents); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - return read_full_stream(f, contents, size); -} - -static int parse_env_file_internal( - FILE *f, - const char *fname, - const char *newline, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { - - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; - _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; - unsigned line = 1; - char *p; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE, - COMMENT, - COMMENT_ESCAPE - } state = PRE_KEY; - - assert(newline); - - if (f) - r = read_full_stream(f, &contents, NULL); - else - r = read_full_file(fname, &contents, NULL); - if (r < 0) - return r; - - for (p = contents; *p; p++) { - char c = *p; - - switch (state) { - - case PRE_KEY: - if (strchr(COMMENTS, c)) - state = COMMENT; - else if (!strchr(WHITESPACE, c)) { - state = KEY; - last_key_whitespace = (size_t) -1; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - break; - - case KEY: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - n_key = 0; - } else if (c == '=') { - state = PRE_VALUE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) - last_key_whitespace = n_key; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - - break; - - case PRE_VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\'') - state = SINGLE_QUOTE_VALUE; - else if (c == '\"') - state = DOUBLE_QUOTE_VALUE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (!strchr(WHITESPACE, c)) { - state = VALUE; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\\') { - state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) - last_value_whitespace = n_value; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE_ESCAPE: - state = VALUE; - - if (!strchr(newline, c)) { - /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case SINGLE_QUOTE_VALUE: - if (c == '\'') - state = PRE_VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case SINGLE_QUOTE_VALUE_ESCAPE: - state = SINGLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case DOUBLE_QUOTE_VALUE: - if (c == '\"') - state = PRE_VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE_ESCAPE: - state = DOUBLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case COMMENT: - if (c == '\\') - state = COMMENT_ESCAPE; - else if (strchr(newline, c)) { - state = PRE_KEY; - line++; - } - break; - - case COMMENT_ESCAPE: - state = COMMENT; - break; - } - } - - if (IN_SET(state, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE)) { - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - if (state == VALUE) - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - value = NULL; - } - - return 0; -} - -static int check_utf8ness_and_warn( - const char *filename, unsigned line, - const char *key, char *value) { - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = NULL; - - 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 = NULL; - - 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; - } - - return 0; -} - -static int parse_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - va_copy(aq, *ap); - - while ((k = va_arg(aq, const char *))) { - char **v; - - v = va_arg(aq, char **); - - if (streq(key, k)) { - va_end(aq); - free(*v); - *v = value; - - if (n_pushed) - (*n_pushed)++; - - return 1; - } - } - - va_end(aq); - free(value); - - return 0; -} - -int parse_env_filev( - FILE *f, - const char *fname, - const char *newline, - va_list ap) { - - int r, n_pushed = 0; - va_list aq; - - if (!newline) - newline = NEWLINE; - - va_copy(aq, ap); - r = parse_env_file_internal(f, fname, newline, parse_env_file_push, &aq, &n_pushed); - va_end(aq); - if (r < 0) - return r; - - return n_pushed; -} - -int parse_env_file( - FILE *f, - const char *fname, - const char *newline, - ...) { - - va_list ap; - int r; - - va_start(ap, newline); - r = parse_env_filev(f, fname, newline, ap); - va_end(ap); - - return r; -} - -#if 0 /* NM_IGNORED */ -static int load_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - char *p; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - p = strjoin(key, "=", value); - if (!p) - return -ENOMEM; - - r = strv_env_replace(m, p); - if (r < 0) { - free(p); - return r; - } - - if (n_pushed) - (*n_pushed)++; - - free(value); - return 0; -} - -int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int load_env_file_push_pairs( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - r = strv_extend(m, key); - if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; -} - -int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int merge_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - char ***env = userdata; - char *expanded_value; - - assert(env); - - if (!value) { - log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); - return 0; - } - - if (!env_name_is_valid(key)) { - log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); - free(value); - return 0; - } - - expanded_value = replace_env(value, *env, - REPLACE_ENV_USE_ENVIRONMENT| - REPLACE_ENV_ALLOW_BRACELESS| - REPLACE_ENV_ALLOW_EXTENDED); - if (!expanded_value) - return -ENOMEM; - - free_and_replace(value, expanded_value); - - return load_env_file_push(filename, line, key, value, env, n_pushed); -} - -int merge_env_file( - char ***env, - FILE *f, - const char *fname) { - - /* NOTE: this function supports braceful and braceless variable expansions, - * plus "extended" substitutions, unlike other exported parsing functions. - */ - - return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL); -} - -static void write_env_var(FILE *f, const char *v) { - const char *p; - - p = strchr(v, '='); - if (!p) { - /* Fallback */ - fputs_unlocked(v, f); - fputc_unlocked('\n', f); - return; - } - - p++; - fwrite_unlocked(v, 1, p-v, f); - - if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc_unlocked('\"', f); - - for (; *p; p++) { - if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc_unlocked('\\', f); - - fputc_unlocked(*p, f); - } - - fputc_unlocked('\"', f); - } else - fputs_unlocked(p, f); - - fputc_unlocked('\n', f); -} - -int write_env_file(const char *fname, char **l) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - char **i; - int r; - - assert(fname); - - r = fopen_temporary(fname, &f, &p); - if (r < 0) - return r; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - (void) fchmod_umask(fileno(f), 0644); - - STRV_FOREACH(i, l) - write_env_var(f, *i); - - r = fflush_and_check(f); - if (r >= 0) { - if (rename(p, fname) >= 0) - return 0; - - r = -errno; - } - - unlink(p); - return r; -} - -int executable_is_script(const char *path, char **interpreter) { - _cleanup_free_ char *line = NULL; - size_t len; - char *ans; - int r; - - assert(path); - - r = read_one_line_file(path, &line); - if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */ - return 0; - if (r < 0) - return r; - - if (!startswith(line, "#!")) - return 0; - - ans = strstrip(line + 2); - len = strcspn(ans, " \t"); - - if (len == 0) - return 0; - - ans = strndup(ans, len); - if (!ans) - return -ENOMEM; - - *interpreter = ans; - return 1; -} -#endif /* NM_IGNORED */ - -/** - * Retrieve one field from a file like /proc/self/status. pattern - * should not include whitespace or the delimiter (':'). pattern matches only - * the beginning of a line. Whitespace before ':' is skipped. Whitespace and - * zeros after the ':' will be skipped. field must be freed afterwards. - * terminator specifies the terminating characters of the field value (not - * included in the value). - */ -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) { - _cleanup_free_ char *status = NULL; - char *t, *f; - size_t len; - int r; - - assert(terminator); - assert(filename); - assert(pattern); - assert(field); - - r = read_full_file(filename, &status, NULL); - if (r < 0) - return r; - - t = status; - - do { - bool pattern_ok; - - do { - t = strstr(t, pattern); - if (!t) - return -ENOENT; - - /* Check that pattern occurs in beginning of line. */ - pattern_ok = (t == status || t[-1] == '\n'); - - t += strlen(pattern); - - } while (!pattern_ok); - - t += strspn(t, " \t"); - if (!*t) - return -ENOENT; - - } while (*t != ':'); - - t++; - - if (*t) { - t += strspn(t, " \t"); - - /* Also skip zeros, because when this is used for - * capabilities, we don't want the zeros. This way the - * same capability set always maps to the same string, - * irrespective of the total capability set size. For - * other numbers it shouldn't matter. */ - t += strspn(t, "0"); - /* Back off one char if there's nothing but whitespace - and zeros */ - if (!*t || isspace(*t)) - t--; - } - - len = strcspn(t, terminator); - - f = strndup(t, len); - if (!f) - return -ENOMEM; - - *field = f; - return 0; -} - -DIR *xopendirat(int fd, const char *name, int flags) { - int nfd; - DIR *d; - - assert(!(flags & O_CREAT)); - - nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); - if (nfd < 0) - return NULL; - - d = fdopendir(nfd); - if (!d) { - safe_close(nfd); - return NULL; - } - - return d; -} - -#if 0 /* NM_IGNORED */ -static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { - char **i; - - assert(path); - assert(mode); - assert(_f); - - if (!path_strv_resolve_uniq(search, root)) - return -ENOMEM; - - STRV_FOREACH(i, search) { - _cleanup_free_ char *p = NULL; - FILE *f; - - if (root) - p = strjoin(root, *i, "/", path); - else - p = strjoin(*i, "/", path); - if (!p) - return -ENOMEM; - - f = fopen(p, mode); - if (f) { - *_f = f; - return 0; - } - - if (errno != ENOENT) - return -errno; - } - - return -ENOENT; -} - -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) { - _cleanup_strv_free_ char **copy = NULL; - - assert(path); - assert(mode); - assert(_f); - - if (path_is_absolute(path)) { - FILE *f; - - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; - } - - return -errno; - } - - copy = strv_copy((char**) search); - if (!copy) - return -ENOMEM; - - return search_and_fopen_internal(path, mode, root, copy, _f); -} - -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) { - _cleanup_strv_free_ char **s = NULL; - - if (path_is_absolute(path)) { - FILE *f; - - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; - } - - return -errno; - } - - s = strv_split_nulstr(search); - if (!s) - return -ENOMEM; - - return search_and_fopen_internal(path, mode, root, s, _f); -} -#endif /* NM_IGNORED */ - -int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { - FILE *f; - char *t; - int r, fd; - - assert(path); - assert(_f); - assert(_temp_path); - - r = tempfn_xxxxxx(path, NULL, &t); - if (r < 0) - return r; - - fd = mkostemp_safe(t); - if (fd < 0) { - free(t); - return -errno; - } - - f = fdopen(fd, "we"); - if (!f) { - unlink_noerrno(t); - free(t); - safe_close(fd); - return -errno; - } - - *_f = f; - *_temp_path = t; - - return 0; -} - -int fflush_and_check(FILE *f) { - assert(f); - - errno = 0; - fflush(f); - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - return 0; -} - -#if 0 /* NM_IGNORED */ -int fflush_sync_and_check(FILE *f) { - int r; - - assert(f); - - r = fflush_and_check(f); - if (r < 0) - return r; - - if (fsync(fileno(f)) < 0) - return -errno; - - r = fsync_directory_of_file(fileno(f)); - if (r < 0) - return r; - - return 0; -} -#endif /* NM_IGNORED */ - -/* This is much like mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern) { - _cleanup_umask_ mode_t u = 0; - int fd; - - assert(pattern); - - u = umask(077); - - fd = mkostemp(pattern, O_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; -} - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - const char *fn; - char *t; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldoXXXXXX - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); - if (!t) - return -ENOMEM; - - strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); - - *ret = path_simplify(t, false); - return 0; -} - -#if 0 /* NM_IGNORED */ -int tempfn_random(const char *p, const char *extra, char **ret) { - const char *fn; - char *t, *x; - uint64_t u; - unsigned i; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldobaa2a261115984a9 - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} - -int tempfn_random_child(const char *p, const char *extra, char **ret) { - char *t, *x; - uint64_t u; - unsigned i; - int r; - - assert(ret); - - /* Turns this: - * /foo/bar/waldo - * Into this: - * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 - */ - - if (!p) { - r = tmp_dir(&p); - if (r < 0) - return r; - } - - extra = strempty(extra); - - t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - if (isempty(p)) - x = stpcpy(stpcpy(t, ".#"), extra); - else - x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} - -int write_timestamp_file_atomic(const char *fn, usec_t n) { - char ln[DECIMAL_STR_MAX(n)+2]; - - /* Creates a "timestamp" file, that contains nothing but a - * usec_t timestamp, formatted in ASCII. */ - - if (n <= 0 || n >= USEC_INFINITY) - return -ERANGE; - - xsprintf(ln, USEC_FMT "\n", n); - - return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); -} - -int read_timestamp_file(const char *fn, usec_t *ret) { - _cleanup_free_ char *ln = NULL; - uint64_t t; - int r; - - r = read_one_line_file(fn, &ln); - if (r < 0) - return r; - - r = safe_atou64(ln, &t); - if (r < 0) - return r; - - if (t <= 0 || t >= (uint64_t) USEC_INFINITY) - return -ERANGE; - - *ret = (usec_t) t; - return 0; -} -#endif /* NM_IGNORED */ - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) { - int r; - - assert(s); - - /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter - * when specified shall initially point to a boolean variable initialized to false. It is set to true after the - * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each - * element, but not before the first one. */ - - if (!f) - f = stdout; - - if (space) { - if (!separator) - separator = " "; - - if (*space) { - r = fputs(separator, f); - if (r < 0) - return r; - } - - *space = true; - } - - return fputs(s, f); -} - -#if 0 /* NM_IGNORED */ -int open_tmpfile_unlinkable(const char *directory, int flags) { - char *p; - int fd, r; - - if (!directory) { - r = tmp_dir(&directory); - if (r < 0) - return r; - } else if (isempty(directory)) - return -EINVAL; - - /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ - - /* Try O_TMPFILE first, if it is supported */ - fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; - - /* Fall back to unguessable name + unlinking */ - p = strjoina(directory, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p); - if (fd < 0) - return fd; - - (void) unlink(p); - - return fd; -} - -int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { - _cleanup_free_ char *tmp = NULL; - int r, fd; - - assert(target); - assert(ret_path); - - /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ - assert((flags & O_EXCL) == 0); - - /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in - * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in - * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ - - fd = open_parent(target, O_TMPFILE|flags, 0640); - if (fd >= 0) { - *ret_path = NULL; - return fd; - } - - log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target); - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; - - fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); - if (fd < 0) - return -errno; - - *ret_path = TAKE_PTR(tmp); - - return fd; -} - -int open_serialization_fd(const char *ident) { - int fd = -1; - - fd = memfd_create(ident, MFD_CLOEXEC); - if (fd < 0) { - const char *path; - - path = getpid_cached() == 1 ? "/run/systemd" : "/tmp"; - fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); - if (fd < 0) - return fd; - - log_debug("Serializing %s to %s.", ident, path); - } else - log_debug("Serializing %s to memfd.", ident); - - return fd; -} - -int link_tmpfile(int fd, const char *path, const char *target) { - - assert(fd >= 0); - assert(target); - - /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd - * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported - * on the directory, and renameat2() is used instead. - * - * Note that in both cases we will not replace existing files. This is because linkat() does not support this - * operation currently (renameat2() does), and there is no nice way to emulate this. */ - - if (path) { - if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) - return -errno; - } else { - char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return -errno; - } - - return 0; -} - -int read_nul_string(FILE *f, char **ret) { - _cleanup_free_ char *x = NULL; - size_t allocated = 0, n = 0; - - assert(f); - assert(ret); - - /* Reads a NUL-terminated string from the specified file. */ - - for (;;) { - int c; - - if (!GREEDY_REALLOC(x, allocated, n+2)) - return -ENOMEM; - - c = fgetc(f); - if (c == 0) /* Terminate at NUL byte */ - break; - if (c == EOF) { - if (ferror(f)) - return -errno; - break; /* Terminate at EOF */ - } - - x[n++] = (char) c; - } - - if (x) - x[n] = 0; - else { - x = new0(char, 1); - if (!x) - return -ENOMEM; - } - - *ret = TAKE_PTR(x); - - return 0; -} - -int mkdtemp_malloc(const char *template, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - assert(ret); - - if (template) - p = strdup(template); - else { - const char *tmp; - - r = tmp_dir(&tmp); - if (r < 0) - return r; - - p = strjoin(tmp, "/XXXXXX"); - } - if (!p) - return -ENOMEM; - - if (!mkdtemp(p)) - return -errno; - - *ret = TAKE_PTR(p); - return 0; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); - -int read_line(FILE *f, size_t limit, char **ret) { - _cleanup_free_ char *buffer = NULL; - size_t n = 0, allocated = 0, count = 0; - - assert(f); - - /* Something like a bounded version of getline(). - * - * Considers EOF, \n and \0 end of line delimiters, and does not include these delimiters in the string - * returned. - * - * Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from - * the number of characters in the returned string). When EOF is hit, 0 is returned. - * - * The input parameter limit is the maximum numbers of characters in the returned string, i.e. excluding - * delimiters. If the limit is hit we fail and return -ENOBUFS. - * - * If a line shall be skipped ret may be initialized as NULL. */ - - if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, 1)) - return -ENOMEM; - } - - { - _unused_ _cleanup_(funlockfilep) FILE *flocked = f; - flockfile(f); - - for (;;) { - int c; - - if (n >= limit) - return -ENOBUFS; - - errno = 0; - c = fgetc_unlocked(f); - if (c == EOF) { - /* if we read an error, and have no data to return, then propagate the error */ - if (ferror_unlocked(f) && n == 0) - return errno > 0 ? -errno : -EIO; - - break; - } - - count++; - - if (IN_SET(c, '\n', 0)) /* Reached a delimiter */ - break; - - if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, n + 2)) - return -ENOMEM; - - buffer[n] = (char) c; - } - - n++; - } - } - - if (ret) { - buffer[n] = 0; - - *ret = TAKE_PTR(buffer); - } - - return (int) count; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h deleted file mode 100644 index 77e6206e..00000000 --- a/src/systemd/src/basic/fileio.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <dirent.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" -#include "time-util.h" - -typedef enum { - WRITE_STRING_FILE_CREATE = 1 << 0, - WRITE_STRING_FILE_ATOMIC = 1 << 1, - WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3, - WRITE_STRING_FILE_SYNC = 1 << 4, - WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5, - - /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one - more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() - and friends. */ - -} WriteStringFileFlags; - -int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); -static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { - return write_string_stream_ts(f, line, flags, NULL); -} -int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts); -static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { - return write_string_file_ts(fn, line, flags, NULL); -} - -int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); - -int read_one_line_file(const char *fn, char **line); -int read_full_file(const char *fn, char **contents, size_t *size); -int read_full_stream(FILE *f, char **contents, size_t *size); - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl); - -int parse_env_filev(FILE *f, const char *fname, const char *separator, va_list ap); -int parse_env_file(FILE *f, const char *fname, const char *separator, ...) _sentinel_; -int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); -int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); - -int merge_env_file(char ***env, FILE *f, const char *fname); - -int write_env_file(const char *fname, char **l); - -int executable_is_script(const char *path, char **interpreter); - -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); - -DIR *xopendirat(int dirfd, const char *name, int flags); - -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); - -#define FOREACH_LINE(line, f, on_error) \ - for (;;) \ - if (!fgets(line, sizeof(line), f)) { \ - if (ferror(f)) { \ - on_error; \ - } \ - break; \ - } else - -int fflush_and_check(FILE *f); -int fflush_sync_and_check(FILE *f); - -int fopen_temporary(const char *path, FILE **_f, char **_temp_path); -int mkostemp_safe(char *pattern); - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret); -int tempfn_random(const char *p, const char *extra, char **ret); -int tempfn_random_child(const char *p, const char *extra, char **ret); - -int write_timestamp_file_atomic(const char *fn, usec_t n); -int read_timestamp_file(const char *fn, usec_t *ret); - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); - -int open_tmpfile_unlinkable(const char *directory, int flags); -int open_tmpfile_linkable(const char *target, int flags, char **ret_path); -int open_serialization_fd(const char *ident); - -int link_tmpfile(int fd, const char *path, const char *target); - -int read_nul_string(FILE *f, char **ret); - -int mkdtemp_malloc(const char *template, char **ret); - -int read_line(FILE *f, size_t limit, char **ret); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c deleted file mode 100644 index 8c19e9c7..00000000 --- a/src/systemd/src/basic/fs-util.c +++ /dev/null @@ -1,1256 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <linux/magic.h> -#include <time.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "user-util.h" -#include "util.h" - -int unlink_noerrno(const char *path) { - PROTECT_ERRNO; - int r; - - r = unlink(path); - if (r < 0) - return -errno; - - return 0; -} - -#if 0 /* NM_IGNORED */ -int rmdir_parents(const char *path, const char *stop) { - size_t l; - int r = 0; - - assert(path); - assert(stop); - - l = strlen(path); - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - while (l > 0) { - char *t; - - /* Skip last component */ - while (l > 0 && path[l-1] != '/') - l--; - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - if (l <= 0) - break; - - t = strndup(path, l); - if (!t) - return -ENOMEM; - - if (path_startswith(stop, t)) { - free(t); - return 0; - } - - r = rmdir(t); - free(t); - - if (r < 0) - if (errno != ENOENT) - return -errno; - } - - return 0; -} - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - struct stat buf; - int ret; - - ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE); - if (ret >= 0) - return 0; - - /* renameat2() exists since Linux 3.15, btrfs added support for it later. - * If it is not implemented, fallback to another method. */ - if (!IN_SET(errno, EINVAL, ENOSYS)) - return -errno; - - /* The link()/unlink() fallback does not work on directories. But - * renameat() without RENAME_NOREPLACE gives the same semantics on - * directories, except when newpath is an *empty* directory. This is - * good enough. */ - ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW); - if (ret >= 0 && S_ISDIR(buf.st_mode)) { - ret = renameat(olddirfd, oldpath, newdirfd, newpath); - return ret >= 0 ? 0 : -errno; - } - - /* If it is not a directory, use the link()/unlink() fallback. */ - ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0); - if (ret < 0) - return -errno; - - ret = unlinkat(olddirfd, oldpath, 0); - if (ret < 0) { - /* backup errno before the following unlinkat() alters it */ - ret = errno; - (void) unlinkat(newdirfd, newpath, 0); - errno = ret; - return -errno; - } - - return 0; -} -#endif /* NM_IGNORED */ - -int readlinkat_malloc(int fd, const char *p, char **ret) { - size_t l = 100; - int r; - - assert(p); - assert(ret); - - for (;;) { - char *c; - ssize_t n; - - c = new(char, l); - if (!c) - return -ENOMEM; - - n = readlinkat(fd, p, c, l-1); - if (n < 0) { - r = -errno; - free(c); - return r; - } - - if ((size_t) n < l-1) { - c[n] = 0; - *ret = c; - return 0; - } - - free(c); - l *= 2; - } -} - -int readlink_malloc(const char *p, char **ret) { - return readlinkat_malloc(AT_FDCWD, p, ret); -} - -#if 0 /* NM_IGNORED */ -int readlink_value(const char *p, char **ret) { - _cleanup_free_ char *link = NULL; - char *value; - int r; - - r = readlink_malloc(p, &link); - if (r < 0) - return r; - - value = basename(link); - if (!value) - return -ENOENT; - - value = strdup(value); - if (!value) - return -ENOMEM; - - *ret = value; - - return 0; -} - -int readlink_and_make_absolute(const char *p, char **r) { - _cleanup_free_ char *target = NULL; - char *k; - int j; - - assert(p); - assert(r); - - j = readlink_malloc(p, &target); - if (j < 0) - return j; - - k = file_in_same_dir(p, target); - if (!k) - return -ENOMEM; - - *r = k; - return 0; -} - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { - assert(path); - - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ - - if (mode != MODE_INVALID) - if (chmod(path, mode) < 0) - return -errno; - - if (uid != UID_INVALID || gid != GID_INVALID) - if (chown(path, uid, gid) < 0) - return -errno; - - return 0; -} - -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ - - if (mode != MODE_INVALID) - if (fchmod(fd, mode) < 0) - return -errno; - - if (uid != UID_INVALID || gid != GID_INVALID) - if (fchown(fd, uid, gid) < 0) - return -errno; - - return 0; -} -#endif /* NM_IGNORED */ - -int fchmod_umask(int fd, mode_t m) { - mode_t u; - int r; - - u = umask(0777); - r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; - umask(u); - - return r; -} - -#if 0 /* NM_IGNORED */ -int fchmod_opath(int fd, mode_t m) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - - /* This function operates also on fd that might have been opened with - * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like - * fchownat() does. */ - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - - if (chmod(procfs_path, m) < 0) - return -errno; - - return 0; -} - -int fd_warn_permissions(const char *path, int fd) { - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; - - if (st.st_mode & 0111) - log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); - - if (st.st_mode & 0002) - log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - - if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) - log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); - - return 0; -} - -int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int fd = -1; - int r, ret = 0; - - assert(path); - - /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink - * itself which is updated, not its target - * - * Returns the first error we encounter, but tries to apply as much as possible. */ - - if (parents) - (void) mkdir_parents(path, 0755); - - /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in - * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and - * won't trigger any driver magic or so. */ - fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) { - if (errno != ENOENT) - return -errno; - - /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file - * here, and nothing else */ - fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); - if (fd < 0) - return -errno; - } - - /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, - * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is - * something fchown(), fchmod(), futimensat() don't allow. */ - xsprintf(fdpath, "/proc/self/fd/%i", fd); - - if (mode != MODE_INVALID) - if (chmod(fdpath, mode) < 0) - ret = -errno; - - if (uid_is_valid(uid) || gid_is_valid(gid)) - if (chown(fdpath, uid, gid) < 0 && ret >= 0) - ret = -errno; - - if (stamp != USEC_INFINITY) { - struct timespec ts[2]; - - timespec_store(&ts[0], stamp); - ts[1] = ts[0]; - r = utimensat(AT_FDCWD, fdpath, ts, 0); - } else - r = utimensat(AT_FDCWD, fdpath, NULL, 0); - if (r < 0 && ret >= 0) - return -errno; - - return ret; -} - -int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); -} - -int symlink_idempotent(const char *from, const char *to) { - int r; - - assert(from); - assert(to); - - if (symlink(from, to) < 0) { - _cleanup_free_ char *p = NULL; - - if (errno != EEXIST) - return -errno; - - r = readlink_malloc(to, &p); - if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */ - return -EEXIST; - if (r < 0) /* Any other error? In that case propagate it as is */ - return r; - - if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */ - return -EEXIST; - } - - return 0; -} - -int symlink_atomic(const char *from, const char *to) { - _cleanup_free_ char *t = NULL; - int r; - - assert(from); - assert(to); - - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - if (symlink(from, t) < 0) - return -errno; - - if (rename(t, to) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mknod_atomic(const char *path, mode_t mode, dev_t dev) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mknod(t, mode, dev) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mkfifo_atomic(const char *path, mode_t mode) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mkfifo(t, mode) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - if (path_is_absolute(path)) - return mkfifo_atomic(path, mode); - - /* We're only interested in the (random) filename. */ - r = tempfn_random_child("", NULL, &t); - if (r < 0) - return r; - - if (mkfifoat(dirfd, t, mode) < 0) - return -errno; - - if (renameat(dirfd, t, dirfd, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int get_files_in_directory(const char *path, char ***list) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - size_t bufsize = 0, n = 0; - _cleanup_strv_free_ char **l = NULL; - - assert(path); - - /* Returns all files in a directory in *list, and the number - * of files as return value. If list is NULL returns only the - * number. */ - - d = opendir(path); - if (!d) - return -errno; - - FOREACH_DIRENT_ALL(de, d, return -errno) { - dirent_ensure_type(d, de); - - if (!dirent_is_file(de)) - continue; - - if (list) { - /* one extra slot is needed for the terminating NULL */ - if (!GREEDY_REALLOC(l, bufsize, n + 2)) - return -ENOMEM; - - l[n] = strdup(de->d_name); - if (!l[n]) - return -ENOMEM; - - l[++n] = NULL; - } else - n++; - } - - if (list) - *list = TAKE_PTR(l); - - return n; -} - -static int getenv_tmp_dir(const char **ret_path) { - const char *n; - int r, ret = 0; - - assert(ret_path); - - /* We use the same order of environment variables python uses in tempfile.gettempdir(): - * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */ - FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") { - const char *e; - - e = secure_getenv(n); - if (!e) - continue; - if (!path_is_absolute(e)) { - r = -ENOTDIR; - goto next; - } - if (!path_is_normalized(e)) { - r = -EPERM; - goto next; - } - - r = is_dir(e, true); - if (r < 0) - goto next; - if (r == 0) { - r = -ENOTDIR; - goto next; - } - - *ret_path = e; - return 1; - - next: - /* Remember first error, to make this more debuggable */ - if (ret >= 0) - ret = r; - } - - if (ret < 0) - return ret; - - *ret_path = NULL; - return ret; -} - -static int tmp_dir_internal(const char *def, const char **ret) { - const char *e; - int r, k; - - assert(def); - assert(ret); - - r = getenv_tmp_dir(&e); - if (r > 0) { - *ret = e; - return 0; - } - - k = is_dir(def, true); - if (k == 0) - k = -ENOTDIR; - if (k < 0) - return r < 0 ? r : k; - - *ret = def; - return 0; -} - -int var_tmp_dir(const char **ret) { - - /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus - * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is - * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR, - * making it a variable that overrides all temporary file storage locations. */ - - return tmp_dir_internal("/var/tmp", ret); -} - -int tmp_dir(const char **ret) { - - /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually - * backed by an in-memory file system: /tmp. */ - - return tmp_dir_internal("/tmp", ret); -} - -int unlink_or_warn(const char *filename) { - if (unlink(filename) < 0 && errno != ENOENT) - /* If the file doesn't exist and the fs simply was read-only (in which - * case unlink() returns EROFS even if the file doesn't exist), don't - * complain */ - if (errno != EROFS || access(filename, F_OK) >= 0) - return log_error_errno(errno, "Failed to remove \"%s\": %m", filename); - - return 0; -} -#endif /* NM_IGNORED */ - -int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int r; - - /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ - xsprintf(path, "/proc/self/fd/%i", what); - - r = inotify_add_watch(fd, path, mask); - if (r < 0) - return -errno; - - return r; -} - -#if 0 /* NM_IGNORED */ -static bool safe_transition(const struct stat *a, const struct stat *b) { - /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to - * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files - * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ - - if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ - return true; - - return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */ -} - -int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) { - _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; - _cleanup_close_ int fd = -1; - unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ - struct stat previous_stat; - bool exists = true; - char *todo; - int r; - - assert(path); - - /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ - if (FLAGS_SET(flags, CHASE_NONEXISTENT | CHASE_OPEN)) - return -EINVAL; - - if (FLAGS_SET(flags, CHASE_STEP | CHASE_OPEN)) - return -EINVAL; - - if (isempty(path)) - return -EINVAL; - - /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following - * symlinks relative to a root directory, instead of the root of the host. - * - * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following - * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is - * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first - * prefixed accordingly. - * - * Algorithmically this operates on two path buffers: "done" are the components of the path we already - * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to - * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning - * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no - * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races - * at a minimum. - * - * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got - * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this - * function what to do when encountering a symlink with an absolute path as directory: prefix it by the - * specified path. - * - * There are three ways to invoke this function: - * - * 1. Without CHASE_STEP or CHASE_OPEN: in this case the path is resolved and the normalized path is returned - * in `ret`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set 0 is returned if the file - * doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set >= 0 is returned if the destination was - * found, -ENOENT if it doesn't. - * - * 2. With CHASE_OPEN: in this case the destination is opened after chasing it as O_PATH and this file - * descriptor is returned as return value. This is useful to open files relative to some root - * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using - * fd_reopen() or such) before it can be used for reading/writing. CHASE_OPEN may not be combined with - * CHASE_NONEXISTENT. - * - * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first - * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if - * a caller wants to trace the a path through the file system verbosely. Returns < 0 on error, > 0 if the - * path is fully normalized, and == 0 for each normalization step. This may be combined with - * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. - * - * */ - - /* A root directory of "/" or "" is identical to none */ - if (empty_or_root(original_root)) - original_root = NULL; - - if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) { - /* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set - * and doesn't care about any of the other special features we provide either. */ - r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); - if (r < 0) - return -errno; - - return r; - } - - if (original_root) { - r = path_make_absolute_cwd(original_root, &root); - if (r < 0) - return r; - - if (flags & CHASE_PREFIX_ROOT) { - - /* We don't support relative paths in combination with a root directory */ - if (!path_is_absolute(path)) - return -EINVAL; - - path = prefix_roota(root, path); - } - } - - r = path_make_absolute_cwd(path, &buffer); - if (r < 0) - return r; - - fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &previous_stat) < 0) - return -errno; - } - - todo = buffer; - for (;;) { - _cleanup_free_ char *first = NULL; - _cleanup_close_ int child = -1; - struct stat st; - size_t n, m; - - /* Determine length of first component in the path */ - n = strspn(todo, "/"); /* The slashes */ - m = n + strcspn(todo + n, "/"); /* The entire length of the component */ - - /* Extract the first component. */ - first = strndup(todo, m); - if (!first) - return -ENOMEM; - - todo += m; - - /* Empty? Then we reached the end. */ - if (isempty(first)) - break; - - /* Just a single slash? Then we reached the end. */ - if (path_equal(first, "/")) { - /* Preserve the trailing slash */ - - if (flags & CHASE_TRAIL_SLASH) - if (!strextend(&done, "/", NULL)) - return -ENOMEM; - - break; - } - - /* Just a dot? Then let's eat this up. */ - if (path_equal(first, "/.")) - continue; - - /* Two dots? Then chop off the last bit of what we already found out. */ - if (path_equal(first, "/..")) { - _cleanup_free_ char *parent = NULL; - _cleanup_close_ int fd_parent = -1; - - /* If we already are at the top, then going up will not change anything. This is in-line with - * how the kernel handles this. */ - if (empty_or_root(done)) - continue; - - parent = dirname_malloc(done); - if (!parent) - return -ENOMEM; - - /* Don't allow this to leave the root dir. */ - if (root && - path_startswith(done, root) && - !path_startswith(parent, root)) - continue; - - free_and_replace(done, parent); - - if (flags & CHASE_STEP) - goto chased_one; - - fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd_parent < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd_parent, &st) < 0) - return -errno; - - if (!safe_transition(&previous_stat, &st)) - return -EPERM; - - previous_stat = st; - } - - safe_close(fd); - fd = TAKE_FD(fd_parent); - - continue; - } - - /* Otherwise let's see what this is. */ - child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (child < 0) { - - if (errno == ENOENT && - (flags & CHASE_NONEXISTENT) && - (isempty(todo) || path_is_normalized(todo))) { - - /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return - * what we got so far. But don't allow this if the remaining path contains "../ or "./" - * or something else weird. */ - - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq_ptr(done, "/")) - *done = '\0'; - - if (!strextend(&done, first, todo, NULL)) - return -ENOMEM; - - exists = false; - break; - } - - return -errno; - } - - if (fstat(child, &st) < 0) - return -errno; - if ((flags & CHASE_SAFE) && - !safe_transition(&previous_stat, &st)) - return -EPERM; - - previous_stat = st; - - if ((flags & CHASE_NO_AUTOFS) && - fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) - return -EREMOTE; - - if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { - char *joined; - - _cleanup_free_ char *destination = NULL; - - /* This is a symlink, in this case read the destination. But let's make sure we don't follow - * symlinks without bounds. */ - if (--max_follow <= 0) - return -ELOOP; - - r = readlinkat_malloc(fd, first + n, &destination); - if (r < 0) - return r; - if (isempty(destination)) - return -EINVAL; - - if (path_is_absolute(destination)) { - - /* An absolute destination. Start the loop from the beginning, but use the root - * directory as base. */ - - safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &st) < 0) - return -errno; - - if (!safe_transition(&previous_stat, &st)) - return -EPERM; - - previous_stat = st; - } - - free(done); - - /* Note that we do not revalidate the root, we take it as is. */ - if (isempty(root)) - done = NULL; - else { - done = strdup(root); - if (!done) - return -ENOMEM; - } - - /* Prefix what's left to do with what we just read, and start the loop again, but - * remain in the current directory. */ - joined = strjoin(destination, todo); - } else - joined = strjoin("/", destination, todo); - if (!joined) - return -ENOMEM; - - free(buffer); - todo = buffer = joined; - - if (flags & CHASE_STEP) - goto chased_one; - - continue; - } - - /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!done) - done = TAKE_PTR(first); - else { - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq(done, "/")) - *done = '\0'; - - if (!strextend(&done, first, NULL)) - return -ENOMEM; - } - - /* And iterate again, but go one directory further down. */ - safe_close(fd); - fd = TAKE_FD(child); - } - - if (!done) { - /* Special case, turn the empty string into "/", to indicate the root directory. */ - done = strdup("/"); - if (!done) - return -ENOMEM; - } - - if (ret) - *ret = TAKE_PTR(done); - - if (flags & CHASE_OPEN) { - /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by - * opening /proc/self/fd/xyz. */ - - assert(fd >= 0); - return TAKE_FD(fd); - } - - if (flags & CHASE_STEP) - return 1; - - return exists; - -chased_one: - if (ret) { - char *c; - - c = strjoin(strempty(done), todo); - if (!c) - return -ENOMEM; - - *ret = c; - } - - return 0; -} - -int chase_symlinks_and_open( - const char *path, - const char *root, - unsigned chase_flags, - int open_flags, - char **ret_path) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - r = open(path, open_flags); - if (r < 0) - return -errno; - - return r; - } - - path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); - if (path_fd < 0) - return path_fd; - - r = fd_reopen(path_fd, open_flags); - if (r < 0) - return r; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - return r; -} - -int chase_symlinks_and_opendir( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - DIR **ret_dir) { - - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - DIR *d; - - if (!ret_dir) - return -EINVAL; - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - d = opendir(path); - if (!d) - return -errno; - - *ret_dir = d; - return 0; - } - - path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); - if (path_fd < 0) - return path_fd; - - xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); - d = opendir(procfs_path); - if (!d) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - *ret_dir = d; - return 0; -} - -int chase_symlinks_and_stat( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - struct stat *ret_stat) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - - assert(path); - assert(ret_stat); - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - if (stat(path, ret_stat) < 0) - return -errno; - - return 1; - } - - path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); - if (path_fd < 0) - return path_fd; - - if (fstat(path_fd, ret_stat) < 0) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - if (chase_flags & CHASE_OPEN) - return TAKE_FD(path_fd); - - return 1; -} - -int access_fd(int fd, int mode) { - char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - int r; - - /* Like access() but operates on an already open fd */ - - xsprintf(p, "/proc/self/fd/%i", fd); - r = access(p, mode); - if (r < 0) - return -errno; - - return r; -} - -void unlink_tempfilep(char (*p)[]) { - /* If the file is created with mkstemp(), it will (almost always) - * change the suffix. Treat this as a sign that the file was - * successfully created. We ignore both the rare case where the - * original suffix is used and unlink failures. */ - if (!endswith(*p, ".XXXXXX")) - (void) unlink_noerrno(*p); -} - -int unlinkat_deallocate(int fd, const char *name, int flags) { - _cleanup_close_ int truncate_fd = -1; - struct stat st; - off_t l, bs; - - /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other - * link to it. This is useful to ensure that other processes that might have the file open for reading won't be - * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up - * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and - * returned to the free pool. - * - * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means - * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other - * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes - * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.) - * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file - * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐). - * - * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the - * primary job – to delete the file – is accomplished. */ - - if ((flags & AT_REMOVEDIR) == 0) { - truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); - if (truncate_fd < 0) { - - /* If this failed because the file doesn't exist propagate the error right-away. Also, - * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is - * returned when this is a directory but we are not supposed to delete those, hence propagate - * the error right-away too. */ - if (IN_SET(errno, ENOENT, EISDIR)) - return -errno; - - if (errno != ELOOP) /* don't complain if this is a symlink */ - log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name); - } - } - - if (unlinkat(fd, name, flags) < 0) - return -errno; - - if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ - return 0; - - if (fstat(truncate_fd, &st) < 0) { - log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name); - return 0; - } - - if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0) - return 0; - - /* If this is a regular file, it actually took up space on disk and there are no other links it's time to - * punch-hole/truncate this to release the disk space. */ - - bs = MAX(st.st_blksize, 512); - l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */ - - if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0) - return 0; /* Successfully punched a hole! 😊 */ - - /* Fall back to truncation */ - if (ftruncate(truncate_fd, 0) < 0) { - log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m"); - return 0; - } - - return 0; -} - -int fsync_directory_of_file(int fd) { - _cleanup_free_ char *path = NULL; - _cleanup_close_ int dfd = -1; - int r; - - r = fd_verify_regular(fd); - if (r < 0) - return r; - - r = fd_get_path(fd, &path); - if (r < 0) { - log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", - fd, - r == -EOPNOTSUPP ? ", ignoring" : ""); - - if (r == -EOPNOTSUPP) - /* If /proc is not available, we're most likely running in some - * chroot environment, and syncing the directory is not very - * important in that case. Let's just silently do nothing. */ - return 0; - - return r; - } - - if (!path_is_absolute(path)) - return -EINVAL; - - dfd = open_parent(path, O_CLOEXEC, 0); - if (dfd < 0) - return dfd; - - if (fsync(dfd) < 0) - return -errno; - - return 0; -} - -int open_parent(const char *path, int flags, mode_t mode) { - _cleanup_free_ char *parent = NULL; - int fd; - - if (isempty(path)) - return -EINVAL; - if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */ - return -EINVAL; - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an - * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */ - - if ((flags & O_PATH) == O_PATH) - flags |= O_DIRECTORY; - else if ((flags & O_TMPFILE) != O_TMPFILE) - flags |= O_DIRECTORY|O_RDONLY; - - fd = open(parent, flags, mode); - if (fd < 0) - return -errno; - - return fd; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h deleted file mode 100644 index 4b656258..00000000 --- a/src/systemd/src/basic/fs-util.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <dirent.h> -#include <fcntl.h> -#include <limits.h> -#include <stdbool.h> -#include <stdint.h> -#include <sys/inotify.h> -#include <sys/types.h> -#include <unistd.h> - -#include "time-util.h" -#include "util.h" - -int unlink_noerrno(const char *path); - -int rmdir_parents(const char *path, const char *stop); - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); - -int readlinkat_malloc(int fd, const char *p, char **ret); -int readlink_malloc(const char *p, char **r); -int readlink_value(const char *p, char **ret); -int readlink_and_make_absolute(const char *p, char **r); - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid); - -int fchmod_umask(int fd, mode_t mode); -int fchmod_opath(int fd, mode_t m); - -int fd_warn_permissions(const char *path, int fd); - -#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) - -int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); -int touch(const char *path); - -int symlink_idempotent(const char *from, const char *to); - -int symlink_atomic(const char *from, const char *to); -int mknod_atomic(const char *path, mode_t mode, dev_t dev); -int mkfifo_atomic(const char *path, mode_t mode); -int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode); - -int get_files_in_directory(const char *path, char ***list); - -int tmp_dir(const char **ret); -int var_tmp_dir(const char **ret); - -int unlink_or_warn(const char *filename); - -#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]; -}; - -int inotify_add_watch_fd(int fd, int what, uint32_t mask); - -enum { - CHASE_PREFIX_ROOT = 1 << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 1 << 1, /* If set, it's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 1 << 2, /* If set, return -EREMOTE if autofs mount point found */ - CHASE_SAFE = 1 << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ - CHASE_OPEN = 1 << 4, /* If set, return an O_PATH object to the final component */ - CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */ - CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */ -}; - -/* How many iterations to execute before returning -ELOOP */ -#define CHASE_SYMLINKS_MAX 32 - -int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); - -int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); -int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); -int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat); - -/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ -static inline void rmdir_and_free(char *p) { - PROTECT_ERRNO; - (void) rmdir(p); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); - -static inline void unlink_and_free(char *p) { - (void) unlink_noerrno(p); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); - -int access_fd(int fd, int mode); - -void unlink_tempfilep(char (*p)[]); -int unlinkat_deallocate(int fd, const char *name, int flags); - -int fsync_directory_of_file(int fd); - -int open_parent(const char *path, int flags, mode_t mode); diff --git a/src/systemd/src/basic/hash-funcs.c b/src/systemd/src/basic/hash-funcs.c deleted file mode 100644 index fc2c4f78..00000000 --- a/src/systemd/src/basic/hash-funcs.c +++ /dev/null @@ -1,120 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <string.h> - -#include "hash-funcs.h" -#include "path-util.h" - -void string_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, strlen(p) + 1, state); -} - -#if 0 /* NM_IGNORED */ -int string_compare_func(const void *a, const void *b) { - return strcmp(a, b); -} - -const struct hash_ops string_hash_ops = { - .hash = string_hash_func, - .compare = string_compare_func -}; - -void path_hash_func(const void *p, struct siphash *state) { - const char *q = p; - size_t n; - - assert(q); - assert(state); - - /* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also - * whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does - * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those - * which begin in a slash or not) will hash differently though. */ - - n = strspn(q, "/"); - if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */ - siphash24_compress(q, 1, state); - q += n; - } - - for (;;) { - /* Determine length of next component */ - n = strcspn(q, "/"); - if (n == 0) /* Reached the end? */ - break; - - /* Add this component to the hash and skip over it */ - siphash24_compress(q, n, state); - q += n; - - /* How many slashes follow this component? */ - n = strspn(q, "/"); - if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */ - break; - - /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */ - siphash24_compress(q, 1, state); - q += n; - } -} - -int path_compare_func(const void *a, const void *b) { - return path_compare(a, b); -} - -const struct hash_ops path_hash_ops = { - .hash = path_hash_func, - .compare = path_compare_func -}; -#endif /* NM_IGNORED */ - -void trivial_hash_func(const void *p, struct siphash *state) { - siphash24_compress(&p, sizeof(p), state); -} - -int trivial_compare_func(const void *a, const void *b) { - return CMP(a, b); -} - -const struct hash_ops trivial_hash_ops = { - .hash = trivial_hash_func, - .compare = trivial_compare_func -}; - -void uint64_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(uint64_t), state); -} - -int uint64_compare_func(const void *_a, const void *_b) { - uint64_t a, b; - a = *(const uint64_t*) _a; - b = *(const uint64_t*) _b; - return CMP(a, b); -} - -const struct hash_ops uint64_hash_ops = { - .hash = uint64_hash_func, - .compare = uint64_compare_func -}; - -#if 0 /* NM_IGNORED */ -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(dev_t), state); -} - -int devt_compare_func(const void *_a, const void *_b) { - dev_t a, b; - a = *(const dev_t*) _a; - b = *(const dev_t*) _b; - return CMP(a, b); -} - -const struct hash_ops devt_hash_ops = { - .hash = devt_hash_func, - .compare = devt_compare_func -}; -#endif -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hash-funcs.h b/src/systemd/src/basic/hash-funcs.h deleted file mode 100644 index fa45cfe2..00000000 --- a/src/systemd/src/basic/hash-funcs.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include "macro.h" -#include "siphash24.h" - -typedef void (*hash_func_t)(const void *p, struct siphash *state); -typedef int (*compare_func_t)(const void *a, const void *b); - -struct hash_ops { - hash_func_t hash; - compare_func_t compare; -}; - -void string_hash_func(const void *p, struct siphash *state); -int string_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops string_hash_ops; - -void path_hash_func(const void *p, struct siphash *state); -int path_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops path_hash_ops; - -/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings - * or suchlike. */ -void trivial_hash_func(const void *p, struct siphash *state); -int trivial_compare_func(const void *a, const void *b) _const_; -extern const struct hash_ops trivial_hash_ops; - -/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit - * values indirectly, since they don't fit in a pointer. */ -void uint64_hash_func(const void *p, struct siphash *state); -int uint64_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops uint64_hash_ops; - -/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on - * 64bit archs. Yuck! */ -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) _pure_; -int devt_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops devt_hash_ops; -#else -#define devt_hash_func uint64_hash_func -#define devt_compare_func uint64_compare_func -#define devt_hash_ops uint64_hash_ops -#endif diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c deleted file mode 100644 index cfddeafe..00000000 --- a/src/systemd/src/basic/hashmap.c +++ /dev/null @@ -1,1988 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "env-util.h" -#include "fileio.h" -#include "hashmap.h" -#include "macro.h" -#include "mempool.h" -#include "process-util.h" -#include "random-util.h" -#include "set.h" -#include "siphash24.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -#if ENABLE_DEBUG_HASHMAP -#include <pthread.h> -#include "list.h" -#endif - -/* - * Implementation of hashmaps. - * Addressing: open - * - uses less RAM compared to closed addressing (chaining), because - * our entries are small (especially in Sets, which tend to contain - * the majority of entries in systemd). - * Collision resolution: Robin Hood - * - tends to equalize displacement of entries from their optimal buckets. - * Probe sequence: linear - * - though theoretically worse than random probing/uniform hashing/double - * hashing, it is good for cache locality. - * - * References: - * Celis, P. 1986. Robin Hood Hashing. - * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. - * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf - * - The results are derived for random probing. Suggests deletion with - * tombstones and two mean-centered search methods. None of that works - * well for linear probing. - * - * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. - * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. - * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 - * http://www.math.uu.se/~svante/papers/sj157.pdf - * - Applies to Robin Hood with linear probing. Contains remarks on - * the unsuitability of mean-centered search with linear probing. - * - * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. - * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. - * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 - * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes - * in a successful search), and Janson writes about displacement. C = d + 1. - * - * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. - * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - * - Explanation of backward shift deletion with pictures. - * - * Khuong, P. 2013. The Other Robin Hood Hashing. - * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ - * - Short summary of random vs. linear probing, and tombstones vs. backward shift. - */ - -/* - * XXX Ideas for improvement: - * For unordered hashmaps, randomize iteration order, similarly to Perl: - * http://blog.booking.com/hardening-perls-hash-function.html - */ - -/* INV_KEEP_FREE = 1 / (1 - max_load_factor) - * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ -#define INV_KEEP_FREE 5U - -/* Fields common to entries of all hashmap/set types */ -struct hashmap_base_entry { - const void *key; -}; - -/* Entry types for specific hashmap/set types - * hashmap_base_entry must be at the beginning of each entry struct. */ - -struct plain_hashmap_entry { - struct hashmap_base_entry b; - void *value; -}; - -struct ordered_hashmap_entry { - struct plain_hashmap_entry p; - unsigned iterate_next, iterate_previous; -}; - -struct set_entry { - struct hashmap_base_entry b; -}; - -/* In several functions it is advantageous to have the hash table extended - * virtually by a couple of additional buckets. We reserve special index values - * for these "swap" buckets. */ -#define _IDX_SWAP_BEGIN (UINT_MAX - 3) -#define IDX_PUT (_IDX_SWAP_BEGIN + 0) -#define IDX_TMP (_IDX_SWAP_BEGIN + 1) -#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) - -#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ -#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ - -assert_cc(IDX_FIRST == _IDX_SWAP_END); -assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); - -/* Storage space for the "swap" buckets. - * All entry types can fit into a ordered_hashmap_entry. */ -struct swap_entries { - struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; -}; - -/* Distance from Initial Bucket */ -typedef uint8_t dib_raw_t; -#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ -#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ -#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ -#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ - -#define DIB_FREE UINT_MAX - -#if ENABLE_DEBUG_HASHMAP -struct hashmap_debug_info { - LIST_FIELDS(struct hashmap_debug_info, debug_list); - unsigned max_entries; /* high watermark of n_entries */ - - /* who allocated this hashmap */ - int line; - const char *file; - const char *func; - - /* fields to detect modification while iterating */ - unsigned put_count; /* counts puts into the hashmap */ - unsigned rem_count; /* counts removals from hashmap */ - unsigned last_rem_idx; /* remembers last removal index */ -}; - -/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ -static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); -static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ - -enum HashmapType { - HASHMAP_TYPE_PLAIN, - HASHMAP_TYPE_ORDERED, - HASHMAP_TYPE_SET, - _HASHMAP_TYPE_MAX -}; - -struct _packed_ indirect_storage { - void *storage; /* where buckets and DIBs are stored */ - uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ - - unsigned n_entries; /* number of stored entries */ - unsigned n_buckets; /* number of buckets */ - - unsigned idx_lowest_entry; /* Index below which all buckets are free. - Makes "while(hashmap_steal_first())" loops - O(n) instead of O(n^2) for unordered hashmaps. */ - uint8_t _pad[3]; /* padding for the whole HashmapBase */ - /* The bitfields in HashmapBase complete the alignment of the whole thing. */ -}; - -struct direct_storage { - /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. - * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, - * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ - uint8_t storage[sizeof(struct indirect_storage)]; -}; - -#define DIRECT_BUCKETS(entry_t) \ - (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) - -/* We should be able to store at least one entry directly. */ -assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); - -/* We have 3 bits for n_direct_entries. */ -assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); - -/* Hashmaps with directly stored entries all use this shared hash key. - * It's no big deal if the key is guessed, because there can be only - * a handful of directly stored entries in a hashmap. When a hashmap - * outgrows direct storage, it gets its own key for indirect storage. */ -static uint8_t shared_hash_key[HASH_KEY_SIZE]; -static bool shared_hash_key_initialized; - -/* Fields that all hashmap/set types must have */ -struct HashmapBase { - const struct hash_ops *hash_ops; /* hash and compare ops to use */ - - union _packed_ { - struct indirect_storage indirect; /* if has_indirect */ - struct direct_storage direct; /* if !has_indirect */ - }; - - enum HashmapType type:2; /* HASHMAP_TYPE_* */ - bool has_indirect:1; /* whether indirect storage is used */ - unsigned n_direct_entries:3; /* Number of entries in direct storage. - * Only valid if !has_indirect. */ - bool from_pool:1; /* whether was allocated from mempool */ - bool dirty:1; /* whether dirtied since last iterated_cache_get() */ - bool cached:1; /* whether this hashmap is being cached */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ -}; - -/* Specific hash types - * HashmapBase must be at the beginning of each hashmap struct. */ - -struct Hashmap { - struct HashmapBase b; -}; - -struct OrderedHashmap { - struct HashmapBase b; - unsigned iterate_list_head, iterate_list_tail; -}; - -struct Set { - struct HashmapBase b; -}; - -typedef struct CacheMem { - const void **ptr; - size_t n_populated, n_allocated; - bool active:1; -} CacheMem; - -struct IteratedCache { - HashmapBase *hashmap; - CacheMem keys, values; -}; - -DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); -DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); -/* No need for a separate Set pool */ -assert_cc(sizeof(Hashmap) == sizeof(Set)); - -struct hashmap_type_info { - size_t head_size; - size_t entry_size; - struct mempool *mempool; - unsigned n_direct_buckets; -}; - -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { - [HASHMAP_TYPE_PLAIN] = { - .head_size = sizeof(Hashmap), - .entry_size = sizeof(struct plain_hashmap_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), - }, - [HASHMAP_TYPE_ORDERED] = { - .head_size = sizeof(OrderedHashmap), - .entry_size = sizeof(struct ordered_hashmap_entry), - .mempool = &ordered_hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), - }, - [HASHMAP_TYPE_SET] = { - .head_size = sizeof(Set), - .entry_size = sizeof(struct set_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), - }, -}; - -#if VALGRIND -__attribute__((destructor)) static void cleanup_pools(void) { - _cleanup_free_ char *t = NULL; - int r; - - /* Be nice to valgrind */ - - /* The pool is only allocated by the main thread, but the memory can - * be passed to other threads. Let's clean up if we are the main thread - * and no other threads are live. */ - if (!is_main_thread()) - return; - - r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t); - if (r < 0 || !streq(t, "1")) - return; - - mempool_drop(&hashmap_pool); - mempool_drop(&ordered_hashmap_pool); -} -#endif - -static unsigned n_buckets(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_buckets - : hashmap_type_info[h->type].n_direct_buckets; -} - -static unsigned n_entries(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_entries - : h->n_direct_entries; -} - -static void n_entries_inc(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries++; - else - h->n_direct_entries++; -} - -static void n_entries_dec(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries--; - else - h->n_direct_entries--; -} - -static void *storage_ptr(HashmapBase *h) { - return h->has_indirect ? h->indirect.storage - : h->direct.storage; -} - -static uint8_t *hash_key(HashmapBase *h) { - return h->has_indirect ? h->indirect.hash_key - : shared_hash_key; -} - -static unsigned base_bucket_hash(HashmapBase *h, const void *p) { - struct siphash state; - uint64_t hash; - - siphash24_init(&state, hash_key(h)); - - h->hash_ops->hash(p, &state); - - hash = siphash24_finalize(&state); - - return (unsigned) (hash % n_buckets(h)); -} -#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) - -static inline void base_set_dirty(HashmapBase *h) { - h->dirty = true; -} -#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h)) - -static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { - static uint8_t current[HASH_KEY_SIZE]; - static bool current_initialized = false; - - /* Returns a hash function key to use. In order to keep things - * fast we will not generate a new key each time we allocate a - * new hash table. Instead, we'll just reuse the most recently - * generated one, except if we never generated one or when we - * are rehashing an entire hash table because we reached a - * fill level */ - - if (!current_initialized || !reuse_is_ok) { - random_bytes(current, sizeof(current)); - current_initialized = true; - } - - memcpy(hash_key, current, sizeof(current)); -} - -static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) - ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); -} - -static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { - return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { - return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct set_entry *set_bucket_at(Set *h, unsigned idx) { - return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { - return &swap->e[idx - _IDX_SWAP_BEGIN]; -} - -/* Returns a pointer to the bucket at index idx. - * Understands real indexes and swap indexes, hence "_virtual". */ -static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, - unsigned idx) { - if (idx < _IDX_SWAP_BEGIN) - return bucket_at(h, idx); - - if (idx < _IDX_SWAP_END) - return &bucket_at_swap(swap, idx)->p.b; - - assert_not_reached("Invalid index"); -} - -static dib_raw_t *dib_raw_ptr(HashmapBase *h) { - return (dib_raw_t*) - ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); -} - -static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { - return idx >= from ? idx - from - : n_buckets(h) + idx - from; -} - -static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { - unsigned initial_bucket; - - if (raw_dib == DIB_RAW_FREE) - return DIB_FREE; - - if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) - return raw_dib; - - /* - * Having an overflow DIB value is very unlikely. The hash function - * would have to be bad. For example, in a table of size 2^24 filled - * to load factor 0.9 the maximum observed DIB is only about 60. - * In theory (assuming I used Maxima correctly), for an infinite size - * hash table with load factor 0.8 the probability of a given entry - * having DIB > 40 is 1.9e-8. - * This returns the correct DIB value by recomputing the hash value in - * the unlikely case. XXX Hitting this case could be a hint to rehash. - */ - initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); - return bucket_distance(h, idx, initial_bucket); -} - -static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { - dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; -} - -static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { - dib_raw_t *dibs; - - dibs = dib_raw_ptr(h); - - for ( ; idx < n_buckets(h); idx++) - if (dibs[idx] != DIB_RAW_FREE) - return idx; - - return IDX_NIL; -} - -static void bucket_mark_free(HashmapBase *h, unsigned idx) { - memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size); - bucket_set_dib(h, idx, DIB_FREE); -} - -static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, - unsigned from, unsigned to) { - struct hashmap_base_entry *e_from, *e_to; - - assert(from != to); - - e_from = bucket_at_virtual(h, swap, from); - e_to = bucket_at_virtual(h, swap, to); - - memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le, *le_to; - - le_to = (struct ordered_hashmap_entry*) e_to; - - if (le_to->iterate_next != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_next); - le->iterate_previous = to; - } - - if (le_to->iterate_previous != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_previous); - le->iterate_next = to; - } - - if (lh->iterate_list_head == from) - lh->iterate_list_head = to; - if (lh->iterate_list_tail == from) - lh->iterate_list_tail = to; - } -} - -static unsigned next_idx(HashmapBase *h, unsigned idx) { - return (idx + 1U) % n_buckets(h); -} - -static unsigned prev_idx(HashmapBase *h, unsigned idx) { - return (n_buckets(h) + idx - 1U) % n_buckets(h); -} - -static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { - switch (h->type) { - - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - return ((struct plain_hashmap_entry*)e)->value; - - case HASHMAP_TYPE_SET: - return (void*) e->key; - - default: - assert_not_reached("Unknown hashmap type"); - } -} - -static void base_remove_entry(HashmapBase *h, unsigned idx) { - unsigned left, right, prev, dib; - dib_raw_t raw_dib, *dibs; - - dibs = dib_raw_ptr(h); - assert(dibs[idx] != DIB_RAW_FREE); - -#if ENABLE_DEBUG_HASHMAP - h->debug.rem_count++; - h->debug.last_rem_idx = idx; -#endif - - left = idx; - /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ - for (right = next_idx(h, left); ; right = next_idx(h, right)) { - raw_dib = dibs[right]; - if (IN_SET(raw_dib, 0, DIB_RAW_FREE)) - break; - - /* The buckets are not supposed to be all occupied and with DIB > 0. - * That would mean we could make everyone better off by shifting them - * backward. This scenario is impossible. */ - assert(left != right); - } - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); - - if (le->iterate_next != IDX_NIL) - ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; - else - lh->iterate_list_tail = le->iterate_previous; - - if (le->iterate_previous != IDX_NIL) - ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; - else - lh->iterate_list_head = le->iterate_next; - } - - /* Now shift all buckets in the interval (left, right) one step backwards */ - for (prev = left, left = next_idx(h, left); left != right; - prev = left, left = next_idx(h, left)) { - dib = bucket_calculate_dib(h, left, dibs[left]); - assert(dib != 0); - bucket_move_entry(h, NULL, left, prev); - bucket_set_dib(h, prev, dib - 1); - } - - bucket_mark_free(h, prev); - n_entries_dec(h); - base_set_dirty(h); -} -#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) - -static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { - struct ordered_hashmap_entry *e; - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - idx = h->iterate_list_head; - e = ordered_bucket_at(h, idx); - } else { - idx = i->idx; - e = ordered_bucket_at(h, idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->p.b.key != i->next_key) { - idx = prev_idx(HASHMAP_BASE(h), idx); - e = ordered_bucket_at(h, idx); - } - assert(e->p.b.key == i->next_key); - } - -#if ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - if (e->iterate_next != IDX_NIL) { - struct ordered_hashmap_entry *n; - i->idx = e->iterate_next; - n = ordered_bucket_at(h, i->idx); - i->next_key = n->p.b.key; - } else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - /* fast forward to the first occupied bucket */ - if (h->has_indirect) { - i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); - h->indirect.idx_lowest_entry = i->idx; - } else - i->idx = skip_free_buckets(h, 0); - - if (i->idx == IDX_NIL) - goto at_end; - } else { - struct hashmap_base_entry *e; - - assert(i->idx > 0); - - e = bucket_at(h, i->idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->key != i->next_key) - e = bucket_at(h, --i->idx); - - assert(e->key == i->next_key); - } - - idx = i->idx; -#if ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - i->idx = skip_free_buckets(h, i->idx + 1); - if (i->idx != IDX_NIL) - i->next_key = bucket_at(h, i->idx)->key; - else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { - if (!h) { - i->idx = IDX_NIL; - return IDX_NIL; - } - -#if ENABLE_DEBUG_HASHMAP - if (i->idx == IDX_FIRST) { - i->put_count = h->debug.put_count; - i->rem_count = h->debug.rem_count; - } else { - /* While iterating, must not add any new entries */ - assert(i->put_count == h->debug.put_count); - /* ... or remove entries other than the current one */ - assert(i->rem_count == h->debug.rem_count || - (i->rem_count == h->debug.rem_count - 1 && - i->prev_idx == h->debug.last_rem_idx)); - /* Reset our removals counter */ - i->rem_count = h->debug.rem_count; - } -#endif - - return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) - : hashmap_iterate_in_internal_order(h, i); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = hashmap_iterate_entry(h, i); - if (idx == IDX_NIL) { - if (value) - *value = NULL; - if (key) - *key = NULL; - - return false; - } - - e = bucket_at(h, idx); - data = entry_value(h, e); - if (value) - *value = data; - if (key) - *key = e->key; - - return true; -} - -bool set_iterate(Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL); -} - -#define HASHMAP_FOREACH_IDX(idx, h, i) \ - for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ - (idx != IDX_NIL); \ - (idx) = hashmap_iterate_entry((h), &(i))) - -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) { - IteratedCache *cache; - - assert(h); - assert(!h->cached); - - if (h->cached) - return NULL; - - cache = new0(IteratedCache, 1); - if (!cache) - return NULL; - - cache->hashmap = h; - h->cached = true; - - return cache; -} - -static void reset_direct_storage(HashmapBase *h) { - const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; - void *p; - - assert(!h->has_indirect); - - p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); - memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); -} - -static bool use_pool(void) { - static int b = -1; - - if (!is_main_thread()) - return false; - - if (b < 0) - b = getenv_bool("SYSTEMD_MEMPOOL") != 0; - - return b; -} - -static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *h; - const struct hashmap_type_info *hi = &hashmap_type_info[type]; - bool up; - - up = use_pool(); - - h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); - if (!h) - return NULL; - - h->type = type; - h->from_pool = up; - h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; - - if (type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*)h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - reset_direct_storage(h); - - if (!shared_hash_key_initialized) { - random_bytes(shared_hash_key, sizeof(shared_hash_key)); - shared_hash_key_initialized= true; - } - -#if ENABLE_DEBUG_HASHMAP - h->debug.func = func; - h->debug.file = file; - h->debug.line = line; - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - return h; -} - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, - enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *q; - - assert(h); - - if (*h) - return 0; - - q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); - if (!q) - return -ENOMEM; - - *h = q; - return 0; -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static void hashmap_free_no_clear(HashmapBase *h) { - assert(!h->has_indirect); - assert(!h->n_direct_entries); - -#if ENABLE_DEBUG_HASHMAP - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - if (h->from_pool) { - /* Ensure that the object didn't get migrated between threads. */ - assert_se(is_main_thread()); - mempool_free_tile(hashmap_type_info[h->type].mempool, h); - } else - free(h); -} - -HashmapBase *internal_hashmap_free(HashmapBase *h) { - - /* Free the hashmap, but nothing in it */ - - if (h) { - internal_hashmap_clear(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h) { - - /* Free the hashmap and all data objects in it, but not the - * keys */ - - if (h) { - internal_hashmap_clear_free(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -Hashmap *hashmap_free_free_free(Hashmap *h) { - - /* Free the hashmap and all data and key objects in it */ - - if (h) { - hashmap_clear_free_free(h); - hashmap_free_no_clear(HASHMAP_BASE(h)); - } - - return NULL; -} - -void internal_hashmap_clear(HashmapBase *h) { - if (!h) - return; - - if (h->has_indirect) { - free(h->indirect.storage); - h->has_indirect = false; - } - - h->n_direct_entries = 0; - reset_direct_storage(h); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - base_set_dirty(h); -} - -void internal_hashmap_clear_free(HashmapBase *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; - idx = skip_free_buckets(h, idx + 1)) - free(entry_value(h, bucket_at(h, idx))); - - internal_hashmap_clear(h); -} - -void hashmap_clear_free_free(Hashmap *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; - idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { - struct plain_hashmap_entry *e = plain_bucket_at(h, idx); - free((void*)e->b.key); - free(e->value); - } - - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -static int resize_buckets(HashmapBase *h, unsigned entries_add); - -/* - * Finds an empty bucket to put an entry into, starting the scan at 'idx'. - * Performs Robin Hood swaps as it goes. The entry to put must be placed - * by the caller into swap slot IDX_PUT. - * If used for in-place resizing, may leave a displaced entry in swap slot - * IDX_PUT. Caller must rehash it next. - * Returns: true if it left a displaced entry to rehash next in IDX_PUT, - * false otherwise. - */ -static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, - struct swap_entries *swap) { - dib_raw_t raw_dib, *dibs; - unsigned dib, distance; - -#if ENABLE_DEBUG_HASHMAP - h->debug.put_count++; -#endif - - dibs = dib_raw_ptr(h); - - for (distance = 0; ; distance++) { - raw_dib = dibs[idx]; - if (IN_SET(raw_dib, DIB_RAW_FREE, DIB_RAW_REHASH)) { - if (raw_dib == DIB_RAW_REHASH) - bucket_move_entry(h, swap, idx, IDX_TMP); - - if (h->has_indirect && h->indirect.idx_lowest_entry > idx) - h->indirect.idx_lowest_entry = idx; - - bucket_set_dib(h, idx, distance); - bucket_move_entry(h, swap, IDX_PUT, idx); - if (raw_dib == DIB_RAW_REHASH) { - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - return true; - } - - return false; - } - - dib = bucket_calculate_dib(h, idx, raw_dib); - - if (dib < distance) { - /* Found a wealthier entry. Go Robin Hood! */ - bucket_set_dib(h, idx, distance); - - /* swap the entries */ - bucket_move_entry(h, swap, idx, IDX_TMP); - bucket_move_entry(h, swap, IDX_PUT, idx); - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - - distance = dib; - } - - idx = next_idx(h, idx); - } -} - -/* - * Puts an entry into a hashmap, boldly - no check whether key already exists. - * The caller must place the entry (only its key and value, not link indexes) - * in swap slot IDX_PUT. - * Caller must ensure: the key does not exist yet in the hashmap. - * that resize is not needed if !may_resize. - * Returns: 1 if entry was put successfully. - * -ENOMEM if may_resize==true and resize failed with -ENOMEM. - * Cannot return -ENOMEM if !may_resize. - */ -static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, - struct swap_entries *swap, bool may_resize) { - struct ordered_hashmap_entry *new_entry; - int r; - - assert(idx < n_buckets(h)); - - new_entry = bucket_at_swap(swap, IDX_PUT); - - if (may_resize) { - r = resize_buckets(h, 1); - if (r < 0) - return r; - if (r > 0) - idx = bucket_hash(h, new_entry->p.b.key); - } - assert(n_entries(h) < n_buckets(h)); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - - new_entry->iterate_next = IDX_NIL; - new_entry->iterate_previous = lh->iterate_list_tail; - - if (lh->iterate_list_tail != IDX_NIL) { - struct ordered_hashmap_entry *old_tail; - - old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); - assert(old_tail->iterate_next == IDX_NIL); - old_tail->iterate_next = IDX_PUT; - } - - lh->iterate_list_tail = IDX_PUT; - if (lh->iterate_list_head == IDX_NIL) - lh->iterate_list_head = IDX_PUT; - } - - assert_se(hashmap_put_robin_hood(h, idx, swap) == false); - - n_entries_inc(h); -#if ENABLE_DEBUG_HASHMAP - h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); -#endif - - base_set_dirty(h); - - return 1; -} -#define hashmap_put_boldly(h, idx, swap, may_resize) \ - hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) - -/* - * Returns 0 if resize is not needed. - * 1 if successfully resized. - * -ENOMEM on allocation failure. - */ -static int resize_buckets(HashmapBase *h, unsigned entries_add) { - struct swap_entries swap; - void *new_storage; - dib_raw_t *old_dibs, *new_dibs; - const struct hashmap_type_info *hi; - unsigned idx, optimal_idx; - unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; - uint8_t new_shift; - bool rehash_next; - - assert(h); - - hi = &hashmap_type_info[h->type]; - new_n_entries = n_entries(h) + entries_add; - - /* overflow? */ - if (_unlikely_(new_n_entries < entries_add)) - return -ENOMEM; - - /* For direct storage we allow 100% load, because it's tiny. */ - if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) - return 0; - - /* - * Load factor = n/m = 1 - (1/INV_KEEP_FREE). - * From it follows: m = n + n/(INV_KEEP_FREE - 1) - */ - new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); - /* overflow? */ - if (_unlikely_(new_n_buckets < new_n_entries)) - return -ENOMEM; - - if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) - return -ENOMEM; - - old_n_buckets = n_buckets(h); - - if (_likely_(new_n_buckets <= old_n_buckets)) - return 0; - - new_shift = log2u_round_up(MAX( - new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), - 2 * sizeof(struct direct_storage))); - - /* Realloc storage (buckets and DIB array). */ - new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, - 1U << new_shift); - if (!new_storage) - return -ENOMEM; - - /* Must upgrade direct to indirect storage. */ - if (!h->has_indirect) { - memcpy(new_storage, h->direct.storage, - old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); - h->indirect.n_entries = h->n_direct_entries; - h->indirect.idx_lowest_entry = 0; - h->n_direct_entries = 0; - } - - /* Get a new hash key. If we've just upgraded to indirect storage, - * allow reusing a previously generated key. It's still a different key - * from the shared one that we used for direct storage. */ - get_hash_key(h->indirect.hash_key, !h->has_indirect); - - h->has_indirect = true; - h->indirect.storage = new_storage; - h->indirect.n_buckets = (1U << new_shift) / - (hi->entry_size + sizeof(dib_raw_t)); - - old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); - new_dibs = dib_raw_ptr(h); - - /* - * Move the DIB array to the new place, replacing valid DIB values with - * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. - * Note: Overlap is not possible, because we have at least doubled the - * number of buckets and dib_raw_t is smaller than any entry type. - */ - for (idx = 0; idx < old_n_buckets; idx++) { - assert(old_dibs[idx] != DIB_RAW_REHASH); - new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE - : DIB_RAW_REHASH; - } - - /* Zero the area of newly added entries (including the old DIB area) */ - memzero(bucket_at(h, old_n_buckets), - (n_buckets(h) - old_n_buckets) * hi->entry_size); - - /* The upper half of the new DIB array needs initialization */ - memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, - (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); - - /* Rehash entries that need it */ - n_rehashed = 0; - for (idx = 0; idx < old_n_buckets; idx++) { - if (new_dibs[idx] != DIB_RAW_REHASH) - continue; - - optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); - - /* - * Not much to do if by luck the entry hashes to its current - * location. Just set its DIB. - */ - if (optimal_idx == idx) { - new_dibs[idx] = 0; - n_rehashed++; - continue; - } - - new_dibs[idx] = DIB_RAW_FREE; - bucket_move_entry(h, &swap, idx, IDX_PUT); - /* bucket_move_entry does not clear the source */ - memzero(bucket_at(h, idx), hi->entry_size); - - do { - /* - * Find the new bucket for the current entry. This may make - * another entry homeless and load it into IDX_PUT. - */ - rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); - n_rehashed++; - - /* Did the current entry displace another one? */ - if (rehash_next) - optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); - } while (rehash_next); - } - - assert(n_rehashed == n_entries(h)); - - return 1; -} - -/* - * Finds an entry with a matching key - * Returns: index of the found entry, or IDX_NIL if not found. - */ -static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { - struct hashmap_base_entry *e; - unsigned dib, distance; - dib_raw_t *dibs = dib_raw_ptr(h); - - assert(idx < n_buckets(h)); - - for (distance = 0; ; distance++) { - if (dibs[idx] == DIB_RAW_FREE) - return IDX_NIL; - - dib = bucket_calculate_dib(h, idx, dibs[idx]); - - if (dib < distance) - return IDX_NIL; - if (dib == distance) { - e = bucket_at(h, idx); - if (h->hash_ops->compare(e->key, key) == 0) - return idx; - } - - idx = next_idx(h, idx); - } -} -#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) - -int hashmap_put(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); - if (e->value == value) - return 0; - return -EEXIST; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int set_put(Set *s, const void *key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned hash, idx; - - assert(s); - - hash = bucket_hash(s, key); - idx = bucket_scan(s, hash, key); - if (idx != IDX_NIL) - return 0; - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = key; - return hashmap_put_boldly(s, hash, &swap, true); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); -#if ENABLE_DEBUG_HASHMAP - /* Although the key is equal, the key pointer may have changed, - * and this would break our assumption for iterating. So count - * this operation as incompatible with iteration. */ - if (e->b.key != key) { - h->b.debug.put_count++; - h->b.debug.rem_count++; - h->b.debug.last_rem_idx = idx; - } -#endif - e->b.key = key; - e->value = value; - hashmap_set_dirty(h); - - return 0; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int hashmap_update(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = plain_bucket_at(h, idx); - e->value = value; - hashmap_set_dirty(h); - - return 0; -} - -void *internal_hashmap_get(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return entry_value(h, e); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **key2) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (key2) - *key2 = (void*) e->b.key; - - return e->value; -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key) { - unsigned hash; - - if (!h) - return false; - - hash = bucket_hash(h, key); - return bucket_scan(h, hash, key) != IDX_NIL; -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - void *data; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - void *data; - - if (!h) { - if (rkey) - *rkey = NULL; - return NULL; - } - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) { - if (rkey) - *rkey = NULL; - return NULL; - } - - e = plain_bucket_at(h, idx); - data = e->value; - if (rkey) - *rkey = (void*) e->b.key; - - remove_entry(h, idx); - - return data; -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx = bucket_scan(h, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(h, new_key); - if (bucket_scan(h, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(h, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned old_hash, new_hash, idx; - - if (!s) - return -ENOENT; - - old_hash = bucket_hash(s, old_key); - idx = bucket_scan(s, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(s, new_key); - if (bucket_scan(s, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(s, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = new_key; - assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); - - return 0; -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx_old, idx_new; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx_old = bucket_scan(h, old_hash, old_key); - if (idx_old == IDX_NIL) - return -ENOENT; - - old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; - - new_hash = bucket_hash(h, new_key); - idx_new = bucket_scan(h, new_hash, new_key); - if (idx_new != IDX_NIL) - if (idx_old != idx_new) { - remove_entry(h, idx_new); - /* Compensate for a possible backward shift. */ - if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) - idx_old = prev_idx(HASHMAP_BASE(h), idx_old); - assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); - } - - remove_entry(h, idx_old); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (e->value != value) - return NULL; - - remove_entry(h, idx); - - return value; -} - -static unsigned find_first_entry(HashmapBase *h) { - Iterator i = ITERATOR_FIRST; - - if (!h || !n_entries(h)) - return IDX_NIL; - - return hashmap_iterate_entry(h, &i); -} - -void *internal_hashmap_first(HashmapBase *h) { - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - return entry_value(h, bucket_at(h, idx)); -} - -void *internal_hashmap_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return (void*) e->key; -} - -void *internal_hashmap_steal_first(HashmapBase *h) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *internal_hashmap_steal_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - void *key; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - key = (void*) e->key; - remove_entry(h, idx); - - return key; -} - -unsigned internal_hashmap_size(HashmapBase *h) { - - if (!h) - return 0; - - return n_entries(h); -} - -unsigned internal_hashmap_buckets(HashmapBase *h) { - - if (!h) - return 0; - - return n_buckets(h); -} - -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { - Iterator i; - unsigned idx; - - assert(h); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); - int r; - - r = hashmap_put(h, pe->b.key, pe->value); - if (r < 0 && r != -EEXIST) - return r; - } - - return 0; -} - -int set_merge(Set *s, Set *other) { - Iterator i; - unsigned idx; - - assert(s); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct set_entry *se = set_bucket_at(other, idx); - int r; - - r = set_put(s, se->b.key); - if (r < 0) - return r; - } - - return 0; -} - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { - int r; - - assert(h); - - r = resize_buckets(h, entries_add); - if (r < 0) - return r; - - return 0; -} - -/* - * The same as hashmap_merge(), but every new item from other is moved to h. - * Keys already in h are skipped and stay in other. - * Returns: 0 on success. - * -ENOMEM on alloc failure, in which case no move has been done. - */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { - struct swap_entries swap; - struct hashmap_base_entry *e, *n; - Iterator i; - unsigned idx; - int r; - - assert(h); - - if (!other) - return 0; - - assert(other->type == h->type); - - /* - * This reserves buckets for the worst case, where none of other's - * entries are yet present in h. This is preferable to risking - * an allocation failure in the middle of the moving and having to - * rollback or return a partial result. - */ - r = resize_buckets(h, n_entries(other)); - if (r < 0) - return r; - - HASHMAP_FOREACH_IDX(idx, other, i) { - unsigned h_hash; - - e = bucket_at(other, idx); - h_hash = bucket_hash(h, e->key); - if (bucket_scan(h, h_hash, e->key) != IDX_NIL) - continue; - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); - - remove_entry(other, idx); - } - - return 0; -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { - struct swap_entries swap; - unsigned h_hash, other_hash, idx; - struct hashmap_base_entry *e, *n; - int r; - - assert(h); - - h_hash = bucket_hash(h, key); - if (bucket_scan(h, h_hash, key) != IDX_NIL) - return -EEXIST; - - if (!other) - return -ENOENT; - - assert(other->type == h->type); - - other_hash = bucket_hash(other, key); - idx = bucket_scan(other, other_hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = bucket_at(other, idx); - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - r = hashmap_put_boldly(h, h_hash, &swap, true); - if (r < 0) - return r; - - remove_entry(other, idx); - return 0; -} - -HashmapBase *internal_hashmap_copy(HashmapBase *h) { - HashmapBase *copy; - int r; - - assert(h); - - copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); - if (!copy) - return NULL; - - switch (h->type) { - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); - break; - case HASHMAP_TYPE_SET: - r = set_merge((Set*)copy, (Set*)h); - break; - default: - assert_not_reached("Unknown hashmap type"); - } - - if (r < 0) { - internal_hashmap_free(copy); - return NULL; - } - - return copy; -} - -char **internal_hashmap_get_strv(HashmapBase *h) { - char **sv; - Iterator i; - unsigned idx, n; - - sv = new(char*, n_entries(h)+1); - if (!sv) - return NULL; - - n = 0; - HASHMAP_FOREACH_IDX(idx, h, i) - sv[n++] = entry_value(h, bucket_at(h, idx)); - sv[n] = NULL; - - return sv; -} - -void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { - struct ordered_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = ordered_bucket_at(h, idx); - if (e->iterate_next == IDX_NIL) - return NULL; - return ordered_bucket_at(h, e->iterate_next)->p.value; -} - -int set_consume(Set *s, void *value) { - int r; - - assert(s); - assert(value); - - r = set_put(s, value); - if (r <= 0) - free(value); - - return r; -} - -int set_put_strdup(Set *s, const char *p) { - char *c; - - assert(s); - assert(p); - - if (set_contains(s, (char*) p)) - return 0; - - c = strdup(p); - if (!c) - return -ENOMEM; - - return set_consume(s, c); -} - -int set_put_strdupv(Set *s, char **l) { - int n = 0, r; - char **i; - - assert(s); - - STRV_FOREACH(i, l) { - r = set_put_strdup(s, *i); - if (r < 0) - return r; - - n += r; - } - - return n; -} - -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { - const char *p = v; - int r; - - assert(s); - assert(v); - - for (;;) { - char *word; - - r = extract_first_word(&p, &word, separators, flags); - if (r <= 0) - return r; - - r = set_consume(s, word); - if (r < 0) - return r; - } -} - -/* expand the cachemem if needed, return true if newly (re)activated. */ -static int cachemem_maintain(CacheMem *mem, unsigned size) { - assert(mem); - - if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) { - if (size > 0) - return -ENOMEM; - } - - if (!mem->active) { - mem->active = true; - return true; - } - - return false; -} - -int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) { - bool sync_keys = false, sync_values = false; - unsigned size; - int r; - - assert(cache); - assert(cache->hashmap); - - size = n_entries(cache->hashmap); - - if (res_keys) { - r = cachemem_maintain(&cache->keys, size); - if (r < 0) - return r; - - sync_keys = r; - } else - cache->keys.active = false; - - if (res_values) { - r = cachemem_maintain(&cache->values, size); - if (r < 0) - return r; - - sync_values = r; - } else - cache->values.active = false; - - if (cache->hashmap->dirty) { - if (cache->keys.active) - sync_keys = true; - if (cache->values.active) - sync_values = true; - - cache->hashmap->dirty = false; - } - - if (sync_keys || sync_values) { - unsigned i, idx; - Iterator iter; - - i = 0; - HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) { - struct hashmap_base_entry *e; - - e = bucket_at(cache->hashmap, idx); - - if (sync_keys) - cache->keys.ptr[i] = e->key; - if (sync_values) - cache->values.ptr[i] = entry_value(cache->hashmap, e); - i++; - } - } - - if (res_keys) - *res_keys = cache->keys.ptr; - if (res_values) - *res_values = cache->values.ptr; - if (res_n_entries) - *res_n_entries = size; - - return 0; -} - -IteratedCache *iterated_cache_free(IteratedCache *cache) { - if (cache) { - free(cache->keys.ptr); - free(cache->values.ptr); - free(cache); - } - - return NULL; -} diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h deleted file mode 100644 index 274afb39..00000000 --- a/src/systemd/src/basic/hashmap.h +++ /dev/null @@ -1,393 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <limits.h> -#include <stdbool.h> -#include <stddef.h> - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -/* - * A hash table implementation. As a minor optimization a NULL hashmap object - * will be treated as empty hashmap for all read operations. That way it is not - * necessary to instantiate an object for each Hashmap use. - * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), - * the implemention will: - * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) - * - perform extra checks for invalid use of iterators - */ - -#define HASH_KEY_SIZE 16 - -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ -typedef struct HashmapBase HashmapBase; - -/* Specific hashmap/set types */ -typedef struct Hashmap Hashmap; /* Maps keys to values */ -typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ -typedef struct Set Set; /* Stores just keys */ - -typedef struct IteratedCache IteratedCache; /* Caches the iterated order of one of the above */ - -/* Ideally the Iterator would be an opaque struct, but it is instantiated - * by hashmap users, so the definition has to be here. Do not use its fields - * directly. */ -typedef struct { - unsigned idx; /* index of an entry to be iterated next */ - const void *next_key; /* expected value of that entry's key pointer */ -#if ENABLE_DEBUG_HASHMAP - unsigned put_count; /* hashmap's put_count recorded at start of iteration */ - unsigned rem_count; /* hashmap's rem_count in previous iteration */ - unsigned prev_idx; /* idx in previous iteration */ -#endif -} Iterator; - -#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) -#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) - -/* Macros for type checking */ -#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ - (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ - __builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ - __builtin_types_compatible_p(typeof(h), Set*)) - -#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ - (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ - -#define HASHMAP_BASE(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ - (HashmapBase*)(h), \ - (void)0) - -#define PLAIN_HASHMAP(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ - (Hashmap*)(h), \ - (void)0) - -#if ENABLE_DEBUG_HASHMAP -# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line -# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ -# define HASHMAP_DEBUG_PASS_ARGS , func, file, line -#else -# define HASHMAP_DEBUG_PARAMS -# define HASHMAP_DEBUG_SRC_ARGS -# define HASHMAP_DEBUG_PASS_ARGS -#endif - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) - -HashmapBase *internal_hashmap_free(HashmapBase *h); -static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h); -static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} - -Hashmap *hashmap_free_free_free(Hashmap *h); -static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); -} - -IteratedCache *iterated_cache_free(IteratedCache *cache); -int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); - -HashmapBase *internal_hashmap_copy(HashmapBase *h); -static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h); -static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); -} -static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); -} - -int hashmap_put(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { - return hashmap_put(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_update(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { - return hashmap_update(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { - return hashmap_replace(PLAIN_HASHMAP(h), key, value); -} - -void *internal_hashmap_get(HashmapBase *h, const void *key); -static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key); -static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} -static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key); -static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value); -static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { - return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa - * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) -#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); -static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} -static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); -/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ -static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} -static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); -static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} -static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} - -unsigned internal_hashmap_size(HashmapBase *h) _pure_; -static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} - -static inline bool hashmap_isempty(Hashmap *h) { - return hashmap_size(h) == 0; -} -static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { - return ordered_hashmap_size(h) == 0; -} - -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; -static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); -static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} -static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} - -void internal_hashmap_clear(HashmapBase *h); -static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -void internal_hashmap_clear_free(HashmapBase *h); -static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} - -void hashmap_clear_free_free(Hashmap *h); -static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - hashmap_clear_free_free(PLAIN_HASHMAP(h)); -} - -/* - * Note about all *_first*() functions - * - * For plain Hashmaps and Sets the order of entries is undefined. - * The functions find whatever entry is first in the implementation - * internal order. - * - * Only for OrderedHashmaps the order is well defined and finding - * the first entry is O(1). - */ - -void *internal_hashmap_steal_first(HashmapBase *h); -static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} - -void *internal_hashmap_steal_first_key(HashmapBase *h); -static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first_key(HashmapBase *h) _pure_; -static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first(HashmapBase *h) _pure_; -static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} - -#define hashmap_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define hashmap_free_with_destructor(_s, _f) \ - ({ \ - hashmap_clear_with_destructor(_s, _f); \ - hashmap_free(_s); \ - }) -#define ordered_hashmap_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = ordered_hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define ordered_hashmap_free_with_destructor(_s, _f) \ - ({ \ - ordered_hashmap_clear_with_destructor(_s, _f); \ - ordered_hashmap_free(_s); \ - }) - -/* no hashmap_next */ -void *ordered_hashmap_next(OrderedHashmap *h, const void *key); - -char **internal_hashmap_get_strv(HashmapBase *h); -static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} -static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} - -/* - * Hashmaps are iterated in unpredictable order. - * OrderedHashmaps are an exception to this. They are iterated in the order - * the entries were inserted. - * It is safe to remove the current entry. - */ -#define HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define ORDERED_HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); - -#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) -#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) -#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) -#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) -#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) -#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) - -DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free); - -#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep) diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c deleted file mode 100644 index 09f2a9e7..00000000 --- a/src/systemd/src/basic/hexdecoct.c +++ /dev/null @@ -1,821 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <ctype.h> -#include <errno.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "string-util.h" -#include "util.h" - -char octchar(int x) { - return '0' + (x & 7); -} - -int unoctchar(char c) { - - if (c >= '0' && c <= '7') - return c - '0'; - - return -EINVAL; -} - -char decchar(int x) { - return '0' + (x % 10); -} - -int undecchar(char c) { - - if (c >= '0' && c <= '9') - return c - '0'; - - return -EINVAL; -} - -char hexchar(int x) { - static const char table[16] = "0123456789abcdef"; - - return table[x & 15]; -} - -int unhexchar(char c) { - - if (c >= '0' && c <= '9') - return c - '0'; - - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - return -EINVAL; -} - -char *hexmem(const void *p, size_t l) { - const uint8_t *x; - char *r, *z; - - z = r = new(char, l * 2 + 1); - if (!r) - return NULL; - - for (x = p; x < (const uint8_t*) p + l; x++) { - *(z++) = hexchar(*x >> 4); - *(z++) = hexchar(*x & 15); - } - - *z = 0; - return r; -} - -static int unhex_next(const char **p, size_t *l) { - int r; - - assert(p); - assert(l); - - /* Find the next non-whitespace character, and decode it. We - * greedily skip all preceeding and all following whitespace. */ - - for (;;) { - if (*l == 0) - return -EPIPE; - - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip leading whitespace */ - (*p)++, (*l)--; - } - - r = unhexchar(**p); - if (r < 0) - return r; - - for (;;) { - (*p)++, (*l)--; - - if (*l == 0 || !strchr(WHITESPACE, **p)) - break; - - /* Skip following whitespace */ - } - - return r; -} - -int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) { - _cleanup_free_ uint8_t *buf = NULL; - const char *x; - uint8_t *z; - - assert(ret); - assert(ret_len); - assert(p || l == 0); - - if (l == (size_t) -1) - l = strlen(p); - - /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */ - buf = malloc((l + 1) / 2 + 1); - if (!buf) - return -ENOMEM; - - for (x = p, z = buf;;) { - int a, b; - - a = unhex_next(&x, &l); - if (a == -EPIPE) /* End of string */ - break; - if (a < 0) - return a; - - b = unhex_next(&x, &l); - if (b < 0) - return b; - - *(z++) = (uint8_t) a << 4 | (uint8_t) b; - } - - *z = 0; - - *ret_len = (size_t) (z - buf); - *ret = TAKE_PTR(buf); - - return 0; -} - -#if 0 /* NM_IGNORED */ -/* https://tools.ietf.org/html/rfc4648#section-6 - * Notice that base32hex differs from base32 in the alphabet it uses. - * The distinction is that the base32hex representation preserves the - * order of the underlying data when compared as bytestrings, this is - * useful when representing NSEC3 hashes, as one can then verify the - * order of hashes directly from their representation. */ -char base32hexchar(int x) { - static const char table[32] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUV"; - - return table[x & 31]; -} - -int unbase32hexchar(char c) { - unsigned offset; - - if (c >= '0' && c <= '9') - return c - '0'; - - offset = '9' - '0' + 1; - - if (c >= 'A' && c <= 'V') - return c - 'A' + offset; - - return -EINVAL; -} - -char *base32hexmem(const void *p, size_t l, bool padding) { - char *r, *z; - const uint8_t *x; - size_t len; - - assert(p || l == 0); - - if (padding) - /* five input bytes makes eight output bytes, padding is added so we must round up */ - len = 8 * (l + 4) / 5; - else { - /* same, but round down as there is no padding */ - len = 8 * l / 5; - - switch (l % 5) { - case 4: - len += 7; - break; - case 3: - len += 5; - break; - case 2: - len += 4; - break; - case 1: - len += 2; - break; - } - } - - z = r = malloc(len + 1); - if (!r) - return NULL; - - for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { - /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ - * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ - *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ - *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */ - *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */ - } - - switch (l % 5) { - case 4: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ - *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ - *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */ - if (padding) - *(z++) = '='; - - break; - - case 3: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - - case 2: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - - case 1: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - } - - *z = 0; - return r; -} - -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) { - _cleanup_free_ uint8_t *r = NULL; - int a, b, c, d, e, f, g, h; - uint8_t *z; - const char *x; - size_t len; - unsigned pad = 0; - - assert(p || l == 0); - assert(mem); - assert(_len); - - if (l == (size_t) -1) - l = strlen(p); - - /* padding ensures any base32hex input has input divisible by 8 */ - if (padding && l % 8 != 0) - return -EINVAL; - - if (padding) { - /* strip the padding */ - while (l > 0 && p[l - 1] == '=' && pad < 7) { - pad++; - l--; - } - } - - /* a group of eight input bytes needs five output bytes, in case of - * padding we need to add some extra bytes */ - len = (l / 8) * 5; - - switch (l % 8) { - case 7: - len += 4; - break; - case 5: - len += 3; - break; - case 4: - len += 2; - break; - case 2: - len += 1; - break; - case 0: - break; - default: - return -EINVAL; - } - - z = r = malloc(len + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < p + (l / 8) * 8; x += 8) { - /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW - * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - f = unbase32hexchar(x[5]); - if (f < 0) - return -EINVAL; - - g = unbase32hexchar(x[6]); - if (g < 0) - return -EINVAL; - - h = unbase32hexchar(x[7]); - if (h < 0) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ - *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */ - } - - switch (l % 8) { - case 7: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - f = unbase32hexchar(x[5]); - if (f < 0) - return -EINVAL; - - g = unbase32hexchar(x[6]); - if (g < 0) - return -EINVAL; - - /* g == 000VV000 */ - if (g & 7) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ - - break; - case 5: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - /* e == 000SSSS0 */ - if (e & 1) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - - break; - case 4: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - /* d == 000W0000 */ - if (d & 15) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - - break; - case 2: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - /* b == 000YYY00 */ - if (b & 3) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - - break; - case 0: - break; - default: - return -EINVAL; - } - - *z = 0; - - *mem = TAKE_PTR(r); - *_len = len; - - return 0; -} - -/* https://tools.ietf.org/html/rfc4648#section-4 */ -char base64char(int x) { - static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - return table[x & 63]; -} - -int unbase64char(char c) { - unsigned offset; - - if (c >= 'A' && c <= 'Z') - return c - 'A'; - - offset = 'Z' - 'A' + 1; - - if (c >= 'a' && c <= 'z') - return c - 'a' + offset; - - offset += 'z' - 'a' + 1; - - if (c >= '0' && c <= '9') - return c - '0' + offset; - - offset += '9' - '0' + 1; - - if (c == '+') - return offset; - - offset++; - - if (c == '/') - return offset; - - return -EINVAL; -} - -ssize_t base64mem(const void *p, size_t l, char **out) { - char *r, *z; - const uint8_t *x; - - assert(p || l == 0); - assert(out); - - /* three input bytes makes four output bytes, padding is added so we must round up */ - z = r = malloc(4 * (l + 2) / 3 + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { - /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ - *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ - } - - switch (l % 3) { - case 2: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ - *(z++) = '='; - - break; - case 1: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ - *(z++) = '='; - *(z++) = '='; - - break; - } - - *z = 0; - *out = r; - return z - r; -} - -static int base64_append_width( - char **prefix, int plen, - const char *sep, int indent, - const void *p, size_t l, - int width) { - - _cleanup_free_ char *x = NULL; - char *t, *s; - ssize_t slen, len, avail; - int line, lines; - - len = base64mem(p, l, &x); - if (len <= 0) - return len; - - lines = DIV_ROUND_UP(len, width); - - slen = strlen_ptr(sep); - t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); - if (!t) - return -ENOMEM; - - memcpy_safe(t + plen, sep, slen); - - for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { - int act = MIN(width, avail); - - if (line > 0 || sep) { - memset(s, ' ', indent); - s += indent; - } - - memcpy(s, x + width * line, act); - s += act; - *(s++) = line < lines - 1 ? '\n' : '\0'; - avail -= act; - } - assert(avail == 0); - - *prefix = t; - return 0; -} - -int base64_append( - char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { - - if (plen > width / 2 || plen + indent > width) - /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); - else - /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); -} - -static int unbase64_next(const char **p, size_t *l) { - int ret; - - assert(p); - assert(l); - - /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We - * greedily skip all preceeding and all following whitespace. */ - - for (;;) { - if (*l == 0) - return -EPIPE; - - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip leading whitespace */ - (*p)++, (*l)--; - } - - if (**p == '=') - ret = INT_MAX; /* return padding as INT_MAX */ - else { - ret = unbase64char(**p); - if (ret < 0) - return ret; - } - - for (;;) { - (*p)++, (*l)--; - - if (*l == 0) - break; - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip following whitespace */ - } - - return ret; -} - -int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { - _cleanup_free_ uint8_t *buf = NULL; - const char *x; - uint8_t *z; - size_t len; - - assert(p || l == 0); - assert(ret); - assert(ret_size); - - if (l == (size_t) -1) - l = strlen(p); - - /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra - * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */ - len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0); - - buf = malloc(len + 1); - if (!buf) - return -ENOMEM; - - for (x = p, z = buf;;) { - int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ - - a = unbase64_next(&x, &l); - if (a == -EPIPE) /* End of string */ - break; - if (a < 0) - return a; - if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */ - return -EINVAL; - - b = unbase64_next(&x, &l); - if (b < 0) - return b; - if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */ - return -EINVAL; - - c = unbase64_next(&x, &l); - if (c < 0) - return c; - - d = unbase64_next(&x, &l); - if (d < 0) - return d; - - if (c == INT_MAX) { /* Padding at the third character */ - - if (d != INT_MAX) /* If the third character is padding, the fourth must be too */ - return -EINVAL; - - /* b == 00YY0000 */ - if (b & 15) - return -EINVAL; - - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; - - *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ - break; - } - - if (d == INT_MAX) { - /* c == 00ZZZZ00 */ - if (c & 3) - return -EINVAL; - - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; - - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - break; - } - - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ - } - - *z = 0; - - *ret_size = (size_t) (z - buf); - *ret = TAKE_PTR(buf); - - return 0; -} - -void hexdump(FILE *f, const void *p, size_t s) { - const uint8_t *b = p; - unsigned n = 0; - - assert(b || s == 0); - - if (!f) - f = stdout; - - while (s > 0) { - size_t i; - - fprintf(f, "%04x ", n); - - for (i = 0; i < 16; i++) { - - if (i >= s) - fputs(" ", f); - else - fprintf(f, "%02x ", b[i]); - - if (i == 7) - fputc(' ', f); - } - - fputc(' ', f); - - for (i = 0; i < 16; i++) { - - if (i >= s) - fputc(' ', f); - else - fputc(isprint(b[i]) ? (char) b[i] : '.', f); - } - - fputc('\n', f); - - if (s < 16) - break; - - n += 16; - b += 16; - s -= 16; - } -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hexdecoct.h b/src/systemd/src/basic/hexdecoct.h deleted file mode 100644 index 9477d16e..00000000 --- a/src/systemd/src/basic/hexdecoct.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" - -char octchar(int x) _const_; -int unoctchar(char c) _const_; - -char decchar(int x) _const_; -int undecchar(char c) _const_; - -char hexchar(int x) _const_; -int unhexchar(char c) _const_; - -char *hexmem(const void *p, size_t l); -int unhexmem(const char *p, size_t l, void **mem, size_t *len); - -char base32hexchar(int x) _const_; -int unbase32hexchar(char c) _const_; - -char base64char(int x) _const_; -int unbase64char(char c) _const_; - -char *base32hexmem(const void *p, size_t l, bool padding); -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len); - -ssize_t base64mem(const void *p, size_t l, char **out); -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int margin, int width); -int unbase64mem(const char *p, size_t l, void **mem, size_t *len); - -void hexdump(FILE *f, const void *p, size_t s); diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c deleted file mode 100644 index 85708394..00000000 --- a/src/systemd/src/basic/hostname-util.c +++ /dev/null @@ -1,295 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <string.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "def.h" -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "macro.h" -#include "string-util.h" - -#if 0 /* NM_IGNORED */ -bool hostname_is_set(void) { - struct utsname u; - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return false; - - /* This is the built-in kernel default host name */ - if (streq(u.nodename, "(none)")) - return false; - - return true; -} - -char* gethostname_malloc(void) { - struct utsname u; - - /* This call tries to return something useful, either the actual hostname - * or it makes something up. The only reason it might fail is OOM. - * It might even return "localhost" if that's set. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(FALLBACK_HOSTNAME); - - return strdup(u.nodename); -} -#endif /* NM_IGNORED */ - -int gethostname_strict(char **ret) { - struct utsname u; - char *k; - - /* This call will rather fail than make up a name. It will not return "localhost" either. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return -ENXIO; - - if (streq(u.nodename, "(none)")) - return -ENXIO; - - if (is_localhost(u.nodename)) - return -ENXIO; - - k = strdup(u.nodename); - if (!k) - return -ENOMEM; - - *ret = k; - return 0; -} - -static bool hostname_valid_char(char c) { - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - IN_SET(c, '-', '_', '.'); -} - -/** - * Check if s looks like a valid host name or FQDN. This does not do - * full DNS validation, but only checks if the name is composed of - * allowed characters and the length is not above the maximum allowed - * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * allow_trailing_dot is true and at least two components are present - * in the name. Note that due to the restricted charset and length - * this call is substantially more conservative than - * dns_name_is_valid(). - */ -bool hostname_is_valid(const char *s, bool allow_trailing_dot) { - unsigned n_dots = 0; - const char *p; - bool dot; - - if (isempty(s)) - return false; - - /* Doesn't accept empty hostnames, hostnames with - * 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) - return false; - - dot = true; - n_dots++; - } else { - if (!hostname_valid_char(*p)) - return false; - - dot = false; - } - } - - if (dot && (n_dots < 2 || !allow_trailing_dot)) - return false; - - if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on - * Linux, but DNS allows domain names - * up to 255 characters */ - return false; - - return true; -} - -char* hostname_cleanup(char *s) { - char *p, *d; - bool dot; - - assert(s); - - strshorten(s, HOST_NAME_MAX); - - for (p = s, d = s, dot = true; *p; p++) { - if (*p == '.') { - if (dot) - continue; - - *(d++) = '.'; - dot = true; - } else if (hostname_valid_char(*p)) { - *(d++) = *p; - dot = false; - } - } - - if (dot && d > s) - d[-1] = 0; - else - *d = 0; - - return s; -} - -bool is_localhost(const char *hostname) { - assert(hostname); - - /* This tries to identify local host and domain names - * described in RFC6761 plus the redhatism of localdomain */ - - return strcaseeq(hostname, "localhost") || - strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localhost.localdomain") || - strcaseeq(hostname, "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); -} - -#if 0 /* NM_IGNORED */ -bool is_gateway_hostname(const char *hostname) { - assert(hostname); - - /* This tries to identify the valid syntaxes for the our - * synthetic "gateway" host. */ - - return - strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.") -#if ENABLE_COMPAT_GATEWAY_HOSTNAME - || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.") -#endif - ; -} - -int sethostname_idempotent(const char *s) { - char buf[HOST_NAME_MAX + 1] = {}; - - assert(s); - - if (gethostname(buf, sizeof(buf)) < 0) - return -errno; - - if (streq(buf, s)) - return 0; - - if (sethostname(s, strlen(s)) < 0) - return -errno; - - return 1; -} - -int shorten_overlong(const char *s, char **ret) { - char *h, *p; - - /* Shorten an overlong name to HOST_NAME_MAX or to the first dot, - * whatever comes earlier. */ - - assert(s); - - h = strdup(s); - if (!h) - return -ENOMEM; - - if (hostname_is_valid(h, false)) { - *ret = h; - return 0; - } - - p = strchr(h, '.'); - if (p) - *p = 0; - - strshorten(h, HOST_NAME_MAX); - - if (!hostname_is_valid(h, false)) { - free(h); - return -EDOM; - } - - *ret = h; - return 1; -} - -int read_etc_hostname_stream(FILE *f, char **ret) { - int r; - - assert(f); - assert(ret); - - for (;;) { - _cleanup_free_ char *line = NULL; - char *p; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */ - return -ENOENT; - - p = strstrip(line); - - /* File may have empty lines or comments, ignore them */ - if (!IN_SET(*p, '\0', '#')) { - char *copy; - - hostname_cleanup(p); /* normalize the hostname */ - - if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */ - return -EBADMSG; - - copy = strdup(p); - if (!copy) - return -ENOMEM; - - *ret = copy; - return 0; - } - } -} - -int read_etc_hostname(const char *path, char **ret) { - _cleanup_fclose_ FILE *f = NULL; - - assert(ret); - - if (!path) - path = "/etc/hostname"; - - f = fopen(path, "re"); - if (!f) - return -errno; - - return read_etc_hostname_stream(f, ret); - -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h deleted file mode 100644 index 74948172..00000000 --- a/src/systemd/src/basic/hostname-util.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stdio.h> - -#include "macro.h" - -bool hostname_is_set(void); - -char* gethostname_malloc(void); -int gethostname_strict(char **ret); - -bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; -char* hostname_cleanup(char *s); - -#define machine_name_is_valid(s) hostname_is_valid(s, false) - -bool is_localhost(const char *hostname); -bool is_gateway_hostname(const char *hostname); - -int sethostname_idempotent(const char *s); - -int shorten_overlong(const char *s, char **ret); - -int read_etc_hostname_stream(FILE *f, char **ret); -int read_etc_hostname(const char *path, char **ret); diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c deleted file mode 100644 index 19d0db25..00000000 --- a/src/systemd/src/basic/in-addr-util.c +++ /dev/null @@ -1,602 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <arpa/inet.h> -#include <endian.h> -#include <errno.h> -#include <net/if.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "parse-util.h" -#include "util.h" - -bool in4_addr_is_null(const struct in_addr *a) { - assert(a); - - return a->s_addr == 0; -} - -int in_addr_is_null(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_null(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_UNSPECIFIED(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in4_addr_is_link_local(const struct in_addr *a) { - assert(a); - - return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); -} - -int in_addr_is_link_local(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_link_local(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_LINKLOCAL(&u->in6); - - return -EAFNOSUPPORT; -} - -int in_addr_is_multicast(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return IN_MULTICAST(be32toh(u->in.s_addr)); - - if (family == AF_INET6) - return IN6_IS_ADDR_MULTICAST(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in4_addr_is_localhost(const struct in_addr *a) { - assert(a); - - /* All of 127.x.x.x is localhost. */ - return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; -} - -int in_addr_is_localhost(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_localhost(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); - - return -EAFNOSUPPORT; -} - -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { - assert(a); - assert(b); - - if (family == AF_INET) - return a->in.s_addr == b->in.s_addr; - - if (family == AF_INET6) - return - a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && - a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && - a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && - a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_intersect( - int family, - const union in_addr_union *a, - unsigned aprefixlen, - const union in_addr_union *b, - unsigned bprefixlen) { - - unsigned m; - - assert(a); - assert(b); - - /* Checks whether there are any addresses that are in both - * networks */ - - m = MIN(aprefixlen, bprefixlen); - - if (family == AF_INET) { - uint32_t x, nm; - - x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); - - return (x & nm) == 0; - } - - if (family == AF_INET6) { - unsigned i; - - if (m > 128) - m = 128; - - for (i = 0; i < 16; i++) { - uint8_t x, nm; - - x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; - - if (m < 8) - nm = 0xFF << (8 - m); - else - nm = 0xFF; - - if ((x & nm) != 0) - return 0; - - if (m > 8) - m -= 8; - else - m = 0; - } - - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { - assert(u); - - /* Increases the network part of an address by one. Returns - * positive it that succeeds, or 0 if this overflows. */ - - if (prefixlen <= 0) - return 0; - - if (family == AF_INET) { - uint32_t c, n; - - if (prefixlen > 32) - prefixlen = 32; - - c = be32toh(u->in.s_addr); - n = c + (1UL << (32 - prefixlen)); - if (n < c) - return 0; - n &= 0xFFFFFFFFUL << (32 - prefixlen); - - u->in.s_addr = htobe32(n); - return 1; - } - - if (family == AF_INET6) { - struct in6_addr add = {}, result; - uint8_t overflow = 0; - unsigned i; - - if (prefixlen > 128) - prefixlen = 128; - - /* First calculate what we have to add */ - add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); - - for (i = 16; i > 0; i--) { - unsigned j = i - 1; - - result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; - overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); - } - - if (overflow) - return 0; - - u->in6 = result; - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { - char *x; - size_t l; - - assert(u); - assert(ret); - - if (family == AF_INET) - l = INET_ADDRSTRLEN; - else if (family == AF_INET6) - l = INET6_ADDRSTRLEN; - else - return -EAFNOSUPPORT; - - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - *ret = x; - return 0; -} - -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { - size_t l; - char *x; - int r; - - assert(u); - assert(ret); - - /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly - * handle IPv6 link-local addresses. */ - - if (family != AF_INET6) - goto fallback; - if (ifindex <= 0) - goto fallback; - - r = in_addr_is_link_local(family, u); - if (r < 0) - return r; - if (r == 0) - goto fallback; - - l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - sprintf(strchr(x, 0), "%%%i", ifindex); - *ret = x; - - return 0; - -fallback: - return in_addr_to_string(family, u, ret); -} - -int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { - union in_addr_union buffer; - assert(s); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - errno = 0; - if (inet_pton(family, s, ret ?: &buffer) <= 0) - return errno > 0 ? -errno : -EINVAL; - - return 0; -} - -int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret) { - int r; - - assert(s); - - r = in_addr_from_string(AF_INET, s, ret); - if (r >= 0) { - if (ret_family) - *ret_family = AF_INET; - return 0; - } - - r = in_addr_from_string(AF_INET6, s, ret); - if (r >= 0) { - if (ret_family) - *ret_family = AF_INET6; - return 0; - } - - return -EINVAL; -} - -#if 0 /* NM_IGNORED */ -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { - const char *suffix; - int r, ifi = 0; - - assert(s); - assert(family); - assert(ret); - - /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") - * if one is found. */ - - suffix = strchr(s, '%'); - if (suffix) { - - if (ifindex) { - /* If we shall return the interface index, try to parse it */ - r = parse_ifindex(suffix + 1, &ifi); - if (r < 0) { - unsigned u; - - u = if_nametoindex(suffix + 1); - if (u <= 0) - return -errno; - - ifi = (int) u; - } - } - - s = strndupa(s, suffix - s); - } - - r = in_addr_from_string_auto(s, family, ret); - if (r < 0) - return r; - - if (ifindex) - *ifindex = ifi; - - return r; -} -#endif /* NM_IGNORED */ - -unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { - assert(addr); - - return 32 - u32ctz(be32toh(addr->s_addr)); -} - -struct in_addr* in4_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 in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { - uint8_t msb_octet = *(uint8_t*) addr; - - /* addr may not be aligned, so make sure we only access it byte-wise */ - - assert(addr); - assert(prefixlen); - - if (msb_octet < 128) - /* class A, leading bits: 0 */ - *prefixlen = 8; - else if (msb_octet < 192) - /* class B, leading bits 10 */ - *prefixlen = 16; - else if (msb_octet < 224) - /* class C, leading bits 110 */ - *prefixlen = 24; - else - /* class D or E, no default prefixlen */ - return -ERANGE; - - return 0; -} - -int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { - unsigned char prefixlen; - int r; - - assert(addr); - assert(mask); - - r = in4_addr_default_prefixlen(addr, &prefixlen); - if (r < 0) - return r; - - in4_addr_prefixlen_to_netmask(mask, prefixlen); - return 0; -} - -#if 0 /* NM_IGNORED */ -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { - assert(addr); - - if (family == AF_INET) { - struct in_addr mask; - - if (!in4_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; -} - -int in_addr_prefix_covers(int family, - const union in_addr_union *prefix, - unsigned char prefixlen, - const union in_addr_union *address) { - - union in_addr_union masked_prefix, masked_address; - int r; - - assert(prefix); - assert(address); - - masked_prefix = *prefix; - r = in_addr_mask(family, &masked_prefix, prefixlen); - if (r < 0) - return r; - - masked_address = *address; - r = in_addr_mask(family, &masked_address, prefixlen); - if (r < 0) - return r; - - return in_addr_equal(family, &masked_prefix, &masked_address); -} - -int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { - uint8_t u; - int r; - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - r = safe_atou8(p, &u); - if (r < 0) - return r; - - if (u > FAMILY_ADDRESS_SIZE(family) * 8) - return -ERANGE; - - *ret = u; - return 0; -} - -int in_addr_prefix_from_string( - const char *p, - int family, - union in_addr_union *ret_prefix, - unsigned char *ret_prefixlen) { - - union in_addr_union buffer; - const char *e, *l; - unsigned char k; - int r; - - assert(p); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - e = strchr(p, '/'); - if (e) - l = strndupa(p, e - p); - else - l = p; - - r = in_addr_from_string(family, l, &buffer); - if (r < 0) - return r; - - if (e) { - r = in_addr_parse_prefixlen(family, e+1, &k); - if (r < 0) - return r; - } else - k = FAMILY_ADDRESS_SIZE(family) * 8; - - if (ret_prefix) - *ret_prefix = buffer; - if (ret_prefixlen) - *ret_prefixlen = k; - - return 0; -} - -int in_addr_prefix_from_string_auto( - const char *p, - int *ret_family, - union in_addr_union *ret_prefix, - unsigned char *ret_prefixlen) { - - union in_addr_union buffer; - const char *e, *l; - unsigned char k; - int family, r; - - assert(p); - - e = strchr(p, '/'); - if (e) - l = strndupa(p, e - p); - else - l = p; - - r = in_addr_from_string_auto(l, &family, &buffer); - if (r < 0) - return r; - - if (e) { - r = in_addr_parse_prefixlen(family, e+1, &k); - if (r < 0) - return r; - } else - k = FAMILY_ADDRESS_SIZE(family) * 8; - - if (ret_family) - *ret_family = family; - if (ret_prefix) - *ret_prefix = buffer; - if (ret_prefixlen) - *ret_prefixlen = k; - - return 0; - -} - -void in_addr_data_hash_func(const void *p, struct siphash *state) { - const struct in_addr_data *a = p; - - siphash24_compress(&a->family, sizeof(a->family), state); - siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); -} - -int in_addr_data_compare_func(const void *a, const void *b) { - const struct in_addr_data *x = a, *y = b; - int r; - - r = CMP(x->family, y->family); - if (r != 0) - return r; - - return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); -} - -const struct hash_ops in_addr_data_hash_ops = { - .hash = in_addr_data_hash_func, - .compare = in_addr_data_compare_func, -}; -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h deleted file mode 100644 index e4be30dc..00000000 --- a/src/systemd/src/basic/in-addr-util.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <netinet/in.h> -#include <stddef.h> -#include <sys/socket.h> - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -union in_addr_union { - struct in_addr in; - struct in6_addr in6; -}; - -struct in_addr_data { - int family; - union in_addr_union address; -}; - -bool in4_addr_is_null(const struct in_addr *a); -int in_addr_is_null(int family, const union in_addr_union *u); - -int in_addr_is_multicast(int family, const union in_addr_union *u); - -bool in4_addr_is_link_local(const struct in_addr *a); -int in_addr_is_link_local(int family, const union in_addr_union *u); - -bool in4_addr_is_localhost(const struct in_addr *a); -int in_addr_is_localhost(int family, const union in_addr_union *u); - -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); -int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); -int in_addr_to_string(int family, const union in_addr_union *u, char **ret); -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, 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 *ret_family, union in_addr_union *ret); -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); -unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); -struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); -int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -int in4_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); -int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); -int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); -int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); -int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); - -static inline size_t FAMILY_ADDRESS_SIZE(int family) { - assert(IN_SET(family, AF_INET, AF_INET6)); - return family == AF_INET6 ? 16 : 4; -} - -#define IN_ADDR_NULL ((union in_addr_union) {}) - -void in_addr_data_hash_func(const void *p, struct siphash *state); -int in_addr_data_compare_func(const void *a, const void *b); -extern const struct hash_ops in_addr_data_hash_ops; diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c deleted file mode 100644 index 6f46c944..00000000 --- a/src/systemd/src/basic/io-util.c +++ /dev/null @@ -1,256 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <poll.h> -#include <stdio.h> -#include <time.h> -#include <unistd.h> - -#include "io-util.h" -#include "time-util.h" - -int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - int count = 0; - - /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything - * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read - * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used - * was set to non-blocking too. */ - - for (;;) { - char buf[LINE_MAX]; - ssize_t l; - int r; - - r = poll(&pollfd, 1, 0); - if (r < 0) { - if (errno == EINTR) - continue; - - return -errno; - - } else if (r == 0) - return count; - - l = read(fd, buf, sizeof(buf)); - if (l < 0) { - - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return count; - - return -errno; - } else if (l == 0) - return count; - - count += (int) l; - } -} - -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { - uint8_t *p = buf; - ssize_t n = 0; - - assert(fd >= 0); - assert(buf); - - /* If called with nbytes == 0, let's call read() at least - * once, to validate the operation */ - - if (nbytes > (size_t) SSIZE_MAX) - return -EINVAL; - - do { - ssize_t k; - - k = read(fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN && do_poll) { - - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ - - (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY); - continue; - } - - return n > 0 ? n : -errno; - } - - if (k == 0) - return n; - - assert((size_t) k <= nbytes); - - p += k; - nbytes -= k; - n += k; - } while (nbytes > 0); - - return n; -} - -int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { - ssize_t n; - - n = loop_read(fd, buf, nbytes, do_poll); - if (n < 0) - return (int) n; - if ((size_t) n != nbytes) - return -EIO; - - return 0; -} - -int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { - const uint8_t *p = buf; - - assert(fd >= 0); - assert(buf); - - if (_unlikely_(nbytes > (size_t) SSIZE_MAX)) - return -EINVAL; - - do { - ssize_t k; - - k = write(fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN && do_poll) { - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via write() */ - - (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); - continue; - } - - return -errno; - } - - if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */ - return -EIO; - - assert((size_t) k <= nbytes); - - p += k; - nbytes -= k; - } while (nbytes > 0); - - return 0; -} - -int pipe_eof(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN|POLLHUP, - }; - - int r; - - r = poll(&pollfd, 1, 0); - if (r < 0) - return -errno; - - if (r == 0) - return 0; - - return pollfd.revents & POLLHUP; -} - -int fd_wait_for_event(int fd, int event, usec_t t) { - - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - - struct timespec ts; - int r; - - r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL); - if (r < 0) - return -errno; - if (r == 0) - return 0; - - return pollfd.revents; -} - -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; -} diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h deleted file mode 100644 index ed189b58..00000000 --- a/src/systemd/src/basic/io-util.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#include <sys/uio.h> - -#include "macro.h" -#include "time-util.h" - -int flush_fd(int fd); - -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); - -int pipe_eof(int fd); - -int fd_wait_for_event(int fd, int event, usec_t timeout); - -ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); - -static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) { - size_t j, r = 0; - - for (j = 0; j < n; j++) - r += i[j].iov_len; - - return r; -} - -static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) { - size_t j; - - for (j = 0; j < n; j++) { - size_t sub; - - if (_unlikely_(k <= 0)) - break; - - sub = MIN(i[j].iov_len, k); - i[j].iov_len -= sub; - i[j].iov_base = (uint8_t*) i[j].iov_base + sub; - k -= sub; - } - - return k; -} - -static inline bool FILE_SIZE_VALID(uint64_t l) { - /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than - * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */ - - return (l >> 63) == 0; -} - -static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { - - /* Same as above, but allows one extra value: -1 as indication for infinity. */ - - if (l == (uint64_t) -1) - return true; - - return FILE_SIZE_VALID(l); - -} - -#define IOVEC_INIT(base, len) { .iov_base = (base), .iov_len = (len) } -#define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len) -#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string)) -#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string) diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h deleted file mode 100644 index 643e0bea..00000000 --- a/src/systemd/src/basic/list.h +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -/* The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define LIST_HEAD(t,name) \ - t *name - -/* The pointers in the linked list's items. Use this in the item structure */ -#define LIST_FIELDS(t,name) \ - t *name##_next, *name##_prev - -/* Initialize the list's head */ -#define LIST_HEAD_INIT(head) \ - do { \ - (head) = NULL; } \ - while (false) - -/* Initialize a list item */ -#define LIST_INIT(name,item) \ - do { \ - typeof(*(item)) *_item = (item); \ - assert(_item); \ - _item->name##_prev = _item->name##_next = NULL; \ - } while (false) - -/* Prepend an item to the list */ -#define LIST_PREPEND(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if ((_item->name##_next = *_head)) \ - _item->name##_next->name##_prev = _item; \ - _item->name##_prev = NULL; \ - *_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 { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if (_item->name##_next) \ - _item->name##_next->name##_prev = _item->name##_prev; \ - if (_item->name##_prev) \ - _item->name##_prev->name##_next = _item->name##_next; \ - else { \ - assert(*_head == _item); \ - *_head = _item->name##_next; \ - } \ - _item->name##_next = _item->name##_prev = NULL; \ - } while (false) - -/* Find the head of the list */ -#define LIST_FIND_HEAD(name,item,head) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (head) = NULL; \ - else { \ - while (_item->name##_prev) \ - _item = _item->name##_prev; \ - (head) = _item; \ - } \ - } while (false) - -/* Find the tail of the list */ -#define LIST_FIND_TAIL(name,item,tail) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (tail) = NULL; \ - else { \ - while (_item->name##_next) \ - _item = _item->name##_next; \ - (tail) = _item; \ - } \ - } while (false) - -/* Insert an item after another one (a = where, b = what) */ -#define LIST_INSERT_AFTER(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if ((_b->name##_next = *_head)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - if ((_b->name##_next = _a->name##_next)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = _a; \ - _a->name##_next = _b; \ - } \ - } while (false) - -/* Insert an item before another one (a = where, b = what) */ -#define LIST_INSERT_BEFORE(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if (!*_head) { \ - _b->name##_next = NULL; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - typeof(*(head)) *_tail = (head); \ - while (_tail->name##_next) \ - _tail = _tail->name##_next; \ - _b->name##_next = NULL; \ - _b->name##_prev = _tail; \ - _tail->name##_next = _b; \ - } \ - } else { \ - if ((_b->name##_prev = _a->name##_prev)) \ - _b->name##_prev->name##_next = _b; \ - else \ - *_head = _b; \ - _b->name##_next = _a; \ - _a->name##_prev = _b; \ - } \ - } while (false) - -#define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ - -#define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) - -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) - -#define LIST_FOREACH_BEFORE(name,i,p) \ - for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) - -#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) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) - -#define LIST_IS_EMPTY(head) \ - (!(head)) diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h deleted file mode 100644 index 28edd572..00000000 --- a/src/systemd/src/basic/log.h +++ /dev/null @@ -1,326 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdarg.h> -#include <stdbool.h> -#include <stdlib.h> -#include <syslog.h> - -#include "macro.h" - -/* Some structures we reference but don't want to pull in headers for */ -struct iovec; -struct signalfd_siginfo; - -typedef enum LogRealm { - LOG_REALM_SYSTEMD, - LOG_REALM_UDEV, - _LOG_REALM_MAX, -} LogRealm; - -#ifndef LOG_REALM -# define LOG_REALM LOG_REALM_SYSTEMD -#endif - -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_NULL, - _LOG_TARGET_MAX, - _LOG_TARGET_INVALID = -1 -} LogTarget; - -#define LOG_REALM_PLUS_LEVEL(realm, level) \ - ((realm) << 10 | (level)) -#define LOG_REALM_REMOVE_LEVEL(realm_level) \ - ((realm_level >> 10)) - -void log_set_target(LogTarget target); -void log_set_max_level_realm(LogRealm realm, int level); -#define log_set_max_level(level) \ - log_set_max_level_realm(LOG_REALM, (level)) - -void log_set_facility(int facility); - -int log_set_target_from_string(const char *e); -int log_set_max_level_from_string_realm(LogRealm realm, const char *e); -#define log_set_max_level_from_string(e) \ - log_set_max_level_from_string_realm(LOG_REALM, (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_; -#if 0 /* NM_IGNORED */ -int log_get_max_level_realm(LogRealm realm) _pure_; -#endif /* NM_IGNORED */ -#define log_get_max_level() \ - log_get_max_level_realm(LOG_REALM) - -/* Functions below that open and close logs or configure logging based on the - * environment should not be called from library code — this is always a job - * for the application itself. - */ - -int log_open(void); -void log_close(void); -void log_forget_fds(void); - -void log_parse_environment_realm(LogRealm realm); -#define log_parse_environment() \ - log_parse_environment_realm(LOG_REALM) - -#if 0 /* NM_IGNORED */ -int log_dispatch_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra, - const char *extra_field, - char *buffer); - -int log_internal_realm( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,7); -#endif /* NM_IGNORED */ -#define log_internal(level, ...) \ - log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) - -#if 0 /* NM_IGNORED */ -int log_internalv_realm( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, - va_list ap) _printf_(6,0); -#define log_internalv(level, ...) \ - log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) - -/* Realm is fixed to LOG_REALM_SYSTEMD for those */ -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 *extra_field, - const char *extra, - const char *format, ...) _printf_(10,11); - -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( - LogRealm realm, - const char *file, - int line, - const char *func); - -int log_format_iovec( - struct iovec *iovec, - size_t iovec_len, - size_t *n, - bool newline_separator, - int error, - const char *format, - va_list ap) _printf_(6, 0); - -int log_struct_iovec_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const struct iovec *input_iovec, - size_t n_input_iovec); - -/* 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_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed(text, ...) \ - log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__) - -_noreturn_ void log_assert_failed_unreachable_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed_unreachable(text, ...) \ - log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__) - -void log_assert_failed_return_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed_return(text, ...) \ - log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__) - -#define log_dispatch(level, error, buffer) \ - log_dispatch_internal(level, error, __FILE__, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer) -#endif /* NM_IGNORED */ - -/* Logging with level */ -#define log_full_errno_realm(realm, level, error, ...) \ - ({ \ - int _level = (level), _e = (error), _realm = (realm); \ - (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \ - ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \ - __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#define log_full_errno(level, error, ...) \ - log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__) - -#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__) - -int log_emergency_level(void); - -/* 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(log_emergency_level(), __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(log_emergency_level(), 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_errno(level, error, ...) \ - log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, __FILE__, __LINE__, __func__, __VA_ARGS__, NULL) -#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) - -#define log_struct_iovec_errno(level, error, iovec, n_iovec) \ - log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, __FILE__, __LINE__, __func__, iovec, n_iovec) -#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec) - -/* This modifies the buffer passed! */ -#define log_dump(level, buffer) \ - log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - 0, __FILE__, __LINE__, __func__, buffer) - -#define log_oom() log_oom_internal(LOG_REALM, __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_; - -/* Helper to prepare various field for structured logging */ -#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ - -void log_received_signal(int level, const struct signalfd_siginfo *si); - -/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */ -void log_set_upgrade_syslog_to_journal(bool b); - -/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */ -void log_set_always_reopen_console(bool b); - -/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not - * desired as we want to reuse our logging streams. It is useful however */ -void log_set_open_when_needed(bool b); - -/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to - * stderr, the console or kmsg */ -void log_set_prohibit_ipc(bool b); - -int log_dup_console(void); - -int log_syntax_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(9, 10); - -int log_syntax_invalid_utf8_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - const char *file, - int line, - const char *func, - const char *rvalue); - -#define log_syntax(unit, level, config_file, config_line, error, ...) \ - ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ - ({ \ - int _level = (level); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \ - : -EINVAL; \ - }) - -#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h deleted file mode 100644 index 22193fe3..00000000 --- a/src/systemd/src/basic/macro.h +++ /dev/null @@ -1,476 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <stdbool.h> -#include <sys/param.h> -#include <sys/sysmacros.h> -#include <sys/types.h> - -#define _printf_(a, b) __attribute__ ((format (printf, a, b))) -#ifdef __clang__ -# define _alloc_(...) -#else -# define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) -#endif -#define _sentinel_ __attribute__ ((sentinel)) -#define _unused_ __attribute__ ((unused)) -#define _destructor_ __attribute__ ((destructor)) -#define _pure_ __attribute__ ((pure)) -#define _const_ __attribute__ ((const)) -#define _deprecated_ __attribute__ ((deprecated)) -#define _packed_ __attribute__ ((packed)) -#define _malloc_ __attribute__ ((malloc)) -#define _weak_ __attribute__ ((weak)) -#define _likely_(x) (__builtin_expect(!!(x), 1)) -#define _unlikely_(x) (__builtin_expect(!!(x), 0)) -#define _public_ __attribute__ ((visibility("default"))) -#define _hidden_ __attribute__ ((visibility("hidden"))) -#define _weakref_(x) __attribute__((weakref(#x))) -#define _alignas_(x) __attribute__((aligned(__alignof(x)))) -#define _cleanup_(x) __attribute__((cleanup(x))) -#if __GNUC__ >= 7 -#define _fallthrough_ __attribute__((fallthrough)) -#else -#define _fallthrough_ -#endif -/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc - * compiler versions */ -#ifndef _noreturn_ -#if __STDC_VERSION__ >= 201112L -#define _noreturn_ _Noreturn -#else -#define _noreturn_ __attribute__((noreturn)) -#endif -#endif - -#if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# if defined(__has_feature) -# if __has_feature(memory_sanitizer) -# define HAS_FEATURE_MEMORY_SANITIZER 1 -# endif -# endif -# if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# define HAS_FEATURE_MEMORY_SANITIZER 0 -# endif -#endif - -#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) -/* Temporarily disable some warnings */ -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") - -#define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") - -#define DISABLE_WARNING_MISSING_PROTOTYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") - -#define DISABLE_WARNING_NONNULL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wnonnull\"") - -#define DISABLE_WARNING_SHADOW \ - _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") -#else -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT -#define DISABLE_WARNING_FORMAT_NONLITERAL -#define DISABLE_WARNING_MISSING_PROTOTYPES -#define DISABLE_WARNING_NONNULL -#define DISABLE_WARNING_SHADOW -#define REENABLE_WARNING -#endif - -/* automake test harness */ -#define EXIT_TEST_SKIP 77 - -#define XSTRINGIFY(x) #x -#define STRINGIFY(x) XSTRINGIFY(x) - -#define XCONCATENATE(x, y) x ## y -#define CONCATENATE(x, y) XCONCATENATE(x, y) - -#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) -#define UNIQ __COUNTER__ - -/* builtins */ -#if __SIZEOF_INT__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffs(x); -#elif __SIZEOF_LONG__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffsl(x); -#else -#error "neither int nor long are four bytes long?!?" -#endif - -/* Rounds up */ - -#define ALIGN4(l) (((l) + 3) & ~3) -#define ALIGN8(l) (((l) + 7) & ~7) - -#if __SIZEOF_POINTER__ == 8 -#define ALIGN(l) ALIGN8(l) -#elif __SIZEOF_POINTER__ == 4 -#define ALIGN(l) ALIGN4(l) -#else -#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))) - -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))) - -/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ -static inline unsigned long ALIGN_POWER2(unsigned long u) { - /* clz(0) is undefined */ - if (u == 1) - return 1; - - /* left-shift overflow is undefined */ - if (__builtin_clzl(u - 1UL) < 1) - return 0; - - return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); -} - -#ifndef __COVERITY__ -# define VOID_0 ((void)0) -#else -# define VOID_0 ((void*)0) -#endif - -#define ELEMENTSOF(x) \ - (__builtin_choose_expr( \ - !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ - sizeof(x)/sizeof((x)[0]), \ - VOID_0)) - -/* - * STRLEN - return the length of a string literal, minus the trailing NUL byte. - * Contrary to strlen(), this is a constant expression. - * @x: a string literal. - */ -#define STRLEN(x) (sizeof(""x"") - 1) - -/* - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - */ -#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member) -#define __container_of(uniq, ptr, type, member) \ - ({ \ - const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \ - (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \ - }) - -#undef MAX -#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) -#define __MAX(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define CONST_MAX(_A, _B) \ - (__builtin_choose_expr( \ - __builtin_constant_p(_A) && \ - __builtin_constant_p(_B) && \ - __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ - ((_A) > (_B)) ? (_A) : (_B), \ - VOID_0)) - -/* takes two types and returns the size of the larger one */ -#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) - -#define MAX3(x, y, z) \ - ({ \ - const typeof(x) _c = MAX(x, y); \ - MAX(_c, z); \ - }) - -#undef MIN -#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) -#define __MIN(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -#define MIN3(x, y, z) \ - ({ \ - const typeof(x) _c = MIN(x, y); \ - MIN(_c, z); \ - }) - -#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) -#define __LESS_BY(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \ - }) - -#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b)) -#define __CMP(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \ - }) - -#undef CLAMP -#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) -#define __CLAMP(xq, x, lowq, low, highq, high) \ - ({ \ - const typeof(x) UNIQ_T(X, xq) = (x); \ - const typeof(low) UNIQ_T(LOW, lowq) = (low); \ - const typeof(high) UNIQ_T(HIGH, highq) = (high); \ - UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \ - UNIQ_T(HIGH, highq) : \ - UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \ - UNIQ_T(LOW, lowq) : \ - 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) \ - ({ \ - const typeof(_x) __x = (_x); \ - const typeof(_y) __y = (_y); \ - (__x / __y + !!(__x % __y)); \ - }) - -#ifdef __COVERITY__ - -/* Use special definitions of assertion macros in order to prevent - * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer - * for uses of assert_se() and assert_return(). - * - * These definitions make expression go through a (trivial) function - * call to ensure they are not discarded. Also use ! or !! to ensure - * the boolean expressions are seen as such. - * - * This technique has been described and recommended in: - * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects - */ - -extern void __coverity_panic__(void); - -static inline int __coverity_check__(int condition) { - return condition; -} - -#define assert_message_se(expr, message) \ - do { \ - if (__coverity_check__(!(expr))) \ - __coverity_panic__(); \ - } while (false) - -#define assert_log(expr, message) __coverity_check__(!!(expr)) - -#else /* ! __COVERITY__ */ - -#define assert_message_se(expr, message) \ - do { \ - if (_unlikely_(!(expr))) \ - log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#define assert_log(expr, message) ((_likely_(expr)) \ - ? (true) \ - : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false)) - -#endif /* __COVERITY__ */ - -#define assert_se(expr) assert_message_se(expr, #expr) - -/* We override the glibc assert() here. */ -#undef assert -#ifdef NDEBUG -#define assert(expr) do {} while (false) -#else -#define assert(expr) assert_message_se(expr, #expr) -#endif - -#define assert_not_reached(t) \ - do { \ - log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#if defined(static_assert) -/* static_assert() is sometimes defined in a way that trips up - * -Wdeclaration-after-statement, hence let's temporarily turn off - * this warning around it. */ -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - static_assert(expr, #expr); \ - REENABLE_WARNING -#else -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - struct CONCATENATE(_assert_struct_, __COUNTER__) { \ - char x[(expr) ? 0 : -1]; \ - }; \ - REENABLE_WARNING -#endif - -#define assert_return(expr, r) \ - do { \ - if (!assert_log(expr, #expr)) \ - return (r); \ - } while (false) - -#define assert_return_errno(expr, r, err) \ - do { \ - if (!assert_log(expr, #expr)) { \ - errno = err; \ - return (r); \ - } \ - } while (false) - -#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) -#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) -#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) -#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) -#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) -#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) -#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p))) -#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) -#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) -#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define CHAR_TO_STR(x) ((char[2]) { x, 0 }) - -#define char_array_0(x) x[sizeof(x)-1] = 0; - -/* 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 (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 : \ - sizeof(type) <= 4 ? 10 : \ - sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) - -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - unsigned ans = 1; \ - while ((_x_ /= 10) != 0) \ - ans++; \ - ans; \ - }) - -#define SET_FLAG(v, flag, b) \ - (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) -#define FLAGS_SET(v, flags) \ - (((v) & (flags)) == (flags)) - -#define CASE_F(X) case X: -#define CASE_F_1(CASE, X) CASE_F(X) -#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__) -#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__) -#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__) -#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__) -#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__) -#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__) -#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__) -#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__) -#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__) -#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__) -#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__) -#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__) -#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__) -#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__) -#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__) -#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__) -#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__) -#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__) -#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__) - -#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME -#define FOR_EACH_MAKE_CASE(...) \ - GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ - CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ - (CASE_F,__VA_ARGS__) - -#define IN_SET(x, ...) \ - ({ \ - bool _found = false; \ - /* If the build breaks in the line below, you need to extend the case macros */ \ - static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \ - switch(x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = true; \ - break; \ - default: \ - break; \ - } \ - _found; \ - }) - -#define SWAP_TWO(x, y) do { \ - typeof(x) _t = (x); \ - (x) = (y); \ - (y) = (_t); \ - } while (false) - -/* Define C11 thread_local attribute even on older gcc compiler - * version */ -#ifndef thread_local -/* - * 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)) -#define thread_local _Thread_local -#else -#define thread_local __thread -#endif -#endif - -#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ - static inline void func##p(type *p) { \ - if (*p) \ - func(*p); \ - } - -#include "log.h" diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c deleted file mode 100644 index dd1b1e5f..00000000 --- a/src/systemd/src/basic/mempool.c +++ /dev/null @@ -1,87 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <stdint.h> -#include <stdlib.h> - -#include "macro.h" -#include "mempool.h" -#include "util.h" - -struct pool { - struct pool *next; - size_t n_tiles; - size_t n_used; -}; - -void* mempool_alloc_tile(struct mempool *mp) { - size_t i; - - /* When a tile is released we add it to the list and simply - * place the next pointer at its offset 0. */ - - assert(mp->tile_size >= sizeof(void*)); - assert(mp->at_least > 0); - - if (mp->freelist) { - void *r; - - r = mp->freelist; - mp->freelist = * (void**) mp->freelist; - return r; - } - - if (_unlikely_(!mp->first_pool) || - _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { - size_t size, n; - struct pool *p; - - n = mp->first_pool ? mp->first_pool->n_tiles : 0; - n = MAX(mp->at_least, n * 2); - size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size); - n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size; - - p = malloc(size); - if (!p) - return NULL; - - p->next = mp->first_pool; - p->n_tiles = n; - p->n_used = 0; - - mp->first_pool = p; - } - - i = mp->first_pool->n_used++; - - return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; -} - -void* mempool_alloc0_tile(struct mempool *mp) { - void *p; - - p = mempool_alloc_tile(mp); - if (p) - memzero(p, mp->tile_size); - return p; -} - -void mempool_free_tile(struct mempool *mp, void *p) { - * (void**) p = mp->freelist; - mp->freelist = p; -} - -#if VALGRIND - -void mempool_drop(struct mempool *mp) { - struct pool *p = mp->first_pool; - while (p) { - struct pool *n; - n = p->next; - free(p); - p = n; - } -} - -#endif diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h deleted file mode 100644 index 4098535c..00000000 --- a/src/systemd/src/basic/mempool.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stddef.h> - -struct pool; - -struct mempool { - struct pool *first_pool; - void *freelist; - size_t tile_size; - unsigned at_least; -}; - -void* mempool_alloc_tile(struct mempool *mp); -void* mempool_alloc0_tile(struct mempool *mp); -void mempool_free_tile(struct mempool *mp, void *p); - -#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ -static struct mempool pool_name = { \ - .tile_size = sizeof(tile_type), \ - .at_least = alloc_at_least, \ -} - -#if VALGRIND -void mempool_drop(struct mempool *mp); -#endif diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c deleted file mode 100644 index b0ccce28..00000000 --- a/src/systemd/src/basic/parse-util.c +++ /dev/null @@ -1,749 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <inttypes.h> -#include <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> - -#include "alloc-util.h" -#include "errno-list.h" -#include "extract-word.h" -#include "locale-util.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "process-util.h" -#include "string-util.h" - -int parse_boolean(const char *v) { - assert(v); - - if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) - return 1; - else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off")) - return 0; - - return -EINVAL; -} - -#if 0 /* NM_IGNORED */ -int parse_pid(const char *s, pid_t* ret_pid) { - unsigned long ul = 0; - pid_t pid; - int r; - - assert(s); - assert(ret_pid); - - r = safe_atolu(s, &ul); - if (r < 0) - return r; - - pid = (pid_t) ul; - - if ((unsigned long) pid != ul) - return -ERANGE; - - if (!pid_is_valid(pid)) - return -ERANGE; - - *ret_pid = pid; - return 0; -} - -int parse_mode(const char *s, mode_t *ret) { - char *x; - long l; - - assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - if (s[0] == '-') - return -ERANGE; - - errno = 0; - l = strtol(s, &x, 8); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l < 0 || l > 07777) - return -ERANGE; - - *ret = (mode_t) l; - return 0; -} - -int parse_ifindex(const char *s, int *ret) { - int ifi, r; - - r = safe_atoi(s, &ifi); - if (r < 0) - return r; - if (ifi <= 0) - return -EINVAL; - - *ret = ifi; - return 0; -} - -int parse_mtu(int family, const char *s, uint32_t *ret) { - uint64_t u; - size_t m; - int r; - - r = parse_size(s, 1024, &u); - if (r < 0) - return r; - - if (u > UINT32_MAX) - return -ERANGE; - - if (family == AF_INET6) - m = IPV6_MIN_MTU; /* This is 1280 */ - else - m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */ - - if (u < m) - return -ERANGE; - - *ret = (uint32_t) u; - return 0; -} - -int parse_size(const char *t, uint64_t base, uint64_t *size) { - - /* Soo, sometimes we want to parse IEC binary suffixes, and - * sometimes SI decimal suffixes. This function can parse - * both. Which one is the right way depends on the - * context. Wikipedia suggests that SI is customary for - * hardware metrics and network speeds, while IEC is - * customary for most data sizes used by software and volatile - * (RAM) memory. Hence be careful which one you pick! - * - * In either case we use just K, M, G as suffix, and not Ki, - * Mi, Gi or so (as IEC would suggest). That's because that's - * frickin' ugly. But this means you really need to make sure - * to document which base you are parsing when you use this - * call. */ - - struct table { - const char *suffix; - unsigned long long factor; - }; - - static const struct table iec[] = { - { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, - { "G", 1024ULL*1024ULL*1024ULL }, - { "M", 1024ULL*1024ULL }, - { "K", 1024ULL }, - { "B", 1ULL }, - { "", 1ULL }, - }; - - static const struct table si[] = { - { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, - { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, - { "T", 1000ULL*1000ULL*1000ULL*1000ULL }, - { "G", 1000ULL*1000ULL*1000ULL }, - { "M", 1000ULL*1000ULL }, - { "K", 1000ULL }, - { "B", 1ULL }, - { "", 1ULL }, - }; - - const struct table *table; - const char *p; - unsigned long long r = 0; - unsigned n_entries, start_pos = 0; - - assert(t); - assert(IN_SET(base, 1000, 1024)); - assert(size); - - if (base == 1000) { - table = si; - n_entries = ELEMENTSOF(si); - } else { - table = iec; - n_entries = ELEMENTSOF(iec); - } - - p = t; - do { - unsigned long long l, tmp; - double frac = 0; - char *e; - unsigned i; - - p += strspn(p, WHITESPACE); - - errno = 0; - l = strtoull(p, &e, 10); - if (errno > 0) - return -errno; - if (e == p) - return -EINVAL; - if (*p == '-') - return -ERANGE; - - if (*e == '.') { - e++; - - /* strtoull() itself would accept space/+/- */ - if (*e >= '0' && *e <= '9') { - unsigned long long l2; - char *e2; - - l2 = strtoull(e, &e2, 10); - if (errno > 0) - return -errno; - - /* Ignore failure. E.g. 10.M is valid */ - frac = l2; - for (; e < e2; e++) - frac /= 10; - } - } - - e += strspn(e, WHITESPACE); - - for (i = start_pos; i < n_entries; i++) - if (startswith(e, table[i].suffix)) - break; - - if (i >= n_entries) - return -EINVAL; - - if (l + (frac > 0) > ULLONG_MAX / table[i].factor) - return -ERANGE; - - tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); - if (tmp > ULLONG_MAX - r) - return -ERANGE; - - r += tmp; - if ((unsigned long long) (uint64_t) r != r) - return -ERANGE; - - p = e + strlen(table[i].suffix); - - start_pos = i + 1; - - } while (*p); - - *size = r; - - return 0; -} - -int parse_range(const char *t, unsigned *lower, unsigned *upper) { - _cleanup_free_ char *word = NULL; - unsigned l, u; - int r; - - assert(lower); - assert(upper); - - /* Extract the lower bound. */ - r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - r = safe_atou(word, &l); - if (r < 0) - return r; - - /* Check for the upper bound and extract it if needed */ - if (!t) - /* Single number with no dashes. */ - u = l; - else if (!*t) - /* Trailing dash is an error. */ - return -EINVAL; - else { - r = safe_atou(t, &u); - if (r < 0) - return r; - } - - *lower = l; - *upper = u; - return 0; -} - -int parse_errno(const char *t) { - int r, e; - - assert(t); - - r = errno_from_name(t); - if (r > 0) - return r; - - r = safe_atoi(t, &e); - if (r < 0) - return r; - - /* 0 is also allowed here */ - if (!errno_is_valid(e) && e != 0) - return -ERANGE; - - return e; -} - -int parse_syscall_and_errno(const char *in, char **name, int *error) { - _cleanup_free_ char *n = NULL; - char *p; - int e = -1; - - assert(in); - assert(name); - assert(error); - - /* - * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255". - * If errno is omitted, then error is set to -1. - * Empty syscall name is not allowed. - * Here, we do not check that the syscall name is valid or not. - */ - - p = strchr(in, ':'); - if (p) { - e = parse_errno(p + 1); - if (e < 0) - return e; - - n = strndup(in, p - in); - } else - n = strdup(in); - - if (!n) - return -ENOMEM; - - if (isempty(n)) - return -EINVAL; - - *error = e; - *name = TAKE_PTR(n); - - return 0; -} - -char *format_bytes(char *buf, size_t l, uint64_t t) { - unsigned i; - - /* This only does IEC units so far */ - - static const struct { - const char *suffix; - uint64_t factor; - } table[] = { - { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "M", UINT64_C(1024)*UINT64_C(1024) }, - { "K", UINT64_C(1024) }, - }; - - if (t == (uint64_t) -1) - return NULL; - - for (i = 0; i < ELEMENTSOF(table); i++) { - - if (t >= table[i].factor) { - snprintf(buf, l, - "%" PRIu64 ".%" PRIu64 "%s", - t / table[i].factor, - ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10), - table[i].suffix); - - goto finish; - } - } - - snprintf(buf, l, "%" PRIu64 "B", t); - -finish: - buf[l-1] = 0; - return buf; - -} -#endif /* NM_IGNORED */ - -int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret_u); - assert(base <= 16); - - /* strtoul() is happy to parse negative values, and silently - * converts them to unsigned values without generating an - * error. We want a clean error, hence let's look for the "-" - * prefix on our own, and generate an error. But let's do so - * only after strtoul() validated that the string is clean - * otherwise, so that we return EINVAL preferably over - * ERANGE. */ - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoul(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (s[0] == '-') - return -ERANGE; - if ((unsigned long) (unsigned) l != l) - return -ERANGE; - - *ret_u = (unsigned) l; - return 0; -} - -int safe_atoi(const char *s, int *ret_i) { - char *x = NULL; - long l; - - assert(s); - assert(ret_i); - - errno = 0; - l = strtol(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if ((long) (int) l != l) - return -ERANGE; - - *ret_i = (int) l; - return 0; -} - -int safe_atollu(const char *s, long long unsigned *ret_llu) { - char *x = NULL; - unsigned long long l; - - assert(s); - assert(ret_llu); - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoull(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (*s == '-') - return -ERANGE; - - *ret_llu = l; - return 0; -} - -int safe_atolli(const char *s, long long int *ret_lli) { - char *x = NULL; - long long l; - - assert(s); - assert(ret_lli); - - errno = 0; - l = strtoll(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - - *ret_lli = l; - return 0; -} - -int safe_atou8(const char *s, uint8_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoul(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint8_t) l != l) - return -ERANGE; - - *ret = (uint8_t) l; - return 0; -} - -int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret); - assert(base <= 16); - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoul(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint16_t) l != l) - return -ERANGE; - - *ret = (uint16_t) l; - return 0; -} - -int safe_atoi16(const char *s, int16_t *ret) { - char *x = NULL; - long l; - - assert(s); - assert(ret); - - errno = 0; - l = strtol(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if ((long) (int16_t) l != l) - return -ERANGE; - - *ret = (int16_t) l; - return 0; -} - -#if 0 /* NM_IGNORED */ -int safe_atod(const char *s, double *ret_d) { - _cleanup_(freelocalep) locale_t loc = (locale_t) 0; - char *x = NULL; - double d = 0; - - assert(s); - assert(ret_d); - - loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); - if (loc == (locale_t) 0) - return -errno; - - errno = 0; - d = strtod_l(s, &x, loc); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - - *ret_d = (double) d; - return 0; -} - -int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { - size_t i; - unsigned val = 0; - const char *s; - - s = *p; - - /* accept any number of digits, strtoull is limted to 19 */ - for (i=0; i < digits; i++,s++) { - if (*s < '0' || *s > '9') { - if (i == 0) - return -EINVAL; - - /* too few digits, pad with 0 */ - for (; i < digits; i++) - val *= 10; - - break; - } - - val *= 10; - val += *s - '0'; - } - - /* maybe round up */ - if (*s >= '5' && *s <= '9') - val++; - - s += strspn(s, DIGITS); - - *p = s; - *res = val; - - return 0; -} - -int parse_percent_unbounded(const char *p) { - const char *pc, *n; - int r, v; - - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - n = strndupa(p, pc - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - - return v; -} - -int parse_percent(const char *p) { - int v; - - v = parse_percent_unbounded(p); - if (v > 100) - return -ERANGE; - - return v; -} - -int parse_permille_unbounded(const char *p) { - const char *pc, *pm, *dot, *n; - int r, q, v; - - pm = endswith(p, "‰"); - if (pm) { - n = strndupa(p, pm - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - } else { - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - dot = memchr(p, '.', pc - p); - if (dot) { - if (dot + 2 != pc) - return -EINVAL; - if (dot[1] < '0' || dot[1] > '9') - return -EINVAL; - q = dot[1] - '0'; - n = strndupa(p, dot - p); - } else { - q = 0; - n = strndupa(p, pc - p); - } - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - if (v > (INT_MAX - q) / 10) - return -ERANGE; - - v = v * 10 + q; - } - - return v; -} - -int parse_permille(const char *p) { - int v; - - v = parse_permille_unbounded(p); - if (v > 1000) - return -ERANGE; - - return v; -} - -int parse_nice(const char *p, int *ret) { - int n, r; - - r = safe_atoi(p, &n); - if (r < 0) - return r; - - if (!nice_is_valid(n)) - return -ERANGE; - - *ret = n; - return 0; -} - -int parse_ip_port(const char *s, uint16_t *ret) { - uint16_t l; - int r; - - r = safe_atou16(s, &l); - if (r < 0) - return r; - - if (l == 0) - return -EINVAL; - - *ret = (uint16_t) l; - - return 0; -} - -int parse_dev(const char *s, dev_t *ret) { - unsigned x, y; - dev_t d; - - if (sscanf(s, "%u:%u", &x, &y) != 2) - return -EINVAL; - - d = makedev(x, y); - if ((unsigned) major(d) != x || (unsigned) minor(d) != y) - return -EINVAL; - - *ret = d; - return 0; -} - -int parse_oom_score_adjust(const char *s, int *ret) { - int r, v; - - assert(s); - assert(ret); - - r = safe_atoi(s, &v); - if (r < 0) - return r; - - if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX) - return -ERANGE; - - *ret = v; - return 0; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h deleted file mode 100644 index f3267f4c..00000000 --- a/src/systemd/src/basic/parse-util.h +++ /dev/null @@ -1,119 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#include "macro.h" - -#define MODE_INVALID ((mode_t) -1) - -int parse_boolean(const char *v) _pure_; -int parse_dev(const char *s, dev_t *ret); -int parse_pid(const char *s, pid_t* ret_pid); -int parse_mode(const char *s, mode_t *ret); -int parse_ifindex(const char *s, int *ret); -int parse_mtu(int family, const char *s, uint32_t *ret); - -int parse_size(const char *t, uint64_t base, uint64_t *size); -int parse_range(const char *t, unsigned *lower, unsigned *upper); -int parse_errno(const char *t); -int parse_syscall_and_errno(const char *in, char **name, int *error); - -#define FORMAT_BYTES_MAX 8 -char *format_bytes(char *buf, size_t l, uint64_t t); - -int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); - -static inline int safe_atou(const char *s, unsigned *ret_u) { - return safe_atou_full(s, 0, ret_u); -} - -int safe_atoi(const char *s, int *ret_i); -int safe_atollu(const char *s, unsigned long long *ret_u); -int safe_atolli(const char *s, long long int *ret_i); - -int safe_atou8(const char *s, uint8_t *ret); - -int safe_atou16_full(const char *s, unsigned base, uint16_t *ret); - -static inline int safe_atou16(const char *s, uint16_t *ret) { - return safe_atou16_full(s, 0, ret); -} - -static inline int safe_atoux16(const char *s, uint16_t *ret) { - return safe_atou16_full(s, 16, ret); -} - -int safe_atoi16(const char *s, int16_t *ret); - -static inline int safe_atou32(const char *s, uint32_t *ret_u) { - assert_cc(sizeof(uint32_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned*) ret_u); -} - -static inline int safe_atoi32(const char *s, int32_t *ret_i) { - assert_cc(sizeof(int32_t) == sizeof(int)); - return safe_atoi(s, (int*) ret_i); -} - -static inline int safe_atou64(const char *s, uint64_t *ret_u) { - assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); - return safe_atollu(s, (unsigned long long*) ret_u); -} - -static inline int safe_atoi64(const char *s, int64_t *ret_i) { - assert_cc(sizeof(int64_t) == sizeof(long long int)); - return safe_atolli(s, (long long int*) ret_i); -} - -#if LONG_MAX == INT_MAX -static inline int safe_atolu(const char *s, unsigned long *ret_u) { - assert_cc(sizeof(unsigned long) == sizeof(unsigned)); - return safe_atou(s, (unsigned*) ret_u); -} -static inline int safe_atoli(const char *s, long int *ret_u) { - assert_cc(sizeof(long int) == sizeof(int)); - return safe_atoi(s, (int*) ret_u); -} -#else -static inline int safe_atolu(const char *s, unsigned long *ret_u) { - assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); - return safe_atollu(s, (unsigned long long*) ret_u); -} -static inline int safe_atoli(const char *s, long int *ret_u) { - assert_cc(sizeof(long int) == sizeof(long long int)); - return safe_atolli(s, (long long int*) ret_u); -} -#endif - -#if SIZE_MAX == UINT_MAX -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned *) ret_u); -} -#else -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); - return safe_atolu(s, ret_u); -} -#endif - -int safe_atod(const char *s, double *ret_d); - -int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); - -int parse_percent_unbounded(const char *p); -int parse_percent(const char *p); - -int parse_permille_unbounded(const char *p); -int parse_permille(const char *p); - -int parse_nice(const char *p, int *ret); - -int parse_ip_port(const char *s, uint16_t *ret); - -int parse_oom_score_adjust(const char *s, int *ret); diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c deleted file mode 100644 index 3656a011..00000000 --- a/src/systemd/src/basic/path-util.c +++ /dev/null @@ -1,1060 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the - * POSIX version which is really broken. We prefer GNU basename(). */ -#include <libgen.h> -#undef basename - -#include "alloc-util.h" -#include "extract-word.h" -#include "fs-util.h" -#include "glob-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "utf8.h" - -bool path_is_absolute(const char *p) { - return p[0] == '/'; -} - -#if 0 /* NM_IGNORED */ -bool is_path(const char *p) { - return !!strchr(p, '/'); -} - -int path_split_and_make_absolute(const char *p, char ***ret) { - char **l; - int r; - - assert(p); - assert(ret); - - l = strv_split(p, ":"); - if (!l) - return -ENOMEM; - - r = path_strv_make_absolute_cwd(l); - if (r < 0) { - strv_free(l); - return r; - } - - *ret = l; - return r; -} - -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) || isempty(prefix)) - return strdup(p); - - if (endswith(prefix, "/")) - return strjoin(prefix, p); - else - return strjoin(prefix, "/", p); -} - -int safe_getcwd(char **ret) { - char *cwd; - - cwd = get_current_dir_name(); - if (!cwd) - return negative_errno(); - - /* Let's make sure the directory is really absolute, to protect us from the logic behind - * CVE-2018-1000001 */ - if (cwd[0] != '/') { - free(cwd); - return -ENOMEDIUM; - } - - *ret = cwd; - return 0; -} - -int path_make_absolute_cwd(const char *p, char **ret) { - char *c; - int r; - - assert(p); - assert(ret); - - /* Similar to path_make_absolute(), but prefixes with the - * current working directory. */ - - if (path_is_absolute(p)) - c = strdup(p); - else { - _cleanup_free_ char *cwd = NULL; - - r = safe_getcwd(&cwd); - if (r < 0) - return r; - - c = path_join(NULL, cwd, p); - } - if (!c) - return -ENOMEM; - - *ret = c; - return 0; -} - -int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *f, *t, *r, *p; - unsigned n_parents = 0; - - assert(from_dir); - assert(to_path); - assert(_r); - - /* Strips the common part, and adds ".." elements as necessary. */ - - if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) - return -EINVAL; - - f = strdupa(from_dir); - t = strdupa(to_path); - - path_simplify(f, true); - path_simplify(t, true); - - /* Skip the common part. */ - for (;;) { - size_t a, b; - - f += *f == '/'; - t += *t == '/'; - - if (!*f) { - if (!*t) - /* from_dir equals to_path. */ - r = strdup("."); - else - /* from_dir is a parent directory of to_path. */ - r = strdup(t); - if (!r) - return -ENOMEM; - - *_r = r; - return 0; - } - - if (!*t) - break; - - a = strcspn(f, "/"); - b = strcspn(t, "/"); - - if (a != b || memcmp(f, t, a) != 0) - break; - - f += a; - t += 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 (; *f;) { - size_t w; - - w = strcspn(f, "/"); - - /* If this includes ".." we can't do a simple series of "..", refuse */ - if (w == 2 && f[0] == '.' && f[1] == '.') - return -EINVAL; - - /* Count number of elements */ - n_parents++; - - f += w; - f += *f == '/'; - } - - r = new(char, n_parents * 3 + strlen(t) + 1); - if (!r) - return -ENOMEM; - - for (p = r; n_parents > 0; n_parents--) - p = mempcpy(p, "../", 3); - - if (*t) - strcpy(p, t); - else - /* Remove trailing slash */ - *(--p) = 0; - - *_r = r; - return 0; -} - -int path_strv_make_absolute_cwd(char **l) { - char **s; - int r; - - /* 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; - - r = path_make_absolute_cwd(*s, &t); - if (r < 0) - return r; - - path_simplify(t, false); - free_and_replace(*s, t); - } - - return 0; -} - -char **path_strv_resolve(char **l, const char *root) { - char **s; - unsigned k = 0; - bool enomem = false; - int r; - - 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) { - _cleanup_free_ char *orig = NULL; - char *t, *u; - - if (!path_is_absolute(*s)) { - free(*s); - continue; - } - - if (root) { - orig = *s; - t = prefix_root(root, orig); - if (!t) { - enomem = true; - continue; - } - } else - t = *s; - - r = chase_symlinks(t, root, 0, &u); - if (r == -ENOENT) { - if (root) { - u = TAKE_PTR(orig); - free(t); - } else - u = t; - } else if (r < 0) { - free(t); - - if (r == -ENOMEM) - enomem = true; - - continue; - } else if (root) { - char *x; - - free(t); - x = path_startswith(u, root); - 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_and_replace(u, orig); - } - } else - free(t); - - l[k++] = u; - } - - l[k] = NULL; - - if (enomem) - return NULL; - - return l; -} - -char **path_strv_resolve_uniq(char **l, const char *root) { - - if (strv_isempty(l)) - return l; - - if (!path_strv_resolve(l, root)) - return NULL; - - return strv_uniq(l); -} -#endif /* NM_IGNORED */ - -char *path_simplify(char *path, bool kill_dots) { - char *f, *t; - bool slash = false, ignore_slash = false, absolute; - - assert(path); - - /* Removes redundant inner and trailing slashes. Also removes unnecessary dots - * if kill_dots is true. Modifies the passed string in-place. - * - * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) - * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) - * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false) - * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) - */ - - absolute = path_is_absolute(path); - - f = path; - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) { - ignore_slash = true; - f++; - } - - for (t = path; *f; f++) { - - if (*f == '/') { - slash = true; - continue; - } - - if (slash) { - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) - continue; - - slash = false; - if (ignore_slash) - ignore_slash = false; - else - *(t++) = '/'; - } - - *(t++) = *f; - } - - /* Special rule, if we are talking of the root directory, a trailing slash is good */ - if (absolute && t == path) - *(t++) = '/'; - - *t = 0; - return path; -} - -char* path_startswith(const char *path, const char *prefix) { - assert(path); - assert(prefix); - - /* Returns a pointer to the start of the first component after the parts matched by - * the prefix, iff - * - both paths are absolute or both paths are relative, - * and - * - each component in prefix in turn matches a component in path at the same position. - * An empty string will be returned when the prefix and path are equivalent. - * - * Returns NULL otherwise. - */ - - 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; - } -} - -int path_compare(const char *a, const char *b) { - int d; - - assert(a); - assert(b); - - /* A relative path and an abolute path must not compare as equal. - * Which one is sorted before the other does not really matter. - * Here a relative path is ordered before an absolute path. */ - d = (a[0] == '/') - (b[0] == '/'); - if (d != 0) - return d; - - for (;;) { - size_t j, k; - - a += strspn(a, "/"); - b += strspn(b, "/"); - - if (*a == 0 && *b == 0) - return 0; - - /* Order prefixes first: "/foo" before "/foo/bar" */ - if (*a == 0) - return -1; - if (*b == 0) - return 1; - - j = strcspn(a, "/"); - k = strcspn(b, "/"); - - /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ - d = memcmp(a, b, MIN(j, k)); - if (d != 0) - return (d > 0) - (d < 0); /* sign of d */ - - /* Sort "/foo/a" before "/foo/aaa" */ - d = (j > k) - (j < k); /* sign of (j - k) */ - if (d != 0) - return d; - - a += j; - b += k; - } -} - -bool path_equal(const char *a, const char *b) { - return path_compare(a, b) == 0; -} - -#if 0 /* NM_IGNORED */ -bool path_equal_or_files_same(const char *a, const char *b, int flags) { - return path_equal(a, b) || files_same(a, b, flags) > 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); - else - return strjoin(path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest); -} - -int find_binary(const char *name, char **ret) { - int last_error, r; - const char *p; - - assert(name); - - if (is_path(name)) { - if (access(name, X_OK) < 0) - return -errno; - - if (ret) { - r = path_make_absolute_cwd(name, ret); - if (r < 0) - return r; - } - - return 0; - } - - /** - * Plain getenv, not secure_getenv, because we want - * to actually allow the user to pick the binary. - */ - p = getenv("PATH"); - if (!p) - p = DEFAULT_PATH; - - last_error = -ENOENT; - - for (;;) { - _cleanup_free_ char *j = NULL, *element = NULL; - - r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - break; - - if (!path_is_absolute(element)) - continue; - - j = strjoin(element, "/", name); - if (!j) - return -ENOMEM; - - if (access(j, X_OK) >= 0) { - /* Found it! */ - - if (ret) { - *ret = path_simplify(j, false); - j = NULL; - } - - return 0; - } - - last_error = -errno; - } - - return last_error; -} - -bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { - bool changed = false; - const char* const* i; - - assert(timestamp); - - if (!paths) - 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; -} - -static int binary_is_good(const char *binary) { - _cleanup_free_ char *p = NULL, *d = NULL; - int r; - - r = find_binary(binary, &p); - if (r == -ENOENT) - return 0; - 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 == -EINVAL) /* not a symlink */ - return 1; - if (r < 0) - return r; - - return !PATH_IN_SET(d, "true" - "/bin/true", - "/usr/bin/true", - "/dev/null"); -} - -int fsck_exists(const char *fstype) { - const char *checker; - - assert(fstype); - - if (streq(fstype, "auto")) - return -EINVAL; - - checker = strjoina("fsck.", fstype); - return binary_is_good(checker); -} - -int mkfs_exists(const char *fstype) { - const char *mkfs; - - assert(fstype); - - if (streq(fstype, "auto")) - return -EINVAL; - - mkfs = strjoina("mkfs.", fstype); - return binary_is_good(mkfs); -} - -char *prefix_root(const char *root, const char *path) { - char *n, *p; - size_t l; - - /* If root is passed, prefixes path with it. Otherwise returns - * it as is. */ - - assert(path); - - /* First, drop duplicate prefixing slashes from the path */ - while (path[0] == '/' && path[1] == '/') - path++; - - if (empty_or_root(root)) - return strdup(path); - - l = strlen(root) + 1 + strlen(path) + 1; - - n = new(char, l); - if (!n) - return NULL; - - p = stpcpy(n, root); - - while (p > n && p[-1] == '/') - p--; - - if (path[0] != '/') - *(p++) = '/'; - - strcpy(p, path); - return n; -} - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) { - char *p; - int r; - - /* - * This function is intended to be used in command line - * parsers, to handle paths that are passed in. It makes the - * path absolute, and reduces it to NULL if omitted or - * root (the latter optionally). - * - * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON - * SUCCESS! Hence, do not pass in uninitialized pointers. - */ - - if (isempty(path)) { - *arg = mfree(*arg); - return 0; - } - - r = path_make_absolute_cwd(path, &p); - if (r < 0) - return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); - - path_simplify(p, false); - if (suppress_root && empty_or_root(p)) - p = mfree(p); - - free_and_replace(*arg, p); - - return 0; -} - -char* dirname_malloc(const char *path) { - char *d, *dir, *dir2; - - assert(path); - - d = strdup(path); - if (!d) - return NULL; - - dir = dirname(d); - assert(dir); - - if (dir == d) - return d; - - dir2 = strdup(dir); - free(d); - - return dir2; -} - -const char *last_path_component(const char *path) { - - /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory. - * - * a/b/c → c - * a/b/c/ → c/ - * x → x - * x/ → x/ - * /y → y - * /y/ → y/ - * / → / - * // → / - * /foo/a → a - * /foo/a/ → a/ - * - * Also, the empty string is mapped to itself. - * - * This is different than basename(), which returns "" when a trailing slash is present. - */ - - unsigned l, k; - - l = k = strlen(path); - if (l == 0) /* special case — an empty string */ - return path; - - while (k > 0 && path[k-1] == '/') - k--; - - if (k == 0) /* the root directory */ - return path + l - 1; - - while (k > 0 && path[k-1] != '/') - k--; - - return path + k; -} -#endif /* NM_IGNORED */ - -bool filename_is_valid(const char *p) { - const char *e; - - if (isempty(p)) - return false; - - if (dot_or_dot_dot(p)) - return false; - - e = strchrnul(p, '/'); - if (*e != 0) - return false; - - if (e - p > FILENAME_MAX) - return false; - - return true; -} - -bool path_is_normalized(const char *p) { - - if (isempty(p)) - return false; - - if (dot_or_dot_dot(p)) - return false; - - if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) - return false; - - if (strlen(p)+1 > PATH_MAX) - return false; - - if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) - return false; - - if (strstr(p, "//")) - return false; - - return true; -} - -#if 0 /* NM_IGNORED */ -char *file_in_same_dir(const char *path, const char *filename) { - char *e, *ret; - size_t k; - - assert(path); - assert(filename); - - /* This removes the last component of path and appends - * filename, unless the latter is absolute anyway or the - * former isn't */ - - if (path_is_absolute(filename)) - return strdup(filename); - - e = strrchr(path, '/'); - if (!e) - return strdup(filename); - - k = strlen(filename); - ret = new(char, (e + 1 - path) + k + 1); - if (!ret) - return NULL; - - memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); - return ret; -} - -bool hidden_or_backup_file(const char *filename) { - const char *p; - - assert(filename); - - if (filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, "~")) - return true; - - p = strrchr(filename, '.'); - if (!p) - return false; - - /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up - * with always new suffixes and that everybody else should just adjust to that, then it really should be on - * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt - * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional - * string. Specifically: there's now: - * - * The generic suffixes "~" and ".bak" for backup files - * The generic prefix "." for hidden files - * - * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" - * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. - */ - - return STR_IN_SET(p + 1, - "rpmnew", - "rpmsave", - "rpmorig", - "dpkg-old", - "dpkg-new", - "dpkg-tmp", - "dpkg-dist", - "dpkg-bak", - "dpkg-backup", - "dpkg-remove", - "ucf-new", - "ucf-old", - "ucf-dist", - "swp", - "bak", - "old", - "new"); -} - -bool is_device_path(const char *path) { - - /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ - - return PATH_STARTSWITH_SET(path, "/dev/", "/sys/"); -} - -bool valid_device_node_path(const char *path) { - - /* Some superficial checks whether the specified path is a valid device node path, all without looking at the - * actual device node. */ - - if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")) - return false; - - if (endswith(path, "/")) /* can't be a device node if it ends in a slash */ - return false; - - return path_is_normalized(path); -} - -bool valid_device_allow_pattern(const char *path) { - assert(path); - - /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= - * accept it */ - - if (startswith(path, "block-") || - startswith(path, "char-")) - return true; - - return valid_device_node_path(path); -} - -int systemd_installation_has_version(const char *root, unsigned minimal_version) { - const char *pattern; - int r; - - /* Try to guess if systemd installation is later than the specified version. This - * is hacky and likely to yield false negatives, particularly if the installation - * is non-standard. False positives should be relatively rare. - */ - - NULSTR_FOREACH(pattern, - /* /lib works for systems without usr-merge, and for systems with a sane - * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary - * for Gentoo which does a merge without making /lib a symlink. - */ - "lib/systemd/libsystemd-shared-*.so\0" - "lib64/systemd/libsystemd-shared-*.so\0" - "usr/lib/systemd/libsystemd-shared-*.so\0" - "usr/lib64/systemd/libsystemd-shared-*.so\0") { - - _cleanup_strv_free_ char **names = NULL; - _cleanup_free_ char *path = NULL; - char *c, **name; - - path = prefix_root(root, pattern); - if (!path) - return -ENOMEM; - - r = glob_extend(&names, path); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - - assert_se(c = endswith(path, "*.so")); - *c = '\0'; /* truncate the glob part */ - - STRV_FOREACH(name, names) { - /* This is most likely to run only once, hence let's not optimize anything. */ - char *t, *t2; - unsigned version; - - t = startswith(*name, path); - if (!t) - continue; - - t2 = endswith(t, ".so"); - if (!t2) - continue; - - t2[0] = '\0'; /* truncate the suffix */ - - r = safe_atou(t, &version); - if (r < 0) { - log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name); - continue; - } - - log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).", - *name, version, - version >= minimal_version ? "OK" : "too old"); - if (version >= minimal_version) - return true; - } - } - - return false; -} -#endif /* NM_IGNORED */ - -bool dot_or_dot_dot(const char *path) { - if (!path) - return false; - if (path[0] != '.') - return false; - if (path[1] == 0) - return true; - if (path[1] != '.') - return false; - - return path[2] == 0; -} - -#if 0 /* NM_IGNORED */ -bool empty_or_root(const char *root) { - - /* For operations relative to some root directory, returns true if the specified root directory is redundant, - * i.e. either / or NULL or the empty string or any equivalent. */ - - if (!root) - return true; - - return root[strspn(root, "/")] == 0; -} - -int path_simplify_and_warn( - char *path, - unsigned flag, - const char *unit, - const char *filename, - unsigned line, - const char *lvalue) { - - bool absolute, fatal = flag & PATH_CHECK_FATAL; - - assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); - - if (!utf8_is_valid(path)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); - return -EINVAL; - } - - if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { - absolute = path_is_absolute(path); - - if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } - - if (absolute && (flag & PATH_CHECK_RELATIVE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } - } - - path_simplify(path, true); - - if (!path_is_normalized(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not normalized%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } - - return 0; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h deleted file mode 100644 index 72f3ce36..00000000 --- a/src/systemd/src/basic/path-util.h +++ /dev/null @@ -1,175 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <stdbool.h> -#include <stddef.h> - -#include "macro.h" -#include "string-util.h" -#include "time-util.h" - -#if 0 /* NM_IGNORED */ -#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin" -#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0" - -#define PATH_NORMAL_SBIN_BIN(x) x "bin" -#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0" - -#if HAVE_SPLIT_BIN -# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x) -#else -# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x) -#endif - -#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/") -#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/") -#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/") -#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/") -#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/") - -#if HAVE_SPLIT_USR -# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR -# define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR -#else -# define DEFAULT_PATH DEFAULT_PATH_NORMAL -# define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR -#endif -#endif /* NM_IGNORED */ - -bool is_path(const char *p) _pure_; -int path_split_and_make_absolute(const char *p, char ***ret); -bool path_is_absolute(const char *p) _pure_; -char* path_make_absolute(const char *p, const char *prefix); -int safe_getcwd(char **ret); -int path_make_absolute_cwd(const char *p, char **ret); -int path_make_relative(const char *from_dir, const char *to_path, char **_r); -char* path_startswith(const char *path, const char *prefix) _pure_; -int path_compare(const char *a, const char *b) _pure_; -bool path_equal(const char *a, const char *b) _pure_; -bool path_equal_or_files_same(const char *a, const char *b, int flags); -char* path_join(const char *root, const char *path, const char *rest); -char* path_simplify(char *path, bool kill_dots); - -static inline bool path_equal_ptr(const char *a, const char *b) { - return !!a == !!b && (!a || path_equal(a, b)); -} - -/* Note: the search terminates on the first NULL item. */ -#define PATH_IN_SET(p, ...) \ - ({ \ - char **_s; \ - bool _found = false; \ - STRV_FOREACH(_s, STRV_MAKE(__VA_ARGS__)) \ - if (path_equal(p, *_s)) { \ - _found = true; \ - break; \ - } \ - _found; \ - }) - -#define PATH_STARTSWITH_SET(p, ...) \ - ({ \ - char **s; \ - bool _found = false; \ - STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ - if (path_startswith(p, *s)) { \ - _found = true; \ - break; \ - } \ - _found; \ - }) - -int path_strv_make_absolute_cwd(char **l); -char** path_strv_resolve(char **l, const char *root); -char** path_strv_resolve_uniq(char **l, const char *root); - -int find_binary(const char *name, char **filename); - -bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); - -int fsck_exists(const char *fstype); -int mkfs_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_simplify(strcpy(prefix, path), false); 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_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) - -char *prefix_root(const char *root, const char *path); - -/* Similar to prefix_root(), but returns an alloca() buffer, or - * possibly a const pointer into the path parameter */ -#define prefix_roota(root, path) \ - ({ \ - const char* _path = (path), *_root = (root), *_ret; \ - char *_p, *_n; \ - size_t _l; \ - while (_path[0] == '/' && _path[1] == '/') \ - _path ++; \ - if (empty_or_root(_root)) \ - _ret = _path; \ - else { \ - _l = strlen(_root) + 1 + strlen(_path) + 1; \ - _n = alloca(_l); \ - _p = stpcpy(_n, _root); \ - while (_p > _n && _p[-1] == '/') \ - _p--; \ - if (_path[0] != '/') \ - *(_p++) = '/'; \ - strcpy(_p, _path); \ - _ret = _n; \ - } \ - _ret; \ - }) - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); - -char* dirname_malloc(const char *path); -const char *last_path_component(const char *path); - -bool filename_is_valid(const char *p) _pure_; -bool path_is_normalized(const char *p) _pure_; - -char *file_in_same_dir(const char *path, const char *filename); - -bool hidden_or_backup_file(const char *filename) _pure_; - -bool is_device_path(const char *path); - -bool valid_device_node_path(const char *path); -bool valid_device_allow_pattern(const char *path); - -int systemd_installation_has_version(const char *root, unsigned minimal_version); - -bool dot_or_dot_dot(const char *path); - -static inline const char *skip_dev_prefix(const char *p) { - const char *e; - - /* Drop any /dev prefix if there is any */ - - e = path_startswith(p, "/dev/"); - - return e ?: p; -} - -bool empty_or_root(const char *root); -static inline const char *empty_to_root(const char *path) { - return isempty(path) ? "/" : path; -} - -enum { - PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */ - PATH_CHECK_ABSOLUTE = 1 << 1, - PATH_CHECK_RELATIVE = 1 << 2, -}; - -int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue); diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c deleted file mode 100644 index 79900df3..00000000 --- a/src/systemd/src/basic/prioq.c +++ /dev/null @@ -1,303 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -/* - * Priority Queue - * The prioq object implements a priority queue. That is, it orders objects by - * their priority and allows O(1) access to the object with the highest - * priority. Insertion and removal are Θ(log n). Optionally, the caller can - * provide a pointer to an index which will be kept up-to-date by the prioq. - * - * The underlying algorithm used in this implementation is a Heap. - */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hashmap.h" -#include "prioq.h" - -struct prioq_item { - void *data; - unsigned *idx; -}; - -struct Prioq { - compare_func_t compare_func; - unsigned n_items, n_allocated; - - struct prioq_item *items; -}; - -Prioq *prioq_new(compare_func_t compare_func) { - Prioq *q; - - q = new0(Prioq, 1); - if (!q) - return q; - - q->compare_func = compare_func; - return q; -} - -Prioq* prioq_free(Prioq *q) { - if (!q) - return NULL; - - free(q->items); - return mfree(q); -} - -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) { - assert(q); - - if (*q) - return 0; - - *q = prioq_new(compare_func); - if (!*q) - return -ENOMEM; - - return 0; -} - -static void swap(Prioq *q, unsigned j, unsigned k) { - void *saved_data; - unsigned *saved_idx; - - assert(q); - assert(j < q->n_items); - assert(k < q->n_items); - - assert(!q->items[j].idx || *(q->items[j].idx) == j); - assert(!q->items[k].idx || *(q->items[k].idx) == k); - - saved_data = q->items[j].data; - saved_idx = q->items[j].idx; - q->items[j].data = q->items[k].data; - q->items[j].idx = q->items[k].idx; - q->items[k].data = saved_data; - q->items[k].idx = saved_idx; - - if (q->items[j].idx) - *q->items[j].idx = j; - - if (q->items[k].idx) - *q->items[k].idx = k; -} - -static unsigned shuffle_up(Prioq *q, unsigned idx) { - assert(q); - - while (idx > 0) { - unsigned k; - - k = (idx-1)/2; - - if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0) - break; - - swap(q, idx, k); - idx = k; - } - - return idx; -} - -static unsigned shuffle_down(Prioq *q, unsigned idx) { - assert(q); - - for (;;) { - unsigned j, k, s; - - k = (idx+1)*2; /* right child */ - j = k-1; /* left child */ - - if (j >= q->n_items) - break; - - if (q->compare_func(q->items[j].data, q->items[idx].data) < 0) - - /* So our left child is smaller than we are, let's - * remember this fact */ - s = j; - else - s = idx; - - if (k < q->n_items && - q->compare_func(q->items[k].data, q->items[s].data) < 0) - - /* So our right child is smaller than we are, let's - * remember this fact */ - s = k; - - /* s now points to the smallest of the three items */ - - if (s == idx) - /* No swap necessary, we're done */ - break; - - swap(q, idx, s); - idx = s; - } - - return idx; -} - -int prioq_put(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - if (q->n_items >= q->n_allocated) { - unsigned n; - struct prioq_item *j; - - n = MAX((q->n_items+1) * 2, 16u); - j = reallocarray(q->items, n, sizeof(struct prioq_item)); - if (!j) - return -ENOMEM; - - q->items = j; - q->n_allocated = n; - } - - k = q->n_items++; - i = q->items + k; - i->data = data; - i->idx = idx; - - if (idx) - *idx = k; - - shuffle_up(q, k); - - return 0; -} - -static void remove_item(Prioq *q, struct prioq_item *i) { - struct prioq_item *l; - - assert(q); - assert(i); - - l = q->items + q->n_items - 1; - - if (i == l) - /* Last entry, let's just remove it */ - q->n_items--; - else { - unsigned k; - - /* Not last entry, let's replace the last entry with - * this one, and reshuffle */ - - k = i - q->items; - - i->data = l->data; - i->idx = l->idx; - if (i->idx) - *i->idx = k; - q->n_items--; - - k = shuffle_down(q, k); - shuffle_up(q, k); - } -} - -_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - assert(q); - - if (idx) { - if (*idx == PRIOQ_IDX_NULL || - *idx > q->n_items) - return NULL; - - i = q->items + *idx; - if (i->data != data) - return NULL; - - return i; - } else { - for (i = q->items; i < q->items + q->n_items; i++) - if (i->data == data) - return i; - return NULL; - } -} - -int prioq_remove(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - if (!q) - return 0; - - i = find_item(q, data, idx); - if (!i) - return 0; - - remove_item(q, i); - return 1; -} - -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - i = find_item(q, data, idx); - if (!i) - return 0; - - k = i - q->items; - k = shuffle_down(q, k); - shuffle_up(q, k); - return 1; -} - -void *prioq_peek(Prioq *q) { - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - return q->items[0].data; -} - -void *prioq_pop(Prioq *q) { - void *data; - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - data = q->items[0].data; - remove_item(q, q->items); - return data; -} - -unsigned prioq_size(Prioq *q) { - - if (!q) - return 0; - - return q->n_items; -} - -bool prioq_isempty(Prioq *q) { - - if (!q) - return true; - - return q->n_items <= 0; -} diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h deleted file mode 100644 index e0361752..00000000 --- a/src/systemd/src/basic/prioq.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> - -#include "hashmap.h" -#include "macro.h" - -typedef struct Prioq Prioq; - -#define PRIOQ_IDX_NULL ((unsigned) -1) - -Prioq *prioq_new(compare_func_t compare); -Prioq *prioq_free(Prioq *q); -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); - -int prioq_put(Prioq *q, void *data, unsigned *idx); -int prioq_remove(Prioq *q, void *data, unsigned *idx); -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); - -void *prioq_peek(Prioq *q) _pure_; -void *prioq_pop(Prioq *q); - -unsigned prioq_size(Prioq *q) _pure_; -bool prioq_isempty(Prioq *q) _pure_; diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c deleted file mode 100644 index 1412f036..00000000 --- a/src/systemd/src/basic/process-util.c +++ /dev/null @@ -1,1505 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <linux/oom.h> -#include <sched.h> -#include <signal.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/mount.h> -#include <sys/personality.h> -#include <sys/prctl.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <syslog.h> -#include <unistd.h> -#if 0 /* NM_IGNORED */ -#if HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> -#endif -#endif /* NM_IGNORED */ - -#include "alloc-util.h" -#include "architecture.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "ioprio.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "process-util.h" -#include "raw-clone.h" -#include "signal-util.h" -#include "stat-util.h" -#include "string-table.h" -#include "string-util.h" -#include "terminal-util.h" -#include "user-util.h" -#include "util.h" - -#if 0 /* NM_IGNORED */ -int get_process_state(pid_t pid) { - const char *p; - char state; - int r; - _cleanup_free_ char *line = NULL; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "stat"); - - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " %c", &state) != 1) - return -EIO; - - return (unsigned char) state; -} - -int get_process_comm(pid_t pid, char **ret) { - _cleanup_free_ char *escaped = NULL, *comm = NULL; - const char *p; - int r; - - assert(ret); - assert(pid >= 0); - - escaped = new(char, TASK_COMM_LEN); - if (!escaped) - return -ENOMEM; - - p = procfs_file_alloca(pid, "comm"); - - r = read_one_line_file(p, &comm); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */ - cellescape(escaped, TASK_COMM_LEN, comm); - - *ret = TAKE_PTR(escaped); - return 0; -} - -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { - _cleanup_fclose_ FILE *f = NULL; - bool space = false; - char *k, *ans = NULL; - const char *p; - int c; - - assert(line); - assert(pid >= 0); - - /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing - * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most - * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If - * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a - * command line that resolves to the empty string will return the "comm" name of the process instead. - * - * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and - * comm_fallback is false). Returns 0 and sets *line otherwise. */ - - p = procfs_file_alloca(pid, "cmdline"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - if (max_length == 1) { - - /* If there's only room for one byte, return the empty string */ - ans = new0(char, 1); - if (!ans) - return -ENOMEM; - - *line = ans; - return 0; - - } else if (max_length == 0) { - size_t len = 0, allocated = 0; - - while ((c = getc(f)) != EOF) { - - if (!GREEDY_REALLOC(ans, allocated, len+3)) { - free(ans); - return -ENOMEM; - } - - if (isprint(c)) { - if (space) { - ans[len++] = ' '; - space = false; - } - - ans[len++] = c; - } else if (len > 0) - space = true; - } - - if (len > 0) - ans[len] = '\0'; - else - ans = mfree(ans); - - } else { - bool dotdotdot = false; - size_t left; - - ans = new(char, max_length); - if (!ans) - return -ENOMEM; - - k = ans; - left = max_length; - while ((c = getc(f)) != EOF) { - - if (isprint(c)) { - - if (space) { - if (left <= 2) { - dotdotdot = true; - break; - } - - *(k++) = ' '; - left--; - space = false; - } - - if (left <= 1) { - dotdotdot = true; - break; - } - - *(k++) = (char) c; - left--; - } else if (k > ans) - space = true; - } - - if (dotdotdot) { - if (max_length <= 4) { - k = ans; - left = max_length; - } else { - k = ans + max_length - 4; - left = 4; - - /* Eat up final spaces */ - while (k > ans && isspace(k[-1])) { - k--; - left++; - } - } - - strncpy(k, "...", left-1); - k[left-1] = 0; - } else - *k = 0; - } - - /* Kernel threads have no argv[] */ - if (isempty(ans)) { - _cleanup_free_ char *t = NULL; - int h; - - free(ans); - - if (!comm_fallback) - return -ENOENT; - - h = get_process_comm(pid, &t); - if (h < 0) - return h; - - if (max_length == 0) - ans = strjoin("[", t, "]"); - else { - size_t l; - - l = strlen(t); - - if (l + 3 <= max_length) - ans = strjoin("[", t, "]"); - else if (max_length <= 6) { - - ans = new(char, max_length); - if (!ans) - return -ENOMEM; - - memcpy(ans, "[...]", max_length-1); - ans[max_length-1] = 0; - } else { - t[max_length - 6] = 0; - - /* Chop off final spaces */ - delete_trailing_chars(t, WHITESPACE); - - ans = strjoin("[", t, "...]"); - } - } - if (!ans) - return -ENOMEM; - } - - *line = ans; - return 0; -} - -int rename_process(const char name[]) { - static size_t mm_size = 0; - static char *mm = NULL; - bool truncated = false; - size_t l; - - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux process names can be 15 chars at max */ - truncated = true; - - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } - - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ - - /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the - * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is - * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if - * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but - * mmap() is not. */ - if (geteuid() != 0) - log_debug("Skipping PR_SET_MM, as we don't have privileges."); - else if (mm_size < l+1) { - size_t nn_size; - char *nn; - - nn_size = PAGE_ALIGN(l+1); - nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) { - log_debug_errno(errno, "mmap() failed: %m"); - goto use_saved_argv; - } - - strncpy(nn, name, nn_size); - - /* Now, let's tell the kernel about this new memory */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m"); - (void) munmap(nn, nn_size); - goto use_saved_argv; - } - - /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's - * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - - if (mm) - (void) munmap(mm, mm_size); - - mm = nn; - mm_size = nn_size; - } else { - strncpy(mm, name, mm_size); - - /* Update the end pointer, continuing regardless of any failure. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - } - -use_saved_argv: - /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if - * it still looks here */ - - if (saved_argc > 0) { - int i; - - if (saved_argv[0]) { - size_t k; - - k = strlen(saved_argv[0]); - strncpy(saved_argv[0], name, k); - if (l > k) - truncated = true; - } - - for (i = 1; i < saved_argc; i++) { - if (!saved_argv[i]) - break; - - memzero(saved_argv[i], strlen(saved_argv[i])); - } - } - - return !truncated; -} - -int is_kernel_thread(pid_t pid) { - _cleanup_free_ char *line = NULL; - unsigned long long flags; - size_t l, i; - const char *p; - char *q; - int r; - - if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */ - return 0; - if (!pid_is_valid(pid)) - return -EINVAL; - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Skip past the comm field */ - q = strrchr(line, ')'); - if (!q) - return -EINVAL; - q++; - - /* Skip 6 fields to reach the flags field */ - for (i = 0; i < 6; i++) { - l = strspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - - l = strcspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - } - - /* Skip preceeding whitespace */ - l = strspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - - /* Truncate the rest */ - l = strcspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q[l] = 0; - - r = safe_atollu(q, &flags); - if (r < 0) - return r; - - return !!(flags & PF_KTHREAD); -} - -int get_process_capeff(pid_t pid, char **capeff) { - const char *p; - int r; - - assert(capeff); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "CapEff", WHITESPACE, capeff); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - -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 == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - return 0; -} - -int get_process_exe(pid_t pid, char **name) { - const char *p; - char *d; - int r; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, name); - if (r < 0) - return r; - - d = endswith(*name, " (deleted)"); - if (d) - *d = '\0'; - - return 0; -} - -static int get_process_id(pid_t pid, const char *field, uid_t *uid) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - const char *p; - - assert(field); - assert(uid); - - if (pid < 0) - return -EINVAL; - - p = procfs_file_alloca(pid, "status"); - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - FOREACH_LINE(line, f, return -errno) { - char *l; - - l = strstrip(line); - - if (startswith(l, field)) { - l += strlen(field); - l += strspn(l, WHITESPACE); - - l[strcspn(l, WHITESPACE)] = 0; - - return parse_uid(l, uid); - } - } - - return -EIO; -} - -int get_process_uid(pid_t pid, uid_t *uid) { - - if (pid == 0 || pid == getpid_cached()) { - *uid = getuid(); - return 0; - } - - return get_process_id(pid, "Uid:", uid); -} - -int get_process_gid(pid_t pid, gid_t *gid) { - - if (pid == 0 || pid == getpid_cached()) { - *gid = getgid(); - return 0; - } - - 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) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - 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); - } - - if (!outcome) { - outcome = strdup(""); - if (!outcome) - return -ENOMEM; - } else - outcome[sz] = '\0'; - - *env = TAKE_PTR(outcome); - - return 0; -} - -int get_process_ppid(pid_t pid, pid_t *_ppid) { - int r; - _cleanup_free_ char *line = NULL; - long unsigned ppid; - const char *p; - - assert(pid >= 0); - assert(_ppid); - - if (pid == 0 || pid == getpid_cached()) { - *_ppid = getppid(); - return 0; - } - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - 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 */ - "%lu ", /* ppid */ - &ppid) != 1) - return -EIO; - - if ((long unsigned) (pid_t) ppid != ppid) - return -ERANGE; - - *_ppid = (pid_t) ppid; - - return 0; -} - -int wait_for_terminate(pid_t pid, siginfo_t *status) { - siginfo_t dummy; - - assert(pid >= 1); - - if (!status) - status = &dummy; - - for (;;) { - zero(*status); - - if (waitid(P_PID, pid, status, WEXITED) < 0) { - - if (errno == EINTR) - continue; - - return negative_errno(); - } - - return 0; - } -} - -/* - * Return values: - * < 0 : wait_for_terminate() failed to get the state of the - * process, the process was terminated by a signal, or - * failed for an unknown reason. - * >=0 : The process terminated normally, and its exit code is - * returned. - * - * 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_check(const char *name, pid_t pid, WaitFlags flags) { - _cleanup_free_ char *buffer = NULL; - siginfo_t status; - int r, prio; - - assert(pid > 1); - - if (!name) { - r = get_process_comm(pid, &buffer); - if (r < 0) - log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid); - else - name = buffer; - } - - prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG; - - r = wait_for_terminate(pid, &status); - if (r < 0) - return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); - - if (status.si_code == CLD_EXITED) { - if (status.si_status != EXIT_SUCCESS) - log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG, - "%s failed with exit status %i.", strna(name), status.si_status); - else - log_debug("%s succeeded.", name); - - return status.si_status; - - } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { - - log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); - return -EPROTO; - } - - log_full(prio, "%s failed due to unknown reason.", strna(name)); - return -EPROTO; -} - -/* - * Return values: - * - * < 0 : wait_for_terminate_with_timeout() failed to get the state of the process, the process timed out, the process - * was terminated by a signal, or failed for an unknown reason. - * - * >=0 : The process terminated normally with no failures. - * - * Success is indicated by a return value of zero, a timeout is indicated by ETIMEDOUT, and all other child failure - * states are indicated by error is indicated by a non-zero value. - * - * This call assumes SIGCHLD has been blocked already, in particular before the child to wait for has been forked off - * to remain entirely race-free. - */ -int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { - sigset_t mask; - int r; - usec_t until; - - assert_se(sigemptyset(&mask) == 0); - assert_se(sigaddset(&mask, SIGCHLD) == 0); - - /* Drop into a sigtimewait-based timeout. Waiting for the - * pid to exit. */ - until = now(CLOCK_MONOTONIC) + timeout; - for (;;) { - usec_t n; - siginfo_t status = {}; - struct timespec ts; - - n = now(CLOCK_MONOTONIC); - if (n >= until) - break; - - r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0; - /* Assuming we woke due to the child exiting. */ - if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { - if (status.si_pid == pid) { - /* This is the correct child.*/ - if (status.si_code == CLD_EXITED) - return (status.si_status == 0) ? 0 : -EPROTO; - else - return -EPROTO; - } - } - /* Not the child, check for errors and proceed appropriately */ - if (r < 0) { - switch (r) { - case -EAGAIN: - /* Timed out, child is likely hung. */ - return -ETIMEDOUT; - case -EINTR: - /* Received a different signal and should retry */ - continue; - default: - /* Return any unexpected errors */ - return r; - } - } - } - - return -EPROTO; -} - -void sigkill_wait(pid_t pid) { - assert(pid > 1); - - if (kill(pid, SIGKILL) > 0) - (void) wait_for_terminate(pid, NULL); -} - -void sigkill_waitp(pid_t *pid) { - PROTECT_ERRNO; - - if (!pid) - return; - if (*pid <= 1) - return; - - sigkill_wait(*pid); -} - -void sigterm_wait(pid_t pid) { - assert(pid > 1); - - if (kill_and_sigcont(pid, SIGTERM) > 0) - (void) wait_for_terminate(pid, NULL); -} - -int kill_and_sigcont(pid_t pid, int sig) { - int r; - - r = kill(pid, sig) < 0 ? -errno : 0; - - /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't - * affected by a process being suspended anyway. */ - if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL)) - (void) kill(pid, SIGCONT); - - return r; -} - -int getenv_for_pid(pid_t pid, const char *field, char **ret) { - _cleanup_fclose_ FILE *f = NULL; - char *value = NULL; - bool done = false; - const char *path; - size_t l; - - assert(pid >= 0); - assert(field); - assert(ret); - - if (pid == 0 || pid == getpid_cached()) { - const char *e; - - e = getenv(field); - if (!e) { - *ret = NULL; - return 0; - } - - value = strdup(e); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } - - path = procfs_file_alloca(pid, "environ"); - - f = fopen(path, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - l = strlen(field); - - do { - char line[LINE_MAX]; - size_t i; - - for (i = 0; i < sizeof(line)-1; i++) { - int c; - - c = getc(f); - if (_unlikely_(c == EOF)) { - done = true; - break; - } else if (c == 0) - break; - - line[i] = c; - } - line[i] = 0; - - if (strneq(line, field, l) && line[l] == '=') { - value = strdup(line + l + 1); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } - - } while (!done); - - *ret = NULL; - return 0; -} - -bool pid_is_unwaited(pid_t pid) { - /* Checks whether a PID is still valid at all, including a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */ - return true; - - if (pid == getpid_cached()) - return true; - - if (kill(pid, 0) >= 0) - return true; - - return errno != ESRCH; -} - -bool pid_is_alive(pid_t pid) { - int r; - - /* Checks whether a PID is still valid and not a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */ - return true; - - if (pid == getpid_cached()) - return true; - - r = get_process_state(pid); - if (IN_SET(r, -ESRCH, 'Z')) - return false; - - return true; -} - -int pid_from_same_root_fs(pid_t pid) { - const char *root; - - if (pid < 0) - return false; - - if (pid == 0 || pid == getpid_cached()) - return true; - - root = procfs_file_alloca(pid, "root"); - - return files_same(root, "/proc/1/root", 0); -} -#endif /* NM_IGNORED */ - -bool is_main_thread(void) { - static thread_local int cached = 0; - - if (_unlikely_(cached == 0)) - cached = getpid_cached() == gettid() ? 1 : -1; - - return cached > 0; -} - -#if 0 /* NM_IGNORED */ -_noreturn_ void freeze(void) { - - log_close(); - - /* Make sure nobody waits for us on a socket anymore */ - close_all_fds(NULL, 0); - - sync(); - - /* Let's not freeze right away, but keep reaping zombies. */ - for (;;) { - int r; - siginfo_t si = {}; - - r = waitid(P_ALL, 0, &si, WEXITED); - if (r < 0 && errno != EINTR) - break; - } - - /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ - for (;;) - pause(); -} - -bool oom_score_adjust_is_valid(int oa) { - return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX; -} - -unsigned long personality_from_string(const char *p) { - int architecture; - - if (!p) - return PERSONALITY_INVALID; - - /* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just - * hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for - * the same register size. */ - - architecture = architecture_from_string(p); - if (architecture < 0) - return PERSONALITY_INVALID; - - if (architecture == native_architecture()) - return PER_LINUX; -#ifdef SECONDARY_ARCHITECTURE - if (architecture == SECONDARY_ARCHITECTURE) - return PER_LINUX32; -#endif - - return PERSONALITY_INVALID; -} - -const char* personality_to_string(unsigned long p) { - int architecture = _ARCHITECTURE_INVALID; - - if (p == PER_LINUX) - architecture = native_architecture(); -#ifdef SECONDARY_ARCHITECTURE - else if (p == PER_LINUX32) - architecture = SECONDARY_ARCHITECTURE; -#endif - - if (architecture < 0) - return NULL; - - return architecture_to_string(architecture); -} - -int safe_personality(unsigned long p) { - int ret; - - /* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno, - * and in others as negative return value containing an errno-like value. Let's work around this: this is a - * wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and - * the return value indicating the same issue, so that we are definitely on the safe side. - * - * See https://github.com/systemd/systemd/issues/6737 */ - - errno = 0; - ret = personality(p); - if (ret < 0) { - if (errno != 0) - return -errno; - - errno = -ret; - } - - return ret; -} - -int opinionated_personality(unsigned long *ret) { - int current; - - /* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit - * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the - * two most relevant personalities: PER_LINUX and PER_LINUX32. */ - - current = safe_personality(PERSONALITY_INVALID); - if (current < 0) - return current; - - if (((unsigned long) current & 0xffff) == PER_LINUX32) - *ret = PER_LINUX32; - else - *ret = PER_LINUX; - - return 0; -} - -void valgrind_summary_hack(void) { -#if HAVE_VALGRIND_VALGRIND_H - if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) { - pid_t pid; - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Failed to fork off valgrind helper: %m"); - else if (pid == 0) - exit(EXIT_SUCCESS); - else { - log_info("Spawned valgrind helper as PID "PID_FMT".", pid); - (void) wait_for_terminate(pid, NULL); - } - } -#endif -} - -int pid_compare_func(const void *a, const void *b) { - const pid_t *p = a, *q = b; - - /* Suitable for usage in qsort() */ - return CMP(*p, *q); -} - -int ioprio_parse_priority(const char *s, int *ret) { - int i, r; - - assert(s); - assert(ret); - - r = safe_atoi(s, &i); - if (r < 0) - return r; - - if (!ioprio_priority_is_valid(i)) - return -EINVAL; - - *ret = i; - return 0; -} -#endif /* NM_IGNORED */ - -/* The cached PID, possible values: - * - * == UNSET [0] → cache not initialized yet - * == BUSY [-1] → some thread is initializing it at the moment - * any other → the cached PID - */ - -#define CACHED_PID_UNSET ((pid_t) 0) -#define CACHED_PID_BUSY ((pid_t) -1) - -static pid_t cached_pid = CACHED_PID_UNSET; - -void reset_cached_pid(void) { - /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ - cached_pid = CACHED_PID_UNSET; -} - -/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc - * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against - * libpthread, as it is part of glibc anyway. */ -extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle); -extern void* __dso_handle __attribute__ ((__weak__)); - -pid_t getpid_cached(void) { - static bool installed = false; - pid_t current_value; - - /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a - * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally - * cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when - * objects were used across fork()s. With this caching the old behaviour is somewhat restored. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=1443976 - * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e - */ - - current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); - - switch (current_value) { - - case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ - pid_t new_pid; - - new_pid = raw_getpid(); - - if (!installed) { - /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's - * only half-documented (glibc doesn't document it but LSB does — though only superficially) - * we'll check for errors only in the most generic fashion possible. */ - - if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { - /* OOM? Let's try again later */ - cached_pid = CACHED_PID_UNSET; - return new_pid; - } - - installed = true; - } - - cached_pid = new_pid; - return new_pid; - } - - case CACHED_PID_BUSY: /* Somebody else is currently initializing */ - return raw_getpid(); - - default: /* Properly initialized */ - return current_value; - } -} - -#if 0 /* NM_IGNORED */ -int must_be_root(void) { - - if (geteuid() == 0) - return 0; - - log_error("Need to be root."); - return -EPERM; -} - -int safe_fork_full( - const char *name, - const int except_fds[], - size_t n_except_fds, - ForkFlags flags, - pid_t *ret_pid) { - - pid_t original_pid, pid; - sigset_t saved_ss, ss; - bool block_signals = false; - int prio, r; - - /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always - * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ - - prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG; - - original_pid = getpid_cached(); - - if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { - - /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can - * be sure that SIGTERMs are not lost we might send to the child. */ - - if (sigfillset(&ss) < 0) - return log_full_errno(prio, errno, "Failed to reset signal set: %m"); - - block_signals = true; - - } else if (flags & FORK_WAIT) { - - /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ - - if (sigemptyset(&ss) < 0) - return log_full_errno(prio, errno, "Failed to clear signal set: %m"); - - if (sigaddset(&ss, SIGCHLD) < 0) - return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m"); - - block_signals = true; - } - - if (block_signals) - if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) - return log_full_errno(prio, errno, "Failed to set signal mask: %m"); - - if (flags & FORK_NEW_MOUNTNS) - pid = raw_clone(SIGCHLD|CLONE_NEWNS); - else - pid = fork(); - if (pid < 0) { - r = -errno; - - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - - return log_full_errno(prio, r, "Failed to fork: %m"); - } - if (pid > 0) { - /* We are in the parent process */ - - log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); - - if (flags & FORK_WAIT) { - r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); - if (r < 0) - return r; - if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */ - return -EPROTO; - } - - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - - if (ret_pid) - *ret_pid = pid; - - return 1; - } - - /* We are in the child process */ - - if (flags & FORK_REOPEN_LOG) { - /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ - log_close(); - log_set_open_when_needed(true); - } - - if (name) { - r = rename_process(name); - if (r < 0) - log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG, - r, "Failed to rename process, ignoring: %m"); - } - - if (flags & FORK_DEATHSIG) - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { - log_full_errno(prio, errno, "Failed to set death signal: %m"); - _exit(EXIT_FAILURE); - } - - if (flags & FORK_RESET_SIGNALS) { - r = reset_all_signal_handlers(); - if (r < 0) { - log_full_errno(prio, r, "Failed to reset signal handlers: %m"); - _exit(EXIT_FAILURE); - } - - /* This implicitly undoes the signal mask stuff we did before the fork()ing above */ - r = reset_signal_mask(); - if (r < 0) { - log_full_errno(prio, r, "Failed to reset signal mask: %m"); - _exit(EXIT_FAILURE); - } - } else if (block_signals) { /* undo what we did above */ - if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) { - log_full_errno(prio, errno, "Failed to restore signal mask: %m"); - _exit(EXIT_FAILURE); - } - } - - if (flags & FORK_DEATHSIG) { - pid_t ppid; - /* Let's see if the parent PID is still the one we started from? If not, then the parent - * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */ - - ppid = getppid(); - if (ppid == 0) - /* Parent is in a differn't PID namespace. */; - else if (ppid != original_pid) { - log_debug("Parent died early, raising SIGTERM."); - (void) raise(SIGTERM); - _exit(EXIT_FAILURE); - } - } - - if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) { - - /* Optionally, make sure we never propagate mounts to the host. */ - - if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { - log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m"); - _exit(EXIT_FAILURE); - } - } - - if (flags & FORK_CLOSE_ALL_FDS) { - /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ - log_close(); - - r = close_all_fds(except_fds, n_except_fds); - if (r < 0) { - log_full_errno(prio, r, "Failed to close all file descriptors: %m"); - _exit(EXIT_FAILURE); - } - } - - /* When we were asked to reopen the logs, do so again now */ - if (flags & FORK_REOPEN_LOG) { - log_open(); - log_set_open_when_needed(false); - } - - if (flags & FORK_NULL_STDIO) { - r = make_null_stdio(); - if (r < 0) { - log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); - _exit(EXIT_FAILURE); - } - } - - if (ret_pid) - *ret_pid = getpid_cached(); - - return 0; -} - -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) { - bool stdout_is_tty, stderr_is_tty; - size_t n, i; - va_list ap; - char **l; - int r; - - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when we go away */ - - r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid); - if (r < 0) - return r; - if (r > 0) - return 0; - - /* In the child: */ - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - safe_close_above_stdio(fd); - } - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = newa(char*, n + 1); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - -int set_oom_score_adjust(int value) { - char t[DECIMAL_STR_MAX(int)]; - - sprintf(t, "%i", value); - - return write_string_file("/proc/self/oom_score_adj", t, - WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); -} - -static const char *const ioprio_class_table[] = { - [IOPRIO_CLASS_NONE] = "none", - [IOPRIO_CLASS_RT] = "realtime", - [IOPRIO_CLASS_BE] = "best-effort", - [IOPRIO_CLASS_IDLE] = "idle" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES); - -static const char *const sigchld_code_table[] = { - [CLD_EXITED] = "exited", - [CLD_KILLED] = "killed", - [CLD_DUMPED] = "dumped", - [CLD_TRAPPED] = "trapped", - [CLD_STOPPED] = "stopped", - [CLD_CONTINUED] = "continued", -}; - -DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int); - -static const char* const sched_policy_table[] = { - [SCHED_OTHER] = "other", - [SCHED_BATCH] = "batch", - [SCHED_IDLE] = "idle", - [SCHED_FIFO] = "fifo", - [SCHED_RR] = "rr" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h deleted file mode 100644 index e11164bd..00000000 --- a/src/systemd/src/basic/process-util.h +++ /dev/null @@ -1,200 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <errno.h> -#include <sched.h> -#include <signal.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/resource.h> -#include <sys/types.h> - -#include "format-util.h" -#include "ioprio.h" -#include "macro.h" -#include "time-util.h" - -#define procfs_file_alloca(pid, field) \ - ({ \ - pid_t _pid_ = (pid); \ - const char *_r_; \ - if (_pid_ == 0) { \ - _r_ = ("/proc/self/" field); \ - } else { \ - _r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ - sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ - } \ - _r_; \ - }) - -int get_process_state(pid_t pid); -int get_process_comm(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); -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); -int get_process_ppid(pid_t pid, pid_t *ppid); - -int wait_for_terminate(pid_t pid, siginfo_t *status); - -typedef enum WaitFlags { - WAIT_LOG_ABNORMAL = 1 << 0, - WAIT_LOG_NON_ZERO_EXIT_STATUS = 1 << 1, - - /* A shortcut for requesting the most complete logging */ - WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, -} WaitFlags; - -int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags); -int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); - -void sigkill_wait(pid_t pid); -void sigkill_waitp(pid_t *pid); -void sigterm_wait(pid_t pid); - -int kill_and_sigcont(pid_t pid, int sig); - -int rename_process(const char name[]); -int is_kernel_thread(pid_t pid); - -int getenv_for_pid(pid_t pid, const char *field, char **_value); - -bool pid_is_alive(pid_t pid); -bool pid_is_unwaited(pid_t pid); -int pid_from_same_root_fs(pid_t pid); - -bool is_main_thread(void); - -_noreturn_ void freeze(void); - -bool oom_score_adjust_is_valid(int oa); - -#ifndef PERSONALITY_INVALID -/* personality(7) documents that 0xffffffffUL is used for querying the - * current personality, hence let's use that here as error - * indicator. */ -#define PERSONALITY_INVALID 0xffffffffLU -#endif - -unsigned long personality_from_string(const char *p); -const char *personality_to_string(unsigned long); - -int safe_personality(unsigned long p); -int opinionated_personality(unsigned long *ret); - -int ioprio_class_to_string_alloc(int i, char **s); -int ioprio_class_from_string(const char *s); - -const char *sigchld_code_to_string(int i) _const_; -int sigchld_code_from_string(const char *s) _pure_; - -int sched_policy_to_string_alloc(int i, char **s); -int sched_policy_from_string(const char *s); - -static inline pid_t PTR_TO_PID(const void *p) { - return (pid_t) ((uintptr_t) p); -} - -static inline void* PID_TO_PTR(pid_t pid) { - return (void*) ((uintptr_t) pid); -} - -void valgrind_summary_hack(void); - -int pid_compare_func(const void *a, const void *b); - -#if 0 /* NM_IGNORED */ -static inline bool nice_is_valid(int n) { - return n >= PRIO_MIN && n < PRIO_MAX; -} - -static inline bool sched_policy_is_valid(int i) { - return IN_SET(i, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR); -} - -static inline bool sched_priority_is_valid(int i) { - return i >= 0 && i <= sched_get_priority_max(SCHED_RR); -} - -static inline bool ioprio_class_is_valid(int i) { - return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); -} - -static inline bool ioprio_priority_is_valid(int i) { - return i >= 0 && i < IOPRIO_BE_NR; -} - -static inline bool pid_is_valid(pid_t p) { - return p > 0; -} - -static inline int sched_policy_to_string_alloc_with_check(int n, char **s) { - if (!sched_policy_is_valid(n)) - return -EINVAL; - - return sched_policy_to_string_alloc(n, s); -} -#endif /* NM_IGNORED */ - -int ioprio_parse_priority(const char *s, int *ret); - -pid_t getpid_cached(void); -void reset_cached_pid(void); - -int must_be_root(void); - -typedef enum ForkFlags { - FORK_RESET_SIGNALS = 1 << 0, - FORK_CLOSE_ALL_FDS = 1 << 1, - FORK_DEATHSIG = 1 << 2, - FORK_NULL_STDIO = 1 << 3, - FORK_REOPEN_LOG = 1 << 4, - FORK_LOG = 1 << 5, - FORK_WAIT = 1 << 6, - FORK_NEW_MOUNTNS = 1 << 7, - FORK_MOUNTNS_SLAVE = 1 << 8, -} ForkFlags; - -int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); - -static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { - return safe_fork_full(name, NULL, 0, flags, ret_pid); -} - -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...); - -int set_oom_score_adjust(int value); - -#if SIZEOF_PID_T == 4 -/* The highest possibly (theoretic) pid_t value on this architecture. */ -#define PID_T_MAX ((pid_t) INT32_MAX) -/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value - * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the - * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by - * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since - * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at - * least to define them here too. */ -#define TASKS_MAX 4194303U -#elif SIZEOF_PID_T == 2 -#define PID_T_MAX ((pid_t) INT16_MAX) -#define TASKS_MAX 32767U -#else -#error "Unknown pid_t size" -#endif - -assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX) - -/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */ -#define TAKE_PID(pid) \ - ({ \ - pid_t _pid_ = (pid); \ - (pid) = 0; \ - _pid_; \ - }) diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c deleted file mode 100644 index 5c07e067..00000000 --- a/src/systemd/src/basic/random-util.c +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#ifdef __x86_64__ -#include <cpuid.h> -#endif - -#include <elf.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/random.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> - -#if HAVE_SYS_AUXV_H -# include <sys/auxv.h> -#endif - -#if USE_SYS_RANDOM_H -# include <sys/random.h> -#else -# include <linux/random.h> -#endif - -#include "fd-util.h" -#include "io-util.h" -#include "missing.h" -#include "random-util.h" -#include "time-util.h" - - -int rdrand64(uint64_t *ret) { - -#ifdef __x86_64__ - static int have_rdrand = -1; - unsigned char err; - - if (have_rdrand < 0) { - uint32_t eax, ebx, ecx, edx; - - /* Check if RDRAND is supported by the CPU */ - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - - have_rdrand = !!(ecx & (1U << 30)); - } - - if (have_rdrand == 0) - return -EOPNOTSUPP; - - asm volatile("rdrand %0;" - "setc %1" - : "=r" (*ret), - "=qm" (err)); - if (!err) - return -EAGAIN; - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { - static int have_syscall = -1; - - _cleanup_close_ int fd = -1; - size_t already_done = 0; - int r; - - /* Gathers some randomness from the kernel. This call will never block. If - * high_quality_required, it will always return some data from the kernel, - * regardless of whether the random pool is fully initialized or not. - * Otherwise, it will return success if at least some random bytes were - * successfully acquired, and an error if the kernel has no entropy whatsover - * for us. */ - - /* Use the getrandom() syscall unless we know we don't have it. */ - if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { -#if !HAVE_GETRANDOM - /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. - * If you don't compile against a libc that provides getrandom(), you don't get it. */ - r = -1; - errno = ENOSYS; -#else - r = getrandom(p, n, GRND_NONBLOCK); -#endif - if (r > 0) { - have_syscall = true; - if ((size_t) r == n) - return 0; - if (!high_quality_required) { - /* Fill in the remaining bytes using pseudorandom values */ - pseudorandom_bytes((uint8_t*) p + r, n - r); - return 0; - } - - already_done = r; - } else if (errno == ENOSYS) - /* We lack the syscall, continue with reading from /dev/urandom. */ - have_syscall = false; - else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to - * use the syscall the next time again though. - * - * If high_quality_required is false, return an error so that - * random_bytes() can produce some pseudorandom - * bytes. Otherwise, fall back to /dev/urandom, which we know - * is empty, but the kernel will produce some bytes for us on - * a best-effort basis. */ - have_syscall = true; - - if (!high_quality_required) { - uint64_t u; - size_t k; - - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality - * randomness is not required, as we don't trust it (who does?). Note that we only do a - * single iteration of RDRAND here, even though the Intel docs suggest calling this in - * a tight loop of 10 invocatins or so. That's because we don't really care about the - * quality here. */ - - if (rdrand64(&u) < 0) - return -ENODATA; - - k = MIN(n, sizeof(u)); - memcpy(p, &u, k); - - /* We only get 64bit out of RDRAND, the rest let's fill up with pseudo-random crap. */ - pseudorandom_bytes((uint8_t*) p + k, n - k); - return 0; - } - } else - return -errno; - } - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true); -} - -void initialize_srand(void) { - static bool srand_called = false; - unsigned x; -#if HAVE_SYS_AUXV_H - void *auxv; -#endif - - if (srand_called) - return; - -#if HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's - * try to make use of that to seed the pseudo-random generator. It's - * better than nothing... */ - - auxv = (void*) getauxval(AT_RANDOM); - if (auxv) { - assert_cc(sizeof(x) <= 16); - memcpy(&x, auxv, sizeof(x)); - } else -#endif - x = 0; - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - srand(x); - srand_called = true; -} - -/* INT_MAX gives us only 31 bits, so use 24 out of that. */ -#if RAND_MAX >= INT_MAX -# define RAND_STEP 3 -#else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */ -# define RAND_STEP 1 -#endif - -void pseudorandom_bytes(void *p, size_t n) { - uint8_t *q; - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) { - unsigned rr; - - rr = (unsigned) rand(); - -#if RAND_STEP >= 3 - if ((size_t) (q - (uint8_t*) p + 2) < n) - q[2] = rr >> 16; -#endif -#if RAND_STEP >= 2 - if ((size_t) (q - (uint8_t*) p + 1) < n) - q[1] = rr >> 8; -#endif - q[0] = rr; - } -} - -void random_bytes(void *p, size_t n) { - int r; - - r = acquire_random_bytes(p, n, false); - if (r >= 0) - return; - - /* If some idiot made /dev/urandom unavailable to us, or the - * kernel has no entropy, use a PRNG instead. */ - return pseudorandom_bytes(p, n); -} diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h deleted file mode 100644 index affcc9ac..00000000 --- a/src/systemd/src/basic/random-util.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -int acquire_random_bytes(void *p, size_t n, bool high_quality_required); -void pseudorandom_bytes(void *p, size_t n); -void random_bytes(void *p, size_t n); -void initialize_srand(void); - -static inline uint64_t random_u64(void) { - uint64_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -static inline uint32_t random_u32(void) { - uint32_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -int rdrand64(uint64_t *ret); diff --git a/src/systemd/src/basic/refcnt.h b/src/systemd/src/basic/refcnt.h deleted file mode 100644 index d2be6086..00000000 --- a/src/systemd/src/basic/refcnt.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -/* A type-safe atomic refcounter. - * - * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ - -typedef struct { - volatile unsigned _value; -} RefCount; - -#define REFCNT_GET(r) ((r)._value) -#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1)) -#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1)) - -#define REFCNT_INIT ((RefCount) { ._value = 1 }) diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h deleted file mode 100644 index 66471381..00000000 --- a/src/systemd/src/basic/set.h +++ /dev/null @@ -1,132 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include "extract-word.h" -#include "hashmap.h" -#include "macro.h" - -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) - -static inline Set *set_free(Set *s) { - internal_hashmap_free(HASHMAP_BASE(s)); - return NULL; -} - -static inline Set *set_free_free(Set *s) { - internal_hashmap_free_free(HASHMAP_BASE(s)); - return NULL; -} - -/* no set_free_free_free */ - -static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int set_put(Set *s, const void *key); -/* no set_update */ -/* no set_replace */ -static inline void *set_get(Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE(s), key); -} -/* no set_get2 */ - -static inline bool set_contains(Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(s), key); -} - -static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); -} - -/* no set_remove2 */ -/* no set_remove_value */ -int set_remove_and_put(Set *s, const void *old_key, const void *new_key); -/* no set_remove_and_replace */ -int set_merge(Set *s, Set *other); - -static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); -} - -static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); -} - -static inline unsigned set_size(Set *s) { - return internal_hashmap_size(HASHMAP_BASE(s)); -} - -static inline bool set_isempty(Set *s) { - return set_size(s) == 0; -} - -static inline unsigned set_buckets(Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE(s)); -} - -bool set_iterate(Set *s, Iterator *i, void **value); - -static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s)); -} - -static inline void set_clear_free(Set *s) { - internal_hashmap_clear_free(HASHMAP_BASE(s)); -} - -/* no set_clear_free_free */ - -static inline void *set_steal_first(Set *s) { - return internal_hashmap_steal_first(HASHMAP_BASE(s)); -} - -#define set_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = set_steal_first(_s))) \ - _f(_item); \ - }) -#define set_free_with_destructor(_s, _f) \ - ({ \ - set_clear_with_destructor(_s, _f); \ - set_free(_s); \ - }) - -/* no set_steal_first_key */ -/* no set_first_key */ - -static inline void *set_first(Set *s) { - return internal_hashmap_first(HASHMAP_BASE(s)); -} - -/* no set_next */ - -static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); -} - -int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); - -#define SET_FOREACH(e, s, i) \ - for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) - -#define SET_FOREACH_MOVE(e, d, s) \ - for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); - -#define _cleanup_set_free_ _cleanup_(set_freep) -#define _cleanup_set_free_free_ _cleanup_(set_free_freep) diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h deleted file mode 100644 index 92f2804c..00000000 --- a/src/systemd/src/basic/signal-util.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <signal.h> - -#include "macro.h" - -int reset_all_signal_handlers(void); -int reset_signal_mask(void); - -int ignore_signals(int sig, ...); -int default_signals(int sig, ...); -int sigaction_many(const struct sigaction *sa, ...); - -int sigset_add_many(sigset_t *ss, ...); -int sigprocmask_many(int how, sigset_t *old, ...); - -const char *signal_to_string(int i) _const_; -int signal_from_string(const char *s) _pure_; - -void nop_signal_handler(int sig); - -static inline void block_signals_reset(sigset_t *ss) { - assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); -} - -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ - sigset_t _t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \ - _t; \ - }) - -static inline bool SIGNAL_VALID(int signo) { - return signo > 0 && signo < _NSIG; -} - -static inline const char* signal_to_string_with_check(int n) { - if (!SIGNAL_VALID(n)) - return NULL; - - return signal_to_string(n); -} diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h deleted file mode 100644 index 77bb9a8c..00000000 --- a/src/systemd/src/basic/siphash24.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#if 0 /* NM_IGNORED */ -struct siphash { - uint64_t v0; - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t padding; - size_t inlen; -}; -#else /* NM_IGNORED */ -struct siphash { - CSipHash _csiphash; -}; - -static inline void -siphash24_init (struct siphash *state, const uint8_t k[16]) -{ - c_siphash_init ((CSipHash *) state, k); -} - -static inline void -siphash24_compress (const void *in, size_t inlen, struct siphash *state) -{ - c_siphash_append ((CSipHash *) state, in, inlen); -} - -static inline uint64_t -siphash24_finalize (struct siphash *state) -{ - return c_siphash_finalize ((CSipHash *) state); -} - -static inline uint64_t -siphash24 (const void *in, size_t inlen, const uint8_t k[16]) -{ - return c_siphash_hash (k, in, inlen); -} -#endif /* NM_IGNORED */ - -void siphash24_init(struct siphash *state, const uint8_t k[16]); -void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) - -uint64_t siphash24_finalize(struct siphash *state); - -uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c deleted file mode 100644 index acc22901..00000000 --- a/src/systemd/src/basic/socket-util.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <arpa/inet.h> -#include <errno.h> -#include <limits.h> -#include <net/if.h> -#include <netdb.h> -#include <netinet/ip.h> -#include <poll.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "format-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "utf8.h" -#include "util.h" - -#if 0 /* NM_IGNORED */ -#if ENABLE_IDN -# define IDN_FLAGS NI_IDN -#else -# define IDN_FLAGS 0 -#endif - -static const char* const socket_address_type_table[] = { - [SOCK_STREAM] = "Stream", - [SOCK_DGRAM] = "Datagram", - [SOCK_RAW] = "Raw", - [SOCK_RDM] = "ReliableDatagram", - [SOCK_SEQPACKET] = "SequentialPacket", - [SOCK_DCCP] = "DatagramCongestionControl", -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); - -int socket_address_parse(SocketAddress *a, const char *s) { - _cleanup_free_ char *n = NULL; - char *e; - int r; - - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_STREAM; - - if (*s == '[') { - uint16_t port; - - /* IPv6 in [x:.....:z]:p notation */ - - e = strchr(s+1, ']'); - if (!e) - return -EINVAL; - - n = strndup(s+1, e-s-1); - if (!n) - return -ENOMEM; - - errno = 0; - if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) - return errno > 0 ? -errno : -EINVAL; - - e++; - if (*e != ':') - return -EINVAL; - - e++; - r = parse_ip_port(e, &port); - if (r < 0) - return r; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->size = sizeof(struct sockaddr_in6); - - } else if (*s == '/') { - /* AF_UNIX socket */ - - size_t l; - - l = strlen(s); - if (l >= sizeof(a->sockaddr.un.sun_path)) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path, s, l); - a->size = offsetof(struct sockaddr_un, sun_path) + l + 1; - - } else if (*s == '@') { - /* Abstract AF_UNIX socket */ - size_t l; - - l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path+1, s+1, l); - a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l; - - } else if (startswith(s, "vsock:")) { - /* AF_VSOCK socket in vsock:cid:port notation */ - const char *cid_start = s + STRLEN("vsock:"); - unsigned port; - - e = strchr(cid_start, ':'); - if (!e) - return -EINVAL; - - r = safe_atou(e+1, &port); - if (r < 0) - return r; - - n = strndup(cid_start, e - cid_start); - if (!n) - return -ENOMEM; - - if (!isempty(n)) { - r = safe_atou(n, &a->sockaddr.vm.svm_cid); - if (r < 0) - return r; - } else - a->sockaddr.vm.svm_cid = VMADDR_CID_ANY; - - a->sockaddr.vm.svm_family = AF_VSOCK; - a->sockaddr.vm.svm_port = port; - a->size = sizeof(struct sockaddr_vm); - - } else { - uint16_t port; - - e = strchr(s, ':'); - if (e) { - r = parse_ip_port(e + 1, &port); - if (r < 0) - return r; - - n = strndup(s, e-s); - if (!n) - return -ENOMEM; - - /* IPv4 in w.x.y.z:p notation? */ - r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); - if (r < 0) - return -errno; - - if (r > 0) { - /* Gotcha, it's a traditional IPv4 address */ - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16(port); - a->size = sizeof(struct sockaddr_in); - } else { - unsigned idx; - - if (strlen(n) > IF_NAMESIZE-1) - return -EINVAL; - - /* Uh, our last resort, an interface name */ - idx = if_nametoindex(n); - if (idx == 0) - return -EINVAL; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->sockaddr.in6.sin6_scope_id = idx; - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } - } else { - - /* Just a port */ - r = parse_ip_port(s, &port); - if (r < 0) - return r; - - if (socket_ipv6_is_supported()) { - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } else { - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16(port); - a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; - a->size = sizeof(struct sockaddr_in); - } - } - } - - return 0; -} - -int socket_address_parse_and_warn(SocketAddress *a, const char *s) { - SocketAddress b; - int r; - - /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */ - - r = socket_address_parse(&b, s); - if (r < 0) - return r; - - if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) { - log_warning("Binding to IPv6 address not available since kernel does not support IPv6."); - return -EAFNOSUPPORT; - } - - *a = b; - return 0; -} - -int socket_address_parse_netlink(SocketAddress *a, const char *s) { - int family; - unsigned group = 0; - _cleanup_free_ char *sfamily = NULL; - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_RAW; - - errno = 0; - if (sscanf(s, "%ms %u", &sfamily, &group) < 1) - return errno > 0 ? -errno : -EINVAL; - - family = netlink_family_from_string(sfamily); - if (family < 0) - return -EINVAL; - - a->sockaddr.nl.nl_family = AF_NETLINK; - a->sockaddr.nl.nl_groups = group; - - a->type = SOCK_RAW; - a->size = sizeof(struct sockaddr_nl); - a->protocol = family; - - return 0; -} - -int socket_address_verify(const SocketAddress *a) { - assert(a); - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->size != sizeof(struct sockaddr_in)) - return -EINVAL; - - if (a->sockaddr.in.sin_port == 0) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_INET6: - if (a->size != sizeof(struct sockaddr_in6)) - return -EINVAL; - - if (a->sockaddr.in6.sin6_port == 0) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_UNIX: - if (a->size < offsetof(struct sockaddr_un, sun_path)) - return -EINVAL; - - if (a->size > offsetof(struct sockaddr_un, sun_path)) { - - if (a->sockaddr.un.sun_path[0] != 0) { - char *e; - - /* path */ - e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); - if (!e) - return -EINVAL; - - if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) - return -EINVAL; - } - } - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) - return -EINVAL; - - return 0; - - case AF_NETLINK: - - if (a->size != sizeof(struct sockaddr_nl)) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_RAW, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_VSOCK: - if (a->size != sizeof(struct sockaddr_vm)) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int socket_address_print(const SocketAddress *a, char **ret) { - int r; - - assert(a); - assert(ret); - - r = socket_address_verify(a); - if (r < 0) - return r; - - if (socket_address_family(a) == AF_NETLINK) { - _cleanup_free_ char *sfamily = NULL; - - r = netlink_family_to_string_alloc(a->protocol, &sfamily); - if (r < 0) - return r; - - r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups); - if (r < 0) - return -ENOMEM; - - return 0; - } - - return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret); -} - -bool socket_address_can_accept(const SocketAddress *a) { - assert(a); - - return - IN_SET(a->type, SOCK_STREAM, SOCK_SEQPACKET); -} - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { - assert(a); - assert(b); - - /* Invalid addresses are unequal to all */ - if (socket_address_verify(a) < 0 || - socket_address_verify(b) < 0) - return false; - - if (a->type != b->type) - return false; - - if (socket_address_family(a) != socket_address_family(b)) - return false; - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr) - return false; - - if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port) - return false; - - break; - - case AF_INET6: - if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0) - return false; - - if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port) - return false; - - break; - - case AF_UNIX: - if (a->size <= offsetof(struct sockaddr_un, sun_path) || - b->size <= offsetof(struct sockaddr_un, sun_path)) - return false; - - if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0)) - return false; - - if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, 0)) - return false; - } else { - if (a->size != b->size) - return false; - - if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0) - return false; - } - - break; - - case AF_NETLINK: - if (a->protocol != b->protocol) - return false; - - if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups) - return false; - - break; - - case AF_VSOCK: - if (a->sockaddr.vm.svm_cid != b->sockaddr.vm.svm_cid) - return false; - - if (a->sockaddr.vm.svm_port != b->sockaddr.vm.svm_port) - return false; - - break; - - default: - /* Cannot compare, so we assume the addresses are different */ - return false; - } - - return true; -} - -bool socket_address_is(const SocketAddress *a, const char *s, int type) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse(&b, s) < 0) - return false; - - b.type = type; - - return socket_address_equal(a, &b); -} - -bool socket_address_is_netlink(const SocketAddress *a, const char *s) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse_netlink(&b, s) < 0) - return false; - - return socket_address_equal(a, &b); -} - -const char* socket_address_get_path(const SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return NULL; - - if (a->sockaddr.un.sun_path[0] == 0) - return NULL; - - return a->sockaddr.un.sun_path; -} - -bool socket_ipv6_is_supported(void) { - if (access("/proc/net/if_inet6", F_OK) != 0) - return false; - - return true; -} - -bool socket_address_matches_fd(const SocketAddress *a, int fd) { - SocketAddress b; - socklen_t solen; - - assert(a); - assert(fd >= 0); - - b.size = sizeof(b.sockaddr); - if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0) - return false; - - if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family) - return false; - - solen = sizeof(b.type); - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0) - return false; - - if (b.type != a->type) - return false; - - if (a->protocol != 0) { - solen = sizeof(b.protocol); - if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0) - return false; - - if (b.protocol != a->protocol) - return false; - } - - return socket_address_equal(a, &b); -} - -int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - - /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ - - assert(sa); - - switch (sa->sa.sa_family) { - - case AF_INET: - *ret_port = be16toh(sa->in.sin_port); - return 0; - - case AF_INET6: - *ret_port = be16toh(sa->in6.sin6_port); - return 0; - - case AF_VSOCK: - *ret_port = sa->vm.svm_port; - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - char *p; - int r; - - assert(sa); - assert(salen >= sizeof(sa->sa.sa_family)); - - switch (sa->sa.sa_family) { - - case AF_INET: { - uint32_t a; - - a = be32toh(sa->in.sin_addr.s_addr); - - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - be16toh(sa->in.sin_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); - if (r < 0) - return -ENOMEM; - break; - } - - case AF_INET6: { - static const unsigned char ipv4_prefix[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF - }; - - if (translate_ipv6 && - memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { - const uint8_t *a = sa->in6.sin6_addr.s6_addr+12; - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a[0], a[1], a[2], a[3], - be16toh(sa->in6.sin6_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a[0], a[1], a[2], a[3]); - if (r < 0) - return -ENOMEM; - } else { - char a[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); - - if (include_port) { - r = asprintf(&p, - "[%s]:%u", - a, - be16toh(sa->in6.sin6_port)); - if (r < 0) - return -ENOMEM; - } else { - p = strdup(a); - if (!p) - return -ENOMEM; - } - } - - break; - } - - case AF_UNIX: - if (salen <= offsetof(struct sockaddr_un, sun_path)) { - p = strdup("<unnamed>"); - if (!p) - return -ENOMEM; - - } else if (sa->un.sun_path[0] == 0) { - /* abstract */ - - /* FIXME: We assume we can print the - * socket path here and that it hasn't - * more than one NUL byte. That is - * actually an invalid assumption */ - - p = new(char, sizeof(sa->un.sun_path)+1); - if (!p) - return -ENOMEM; - - p[0] = '@'; - memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1); - p[sizeof(sa->un.sun_path)] = 0; - - } else { - p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path)); - if (!p) - return -ENOMEM; - } - - break; - - case AF_VSOCK: - if (include_port) - r = asprintf(&p, - "vsock:%u:%u", - sa->vm.svm_cid, - sa->vm.svm_port); - else - r = asprintf(&p, "vsock:%u", sa->vm.svm_cid); - if (r < 0) - return -ENOMEM; - break; - - default: - return -EOPNOTSUPP; - } - - *ret = p; - return 0; -} - -int getpeername_pretty(int fd, bool include_port, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - int r; - - assert(fd >= 0); - assert(ret); - - if (getpeername(fd, &sa.sa, &salen) < 0) - return -errno; - - if (sa.sa.sa_family == AF_UNIX) { - struct ucred ucred = {}; - - /* UNIX connection sockets are anonymous, so let's use - * PID/UID as pretty credentials instead */ - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0) - return -ENOMEM; - - return 0; - } - - /* For remote sockets we translate IPv6 addresses back to IPv4 - * if applicable, since that's nicer. */ - - return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); -} - -int getsockname_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - /* For local sockets we do not translate IPv6 addresses back - * to IPv6 if applicable, since this is usually used for - * listening sockets where the difference between IPv4 and - * IPv6 matters. */ - - return sockaddr_pretty(&sa.sa, salen, false, true, ret); -} - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) { - int r; - char host[NI_MAXHOST], *ret; - - assert(_ret); - - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS); - if (r != 0) { - int saved_errno = errno; - - r = sockaddr_pretty(&sa->sa, salen, true, true, &ret); - if (r < 0) - return r; - - log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); - } else { - ret = strdup(host); - if (!ret) - return -ENOMEM; - } - - *_ret = ret; - return 0; -} - -int socket_address_unlink(SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return 0; - - if (a->sockaddr.un.sun_path[0] == 0) - return 0; - - if (unlink(a->sockaddr.un.sun_path) < 0) - return -errno; - - return 1; -} - -static const char* const netlink_family_table[] = { - [NETLINK_ROUTE] = "route", - [NETLINK_FIREWALL] = "firewall", - [NETLINK_INET_DIAG] = "inet-diag", - [NETLINK_NFLOG] = "nflog", - [NETLINK_XFRM] = "xfrm", - [NETLINK_SELINUX] = "selinux", - [NETLINK_ISCSI] = "iscsi", - [NETLINK_AUDIT] = "audit", - [NETLINK_FIB_LOOKUP] = "fib-lookup", - [NETLINK_CONNECTOR] = "connector", - [NETLINK_NETFILTER] = "netfilter", - [NETLINK_IP6_FW] = "ip6-fw", - [NETLINK_DNRTMSG] = "dnrtmsg", - [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", - [NETLINK_GENERIC] = "generic", - [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs", - [NETLINK_RDMA] = "rdma", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); - -static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = { - [SOCKET_ADDRESS_DEFAULT] = "default", - [SOCKET_ADDRESS_BOTH] = "both", - [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); - -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *n) { - int r; - - r = parse_boolean(n); - if (r > 0) - return SOCKET_ADDRESS_IPV6_ONLY; - if (r == 0) - return SOCKET_ADDRESS_BOTH; - - return socket_address_bind_ipv6_only_from_string(n); -} - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { - assert(a); - assert(b); - - if (a->sa.sa_family != b->sa.sa_family) - return false; - - if (a->sa.sa_family == AF_INET) - return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr; - - if (a->sa.sa_family == AF_INET6) - return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0; - - if (a->sa.sa_family == AF_VSOCK) - return a->vm.svm_cid == b->vm.svm_cid; - - return false; -} - -int fd_inc_sndbuf(int fd, size_t n) { - int r, value; - socklen_t l = sizeof(value); - - r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) - return 0; - - /* If we have the privileges we will ignore the kernel limit. */ - - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - return -errno; - - return 1; -} - -int fd_inc_rcvbuf(int fd, size_t n) { - int r, value; - socklen_t l = sizeof(value); - - r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) - return 0; - - /* If we have the privileges we will ignore the kernel limit. */ - - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - return -errno; - return 1; -} - -static const char* const ip_tos_table[] = { - [IPTOS_LOWDELAY] = "low-delay", - [IPTOS_THROUGHPUT] = "throughput", - [IPTOS_RELIABILITY] = "reliability", - [IPTOS_LOWCOST] = "low-cost", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); - -bool ifname_valid(const char *p) { - bool numeric = true; - - /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources - * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We - * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ - - if (isempty(p)) - return false; - - if (strlen(p) >= IFNAMSIZ) - return false; - - if (dot_or_dot_dot(p)) - return false; - - while (*p) { - if ((unsigned char) *p >= 127U) - return false; - - if ((unsigned char) *p <= 32U) - return false; - - if (IN_SET(*p, ':', '/')) - return false; - - numeric = numeric && (*p >= '0' && *p <= '9'); - p++; - } - - if (numeric) - return false; - - return true; -} - -bool address_label_valid(const char *p) { - - if (isempty(p)) - return false; - - if (strlen(p) >= IFNAMSIZ) - return false; - - while (*p) { - if ((uint8_t) *p >= 127U) - return false; - - if ((uint8_t) *p <= 31U) - return false; - p++; - } - - return true; -} - -int getpeercred(int fd, struct ucred *ucred) { - socklen_t n = sizeof(struct ucred); - struct ucred u; - int r; - - assert(fd >= 0); - assert(ucred); - - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n); - if (r < 0) - return -errno; - - if (n != sizeof(struct ucred)) - return -EIO; - - /* Check if the data is actually useful and not suppressed due to namespacing issues */ - if (!pid_is_valid(u.pid)) - return -ENODATA; - - /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of - * receiving in "invalid" user/group we get the overflow UID/GID. */ - - *ucred = u; - return 0; -} - -int getpeersec(int fd, char **ret) { - _cleanup_free_ char *s = NULL; - socklen_t n = 64; - - assert(fd >= 0); - assert(ret); - - for (;;) { - s = new0(char, n+1); - if (!s) - return -ENOMEM; - - if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) - break; - - if (errno != ERANGE) - return -errno; - - s = mfree(s); - } - - if (isempty(s)) - return -EOPNOTSUPP; - - *ret = TAKE_PTR(s); - - return 0; -} - -int getpeergroups(int fd, gid_t **ret) { - socklen_t n = sizeof(gid_t) * 64; - _cleanup_free_ gid_t *d = NULL; - - assert(fd >= 0); - assert(ret); - - for (;;) { - d = malloc(n); - if (!d) - return -ENOMEM; - - if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0) - break; - - if (errno != ERANGE) - return -errno; - - d = mfree(d); - } - - assert_se(n % sizeof(gid_t) == 0); - n /= sizeof(gid_t); - - if ((socklen_t) (int) n != n) - return -E2BIG; - - *ret = TAKE_PTR(d); - - return (int) n; -} - -ssize_t send_one_fd_iov_sa( - int transport_fd, - int fd, - struct iovec *iov, size_t iovlen, - const struct sockaddr *sa, socklen_t len, - int flags) { - - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_name = (struct sockaddr*) sa, - .msg_namelen = len, - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - ssize_t k; - - assert(transport_fd >= 0); - - /* - * We need either an FD or data to send. - * If there's nothing, return an error. - */ - if (fd < 0 && !iov) - return -EINVAL; - - if (fd >= 0) { - struct cmsghdr *cmsg; - - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); - - 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), &fd, sizeof(int)); - - mh.msg_controllen = CMSG_SPACE(sizeof(int)); - } - k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags); - if (k < 0) - return (ssize_t) -errno; - - return k; -} - -int send_one_fd_sa( - int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags) { - - assert(fd >= 0); - - return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags); -} - -ssize_t receive_one_fd_iov( - int transport_fd, - struct iovec *iov, size_t iovlen, - int flags, - int *ret_fd) { - - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - struct cmsghdr *cmsg, *found = NULL; - ssize_t k; - - assert(transport_fd >= 0); - assert(ret_fd); - - /* - * Receive a single FD via @transport_fd. We don't care for - * the transport-type. We retrieve a single FD at most, so for - * packet-based transports, the caller must ensure to send - * only a single FD per packet. This is best used in - * combination with send_one_fd(). - */ - - k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); - if (k < 0) - return (ssize_t) -errno; - - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - assert(!found); - found = cmsg; - break; - } - } - - if (!found) - cmsg_close_all(&mh); - - /* If didn't receive an FD or any data, return an error. */ - if (k == 0 && !found) - return -EIO; - - if (found) - *ret_fd = *(int*) CMSG_DATA(found); - else - *ret_fd = -1; - - return k; -} - -int receive_one_fd(int transport_fd, int flags) { - int fd; - ssize_t k; - - k = receive_one_fd_iov(transport_fd, NULL, 0, flags, &fd); - if (k == 0) - return fd; - - /* k must be negative, since receive_one_fd_iov() only returns - * a positive value if data was received through the iov. */ - assert(k < 0); - return (int) k; -} -#endif /* NM_IGNORED */ - -ssize_t next_datagram_size_fd(int fd) { - ssize_t l; - int k; - - /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will - * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't - * do. This difference is actually of major importance as we need to be sure that the size returned here - * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of - * the wrong size. */ - - l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); - if (l < 0) { - if (IN_SET(errno, EOPNOTSUPP, EFAULT)) - goto fallback; - - return -errno; - } - if (l == 0) - goto fallback; - - return l; - -fallback: - k = 0; - - /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD - * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ - - if (ioctl(fd, FIONREAD, &k) < 0) - return -errno; - - return (ssize_t) k; -} - -#if 0 /* NM_IGNORED */ -int flush_accept(int fd) { - - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - int r; - - /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ - - for (;;) { - int cfd; - - r = poll(&pollfd, 1, 0); - if (r < 0) { - if (errno == EINTR) - continue; - - return -errno; - - } else if (r == 0) - return 0; - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return 0; - - return -errno; - } - - close(cfd); - } -} - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { - struct cmsghdr *cmsg; - - assert(mh); - - CMSG_FOREACH(cmsg, mh) - if (cmsg->cmsg_level == level && - cmsg->cmsg_type == type && - (length == (socklen_t) -1 || length == cmsg->cmsg_len)) - return cmsg; - - return NULL; -} - -int socket_ioctl_fd(void) { - int fd; - - /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for - * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not - * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more - * generic AF_NETLINK. */ - - fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) - fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC); - if (fd < 0) - return -errno; - - return fd; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h deleted file mode 100644 index d7b814ae..00000000 --- a/src/systemd/src/basic/socket-util.h +++ /dev/null @@ -1,187 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <netinet/ether.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stddef.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <linux/netlink.h> -#include <linux/if_infiniband.h> -#include <linux/if_packet.h> - -#include "macro.h" -#include "missing.h" -#include "util.h" - -union sockaddr_union { - /* The minimal, abstract version */ - struct sockaddr sa; - - /* The libc provided version that allocates "enough room" for every protocol */ - struct sockaddr_storage storage; - - /* Protoctol-specific implementations */ - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_nl nl; - struct sockaddr_ll ll; -#if 0 /* NM_IGNORED */ - struct sockaddr_vm vm; -#endif /* NM_IGNORED */ - - /* Ensure there is enough space to store Infiniband addresses */ - uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; - - /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path - * component is always followed by at least one NUL byte. */ - uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; -}; - -typedef struct SocketAddress { - union sockaddr_union sockaddr; - - /* We store the size here explicitly due to the weird - * sockaddr_un semantics for abstract sockets */ - socklen_t size; - - /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ - int type; - - /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */ - int protocol; -} SocketAddress; - -typedef enum SocketAddressBindIPv6Only { - SOCKET_ADDRESS_DEFAULT, - SOCKET_ADDRESS_BOTH, - SOCKET_ADDRESS_IPV6_ONLY, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 -} SocketAddressBindIPv6Only; - -#define socket_address_family(a) ((a)->sockaddr.sa.sa_family) - -const char* socket_address_type_to_string(int t) _const_; -int socket_address_type_from_string(const char *s) _pure_; - -int socket_address_parse(SocketAddress *a, const char *s); -int socket_address_parse_and_warn(SocketAddress *a, const char *s); -int socket_address_parse_netlink(SocketAddress *a, const char *s); -int socket_address_print(const SocketAddress *a, char **p); -int socket_address_verify(const SocketAddress *a) _pure_; -int socket_address_unlink(SocketAddress *a); - -bool socket_address_can_accept(const SocketAddress *a) _pure_; - -int socket_address_listen( - const SocketAddress *a, - int flags, - int backlog, - SocketAddressBindIPv6Only only, - const char *bind_to_device, - bool reuse_port, - bool free_bind, - bool transparent, - mode_t directory_mode, - mode_t socket_mode, - const char *label); -int make_socket_fd(int log_level, const char* address, int type, int flags); - -bool socket_address_is(const SocketAddress *a, const char *s, int type); -bool socket_address_is_netlink(const SocketAddress *a, const char *s); - -bool socket_address_matches_fd(const SocketAddress *a, int fd); - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; - -const char* socket_address_get_path(const SocketAddress *a); - -bool socket_ipv6_is_supported(void); - -int sockaddr_port(const struct sockaddr *_sa, unsigned *port); - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); -int getpeername_pretty(int fd, bool include_port, char **ret); -int getsockname_pretty(int fd, char **ret); - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); - -const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *s); - -int netlink_family_to_string_alloc(int b, char **s); -int netlink_family_from_string(const char *s) _pure_; - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); - -int fd_inc_sndbuf(int fd, size_t n); -int fd_inc_rcvbuf(int fd, size_t n); - -int ip_tos_to_string_alloc(int i, char **s); -int ip_tos_from_string(const char *s); - -bool ifname_valid(const char *p); -bool address_label_valid(const char *p); - -int getpeercred(int fd, struct ucred *ucred); -int getpeersec(int fd, char **ret); -int getpeergroups(int fd, gid_t **ret); - -ssize_t send_one_fd_iov_sa( - int transport_fd, - int fd, - struct iovec *iov, size_t iovlen, - const struct sockaddr *sa, socklen_t len, - int flags); -int send_one_fd_sa(int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags); -#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags) -#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags) -ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd); -int receive_one_fd(int transport_fd, int flags); - -ssize_t next_datagram_size_fd(int fd); - -int flush_accept(int fd); - -#define CMSG_FOREACH(cmsg, mh) \ - for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); - -/* - * Certain hardware address types (e.g Infiniband) do not fit into sll_addr - * (8 bytes) and run over the structure. This macro returns the correct size that - * must be passed to kernel. - */ -#define SOCKADDR_LL_LEN(sa) \ - ({ \ - const struct sockaddr_ll *_sa = &(sa); \ - size_t _mac_len = sizeof(_sa->sll_addr); \ - assert(_sa->sll_family == AF_PACKET); \ - if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \ - _mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \ - if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \ - _mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \ - offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \ - }) - -/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ -#define SOCKADDR_UN_LEN(sa) \ - ({ \ - const struct sockaddr_un *_sa = &(sa); \ - assert(_sa->sun_family == AF_UNIX); \ - offsetof(struct sockaddr_un, sun_path) + \ - (_sa->sun_path[0] == 0 ? \ - 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ - strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ - }) - -int socket_ioctl_fd(void); diff --git a/src/systemd/src/basic/sparse-endian.h b/src/systemd/src/basic/sparse-endian.h deleted file mode 100644 index 5e59de54..00000000 --- a/src/systemd/src/basic/sparse-endian.h +++ /dev/null @@ -1,93 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef SPARSE_ENDIAN_H -#define SPARSE_ENDIAN_H - -#include <byteswap.h> -#include <endian.h> -#include <stdint.h> - -#ifdef __CHECKER__ -#define __sd_bitwise __attribute__((bitwise)) -#define __sd_force __attribute__((force)) -#else -#define __sd_bitwise -#define __sd_force -#endif - -typedef uint16_t __sd_bitwise le16_t; -typedef uint16_t __sd_bitwise be16_t; -typedef uint32_t __sd_bitwise le32_t; -typedef uint32_t __sd_bitwise be32_t; -typedef uint64_t __sd_bitwise le64_t; -typedef uint64_t __sd_bitwise be64_t; - -#undef htobe16 -#undef htole16 -#undef be16toh -#undef le16toh -#undef htobe32 -#undef htole32 -#undef be32toh -#undef le32toh -#undef htobe64 -#undef htole64 -#undef be64toh -#undef le64toh - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define bswap_16_on_le(x) __bswap_16(x) -#define bswap_32_on_le(x) __bswap_32(x) -#define bswap_64_on_le(x) __bswap_64(x) -#define bswap_16_on_be(x) (x) -#define bswap_32_on_be(x) (x) -#define bswap_64_on_be(x) (x) -#elif __BYTE_ORDER == __BIG_ENDIAN -#define bswap_16_on_le(x) (x) -#define bswap_32_on_le(x) (x) -#define bswap_64_on_le(x) (x) -#define bswap_16_on_be(x) __bswap_16(x) -#define bswap_32_on_be(x) __bswap_32(x) -#define bswap_64_on_be(x) __bswap_64(x) -#endif - -static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); } -static inline le32_t htole32(uint32_t value) { return (le32_t __sd_force) bswap_32_on_be(value); } -static inline le64_t htole64(uint64_t value) { return (le64_t __sd_force) bswap_64_on_be(value); } - -static inline be16_t htobe16(uint16_t value) { return (be16_t __sd_force) bswap_16_on_le(value); } -static inline be32_t htobe32(uint32_t value) { return (be32_t __sd_force) bswap_32_on_le(value); } -static inline be64_t htobe64(uint64_t value) { return (be64_t __sd_force) bswap_64_on_le(value); } - -static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __sd_force)value); } -static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __sd_force)value); } -static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __sd_force)value); } - -static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __sd_force)value); } -static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __sd_force)value); } -static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __sd_force)value); } - -#undef __sd_bitwise -#undef __sd_force - -#endif /* SPARSE_ENDIAN_H */ diff --git a/src/systemd/src/basic/stat-util.c b/src/systemd/src/basic/stat-util.c deleted file mode 100644 index d645ce30..00000000 --- a/src/systemd/src/basic/stat-util.c +++ /dev/null @@ -1,272 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/magic.h> -#include <sched.h> -#include <sys/stat.h> -#include <sys/statvfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include "dirent-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "macro.h" -#include "missing.h" -#include "stat-util.h" -#include "string-util.h" - -#if 0 /* NM_IGNORED */ -int is_symlink(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!S_ISLNK(info.st_mode); -} - -int is_dir(const char* path, bool follow) { - struct stat st; - int r; - - assert(path); - - if (follow) - r = stat(path, &st); - else - r = lstat(path, &st); - if (r < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -int is_dir_fd(int fd) { - struct stat st; - int r; - - r = fstat(fd, &st); - if (r < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -int is_device_node(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); -} - -int dir_is_empty(const char *path) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - - d = opendir(path); - if (!d) - return -errno; - - FOREACH_DIRENT(de, d, return -errno) - return 0; - - return 1; -} - -bool null_or_empty(struct stat *st) { - assert(st); - - if (S_ISREG(st->st_mode) && st->st_size <= 0) - return true; - - /* We don't want to hardcode the major/minor of /dev/null, - * hence we do a simpler "is this a device node?" check. */ - - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) - return true; - - return false; -} - -int null_or_empty_path(const char *fn) { - struct stat st; - - assert(fn); - - if (stat(fn, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -int null_or_empty_fd(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -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 files_same(const char *filea, const char *fileb, int flags) { - struct stat a, b; - - assert(filea); - assert(fileb); - - if (fstatat(AT_FDCWD, filea, &a, flags) < 0) - return -errno; - - if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) - return -errno; - - return a.st_dev == b.st_dev && - a.st_ino == b.st_ino; -} - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { - assert(s); - assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); - - return F_TYPE_EQUAL(s->f_type, magic_value); -} - -int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_fs_type(&s, magic_value); -} - -int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (fd < 0) - return -errno; - - return fd_is_fs_type(fd, magic_value); -} - -bool is_temporary_fs(const struct statfs *s) { - return is_fs_type(s, TMPFS_MAGIC) || - is_fs_type(s, RAMFS_MAGIC); -} - -bool is_network_fs(const struct statfs *s) { - return is_fs_type(s, CIFS_MAGIC_NUMBER) || - is_fs_type(s, CODA_SUPER_MAGIC) || - is_fs_type(s, NCP_SUPER_MAGIC) || - is_fs_type(s, NFS_SUPER_MAGIC) || - is_fs_type(s, SMB_SUPER_MAGIC) || - is_fs_type(s, V9FS_MAGIC) || - is_fs_type(s, AFS_SUPER_MAGIC) || - is_fs_type(s, OCFS2_SUPER_MAGIC); -} - -int fd_is_temporary_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_temporary_fs(&s); -} - -int fd_is_network_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_network_fs(&s); -} - -int fd_is_network_ns(int fd) { - int r; - - r = fd_is_fs_type(fd, NSFS_MAGIC); - if (r <= 0) - return r; - - r = ioctl(fd, NS_GET_NSTYPE); - if (r < 0) - return -errno; - - return r == CLONE_NEWNET; -} - -int path_is_temporary_fs(const char *path) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (fd < 0) - return -errno; - - return fd_is_temporary_fs(fd); -} -#endif /* NM_IGNORED */ - -int stat_verify_regular(const struct stat *st) { - assert(st); - - /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error - * code. */ - - if (S_ISDIR(st->st_mode)) - return -EISDIR; - - if (S_ISLNK(st->st_mode)) - return -ELOOP; - - if (!S_ISREG(st->st_mode)) - return -EBADFD; - - return 0; -} - -int fd_verify_regular(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return stat_verify_regular(&st); -} diff --git a/src/systemd/src/basic/stat-util.h b/src/systemd/src/basic/stat-util.h deleted file mode 100644 index 1a725f1d..00000000 --- a/src/systemd/src/basic/stat-util.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <sys/vfs.h> - -#include "macro.h" - -int is_symlink(const char *path); -int is_dir(const char *path, bool follow); -int is_dir_fd(int fd); -int is_device_node(const char *path); - -int dir_is_empty(const char *path); - -static inline int dir_is_populated(const char *path) { - int r; - r = dir_is_empty(path); - if (r < 0) - return r; - return !r; -} - -bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); -int null_or_empty_fd(int fd); - -int path_is_read_only_fs(const char *path); - -int files_same(const char *filea, const char *fileb, int flags); - -/* The .f_type field of struct statfs is really weird defined on - * different archs. Let's give its type a name. */ -typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; -int fd_is_fs_type(int fd, statfs_f_type_t magic_value); -int path_is_fs_type(const char *path, statfs_f_type_t magic_value); - -bool is_temporary_fs(const struct statfs *s) _pure_; -bool is_network_fs(const struct statfs *s) _pure_; - -int fd_is_temporary_fs(int fd); -int fd_is_network_fs(int fd); - -int fd_is_network_ns(int fd); - -int path_is_temporary_fs(const char *path); - -/* Because statfs.t_type can be int on some architectures, we have to cast - * the const magic to the type, otherwise the compiler warns about - * signed/unsigned comparison, because the magic can be 32 bit unsigned. - */ -#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b) - -int stat_verify_regular(const struct stat *st); -int fd_verify_regular(int fd); diff --git a/src/systemd/src/basic/stdio-util.h b/src/systemd/src/basic/stdio-util.h deleted file mode 100644 index 73c03274..00000000 --- a/src/systemd/src/basic/stdio-util.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <printf.h> -#include <stdarg.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" - -#define snprintf_ok(buf, len, fmt, ...) \ - ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) - -#define xsprintf(buf, fmt, ...) \ - assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") - -#define VA_FORMAT_ADVANCE(format, ap) \ -do { \ - int _argtypes[128]; \ - size_t _i, _k; \ - _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \ - assert(_k < ELEMENTSOF(_argtypes)); \ - for (_i = 0; _i < _k; _i++) { \ - if (_argtypes[_i] & PA_FLAG_PTR) { \ - (void) va_arg(ap, void*); \ - continue; \ - } \ - \ - switch (_argtypes[_i]) { \ - case PA_INT: \ - case PA_INT|PA_FLAG_SHORT: \ - case PA_CHAR: \ - (void) va_arg(ap, int); \ - break; \ - case PA_INT|PA_FLAG_LONG: \ - (void) va_arg(ap, long int); \ - break; \ - case PA_INT|PA_FLAG_LONG_LONG: \ - (void) va_arg(ap, long long int); \ - break; \ - case PA_WCHAR: \ - (void) va_arg(ap, wchar_t); \ - break; \ - case PA_WSTRING: \ - case PA_STRING: \ - case PA_POINTER: \ - (void) va_arg(ap, void*); \ - break; \ - case PA_FLOAT: \ - case PA_DOUBLE: \ - (void) va_arg(ap, double); \ - break; \ - case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \ - (void) va_arg(ap, long double); \ - break; \ - default: \ - assert_not_reached("Unknown format string argument."); \ - } \ - } \ -} while (false) diff --git a/src/systemd/src/basic/string-table.c b/src/systemd/src/basic/string-table.c deleted file mode 100644 index 94412ed2..00000000 --- a/src/systemd/src/basic/string-table.c +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include "string-table.h" -#include "string-util.h" - -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; -} diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h deleted file mode 100644 index 9bd78793..00000000 --- a/src/systemd/src/basic/string-table.h +++ /dev/null @@ -1,112 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#pragma once - -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> - -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); - -/* For basic lookup tables with strictly enumerated entries */ -#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]; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - scope type name##_from_string(const char *s) { \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ - scope type name##_from_string(const char *s) { \ - int b; \ - if (!s) \ - return -1; \ - b = parse_boolean(s); \ - if (b == 0) \ - return (type) 0; \ - else if (b > 0) \ - return yes; \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \ - scope int name##_to_string_alloc(type i, char **str) { \ - char *s; \ - if (i < 0 || i > max) \ - return -ERANGE; \ - if (i < (type) ELEMENTSOF(name##_table)) { \ - s = strdup(name##_table[i]); \ - if (!s) \ - return -ENOMEM; \ - } else { \ - if (asprintf(&s, "%i", i) < 0) \ - return -ENOMEM; \ - } \ - *str = s; \ - return 0; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ - type name##_from_string(const char *s) { \ - type i; \ - unsigned u = 0; \ - if (!s) \ - return (type) -1; \ - for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \ - if (streq_ptr(name##_table[i], s)) \ - return i; \ - if (safe_atou(s, &u) >= 0 && u <= max) \ - return (type) u; \ - return (type) -1; \ - } \ - -#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) - -#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) - -#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) - -#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) - -/* For string conversions where numbers are also acceptable */ -#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) - -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) - -#define DUMP_STRING_TABLE(name,type,max) \ - do { \ - type _k; \ - flockfile(stdout); \ - for (_k = 0; _k < (max); _k++) { \ - const char *_t; \ - _t = name##_to_string(_k); \ - if (!_t) \ - continue; \ - fputs_unlocked(_t, stdout); \ - fputc_unlocked('\n', stdout); \ - } \ - funlockfile(stdout); \ - } while(false) diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c deleted file mode 100644 index 1747f35b..00000000 --- a/src/systemd/src/basic/string-util.c +++ /dev/null @@ -1,1079 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "gunicode.h" -#include "locale-util.h" -#include "macro.h" -#include "string-util.h" -#include "terminal-util.h" -#include "utf8.h" -#include "util.h" -#include "fileio.h" - -int strcmp_ptr(const char *a, const char *b) { - - /* Like strcmp(), but tries to make sense of NULL pointers */ - if (a && b) - return strcmp(a, b); - - if (!a && b) - return -1; - - if (a && !b) - return 1; - - return 0; -} - -char* endswith(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (memcmp(s + sl - pl, postfix, pl) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* endswith_no_case(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (strcasecmp(s + sl - pl, postfix) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* first_word(const char *s, const char *word) { - size_t sl, wl; - const char *p; - - assert(s); - assert(word); - - /* Checks if the string starts with the specified word, either - * followed by NUL or by whitespace. Returns a pointer to the - * NUL or the first character after the whitespace. */ - - sl = strlen(s); - wl = strlen(word); - - if (sl < wl) - return NULL; - - if (wl == 0) - return (char*) s; - - if (memcmp(s, word, wl) != 0) - return NULL; - - p = s + wl; - if (*p == 0) - return (char*) p; - - if (!strchr(WHITESPACE, *p)) - return NULL; - - p += strspn(p, WHITESPACE); - return (char*) p; -} - -static size_t strcspn_escaped(const char *s, const char *reject) { - bool escaped = false; - int n; - - for (n=0; s[n]; n++) { - if (escaped) - escaped = false; - else if (s[n] == '\\') - escaped = true; - else if (strchr(reject, s[n])) - break; - } - - /* if s ends in \, return index of previous char */ - return n - escaped; -} - -/* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, bool quoted) { - const char *current; - - current = *state; - - if (!*current) { - assert(**state == '\0'); - return NULL; - } - - current += strspn(current, separator); - if (!*current) { - *state = current; - return NULL; - } - - if (quoted && strchr("\'\"", *current)) { - char quotechars[2] = {*current, '\0'}; - - *l = strcspn_escaped(current + 1, quotechars); - if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || - (current[*l + 2] && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end */ - *state = current; - return NULL; - } - *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); - *state = current + *l; - } - - return current; -} - -char *strnappend(const char *s, const char *suffix, size_t b) { - size_t a; - char *r; - - if (!s && !suffix) - return strdup(""); - - if (!s) - return strndup(suffix, b); - - if (!suffix) - return strdup(s); - - assert(s); - assert(suffix); - - a = strlen(s); - if (b > ((size_t) -1) - a) - return NULL; - - r = new(char, a+b+1); - if (!r) - return NULL; - - memcpy(r, s, a); - memcpy(r+a, suffix, b); - r[a+b] = 0; - - return r; -} - -char *strappend(const char *s, const char *suffix) { - return strnappend(s, suffix, strlen_ptr(suffix)); -} - -#if 0 /* NM_IGNORED */ -char *strjoin_real(const char *x, ...) { - va_list ap; - size_t l; - char *r, *p; - - va_start(ap, x); - - if (x) { - l = strlen(x); - - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - if (n > ((size_t) -1) - l) { - va_end(ap); - return NULL; - } - - l += n; - } - } else - l = 0; - - va_end(ap); - - r = new(char, l+1); - if (!r) - return NULL; - - if (x) { - p = stpcpy(r, x); - - va_start(ap, x); - - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - p = stpcpy(p, t); - } - - va_end(ap); - } else - r[0] = 0; - - return r; -} - -char *strstrip(char *s) { - if (!s) - return NULL; - - /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */ - - return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE); -} - -char *delete_chars(char *s, const char *bad) { - char *f, *t; - - /* Drops all specified bad characters, regardless where in the string */ - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - for (f = s, t = s; *f; f++) { - if (strchr(bad, *f)) - continue; - - *(t++) = *f; - } - - *t = 0; - - return s; -} - -char *delete_trailing_chars(char *s, const char *bad) { - char *p, *c = s; - - /* Drops all specified bad characters, at the end of the string */ - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - for (p = s; *p; p++) - if (!strchr(bad, *p)) - c = p + 1; - - *c = 0; - - return s; -} -#endif /* NM_IGNORED */ - -char *truncate_nl(char *s) { - assert(s); - - s[strcspn(s, NEWLINE)] = 0; - return s; -} - -char ascii_tolower(char x) { - - if (x >= 'A' && x <= 'Z') - return x - 'A' + 'a'; - - return x; -} - -char ascii_toupper(char x) { - - if (x >= 'a' && x <= 'z') - return x - 'a' + 'A'; - - return x; -} - -char *ascii_strlower(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_tolower(*p); - - return t; -} - -char *ascii_strupper(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_toupper(*p); - - return t; -} - -char *ascii_strlower_n(char *t, size_t n) { - size_t i; - - if (n <= 0) - return t; - - for (i = 0; i < n; i++) - t[i] = ascii_tolower(t[i]); - - return t; -} - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n) { - - for (; n > 0; a++, b++, n--) { - int x, y; - - x = (int) (uint8_t) ascii_tolower(*a); - y = (int) (uint8_t) ascii_tolower(*b); - - if (x != y) - return x - y; - } - - return 0; -} - -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { - int r; - - r = ascii_strcasecmp_n(a, b, MIN(n, m)); - if (r != 0) - return r; - - if (n < m) - return -1; - else if (n > m) - return 1; - else - return 0; -} - -bool chars_intersect(const char *a, const char *b) { - const char *p; - - /* Returns true if any of the chars in a are in b. */ - for (p = a; *p; p++) - if (strchr(b, *p)) - return true; - - return false; -} - -bool string_has_cc(const char *p, const char *ok) { - const char *t; - - assert(p); - - /* - * Check if a string contains control characters. If 'ok' is - * non-NULL it may be a string containing additional CCs to be - * considered OK. - */ - - for (t = p; *t; t++) { - if (ok && strchr(ok, *t)) - continue; - - if (*t > 0 && *t < ' ') - return true; - - if (*t == 127) - return true; - } - - return false; -} - -#if 0 /* NM_IGNORED */ -static int write_ellipsis(char *buf, bool unicode) { - if (unicode || is_locale_utf8()) { - buf[0] = 0xe2; /* tri-dot ellipsis: … */ - buf[1] = 0x80; - buf[2] = 0xa6; - } else { - buf[0] = '.'; - buf[1] = '.'; - buf[2] = '.'; - } - - return 3; -} - -static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x, need_space, suffix_len; - char *t; - - assert(s); - assert(percent <= 100); - assert(new_length != (size_t) -1); - - if (old_length <= new_length) - return strndup(s, old_length); - - /* Special case short ellipsations */ - switch (new_length) { - - case 0: - return strdup(""); - - case 1: - if (is_locale_utf8()) - return strdup("…"); - else - return strdup("."); - - case 2: - if (!is_locale_utf8()) - return strdup(".."); - - break; - - default: - break; - } - - /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one - * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage, - * either for the UTF-8 encoded character or for three ASCII characters. */ - need_space = is_locale_utf8() ? 1 : 3; - - t = new(char, new_length+3); - if (!t) - return NULL; - - assert(new_length >= need_space); - - x = ((new_length - need_space) * percent + 50) / 100; - assert(x <= new_length - need_space); - - memcpy(t, s, x); - write_ellipsis(t + x, false); - suffix_len = new_length - x - need_space; - memcpy(t + x + 3, s + old_length - suffix_len, suffix_len); - *(t + x + 3 + suffix_len) = '\0'; - - return t; -} - -char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x, k, len, len2; - const char *i, *j; - char *e; - int r; - - /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up - * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8 - * strings. - * - * Ellipsation is done in a locale-dependent way: - * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...") - * 2. Otherwise, a unicode ellipsis is used ("…") - * - * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or - * the current locale is UTF-8. - */ - - assert(s); - assert(percent <= 100); - - if (new_length == (size_t) -1) - return strndup(s, old_length); - - if (new_length == 0) - return strdup(""); - - /* If no multibyte characters use ascii_ellipsize_mem for speed */ - if (ascii_is_valid_n(s, old_length)) - return ascii_ellipsize_mem(s, old_length, new_length, percent); - - x = ((new_length - 1) * percent) / 100; - assert(x <= new_length - 1); - - k = 0; - for (i = s; i < s + old_length; i = utf8_next_char(i)) { - char32_t c; - int w; - - r = utf8_encoded_to_unichar(i, &c); - if (r < 0) - return NULL; - - w = unichar_iswide(c) ? 2 : 1; - if (k + w <= x) - k += w; - else - break; - } - - for (j = s + old_length; j > i; ) { - char32_t c; - int w; - const char *jj; - - jj = utf8_prev_char(j); - r = utf8_encoded_to_unichar(jj, &c); - if (r < 0) - return NULL; - - w = unichar_iswide(c) ? 2 : 1; - if (k + w <= new_length) { - k += w; - j = jj; - } else - break; - } - assert(i <= j); - - /* we don't actually need to ellipsize */ - if (i == j) - return memdup_suffix0(s, old_length); - - /* make space for ellipsis, if possible */ - if (j < s + old_length) - j = utf8_next_char(j); - else if (i > s) - i = utf8_prev_char(i); - - len = i - s; - len2 = s + old_length - j; - e = new(char, len + 3 + len2 + 1); - if (!e) - return NULL; - - /* - printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n", - old_length, new_length, x, len, len2, k); - */ - - memcpy(e, s, len); - write_ellipsis(e + len, true); - memcpy(e + len + 3, j, len2); - *(e + len + 3 + len2) = '\0'; - - return e; -} - -char *cellescape(char *buf, size_t len, const char *s) { - /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII - * characters are copied as they are, everything else is escaped. The result - * is different then if escaping and ellipsization was performed in two - * separate steps, because each sequence is either stored in full or skipped. - * - * This function should be used for logging about strings which expected to - * be plain ASCII in a safe way. - * - * An ellipsis will be used if s is too long. It was always placed at the - * very end. - */ - - size_t i = 0, last_char_width[4] = {}, k = 0, j; - - assert(len > 0); /* at least a terminating NUL */ - - for (;;) { - char four[4]; - int w; - - if (*s == 0) /* terminating NUL detected? then we are done! */ - goto done; - - w = cescape_char(*s, four); - if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's - * ellipsize at the previous location */ - break; - - /* OK, there was space, let's add this escaped character to the buffer */ - memcpy(buf + i, four, w); - i += w; - - /* And remember its width in the ring buffer */ - last_char_width[k] = w; - k = (k + 1) % 4; - - s++; - } - - /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4 - * characters ideally, but the buffer is shorter than that in the first place take what we can get */ - for (j = 0; j < ELEMENTSOF(last_char_width); j++) { - - if (i + 4 <= len) /* nice, we reached our space goal */ - break; - - k = k == 0 ? 3 : k - 1; - if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */ - break; - - assert(i >= last_char_width[k]); - i -= last_char_width[k]; - } - - if (i + 4 <= len) /* yay, enough space */ - i += write_ellipsis(buf + i, false); - else if (i + 3 <= len) { /* only space for ".." */ - buf[i++] = '.'; - buf[i++] = '.'; - } else if (i + 2 <= len) /* only space for a single "." */ - buf[i++] = '.'; - else - assert(i + 1 <= len); - - done: - buf[i] = '\0'; - return buf; -} -#endif /* NM_IGNORED */ - -bool nulstr_contains(const char *nulstr, const char *needle) { - const char *i; - - if (!nulstr) - return false; - - NULSTR_FOREACH(i, nulstr) - if (streq(i, needle)) - return true; - - return false; -} - -char* strshorten(char *s, size_t l) { - assert(s); - - if (strnlen(s, l+1) > l) - s[l] = 0; - - return s; -} - -char *strreplace(const char *text, const char *old_string, const char *new_string) { - size_t l, old_len, new_len, allocated = 0; - char *t, *ret = NULL; - const char *f; - - assert(old_string); - assert(new_string); - - if (!text) - return NULL; - - old_len = strlen(old_string); - new_len = strlen(new_string); - - l = strlen(text); - if (!GREEDY_REALLOC(ret, allocated, l+1)) - return NULL; - - f = text; - t = ret; - while (*f) { - size_t d, nl; - - if (!startswith(f, old_string)) { - *(t++) = *(f++); - continue; - } - - d = t - ret; - nl = l - old_len + new_len; - - if (!GREEDY_REALLOC(ret, allocated, nl + 1)) - return mfree(ret); - - l = nl; - t = ret + d; - - t = stpcpy(t, new_string); - f += old_len; - } - - *t = 0; - return ret; -} - -static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) { - if (!offsets) - return; - - if ((size_t) diff < offsets[0]) - shift[0] += size; - if ((size_t) diff < offsets[1]) - shift[1] += size; -} - -char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { - const char *i, *begin = NULL; - enum { - STATE_OTHER, - STATE_ESCAPE, - STATE_CSI, - STATE_CSO, - } state = STATE_OTHER; - char *obuf = NULL; - size_t osz = 0, isz, shift[2] = {}; - FILE *f; - - assert(ibuf); - assert(*ibuf); - - /* This does three things: - * - * 1. Replaces TABs by 8 spaces - * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences - * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences - * - * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as are any - * other special characters. Truncated ANSI sequences are left-as is too. This call is supposed to suppress the - * most basic formatting noise, but nothing else. - * - * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */ - - isz = _isz ? *_isz : strlen(*ibuf); - - f = open_memstream(&obuf, &osz); - if (!f) - return NULL; - - /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we created f here - * and it doesn't leave our scope. */ - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - for (i = *ibuf; i < *ibuf + isz + 1; i++) { - - switch (state) { - - case STATE_OTHER: - if (i >= *ibuf + isz) /* EOT */ - break; - else if (*i == '\x1B') - state = STATE_ESCAPE; - else if (*i == '\t') { - fputs(" ", f); - advance_offsets(i - *ibuf, highlight, shift, 7); - } else - fputc(*i, f); - - break; - - case STATE_ESCAPE: - if (i >= *ibuf + isz) { /* EOT */ - fputc('\x1B', f); - advance_offsets(i - *ibuf, highlight, shift, 1); - break; - } else if (*i == '[') { /* ANSI CSI */ - state = STATE_CSI; - begin = i + 1; - } else if (*i == ']') { /* ANSI CSO */ - state = STATE_CSO; - begin = i + 1; - } else { - fputc('\x1B', f); - fputc(*i, f); - advance_offsets(i - *ibuf, highlight, shift, 1); - state = STATE_OTHER; - } - - break; - - case STATE_CSI: - - if (i >= *ibuf + isz || /* EOT … */ - !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */ - fputc('\x1B', f); - fputc('[', f); - advance_offsets(i - *ibuf, highlight, shift, 2); - state = STATE_OTHER; - i = begin-1; - } else if (*i == 'm') - state = STATE_OTHER; - - break; - - case STATE_CSO: - - if (i >= *ibuf + isz || /* EOT … */ - (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */ - fputc('\x1B', f); - fputc(']', f); - advance_offsets(i - *ibuf, highlight, shift, 2); - state = STATE_OTHER; - i = begin-1; - } else if (*i == '\a') - state = STATE_OTHER; - - break; - } - } - - if (fflush_and_check(f) < 0) { - fclose(f); - return mfree(obuf); - } - - fclose(f); - - free(*ibuf); - *ibuf = obuf; - - if (_isz) - *_isz = osz; - - if (highlight) { - highlight[0] += shift[0]; - highlight[1] += shift[1]; - } - - return obuf; -} - -#if 0 /* NM_IGNORED */ -char *strextend_with_separator(char **x, const char *separator, ...) { - bool need_separator; - size_t f, l, l_separator; - char *r, *p; - va_list ap; - - assert(x); - - l = f = strlen_ptr(*x); - - need_separator = !isempty(*x); - l_separator = strlen_ptr(separator); - - va_start(ap, separator); - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - - if (need_separator) - n += l_separator; - - if (n > ((size_t) -1) - l) { - va_end(ap); - return NULL; - } - - l += n; - need_separator = true; - } - va_end(ap); - - need_separator = !isempty(*x); - - r = realloc(*x, l+1); - if (!r) - return NULL; - - p = r + f; - - va_start(ap, separator); - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - if (need_separator && separator) - p = stpcpy(p, separator); - - p = stpcpy(p, t); - - need_separator = true; - } - va_end(ap); - - assert(p == r + l); - - *p = 0; - *x = r; - - return r + l; -} -#endif /* NM_IGNORED */ - -char *strrep(const char *s, unsigned n) { - size_t l; - char *r, *p; - unsigned i; - - assert(s); - - l = strlen(s); - p = r = malloc(l * n + 1); - if (!r) - return NULL; - - for (i = 0; i < n; i++) - p = stpcpy(p, s); - - *p = 0; - return r; -} - -int split_pair(const char *s, const char *sep, char **l, char **r) { - char *x, *a, *b; - - assert(s); - assert(sep); - assert(l); - assert(r); - - if (isempty(sep)) - return -EINVAL; - - x = strstr(s, sep); - if (!x) - return -EINVAL; - - a = strndup(s, x - s); - if (!a) - return -ENOMEM; - - b = strdup(x + strlen(sep)); - if (!b) { - free(a); - return -ENOMEM; - } - - *l = a; - *r = b; - - return 0; -} - -int free_and_strdup(char **p, const char *s) { - char *t; - - assert(p); - - /* Replaces a string pointer with an strdup()ed new string, - * possibly freeing the old one. */ - - if (streq_ptr(*p, s)) - return 0; - - if (s) { - t = strdup(s); - if (!t) - return -ENOMEM; - } else - t = NULL; - - free(*p); - *p = t; - - return 1; -} - -#if !HAVE_EXPLICIT_BZERO -/* - * Pointer to memset is volatile so that compiler must de-reference - * the pointer and can't assume that it points to any function in - * particular (such as memset, which it then might further "optimize") - * This approach is inspired by openssl's crypto/mem_clr.c. - */ -typedef void *(*memset_t)(void *,int,size_t); - -static volatile memset_t memset_func = memset; - -void explicit_bzero(void *p, size_t l) { - memset_func(p, '\0', l); -} -#endif - -char* string_erase(char *x) { - if (!x) - return NULL; - - /* A delicious drop of snake-oil! To be called on memory where - * we stored passphrases or so, after we used them. */ - explicit_bzero(x, strlen(x)); - return x; -} - -char *string_free_erase(char *s) { - return mfree(string_erase(s)); -} - -bool string_is_safe(const char *p) { - const char *t; - - if (!p) - return false; - - for (t = p; *t; t++) { - if (*t > 0 && *t < ' ') /* no control characters */ - return false; - - if (strchr(QUOTES "\\\x7f", *t)) - return false; - } - - return true; -} diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h deleted file mode 100644 index c0cc4e78..00000000 --- a/src/systemd/src/basic/string-util.h +++ /dev/null @@ -1,230 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <stdbool.h> -#include <stddef.h> -#include <string.h> - -#include "macro.h" - -/* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n\r" -#define NEWLINE "\n\r" -#define QUOTES "\"\'" -#define COMMENTS "#;" -#define GLOB_CHARS "*?[" -#define DIGITS "0123456789" -#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" -#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS -#define ALPHANUMERICAL LETTERS DIGITS -#define HEXDIGITS DIGITS "abcdefABCDEF" - -#define streq(a,b) (strcmp((a),(b)) == 0) -#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) -#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) -#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) - -int strcmp_ptr(const char *a, const char *b) _pure_; - -static inline bool streq_ptr(const char *a, const char *b) { - return strcmp_ptr(a, b) == 0; -} - -static inline const char* strempty(const char *s) { - return s ?: ""; -} - -static inline const char* strnull(const char *s) { - return s ?: "(null)"; -} - -static inline const char *strna(const char *s) { - return s ?: "n/a"; -} - -static inline bool isempty(const char *p) { - return !p || !p[0]; -} - -static inline const char *empty_to_null(const char *p) { - return isempty(p) ? NULL : p; -} - -static inline const char *empty_to_dash(const char *str) { - return isempty(str) ? "-" : str; -} - -static inline char *startswith(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -static inline char *startswith_no_case(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncasecmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -char *endswith(const char *s, const char *postfix) _pure_; -char *endswith_no_case(const char *s, const char *postfix) _pure_; - -char *first_word(const char *s, const char *word) _pure_; - -const char* split(const char **state, size_t *l, const char *separator, bool quoted); - -#define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, false, state) - -#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, false, state) - -#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) - -char *strappend(const char *s, const char *suffix); -char *strnappend(const char *s, const char *suffix, size_t length); - -char *strjoin_real(const char *x, ...) _sentinel_; -#define strjoin(a, ...) strjoin_real((a), __VA_ARGS__, NULL) - -#define strjoina(a, ...) \ - ({ \ - const char *_appendees_[] = { a, __VA_ARGS__ }; \ - char *_d_, *_p_; \ - size_t _len_ = 0; \ - size_t _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_; \ - }) - -char *strstrip(char *s); -char *delete_chars(char *s, const char *bad); -char *delete_trailing_chars(char *s, const char *bad); -char *truncate_nl(char *s); - -static inline char *skip_leading_chars(const char *s, const char *bad) { - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - return (char*) s + strspn(s, bad); -} - -char ascii_tolower(char x); -char *ascii_strlower(char *s); -char *ascii_strlower_n(char *s, size_t n); - -char ascii_toupper(char x); -char *ascii_strupper(char *s); - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n); -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); - -bool chars_intersect(const char *a, const char *b) _pure_; - -static inline bool _pure_ in_charset(const char *s, const char* charset) { - assert(s); - assert(charset); - return s[strspn(s, charset)] == '\0'; -} - -bool string_has_cc(const char *p, const char *ok) _pure_; - -char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); -static inline char *ellipsize(const char *s, size_t length, unsigned percent) { - return ellipsize_mem(s, strlen(s), length, percent); -} - -char *cellescape(char *buf, size_t len, const char *s); - -/* This limit is arbitrary, enough to give some idea what the string contains */ -#define CELLESCAPE_DEFAULT_LENGTH 64 - -bool nulstr_contains(const char *nulstr, const char *needle); - -char* strshorten(char *s, size_t l); - -char *strreplace(const char *text, const char *old_string, const char *new_string); - -char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); - -char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_; - -#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__) - -char *strrep(const char *s, unsigned n); - -int split_pair(const char *s, const char *sep, char **l, char **r); - -int free_and_strdup(char **p, const char *s); - -/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ -static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { - - if (needlelen <= 0) - return (void*) haystack; - - if (haystacklen < needlelen) - return NULL; - - assert(haystack); - assert(needle); - - return memmem(haystack, haystacklen, needle, needlelen); -} - -#if !HAVE_EXPLICIT_BZERO -void explicit_bzero(void *p, size_t l); -#endif - -char *string_erase(char *x); - -char *string_free_erase(char *s); -DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase); -#define _cleanup_string_free_erase_ _cleanup_(string_free_erasep) - -bool string_is_safe(const char *p) _pure_; - -static inline size_t strlen_ptr(const char *s) { - if (!s) - return 0; - - return strlen(s); -} - -/* Like startswith(), but operates on arbitrary memory blocks */ -static inline void *memory_startswith(const void *p, size_t sz, const char *token) { - size_t n; - - assert(token); - - n = strlen(token); - if (sz < n) - return NULL; - - assert(p); - - if (memcmp(p, token, n) != 0) - return NULL; - - return (uint8_t*) p + n; -} diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c deleted file mode 100644 index 6f80b317..00000000 --- a/src/systemd/src/basic/strv.c +++ /dev/null @@ -1,888 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <fnmatch.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "fileio.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -char *strv_find(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (streq(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_prefix(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (startswith(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_startswith(char **l, const char *name) { - char **i, *e; - - assert(name); - - /* Like strv_find_prefix, but actually returns only the - * suffix, not the whole item */ - - STRV_FOREACH(i, l) { - e = startswith(*i, name); - if (e) - return e; - } - - return NULL; -} - -void strv_clear(char **l) { - char **k; - - if (!l) - return; - - for (k = l; *k; k++) - free(*k); - - *l = NULL; -} - -char **strv_free(char **l) { - strv_clear(l); - return mfree(l); -} - -char **strv_free_erase(char **l) { - char **i; - - STRV_FOREACH(i, l) - string_erase(*i); - - return strv_free(l); -} - -char **strv_copy(char * const *l) { - char **r, **k; - - k = r = new(char*, strv_length(l) + 1); - if (!r) - return NULL; - - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } - - *k = NULL; - return r; -} - -size_t strv_length(char * const *l) { - size_t n = 0; - - if (!l) - return 0; - - for (; *l; l++) - n++; - - return n; -} - -char **strv_new_ap(const char *x, va_list ap) { - const char *s; - _cleanup_strv_free_ char **a = NULL; - size_t n = 0, i = 0; - va_list aq; - - /* As a special trick we ignore all listed strings that equal - * STRV_IGNORE. This is supposed to be used with the - * STRV_IFNOTNULL() macro to include possibly NULL strings in - * the string list. */ - - if (x) { - n = x == STRV_IGNORE ? 0 : 1; - - va_copy(aq, ap); - while ((s = va_arg(aq, const char*))) { - if (s == STRV_IGNORE) - continue; - - n++; - } - - va_end(aq); - } - - a = new(char*, n+1); - if (!a) - return NULL; - - if (x) { - if (x != STRV_IGNORE) { - a[i] = strdup(x); - if (!a[i]) - return NULL; - i++; - } - - while ((s = va_arg(ap, const char*))) { - - if (s == STRV_IGNORE) - continue; - - a[i] = strdup(s); - if (!a[i]) - return NULL; - - i++; - } - } - - a[i] = NULL; - - return TAKE_PTR(a); -} - -char **strv_new(const char *x, ...) { - char **r; - va_list ap; - - va_start(ap, x); - r = strv_new_ap(x, ap); - va_end(ap); - - return r; -} - -int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { - char **s, **t; - size_t p, q, i = 0, j; - - assert(a); - - if (strv_isempty(b)) - return 0; - - p = strv_length(*a); - q = strv_length(b); - - t = reallocarray(*a, p + q + 1, sizeof(char *)); - if (!t) - return -ENOMEM; - - t[p] = NULL; - *a = t; - - STRV_FOREACH(s, b) { - - if (filter_duplicates && strv_contains(t, *s)) - continue; - - t[p+i] = strdup(*s); - if (!t[p+i]) - goto rollback; - - i++; - t[p+i] = NULL; - } - - assert(i <= q); - - return (int) i; - -rollback: - for (j = 0; j < i; j++) - free(t[p + j]); - - t[p] = NULL; - return -ENOMEM; -} - -int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { - int r; - char **s; - - STRV_FOREACH(s, b) { - char *v; - - v = strappend(*s, suffix); - if (!v) - return -ENOMEM; - - r = strv_push(a, v); - if (r < 0) { - free(v); - return r; - } - } - - return 0; -} - -char **strv_split(const char *s, const char *separator) { - const char *word, *state; - size_t l; - size_t n, i; - char **r; - - assert(s); - - s += strspn(s, separator); - if (isempty(s)) - return new0(char*, 1); - - n = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) - n++; - - r = new(char*, n+1); - if (!r) - return NULL; - - i = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { - r[i] = strndup(word, l); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - } - - r[i] = NULL; - return r; -} - -char **strv_split_newlines(const char *s) { - char **l; - size_t n; - - assert(s); - - /* Special version of strv_split() that splits on newlines and - * suppresses an empty string at the end */ - - l = strv_split(s, NEWLINE); - if (!l) - return NULL; - - n = strv_length(l); - if (n <= 0) - return l; - - if (isempty(l[n - 1])) - l[n - 1] = mfree(l[n - 1]); - - return l; -} - -#if 0 /* NM_IGNORED */ -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { - _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; - int r; - - assert(t); - assert(s); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&s, &word, separators, flags); - if (r < 0) - return r; - if (r == 0) - break; - - if (!GREEDY_REALLOC(l, allocated, n + 2)) - return -ENOMEM; - - l[n++] = TAKE_PTR(word); - - l[n] = NULL; - } - - if (!l) { - l = new0(char*, 1); - if (!l) - return -ENOMEM; - } - - *t = TAKE_PTR(l); - - return (int) n; -} -#endif /* NM_IGNORED */ - -char *strv_join(char **l, const char *separator) { - char *r, *e; - char **s; - size_t n, k; - - if (!separator) - separator = " "; - - k = strlen(separator); - - n = 0; - STRV_FOREACH(s, l) { - if (s != l) - n += k; - n += strlen(*s); - } - - r = new(char, n+1); - if (!r) - return NULL; - - e = r; - STRV_FOREACH(s, l) { - if (s != l) - e = stpcpy(e, separator); - - e = stpcpy(e, *s); - } - - *e = 0; - - return r; -} - -int strv_push(char ***l, char *value) { - char **c; - size_t n, m; - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = reallocarray(*l, m, sizeof(char*)); - if (!c) - return -ENOMEM; - - c[n] = value; - c[n+1] = NULL; - - *l = c; - return 0; -} - -int strv_push_pair(char ***l, char *a, char *b) { - char **c; - size_t 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 = reallocarray(*l, m, sizeof(char*)); - if (!c) - return -ENOMEM; - - if (a) - c[n++] = a; - if (b) - c[n++] = b; - c[n] = NULL; - - *l = c; - return 0; -} - -int strv_insert(char ***l, size_t position, char *value) { - char **c; - size_t n, m, i; - - if (!value) - return 0; - - n = strv_length(*l); - position = MIN(position, n); - - /* increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = new(char*, m); - if (!c) - return -ENOMEM; - - for (i = 0; i < position; i++) - c[i] = (*l)[i]; - c[position] = value; - for (i = position; i < n; i++) - c[i+1] = (*l)[i]; - - c[n+1] = NULL; - - free(*l); - *l = c; - - return 0; -} - -int strv_consume(char ***l, char *value) { - int r; - - r = strv_push(l, value); - if (r < 0) - free(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; - - r = strv_push_prepend(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_extend(char ***l, const char *value) { - char *v; - - if (!value) - return 0; - - v = strdup(value); - if (!v) - return -ENOMEM; - - return strv_consume(l, v); -} - -int strv_extend_front(char ***l, const char *value) { - size_t n, m; - char *v, **c; - - assert(l); - - /* Like strv_extend(), but prepends rather than appends the new entry */ - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and overflow check. */ - m = n + 2; - if (m < n) - return -ENOMEM; - - v = strdup(value); - if (!v) - return -ENOMEM; - - c = reallocarray(*l, m, sizeof(char*)); - if (!c) { - free(v); - return -ENOMEM; - } - - memmove(c+1, c, n * sizeof(char*)); - c[0] = v; - c[n+1] = NULL; - - *l = c; - return 0; -} - -char **strv_uniq(char **l) { - char **i; - - /* Drops duplicate entries. The first identical string will be - * kept, the others dropped */ - - STRV_FOREACH(i, l) - strv_remove(i+1, *i); - - 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; - - if (!l) - return NULL; - - assert(s); - - /* Drops every occurrence of s in the string list, edits - * in-place. */ - - for (f = t = l; *f; f++) - if (streq(*f, s)) - free(*f); - else - *(t++) = *f; - - *t = NULL; - return l; -} - -char **strv_parse_nulstr(const char *s, size_t l) { - /* l is the length of the input data, which will be split at NULs into - * elements of the resulting strv. Hence, the number of items in the resulting strv - * will be equal to one plus the number of NUL bytes in the l bytes starting at s, - * unless s[l-1] is NUL, in which case the final empty string is not stored in - * the resulting strv, and length is equal to the number of NUL bytes. - * - * Note that contrary to a normal nulstr which cannot contain empty strings, because - * the input data is terminated by any two consequent NUL bytes, this parser accepts - * empty strings in s. - */ - - const char *p; - size_t c = 0, i = 0; - char **v; - - assert(s || l <= 0); - - if (l <= 0) - return new0(char*, 1); - - for (p = s; p < s + l; p++) - if (*p == 0) - c++; - - if (s[l-1] != 0) - c++; - - v = new0(char*, c+1); - if (!v) - return NULL; - - p = s; - while (p < s + l) { - const char *e; - - e = memchr(p, 0, s + l - p); - - v[i] = strndup(p, e ? e - p : s + l - p); - if (!v[i]) { - strv_free(v); - return NULL; - } - - i++; - - if (!e) - break; - - p = e + 1; - } - - assert(i == c); - - return v; -} - -char **strv_split_nulstr(const char *s) { - const char *i; - char **r = NULL; - - NULSTR_FOREACH(i, s) - if (strv_extend(&r, i) < 0) { - strv_free(r); - return NULL; - } - - if (!r) - return strv_new(NULL, NULL); - - return r; -} - -int strv_make_nulstr(char **l, char **p, size_t *q) { - /* A valid nulstr with two NULs at the end will be created, but - * q will be the length without the two trailing NULs. Thus the output - * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, - * and can also be parsed by strv_parse_nulstr as long as the length - * is provided separately. - */ - - size_t n_allocated = 0, n = 0; - _cleanup_free_ char *m = NULL; - char **i; - - assert(p); - assert(q); - - STRV_FOREACH(i, l) { - size_t z; - - z = strlen(*i); - - if (!GREEDY_REALLOC(m, n_allocated, n + z + 2)) - return -ENOMEM; - - memcpy(m + n, *i, z + 1); - n += z + 1; - } - - if (!m) { - m = new0(char, 1); - if (!m) - return -ENOMEM; - n = 1; - } else - /* make sure there is a second extra NUL at the end of resulting nulstr */ - m[n] = '\0'; - - assert(n > 0); - *p = m; - *q = n - 1; - - m = NULL; - - return 0; -} - -bool strv_overlap(char **a, char **b) { - char **i; - - STRV_FOREACH(i, a) - if (strv_contains(b, *i)) - return true; - - return false; -} - -static int str_compare(const void *_a, const void *_b) { - const char **a = (const char**) _a, **b = (const char**) _b; - - return strcmp(*a, *b); -} - -char **strv_sort(char **l) { - qsort_safe(l, strv_length(l), sizeof(char*), str_compare); - return l; -} - -bool strv_equal(char **a, char **b) { - - if (strv_isempty(a)) - return strv_isempty(b); - - if (strv_isempty(b)) - return false; - - for ( ; *a || *b; ++a, ++b) - if (!streq_ptr(*a, *b)) - return false; - - return true; -} - -void strv_print(char **l) { - char **s; - - STRV_FOREACH(s, l) - puts(*s); -} - -int strv_extendf(char ***l, const char *format, ...) { - va_list ap; - char *x; - int r; - - va_start(ap, format); - r = vasprintf(&x, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return strv_consume(l, x); -} - -char **strv_reverse(char **l) { - size_t n, i; - - n = strv_length(l); - if (n <= 1) - return l; - - for (i = 0; i < n / 2; i++) - SWAP_TWO(l[i], l[n-1-i]); - - return l; -} - -char **strv_shell_escape(char **l, const char *bad) { - char **s; - - /* Escapes every character in every string in l that is in bad, - * edits in-place, does not roll-back on error. */ - - STRV_FOREACH(s, l) { - char *v; - - v = shell_escape(*s, bad); - if (!v) - return NULL; - - free(*s); - *s = v; - } - - return l; -} - -bool strv_fnmatch(char* const* patterns, const char *s, int flags) { - char* const* p; - - STRV_FOREACH(p, patterns) - if (fnmatch(*p, s, flags) == 0) - return true; - - return false; -} - -char ***strv_free_free(char ***l) { - char ***i; - - if (!l) - return NULL; - - for (i = l; *i; i++) - strv_free(*i); - - return mfree(l); -} - -char **strv_skip(char **l, size_t n) { - - while (n > 0) { - if (strv_isempty(l)) - return l; - - l++, n--; - } - - return l; -} - -int strv_extend_n(char ***l, const char *value, size_t n) { - size_t i, j, k; - char **nl; - - assert(l); - - if (!value) - return 0; - if (n == 0) - return 0; - - /* Adds the value n times to l */ - - k = strv_length(*l); - - nl = reallocarray(*l, k + n + 1, sizeof(char *)); - if (!nl) - return -ENOMEM; - - *l = nl; - - for (i = k; i < k + n; i++) { - nl[i] = strdup(value); - if (!nl[i]) - goto rollback; - } - - nl[i] = NULL; - return 0; - -rollback: - for (j = k; j < i; j++) - free(nl[j]); - - nl[k] = NULL; - return -ENOMEM; -} - -int fputstrv(FILE *f, char **l, const char *separator, bool *space) { - bool b = false; - char **s; - int r; - - /* Like fputs(), but for strv, and with a less stupid argument order */ - - if (!space) - space = &b; - - STRV_FOREACH(s, l) { - r = fputs_with_space(f, *s, separator, space); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h deleted file mode 100644 index 51d03db9..00000000 --- a/src/systemd/src/basic/strv.h +++ /dev/null @@ -1,177 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <fnmatch.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> - -#include "alloc-util.h" -#include "extract-word.h" -#include "macro.h" -#include "util.h" - -char *strv_find(char **l, const char *name) _pure_; -char *strv_find_prefix(char **l, const char *name) _pure_; -char *strv_find_startswith(char **l, const char *name) _pure_; - -char **strv_free(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); -#define _cleanup_strv_free_ _cleanup_(strv_freep) - -char **strv_free_erase(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); -#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) - -void strv_clear(char **l); - -char **strv_copy(char * const *l); -size_t strv_length(char * const *l) _pure_; - -int strv_extend_strv(char ***a, char **b, bool filter_duplicates); -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_extend_front(char ***l, const char *value); -int strv_push(char ***l, char *value); -int strv_push_pair(char ***l, char *a, char *b); -int strv_insert(char ***l, size_t position, char *value); - -static inline int strv_push_prepend(char ***l, char *value) { - return strv_insert(l, 0, 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))) - -char **strv_new(const char *x, ...) _sentinel_; -char **strv_new_ap(const char *x, va_list ap); - -#define STRV_IGNORE ((const char *) -1) - -static inline const char* STRV_IFNOTNULL(const char *x) { - return x ? x : STRV_IGNORE; -} - -static inline bool strv_isempty(char * const *l) { - return !l || !*l; -} - -char **strv_split(const char *s, const char *separator); -char **strv_split_newlines(const char *s); - -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); - -char *strv_join(char **l, const char *separator); - -char **strv_parse_nulstr(const char *s, size_t l); -char **strv_split_nulstr(const char *s); -int strv_make_nulstr(char **l, char **p, size_t *n); - -bool strv_overlap(char **a, char **b) _pure_; - -#define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) - -#define STRV_FOREACH_BACKWARDS(s, l) \ - for (s = ({ \ - char **_l = l; \ - _l ? _l + strv_length(_l) - 1U : NULL; \ - }); \ - (l) && ((s) >= (l)); \ - (s)--) - -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) - -char **strv_sort(char **l); -void strv_print(char **l); - -#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) - -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) - -#define strv_from_stdarg_alloca(first) \ - ({ \ - char **_l; \ - \ - if (!first) \ - _l = (char**) &first; \ - else { \ - size_t _n; \ - va_list _ap; \ - \ - _n = 1; \ - va_start(_ap, first); \ - while (va_arg(_ap, char*)) \ - _n++; \ - va_end(_ap); \ - \ - _l = newa(char*, _n+1); \ - _l[_n = 0] = (char*) first; \ - va_start(_ap, first); \ - for (;;) { \ - _l[++_n] = va_arg(_ap, char*); \ - if (!_l[_n]) \ - break; \ - } \ - va_end(_ap); \ - } \ - _l; \ - }) - -#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) -#define STRPTR_IN_SET(x, ...) \ - ({ \ - const char* _x = (x); \ - _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ - }) - -#define FOREACH_STRING(x, ...) \ - for (char **_l = ({ \ - char **_ll = STRV_MAKE(__VA_ARGS__); \ - x = _ll ? _ll[0] : NULL; \ - _ll; \ - }); \ - _l && *_l; \ - x = ({ \ - _l ++; \ - _l[0]; \ - })) - -char **strv_reverse(char **l); -char **strv_shell_escape(char **l, const char *bad); - -bool strv_fnmatch(char* const* patterns, const char *s, int flags); - -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); -} - -char ***strv_free_free(char ***l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free); - -char **strv_skip(char **l, size_t n); - -int strv_extend_n(char ***l, const char *value, size_t n); - -int fputstrv(FILE *f, char **l, const char *separator, bool *space); - -#define strv_free_and_replace(a, b) \ - ({ \ - strv_free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c deleted file mode 100644 index c0f53a47..00000000 --- a/src/systemd/src/basic/time-util.c +++ /dev/null @@ -1,1503 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/timerfd.h> -#include <sys/timex.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" - -static clockid_t map_clock_id(clockid_t c) { - - /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will - * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is - * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on - * those archs. */ - - switch (c) { - - case CLOCK_BOOTTIME_ALARM: - return CLOCK_BOOTTIME; - - case CLOCK_REALTIME_ALARM: - return CLOCK_REALTIME; - - default: - return c; - } -} - -usec_t now(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load(&ts); -} - -nsec_t now_nsec(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load_nsec(&ts); -} - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - - return ts; -} - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = u; - return ts; - } - - ts->realtime = u; - - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - - return ts; -} - -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { - int64_t delta; - - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = ts->boottime = u; - return ts; - } - - ts->realtime = u; - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - ts->monotonic = u; - delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta); - - return ts; -} - -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - dual_timestamp_get(ts); - delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub_signed(ts->realtime, delta); - ts->monotonic = usec_sub_signed(ts->monotonic, delta); - - return ts; -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { - - switch (clock) { - - case CLOCK_REALTIME: - case CLOCK_REALTIME_ALARM: - return ts->realtime; - - case CLOCK_MONOTONIC: - return ts->monotonic; - - case CLOCK_BOOTTIME: - case CLOCK_BOOTTIME_ALARM: - return ts->boottime; - - default: - return USEC_INFINITY; - } -} - -usec_t timespec_load(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec < 0 || ts->tv_nsec < 0) - return USEC_INFINITY; - - if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) ts->tv_sec * USEC_PER_SEC + - (usec_t) ts->tv_nsec / NSEC_PER_USEC; -} - -nsec_t timespec_load_nsec(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec < 0 || ts->tv_nsec < 0) - return NSEC_INFINITY; - - if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) - return NSEC_INFINITY; - - return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; -} - -struct timespec *timespec_store(struct timespec *ts, usec_t u) { - assert(ts); - - if (u == USEC_INFINITY || - u / USEC_PER_SEC >= TIME_T_MAX) { - ts->tv_sec = (time_t) -1; - ts->tv_nsec = (long) -1; - return ts; - } - - ts->tv_sec = (time_t) (u / USEC_PER_SEC); - ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); - - return ts; -} - -#if 0 /* NM_IGNORED */ -usec_t timeval_load(const struct timeval *tv) { - assert(tv); - - if (tv->tv_sec < 0 || tv->tv_usec < 0) - return USEC_INFINITY; - - if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) tv->tv_sec * USEC_PER_SEC + - (usec_t) tv->tv_usec; -} - -struct timeval *timeval_store(struct timeval *tv, usec_t u) { - assert(tv); - - if (u == USEC_INFINITY || - u / USEC_PER_SEC > TIME_T_MAX) { - tv->tv_sec = (time_t) -1; - tv->tv_usec = (suseconds_t) -1; - } else { - tv->tv_sec = (time_t) (u / USEC_PER_SEC); - tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); - } - - return tv; -} - -static char *format_timestamp_internal( - char *buf, - size_t l, - usec_t t, - bool utc, - bool us) { - - /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our - * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ - static const char * const weekdays[] = { - [0] = "Sun", - [1] = "Mon", - [2] = "Tue", - [3] = "Wed", - [4] = "Thu", - [5] = "Fri", - [6] = "Sat", - }; - - struct tm tm; - time_t sec; - size_t n; - - assert(buf); - - if (l < - 3 + /* week day */ - 1 + 10 + /* space and date */ - 1 + 8 + /* space and time */ - (us ? 1 + 6 : 0) + /* "." and microsecond part */ - 1 + 1 + /* space and shortest possible zone */ - 1) - return NULL; /* Not enough space even for the shortest form. */ - if (t <= 0 || t == USEC_INFINITY) - return NULL; /* Timestamp is unset */ - - /* Let's not format times with years > 9999 */ - if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { - assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1); - strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); - return buf; - } - - sec = (time_t) (t / USEC_PER_SEC); /* Round down */ - - if (!localtime_or_gmtime_r(&sec, &tm, utc)) - return NULL; - - /* Start with the week day */ - assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays)); - memcpy(buf, weekdays[tm.tm_wday], 4); - - /* Add the main components */ - if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) - return NULL; /* Doesn't fit */ - - /* Append the microseconds part, if that's requested */ - if (us) { - n = strlen(buf); - if (n + 8 > l) - return NULL; /* Microseconds part doesn't fit. */ - - sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC); - } - - /* Append the timezone */ - n = strlen(buf); - if (utc) { - /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the - * obsolete "GMT" instead. */ - if (n + 5 > l) - return NULL; /* "UTC" doesn't fit. */ - - strcpy(buf + n, " UTC"); - - } else if (!isempty(tm.tm_zone)) { - size_t tn; - - /* An explicit timezone is specified, let's use it, if it fits */ - tn = strlen(tm.tm_zone); - if (n + 1 + tn + 1 > l) { - /* The full time zone does not fit in. Yuck. */ - - if (n + 1 + _POSIX_TZNAME_MAX + 1 > l) - return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ - - /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX - * minimum time zone length. In this case suppress the timezone entirely, in order not to dump - * an overly long, hard to read string on the user. This should be safe, because the user will - * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ - } else { - buf[n++] = ' '; - strcpy(buf + n, tm.tm_zone); - } - } - - return buf; -} - -char *format_timestamp(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, false); -} - -char *format_timestamp_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, false); -} - -char *format_timestamp_us(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, true); -} - -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, true); -} - -char *format_timestamp_relative(char *buf, size_t l, usec_t t) { - const char *s; - usec_t n, d; - - if (t <= 0 || t == USEC_INFINITY) - return NULL; - - n = now(CLOCK_REALTIME); - if (n > t) { - d = n - t; - s = "ago"; - } else { - d = t - n; - s = "left"; - } - - if (d >= USEC_PER_YEAR) - snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s", - d / USEC_PER_YEAR, - (d % USEC_PER_YEAR) / USEC_PER_MONTH, s); - else if (d >= USEC_PER_MONTH) - snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s", - d / USEC_PER_MONTH, - (d % USEC_PER_MONTH) / USEC_PER_DAY, s); - else if (d >= USEC_PER_WEEK) - snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s", - d / USEC_PER_WEEK, - (d % USEC_PER_WEEK) / USEC_PER_DAY, s); - else if (d >= 2*USEC_PER_DAY) - snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); - else if (d >= 25*USEC_PER_HOUR) - snprintf(buf, l, "1 day " USEC_FMT "h %s", - (d - USEC_PER_DAY) / USEC_PER_HOUR, s); - else if (d >= 6*USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h %s", - d / USEC_PER_HOUR, s); - else if (d >= USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", - d / USEC_PER_HOUR, - (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); - else if (d >= 5*USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min %s", - d / USEC_PER_MINUTE, s); - else if (d >= USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", - d / USEC_PER_MINUTE, - (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); - else if (d >= USEC_PER_SEC) - snprintf(buf, l, USEC_FMT "s %s", - d / USEC_PER_SEC, s); - else if (d >= USEC_PER_MSEC) - snprintf(buf, l, USEC_FMT "ms %s", - d / USEC_PER_MSEC, s); - else if (d > 0) - snprintf(buf, l, USEC_FMT"us %s", - d, s); - else - snprintf(buf, l, "now"); - - buf[l-1] = 0; - return buf; -} -#endif /* NM_IGNORED */ - -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { - static const struct { - const char *suffix; - usec_t usec; - } table[] = { - { "y", USEC_PER_YEAR }, - { "month", USEC_PER_MONTH }, - { "w", USEC_PER_WEEK }, - { "d", USEC_PER_DAY }, - { "h", USEC_PER_HOUR }, - { "min", USEC_PER_MINUTE }, - { "s", USEC_PER_SEC }, - { "ms", USEC_PER_MSEC }, - { "us", 1 }, - }; - - size_t i; - char *p = buf; - bool something = false; - - assert(buf); - assert(l > 0); - - if (t == USEC_INFINITY) { - strncpy(p, "infinity", l-1); - p[l-1] = 0; - return p; - } - - if (t <= 0) { - strncpy(p, "0", l-1); - p[l-1] = 0; - return p; - } - - /* The result of this function can be parsed with parse_sec */ - - for (i = 0; i < ELEMENTSOF(table); i++) { - int k = 0; - size_t n; - bool done = false; - usec_t a, b; - - if (t <= 0) - break; - - if (t < accuracy && something) - break; - - if (t < table[i].usec) - continue; - - if (l <= 1) - break; - - a = t / table[i].usec; - b = t % table[i].usec; - - /* Let's see if we should shows this in dot notation */ - if (t < USEC_PER_MINUTE && b > 0) { - usec_t cc; - signed char j; - - j = 0; - for (cc = table[i].usec; cc > 1; cc /= 10) - j++; - - for (cc = accuracy; cc > 1; cc /= 10) { - b /= 10; - j--; - } - - if (j > 0) { - k = snprintf(p, l, - "%s"USEC_FMT".%0*"PRI_USEC"%s", - p > buf ? " " : "", - a, - j, - b, - table[i].suffix); - - t = 0; - done = true; - } - } - - /* No? Then let's show it normally */ - if (!done) { - k = snprintf(p, l, - "%s"USEC_FMT"%s", - p > buf ? " " : "", - a, - table[i].suffix); - - t = b; - } - - n = MIN((size_t) k, l); - - l -= n; - p += n; - - something = true; - } - - *p = 0; - - return buf; -} - -#if 0 /* NM_IGNORED */ -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - - assert(f); - assert(name); - assert(t); - - if (!dual_timestamp_is_set(t)) - return; - - fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", - name, - t->realtime, - t->monotonic); -} - -int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - uint64_t a, b; - int r, pos; - - assert(value); - assert(t); - - pos = strspn(value, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - pos += strspn(value + pos, DIGITS); - pos += strspn(value + pos, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - - r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); - if (r != 2) { - log_debug("Failed to parse dual timestamp value \"%s\".", value); - return -EINVAL; - } - - if (value[pos] != '\0') - /* trailing garbage */ - return -EINVAL; - - t->realtime = a; - t->monotonic = b; - - return 0; -} - -int timestamp_deserialize(const char *value, usec_t *timestamp) { - int r; - - assert(value); - - r = safe_atou64(value, timestamp); - if (r < 0) - return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); - - return r; -} - -static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { - static const struct { - const char *name; - const int nr; - } day_nr[] = { - { "Sunday", 0 }, - { "Sun", 0 }, - { "Monday", 1 }, - { "Mon", 1 }, - { "Tuesday", 2 }, - { "Tue", 2 }, - { "Wednesday", 3 }, - { "Wed", 3 }, - { "Thursday", 4 }, - { "Thu", 4 }, - { "Friday", 5 }, - { "Fri", 5 }, - { "Saturday", 6 }, - { "Sat", 6 }, - }; - - const char *k, *utc = NULL, *tzn = NULL; - struct tm tm, copy; - time_t x; - usec_t x_usec, plus = 0, minus = 0, ret; - int r, weekday = -1, dst = -1; - size_t i; - - /* Allowed syntaxes: - * - * 2012-09-22 16:34:22 - * 2012-09-22 16:34 (seconds will be set to 0) - * 2012-09-22 (time will be set to 00:00:00) - * 16:34:22 (date will be set to today) - * 16:34 (date will be set to today, seconds to 0) - * now - * yesterday (time is set to 00:00:00) - * today (time is set to 00:00:00) - * tomorrow (time is set to 00:00:00) - * +5min - * -5days - * @2147483647 (seconds since epoch) - */ - - assert(t); - assert(usec); - - if (t[0] == '@' && !with_tz) - return parse_sec(t + 1, usec); - - ret = now(CLOCK_REALTIME); - - if (!with_tz) { - if (streq(t, "now")) - goto finish; - - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; - - goto finish; - - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &plus); - if (r < 0) - return r; - - goto finish; - } - - /* See if the timestamp is suffixed with UTC */ - utc = endswith_no_case(t, " UTC"); - if (utc) - t = strndupa(t, utc - t); - else { - const char *e = NULL; - int j; - - tzset(); - - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ - - for (j = 0; j <= 1; j++) { - - if (isempty(tzname[j])) - continue; - - e = endswith_no_case(t, tzname[j]); - if (!e) - continue; - if (e == t) - continue; - if (e[-1] != ' ') - continue; - - break; - } - - if (IN_SET(j, 0, 1)) { - /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); - dst = j; - tzn = tzname[j]; - } - } - } - - x = (time_t) (ret / USEC_PER_SEC); - x_usec = 0; - - if (!localtime_or_gmtime_r(&x, &tm, utc)) - return -EINVAL; - - tm.tm_isdst = dst; - if (!with_tz && tzn) - tm.tm_zone = tzn; - - if (streq(t, "today")) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - - } else if (streq(t, "yesterday")) { - tm.tm_mday--; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - - } else if (streq(t, "tomorrow")) { - tm.tm_mday++; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - for (i = 0; i < ELEMENTSOF(day_nr); i++) { - size_t skip; - - if (!startswith_no_case(t, day_nr[i].name)) - continue; - - skip = strlen(day_nr[i].name); - if (t[skip] != ' ') - continue; - - weekday = day_nr[i].nr; - t += skip + 1; - break; - } - - copy = tm; - k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%y-%m-%d", &tm); - if (k && *k == 0) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d", &tm); - if (k && *k == 0) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - return -EINVAL; - -parse_usec: - { - unsigned add; - - k++; - r = parse_fractional_part_u(&k, 6, &add); - if (r < 0) - return -EINVAL; - - if (*k) - return -EINVAL; - - x_usec = add; - } - -from_tm: - if (weekday >= 0 && tm.tm_wday != weekday) - return -EINVAL; - - x = mktime_or_timegm(&tm, utc); - if (x < 0) - return -EINVAL; - - ret = (usec_t) x * USEC_PER_SEC + x_usec; - if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) - return -EINVAL; - -finish: - if (ret + plus < ret) /* overflow? */ - return -EINVAL; - ret += plus; - if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) - return -EINVAL; - - if (ret >= minus) - ret -= minus; - else - return -EINVAL; - - *usec = ret; - - return 0; -} - -typedef struct ParseTimestampResult { - usec_t usec; - int return_value; -} ParseTimestampResult; - -int parse_timestamp(const char *t, usec_t *usec) { - char *last_space, *tz = NULL; - ParseTimestampResult *shared, tmp; - int r; - - last_space = strrchr(t, ' '); - if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG)) - tz = last_space + 1; - - if (!tz || endswith_no_case(t, " UTC")) - return parse_timestamp_impl(t, usec, false); - - shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); - if (shared == MAP_FAILED) - return negative_errno(); - - r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - if (r == 0) { - bool with_tz = true; - - if (setenv("TZ", tz, 1) != 0) { - shared->return_value = negative_errno(); - _exit(EXIT_FAILURE); - } - - tzset(); - - /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. - * Otherwise just cut it off. */ - with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]); - - /* Cut off the timezone if we dont need it. */ - if (with_tz) - t = strndupa(t, last_space - t); - - shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); - - _exit(EXIT_SUCCESS); - } - - tmp = *shared; - if (munmap(shared, sizeof *shared) != 0) - return negative_errno(); - - if (tmp.return_value == 0) - *usec = tmp.usec; - - return tmp.return_value; -} - -static char* extract_multiplier(char *p, usec_t *multiplier) { - static const struct { - const char *suffix; - usec_t usec; - } table[] = { - { "seconds", USEC_PER_SEC }, - { "second", USEC_PER_SEC }, - { "sec", USEC_PER_SEC }, - { "s", USEC_PER_SEC }, - { "minutes", USEC_PER_MINUTE }, - { "minute", USEC_PER_MINUTE }, - { "min", USEC_PER_MINUTE }, - { "months", USEC_PER_MONTH }, - { "month", USEC_PER_MONTH }, - { "M", USEC_PER_MONTH }, - { "msec", USEC_PER_MSEC }, - { "ms", USEC_PER_MSEC }, - { "m", USEC_PER_MINUTE }, - { "hours", USEC_PER_HOUR }, - { "hour", USEC_PER_HOUR }, - { "hr", USEC_PER_HOUR }, - { "h", USEC_PER_HOUR }, - { "days", USEC_PER_DAY }, - { "day", USEC_PER_DAY }, - { "d", USEC_PER_DAY }, - { "weeks", USEC_PER_WEEK }, - { "week", USEC_PER_WEEK }, - { "w", USEC_PER_WEEK }, - { "years", USEC_PER_YEAR }, - { "year", USEC_PER_YEAR }, - { "y", USEC_PER_YEAR }, - { "usec", 1ULL }, - { "us", 1ULL }, - { "µs", 1ULL }, - }; - size_t i; - - for (i = 0; i < ELEMENTSOF(table); i++) { - char *e; - - e = startswith(p, table[i].suffix); - if (e) { - *multiplier = table[i].usec; - return e; - } - } - - return p; -} - -int parse_time(const char *t, usec_t *usec, usec_t default_unit) { - const char *p, *s; - usec_t r = 0; - bool something = false; - - assert(t); - assert(usec); - assert(default_unit > 0); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *usec = USEC_INFINITY; - return 0; - } - - for (;;) { - usec_t multiplier = default_unit, k; - long long l, z = 0; - unsigned n = 0; - char *e; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - if (*p == '-') /* Don't allow "-0" */ - return -ERANGE; - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - /* Don't allow "0.-0", "3.+1" or "3. 1" */ - if (*b == '-' || *b == '+' || isspace(*b)) - return -EINVAL; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - if (z < 0) - return -ERANGE; - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - p = extract_multiplier(e, &multiplier); - - something = true; - - k = (usec_t) z * multiplier; - - for (; n > 0; n--) - k /= 10; - - r += (usec_t) l * multiplier + k; - } - - *usec = r; - - return 0; -} - -int parse_sec(const char *t, usec_t *usec) { - return parse_time(t, usec, USEC_PER_SEC); -} - -int parse_sec_fix_0(const char *t, usec_t *usec) { - assert(t); - assert(usec); - - t += strspn(t, WHITESPACE); - - if (streq(t, "0")) { - *usec = USEC_INFINITY; - return 0; - } - - return parse_sec(t, usec); -} - -int parse_nsec(const char *t, nsec_t *nsec) { - static const struct { - const char *suffix; - nsec_t nsec; - } table[] = { - { "seconds", NSEC_PER_SEC }, - { "second", NSEC_PER_SEC }, - { "sec", NSEC_PER_SEC }, - { "s", NSEC_PER_SEC }, - { "minutes", NSEC_PER_MINUTE }, - { "minute", NSEC_PER_MINUTE }, - { "min", NSEC_PER_MINUTE }, - { "months", NSEC_PER_MONTH }, - { "month", NSEC_PER_MONTH }, - { "msec", NSEC_PER_MSEC }, - { "ms", NSEC_PER_MSEC }, - { "m", NSEC_PER_MINUTE }, - { "hours", NSEC_PER_HOUR }, - { "hour", NSEC_PER_HOUR }, - { "hr", NSEC_PER_HOUR }, - { "h", NSEC_PER_HOUR }, - { "days", NSEC_PER_DAY }, - { "day", NSEC_PER_DAY }, - { "d", NSEC_PER_DAY }, - { "weeks", NSEC_PER_WEEK }, - { "week", NSEC_PER_WEEK }, - { "w", NSEC_PER_WEEK }, - { "years", NSEC_PER_YEAR }, - { "year", NSEC_PER_YEAR }, - { "y", NSEC_PER_YEAR }, - { "usec", NSEC_PER_USEC }, - { "us", NSEC_PER_USEC }, - { "µs", NSEC_PER_USEC }, - { "nsec", 1ULL }, - { "ns", 1ULL }, - { "", 1ULL }, /* default is nsec */ - }; - - const char *p, *s; - nsec_t r = 0; - bool something = false; - - assert(t); - assert(nsec); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *nsec = NSEC_INFINITY; - return 0; - } - - for (;;) { - long long l, z = 0; - size_t n = 0, i; - char *e; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - if (*p == '-') - return -ERANGE; - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - if (*b == '-' || *b == '+' || isspace(*b)) - return -EINVAL; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - if (z < 0) - return -ERANGE; - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - - for (i = 0; i < ELEMENTSOF(table); i++) - if (startswith(e, table[i].suffix)) { - nsec_t k = (nsec_t) z * table[i].nsec; - - for (; n > 0; n--) - k /= 10; - - r += (nsec_t) l * table[i].nsec + k; - p = e + strlen(table[i].suffix); - - something = true; - break; - } - - if (i >= ELEMENTSOF(table)) - return -EINVAL; - - } - - *nsec = r; - - return 0; -} - -bool ntp_synced(void) { - struct timex txc = {}; - - if (adjtimex(&txc) < 0) - return false; - - if (txc.status & STA_UNSYNC) - return false; - - return true; -} - -int get_timezones(char ***ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_strv_free_ char **zones = NULL; - size_t n_zones = 0, n_allocated = 0; - - assert(ret); - - zones = strv_new("UTC", NULL); - if (!zones) - return -ENOMEM; - - n_allocated = 2; - n_zones = 1; - - f = fopen("/usr/share/zoneinfo/zone.tab", "re"); - if (f) { - char l[LINE_MAX]; - - FOREACH_LINE(l, f, return -errno) { - char *p, *w; - size_t k; - - p = strstrip(l); - - if (isempty(p) || *p == '#') - continue; - - /* Skip over country code */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Skip over coordinates */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Found timezone name */ - k = strcspn(p, WHITESPACE); - if (k <= 0) - continue; - - w = strndup(p, k); - if (!w) - return -ENOMEM; - - if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) { - free(w); - return -ENOMEM; - } - - zones[n_zones++] = w; - zones[n_zones] = NULL; - } - - strv_sort(zones); - - } else if (errno != ENOENT) - return -errno; - - *ret = TAKE_PTR(zones); - - return 0; -} -#endif /* NM_IGNORED */ - -bool timezone_is_valid(const char *name, int log_level) { - bool slash = false; - const char *p, *t; - _cleanup_close_ int fd = -1; - char buf[4]; - int r; - - if (isempty(name)) - return false; - - if (name[0] == '/') - return false; - - for (p = name; *p; p++) { - if (!(*p >= '0' && *p <= '9') && - !(*p >= 'a' && *p <= 'z') && - !(*p >= 'A' && *p <= 'Z') && - !IN_SET(*p, '-', '_', '+', '/')) - return false; - - if (*p == '/') { - - if (slash) - return false; - - slash = true; - } else - slash = false; - } - - if (slash) - return false; - - if (p - name >= PATH_MAX) - return false; - - t = strjoina("/usr/share/zoneinfo/", name); - - fd = open(t, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t); - return false; - } - - r = fd_verify_regular(fd); - if (r < 0) { - log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); - return false; - } - - r = loop_read_exact(fd, buf, 4, false); - if (r < 0) { - log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t); - return false; - } - - /* Magic from tzfile(5) */ - if (memcmp(buf, "TZif", 4) != 0) { - log_full(log_level, "Timezone file '%s' has wrong magic bytes", t); - return false; - } - - return true; -} - -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - -bool clock_supported(clockid_t clock) { - struct timespec ts; - - switch (clock) { - - case CLOCK_MONOTONIC: - case CLOCK_REALTIME: - return true; - - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; - - _fallthrough_; - default: - /* For everything else, check properly */ - return clock_gettime(clock, &ts) >= 0; - } -} - -#if 0 /* NM_IGNORED */ -int get_timezone(char **tz) { - _cleanup_free_ char *t = NULL; - const char *e; - char *z; - int r; - - r = readlink_malloc("/etc/localtime", &t); - if (r < 0) - return r; /* returns EINVAL if not a symlink */ - - e = path_startswith(t, "/usr/share/zoneinfo/"); - if (!e) - e = path_startswith(t, "../usr/share/zoneinfo/"); - if (!e) - return -EINVAL; - - if (!timezone_is_valid(e, LOG_DEBUG)) - return -EINVAL; - - z = strdup(e); - if (!z) - return -ENOMEM; - - *tz = z; - return 0; -} - -time_t mktime_or_timegm(struct tm *tm, bool utc) { - return utc ? timegm(tm) : mktime(tm); -} - -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) { - return utc ? gmtime_r(t, tm) : localtime_r(t, tm); -} - -unsigned long usec_to_jiffies(usec_t u) { - static thread_local unsigned long hz = 0; - long r; - - if (hz == 0) { - r = sysconf(_SC_CLK_TCK); - - assert(r > 0); - hz = r; - } - - return DIV_ROUND_UP(u , USEC_PER_SEC / hz); -} - -usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { - usec_t a, b; - - if (x == USEC_INFINITY) - return USEC_INFINITY; - if (map_clock_id(from) == map_clock_id(to)) - return x; - - a = now(from); - b = now(to); - - if (x > a) - /* x lies in the future */ - return usec_add(b, usec_sub_unsigned(x, a)); - else - /* x lies in the past */ - return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); -} - -bool in_utc_timezone(void) { - tzset(); - - return timezone == 0 && daylight == 0; -} - -int time_change_fd(void) { - - /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */ - static const struct itimerspec its = { - .it_value.tv_sec = TIME_T_MAX, - }; - - _cleanup_close_ int fd; - - assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); - - /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to - * CLOCK_MONOTONIC. */ - - fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - return -errno; - - if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) - return -errno; - - return TAKE_FD(fd); -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h deleted file mode 100644 index 344f2dc5..00000000 --- a/src/systemd/src/basic/time-util.h +++ /dev/null @@ -1,183 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <time.h> - -typedef uint64_t usec_t; -typedef uint64_t nsec_t; - -#define PRI_NSEC PRIu64 -#define PRI_USEC PRIu64 -#define NSEC_FMT "%" PRI_NSEC -#define USEC_FMT "%" PRI_USEC - -#include "macro.h" - -typedef struct dual_timestamp { - usec_t realtime; - usec_t monotonic; -} dual_timestamp; - -typedef struct triple_timestamp { - usec_t realtime; - usec_t monotonic; - usec_t boottime; -} triple_timestamp; - -#define USEC_INFINITY ((usec_t) -1) -#define NSEC_INFINITY ((nsec_t) -1) - -#define MSEC_PER_SEC 1000ULL -#define USEC_PER_SEC ((usec_t) 1000000ULL) -#define USEC_PER_MSEC ((usec_t) 1000ULL) -#define NSEC_PER_SEC ((nsec_t) 1000000000ULL) -#define NSEC_PER_MSEC ((nsec_t) 1000000ULL) -#define NSEC_PER_USEC ((nsec_t) 1000ULL) - -#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC)) -#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC)) -#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE)) -#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE)) -#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR)) -#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR)) -#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY)) -#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY)) -#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC)) -#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC)) -#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) -#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) - -/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this - * to 6. Let's rely on that. */ -#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1) -#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ -#define FORMAT_TIMESTAMP_RELATIVE_MAX 256 -#define FORMAT_TIMESPAN_MAX 64 - -#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) - -#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) -#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) - -usec_t now(clockid_t clock); -nsec_t now_nsec(clockid_t clock); - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts); -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts); -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); - -#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC) - -#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) - -static inline bool dual_timestamp_is_set(const dual_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY)); -} - -static inline bool triple_timestamp_is_set(const triple_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) || - (ts->boottime > 0 && ts->boottime != USEC_INFINITY)); -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); - -usec_t timespec_load(const struct timespec *ts) _pure_; -nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; -struct timespec *timespec_store(struct timespec *ts, usec_t u); - -usec_t timeval_load(const struct timeval *tv) _pure_; -struct timeval *timeval_store(struct timeval *tv, usec_t u); - -char *format_timestamp(char *buf, size_t l, usec_t t); -char *format_timestamp_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_us(char *buf, size_t l, usec_t t); -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_relative(char *buf, size_t l, usec_t t); -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); - -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); -int dual_timestamp_deserialize(const char *value, dual_timestamp *t); -int timestamp_deserialize(const char *value, usec_t *timestamp); - -int parse_timestamp(const char *t, usec_t *usec); - -int parse_sec(const char *t, usec_t *usec); -int parse_sec_fix_0(const char *t, usec_t *usec); -int parse_time(const char *t, usec_t *usec, usec_t default_unit); -int parse_nsec(const char *t, nsec_t *nsec); - -bool ntp_synced(void); - -int get_timezones(char ***l); -bool timezone_is_valid(const char *name, int log_level); - -bool clock_boottime_supported(void); -bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); - -usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); - -int get_timezone(char **timezone); - -time_t mktime_or_timegm(struct tm *tm, bool utc); -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc); - -unsigned long usec_to_jiffies(usec_t usec); - -bool in_utc_timezone(void); - -static inline usec_t usec_add(usec_t a, usec_t b) { - usec_t c; - - /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't - * overflow. */ - - c = a + b; - if (c < a || c < b) /* overflow check */ - return USEC_INFINITY; - - return c; -} - -static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { - - if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ - return USEC_INFINITY; - if (timestamp < delta) - return 0; - - return timestamp - delta; -} - -static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { - if (delta < 0) - return usec_add(timestamp, (usec_t) (-delta)); - else - return usec_sub_unsigned(timestamp, (usec_t) delta); -} - -#if SIZEOF_TIME_T == 8 -/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year - * territory. However, since we want to stay away from this in all timezones we take one day off. */ -#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000) -#elif SIZEOF_TIME_T == 4 -/* With a 32bit time_t we can't go beyond 2038... */ -#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000) -#else -#error "Yuck, time_t is neither 4 nor 8 bytes wide?" -#endif - -int time_change_fd(void); diff --git a/src/systemd/src/basic/umask-util.h b/src/systemd/src/basic/umask-util.h deleted file mode 100644 index e964292e..00000000 --- a/src/systemd/src/basic/umask-util.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "macro.h" - -static inline void umaskp(mode_t *u) { - umask(*u); -} - -#define _cleanup_umask_ _cleanup_(umaskp) - -struct _umask_struct_ { - mode_t mask; - bool quit; -}; - -static inline void _reset_umask_(struct _umask_struct_ *s) { - umask(s->mask); -}; - -#define RUN_WITH_UMASK(mask) \ - for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \ - !_saved_umask_.quit ; \ - _saved_umask_.quit = true) diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c deleted file mode 100644 index 7238bca7..00000000 --- a/src/systemd/src/basic/utf8.c +++ /dev/null @@ -1,453 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -/* Parts of this file are based on the GLIB utf8 validation functions. The - * original license text follows. */ - -/* gutf8.c - Operations on UTF-8 strings. - * - * Copyright (C) 1999 Tom Tromey - * Copyright (C) 2000 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "gunicode.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.h" - -bool unichar_is_valid(char32_t ch) { - - if (ch >= 0x110000) /* End of unicode space */ - return false; - if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ - return false; - if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ - return false; - if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ - return false; - - return true; -} - -static bool unichar_is_control(char32_t ch) { - - /* - 0 to ' '-1 is the C0 range. - DEL=0x7F, and DEL+1 to 0x9F is C1 range. - '\t' is in C0 range, but more or less harmless and commonly used. - */ - - return (ch < ' ' && !IN_SET(ch, '\t', '\n')) || - (0x7F <= ch && ch <= 0x9F); -} - -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) { - unsigned char c; - - assert(str); - - c = (unsigned char) str[0]; - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - - return 0; -} - -/* decode one unicode char */ -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { - char32_t unichar; - int len, i; - - assert(str); - - len = utf8_encoded_expected_len(str); - - switch (len) { - case 1: - *ret_unichar = (char32_t)str[0]; - return 0; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (char32_t)str[0] & 0x0f; - break; - case 4: - unichar = (char32_t)str[0] & 0x07; - break; - case 5: - unichar = (char32_t)str[0] & 0x03; - break; - case 6: - unichar = (char32_t)str[0] & 0x01; - break; - default: - return -EINVAL; - } - - for (i = 1; i < len; i++) { - if (((char32_t)str[i] & 0xc0) != 0x80) - return -EINVAL; - unichar <<= 6; - unichar |= (char32_t)str[i] & 0x3f; - } - - *ret_unichar = unichar; - - return 0; -} - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { - const char *p; - - assert(str); - - for (p = str; length;) { - int encoded_len, r; - char32_t val; - - encoded_len = utf8_encoded_valid_unichar(p); - if (encoded_len < 0 || - (size_t) encoded_len > length) - return false; - - r = utf8_encoded_to_unichar(p, &val); - if (r < 0 || - unichar_is_control(val) || - (!newline && val == '\n')) - return false; - - length -= encoded_len; - p += encoded_len; - } - - return true; -} - -const char *utf8_is_valid(const char *str) { - const uint8_t *p; - - assert(str); - - for (p = (const uint8_t*) str; *p; ) { - int len; - - len = utf8_encoded_valid_unichar((const char *)p); - if (len < 0) - return NULL; - - p += len; - } - - return str; -} - -char *utf8_escape_invalid(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) { - s = mempcpy(s, str, len); - str += len; - } else { - 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; - } - } - - *s = '\0'; - - return p; -} - -char *ascii_is_valid(const char *str) { - const char *p; - - /* Check whether the string consists of valid ASCII bytes, - * i.e values between 0 and 127, inclusive. */ - - assert(str); - - for (p = str; *p; p++) - if ((unsigned char) *p >= 128) - return NULL; - - return (char*) str; -} - -char *ascii_is_valid_n(const char *str, size_t len) { - size_t i; - - /* Very similar to ascii_is_valid(), but checks exactly len - * bytes and rejects any NULs in that range. */ - - assert(str); - - for (i = 0; i < len; i++) - if ((unsigned char) str[i] >= 128 || str[i] == 0) - return NULL; - - 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, char32_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; - } - - return 0; -} - -char *utf16_to_utf8(const void *s, size_t length) { - const uint8_t *f; - char *r, *t; - - r = new(char, (length * 4 + 1) / 2 + 1); - if (!r) - return NULL; - - f = s; - t = r; - - while (f < (const uint8_t*) s + length) { - char16_t w1, w2; - - /* see RFC 2781 section 2.2 */ - - 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; -} - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(char32_t unichar) { - - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - - return 6; -} - -/* validate one encoded unicode char and return its length */ -int utf8_encoded_valid_unichar(const char *str) { - int len, i, r; - char32_t unichar; - - assert(str); - - len = utf8_encoded_expected_len(str); - if (len == 0) - return -EINVAL; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -EINVAL; - - r = utf8_encoded_to_unichar(str, &unichar); - if (r < 0) - return r; - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != len) - return -EINVAL; - - /* check if value has valid range */ - if (!unichar_is_valid(unichar)) - return -EINVAL; - - return len; -} - -size_t utf8_n_codepoints(const char *str) { - size_t n = 0; - - /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */ - - while (*str != 0) { - int k; - - k = utf8_encoded_valid_unichar(str); - if (k < 0) - return (size_t) -1; - - str += k; - n++; - } - - return n; -} - -#if 0 /* NM_IGNORED */ -size_t utf8_console_width(const char *str) { - size_t n = 0; - - /* Returns the approximate width a string will take on screen when printed on a character cell - * terminal/console. */ - - while (*str != 0) { - char32_t c; - - if (utf8_encoded_to_unichar(str, &c) < 0) - return (size_t) -1; - - str = utf8_next_char(str); - - n += unichar_iswide(c) ? 2 : 1; - } - - return n; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h deleted file mode 100644 index f37f87ff..00000000 --- a/src/systemd/src/basic/utf8.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#if 0 /* NM_IGNORED */ -#include <uchar.h> -#endif /* NM_IGNORED */ - -#include "macro.h" -#include "missing.h" - -#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" -#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" - -bool unichar_is_valid(char32_t c); - -const char *utf8_is_valid(const char *s) _pure_; -char *ascii_is_valid(const char *s) _pure_; -char *ascii_is_valid_n(const char *str, size_t len); - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; -#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, char32_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, char32_t *ret_unichar); - -static inline bool utf16_is_surrogate(char16_t c) { - return (0xd800 <= c && c <= 0xdfff); -} - -static inline bool utf16_is_trailing_surrogate(char16_t c) { - return (0xdc00 <= c && c <= 0xdfff); -} - -static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { - return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; -} - -size_t utf8_n_codepoints(const char *str); -size_t utf8_console_width(const char *str); diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c deleted file mode 100644 index 2c6101ea..00000000 --- a/src/systemd/src/basic/util.c +++ /dev/null @@ -1,609 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <alloca.h> -#include <errno.h> -#include <fcntl.h> -#include <sched.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/prctl.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "build.h" -#include "cgroup-util.h" -#include "def.h" -#include "device-nodes.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "format-util.h" -#include "hashmap.h" -#include "hostname-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "procfs-util.h" -#include "set.h" -#include "signal-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "user-util.h" -#include "util.h" -#include "virt.h" - -#if 0 /* NM_IGNORED */ -int saved_argc = 0; -char **saved_argv = NULL; -static int saved_in_initrd = -1; -#endif /* NM_IGNORED */ - -size_t page_size(void) { - static thread_local size_t pgsz = 0; - long r; - - if (_likely_(pgsz > 0)) - return pgsz; - - r = sysconf(_SC_PAGESIZE); - assert(r > 0); - - pgsz = (size_t) r; - return pgsz; -} - -#if 0 /* NM_IGNORED */ -bool plymouth_running(void) { - return access("/run/plymouth/pid", F_OK) >= 0; -} - -bool display_is_local(const char *display) { - assert(display); - - return - display[0] == ':' && - display[1] >= '0' && - display[1] <= '9'; -} - -bool kexec_loaded(void) { - _cleanup_free_ char *s = NULL; - - if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0) - return false; - - return s[0] == '1'; -} - -int prot_from_flags(int flags) { - - switch (flags & O_ACCMODE) { - - case O_RDONLY: - return PROT_READ; - - case O_WRONLY: - return PROT_WRITE; - - case O_RDWR: - return PROT_READ|PROT_WRITE; - - default: - return -EINVAL; - } -} - -bool in_initrd(void) { - struct statfs s; - - if (saved_in_initrd >= 0) - return saved_in_initrd; - - /* We make two checks here: - * - * 1. the flag file /etc/initrd-release must exist - * 2. the root file system must be a memory file system - * - * The second check is extra paranoia, since misdetecting an - * initrd can have bad consequences due the initrd - * emptying when transititioning to the main systemd. - */ - - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); - - return saved_in_initrd; -} - -void in_initrd_force(bool value) { - saved_in_initrd = value; -} - -/* hey glibc, APIs with callbacks without a user pointer are so useless */ -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar) (const void *, const void *, void *), void *arg) { - size_t l, u, idx; - const void *p; - int comparison; - - assert(!size_multiply_overflow(nmemb, size)); - - l = 0; - u = nmemb; - while (l < u) { - idx = (l + u) / 2; - p = (const uint8_t*) base + idx * size; - comparison = compar(key, p, arg); - if (comparison < 0) - u = idx; - else if (comparison > 0) - l = idx + 1; - else - return (void *)p; - } - return NULL; -} - -int on_ac_power(void) { - bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; - - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_close_ int fd = -1, device = -1; - char contents[6]; - ssize_t n; - - device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (device < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR)) - continue; - - return -errno; - } - - fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 6 || memcmp(contents, "Mains\n", 6)) - continue; - - safe_close(fd); - fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 2 || contents[1] != '\n') - return -EIO; - - if (contents[0] == '1') { - found_online = true; - break; - } else if (contents[0] == '0') - found_offline = true; - else - return -EIO; - } - - return found_online || !found_offline; -} - -int container_get_leader(const char *machine, pid_t *pid) { - _cleanup_free_ char *s = NULL, *class = NULL; - const char *p; - pid_t leader; - int r; - - assert(machine); - assert(pid); - - if (streq(machine, ".host")) { - *pid = 1; - return 0; - } - - if (!machine_name_is_valid(machine)) - return -EINVAL; - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(NULL, p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); - if (r == -ENOENT) - return -EHOSTDOWN; - if (r < 0) - return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - - r = parse_pid(s, &leader); - if (r < 0) - return r; - if (leader <= 1) - return -EIO; - - *pid = leader; - return 0; -} - -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; - int rfd = -1; - - assert(pid >= 0); - - if (mntns_fd) { - const char *mntns; - - mntns = procfs_file_alloca(pid, "ns/mnt"); - mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntnsfd < 0) - return -errno; - } - - if (pidns_fd) { - const char *pidns; - - pidns = procfs_file_alloca(pid, "ns/pid"); - pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (pidnsfd < 0) - return -errno; - } - - if (netns_fd) { - const char *netns; - - netns = procfs_file_alloca(pid, "ns/net"); - netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netnsfd < 0) - return -errno; - } - - if (userns_fd) { - const char *userns; - - userns = procfs_file_alloca(pid, "ns/user"); - usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (usernsfd < 0 && errno != ENOENT) - return -errno; - } - - if (root_fd) { - const char *root; - - root = procfs_file_alloca(pid, "root"); - rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rfd < 0) - return -errno; - } - - if (pidns_fd) - *pidns_fd = pidnsfd; - - if (mntns_fd) - *mntns_fd = mntnsfd; - - if (netns_fd) - *netns_fd = netnsfd; - - if (userns_fd) - *userns_fd = usernsfd; - - if (root_fd) - *root_fd = rfd; - - pidnsfd = mntnsfd = netnsfd = usernsfd = -1; - - return 0; -} - -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { - if (userns_fd >= 0) { - /* Can't setns to your own userns, since then you could - * escalate from non-root to root in your own namespace, so - * check if namespaces equal before attempting to enter. */ - _cleanup_free_ char *userns_fd_path = NULL; - int r; - if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) - return -ENOMEM; - - r = files_same(userns_fd_path, "/proc/self/ns/user", 0); - if (r < 0) - return r; - if (r) - userns_fd = -1; - } - - if (pidns_fd >= 0) - if (setns(pidns_fd, CLONE_NEWPID) < 0) - return -errno; - - if (mntns_fd >= 0) - if (setns(mntns_fd, CLONE_NEWNS) < 0) - return -errno; - - if (netns_fd >= 0) - if (setns(netns_fd, CLONE_NEWNET) < 0) - return -errno; - - if (userns_fd >= 0) - if (setns(userns_fd, CLONE_NEWUSER) < 0) - return -errno; - - if (root_fd >= 0) { - if (fchdir(root_fd) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - } - - return reset_uid_gid(); -} - -uint64_t physical_memory(void) { - _cleanup_free_ char *root = NULL, *value = NULL; - uint64_t mem, lim; - size_t ps; - long sc; - int r; - - /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of - * memory. - * - * In order to support containers nicely that have a configured memory limit we'll take the minimum of the - * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ - - sc = sysconf(_SC_PHYS_PAGES); - assert(sc > 0); - - ps = page_size(); - mem = (uint64_t) sc * (uint64_t) ps; - - r = cg_get_root_path(&root); - if (r < 0) { - log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m"); - return mem; - } - - r = cg_all_unified(); - if (r < 0) { - log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m"); - return mem; - } - if (r > 0) { - r = cg_get_attribute("memory", root, "memory.max", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - - if (streq(value, "max")) - return mem; - } else { - r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - } - - r = safe_atou64(value, &lim); - if (r < 0) { - log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value); - return mem; - } - if (lim == UINT64_MAX) - return mem; - - /* Make sure the limit is a multiple of our own page size */ - lim /= ps; - lim *= ps; - - return MIN(mem, lim); -} - -uint64_t physical_memory_scale(uint64_t v, uint64_t max) { - uint64_t p, m, ps, r; - - assert(max > 0); - - /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success - * the result is a multiple of the page size (rounds down). */ - - ps = page_size(); - assert(ps > 0); - - p = physical_memory() / ps; - assert(p > 0); - - m = p * v; - if (m / p != v) - return UINT64_MAX; - - m /= max; - - r = m * ps; - if (r / ps != m) - return UINT64_MAX; - - return r; -} - -uint64_t system_tasks_max(void) { - - uint64_t a = TASKS_MAX, b = TASKS_MAX; - _cleanup_free_ char *root = NULL; - int r; - - /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this - * limit: - * - * a) the maximum tasks value the kernel allows on this architecture - * b) the cgroups pids_max attribute for the system - * c) the kernel's configured maximum PID value - * - * And then pick the smallest of the three */ - - r = procfs_tasks_get_limit(&a); - if (r < 0) - log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m"); - - r = cg_get_root_path(&root); - if (r < 0) - log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); - else { - _cleanup_free_ char *value = NULL; - - r = cg_get_attribute("pids", root, "pids.max", &value); - if (r < 0) - log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); - else if (!streq(value, "max")) { - r = safe_atou64(value, &b); - if (r < 0) - log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); - } - } - - return MIN3(TASKS_MAX, - a <= 0 ? TASKS_MAX : a, - b <= 0 ? TASKS_MAX : b); -} - -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { - uint64_t t, m; - - assert(max > 0); - - /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages - * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ - - t = system_tasks_max(); - assert(t > 0); - - m = t * v; - if (m / t != v) /* overflow? */ - return UINT64_MAX; - - return m / max; -} - -int version(void) { - puts(PACKAGE_STRING "\n" - SYSTEMD_FEATURES); - return 0; -} - -/* This is a direct translation of str_verscmp from boot.c */ -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static int c_order(int c) { - if (c == 0 || is_digit(c)) - return 0; - - if ((c >= 'a') && (c <= 'z')) - return c; - - return c + 0x10000; -} - -int str_verscmp(const char *s1, const char *s2) { - const char *os1, *os2; - - assert(s1); - assert(s2); - - os1 = s1; - os2 = s2; - - while (*s1 || *s2) { - int first; - - while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { - int order; - - order = c_order(*s1) - c_order(*s2); - if (order != 0) - return order; - s1++; - s2++; - } - - while (*s1 == '0') - s1++; - while (*s2 == '0') - s2++; - - first = 0; - while (is_digit(*s1) && is_digit(*s2)) { - if (first == 0) - first = *s1 - *s2; - s1++; - s2++; - } - - if (is_digit(*s1)) - return 1; - if (is_digit(*s2)) - return -1; - - if (first != 0) - return first; - } - - return strcmp(os1, os2); -} - -/* Turn off core dumps but only if we're running outside of a container. */ -void disable_coredumps(void) { - int r; - - if (detect_container() > 0) - return; - - r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); - if (r < 0) - log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h deleted file mode 100644 index 308933ac..00000000 --- a/src/systemd/src/basic/util.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <locale.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/inotify.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "format-util.h" -#include "macro.h" -#include "missing.h" -#include "time-util.h" - -size_t page_size(void) _pure_; -#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) - -static inline const char* yes_no(bool b) { - return b ? "yes" : "no"; -} - -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* enable_disable(bool b) { - return b ? "enable" : "disable"; -} - -bool plymouth_running(void); - -bool display_is_local(const char *display) _pure_; - -#define NULSTR_FOREACH(i, l) \ - for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) - -#define NULSTR_FOREACH_PAIR(i, j, l) \ - for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) - -extern int saved_argc; -extern char **saved_argv; - -bool kexec_loaded(void); - -int prot_from_flags(int flags) _const_; - -bool in_initrd(void); -void in_initrd_force(bool value); - -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar) (const void *, const void *, void *), - void *arg); - -/** - * Normal bsearch requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void* bsearch_safe(const void *key, const void *base, - size_t nmemb, size_t size, comparison_fn_t compar) { - if (nmemb <= 0) - return NULL; - - assert(base); - return bsearch(key, base, nmemb, size, compar); -} - -/** - * Normal qsort requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) { - if (nmemb <= 1) - return; - - assert(base); - qsort(base, nmemb, size, compar); -} - -/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so - * is the prototype for the comparison function */ -#define typesafe_qsort(p, n, func) \ - ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ - }) - -static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void *userdata) { - if (nmemb <= 1) - return; - - assert(base); - qsort_r(base, nmemb, size, compar, userdata); -} - -/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { - if (n == 0) - return; - assert(src); - memcpy(dst, src, n); -} - -/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ -static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { - if (n == 0) - return 0; - assert(s1); - assert(s2); - return memcmp(s1, s2, n); -} - -int on_ac_power(void); - -#define memzero(x,l) (memset((x), 0, (l))) -#define zero(x) (memzero(&(x), sizeof(x))) - -static inline void *mempset(void *s, int c, size_t n) { - memset(s, c, n); - return (uint8_t*)s + n; -} - -static inline void _reset_errno_(int *saved_errno) { - errno = *saved_errno; -} - -#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno - -static inline int negative_errno(void) { - /* This helper should be used to shut up gcc if you know 'errno' is - * negative. Instead of "return -errno;", use "return negative_errno();" - * It will suppress bogus gcc warnings in case it assumes 'errno' might - * be 0 and thus the caller's error-handling might not be triggered. */ - assert_return(errno > 0, -EINVAL); - return -errno; -} - -static inline unsigned u64log2(uint64_t n) { -#if __SIZEOF_LONG_LONG__ == 8 - return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; -#else -#error "Wut?" -#endif -} - -static inline unsigned u32ctz(uint32_t n) { -#if __SIZEOF_INT__ == 4 - return __builtin_ctz(n); -#else -#error "Wut?" -#endif -} - -static inline unsigned log2i(int x) { - assert(x > 0); - - return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u(unsigned x) { - assert(x > 0); - - return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u_round_up(unsigned x) { - assert(x > 0); - - if (x == 1) - return 0; - - return log2u(x - 1) + 1; -} - -int container_get_leader(const char *machine, pid_t *pid); - -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd); -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); - -uint64_t physical_memory(void); -uint64_t physical_memory_scale(uint64_t v, uint64_t max); - -uint64_t system_tasks_max(void); -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); - -int version(void); - -int str_verscmp(const char *s1, const char *s2); - -void disable_coredumps(void); diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c index 4fbecb54..154ba6b7 100644 --- a/src/systemd/src/libsystemd-network/arp-util.c +++ b/src/systemd/src/libsystemd-network/arp-util.c @@ -3,7 +3,7 @@ Copyright © 2014 Axis Communications AB. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <linux/filter.h> #include <arpa/inet.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index b1afbf00..7fc8248a 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <linux/if_infiniband.h> #include <net/if_arp.h> @@ -13,13 +13,9 @@ #include "network-internal.h" #include "siphash24.h" #include "sparse-endian.h" +#include "stdio-util.h" #include "virt.h" -#if 0 /* NM_IGNORED */ -#else /* NM_IGNORED */ -#include <net/if.h> -#endif /* NM_IGNORED */ - #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) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) @@ -123,8 +119,13 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { assert(len); r = sd_id128_get_machine(&machine_id); - if (r < 0) + if (r < 0) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); +#else return r; +#endif + } unaligned_write_be16(&duid->type, DUID_TYPE_EN); unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); @@ -160,38 +161,37 @@ int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { } #endif -int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) { +int dhcp_identifier_set_iaid( + int ifindex, + const uint8_t *mac, + size_t mac_len, + bool legacy_unstable_byteorder, + void *_id) { #if 0 /* NM_IGNORED */ /* name is a pointer to memory in the sd_device struct, so must * have the same scope */ _cleanup_(sd_device_unrefp) sd_device *device = NULL; -#else /* NM_IGNORED */ - char name_buf[IF_NAMESIZE]; -#endif /* NM_IGNORED */ const char *name = NULL; uint64_t id; + uint32_t id32; -#if 0 /* NM_IGNORED */ if (detect_container() <= 0) { /* not in a container, udev will be around */ - char ifindex_str[2 + DECIMAL_STR_MAX(int)]; - int initialized, r; + char ifindex_str[1 + DECIMAL_STR_MAX(int)]; + int r; - sprintf(ifindex_str, "n%d", ifindex); + xsprintf(ifindex_str, "n%d", ifindex); if (sd_device_new_from_device_id(&device, ifindex_str) >= 0) { - r = sd_device_get_is_initialized(device, &initialized); + r = sd_device_get_is_initialized(device); if (r < 0) return r; - if (!initialized) + if (r == 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) id = siphash24(name, strlen(name), HASH_KEY.bytes); @@ -199,10 +199,23 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i /* fall back to MAC address if no predictable name available */ id = siphash24(mac, mac_len, HASH_KEY.bytes); - id = htole64(id); + id32 = (id & 0xffffffff) ^ (id >> 32); - /* fold into 32 bits */ - unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32)); + if (legacy_unstable_byteorder) + /* for historical reasons (a bug), the bits were swapped and thus + * the result was endianness dependent. Preserve that behavior. */ + id32 = __bswap_32(id32); + else + /* the fixed behavior returns a stable byte order. Since LE is expected + * to be more common, swap the bytes on LE to give the same as legacy + * behavior. */ + id32 = be32toh(id32); + unaligned_write_ne32(_id, id32); return 0; +#else /* NM_IGNORED */ + /* for NetworkManager, we don't use this function and we should never call here. + * This got replaced by nm_utils_create_dhcp_iaid(). */ + g_return_val_if_reached (-EINVAL); +#endif /* NM_IGNORED */ } diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index e6834039..b3115125 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -57,4 +57,4 @@ int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *add int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); -int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id); +int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, void *_id); diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 9d245a90..122042ab 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -41,7 +41,6 @@ struct sd_dhcp_lease { /* each 0 if unset */ be32_t address; be32_t server_address; - be32_t router; be32_t next_server; bool have_subnet_mask; @@ -50,6 +49,9 @@ struct sd_dhcp_lease { bool have_broadcast; be32_t broadcast; + struct in_addr *router; + size_t router_size; + struct in_addr *dns; size_t dns_size; diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 80e9577c..810a2633 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -3,9 +3,8 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <net/ethernet.h> #include <net/if.h> #include <net/if_arp.h> @@ -52,12 +51,16 @@ 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_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 */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ 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_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + + /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally + * compare chaddr for ETH_ALEN bytes. */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 12), /* A (the MAC address length) == ETH_ALEN ? */ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(ð_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ @@ -70,6 +73,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ @@ -80,7 +84,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, .filter = filter }; _cleanup_close_ int s = -1; - int r, on = 1; + int r; assert(ifindex > 0); assert(link); @@ -89,9 +93,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, if (s < 0) return -errno; - r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); + r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true); if (r < 0) - return -errno; + return r; r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (r < 0) @@ -151,19 +155,19 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { }; _cleanup_close_ int s = -1; char ifname[IF_NAMESIZE] = ""; - int r, on = 1, tos = IPTOS_CLASS_CS6; + int r; s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; if (ifindex > 0) { if (if_indextoname(ifindex, ifname) == 0) @@ -175,18 +179,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { } if (address == INADDR_ANY) { - r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true); if (r < 0) - return -errno; + return r; } else { - r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true); if (r < 0) - return -errno; + return r; } r = bind(s, &src.sa, sizeof(src.in)); diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c index 4a4f7de0..c5fbe749 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -3,9 +3,8 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <stdint.h> #include <stdio.h> #include <string.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c index 8f8acb0d..91e8a54e 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -3,9 +3,8 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <net/ethernet.h> #include <net/if_arp.h> #include <string.h> @@ -108,70 +107,62 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui /* IP */ - if (packet->ip.version != IPVERSION) { - log_debug("ignoring packet: not IPv4"); - return -EINVAL; - } + if (packet->ip.version != IPVERSION) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: not IPv4"); - if (packet->ip.ihl < 5) { - log_debug("ignoring packet: IPv4 IHL (%u words) invalid", - packet->ip.ihl); - return -EINVAL; - } + if (packet->ip.ihl < 5) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: IPv4 IHL (%u words) invalid", + packet->ip.ihl); hdrlen = packet->ip.ihl * 4; - if (hdrlen < 20) { - log_debug("ignoring packet: IPv4 IHL (%zu bytes) " - "smaller than minimum (20 bytes)", hdrlen); - return -EINVAL; - } - - if (len < hdrlen) { - log_debug("ignoring packet: packet (%zu bytes) " - "smaller than expected (%zu) by IP header", len, - hdrlen); - return -EINVAL; - } + if (hdrlen < 20) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: IPv4 IHL (%zu bytes) " + "smaller than minimum (20 bytes)", + hdrlen); + + if (len < hdrlen) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: packet (%zu bytes) " + "smaller than expected (%zu) by IP header", + len, hdrlen); /* UDP */ - if (packet->ip.protocol != IPPROTO_UDP) { - log_debug("ignoring packet: not UDP"); - return -EINVAL; - } + if (packet->ip.protocol != IPPROTO_UDP) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: not UDP"); - if (len < hdrlen + be16toh(packet->udp.len)) { - log_debug("ignoring packet: packet (%zu bytes) " - "smaller than expected (%zu) by UDP header", len, - hdrlen + be16toh(packet->udp.len)); - return -EINVAL; - } + if (len < hdrlen + be16toh(packet->udp.len)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: packet (%zu bytes) " + "smaller than expected (%zu) by UDP header", + len, hdrlen + be16toh(packet->udp.len)); - if (be16toh(packet->udp.dest) != port) { - log_debug("ignoring packet: to port %u, which " - "is not the DHCP client port (%u)", - be16toh(packet->udp.dest), port); - return -EINVAL; - } + if (be16toh(packet->udp.dest) != port) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: to port %u, which " + "is not the DHCP client port (%u)", + be16toh(packet->udp.dest), port); /* checksums - computing these is relatively expensive, so only do it if all the other checks have passed */ - if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) { - log_debug("ignoring packet: invalid IP checksum"); - return -EINVAL; - } + if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: invalid IP checksum"); if (checksum && packet->udp.check) { packet->ip.check = packet->udp.len; packet->ip.ttl = 0; if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, - be16toh(packet->udp.len) + 12)) { - log_debug("ignoring packet: invalid UDP checksum"); - return -EINVAL; - } + be16toh(packet->udp.len) + 12)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: invalid UDP checksum"); } return 0; diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 06e2e532..157fc0aa 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -73,8 +73,6 @@ struct DHCP6IA { struct ia_pd ia_pd; struct ia_ta ia_ta; }; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; LIST_HEAD(DHCP6Address, addresses); }; @@ -86,8 +84,8 @@ typedef struct DHCP6IA DHCP6IA; int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); -int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd); +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); +int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h index ff0b0f00..e004f48b 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h @@ -37,7 +37,6 @@ struct sd_dhcp6_lease { size_t ntp_fqdn_count; }; -int dhcp6_lease_clear_timers(DHCP6IA *ia); int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); @@ -50,6 +49,7 @@ int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); +int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index 98aa6261..d786756e 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -3,9 +3,8 @@ Copyright © 2014 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <netinet/in.h> #include <netinet/ip6.h> #include <stdio.h> @@ -27,7 +26,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { .in6.sin6_scope_id = index, }; _cleanup_close_ int s = -1; - int r, off = 0, on = 1; + int r; assert(index > 0); assert(local_address); @@ -38,17 +37,17 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; r = bind(s, &src.sa, sizeof(src.in6)); if (r < 0) diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index 22970443..3d5abe64 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -3,9 +3,8 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <netinet/in.h> #include <string.h> @@ -39,9 +38,9 @@ typedef struct DHCP6PDPrefixOption { uint8_t options[]; } _packed_ DHCP6PDPrefixOption; -#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) -#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) -#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) +#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) +#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) +#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { @@ -51,14 +50,14 @@ static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option)) + if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; option->code = htobe16(optcode); option->len = htobe16(optlen); - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); + *buf += offsetof(DHCP6Option, data); + *buflen -= offsetof(DHCP6Option, data); return 0; } @@ -81,14 +80,17 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, return 0; } -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; size_t iaid_offset, ia_buflen, ia_addrlen = 0; DHCP6Address *addr; int r; - assert_return(buf && *buf && buflen && ia, -EINVAL); + assert_return(buf, -EINVAL); + assert_return(*buf, -EINVAL); + assert_return(buflen, -EINVAL); + assert_return(ia, -EINVAL); switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: @@ -111,8 +113,8 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { ia_hdr = *buf; ia_buflen = *buflen; - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); + *buf += offsetof(DHCP6Option, data); + *buflen -= offsetof(DHCP6Option, data); memcpy(*buf, (char*) ia + iaid_offset, len); @@ -130,7 +132,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buf += sizeof(addr->iaaddr); *buflen -= sizeof(addr->iaaddr); - ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr); + ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr); } r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); @@ -167,7 +169,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } -int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) { +int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) { DHCP6Option *option = (DHCP6Option *)buf; size_t i = sizeof(*option) + sizeof(pd->ia_pd); DHCP6Address *prefix; @@ -212,7 +214,7 @@ static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, si assert_return(optcode, -EINVAL); assert_return(optlen, -EINVAL); - if (*buflen < sizeof(DHCP6Option)) + if (*buflen < offsetof(DHCP6Option, data)) return -ENOMSG; len = be16toh(option->len); @@ -253,7 +255,7 @@ int dhcp6_option_parse_status(DHCP6Option *option, size_t len) { DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; if (len < sizeof(DHCP6StatusOption) || - be16toh(option->len) + sizeof(DHCP6Option) < sizeof(DHCP6StatusOption)) + be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption)) return -ENOBUFS; return be16toh(statusopt->status); @@ -266,7 +268,7 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, uint32_t lt_valid, lt_pref; int r; - if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option)) + if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option)) return -ENOBUFS; lt_valid = be32toh(addr_option->iaaddr.lifetime_valid); @@ -279,8 +281,8 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, return 0; } - if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*addr_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option)); if (r != 0) return r < 0 ? r: 0; } @@ -306,7 +308,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, uint32_t lt_valid, lt_pref; int r; - if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option)) + if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option)) return -ENOBUFS; lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid); @@ -319,8 +321,8 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, return 0; } - if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*pdprefix_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option)); if (r != 0) return r < 0 ? r: 0; } @@ -356,10 +358,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { switch (iatype) { case SD_DHCP6_OPTION_IA_NA: - if (len < DHCP6_OPTION_IA_NA_LEN) { - r = -ENOBUFS; - goto error; - } + if (len < DHCP6_OPTION_IA_NA_LEN) + return -ENOBUFS; iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); @@ -370,18 +370,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds", lt_t1, lt_t2); - r = -EINVAL; - goto error; + return -EINVAL; } break; case SD_DHCP6_OPTION_IA_PD: - if (len < sizeof(ia->ia_pd)) { - r = -ENOBUFS; - goto error; - } + if (len < sizeof(ia->ia_pd)) + return -ENOBUFS; iaaddr_offset = sizeof(ia->ia_pd); memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd)); @@ -392,17 +389,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", lt_t1, lt_t2); - r = -EINVAL; - goto error; + return -EINVAL; } break; case SD_DHCP6_OPTION_IA_TA: - if (len < DHCP6_OPTION_IA_TA_LEN) { - r = -ENOBUFS; - goto error; - } + if (len < DHCP6_OPTION_IA_TA_LEN) + return -ENOBUFS; iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); @@ -410,8 +404,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; default: - r = -ENOMSG; - goto error; + return -ENOMSG; } ia->type = iatype; @@ -420,10 +413,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { while (i < len) { DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; - if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { - r = -ENOBUFS; - goto error; - } + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) + return -ENOBUFS; opt = be16toh(option->code); optlen = be16toh(option->len); @@ -433,13 +424,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { log_dhcp6_client(client, "IA Address option not in IA NA or TA option"); - r = -EINVAL; - goto error; + return -EINVAL; } r = dhcp6_option_parse_address(option, ia, <_valid); if (r < 0) - goto error; + return r; if (lt_valid < lt_min) lt_min = lt_valid; @@ -450,13 +440,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) { log_dhcp6_client(client, "IA PD Prefix option not in IA PD option"); - r = -EINVAL; - goto error; + return -EINVAL; } r = dhcp6_option_parse_pdprefix(option, ia, <_valid); if (r < 0) - goto error; + return r; if (lt_valid < lt_min) lt_min = lt_valid; @@ -465,17 +454,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); - if (status < 0) { - r = status; - goto error; - } + status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); + if (status < 0) + return status; if (status > 0) { log_dhcp6_client(client, "IA status %d", status); - r = -EINVAL; - goto error; + return -EINVAL; } break; @@ -519,8 +505,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; } -error: - return r; + return 0; } int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h index 328d51f8..88b54933 100644 --- a/src/systemd/src/libsystemd-network/lldp-internal.h +++ b/src/systemd/src/libsystemd-network/lldp-internal.h @@ -34,3 +34,6 @@ struct sd_lldp { #define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) #define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) + +const char* lldp_event_to_string(sd_lldp_event e) _const_; +sd_lldp_event lldp_event_from_string(const char *s) _pure_; diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index cd9358a4..bbe7bf9d 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include "alloc-util.h" #include "escape.h" @@ -9,40 +9,24 @@ #include "in-addr-util.h" #include "lldp-internal.h" #include "lldp-neighbor.h" +#include "missing.h" #include "unaligned.h" +#include "util.h" -static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) { - const LLDPNeighborID *id = p; - +static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { siphash24_compress(id->chassis_id, id->chassis_id_size, state); siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); siphash24_compress(id->port_id, id->port_id_size, state); siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); } -static int lldp_neighbor_id_compare_func(const void *a, const void *b) { - const LLDPNeighborID *x = a, *y = b; - int r; - - r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size)); - if (r != 0) - return r; - - r = CMP(x->chassis_id_size, y->chassis_id_size); - if (r != 0) - return r; - - r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size)); - if (r != 0) - return r; - - return CMP(x->port_id_size, y->port_id_size); +int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { + return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) + ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); } -const struct hash_ops lldp_neighbor_id_hash_ops = { - .hash = lldp_neighbor_id_hash_func, - .compare = lldp_neighbor_id_compare_func -}; +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func, + sd_lldp_neighbor, lldp_neighbor_unlink); int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { const sd_lldp_neighbor *x = a, *y = b; @@ -100,7 +84,12 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { if (!n->lldp) return NULL; - assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n); + /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is + * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register + * ourselves from the hashtable and sometimes are called after we already are de-registered. */ + + (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n); + assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); n->lldp = NULL; @@ -704,7 +693,7 @@ _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { return type == k; } -_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) { +_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) { const uint8_t *d; size_t length; int r; @@ -733,7 +722,7 @@ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], u return 0; } -_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) { +_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) { uint8_t k[3], st; int r; diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h index 494bc517..62dbff42 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.h +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h @@ -80,7 +80,8 @@ static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; } -extern const struct hash_ops lldp_neighbor_id_hash_ops; +extern const struct hash_ops lldp_neighbor_hash_ops; +int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y); int lldp_neighbor_prioq_compare_func(const void *a, const void *b); sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c index db779aaa..5ba9f081 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.c +++ b/src/systemd/src/libsystemd-network/lldp-network.c @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <linux/filter.h> #include <netinet/if_ether.h> #include "fd-util.h" #include "lldp-network.h" +#include "missing.h" #include "socket-util.h" int lldp_network_bind_raw_socket(int ifindex) { diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index 57a59030..f85db47b 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> #include <linux/if.h> @@ -12,6 +12,7 @@ #include "alloc-util.h" #include "condition.h" #include "conf-parser.h" +#include "device-util.h" #include "dhcp-lease-internal.h" #include "ether-addr-util.h" #include "hexdecoct.h" @@ -43,31 +44,35 @@ const char *net_get_name(sd_device *device) { int net_get_unique_predictable_data(sd_device *device, uint64_t *result) { size_t l, sz = 0; - const char *name = NULL; + const char *name; int r; uint8_t *v; assert(device); + /* net_get_name() will return one of the device names based on stable information about the + * device. If this is not available, we fall back to using the device name. */ name = net_get_name(device); if (!name) - return -ENOENT; + (void) sd_device_get_sysname(device, &name); + if (!name) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA), + "No stable identifying information found"); + log_device_debug(device, "Using \"%s\" as stable identifying information", name); l = strlen(name); sz = sizeof(sd_id128_t) + l; - v = alloca(sz); + v = newa(uint8_t, sz); - /* fetch some persistent data unique to this machine */ + /* Fetch some persistent data unique to this machine */ r = sd_id128_get_machine((sd_id128_t*) v); if (r < 0) return r; memcpy(v + sizeof(sd_id128_t), name, l); - /* Let's hash the machine ID plus the device name. We - * use a fixed, but originally randomly created hash - * key here. */ + /* Let's hash the machine ID plus the device name. We use + * a fixed, but originally randomly created hash key here. */ *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); - return 0; } @@ -105,7 +110,6 @@ bool net_match_config(Set *match_mac, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, - const char *dev_parent_driver, const char *dev_driver, const char *dev_type, const char *dev_name) { @@ -258,11 +262,10 @@ int config_parse_ifalias(const char *unit, return 0; } - free(*s); - if (*n) - *s = TAKE_PTR(n); + if (isempty(n)) + *s = mfree(*s); else - *s = NULL; + free_and_replace(*s, n); return 0; } @@ -297,7 +300,7 @@ int config_parse_hwaddr(const char *unit, return 0; } - *hwaddr = TAKE_PTR(n); + free_and_replace(*hwaddr, n); return 0; } @@ -375,36 +378,6 @@ int config_parse_hwaddrs(const char *unit, return 0; } -int config_parse_iaid(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - uint32_t iaid; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atou32(rvalue, &iaid); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Unable to read IAID, ignoring assignment: %s", rvalue); - return 0; - } - - *((uint32_t *)data) = iaid; - - return 0; -} - int config_parse_bridge_port_priority( const char *unit, const char *filename, @@ -444,16 +417,33 @@ int config_parse_bridge_port_priority( } #endif /* NM_IGNORED */ -void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { - unsigned i; +size_t serialize_in_addrs(FILE *f, + const struct in_addr *addresses, + size_t size, + bool with_leading_space, + bool (*predicate)(const struct in_addr *addr)) { + size_t count; + size_t i; assert(f); assert(addresses); - assert(size); - for (i = 0; i < size; i++) - fprintf(f, "%s%s", inet_ntoa(addresses[i]), - (i < (size - 1)) ? " ": ""); + count = 0; + + for (i = 0; i < size; i++) { + char sbuf[INET_ADDRSTRLEN]; + + if (predicate && !predicate(&addresses[i])) + continue; + if (with_leading_space) + fputc(' ', f); + else + with_leading_space = true; + fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f); + count++; + } + + return count; } int deserialize_in_addrs(struct in_addr **ret, const char *string) { @@ -487,7 +477,7 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { size++; } - *ret = TAKE_PTR(addresses); + *ret = size > 0 ? TAKE_PTR(addresses) : NULL; return size; } @@ -556,6 +546,7 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz fprintf(f, "%s=", key); for (i = 0; i < size; i++) { + char sbuf[INET_ADDRSTRLEN]; struct in_addr dest, gw; uint8_t length; @@ -563,8 +554,8 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0); assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0); - fprintf(f, "%s/%" PRIu8, inet_ntoa(dest), length); - fprintf(f, ",%s%s", inet_ntoa(gw), (i < (size - 1)) ? " ": ""); + fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length); + fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": ""); } fputs("\n", f); diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 06d61184..9119d9a4 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -8,7 +8,9 @@ #include "condition.h" #include "conf-parser.h" +#include "def.h" #include "set.h" +#include "strv.h" #define LINK_BRIDGE_PORT_PRIORITY_INVALID 128 #define LINK_BRIDGE_PORT_PRIORITY_MAX 63 @@ -25,7 +27,6 @@ bool net_match_config(Set *match_mac, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, - const char *dev_parent_driver, const char *dev_driver, const char *dev_type, const char *dev_name); @@ -36,14 +37,17 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); -CONFIG_PARSER_PROTOTYPE(config_parse_iaid); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); int net_get_unique_predictable_data(sd_device *device, uint64_t *result); const char *net_get_name(sd_device *device); #endif /* NM_IGNORED */ -void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size); +size_t serialize_in_addrs(FILE *f, + const struct in_addr *addresses, + size_t size, + bool with_leading_space, + bool (*predicate)(const struct in_addr *addr)); int deserialize_in_addrs(struct in_addr **addresses, const char *string); void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size); @@ -57,3 +61,5 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size); + +#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network")) diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 0c385a84..27f79638 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -3,9 +3,8 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <net/ethernet.h> #include <net/if_arp.h> #include <stdio.h> @@ -23,11 +22,13 @@ #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" #include "dns-domain.h" +#include "event-util.h" #include "hostname-util.h" +#include "io-util.h" #include "random-util.h" #include "string-util.h" -#include "util.h" #include "strv.h" +#include "util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -88,7 +89,7 @@ struct sd_dhcp_client { uint32_t mtu; uint32_t xid; usec_t start_time; - unsigned int attempt; + unsigned attempt; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -118,19 +119,19 @@ static const uint8_t default_req_opts[] = { */ /* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */ static const uint8_t default_req_opts_anonymize[] = { - SD_DHCP_OPTION_SUBNET_MASK, /* 1 */ - SD_DHCP_OPTION_ROUTER, /* 3 */ - SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ - SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ - SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ - SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ - SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ - SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ - SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ - SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ - SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ - SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */ + SD_DHCP_OPTION_SUBNET_MASK, /* 1 */ + SD_DHCP_OPTION_ROUTER, /* 3 */ + SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ + SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ + SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ + SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ + SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ + SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ + SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ + SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ + SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ + SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */ }; static int client_receive_message_raw( @@ -342,8 +343,9 @@ int sd_dhcp_client_set_client_id( */ static int dhcp_client_set_iaid_duid_internal( sd_dhcp_client *client, + bool iaid_append, + bool iaid_set, uint32_t iaid, - bool append_iaid, uint16_t duid_type, const void *duid, size_t duid_len, @@ -354,9 +356,9 @@ static int dhcp_client_set_iaid_duid_internal( size_t len; assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); + assert_return(duid_len == 0 || duid, -EINVAL); - if (duid != NULL) { + if (duid) { r = dhcp_validate_duid_len(duid_type, duid_len, true); if (r < 0) return r; @@ -365,26 +367,27 @@ static int dhcp_client_set_iaid_duid_internal( zero(client->client_id); client->client_id.type = 255; - if (append_iaid) { - /* If IAID is not configured, generate it. */ - if (iaid == 0) { + if (iaid_append) { + if (iaid_set) + client->client_id.ns.iaid = htobe32(iaid); + else { r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, + true, &client->client_id.ns.iaid); if (r < 0) return r; - } else - client->client_id.ns.iaid = htobe32(iaid); + } } - if (duid != NULL) { + if (duid) { client->client_id.ns.duid.type = htobe16(duid_type); memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); len = sizeof(client->client_id.ns.duid.type) + duid_len; } else switch (duid_type) { case DUID_TYPE_LLT: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); @@ -397,7 +400,7 @@ static int dhcp_client_set_iaid_duid_internal( return r; break; case DUID_TYPE_LL: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); @@ -414,10 +417,10 @@ static int dhcp_client_set_iaid_duid_internal( } client->client_id_len = sizeof(client->client_id.type) + len + - (append_iaid ? sizeof(client->client_id.ns.iaid) : 0); + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : ""); + log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : ""); client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); sd_dhcp_client_start(client); } @@ -427,18 +430,20 @@ static int dhcp_client_set_iaid_duid_internal( int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time); } int sd_dhcp_client_set_duid( @@ -446,13 +451,13 @@ int sd_dhcp_client_set_duid( uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_duid_llt( sd_dhcp_client *client, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time); } #endif /* NM_IGNORED */ @@ -545,11 +550,10 @@ static int client_initialize(sd_dhcp_client *client) { client->fd = asynchronous_close(client->fd); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); - client->timeout_expire = sd_event_source_unref(client->timeout_expire); + (void) event_source_disable(client->timeout_resend); + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_expire); client->attempt = 1; @@ -650,7 +654,8 @@ static int client_message_init( client->client_id.type = 255; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid); + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, + true, &client->client_id.ns.iaid); if (r < 0) return r; @@ -698,7 +703,7 @@ static int client_message_init( let the server know how large the server may make its DHCP messages. Note (from ConnMan): Some DHCP servers will send bigger DHCP packets - than the defined default size unless the Maximum Messge Size option + than the defined default size unless the Maximum Message Size option is explicitly set RFC3442 "Requirements to Avoid Sizing Constraints": @@ -1063,22 +1068,11 @@ static int client_timeout_resend( next_timeout += (random_u32() & 0x1fffff); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - next_timeout, 10 * USEC_PER_MSEC, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + next_timeout, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp4-resend-timer", true); if (r < 0) goto error; @@ -1175,31 +1169,16 @@ static int client_initialize_time_events(sd_dhcp_client *client) { assert(client); assert(client->event); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - if (client->start_delay) { assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); usec += client->start_delay; } - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - usec, 0, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); - if (r < 0) - goto error; - -error: + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + usec, 0, + client_timeout_resend, client, + client->event_priority, "dhcp4-resend-timer", true); if (r < 0) client_stop(client, r); @@ -1457,13 +1436,14 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { assert(client->lease); assert(client->lease->lifetime); - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); - client->timeout_expire = sd_event_source_unref(client->timeout_expire); - /* don't set timers for infinite leases */ - if (client->lease->lifetime == 0xffffffff) + if (client->lease->lifetime == 0xffffffff) { + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_expire); + return 0; + } r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) @@ -1515,19 +1495,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { } /* arm lifetime timeout */ - r = sd_event_add_time(client->event, &client->timeout_expire, - clock_boottime_or_monotonic(), - lifetime_timeout, 10 * USEC_PER_MSEC, - client_timeout_expire, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_expire, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime"); + r = event_reset_time(client->event, &client->timeout_expire, + clock_boottime_or_monotonic(), + lifetime_timeout, 10 * USEC_PER_MSEC, + client_timeout_expire, client, + client->event_priority, "dhcp4-lifetime", true); if (r < 0) return r; @@ -1539,21 +1511,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; /* arm T2 timeout */ - r = sd_event_add_time(client->event, - &client->timeout_t2, - clock_boottime_or_monotonic(), - t2_timeout, - 10 * USEC_PER_MSEC, - client_timeout_t2, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_t2, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout"); + r = event_reset_time(client->event, &client->timeout_t2, + clock_boottime_or_monotonic(), + t2_timeout, 10 * USEC_PER_MSEC, + client_timeout_t2, client, + client->event_priority, "dhcp4-t2-timeout", true); if (r < 0) return r; @@ -1565,20 +1527,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; /* arm T1 timeout */ - r = sd_event_add_time(client->event, - &client->timeout_t1, - clock_boottime_or_monotonic(), - t1_timeout, 10 * USEC_PER_MSEC, - client_timeout_t1, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_t1, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer"); + r = event_reset_time(client->event, &client->timeout_t1, + clock_boottime_or_monotonic(), + t1_timeout, 10 * USEC_PER_MSEC, + client_timeout_t1, client, + client->event_priority, "dhcp4-t1-timer", true); if (r < 0) return r; @@ -1603,26 +1556,14 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i r = client_handle_offer(client, message, len); if (r >= 0) { - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); - client->state = DHCP_STATE_REQUESTING; client->attempt = 1; - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - 0, 0, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp4-resend-timer", true); if (r < 0) goto error; } else if (r == -ENOMSG) @@ -1639,8 +1580,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i r = client_handle_ack(client, message, len); if (r >= 0) { client->start_delay = 0; - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); + (void) event_source_disable(client->timeout_resend); client->receive_message = sd_event_source_unref(client->receive_message); client->fd = asynchronous_close(client->fd); @@ -1680,9 +1620,6 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i } else if (r == -EADDRNOTAVAIL) { /* got a NAK, let's restart the client */ - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); - client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); r = client_initialize(client); @@ -1743,8 +1680,7 @@ static int client_receive_message_udp( sd_dhcp_client *client = userdata; _cleanup_free_ DHCPMessage *message = NULL; - const struct ether_addr zero_mac = {}; - const struct ether_addr *expected_chaddr = NULL; + const uint8_t *expected_chaddr = NULL; uint8_t expected_hlen = 0; ssize_t len, buflen; @@ -1752,6 +1688,12 @@ static int client_receive_message_udp( assert(client); buflen = next_datagram_size_fd(fd); + if (buflen == -ENETDOWN) { + /* the link is down. Don't return an error or the I/O event + source will be disconnected and we won't be able to receive + packets again when the link comes back. */ + return 0; + } if (buflen < 0) return buflen; @@ -1761,7 +1703,8 @@ static int client_receive_message_udp( len = recv(fd, message, buflen, 0); if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + /* see comment above for why we shouldn't error out on ENETDOWN. */ + if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) return 0; return log_dhcp_client_errno(client, errno, @@ -1789,11 +1732,7 @@ static int client_receive_message_udp( if (client->arp_type == ARPHRD_ETHER) { expected_hlen = ETH_ALEN; - expected_chaddr = (const struct ether_addr *) &client->mac_addr; - } else { - /* Non-Ethernet links expect zero chaddr */ - expected_hlen = 0; - expected_chaddr = &zero_mac; + expected_chaddr = &client->mac_addr[0]; } if (message->hlen != expected_hlen) { @@ -1801,7 +1740,7 @@ static int client_receive_message_udp( return 0; } - if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { + if (expected_hlen > 0 && memcmp(&message->chaddr[0], expected_chaddr, expected_hlen)) { log_dhcp_client(client, "Received chaddr does not match expected: ignoring"); return 0; } @@ -1843,6 +1782,8 @@ static int client_receive_message_raw( assert(client); buflen = next_datagram_size_fd(fd); + if (buflen == -ENETDOWN) + return 0; if (buflen < 0) return buflen; @@ -1850,12 +1791,11 @@ static int client_receive_message_raw( if (!packet) return -ENOMEM; - iov.iov_base = packet; - iov.iov_len = buflen; + iov = IOVEC_MAKE(packet, buflen); len = recvmsg(fd, &msg, 0); if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) return 0; return log_dhcp_client_errno(client, errno, @@ -1863,7 +1803,7 @@ static int client_receive_message_raw( } else if ((size_t)len < sizeof(DHCPPacket)) return 0; - CMSG_FOREACH(cmsg, &msg) { + CMSG_FOREACH(cmsg, &msg) if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA && cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { @@ -1872,7 +1812,6 @@ static int client_receive_message_raw( checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); break; } - } r = dhcp_packet_verify_headers(packet, len, checksum, client->port); if (r < 0) @@ -1953,33 +1892,17 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) { return client->event; } -sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref++; - - return client; -} - -sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref--; - - if (client->n_ref > 0) - return NULL; +static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) { + assert(client); log_dhcp_client(client, "FREE"); - client_initialize(client); + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->timeout_t1 = sd_event_source_unref(client->timeout_t1); + client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + client->timeout_expire = sd_event_source_unref(client->timeout_expire); - client->receive_message = sd_event_source_unref(client->receive_message); + client_initialize(client); sd_dhcp_client_detach_event(client); @@ -1992,24 +1915,27 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { return mfree(client); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free); + int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL; assert_return(ret, -EINVAL); - client = new0(sd_dhcp_client, 1); + client = new(sd_dhcp_client, 1); if (!client) return -ENOMEM; - client->n_ref = 1; - client->state = DHCP_STATE_INIT; - client->ifindex = -1; - client->fd = -1; - client->attempt = 1; - client->mtu = DHCP_DEFAULT_MIN_SIZE; - client->port = DHCP_PORT_CLIENT; - - client->anonymize = !!anonymize; + *client = (sd_dhcp_client) { + .n_ref = 1, + .state = DHCP_STATE_INIT, + .ifindex = -1, + .fd = -1, + .attempt = 1, + .mtu = DHCP_DEFAULT_MIN_SIZE, + .port = DHCP_PORT_CLIENT, + .anonymize = !!anonymize, + }; /* NOTE: this could be moved to a function. */ if (anonymize) { client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index cac07d3e..39d2a6d0 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -3,10 +3,9 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> -#include <errno.h> #include <stdio.h> #include <stdio_ext.h> #include <stdlib.h> @@ -18,6 +17,7 @@ #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" #include "dns-domain.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "hexdecoct.h" @@ -28,6 +28,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "tmpfile-util.h" #include "unaligned.h" int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { @@ -151,15 +152,15 @@ int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) { return 0; } -int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) { +int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - if (lease->router == 0) + if (lease->router_size <= 0) return -ENODATA; - addr->s_addr = lease->router; - return 0; + *addr = lease->router; + return (int) lease->router_size; } int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) { @@ -248,27 +249,8 @@ int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, s return 0; } -sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref++; - - return lease; -} - -sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref--; - - if (lease->n_ref > 0) - return NULL; +static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { + assert(lease); while (lease->private_options) { struct sd_dhcp_raw_option *option = lease->private_options; @@ -280,6 +262,7 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { } free(lease->root_path); + free(lease->router); free(lease->timezone); free(lease->hostname); free(lease->domainname); @@ -292,6 +275,8 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { return mfree(lease); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_lease, sd_dhcp_lease, dhcp_lease_free); + static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { assert(option); assert(ret); @@ -351,8 +336,7 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { if (!string) return -ENOMEM; - free(*ret); - *ret = string; + free_and_replace(*ret, string); } return 0; @@ -373,7 +357,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { return 0; } - r = dns_name_normalize(name, &normalized); + r = dns_name_normalize(name, 0, &normalized); if (r < 0) return r; @@ -388,23 +372,6 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { return 0; } -static void filter_bogus_addresses(struct in_addr *addresses, size_t *n) { - size_t i, j; - - /* Silently filter DNS/NTP servers supplied to us that do not make outside of the local scope. */ - - for (i = 0, j = 0; i < *n; i ++) { - - if (in4_addr_is_null(addresses+i) || - in4_addr_is_localhost(addresses+i)) - continue; - - addresses[j++] = addresses[i]; - } - - *n = j; -} - static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { assert(option); assert(ret); @@ -426,8 +393,6 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add if (!addresses) return -ENOMEM; - filter_bogus_addresses(addresses, &n_addresses); - free(*ret); *ret = addresses; *n_ret = n_addresses; @@ -572,11 +537,9 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_ROUTER: - if (len >= 4) { - r = lease_parse_be32(option, 4, &lease->router); - if (r < 0) - log_debug_errno(r, "Failed to parse router address, ignoring: %m"); - } + r = lease_parse_in_addrs(option, len, &lease->router, &lease->router_size); + if (r < 0) + log_debug_errno(r, "Failed to parse router addresses, ignoring: %m"); break; case SD_DHCP_OPTION_DOMAIN_NAME_SERVER: @@ -838,7 +801,6 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { if (!lease) return -ENOMEM; - lease->router = INADDR_ANY; lease->n_ref = 1; *ret = lease; @@ -853,6 +815,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const struct in_addr *addresses; const void *client_id, *data; size_t client_id_len, data_len; + char sbuf[INET_ADDRSTRLEN]; const char *string; uint16_t mtu; _cleanup_free_ sd_dhcp_route **routes = NULL; @@ -875,27 +838,30 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_address(lease, &address); if (r >= 0) - fprintf(f, "ADDRESS=%s\n", inet_ntoa(address)); + fprintf(f, "ADDRESS=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_netmask(lease, &address); if (r >= 0) - fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); + fprintf(f, "NETMASK=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); - r = sd_dhcp_lease_get_router(lease, &address); - if (r >= 0) - fprintf(f, "ROUTER=%s\n", inet_ntoa(address)); + r = sd_dhcp_lease_get_router(lease, &addresses); + if (r > 0) { + fputs("ROUTER=", f); + serialize_in_addrs(f, addresses, r, false, NULL); + fputc('\n', f); + } r = sd_dhcp_lease_get_server_identifier(lease, &address); if (r >= 0) - fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address)); + fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_next_server(lease, &address); if (r >= 0) - fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address)); + fprintf(f, "NEXT_SERVER=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_broadcast(lease, &address); if (r >= 0) - fprintf(f, "BROADCAST=%s\n", inet_ntoa(address)); + fprintf(f, "BROADCAST=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_mtu(lease, &mtu); if (r >= 0) @@ -916,15 +882,15 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_dns(lease, &addresses); if (r > 0) { fputs("DNS=", f); - serialize_in_addrs(f, addresses, r); - fputs("\n", f); + serialize_in_addrs(f, addresses, r, false, NULL); + fputc('\n', f); } r = sd_dhcp_lease_get_ntp(lease, &addresses); if (r > 0) { fputs("NTP=", f); - serialize_in_addrs(f, addresses, r); - fputs("\n", f); + serialize_in_addrs(f, addresses, r, false, NULL); + fputc('\n', f); } r = sd_dhcp_lease_get_domainname(lease, &string); @@ -935,7 +901,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r > 0) { fputs("DOMAIN_SEARCH_LIST=", f); fputstrv(f, search_domains, NULL, NULL); - fputs("\n", f); + fputc('\n', f); } r = sd_dhcp_lease_get_hostname(lease, &string); @@ -1036,7 +1002,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (r < 0) return r; - r = parse_env_file(NULL, lease_file, NEWLINE, + r = parse_env_file(NULL, lease_file, "ADDRESS", &address, "ROUTER", &router, "NETMASK", &netmask, @@ -1087,8 +1053,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "OPTION_251", &options[27], "OPTION_252", &options[28], "OPTION_253", &options[29], - "OPTION_254", &options[30], - NULL); + "OPTION_254", &options[30]); if (r < 0) return r; @@ -1099,9 +1064,11 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } if (router) { - r = inet_pton(AF_INET, router, &lease->router); - if (r <= 0) - log_debug("Failed to parse router %s, ignoring.", router); + r = deserialize_in_addrs(&lease->router, router); + if (r < 0) + log_debug_errno(r, "Failed to deserialize router addresses %s, ignoring: %m", router); + else + lease->router_size = r; } if (netmask) { @@ -1320,3 +1287,9 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) { *gateway = route->gw_addr; return 0; } + +int sd_dhcp_route_get_option(sd_dhcp_route *route) { + assert_return(route, -EINVAL); + + return route->option; +} diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index f0cd2f97..b72cd82d 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -3,9 +3,8 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <string.h> #include <sys/ioctl.h> #include <linux/if_infiniband.h> @@ -18,6 +17,7 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" +#include "event-util.h" #include "fd-util.h" #include "hostname-util.h" #include "in-addr-util.h" @@ -29,6 +29,13 @@ #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN +/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ +enum { + DHCP6_REQUEST_IA_NA = 1, + DHCP6_REQUEST_IA_TA = 2, /* currently not used */ + DHCP6_REQUEST_IA_PD = 4, +}; + struct sd_dhcp6_client { unsigned n_ref; @@ -42,12 +49,15 @@ struct sd_dhcp6_client { uint16_t arp_type; DHCP6IA ia_na; DHCP6IA ia_pd; - bool prefix_delegation; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + unsigned request; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; int fd; bool information_request; + bool iaid_set; be16_t *req_opts; size_t req_opts_allocated; size_t req_opts_len; @@ -212,10 +222,10 @@ static int dhcp6_client_set_duid_internal( #if 0 /* NM_IGNORED */ switch (duid_type) { case DUID_TYPE_LLT: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; - r = dhcp_identifier_set_duid_llt(&client->duid, 0, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); if (r < 0) return r; break; @@ -225,7 +235,7 @@ static int dhcp6_client_set_duid_internal( return r; break; case DUID_TYPE_LL: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); @@ -255,7 +265,6 @@ int sd_dhcp6_client_set_duid( return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0); } -#if 0 /* NM_IGNORED */ int sd_dhcp6_client_set_duid_llt( sd_dhcp6_client *client, usec_t llt_time) { @@ -268,10 +277,10 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { client->ia_na.ia_na.id = htobe32(iaid); client->ia_pd.ia_pd.id = htobe32(iaid); + client->iaid_set = true; return 0; } -#endif /* NM_IGNORED */ int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, @@ -337,10 +346,44 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) return 0; } -int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) { +int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) { + assert_return(client, -EINVAL); + assert_return(delegation, -EINVAL); + + *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD); + + return 0; +} + +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { + assert_return(client, -EINVAL); + + SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation); + + return 0; +} + +int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) { + assert_return(client, -EINVAL); + assert_return(request, -EINVAL); + + *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA); + + return 0; +} + +int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { assert_return(client, -EINVAL); - client->prefix_delegation = delegation; + SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request); + + return 0; +} + +int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { + assert_return(client, -EINVAL); + + client->transaction_id = transaction_id; return 0; } @@ -364,21 +407,10 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) { - assert(client); - - if (client->lease) { - dhcp6_lease_clear_timers(&client->lease->ia); - sd_dhcp6_lease_unref(client->lease); - } - - client->lease = lease; -} - static int client_reset(sd_dhcp6_client *client) { assert(client); - client_set_lease(client, NULL); + client->lease = sd_dhcp6_lease_unref(client->lease); client->receive_message = sd_event_source_unref(client->receive_message); @@ -386,16 +418,13 @@ static int client_reset(sd_dhcp6_client *client) { client->transaction_id = 0; client->transaction_start = 0; - client->ia_na.timeout_t1 = - sd_event_source_unref(client->ia_na.timeout_t1); - client->ia_na.timeout_t2 = - sd_event_source_unref(client->ia_na.timeout_t2); - client->retransmit_time = 0; client->retransmit_count = 0; - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->timeout_resend_expire = - sd_event_source_unref(client->timeout_resend_expire); + + (void) event_source_disable(client->timeout_resend); + (void) event_source_disable(client->timeout_resend_expire); + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); client->state = DHCP6_STATE_STOPPED; @@ -448,9 +477,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na); - if (r < 0) - return r; + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { + r = dhcp6_option_append_ia(&opt, &optlen, + &client->ia_na); + if (r < 0) + return r; + } if (client->fqdn) { r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); @@ -458,7 +490,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } - if (client->prefix_delegation) { + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd); if (r < 0) return r; @@ -483,9 +515,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { + r = dhcp6_option_append_ia(&opt, &optlen, + &client->lease->ia); + if (r < 0) + return r; + } if (client->fqdn) { r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); @@ -493,7 +528,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } - if (client->prefix_delegation) { + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); if (r < 0) return r; @@ -507,9 +542,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { case DHCP6_STATE_REBIND: message->type = DHCP6_REBIND; - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { + r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); + if (r < 0) + return r; + } if (client->fqdn) { r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); @@ -517,7 +554,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } - if (client->prefix_delegation) { + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); if (r < 0) return r; @@ -574,8 +611,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) assert(client); assert(client->lease); - client->lease->ia.timeout_t2 = - sd_event_source_unref(client->lease->ia.timeout_t2); + (void) event_source_disable(client->timeout_t2); log_dhcp6_client(client, "Timeout T2"); @@ -591,8 +627,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) assert(client); assert(client->lease); - client->lease->ia.timeout_t1 = - sd_event_source_unref(client->lease->ia.timeout_t1); + (void) event_source_disable(client->timeout_t1); log_dhcp6_client(client, "Timeout T1"); @@ -640,7 +675,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda assert(client); assert(client->event); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); + (void) event_source_disable(client->timeout_resend); switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: @@ -682,7 +717,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda init_retransmit_time = DHCP6_REB_TIMEOUT; max_retransmit_time = DHCP6_REB_MAX_RT; - if (!client->timeout_resend_expire) { + if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire); if (r < 0) { @@ -731,43 +766,24 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda log_dhcp6_client(client, "Next retransmission in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC)); - r = sd_event_add_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - time_now + client->retransmit_time, - 10 * USEC_PER_MSEC, client_timeout_resend, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer"); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + time_now + client->retransmit_time, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timer", true); if (r < 0) goto error; - if (max_retransmit_duration && !client->timeout_resend_expire) { + if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) { log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", max_retransmit_duration / USEC_PER_SEC); - r = sd_event_add_time(client->event, - &client->timeout_resend_expire, - clock_boottime_or_monotonic(), - time_now + max_retransmit_duration, - USEC_PER_SEC, - client_timeout_resend_expire, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend_expire, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer"); + r = event_reset_time(client->event, &client->timeout_resend_expire, + clock_boottime_or_monotonic(), + time_now + max_retransmit_duration, USEC_PER_SEC, + client_timeout_resend_expire, client, + client->event_priority, "dhcp6-resend-expire-timer", true); if (r < 0) goto error; } @@ -781,19 +797,20 @@ error: static int client_ensure_iaid(sd_dhcp6_client *client) { int r; - be32_t iaid; + uint32_t iaid; assert(client); - if (client->ia_na.ia_na.id) + if (client->iaid_set) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid); + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid); if (r < 0) return r; client->ia_na.ia_na.id = iaid; client->ia_pd.ia_pd.id = iaid; + client->iaid_set = true; return 0; } @@ -803,10 +820,11 @@ static int client_parse_message( DHCP6Message *message, size_t len, sd_dhcp6_lease *lease) { + + uint32_t lt_t1 = ~0, lt_t2 = ~0; + bool clientid = false; size_t pos = 0; int r; - bool clientid = false; - uint32_t lt_t1 = ~0, lt_t2 = ~0; assert(client); assert(message); @@ -816,20 +834,22 @@ static int client_parse_message( len -= sizeof(DHCP6Message); while (pos < len) { - DHCP6Option *option = (DHCP6Option *)&message->options[pos]; + DHCP6Option *option = (DHCP6Option *) &message->options[pos]; uint16_t optcode, optlen; - int status; - uint8_t *optval; be32_t iaid_lease; + uint8_t *optval; + int status; - if (len < pos + offsetof(DHCP6Option, data) || - len < pos + offsetof(DHCP6Option, data) + be16toh(option->len)) + if (len < pos + offsetof(DHCP6Option, data)) return -ENOBUFS; optcode = be16toh(option->code); optlen = be16toh(option->len); optval = option->data; + if (len < pos + offsetof(DHCP6Option, data) + optlen) + return -ENOBUFS; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) { @@ -927,7 +947,7 @@ static int client_parse_message( if (r < 0 && r != -ENOMSG) return r; - r = dhcp6_lease_get_iaid(lease, &iaid_lease); + r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -980,7 +1000,7 @@ static int client_parse_message( break; } - pos += sizeof(*option) + optlen; + pos += offsetof(DHCP6Option, data) + optlen; } if (!clientid) { @@ -1040,8 +1060,8 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si return 0; } - client_set_lease(client, lease); - lease = NULL; + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); return DHCP6_STATE_BOUND; } @@ -1069,8 +1089,8 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver r = dhcp6_lease_get_preference(client->lease, &pref_lease); if (r < 0 || pref_advertise > pref_lease) { - client_set_lease(client, lease); - lease = NULL; + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); r = 0; } @@ -1097,6 +1117,12 @@ static int client_receive_message( assert(client->event); buflen = next_datagram_size_fd(fd); + if (buflen == -ENETDOWN) { + /* the link is down. Don't return an error or the I/O event + source will be disconnected and we won't be able to receive + packets again when the link comes back. */ + return 0; + } if (buflen < 0) return buflen; @@ -1106,7 +1132,8 @@ static int client_receive_message( len = recv(fd, message, buflen, 0); if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + /* see comment above for why we shouldn't error out on ENETDOWN. */ + if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) return 0; return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m"); @@ -1201,19 +1228,41 @@ static int client_receive_message( return 0; } +static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, + uint32_t *lifetime_t2) { + assert_return(client, -EINVAL); + assert_return(client->lease, -EINVAL); + + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { + *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2); + + return 0; + } + + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { + *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2); + + return 0; + } + + return -ENOMSG; +} + static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { int r; usec_t timeout, time_now; char time_string[FORMAT_TIMESPAN_MAX]; + uint32_t lifetime_t1, lifetime_t2; assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); assert_return(client->state != state, -EINVAL); - client->timeout_resend_expire = - sd_event_source_unref(client->timeout_resend_expire); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); + (void) event_source_disable(client->timeout_resend_expire); + (void) event_source_disable(client->timeout_resend); client->retransmit_time = 0; client->retransmit_count = 0; @@ -1264,59 +1313,40 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { case DHCP6_STATE_BOUND: - if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff || - client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) { + r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2); + if (r < 0) + goto error; + if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) { log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - be32toh(client->lease->ia.ia_na.lifetime_t1), - be32toh(client->lease->ia.ia_na.lifetime_t2)); + lifetime_t1, lifetime_t2); return 0; } - timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC); + timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC); log_dhcp6_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); - client->lease->ia.timeout_t1 = sd_event_source_unref(client->lease->ia.timeout_t1); - r = sd_event_add_time(client->event, - &client->lease->ia.timeout_t1, - clock_boottime_or_monotonic(), time_now + timeout, - 10 * USEC_PER_SEC, client_timeout_t1, - client); + r = event_reset_time(client->event, &client->timeout_t1, + clock_boottime_or_monotonic(), + time_now + timeout, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); if (r < 0) goto error; - r = sd_event_source_set_priority(client->lease->ia.timeout_t1, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout"); - if (r < 0) - goto error; - - timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC); + timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC); log_dhcp6_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); - client->lease->ia.timeout_t2 = sd_event_source_unref(client->lease->ia.timeout_t2); - r = sd_event_add_time(client->event, - &client->lease->ia.timeout_t2, - clock_boottime_or_monotonic(), time_now + timeout, - 10 * USEC_PER_SEC, client_timeout_t2, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->lease->ia.timeout_t2, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout"); + r = event_reset_time(client->event, &client->timeout_t2, + clock_boottime_or_monotonic(), + time_now + timeout, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); if (r < 0) goto error; @@ -1328,18 +1358,11 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { client->transaction_id = random_u32() & htobe32(0x00ffffff); client->transaction_start = time_now; - r = sd_event_add_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout"); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timeout", true); if (r < 0) goto error; @@ -1378,6 +1401,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) return -EBUSY; + if (!client->information_request && !client->request) + return -EINVAL; + r = client_reset(client); if (r < 0) return r; @@ -1446,27 +1472,13 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { return client->event; } -sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref++; - - return client; -} - -sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref--; +static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { + assert(client); - if (client->n_ref > 0) - return NULL; + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire); + client->timeout_t1 = sd_event_source_unref(client->timeout_t1); + client->timeout_t2 = sd_event_source_unref(client->timeout_t2); client_reset(client); @@ -1479,29 +1491,36 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { return mfree(client); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free); + int sd_dhcp6_client_new(sd_dhcp6_client **ret) { _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + _cleanup_free_ be16_t *req_opts = NULL; size_t t; assert_return(ret, -EINVAL); - client = new0(sd_dhcp6_client, 1); - if (!client) + req_opts = new(be16_t, ELEMENTSOF(default_req_opts)); + if (!req_opts) return -ENOMEM; - client->n_ref = 1; - client->ia_na.type = SD_DHCP6_OPTION_IA_NA; - client->ia_pd.type = SD_DHCP6_OPTION_IA_PD; - client->ifindex = -1; - client->fd = -1; + for (t = 0; t < ELEMENTSOF(default_req_opts); t++) + req_opts[t] = htobe16(default_req_opts[t]); - client->req_opts_len = ELEMENTSOF(default_req_opts); - client->req_opts = new0(be16_t, client->req_opts_len); - if (!client->req_opts) + client = new(sd_dhcp6_client, 1); + if (!client) return -ENOMEM; - for (t = 0; t < client->req_opts_len; t++) - client->req_opts[t] = htobe16(default_req_opts[t]); + *client = (sd_dhcp6_client) { + .n_ref = 1, + .ia_na.type = SD_DHCP6_OPTION_IA_NA, + .ia_pd.type = SD_DHCP6_OPTION_IA_PD, + .ifindex = -1, + .request = DHCP6_REQUEST_IA_NA, + .fd = -1, + .req_opts_len = ELEMENTSOF(default_req_opts), + .req_opts = TAKE_PTR(req_opts), + }; *ret = TAKE_PTR(client); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index cca0b500..7263c96f 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -3,9 +3,8 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include "alloc-util.h" #include "dhcp6-lease-internal.h" @@ -13,15 +12,6 @@ #include "strv.h" #include "util.h" -int dhcp6_lease_clear_timers(DHCP6IA *ia) { - assert_return(ia, -EINVAL); - - ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1); - ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2); - - return 0; -} - int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { DHCP6Address *addr; uint32_t valid = 0, t; @@ -50,8 +40,6 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { if (!ia) return NULL; - dhcp6_lease_clear_timers(ia); - while (ia->addresses) { address = ia->addresses; @@ -65,15 +53,16 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { + uint8_t *serverid; + assert_return(lease, -EINVAL); assert_return(id, -EINVAL); - free(lease->serverid); - - lease->serverid = memdup(id, len); - if (!lease->serverid) - return -EINVAL; + serverid = memdup(id, len); + if (!serverid) + return -ENOMEM; + free_and_replace(lease->serverid, serverid); lease->serverid_len = len; return 0; @@ -138,6 +127,15 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { return 0; } +int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { + assert_return(lease, -EINVAL); + assert_return(iaid, -EINVAL); + + *iaid = lease->pd.ia_pd.id; + + return 0; +} + int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, uint32_t *lifetime_preferred, uint32_t *lifetime_valid) { @@ -376,27 +374,8 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { return -ENOENT; } -sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref++; - - return lease; -} - -sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref--; - - if (lease->n_ref > 0) - return NULL; +static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { + assert(lease); free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); @@ -412,6 +391,8 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { return mfree(lease); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free); + int dhcp6_lease_new(sd_dhcp6_lease **ret) { sd_dhcp6_lease *lease; diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index a39a865a..6de4adb5 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -3,10 +3,9 @@ Copyright © 2014 Axis Communications AB. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> -#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -16,6 +15,7 @@ #include "alloc-util.h" #include "arp-util.h" #include "ether-addr-util.h" +#include "event-util.h" #include "fd-util.h" #include "in-addr-util.h" #include "list.h" @@ -91,7 +91,7 @@ static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_count static void ipv4acd_reset(sd_ipv4acd *acd) { assert(acd); - acd->timer_event_source = sd_event_source_unref(acd->timer_event_source); + (void) event_source_disable(acd->timer_event_source); acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source); acd->fd = safe_close(acd->fd); @@ -99,25 +99,10 @@ static void ipv4acd_reset(sd_ipv4acd *acd) { ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true); } -sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) { - if (!acd) - return NULL; - - assert_se(acd->n_ref >= 1); - acd->n_ref++; - - return acd; -} - -sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) { - if (!acd) - return NULL; - - assert_se(acd->n_ref >= 1); - acd->n_ref--; +static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) { + assert(acd); - if (acd->n_ref > 0) - return NULL; + acd->timer_event_source = sd_event_source_unref(acd->timer_event_source); ipv4acd_reset(acd); sd_ipv4acd_detach_event(acd); @@ -125,19 +110,23 @@ sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) { return mfree(acd); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free); + int sd_ipv4acd_new(sd_ipv4acd **ret) { _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL; assert_return(ret, -EINVAL); - acd = new0(sd_ipv4acd, 1); + acd = new(sd_ipv4acd, 1); if (!acd) return -ENOMEM; - acd->n_ref = 1; - acd->state = IPV4ACD_STATE_INIT; - acd->ifindex = -1; - acd->fd = -1; + *acd = (sd_ipv4acd) { + .n_ref = 1, + .state = IPV4ACD_STATE_INIT, + .ifindex = -1, + .fd = -1, + }; *ret = TAKE_PTR(acd); @@ -168,9 +157,7 @@ int sd_ipv4acd_stop(sd_ipv4acd *acd) { static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata); static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) { - _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL; usec_t next_timeout, time_now; - int r; assert(acd); @@ -181,20 +168,11 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0); - r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd); - if (r < 0) - return r; - - r = sd_event_source_set_priority(timer, acd->event_priority); - if (r < 0) - return r; - - (void) sd_event_source_set_description(timer, "ipv4acd-timer"); - - sd_event_source_unref(acd->timer_event_source); - acd->timer_event_source = TAKE_PTR(timer); - - return 0; + return event_reset_time(acd->event, &acd->timer_event_source, + clock_boottime_or_monotonic(), + time_now + next_timeout, 0, + ipv4acd_on_timeout, acd, + acd->event_priority, "ipv4acd-timer", true); } static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index 69fa60e2..8b1e9665 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -3,10 +3,9 @@ Copyright © 2014 Axis Communications AB. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> -#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -57,30 +56,15 @@ struct sd_ipv4ll { static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); -sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref++; - - return ll; -} - -sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref--; - - if (ll->n_ref > 0) - return NULL; +static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) { + assert(ll); sd_ipv4acd_unref(ll->acd); return mfree(ll); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll, sd_ipv4ll, ipv4ll_free); + int sd_ipv4ll_new(sd_ipv4ll **ret) { _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL; int r; diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 74ba8236..741128e1 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -1,39 +1,50 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> #include <linux/sockios.h> +#include <sys/ioctl.h> #include "sd-lldp.h" #include "alloc-util.h" +#include "ether-addr-util.h" +#include "event-util.h" #include "fd-util.h" #include "lldp-internal.h" #include "lldp-neighbor.h" #include "lldp-network.h" #include "socket-util.h" -#include "ether-addr-util.h" +#include "string-table.h" #define LLDP_DEFAULT_NEIGHBORS_MAX 128U -static void lldp_flush_neighbors(sd_lldp *lldp) { - sd_lldp_neighbor *n; +static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = { + [SD_LLDP_EVENT_ADDED] = "added", + [SD_LLDP_EVENT_REMOVED] = "removed", + [SD_LLDP_EVENT_UPDATED] = "updated", + [SD_LLDP_EVENT_REFRESHED] = "refreshed", +}; + +DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event); +static void lldp_flush_neighbors(sd_lldp *lldp) { assert(lldp); - while ((n = hashmap_first(lldp->neighbor_by_id))) - lldp_neighbor_unlink(n); + hashmap_clear(lldp->neighbor_by_id); } static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { assert(lldp); + assert(event >= 0 && event < _SD_LLDP_EVENT_MAX); - log_lldp("Invoking callback for '%c'.", event); - - if (!lldp->callback) + if (!lldp->callback) { + log_lldp("Received '%s' event.", lldp_event_to_string(event)); return; + } + log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event)); lldp->callback(lldp, event, n, lldp->userdata); } @@ -121,7 +132,7 @@ static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { } if (lldp_neighbor_equal(n, old)) { - /* Is this equal, then restart the TTL counter, but don't do anyting else. */ + /* Is this equal, then restart the TTL counter, but don't do anything else. */ old->timestamp = n->timestamp; lldp_start_timer(lldp, old); lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); @@ -225,7 +236,7 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v static void lldp_reset(sd_lldp *lldp) { assert(lldp); - lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); + (void) event_source_disable(lldp->timer_event_source); lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); lldp->fd = safe_close(lldp->fd); } @@ -331,27 +342,10 @@ _public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) { return 0; } -_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) { - - if (!lldp) - return NULL; - - assert(lldp->n_ref > 0); - lldp->n_ref++; - - return lldp; -} - -_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) { - - if (!lldp) - return NULL; - - assert(lldp->n_ref > 0); - lldp->n_ref --; +static sd_lldp* lldp_free(sd_lldp *lldp) { + assert(lldp); - if (lldp->n_ref > 0) - return NULL; + lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); lldp_reset(lldp); sd_lldp_detach_event(lldp); @@ -362,22 +356,26 @@ _public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) { return mfree(lldp); } +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free); + _public_ int sd_lldp_new(sd_lldp **ret) { _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; int r; assert_return(ret, -EINVAL); - lldp = new0(sd_lldp, 1); + lldp = new(sd_lldp, 1); if (!lldp) return -ENOMEM; - lldp->n_ref = 1; - lldp->fd = -1; - lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX; - lldp->capability_mask = (uint16_t) -1; + *lldp = (sd_lldp) { + .n_ref = 1, + .fd = -1, + .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, + .capability_mask = (uint16_t) -1, + }; - lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops); + lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); if (!lldp->neighbor_by_id) return -ENOMEM; @@ -390,10 +388,8 @@ _public_ int sd_lldp_new(sd_lldp **ret) { return 0; } -static int neighbor_compare_func(const void *a, const void *b) { - const sd_lldp_neighbor * const*x = a, * const *y = b; - - return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id); +static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) { + return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); } static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { @@ -413,7 +409,6 @@ static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { sd_lldp_neighbor *n; - int r; assert(lldp); @@ -421,35 +416,17 @@ static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { lldp_neighbor_start_ttl(neighbor); n = prioq_peek(lldp->neighbor_by_expiry); - if (!n) { - - if (lldp->timer_event_source) - return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF); - - return 0; - } - - if (lldp->timer_event_source) { - r = sd_event_source_set_time(lldp->timer_event_source, n->until); - if (r < 0) - return r; - - return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT); - } + if (!n) + return event_source_disable(lldp->timer_event_source); if (!lldp->event) return 0; - r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp); - if (r < 0) - return r; - - r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority); - if (r < 0) - return r; - - (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer"); - return 0; + return event_reset_time(lldp->event, &lldp->timer_event_source, + clock_boottime_or_monotonic(), + n->until, 0, + on_timer_event, lldp, + lldp->event_priority, "lldp-timer", true); } _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { @@ -481,7 +458,7 @@ _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); /* Return things in a stable order */ - qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func); + typesafe_qsort(l, k, neighbor_compare_func); *ret = l; return k; diff --git a/src/systemd/src/libsystemd/sd-event/event-source.h b/src/systemd/src/libsystemd/sd-event/event-source.h new file mode 100644 index 00000000..99ab8fc1 --- /dev/null +++ b/src/systemd/src/libsystemd/sd-event/event-source.h @@ -0,0 +1,206 @@ +#pragma once +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <sys/wait.h> + +#include "sd-event.h" + +#include "fs-util.h" +#include "hashmap.h" +#include "list.h" +#include "prioq.h" + +typedef enum EventSourceType { + SOURCE_IO, + SOURCE_TIME_REALTIME, + SOURCE_TIME_BOOTTIME, + SOURCE_TIME_MONOTONIC, + SOURCE_TIME_REALTIME_ALARM, + SOURCE_TIME_BOOTTIME_ALARM, + SOURCE_SIGNAL, + SOURCE_CHILD, + SOURCE_DEFER, + SOURCE_POST, + SOURCE_EXIT, + SOURCE_WATCHDOG, + SOURCE_INOTIFY, + _SOURCE_EVENT_SOURCE_TYPE_MAX, + _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 +} EventSourceType; + +/* All objects we use in epoll events start with this value, so that + * we know how to dispatch it */ +typedef enum WakeupType { + WAKEUP_NONE, + WAKEUP_EVENT_SOURCE, + WAKEUP_CLOCK_DATA, + WAKEUP_SIGNAL_DATA, + WAKEUP_INOTIFY_DATA, + _WAKEUP_TYPE_MAX, + _WAKEUP_TYPE_INVALID = -1, +} WakeupType; + +struct inode_data; + +struct sd_event_source { + WakeupType wakeup; + + unsigned n_ref; + + sd_event *event; + void *userdata; + sd_event_handler_t prepare; + + char *description; + + EventSourceType type:5; + signed int enabled:3; + bool pending:1; + bool dispatching:1; + bool floating:1; + + int64_t priority; + unsigned pending_index; + unsigned prepare_index; + uint64_t pending_iteration; + uint64_t prepare_iteration; + + sd_event_destroy_t destroy_callback; + + LIST_FIELDS(sd_event_source, sources); + + union { + struct { + sd_event_io_handler_t callback; + int fd; + uint32_t events; + uint32_t revents; + bool registered:1; + bool owned:1; + } io; + struct { + sd_event_time_handler_t callback; + usec_t next, accuracy; + unsigned earliest_index; + unsigned latest_index; + } time; + struct { + sd_event_signal_handler_t callback; + struct signalfd_siginfo siginfo; + int sig; + } signal; + struct { + sd_event_child_handler_t callback; + siginfo_t siginfo; + pid_t pid; + int options; + } child; + struct { + sd_event_handler_t callback; + } defer; + struct { + sd_event_handler_t callback; + } post; + struct { + sd_event_handler_t callback; + unsigned prioq_index; + } exit; + struct { + sd_event_inotify_handler_t callback; + uint32_t mask; + struct inode_data *inode_data; + LIST_FIELDS(sd_event_source, by_inode_data); + } inotify; + }; +}; + +struct clock_data { + WakeupType wakeup; + int fd; + + /* For all clocks we maintain two priority queues each, one + * ordered for the earliest times the events may be + * dispatched, and one ordered by the latest times they must + * have been dispatched. The range between the top entries in + * the two prioqs is the time window we can freely schedule + * wakeups in */ + + Prioq *earliest; + Prioq *latest; + usec_t next; + + bool needs_rearm:1; +}; + +struct signal_data { + WakeupType wakeup; + + /* For each priority we maintain one signal fd, so that we + * only have to dequeue a single event per priority at a + * time. */ + + int fd; + int64_t priority; + sigset_t sigset; + sd_event_source *current; +}; + +/* A structure listing all event sources currently watching a specific inode */ +struct inode_data { + /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */ + ino_t ino; + dev_t dev; + + /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can + * rearrange the priority still until then, as we need the original inode to change the priority as we need to + * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the + * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of + * the sd-event object, so that it is efficient to close everything, before entering the next event loop + * iteration. */ + int fd; + + /* The inotify "watch descriptor" */ + int wd; + + /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has + * most recently been set on the watch descriptor. */ + uint32_t combined_mask; + + /* All event sources subscribed to this inode */ + LIST_HEAD(sd_event_source, event_sources); + + /* The inotify object we watch this inode with */ + struct inotify_data *inotify_data; + + /* A linked list of all inode data objects with fds to close (see above) */ + LIST_FIELDS(struct inode_data, to_close); +}; + +/* A structure encapsulating an inotify fd */ +struct inotify_data { + WakeupType wakeup; + + /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at + * a time */ + + int fd; + int64_t priority; + + Hashmap *inodes; /* The inode_data structures keyed by dev+ino */ + Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */ + + /* The buffer we read inotify events into */ + union inotify_event_buffer buffer; + size_t buffer_filled; /* fill level of the buffer */ + + /* How many event sources are currently marked pending for this inotify. We won't read new events off the + * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing + * the events locally if they can't be coalesced). */ + unsigned n_pending; + + /* A linked list of all inotify objects with data already read, that still need processing. We keep this list + * to make it efficient to figure out what inotify objects to process data on next. */ + LIST_FIELDS(struct inotify_data, buffered); +}; diff --git a/src/systemd/src/libsystemd/sd-event/event-util.c b/src/systemd/src/libsystemd/sd-event/event-util.c new file mode 100644 index 00000000..2afacfe6 --- /dev/null +++ b/src/systemd/src/libsystemd/sd-event/event-util.c @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "nm-sd-adapt-core.h" + + +#include "event-source.h" +#include "event-util.h" +#include "log.h" +#include "string-util.h" + +int event_reset_time( + sd_event *e, + sd_event_source **s, + clockid_t clock, + uint64_t usec, + uint64_t accuracy, + sd_event_time_handler_t callback, + void *userdata, + int64_t priority, + const char *description, + bool force_reset) { + + bool created = false; + int enabled, r; + clockid_t c; + + assert(e); + assert(s); + + if (*s) { + if (!force_reset) { + r = sd_event_source_get_enabled(*s, &enabled); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to query whether event source \"%s\" is enabled or not: %m", + strna((*s)->description ?: description)); + + if (enabled != SD_EVENT_OFF) + return 0; + } + + r = sd_event_source_get_time_clock(*s, &c); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to get clock id of event source \"%s\": %m", strna((*s)->description ?: description)); + + if (c != clock) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "sd-event: Current clock id %i of event source \"%s\" is different from specified one %i.", + (int)c, + strna((*s)->description ? : description), + (int)clock); + + r = sd_event_source_set_time(*s, usec); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set time for event source \"%s\": %m", strna((*s)->description ?: description)); + + r = sd_event_source_set_time_accuracy(*s, accuracy); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set accuracy for event source \"%s\": %m", strna((*s)->description ?: description)); + + /* callback function is not updated, as we do not have sd_event_source_set_time_callback(). */ + + (void) sd_event_source_set_userdata(*s, userdata); + + r = sd_event_source_set_enabled(*s, SD_EVENT_ONESHOT); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to enable event source \"%s\": %m", strna((*s)->description ?: description)); + } else { + r = sd_event_add_time(e, s, clock, usec, accuracy, callback, userdata); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to create timer event \"%s\": %m", strna(description)); + + created = true; + } + + r = sd_event_source_set_priority(*s, priority); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set priority for event source \"%s\": %m", strna((*s)->description ?: description)); + + if (description) { + r = sd_event_source_set_description(*s, description); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set description for event source \"%s\": %m", description); + } + + return created; +} + +int event_source_disable(sd_event_source *s) { + if (!s) + return 0; + + return sd_event_source_set_enabled(s, SD_EVENT_OFF); +} + +int event_source_is_enabled(sd_event_source *s) { + if (!s) + return false; + + return sd_event_source_get_enabled(s, NULL); +} diff --git a/src/systemd/src/libsystemd/sd-event/event-util.h b/src/systemd/src/libsystemd/sd-event/event-util.h new file mode 100644 index 00000000..00180955 --- /dev/null +++ b/src/systemd/src/libsystemd/sd-event/event-util.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdbool.h> + +#include "sd-event.h" + +int event_reset_time(sd_event *e, sd_event_source **s, + clockid_t clock, uint64_t usec, uint64_t accuracy, + sd_event_time_handler_t callback, void *userdata, + int64_t priority, const char *description, bool force_reset); +int event_source_disable(sd_event_source *s); +int event_source_is_enabled(sd_event_source *s); diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 2af3572d..6f77421b 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <sys/epoll.h> #include <sys/timerfd.h> @@ -11,6 +11,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "event-source.h" #include "fd-util.h" #include "fs-util.h" #include "hashmap.h" @@ -28,24 +29,6 @@ #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) -typedef enum EventSourceType { - SOURCE_IO, - SOURCE_TIME_REALTIME, - SOURCE_TIME_BOOTTIME, - SOURCE_TIME_MONOTONIC, - SOURCE_TIME_REALTIME_ALARM, - SOURCE_TIME_BOOTTIME_ALARM, - SOURCE_SIGNAL, - SOURCE_CHILD, - SOURCE_DEFER, - SOURCE_POST, - SOURCE_EXIT, - SOURCE_WATCHDOG, - SOURCE_INOTIFY, - _SOURCE_EVENT_SOURCE_TYPE_MAX, - _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 -} EventSourceType; - static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { [SOURCE_IO] = "io", [SOURCE_TIME_REALTIME] = "realtime", @@ -64,183 +47,8 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); -/* All objects we use in epoll events start with this value, so that - * we know how to dispatch it */ -typedef enum WakeupType { - WAKEUP_NONE, - WAKEUP_EVENT_SOURCE, - WAKEUP_CLOCK_DATA, - WAKEUP_SIGNAL_DATA, - WAKEUP_INOTIFY_DATA, - _WAKEUP_TYPE_MAX, - _WAKEUP_TYPE_INVALID = -1, -} WakeupType; - #define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) -struct inode_data; - -struct sd_event_source { - WakeupType wakeup; - - unsigned n_ref; - - sd_event *event; - void *userdata; - sd_event_handler_t prepare; - - char *description; - - EventSourceType type:5; - signed int enabled:3; - bool pending:1; - bool dispatching:1; - bool floating:1; - - int64_t priority; - unsigned pending_index; - unsigned prepare_index; - uint64_t pending_iteration; - uint64_t prepare_iteration; - - sd_event_destroy_t destroy_callback; - - LIST_FIELDS(sd_event_source, sources); - - union { - struct { - sd_event_io_handler_t callback; - int fd; - uint32_t events; - uint32_t revents; - bool registered:1; - bool owned:1; - } io; - struct { - sd_event_time_handler_t callback; - usec_t next, accuracy; - unsigned earliest_index; - unsigned latest_index; - } time; - struct { - sd_event_signal_handler_t callback; - struct signalfd_siginfo siginfo; - int sig; - } signal; - struct { - sd_event_child_handler_t callback; - siginfo_t siginfo; - pid_t pid; - int options; - } child; - struct { - sd_event_handler_t callback; - } defer; - struct { - sd_event_handler_t callback; - } post; - struct { - sd_event_handler_t callback; - unsigned prioq_index; - } exit; - struct { - sd_event_inotify_handler_t callback; - uint32_t mask; - struct inode_data *inode_data; - LIST_FIELDS(sd_event_source, by_inode_data); - } inotify; - }; -}; - -struct clock_data { - WakeupType wakeup; - int fd; - - /* For all clocks we maintain two priority queues each, one - * ordered for the earliest times the events may be - * dispatched, and one ordered by the latest times they must - * have been dispatched. The range between the top entries in - * the two prioqs is the time window we can freely schedule - * wakeups in */ - - Prioq *earliest; - Prioq *latest; - usec_t next; - - bool needs_rearm:1; -}; - -struct signal_data { - WakeupType wakeup; - - /* For each priority we maintain one signal fd, so that we - * only have to dequeue a single event per priority at a - * time. */ - - int fd; - int64_t priority; - sigset_t sigset; - sd_event_source *current; -}; - -/* A structure listing all event sources currently watching a specific inode */ -struct inode_data { - /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */ - ino_t ino; - dev_t dev; - - /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can - * rearrange the priority still until then, as we need the original inode to change the priority as we need to - * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the - * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of - * the sd-event object, so that it is efficient to close everything, before entering the next event loop - * iteration. */ - int fd; - - /* The inotify "watch descriptor" */ - int wd; - - /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has - * most recently been set on the watch descriptor. */ - uint32_t combined_mask; - - /* All event sources subscribed to this inode */ - LIST_HEAD(sd_event_source, event_sources); - - /* The inotify object we watch this inode with */ - struct inotify_data *inotify_data; - - /* A linked list of all inode data objects with fds to close (see above) */ - LIST_FIELDS(struct inode_data, to_close); -}; - -/* A structure encapsulating an inotify fd */ -struct inotify_data { - WakeupType wakeup; - - /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at - * a time */ - - int fd; - int64_t priority; - - Hashmap *inodes; /* The inode_data structures keyed by dev+ino */ - Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */ - - /* The buffer we read inotify events into */ - union inotify_event_buffer buffer; - size_t buffer_filled; /* fill level of the buffer */ - - /* How many event sources are currently marked pending for this inotify. We won't read new events off the - * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing - * the events locally if they can't be coalesced). */ - unsigned n_pending; - - /* A linked list of all inotify objects with data already read, that still need processing. We keep this list - * to make it efficient to figure out what inotify objects to process data on next. */ - LIST_FIELDS(struct inotify_data, buffered); -}; - struct sd_event { unsigned n_ref; @@ -316,6 +124,7 @@ static sd_event *event_resolve(sd_event *e) { static int pending_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->pending); assert(y->pending); @@ -327,22 +136,17 @@ static int pending_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; + r = CMP(x->priority, y->priority); + if (r != 0) + return r; /* Older entries first */ - if (x->pending_iteration < y->pending_iteration) - return -1; - if (x->pending_iteration > y->pending_iteration) - return 1; - - return 0; + return CMP(x->pending_iteration, y->pending_iteration); } static int prepare_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->prepare); assert(y->prepare); @@ -356,18 +160,12 @@ static int prepare_prioq_compare(const void *a, const void *b) { /* Move most recently prepared ones last, so that we can stop * preparing as soon as we hit one that has already been * prepared in the current iteration */ - if (x->prepare_iteration < y->prepare_iteration) - return -1; - if (x->prepare_iteration > y->prepare_iteration) - return 1; + r = CMP(x->prepare_iteration, y->prepare_iteration); + if (r != 0) + return r; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static int earliest_time_prioq_compare(const void *a, const void *b) { @@ -389,12 +187,7 @@ static int earliest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (x->time.next < y->time.next) - return -1; - if (x->time.next > y->time.next) - return 1; - - return 0; + return CMP(x->time.next, y->time.next); } static usec_t time_event_source_latest(const sd_event_source *s) { @@ -420,12 +213,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (time_event_source_latest(x) < time_event_source_latest(y)) - return -1; - if (time_event_source_latest(x) > time_event_source_latest(y)) - return 1; - - return 0; + return CMP(time_event_source_latest(x), time_event_source_latest(y)); } static int exit_prioq_compare(const void *a, const void *b) { @@ -441,12 +229,7 @@ static int exit_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static void free_clock_data(struct clock_data *d) { @@ -458,7 +241,7 @@ static void free_clock_data(struct clock_data *d) { prioq_free(d->latest); } -static void event_free(sd_event *e) { +static sd_event *event_free(sd_event *e) { sd_event_source *s; assert(e); @@ -494,7 +277,8 @@ static void event_free(sd_event *e) { hashmap_free(e->child_sources); set_free(e->post_sources); - free(e); + + return mfree(e); } _public_ int sd_event_new(sd_event** ret) { @@ -555,30 +339,7 @@ fail: return r; } -_public_ sd_event* sd_event_ref(sd_event *e) { - - if (!e) - return NULL; - - assert(e->n_ref >= 1); - e->n_ref++; - - return e; -} - -_public_ sd_event* sd_event_unref(sd_event *e) { - - if (!e) - return NULL; - - assert(e->n_ref >= 1); - e->n_ref--; - - if (e->n_ref <= 0) - event_free(e); - - return NULL; -} +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free); static bool event_pid_changed(sd_event *e) { assert(e); @@ -711,6 +472,17 @@ static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) { } } +static void event_free_signal_data(sd_event *e, struct signal_data *d) { + assert(e); + + if (!d) + return; + + hashmap_remove(e->signal_data, &d->priority); + safe_close(d->fd); + free(d); +} + static int event_make_signal_data( sd_event *e, int sig, @@ -800,11 +572,8 @@ static int event_make_signal_data( return 0; fail: - if (added) { - d->fd = safe_close(d->fd); - hashmap_remove(e->signal_data, &d->priority); - free(d); - } + if (added) + event_free_signal_data(e, d); return r; } @@ -823,11 +592,8 @@ static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig assert_se(sigdelset(&d->sigset, sig) >= 0); if (sigisemptyset(&d->sigset)) { - /* If all the mask is all-zero we can get rid of the structure */ - hashmap_remove(e->signal_data, &d->priority); - safe_close(d->fd); - free(d); + event_free_signal_data(e, d); return; } @@ -974,7 +740,7 @@ static void source_disconnect(sd_event_source *s) { * continued to being watched. That's because inotify doesn't really have an API for that: we * can only change watch masks with access to the original inode either by fd or by path. But * paths aren't stable, and keeping an O_PATH fd open all the time would mean wasting an fd - * continously and keeping the mount busy which we can't really do. We could reconstruct the + * continuously and keeping the mount busy which we can't really do. We could reconstruct the * original inode from /proc/self/fdinfo/$INOTIFY_FD (as all watch descriptors are listed * there), but given the need for open_by_handle_at() which is privileged and not universally * available this would be quite an incomplete solution. Hence we go the other way, leave the @@ -1023,6 +789,7 @@ static void source_free(sd_event_source *s) { free(s->description); free(s); } +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, source_free); static int source_set_pending(sd_event_source *s, bool b) { int r; @@ -1116,7 +883,7 @@ _public_ int sd_event_add_io( sd_event_io_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1139,13 +906,12 @@ _public_ int sd_event_add_io( s->enabled = SD_EVENT_ON; r = source_io_register(s, s->enabled, events); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1154,7 +920,7 @@ static void initialize_perturb(sd_event *e) { sd_id128_t bootid = {}; /* When we sleep for longer, we try to realign the wakeup to - the same time wihtin each minute/second/250ms, so that + the same time within each minute/second/250ms, so that events all across the system can be coalesced into a single CPU wakeup. However, let's take some system-specific randomness for this value, so that in a network of systems @@ -1220,7 +986,7 @@ _public_ int sd_event_add_time( void *userdata) { EventSourceType type; - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; struct clock_data *d; int r; @@ -1272,20 +1038,17 @@ _public_ int sd_event_add_time( r = prioq_put(d->earliest, s, &s->time.earliest_index); if (r < 0) - goto fail; + return r; r = prioq_put(d->latest, s, &s->time.latest_index); if (r < 0) - goto fail; + return r; if (ret) *ret = s; + TAKE_PTR(s); return 0; - -fail: - source_free(s); - return r; } #if 0 /* NM_IGNORED */ @@ -1302,7 +1065,7 @@ _public_ int sd_event_add_signal( sd_event_signal_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; struct signal_data *d; sigset_t ss; int r; @@ -1342,16 +1105,15 @@ _public_ int sd_event_add_signal( e->signal_sources[sig] = s; r = event_make_signal_data(e, sig, &d); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } /* Use the signal name as description for the event source by default */ (void) sd_event_source_set_description(s, signal_to_string(sig)); if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1365,7 +1127,7 @@ _public_ int sd_event_add_child( sd_event_child_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1395,17 +1157,14 @@ _public_ int sd_event_add_child( s->enabled = SD_EVENT_ONESHOT; r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } e->n_enabled_child_sources++; r = event_make_signal_data(e, SIGCHLD, NULL); if (r < 0) { e->n_enabled_child_sources--; - source_free(s); return r; } @@ -1413,6 +1172,7 @@ _public_ int sd_event_add_child( if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1423,7 +1183,7 @@ _public_ int sd_event_add_defer( sd_event_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1441,13 +1201,12 @@ _public_ int sd_event_add_defer( s->enabled = SD_EVENT_ONESHOT; r = source_set_pending(s, true); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1458,7 +1217,7 @@ _public_ int sd_event_add_post( sd_event_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1480,13 +1239,12 @@ _public_ int sd_event_add_post( s->enabled = SD_EVENT_ON; r = set_put(e->post_sources, s); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1497,7 +1255,7 @@ _public_ int sd_event_add_exit( sd_event_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1520,13 +1278,12 @@ _public_ int sd_event_add_exit( s->enabled = SD_EVENT_ONESHOT; r = prioq_put(s->event->exit, s, &s->exit.prioq_index); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1623,38 +1380,27 @@ static int event_make_inotify_data( return 1; } -static int inode_data_compare(const void *a, const void *b) { - const struct inode_data *x = a, *y = b; +static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) { + int r; assert(x); assert(y); - if (x->dev < y->dev) - return -1; - if (x->dev > y->dev) - return 1; - - if (x->ino < y->ino) - return -1; - if (x->ino > y->ino) - return 1; + r = CMP(x->dev, y->dev); + if (r != 0) + return r; - return 0; + return CMP(x->ino, y->ino); } -static void inode_data_hash_func(const void *p, struct siphash *state) { - const struct inode_data *d = p; - - assert(p); +static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) { + assert(d); siphash24_compress(&d->dev, sizeof(d->dev), state); siphash24_compress(&d->ino, sizeof(d->ino), state); } -const struct hash_ops inode_data_hash_ops = { - .hash = inode_data_hash_func, - .compare = inode_data_compare -}; +DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare); static void event_free_inode_data( sd_event *e, @@ -1781,7 +1527,7 @@ static uint32_t inode_data_determine_mask(struct inode_data *d) { * * Note that we add all sources to the mask here, regardless whether enabled, disabled or oneshot. That's * because we cannot change the mask anymore after the event source was created once, since the kernel has no - * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and supress + * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and suppress * events we don't care for client-side. */ LIST_FOREACH(inotify.by_inode_data, s, d->event_sources) { @@ -1843,11 +1589,10 @@ _public_ int sd_event_add_inotify( sd_event_inotify_handler_t callback, void *userdata) { - bool rm_inotify = false, rm_inode = false; struct inotify_data *inotify_data = NULL; struct inode_data *inode_data = NULL; _cleanup_close_ int fd = -1; - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; struct stat st; int r; @@ -1885,13 +1630,13 @@ _public_ int sd_event_add_inotify( /* Allocate an inotify object for this priority, and an inode object within it */ r = event_make_inotify_data(e, SD_EVENT_PRIORITY_NORMAL, &inotify_data); if (r < 0) - goto fail; - rm_inotify = r > 0; + return r; r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data); - if (r < 0) - goto fail; - rm_inode = r > 0; + if (r < 0) { + event_free_inotify_data(e, inotify_data); + return r; + } /* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of * the event source, until then, for which we need the original inode. */ @@ -1904,72 +1649,45 @@ _public_ int sd_event_add_inotify( LIST_PREPEND(inotify.by_inode_data, inode_data->event_sources, s); s->inotify.inode_data = inode_data; - rm_inode = rm_inotify = false; - /* Actually realize the watch now */ r = inode_data_realize_watch(e, inode_data); if (r < 0) - goto fail; + return r; (void) sd_event_source_set_description(s, path); if (ret) *ret = s; + TAKE_PTR(s); return 0; - -fail: - source_free(s); - - if (rm_inode) - event_free_inode_data(e, inode_data); - - if (rm_inotify) - event_free_inotify_data(e, inotify_data); - - return r; } -_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { - +static sd_event_source* event_source_free(sd_event_source *s) { if (!s) return NULL; - assert(s->n_ref >= 1); - s->n_ref++; - - return s; -} - -_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) { + /* Here's a special hack: when we are called from a + * dispatch handler we won't free the event source + * immediately, but we will detach the fd from the + * epoll. This way it is safe for the caller to unref + * the event source and immediately close the fd, but + * we still retain a valid event source object after + * the callback. */ - if (!s) - return NULL; - - assert(s->n_ref >= 1); - s->n_ref--; - - if (s->n_ref <= 0) { - /* Here's a special hack: when we are called from a - * dispatch handler we won't free the event source - * immediately, but we will detach the fd from the - * epoll. This way it is safe for the caller to unref - * the event source and immediately close the fd, but - * we still retain a valid event source object after - * the callback. */ - - if (s->dispatching) { - if (s->type == SOURCE_IO) - source_io_unregister(s); + if (s->dispatching) { + if (s->type == SOURCE_IO) + source_io_unregister(s); - source_disconnect(s); - } else - source_free(s); - } + source_disconnect(s); + } else + source_free(s); return NULL; } +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event_source, sd_event_source, event_source_free); + _public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { assert_return(s, -EINVAL); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -1980,9 +1698,11 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des _public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { assert_return(s, -EINVAL); assert_return(description, -EINVAL); - assert_return(s->description, -ENXIO); assert_return(!event_pid_changed(s->event), -ECHILD); + if (!s->description) + return -ENXIO; + *description = s->description; return 0; } @@ -2234,11 +1954,11 @@ fail: _public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { assert_return(s, -EINVAL); - assert_return(m, -EINVAL); assert_return(!event_pid_changed(s->event), -ECHILD); - *m = s->enabled; - return 0; + if (m) + *m = s->enabled; + return s->enabled != SD_EVENT_OFF; } _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { @@ -3796,4 +3516,32 @@ _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_d return !!s->destroy_callback; } + +_public_ int sd_event_source_get_floating(sd_event_source *s) { + assert_return(s, -EINVAL); + + return s->floating; +} + +_public_ int sd_event_source_set_floating(sd_event_source *s, int b) { + assert_return(s, -EINVAL); + + if (s->floating == !!b) + return 0; + + if (!s->event) /* Already disconnected */ + return -ESTALE; + + s->floating = b; + + if (b) { + sd_event_source_ref(s); + sd_event_unref(s->event); + } else { + sd_event_ref(s->event); + sd_event_source_unref(s); + } + + return 1; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c index d4d668e8..6245e9df 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <fcntl.h> #include <unistd.h> @@ -187,16 +186,13 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { return id128_write_fd(fd, f, id, do_sync); } -void id128_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, 16, state); +void id128_hash_func(const sd_id128_t *p, struct siphash *state) { + siphash24_compress(p, sizeof(sd_id128_t), state); } -int id128_compare_func(const void *a, const void *b) { +int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) { return memcmp(a, b, 16); } -const struct hash_ops id128_hash_ops = { - .hash = id128_hash_func, - .compare = id128_compare_func, -}; +DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); #endif /* NM_IGNORED */ diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h index 44f159c0..65f14ab2 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.h +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h @@ -28,6 +28,6 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret); int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); -void id128_hash_func(const void *p, struct siphash *state); -int id128_compare_func(const void *a, const void *b) _pure_; +void id128_hash_func(const sd_id128_t *p, struct siphash *state); +int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index 483b2fe0..13a28291 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" -#include <errno.h> #include <fcntl.h> #include <unistd.h> @@ -21,7 +20,7 @@ #include "util.h" #if 0 /* NM_IGNORED */ -_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) { +_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { unsigned n; assert_return(s, NULL); @@ -277,7 +276,9 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - r = acquire_random_bytes(&t, sizeof t, true); + /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be + * fine for UUIDS. */ + r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND); if (r < 0) return r; @@ -289,19 +290,15 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { return 0; } -_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) { +static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) { _cleanup_(khash_unrefp) khash *h = NULL; - sd_id128_t m, result; + sd_id128_t result; const void *p; int r; - assert_return(ret, -EINVAL); - - r = sd_id128_get_machine(&m); - if (r < 0) - return r; + assert(ret); - r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m)); + r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base)); if (r < 0) return r; @@ -319,4 +316,30 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re *ret = make_v4_uuid(result); return 0; } + +_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) { + sd_id128_t id; + int r; + + assert_return(ret, -EINVAL); + + r = sd_id128_get_machine(&id); + if (r < 0) + return r; + + return get_app_specific(id, app_id, ret); +} + +_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) { + sd_id128_t id; + int r; + + assert_return(ret, -EINVAL); + + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + return get_app_specific(id, app_id, ret); +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index 048075f2..ebea861f 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #if 0 /* NM_IGNORED */ #if HAVE_LIBIDN2 @@ -21,6 +21,7 @@ #include "dns-domain.h" #include "hashmap.h" #include "hexdecoct.h" +#include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" @@ -28,9 +29,9 @@ #include "strv.h" #include "utf8.h" -int dns_label_unescape(const char **name, char *dest, size_t sz) { +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) { const char *n; - char *d; + char *d, last_char = 0; int r = 0; assert(name); @@ -40,13 +41,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { d = dest; for (;;) { - if (*n == '.') { - n++; - break; - } + if (*n == 0 || *n == '.') { + if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') + /* Trailing dash */ + return -EINVAL; - if (*n == 0) + if (*n == '.') + n++; break; + } if (r >= DNS_LABEL_MAX) return -EINVAL; @@ -56,6 +59,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (*n == '\\') { /* Escaped character */ + if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES)) + return -EINVAL; n++; @@ -66,6 +71,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { else if (IN_SET(*n, '\\', '.')) { /* Escaped backslash or dot */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) + return -EINVAL; + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -94,6 +103,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (k > 255) return -EINVAL; + if (FLAGS_SET(flags, DNS_LABEL_LDH) && + !valid_ldh_char((char) k)) + return -EINVAL; + + last_char = (char) k; if (d) *(d++) = (char) k; sz--; @@ -107,6 +121,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { /* Normal character */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) { + if (!valid_ldh_char(*n)) + return -EINVAL; + if (r == 0 && *n == '-') + /* Leading dash */ + return -EINVAL; + } + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -189,7 +212,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha terminal--; } - r = dns_label_unescape(&name, dest, sz); + r = dns_label_unescape(&name, dest, sz, 0); if (r < 0) return r; @@ -386,7 +409,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, #endif #endif /* NM_IGNORED */ -int dns_name_concat(const char *a, const char *b, char **_ret) { +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) { _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; const char *p; @@ -403,7 +426,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, flags); if (r < 0) return r; if (r == 0) { @@ -469,8 +492,7 @@ finish: } #if 0 /* NM_IGNORED */ -void dns_name_hash_func(const void *s, struct siphash *state) { - const char *p = s; +void dns_name_hash_func(const char *p, struct siphash *state) { int r; assert(p); @@ -478,7 +500,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) { for (;;) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) break; if (r == 0) @@ -493,15 +515,15 @@ void dns_name_hash_func(const void *s, struct siphash *state) { string_hash_func("", state); } -int dns_name_compare_func(const void *a, const void *b) { +int dns_name_compare_func(const char *a, const char *b) { const char *x, *y; int r, q; assert(a); assert(b); - x = (const char *) a + strlen(a); - y = (const char *) b + strlen(b); + x = a + strlen(a); + y = b + strlen(b); for (;;) { char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; @@ -520,10 +542,7 @@ int dns_name_compare_func(const void *a, const void *b) { } } -const struct hash_ops dns_name_hash_ops = { - .hash = dns_name_hash_func, - .compare = dns_name_compare_func -}; +DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func); int dns_name_equal(const char *x, const char *y) { int r, q; @@ -534,11 +553,11 @@ int dns_name_equal(const char *x, const char *y) { for (;;) { char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -565,14 +584,14 @@ int dns_name_endswith(const char *name, const char *suffix) { for (;;) { char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_n) saved_n = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -603,13 +622,13 @@ int dns_name_startswith(const char *name, const char *prefix) { for (;;) { char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, lp, sizeof(lp)); + r = dns_label_unescape(&p, lp, sizeof lp, 0); if (r < 0) return r; if (r == 0) return true; - q = dns_label_unescape(&n, ln, sizeof(ln)); + q = dns_label_unescape(&n, ln, sizeof ln, 0); if (q < 0) return q; @@ -638,14 +657,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char if (!saved_before) saved_before = n; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_after) saved_after = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -668,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char /* Found it! Now generate the new name */ prefix = strndupa(name, saved_before - name); - r = dns_name_concat(prefix, new_suffix, ret); + r = dns_name_concat(prefix, new_suffix, 0, ret); if (r < 0) return r; @@ -746,7 +765,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { for (i = 0; i < ELEMENTSOF(a); i++) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) return r; if (r == 0) @@ -783,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { char label[DNS_LABEL_MAX+1]; int x, y; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -792,7 +811,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { if (x < 0) return -EINVAL; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -861,7 +880,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo * dns_label_unescape() returns 0 when it hits the end * of the domain name, which we rely on here to encode * the trailing NUL byte. */ - r = dns_label_unescape(&domain, (char *) out, len); + r = dns_label_unescape(&domain, (char *) out, len, 0); if (r < 0) return r; @@ -929,7 +948,7 @@ bool dns_srv_type_is_valid(const char *name) { /* This more or less implements RFC 6335, Section 5.1 */ - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return false; if (r == 0) @@ -989,7 +1008,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha return -EINVAL; if (!name) - return dns_name_concat(type, domain, ret); + return dns_name_concat(type, domain, 0, ret); if (!dns_service_name_is_valid(name)) return -EINVAL; @@ -998,11 +1017,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha if (r < 0) return r; - r = dns_name_concat(type, domain, &n); + r = dns_name_concat(type, domain, 0, &n); if (r < 0) return r; - return dns_name_concat(escaped, n, ret); + return dns_name_concat(escaped, n, 0, ret); } static bool dns_service_name_label_is_valid(const char *label, size_t n) { @@ -1027,7 +1046,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do assert(joined); /* Get first label from the full name */ - an = dns_label_unescape(&p, a, sizeof(a)); + an = dns_label_unescape(&p, a, sizeof(a), 0); if (an < 0) return an; @@ -1035,7 +1054,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do x++; /* If there was a first label, try to get the second one */ - bn = dns_label_unescape(&p, b, sizeof(b)); + bn = dns_label_unescape(&p, b, sizeof(b), 0); if (bn < 0) return bn; @@ -1044,7 +1063,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do /* If there was a second label, try to get the third one */ q = p; - cn = dns_label_unescape(&p, c, sizeof(c)); + cn = dns_label_unescape(&p, c, sizeof(c), 0); if (cn < 0) return cn; @@ -1094,7 +1113,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do d = joined; finish: - r = dns_name_normalize(d, &domain); + r = dns_name_normalize(d, 0, &domain); if (r < 0) return r; @@ -1110,7 +1129,7 @@ finish: return 0; } -static int dns_name_build_suffix_table(const char *name, const char*table[]) { +static int dns_name_build_suffix_table(const char *name, const char *table[]) { const char *p; unsigned n = 0; int r; @@ -1239,12 +1258,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { } x = a_labels[n - 1 - k]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; y = b_labels[m - 1 - k]; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -1295,7 +1314,7 @@ int dns_name_apply_idna(const char *name, char **ret) { log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r)); if (r == IDN2_2HYPHEN) - /* The name has two hypens — forbidden by IDNA2008 in some cases */ + /* The name has two hyphens — forbidden by IDNA2008 in some cases */ return 0; if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) return -ENOSPC; @@ -1312,13 +1331,13 @@ int dns_name_apply_idna(const char *name, char **ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return r; if (r == 0) break; - q = dns_label_apply_idna(label, r, label, sizeof(label)); + q = dns_label_apply_idna(label, r, label, sizeof label); if (q < 0) return q; if (q > 0) diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index 95f4069c..17db7c52 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include <errno.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -24,13 +23,18 @@ /* Maximum number of labels per valid hostname */ #define DNS_N_LABELS_MAX 127 -int dns_label_unescape(const char **name, char *dest, size_t sz); +typedef enum DNSLabelFlags { + DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */ + DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */ +} DNSLabelFlags; + +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags); int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); int dns_label_escape_new(const char *p, size_t l, char **ret); static inline int dns_name_parent(const char **name) { - return dns_label_unescape(name, NULL, DNS_LABEL_MAX); + return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0); } #if 0 /* NM_IGNORED */ @@ -40,18 +44,29 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, #endif #endif /* NM_IGNORED */ -int dns_name_concat(const char *a, const char *b, char **ret); +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret); -static inline int dns_name_normalize(const char *s, char **ret) { +static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) { /* dns_name_concat() normalizes as a side-effect */ - return dns_name_concat(s, NULL, ret); + return dns_name_concat(s, NULL, flags, ret); } static inline int dns_name_is_valid(const char *s) { int r; /* dns_name_normalize() verifies as a side effect */ - r = dns_name_normalize(s, NULL); + r = dns_name_normalize(s, 0, NULL); + if (r == -EINVAL) + return 0; + if (r < 0) + return r; + return 1; +} + +static inline int dns_name_is_valid_ldh(const char *s) { + int r; + + r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL); if (r == -EINVAL) return 0; if (r < 0) @@ -59,8 +74,8 @@ static inline int dns_name_is_valid(const char *s) { return 1; } -void dns_name_hash_func(const void *s, struct siphash *state); -int dns_name_compare_func(const void *a, const void *b); +void dns_name_hash_func(const char *s, struct siphash *state); +int dns_name_compare_func(const char *a, const char *b); extern const struct hash_ops dns_name_hash_ops; int dns_name_between(const char *a, const char *b, const char *c); diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h index 4742b2e7..b3ee7bbc 100644 --- a/src/systemd/src/systemd/_sd-common.h +++ b/src/systemd/src/systemd/_sd-common.h @@ -23,24 +23,26 @@ # error "Do not include _sd-common.h directly; it is a private header." #endif +typedef void (*_sd_destroy_t)(void *userdata); + #ifndef _sd_printf_ # if __GNUC__ >= 4 -# define _sd_printf_(a,b) __attribute__ ((format (printf, a, b))) +# define _sd_printf_(a,b) __attribute__((__format__(printf, a, b))) # else # define _sd_printf_(a,b) # endif #endif #ifndef _sd_sentinel_ -# define _sd_sentinel_ __attribute__((sentinel)) +# define _sd_sentinel_ __attribute__((__sentinel__)) #endif #ifndef _sd_packed_ -# define _sd_packed_ __attribute__((packed)) +# define _sd_packed_ __attribute__((__packed__)) #endif #ifndef _sd_pure_ -# define _sd_pure_ __attribute__((pure)) +# define _sd_pure_ __attribute__((__pure__)) #endif #ifndef _SD_STRINGIFY @@ -70,6 +72,14 @@ # endif #endif +#ifndef _SD_ARRAY_STATIC +# if __STDC_VERSION__ >= 199901L +# define _SD_ARRAY_STATIC static +# else +# define _SD_ARRAY_STATIC +# endif +#endif + #define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func) \ static __inline__ void func##p(type **p) { \ if (*p) \ diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index e3885520..bd0d429d 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -23,6 +23,7 @@ #include <net/ethernet.h> #include <netinet/in.h> #include <sys/types.h> +#include <stdbool.h> #include "sd-dhcp-lease.h" #include "sd-event.h" @@ -127,12 +128,14 @@ int sd_dhcp_client_set_client_id( size_t data_len); int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len); int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint64_t llt_time); int sd_dhcp_client_set_duid( diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 2a60145f..d299c791 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -39,7 +39,7 @@ int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2); int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); @@ -57,6 +57,7 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway); +int sd_dhcp_route_get_option(sd_dhcp_route *route); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref); diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h index fa36dca9..43d38f5c 100644 --- a/src/systemd/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/src/systemd/sd-dhcp6-client.h @@ -21,7 +21,6 @@ #include <inttypes.h> #include <net/ethernet.h> -#include <stdbool.h> #include <sys/types.h> #include "sd-dhcp6-lease.h" @@ -120,8 +119,15 @@ int sd_dhcp6_client_get_information_request( int sd_dhcp6_client_set_request_option( sd_dhcp6_client *client, uint16_t option); +int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, + int *delegation); int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, - bool delegation); + int delegation); +int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, + int *request); +int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, + int request); +int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index eb35b834..787a12f2 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -33,7 +33,8 @@ - Supports event source prioritization - Scales better with a large number of time events because it does not require one timerfd each - Automatically tries to coalesce timer events system-wide - - Handles signals and child PIDs + - Handles signals, child PIDs, inotify events + - Supports systemd-style automatic watchdog event generation */ _SD_BEGIN_DECLARATIONS; @@ -76,7 +77,7 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, typedef void* sd_event_child_handler_t; #endif typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata); -typedef void (*sd_event_destroy_t)(void *userdata); +typedef _sd_destroy_t sd_event_destroy_t; int sd_event_default(sd_event **e); @@ -142,6 +143,8 @@ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid); int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback); int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); +int sd_event_source_get_floating(sd_event_source *s); +int sd_event_source_set_floating(sd_event_source *s, int b); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h index 143a0ffb..bdf88ed5 100644 --- a/src/systemd/src/systemd/sd-id128.h +++ b/src/systemd/src/systemd/sd-id128.h @@ -35,24 +35,26 @@ union sd_id128 { #define SD_ID128_STRING_MAX 33 -char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]); +char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]); int sd_id128_from_string(const char *s, sd_id128_t *ret); int sd_id128_randomize(sd_id128_t *ret); int sd_id128_get_machine(sd_id128_t *ret); -int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); int sd_id128_get_boot(sd_id128_t *ret); int sd_id128_get_invocation(sd_id128_t *ret); -#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ - ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ - 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) +int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); +int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); #define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }} +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + + /* 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 parameter or an expression with side @@ -108,7 +110,12 @@ _sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { return a.qwords[0] == 0 && a.qwords[1] == 0; } +_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) { + return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF); +} + #define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) +#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }}) _SD_END_DECLARATIONS; diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h index d650794c..bf3afadc 100644 --- a/src/systemd/src/systemd/sd-lldp.h +++ b/src/systemd/src/systemd/sd-lldp.h @@ -27,7 +27,7 @@ _SD_BEGIN_DECLARATIONS; -/* IEEE 802.3AB Clause 9: TLV Types */ +/* IEEE 802.1AB-2009 Clause 8: TLV Types */ enum { SD_LLDP_TYPE_END = 0, SD_LLDP_TYPE_CHASSIS_ID = 1, @@ -41,7 +41,7 @@ enum { SD_LLDP_TYPE_PRIVATE = 127, }; -/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ +/* IEEE 802.1AB-2009 Clause 8.5.2: Chassis subtypes */ enum { SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, @@ -53,7 +53,7 @@ enum { SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7, }; -/* IEEE 802.3AB Clause 9.5.3: Port subtype */ +/* IEEE 802.1AB-2009 Clause 8.5.3: Port subtype */ enum { SD_LLDP_PORT_SUBTYPE_RESERVED = 0, SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, @@ -65,6 +65,7 @@ enum { SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, }; +/* IEEE 802.1AB-2009 Clause 8.5.8: System capabilities */ enum { SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, @@ -95,6 +96,7 @@ enum { #define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } +/* IEEE 802.1AB-2009 Annex E */ enum { SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, @@ -105,14 +107,24 @@ enum { SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, }; +/* IEEE 802.1AB-2009 Annex F */ +enum { + SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS = 1, + SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI = 2, + SD_LLDP_OUI_802_3_SUBTYPE_LINK_AGGREGATION = 3, + SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4, +}; + typedef struct sd_lldp sd_lldp; typedef struct sd_lldp_neighbor sd_lldp_neighbor; typedef enum sd_lldp_event { - SD_LLDP_EVENT_ADDED = 'a', - SD_LLDP_EVENT_REMOVED = 'r', - SD_LLDP_EVENT_UPDATED = 'u', - SD_LLDP_EVENT_REFRESHED = 'f', + SD_LLDP_EVENT_ADDED, + SD_LLDP_EVENT_REMOVED, + SD_LLDP_EVENT_UPDATED, + SD_LLDP_EVENT_REFRESHED, + _SD_LLDP_EVENT_MAX, + _SD_LLDP_EVENT_INVALID = -1, } sd_lldp_event; typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata); @@ -166,8 +178,8 @@ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); -int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype); -int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype); +int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype); +int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype); int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h index 6b6249ca..d1bee343 100644 --- a/src/systemd/src/systemd/sd-ndisc.h +++ b/src/systemd/src/systemd/sd-ndisc.h @@ -55,8 +55,10 @@ typedef struct sd_ndisc sd_ndisc; typedef struct sd_ndisc_router sd_ndisc_router; typedef enum sd_ndisc_event { - SD_NDISC_EVENT_TIMEOUT = 't', - SD_NDISC_EVENT_ROUTER = 'r', + SD_NDISC_EVENT_TIMEOUT, + SD_NDISC_EVENT_ROUTER, + _SD_NDISC_EVENT_MAX, + _SD_NDISC_EVENT_INVALID = -1, } sd_ndisc_event; typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata); |