systemd: update code from upstream (2025-05-05)
This is a direct dump from systemd git. $ git clean -fdx && \ git cat-file -p HEAD | sed '1,/^======$/ d' | bash - && \ git add . ====== SYSTEMD_DIR=../systemd COMMIT=a50fa2a40f4a91d49503d3588a3dd29ea05e559b ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) git ls-files -z :/src/libnm-systemd-core/src/ \ :/src/libnm-systemd-shared/src/ \ :/src/libnm-std-aux/unaligned-fundamental.h \ :/src/libnm-std-aux/unaligned.h | \ xargs -0 rm -f nm_copy_sd_shared() { mkdir -p "./src/libnm-systemd-shared/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-shared/$1" } nm_copy_sd_core() { mkdir -p "./src/libnm-systemd-core/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-core/$1" } nm_copy_sd_stdaux() { mkdir -p "./src/libnm-std-aux/" cp "$SYSTEMD_DIR/$1" "./src/libnm-std-aux/${1##*/}" } nm_copy_sd_core "src/libsystemd-network/dhcp-duid-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-client-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h" nm_copy_sd_core "src/libsystemd-network/network-common.c" nm_copy_sd_core "src/libsystemd-network/network-common.h" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-duid.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c" nm_copy_sd_core "src/libsystemd/sd-device/device-internal.h" nm_copy_sd_core "src/libsystemd/sd-device/device-private.c" nm_copy_sd_core "src/libsystemd/sd-device/device-private.h" nm_copy_sd_core "src/libsystemd/sd-device/device-util.c" nm_copy_sd_core "src/libsystemd/sd-device/device-util.h" nm_copy_sd_core "src/libsystemd/sd-device/sd-device.c" nm_copy_sd_core "src/libsystemd/sd-event/event-source.h" nm_copy_sd_core "src/libsystemd/sd-event/event-util.c" nm_copy_sd_core "src/libsystemd/sd-event/event-util.h" nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h" nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c" nm_copy_sd_core "src/systemd/_sd-common.h" nm_copy_sd_core "src/systemd/sd-device.h" nm_copy_sd_core "src/systemd/sd-dhcp-duid.h" nm_copy_sd_core "src/systemd/sd-dhcp6-client.h" nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp6-option.h" nm_copy_sd_core "src/systemd/sd-dhcp6-protocol.h" nm_copy_sd_core "src/systemd/sd-event.h" nm_copy_sd_core "src/systemd/sd-id128.h" nm_copy_sd_core "src/systemd/sd-ndisc.h" nm_copy_sd_shared "src/basic/alloc-util.c" nm_copy_sd_shared "src/basic/alloc-util.h" nm_copy_sd_shared "src/basic/arphrd-util.h" nm_copy_sd_shared "src/basic/assert-util.h" nm_copy_sd_shared "src/basic/bitfield.h" nm_copy_sd_shared "src/basic/btrfs.c" nm_copy_sd_shared "src/basic/btrfs.h" nm_copy_sd_shared "src/basic/cgroup-util.h" nm_copy_sd_shared "src/basic/chase.h" nm_copy_sd_shared "src/basic/chattr-util.c" nm_copy_sd_shared "src/basic/chattr-util.h" nm_copy_sd_shared "src/basic/constants.h" nm_copy_sd_shared "src/basic/devnum-util.c" nm_copy_sd_shared "src/basic/devnum-util.h" nm_copy_sd_shared "src/basic/dns-def.h" nm_copy_sd_shared "src/basic/env-file.c" nm_copy_sd_shared "src/basic/env-file.h" nm_copy_sd_shared "src/basic/env-util.c" nm_copy_sd_shared "src/basic/env-util.h" nm_copy_sd_shared "src/basic/errno-util.h" nm_copy_sd_shared "src/basic/escape.c" nm_copy_sd_shared "src/basic/escape.h" nm_copy_sd_shared "src/basic/ether-addr-util.c" nm_copy_sd_shared "src/basic/ether-addr-util.h" nm_copy_sd_shared "src/basic/extract-word.c" nm_copy_sd_shared "src/basic/extract-word.h" nm_copy_sd_shared "src/basic/fd-util.c" nm_copy_sd_shared "src/basic/fd-util.h" nm_copy_sd_shared "src/basic/fileio.c" nm_copy_sd_shared "src/basic/fileio.h" nm_copy_sd_shared "src/basic/format-ifname.c" nm_copy_sd_shared "src/basic/format-ifname.h" nm_copy_sd_shared "src/basic/format-util.c" nm_copy_sd_shared "src/basic/format-util.h" nm_copy_sd_shared "src/basic/fs-util.c" nm_copy_sd_shared "src/basic/fs-util.h" nm_copy_sd_shared "src/basic/glyph-util.c" nm_copy_sd_shared "src/basic/glyph-util.h" nm_copy_sd_shared "src/basic/hash-funcs.c" nm_copy_sd_shared "src/basic/hash-funcs.h" nm_copy_sd_shared "src/basic/hashmap.c" nm_copy_sd_shared "src/basic/hashmap.h" nm_copy_sd_shared "src/basic/hexdecoct.c" nm_copy_sd_shared "src/basic/hexdecoct.h" nm_copy_sd_shared "src/basic/hostname-util.c" nm_copy_sd_shared "src/basic/hostname-util.h" nm_copy_sd_shared "src/basic/in-addr-util.c" nm_copy_sd_shared "src/basic/in-addr-util.h" nm_copy_sd_shared "src/basic/inotify-util.c" nm_copy_sd_shared "src/basic/inotify-util.h" nm_copy_sd_shared "src/basic/io-util.c" nm_copy_sd_shared "src/basic/io-util.h" nm_copy_sd_shared "src/basic/iovec-util.h" nm_copy_sd_shared "src/basic/label.c" nm_copy_sd_shared "src/basic/label.h" nm_copy_sd_shared "src/basic/list.h" nm_copy_sd_shared "src/basic/locale-util.c" nm_copy_sd_shared "src/basic/locale-util.h" nm_copy_sd_shared "src/basic/lock-util.h" nm_copy_sd_shared "src/basic/log.h" nm_copy_sd_shared "src/basic/macro.h" nm_copy_sd_shared "src/basic/memory-util.c" nm_copy_sd_shared "src/basic/memory-util.h" nm_copy_sd_shared "src/basic/mempool.c" nm_copy_sd_shared "src/basic/mempool.h" nm_copy_sd_shared "src/basic/missing_fcntl.h" nm_copy_sd_shared "src/basic/missing_fs.h" nm_copy_sd_shared "src/basic/missing_pidfd.h" nm_copy_sd_shared "src/basic/missing_random.h" nm_copy_sd_shared "src/basic/missing_socket.h" nm_copy_sd_shared "src/basic/missing_syscall.h" nm_copy_sd_shared "src/basic/missing_wait.h" nm_copy_sd_shared "src/basic/mountpoint-util.c" nm_copy_sd_shared "src/basic/mountpoint-util.h" nm_copy_sd_shared "src/basic/namespace-util.h" nm_copy_sd_shared "src/basic/ordered-set.c" nm_copy_sd_shared "src/basic/ordered-set.h" nm_copy_sd_shared "src/basic/origin-id.h" nm_copy_sd_shared "src/basic/parse-util.c" nm_copy_sd_shared "src/basic/parse-util.h" nm_copy_sd_shared "src/basic/path-util.c" nm_copy_sd_shared "src/basic/path-util.h" nm_copy_sd_shared "src/basic/pidfd-util.c" nm_copy_sd_shared "src/basic/pidfd-util.h" nm_copy_sd_shared "src/basic/pidref.h" nm_copy_sd_shared "src/basic/prioq.c" nm_copy_sd_shared "src/basic/prioq.h" nm_copy_sd_shared "src/basic/process-util.c" nm_copy_sd_shared "src/basic/process-util.h" nm_copy_sd_shared "src/basic/random-util.c" nm_copy_sd_shared "src/basic/random-util.h" nm_copy_sd_shared "src/basic/ratelimit.c" nm_copy_sd_shared "src/basic/ratelimit.h" nm_copy_sd_shared "src/basic/set.h" nm_copy_sd_shared "src/basic/sha256.c" nm_copy_sd_shared "src/basic/sha256.h" nm_copy_sd_shared "src/basic/signal-util.c" nm_copy_sd_shared "src/basic/signal-util.h" nm_copy_sd_shared "src/basic/siphash24.h" nm_copy_sd_shared "src/basic/socket-util.c" nm_copy_sd_shared "src/basic/socket-util.h" nm_copy_sd_shared "src/basic/sort-util.h" nm_copy_sd_shared "src/basic/sparse-endian.h" nm_copy_sd_shared "src/basic/stat-util.c" nm_copy_sd_shared "src/basic/stat-util.h" nm_copy_sd_shared "src/basic/stdio-util.h" nm_copy_sd_shared "src/basic/string-table.c" nm_copy_sd_shared "src/basic/string-table.h" nm_copy_sd_shared "src/basic/string-util.c" nm_copy_sd_shared "src/basic/string-util.h" nm_copy_sd_shared "src/basic/strv.c" nm_copy_sd_shared "src/basic/strv.h" nm_copy_sd_shared "src/basic/strxcpyx.c" nm_copy_sd_shared "src/basic/strxcpyx.h" nm_copy_sd_shared "src/basic/time-util.c" nm_copy_sd_shared "src/basic/time-util.h" nm_copy_sd_shared "src/basic/tmpfile-util.c" nm_copy_sd_shared "src/basic/tmpfile-util.h" nm_copy_sd_shared "src/basic/umask-util.h" nm_copy_sd_shared "src/basic/user-util.c" nm_copy_sd_shared "src/basic/user-util.h" nm_copy_sd_shared "src/basic/utf8.c" nm_copy_sd_shared "src/basic/utf8.h" nm_copy_sd_shared "src/basic/include/net/if.h" nm_copy_sd_shared "src/basic/include/netinet/in.h" nm_copy_sd_shared "src/fundamental/assert-fundamental.h" nm_copy_sd_shared "src/fundamental/iovec-util-fundamental.h" nm_copy_sd_shared "src/fundamental/logarithm.h" nm_copy_sd_shared "src/fundamental/macro-fundamental.h" nm_copy_sd_shared "src/fundamental/memory-util-fundamental.h" nm_copy_sd_shared "src/fundamental/sha256-fundamental.c" nm_copy_sd_shared "src/fundamental/sha256-fundamental.h" nm_copy_sd_shared "src/fundamental/string-util-fundamental.c" nm_copy_sd_shared "src/fundamental/string-util-fundamental.h" nm_copy_sd_shared "src/shared/dns-domain.c" nm_copy_sd_shared "src/shared/dns-domain.h" nm_copy_sd_shared "src/shared/log-link.h" nm_copy_sd_shared "src/shared/web-util.c" nm_copy_sd_shared "src/shared/web-util.h" nm_copy_sd_stdaux "src/basic/unaligned.h" nm_copy_sd_stdaux "src/fundamental/unaligned-fundamental.h"
This commit is contained in:
@@ -8,8 +8,8 @@
|
||||
#include <net/ethernet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
#include "sd-dhcp6-client.h"
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "dhcp-duid-internal.h"
|
||||
#include "dhcp6-client-internal.h"
|
||||
|
@@ -8,10 +8,10 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "sd-dhcp6-lease.h"
|
||||
#include "dns-resolver-internal.h"
|
||||
|
||||
#include "dhcp6-option.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "dns-resolver-internal.h"
|
||||
#include "macro.h"
|
||||
#include "set.h"
|
||||
#include "time-util.h"
|
||||
|
@@ -4,13 +4,13 @@
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/if_packet.h>
|
||||
|
||||
#include "dhcp6-internal.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
|
@@ -4,9 +4,9 @@
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_infiniband.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "sd-dhcp6-client.h"
|
||||
|
||||
@@ -1289,7 +1289,8 @@ static int client_receive_message(
|
||||
|
||||
sd_dhcp6_client *client = ASSERT_PTR(userdata);
|
||||
DHCP6_CLIENT_DONT_DESTROY(client);
|
||||
/* This needs to be initialized with zero. See #20741. */
|
||||
/* This needs to be initialized with zero. See #20741.
|
||||
* The issue is fixed on glibc-2.35 (8fba672472ae0055387e9315fc2eddfa6775ca79). */
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
|
||||
struct iovec iov;
|
||||
union sockaddr_union sa = {};
|
||||
|
@@ -13,6 +13,43 @@
|
||||
struct sd_device {
|
||||
unsigned n_ref;
|
||||
|
||||
/* syspath */
|
||||
char *syspath;
|
||||
const char *devpath;
|
||||
const char *sysnum;
|
||||
char *sysname;
|
||||
|
||||
/* only set when device is passed through netlink */
|
||||
sd_device_action_t action;
|
||||
uint64_t seqnum;
|
||||
|
||||
/* basic kernel properties */
|
||||
char *subsystem;
|
||||
char *driver_subsystem; /* only set for the 'drivers' subsystem */
|
||||
char *driver;
|
||||
char *devtype;
|
||||
|
||||
/* device node properties */
|
||||
char *devname;
|
||||
dev_t devnum;
|
||||
mode_t devmode;
|
||||
uid_t devuid;
|
||||
gid_t devgid;
|
||||
|
||||
/* block device properties */
|
||||
uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
|
||||
|
||||
/* network interface properties */
|
||||
int ifindex;
|
||||
|
||||
/* determined by devnnum, ifindex, subsystem, and sysname */
|
||||
char *device_id;
|
||||
|
||||
/* sysfs attributes */
|
||||
Hashmap *sysattr_values; /* cached sysattr values */
|
||||
Set *sysattrs; /* names of sysattrs */
|
||||
Iterator sysattrs_iterator;
|
||||
|
||||
/* The database version indicates the supported features by the udev database.
|
||||
* This is saved and parsed in V field.
|
||||
*
|
||||
@@ -21,68 +58,38 @@ struct sd_device {
|
||||
*/
|
||||
unsigned database_version;
|
||||
|
||||
sd_device *parent;
|
||||
/* when device is initialized by udevd */
|
||||
usec_t usec_initialized;
|
||||
|
||||
OrderedHashmap *properties;
|
||||
/* properties */
|
||||
OrderedHashmap *properties; /* all properties set from uevent and by udevd */
|
||||
Iterator properties_iterator;
|
||||
uint64_t properties_generation; /* changes whenever the properties are changed */
|
||||
uint64_t properties_iterator_generation; /* generation when iteration was started */
|
||||
OrderedHashmap *properties_db; /* the subset of the properties that should be written to the db */
|
||||
char **properties_strv; /* the properties hashmap as a strv */
|
||||
char *properties_nulstr; /* the same as a nulstr */
|
||||
size_t properties_nulstr_len;
|
||||
|
||||
/* the subset of the properties that should be written to the db */
|
||||
OrderedHashmap *properties_db;
|
||||
|
||||
Hashmap *sysattr_values; /* cached sysattr values */
|
||||
|
||||
Set *sysattrs; /* names of sysattrs */
|
||||
Iterator sysattrs_iterator;
|
||||
|
||||
/* TAG keyword */
|
||||
Set *all_tags, *current_tags;
|
||||
Iterator all_tags_iterator, current_tags_iterator;
|
||||
uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */
|
||||
uint64_t tags_generation; /* changes whenever the tags are changed */
|
||||
|
||||
/* SYMLINK keyword */
|
||||
Set *devlinks;
|
||||
Iterator devlinks_iterator;
|
||||
uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
|
||||
uint64_t devlinks_iterator_generation; /* generation when iteration was started */
|
||||
int devlink_priority;
|
||||
|
||||
/* parent and child devices */
|
||||
sd_device *parent;
|
||||
Hashmap *children;
|
||||
Iterator children_iterator;
|
||||
bool children_enumerated;
|
||||
|
||||
int ifindex;
|
||||
char *devtype;
|
||||
char *devname;
|
||||
dev_t devnum;
|
||||
|
||||
char **properties_strv; /* the properties hashmap as a strv */
|
||||
char *properties_nulstr; /* the same as a nulstr */
|
||||
size_t properties_nulstr_len;
|
||||
|
||||
char *syspath;
|
||||
const char *devpath;
|
||||
const char *sysnum;
|
||||
char *sysname;
|
||||
|
||||
char *subsystem;
|
||||
char *driver_subsystem; /* only set for the 'drivers' subsystem */
|
||||
char *driver;
|
||||
|
||||
char *device_id;
|
||||
|
||||
usec_t usec_initialized;
|
||||
|
||||
mode_t devmode;
|
||||
uid_t devuid;
|
||||
gid_t devgid;
|
||||
|
||||
uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
|
||||
|
||||
/* only set when device is passed through netlink */
|
||||
sd_device_action_t action;
|
||||
uint64_t seqnum;
|
||||
|
||||
bool parent_set:1; /* no need to try to reload parent */
|
||||
bool sysattrs_read:1; /* don't try to re-read sysattrs once read */
|
||||
bool property_tags_outdated:1; /* need to update TAGS= or CURRENT_TAGS= property */
|
||||
@@ -92,7 +99,6 @@ struct sd_device {
|
||||
bool driver_set:1; /* don't reread driver */
|
||||
bool uevent_loaded:1; /* don't reread uevent */
|
||||
bool db_loaded; /* don't reread db */
|
||||
|
||||
bool is_initialized:1;
|
||||
bool sealed:1; /* don't read more information from uevent/db */
|
||||
bool db_persist:1; /* don't clean up the db when switching from initrd to real root */
|
||||
@@ -106,6 +112,8 @@ static inline int device_add_property_internal(sd_device *device, const char *ke
|
||||
|
||||
int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
|
||||
int device_set_ifindex(sd_device *device, const char *ifindex);
|
||||
int device_set_devuid(sd_device *device, const char *uid);
|
||||
int device_set_devgid(sd_device *device, const char *gid);
|
||||
int device_set_devmode(sd_device *device, const char *devmode);
|
||||
int device_set_devname(sd_device *device, const char *devname);
|
||||
int device_set_devtype(sd_device *device, const char *devtype);
|
||||
|
@@ -116,6 +116,10 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret) {
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_read_uevent_file(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = device_read_db(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -134,6 +138,10 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_read_uevent_file(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = device_read_db(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -147,7 +155,7 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_set_devuid(sd_device *device, const char *uid) {
|
||||
int device_set_devuid(sd_device *device, const char *uid) {
|
||||
uid_t u;
|
||||
int r;
|
||||
|
||||
@@ -172,6 +180,10 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_read_uevent_file(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = device_read_db(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -185,7 +197,7 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_set_devgid(sd_device *device, const char *gid) {
|
||||
int device_set_devgid(sd_device *device, const char *gid) {
|
||||
gid_t g;
|
||||
int r;
|
||||
|
||||
@@ -429,10 +441,11 @@ static int device_verify(sd_device *device) {
|
||||
return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
|
||||
"sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
|
||||
|
||||
if (streq(device->subsystem, "drivers")) {
|
||||
if (device_in_subsystem(device, "drivers")) {
|
||||
r = device_set_drivers_subsystem(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to set driver subsystem: %m");
|
||||
}
|
||||
|
||||
device->sealed = true;
|
||||
@@ -679,8 +692,8 @@ int device_clone_with_db(sd_device *device, sd_device **ret) {
|
||||
void device_cleanup_tags(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
device->all_tags = set_free_free(device->all_tags);
|
||||
device->current_tags = set_free_free(device->current_tags);
|
||||
device->all_tags = set_free(device->all_tags);
|
||||
device->current_tags = set_free(device->current_tags);
|
||||
device->property_tags_outdated = true;
|
||||
device->tags_generation++;
|
||||
}
|
||||
@@ -688,7 +701,7 @@ void device_cleanup_tags(sd_device *device) {
|
||||
void device_cleanup_devlinks(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
set_free_free(device->devlinks);
|
||||
set_free(device->devlinks);
|
||||
device->devlinks = NULL;
|
||||
device->property_devlinks_outdated = true;
|
||||
device->devlinks_generation++;
|
||||
@@ -948,7 +961,3 @@ static const char* const device_action_table[_SD_DEVICE_ACTION_MAX] = {
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(device_action, sd_device_action_t);
|
||||
|
||||
void dump_device_action_table(void) {
|
||||
DUMP_STRING_TABLE(device_action, sd_device_action_t, _SD_DEVICE_ACTION_MAX);
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "chase.h"
|
||||
#include "macro.h"
|
||||
|
||||
int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum);
|
||||
@@ -17,8 +18,10 @@ int device_new_from_strv(sd_device **ret, char **strv);
|
||||
|
||||
int device_opendir(sd_device *device, const char *subdir, DIR **ret);
|
||||
|
||||
int device_get_sysnum_unsigned(sd_device *device, unsigned *ret);
|
||||
int device_get_property_bool(sd_device *device, const char *key);
|
||||
int device_get_property_int(sd_device *device, const char *key, int *ret);
|
||||
int device_get_ifname(sd_device *device, const char **ret);
|
||||
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
|
||||
int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);
|
||||
static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
|
||||
@@ -31,9 +34,9 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret);
|
||||
int device_get_devnode_uid(sd_device *device, uid_t *ret);
|
||||
int device_get_devnode_gid(sd_device *device, gid_t *ret);
|
||||
|
||||
int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **ret_resolved, int *ret_fd);
|
||||
void device_clear_sysattr_cache(sd_device *device);
|
||||
int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
|
||||
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
|
||||
int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error);
|
||||
|
||||
void device_seal(sd_device *device);
|
||||
void device_set_is_initialized(sd_device *device);
|
||||
@@ -76,4 +79,3 @@ int device_read_uevent_file(sd_device *device);
|
||||
int device_set_action(sd_device *device, sd_device_action_t a);
|
||||
sd_device_action_t device_action_from_string(const char *s) _pure_;
|
||||
const char* device_action_to_string(sd_device_action_t a) _const_;
|
||||
void dump_device_action_table(void);
|
||||
|
150
src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
Normal file
150
src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "devnum-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
int devname_from_devnum(mode_t mode, dev_t devnum, char **ret) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
const char *devname;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (devnum_is_zero(devnum))
|
||||
return device_path_make_inaccessible(mode, ret);
|
||||
|
||||
r = device_new_from_mode_and_devnum(&dev, mode, devnum);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_get_devname(dev, &devname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return strdup_to(ret, devname);
|
||||
}
|
||||
|
||||
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret_devname) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
r = device_new_from_mode_and_devnum(&dev, mode, devnum);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = sd_device_open(dev, flags);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (ret_devname) {
|
||||
const char *devname;
|
||||
|
||||
r = sd_device_get_devname(dev, &devname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strdup_to(ret_devname, devname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
static int add_string_field(
|
||||
sd_device *device,
|
||||
const char *field,
|
||||
int (*func)(sd_device *dev, const char **s),
|
||||
char ***strv) {
|
||||
|
||||
const char *s;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(field);
|
||||
assert(func);
|
||||
assert(strv);
|
||||
|
||||
r = func(device, &s);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_device_debug_errno(device, r, "Failed to get device \"%s\" property, ignoring: %m", field);
|
||||
if (r >= 0)
|
||||
(void) strv_extend_assignment(strv, field, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char** device_make_log_fields(sd_device *device) {
|
||||
_cleanup_strv_free_ char **strv = NULL;
|
||||
dev_t devnum;
|
||||
int ifindex;
|
||||
sd_device_action_t action;
|
||||
uint64_t seqnum, diskseq;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
(void) add_string_field(device, "SYSPATH", sd_device_get_syspath, &strv);
|
||||
(void) add_string_field(device, "SUBSYSTEM", sd_device_get_subsystem, &strv);
|
||||
(void) add_string_field(device, "DEVTYPE", sd_device_get_devtype, &strv);
|
||||
(void) add_string_field(device, "DRIVER", sd_device_get_driver, &strv);
|
||||
(void) add_string_field(device, "DEVPATH", sd_device_get_devpath, &strv);
|
||||
(void) add_string_field(device, "DEVNAME", sd_device_get_devname, &strv);
|
||||
(void) add_string_field(device, "SYSNAME", sd_device_get_sysname, &strv);
|
||||
(void) add_string_field(device, "SYSNUM", sd_device_get_sysnum, &strv);
|
||||
|
||||
r = sd_device_get_devnum(device, &devnum);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_device_debug_errno(device, r, "Failed to get device \"DEVNUM\" property, ignoring: %m");
|
||||
if (r >= 0)
|
||||
(void) strv_extendf(&strv, "DEVNUM="DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(devnum));
|
||||
|
||||
r = sd_device_get_ifindex(device, &ifindex);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_device_debug_errno(device, r, "Failed to get device \"IFINDEX\" property, ignoring: %m");
|
||||
if (r >= 0)
|
||||
(void) strv_extendf(&strv, "IFINDEX=%i", ifindex);
|
||||
|
||||
r = sd_device_get_action(device, &action);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_device_debug_errno(device, r, "Failed to get device \"ACTION\" property, ignoring: %m");
|
||||
if (r >= 0)
|
||||
(void) strv_extendf(&strv, "ACTION=%s", device_action_to_string(action));
|
||||
|
||||
r = sd_device_get_seqnum(device, &seqnum);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_device_debug_errno(device, r, "Failed to get device \"SEQNUM\" property, ignoring: %m");
|
||||
if (r >= 0)
|
||||
(void) strv_extendf(&strv, "SEQNUM=%"PRIu64, seqnum);
|
||||
|
||||
r = sd_device_get_diskseq(device, &diskseq);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_device_debug_errno(device, r, "Failed to get device \"DISKSEQ\" property, ignoring: %m");
|
||||
if (r >= 0)
|
||||
(void) strv_extendf(&strv, "DISKSEQ=%"PRIu64, diskseq);
|
||||
|
||||
return TAKE_PTR(strv);
|
||||
}
|
||||
|
||||
bool device_in_subsystem(sd_device *device, const char *subsystem) {
|
||||
const char *s = NULL;
|
||||
|
||||
assert(device);
|
||||
|
||||
(void) sd_device_get_subsystem(device, &s);
|
||||
return streq_ptr(s, subsystem);
|
||||
}
|
||||
|
||||
bool device_is_devtype(sd_device *device, const char *devtype) {
|
||||
const char *s = NULL;
|
||||
|
||||
assert(device);
|
||||
|
||||
(void) sd_device_get_devtype(device, &s);
|
||||
return streq_ptr(s, devtype);
|
||||
}
|
@@ -110,6 +110,19 @@ bool device_is_devtype(sd_device *device, const char *devtype);
|
||||
static inline bool device_property_can_set(const char *property) {
|
||||
return property &&
|
||||
!STR_IN_SET(property,
|
||||
"ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
|
||||
"IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
|
||||
/* basic properties set by kernel, only in netlink event */
|
||||
"ACTION", "SEQNUM", "SYNTH_UUID",
|
||||
/* basic properties set by kernel, both in netlink event and uevent file */
|
||||
"DEVPATH", "DEVPATH_OLD", "SUBSYSTEM", "DEVTYPE", "DRIVER", "MODALIAS",
|
||||
/* device node */
|
||||
"DEVNAME", "DEVMODE", "DEVUID", "DEVGID", "MAJOR", "MINOR",
|
||||
/* block device */
|
||||
"DISKSEQ", "PARTN",
|
||||
/* network interface (INTERFACE_OLD is set by udevd) */
|
||||
"IFINDEX", "INTERFACE", "INTERFACE_OLD",
|
||||
/* basic properties set by udevd */
|
||||
"DEVLINKS", "TAGS", "CURRENT_TAGS", "USEC_INITIALIZED", "UDEV_DATABASE_VERSION") &&
|
||||
/* Similar to SYNTH_UUID, but set based on KEY=VALUE arguments passed by userspace.
|
||||
* See kernel's f36776fafbaa0094390dd4e7e3e29805e0b82730 (v4.13) */
|
||||
!startswith(property, "SYNTH_ARG_");
|
||||
}
|
||||
|
@@ -366,7 +366,7 @@ _public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
|
||||
r = rtnl_get_ifname_full(NULL, ifindex, &ifname, NULL);
|
||||
r = rtnl_get_ifname(NULL, ifindex, &ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -507,7 +507,7 @@ _public_ int sd_device_new_from_subsystem_sysname(
|
||||
if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
|
||||
r = device_new_from_path_join(&device, subsystem, subsys, "drivers", "/sys/bus/", subsys, "/drivers", NULL);
|
||||
else
|
||||
r = device_new_from_path_join(&device, subsystem, subsys, sep, "/sys/bus/", subsys, "/drivers/", sep);
|
||||
r = device_new_from_path_join(&device, subsystem, subsys, sysname + (sep - name), "/sys/bus/", subsys, "/drivers/", sep);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -766,16 +766,24 @@ static int handle_uevent_line(
|
||||
assert(major);
|
||||
assert(minor);
|
||||
|
||||
if (streq(key, "SUBSYSTEM"))
|
||||
return device_set_subsystem(device, value);
|
||||
if (streq(key, "DEVTYPE"))
|
||||
return device_set_devtype(device, value);
|
||||
if (streq(key, "IFINDEX"))
|
||||
return device_set_ifindex(device, value);
|
||||
if (streq(key, "DEVNAME"))
|
||||
return device_set_devname(device, value);
|
||||
if (streq(key, "DEVUID"))
|
||||
return device_set_devuid(device, value);
|
||||
if (streq(key, "DEVGID"))
|
||||
return device_set_devgid(device, value);
|
||||
if (streq(key, "DEVMODE"))
|
||||
return device_set_devmode(device, value);
|
||||
if (streq(key, "DISKSEQ"))
|
||||
return device_set_diskseq(device, value);
|
||||
if (streq(key, "DRIVER"))
|
||||
return device_set_driver(device, value);
|
||||
if (streq(key, "MAJOR"))
|
||||
*major = value;
|
||||
else if (streq(key, "MINOR"))
|
||||
@@ -787,89 +795,59 @@ static int handle_uevent_line(
|
||||
}
|
||||
|
||||
int device_read_uevent_file(sd_device *device) {
|
||||
_cleanup_free_ char *uevent = NULL;
|
||||
const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
|
||||
char *path;
|
||||
size_t uevent_len;
|
||||
int r;
|
||||
|
||||
enum {
|
||||
PRE_KEY,
|
||||
KEY,
|
||||
PRE_VALUE,
|
||||
VALUE,
|
||||
INVALID_LINE,
|
||||
} state = PRE_KEY;
|
||||
|
||||
assert(device);
|
||||
|
||||
if (device->uevent_loaded || device->sealed)
|
||||
return 0;
|
||||
|
||||
r = sd_device_get_syspath(device, &syspath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
device->uevent_loaded = true;
|
||||
|
||||
path = strjoina(syspath, "/uevent");
|
||||
|
||||
r = read_full_virtual_file(path, &uevent, &uevent_len);
|
||||
if (r == -EACCES || ERRNO_IS_NEG_DEVICE_ABSENT(r))
|
||||
const char *uevent;
|
||||
r = sd_device_get_sysattr_value(device, "uevent", &uevent);
|
||||
if (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_DEVICE_ABSENT(r))
|
||||
/* The uevent files may be write-only, the device may be already removed, or the device
|
||||
* may not have the uevent file. */
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file: %m");
|
||||
|
||||
for (size_t i = 0; i < uevent_len; i++)
|
||||
switch (state) {
|
||||
case PRE_KEY:
|
||||
if (!strchr(NEWLINE, uevent[i])) {
|
||||
key = &uevent[i];
|
||||
|
||||
state = KEY;
|
||||
}
|
||||
|
||||
break;
|
||||
case KEY:
|
||||
if (uevent[i] == '=') {
|
||||
uevent[i] = '\0';
|
||||
|
||||
state = PRE_VALUE;
|
||||
} else if (strchr(NEWLINE, uevent[i])) {
|
||||
uevent[i] = '\0';
|
||||
log_device_debug(device, "sd-device: Invalid uevent line '%s', ignoring", key);
|
||||
|
||||
state = PRE_KEY;
|
||||
}
|
||||
|
||||
break;
|
||||
case PRE_VALUE:
|
||||
value = &uevent[i];
|
||||
state = VALUE;
|
||||
|
||||
_fallthrough_; /* to handle empty property */
|
||||
case VALUE:
|
||||
if (strchr(NEWLINE, uevent[i])) {
|
||||
uevent[i] = '\0';
|
||||
|
||||
r = handle_uevent_line(device, key, value, &major, &minor);
|
||||
_cleanup_strv_free_ char **v = NULL;
|
||||
r = strv_split_newlines_full(&v, uevent, EXTRACT_RETAIN_ESCAPE);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value);
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to parse uevent file: %m");
|
||||
|
||||
state = PRE_KEY;
|
||||
const char *major = NULL, *minor = NULL;
|
||||
STRV_FOREACH(s, v) {
|
||||
char *eq = strchr(*s, '=');
|
||||
if (!eq) {
|
||||
log_device_debug(device, "sd-device: Invalid uevent line, ignoring: %s", *s);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
*eq = '\0';
|
||||
|
||||
r = handle_uevent_line(device, *s, eq + 1, &major, &minor);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m",
|
||||
*s, eq + 1);
|
||||
}
|
||||
|
||||
if (major) {
|
||||
r = device_set_devnum(device, major, minor);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, strna(minor), path);
|
||||
log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to set 'MAJOR=%s' and/or 'MINOR=%s' from uevent, ignoring: %m",
|
||||
major, strna(minor));
|
||||
}
|
||||
|
||||
if (device_in_subsystem(device, "drivers")) {
|
||||
r = device_set_drivers_subsystem(device);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to set driver subsystem, ignoring: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -893,6 +871,21 @@ _public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_ifname(sd_device *device, const char **ret) {
|
||||
int r;
|
||||
|
||||
assert_return(device, -EINVAL);
|
||||
|
||||
/* First, check if the device is a network interface. */
|
||||
r = sd_device_get_ifindex(device, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The sysname and ifname may be different, as '!' in sysname are replaced with '/'.
|
||||
* For network interfaces, we can use INTERFACE property. */
|
||||
return sd_device_get_property_value(device, "INTERFACE", ret);
|
||||
}
|
||||
|
||||
_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
|
||||
int r;
|
||||
|
||||
@@ -1214,36 +1207,29 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
|
||||
|
||||
assert_return(device, -EINVAL);
|
||||
|
||||
if (!device->subsystem_set) {
|
||||
_cleanup_free_ char *subsystem = NULL;
|
||||
const char *syspath;
|
||||
char *path;
|
||||
|
||||
r = sd_device_get_syspath(device, &syspath);
|
||||
r = device_read_uevent_file(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* read 'subsystem' link */
|
||||
path = strjoina(syspath, "/subsystem");
|
||||
r = readlink_value(path, &subsystem);
|
||||
if (!device->subsystem_set) {
|
||||
const char *subsystem;
|
||||
|
||||
r = sd_device_get_sysattr_value(device, "subsystem", &subsystem);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to read subsystem for %s: %m",
|
||||
device->devpath);
|
||||
|
||||
if (subsystem)
|
||||
if (r >= 0)
|
||||
r = device_set_subsystem(device, subsystem);
|
||||
/* use implicit names */
|
||||
else if (!isempty(path_startswith(device->devpath, "/module/")))
|
||||
r = device_set_subsystem(device, "module");
|
||||
else if (strstr(syspath, "/drivers/") || endswith(syspath, "/drivers"))
|
||||
else if (strstr(device->devpath, "/drivers/") || endswith(device->devpath, "/drivers"))
|
||||
r = device_set_drivers_subsystem(device);
|
||||
else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/")))
|
||||
r = device_set_subsystem(device, "subsystem");
|
||||
else {
|
||||
device->subsystem_set = true;
|
||||
r = 0;
|
||||
}
|
||||
else
|
||||
r = device_set_subsystem(device, NULL);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to set subsystem for %s: %m",
|
||||
@@ -1352,23 +1338,21 @@ int device_set_driver(sd_device *device, const char *driver) {
|
||||
}
|
||||
|
||||
_public_ int sd_device_get_driver(sd_device *device, const char **ret) {
|
||||
assert_return(device, -EINVAL);
|
||||
|
||||
if (!device->driver_set) {
|
||||
_cleanup_free_ char *driver = NULL;
|
||||
const char *syspath;
|
||||
char *path;
|
||||
int r;
|
||||
|
||||
r = sd_device_get_syspath(device, &syspath);
|
||||
assert_return(device, -EINVAL);
|
||||
|
||||
r = device_read_uevent_file(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = strjoina(syspath, "/driver");
|
||||
r = readlink_value(path, &driver);
|
||||
if (!device->driver_set) {
|
||||
const char *driver = NULL;
|
||||
|
||||
r = sd_device_get_sysattr_value(device, "driver", &driver);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_device_debug_errno(device, r,
|
||||
"sd-device: readlink(\"%s\") failed: %m", path);
|
||||
"sd-device: Failed to read driver: %m");
|
||||
|
||||
r = device_set_driver(device, driver);
|
||||
if (r < 0)
|
||||
@@ -1476,6 +1460,26 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_sysnum_unsigned(sd_device *device, unsigned *ret) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
const char *s;
|
||||
r = sd_device_get_sysnum(device, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
unsigned n;
|
||||
r = safe_atou_full(s, SAFE_ATO_REFUSE_PLUS_MINUS | SAFE_ATO_REFUSE_LEADING_WHITESPACE | 10, &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) {
|
||||
assert_return(device, -EINVAL);
|
||||
|
||||
@@ -1702,18 +1706,13 @@ _public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
|
||||
|
||||
if (!device->device_id) {
|
||||
_cleanup_free_ char *id = NULL;
|
||||
const char *subsystem;
|
||||
dev_t devnum;
|
||||
int ifindex, r;
|
||||
|
||||
r = sd_device_get_subsystem(device, &subsystem);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_device_get_devnum(device, &devnum) >= 0) {
|
||||
/* use dev_t — b259:131072, c254:0 */
|
||||
if (asprintf(&id, "%c" DEVNUM_FORMAT_STR,
|
||||
streq(subsystem, "block") ? 'b' : 'c',
|
||||
device_in_subsystem(device, "block") ? 'b' : 'c',
|
||||
DEVNUM_FORMAT_VAL(devnum)) < 0)
|
||||
return -ENOMEM;
|
||||
} else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
|
||||
@@ -1731,13 +1730,18 @@ _public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
|
||||
if (r == O_DIRECTORY)
|
||||
return -EINVAL;
|
||||
|
||||
if (streq(subsystem, "drivers")) {
|
||||
if (device_in_subsystem(device, "drivers"))
|
||||
/* the 'drivers' pseudo-subsystem is special, and needs the real
|
||||
* subsystem encoded as well */
|
||||
assert(device->driver_subsystem);
|
||||
id = strjoin("+drivers:", device->driver_subsystem, ":", sysname);
|
||||
} else
|
||||
id = strjoin("+drivers:", ASSERT_PTR(device->driver_subsystem), ":", sysname);
|
||||
else {
|
||||
const char *subsystem;
|
||||
r = sd_device_get_subsystem(device, &subsystem);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
id = strjoin("+", subsystem, ":", sysname);
|
||||
}
|
||||
if (!id)
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -2326,134 +2330,224 @@ void device_clear_sysattr_cache(sd_device *device) {
|
||||
device->sysattr_values = hashmap_free(device->sysattr_values);
|
||||
}
|
||||
|
||||
int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
|
||||
_unused_ _cleanup_free_ char *old_value = NULL;
|
||||
_cleanup_free_ char *new_key = NULL;
|
||||
typedef struct SysAttrCacheEntry {
|
||||
char *key;
|
||||
char *value;
|
||||
int error;
|
||||
} SysAttrCacheEntry;
|
||||
|
||||
static SysAttrCacheEntry* sysattr_cache_entry_free(SysAttrCacheEntry *p) {
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
free(p->key);
|
||||
free(p->value);
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
sysattr_cache_hash_ops,
|
||||
char, path_hash_func, path_compare,
|
||||
SysAttrCacheEntry, sysattr_cache_entry_free);
|
||||
|
||||
static int device_cache_sysattr_value_full(sd_device *device, char *key, char *value, int error, bool ignore_uevent) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(key);
|
||||
assert(value || error > 0);
|
||||
|
||||
/* This takes the reference of the input value. The input value may be NULL.
|
||||
* This replaces the value if it already exists. */
|
||||
/* This takes the reference of the input arguments when cached, hence the caller must not free them
|
||||
* when a positive return value is returned. The input value may be NULL. This replaces an already
|
||||
* existing entry. */
|
||||
|
||||
/* First, remove the old cache entry. So, we do not need to clear cache on error. */
|
||||
old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key);
|
||||
if (!new_key) {
|
||||
new_key = strdup(key);
|
||||
if (!new_key)
|
||||
if (ignore_uevent && streq(last_path_component(key), "uevent"))
|
||||
return 0; /* not cached */
|
||||
|
||||
/* Remove the old cache entry. So, we do not need to clear cache on error. */
|
||||
sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, key));
|
||||
|
||||
/* We use ENOANO as a recognizable error code when we have not read the attribute. */
|
||||
if (error == ENOANO)
|
||||
error = ESTALE;
|
||||
|
||||
_cleanup_free_ SysAttrCacheEntry *entry = new(SysAttrCacheEntry, 1);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = hashmap_ensure_put(&device->sysattr_values, &path_hash_ops_free_free, new_key, value);
|
||||
*entry = (SysAttrCacheEntry) {
|
||||
.key = key,
|
||||
.value = value,
|
||||
.error = error,
|
||||
};
|
||||
|
||||
r = hashmap_ensure_put(&device->sysattr_values, &sysattr_cache_hash_ops, entry->key, entry);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TAKE_PTR(new_key);
|
||||
|
||||
return 0;
|
||||
TAKE_PTR(entry);
|
||||
return 1; /* cached */
|
||||
}
|
||||
|
||||
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
|
||||
const char *k = NULL, *value;
|
||||
int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error) {
|
||||
return device_cache_sysattr_value_full(device, key, value, error, /* ignore_uevent = */ true);
|
||||
}
|
||||
|
||||
static int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
|
||||
SysAttrCacheEntry *entry;
|
||||
|
||||
assert(device);
|
||||
assert(key);
|
||||
|
||||
value = hashmap_get2(device->sysattr_values, key, (void **) &k);
|
||||
if (!k)
|
||||
return -ESTALE; /* We have not read the attribute. */
|
||||
if (!value)
|
||||
return -ENOENT; /* We have looked up the attribute before and it did not exist. */
|
||||
entry = hashmap_get(device->sysattr_values, key);
|
||||
if (!entry)
|
||||
return -ENOANO; /* We have not read the attribute. */
|
||||
if (!entry->value) {
|
||||
/* We have looked up the attribute before and failed. Return the cached error code. */
|
||||
assert(entry->error > 0);
|
||||
return -entry->error;
|
||||
}
|
||||
if (ret_value)
|
||||
*ret_value = value;
|
||||
*ret_value = entry->value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We cache all sysattr lookups. If an attribute does not exist, it is stored
|
||||
* with a NULL value in the cache, otherwise the returned string is stored */
|
||||
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
|
||||
_cleanup_free_ char *value = NULL, *path = NULL;
|
||||
int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **ret_resolved, int *ret_fd) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(path);
|
||||
|
||||
const char *syspath;
|
||||
struct stat statbuf;
|
||||
r = sd_device_get_syspath(device, &syspath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Here, CHASE_PREFIX_ROOT is borrowed. If the flag is set or the specified path is relative, then
|
||||
* the path will be prefixed with the syspath. Note, we do not pass CHASE_PREFIX_ROOT flag with
|
||||
* syspath as root to chase(), but we manually concatenate the specified path with syspath before
|
||||
* calling chase(). Otherwise, we cannot set/get attributes of parent or sibling devices. */
|
||||
_cleanup_free_ char *prefixed = NULL;
|
||||
if (FLAGS_SET(flags, CHASE_PREFIX_ROOT) || !path_is_absolute(path)) {
|
||||
prefixed = path_join(syspath, path);
|
||||
if (!prefixed)
|
||||
return -ENOMEM;
|
||||
path = prefixed;
|
||||
flags &= ~CHASE_PREFIX_ROOT;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
r = chase(path, /* root = */ NULL, CHASE_NO_AUTOFS | flags, &resolved, ret_fd ? &fd : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Refuse to reading/writing files outside of sysfs. */
|
||||
if (!path_startswith(resolved, "/sys/"))
|
||||
return -EINVAL;
|
||||
|
||||
if (ret_resolved) {
|
||||
/* Always return relative path. */
|
||||
r = path_make_relative(syspath, resolved, ret_resolved);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_fd)
|
||||
*ret_fd = TAKE_FD(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
|
||||
_cleanup_free_ char *resolved = NULL, *value = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert_return(device, -EINVAL);
|
||||
assert_return(sysattr, -EINVAL);
|
||||
|
||||
/* look for possibly already cached result */
|
||||
/* Look for possibly already cached result. */
|
||||
r = device_get_cached_sysattr_value(device, sysattr, ret_value);
|
||||
if (r != -ESTALE)
|
||||
if (r != -ENOANO)
|
||||
return r;
|
||||
|
||||
/* Special cases: read the symlink and return the last component of the value. Some core links return
|
||||
* only the last element of the target path, these are just values, the paths should not be exposed. */
|
||||
if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
|
||||
_cleanup_free_ char *prefixed = NULL;
|
||||
const char *syspath;
|
||||
|
||||
r = sd_device_get_syspath(device, &syspath);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = path_join(syspath, sysattr);
|
||||
if (!path)
|
||||
prefixed = path_join(syspath, sysattr);
|
||||
if (!prefixed)
|
||||
return -ENOMEM;
|
||||
|
||||
if (lstat(path, &statbuf) < 0) {
|
||||
int k;
|
||||
|
||||
r = -errno;
|
||||
|
||||
/* remember that we could not access the sysattr */
|
||||
k = device_cache_sysattr_value(device, sysattr, NULL);
|
||||
if (k < 0)
|
||||
log_device_debug_errno(device, k,
|
||||
"sd-device: failed to cache attribute '%s' with NULL, ignoring: %m",
|
||||
sysattr);
|
||||
|
||||
return r;
|
||||
} else if (S_ISLNK(statbuf.st_mode)) {
|
||||
/* Some core links return only the last element of the target path,
|
||||
* these are just values, the paths should not be exposed. */
|
||||
if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
|
||||
r = readlink_value(path, &value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
return -EINVAL;
|
||||
} else if (S_ISDIR(statbuf.st_mode))
|
||||
/* skip directories */
|
||||
return -EISDIR;
|
||||
else if (!(statbuf.st_mode & S_IRUSR))
|
||||
/* skip non-readable files */
|
||||
return -EPERM;
|
||||
else {
|
||||
size_t size;
|
||||
|
||||
/* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to
|
||||
* also get the size of the result. See issue #20025. */
|
||||
r = read_full_virtual_file(path, &value, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* drop trailing newlines */
|
||||
while (size > 0 && strchr(NEWLINE, value[--size]))
|
||||
value[size] = '\0';
|
||||
r = readlink_value(prefixed, &value);
|
||||
if (r != -EINVAL) /* -EINVAL means the path is not a symlink. */
|
||||
goto cache_result;
|
||||
}
|
||||
|
||||
r = device_chase(device, sysattr, CHASE_PREFIX_ROOT, &resolved, &fd);
|
||||
if (r < 0)
|
||||
goto cache_result;
|
||||
|
||||
/* Look for cached result again with the resolved path. */
|
||||
r = device_get_cached_sysattr_value(device, resolved, ret_value);
|
||||
if (r != -ENOANO)
|
||||
return r;
|
||||
|
||||
/* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to also get the
|
||||
* size of the result. See issue #20025. */
|
||||
size_t size;
|
||||
r = read_virtual_file_fd(fd, SIZE_MAX, &value, &size);
|
||||
if (r < 0)
|
||||
goto cache_result;
|
||||
|
||||
delete_trailing_chars(value, NEWLINE);
|
||||
r = 0;
|
||||
|
||||
cache_result:
|
||||
if (r == -ENOMEM)
|
||||
return r; /* Do not cache -ENOMEM, as the failure may be transient. */
|
||||
|
||||
if (!resolved) {
|
||||
/* If we have not or could not chase the path, assume 'sysattr' is normalized. */
|
||||
resolved = strdup(sysattr);
|
||||
if (!resolved)
|
||||
return RET_GATHER(r, -ENOMEM);
|
||||
}
|
||||
|
||||
int k = device_cache_sysattr_value_full(device, resolved, value, -r, /* ignore_uevent = */ false);
|
||||
if (k < 0) {
|
||||
if (r < 0)
|
||||
log_device_debug_errno(device, k,
|
||||
"sd-device: failed to cache error code (%i) in reading attribute '%s', ignoring: %m",
|
||||
-r, resolved);
|
||||
else {
|
||||
/* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
|
||||
* sysattr value is critical unlike the other places. */
|
||||
r = device_cache_sysattr_value(device, sysattr, value);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(device, r,
|
||||
log_device_debug_errno(device, k,
|
||||
"sd-device: failed to cache attribute '%s' with '%s'%s: %m",
|
||||
sysattr, value, ret_value ? "" : ", ignoring");
|
||||
resolved, value, ret_value ? "" : ", ignoring");
|
||||
if (ret_value)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
return k;
|
||||
}
|
||||
|
||||
if (ret_value)
|
||||
return r;
|
||||
}
|
||||
assert(k > 0);
|
||||
|
||||
if (ret_value && r >= 0)
|
||||
*ret_value = value;
|
||||
|
||||
/* device_cache_sysattr_value_full() takes 'resolved' and 'value' on success. */
|
||||
TAKE_PTR(resolved);
|
||||
TAKE_PTR(value);
|
||||
return 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) {
|
||||
@@ -2527,19 +2621,22 @@ int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
|
||||
return parse_boolean(value);
|
||||
}
|
||||
|
||||
static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
|
||||
_cleanup_free_ char *key = NULL;
|
||||
static int device_remove_cached_sysattr_value(sd_device *device, const char *sysattr) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(_key);
|
||||
assert(sysattr);
|
||||
|
||||
free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
r = device_chase(device, sysattr, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &resolved, /* ret_fd = */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, resolved));
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
|
||||
_cleanup_free_ char *value = NULL, *path = NULL;
|
||||
const char *syspath;
|
||||
size_t len;
|
||||
_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value) {
|
||||
int r;
|
||||
|
||||
assert_return(device, -EINVAL);
|
||||
@@ -2547,52 +2644,43 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
|
||||
|
||||
/* Set the attribute and save it in the cache. */
|
||||
|
||||
if (!_value) {
|
||||
if (!value)
|
||||
/* If input value is NULL, then clear cache and not write anything. */
|
||||
device_remove_cached_sysattr_value(device, sysattr);
|
||||
return 0;
|
||||
}
|
||||
return device_remove_cached_sysattr_value(device, sysattr);
|
||||
|
||||
r = sd_device_get_syspath(device, &syspath);
|
||||
if (r < 0)
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
r = device_chase(device, sysattr, CHASE_PREFIX_ROOT, &resolved, &fd);
|
||||
if (r < 0) {
|
||||
/* On failure, clear cache entry, hopefully, 'sysattr' is normalized. */
|
||||
sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, sysattr));
|
||||
return r;
|
||||
|
||||
path = path_join(syspath, sysattr);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
len = strlen(_value);
|
||||
|
||||
/* drop trailing newlines */
|
||||
while (len > 0 && strchr(NEWLINE, _value[len - 1]))
|
||||
len--;
|
||||
}
|
||||
|
||||
/* value length is limited to 4k */
|
||||
if (len > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
value = strndup(_value, len);
|
||||
if (!value)
|
||||
_cleanup_free_ char *copied = strndup(value, 4096);
|
||||
if (!copied)
|
||||
return -ENOMEM;
|
||||
|
||||
r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
|
||||
/* drop trailing newlines */
|
||||
delete_trailing_chars(copied, NEWLINE);
|
||||
|
||||
r = write_string_file_fd(fd, copied, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_AVOID_NEWLINE);
|
||||
if (r < 0) {
|
||||
/* On failure, clear cache entry, as we do not know how it fails. */
|
||||
device_remove_cached_sysattr_value(device, sysattr);
|
||||
sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, resolved));
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Do not cache action string written into uevent file. */
|
||||
if (streq(sysattr, "uevent"))
|
||||
return 0;
|
||||
|
||||
r = device_cache_sysattr_value(device, sysattr, value);
|
||||
r = device_cache_sysattr_value(device, resolved, copied, 0);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(device, r,
|
||||
"sd-device: failed to cache attribute '%s' with '%s', ignoring: %m",
|
||||
sysattr, value);
|
||||
else
|
||||
TAKE_PTR(value);
|
||||
"sd-device: failed to cache written attribute '%s' with '%s', ignoring: %m",
|
||||
resolved, copied);
|
||||
else if (r > 0) {
|
||||
TAKE_PTR(resolved);
|
||||
TAKE_PTR(copied);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2605,10 +2693,8 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
|
||||
assert_return(device, -EINVAL);
|
||||
assert_return(sysattr, -EINVAL);
|
||||
|
||||
if (!format) {
|
||||
device_remove_cached_sysattr_value(device, sysattr);
|
||||
return 0;
|
||||
}
|
||||
if (!format)
|
||||
return device_remove_cached_sysattr_value(device, sysattr);
|
||||
|
||||
va_start(ap, format);
|
||||
r = vasprintf(&value, format, ap);
|
||||
@@ -2621,16 +2707,7 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
|
||||
}
|
||||
|
||||
_public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
|
||||
const char *s;
|
||||
|
||||
assert_return(device, -EINVAL);
|
||||
|
||||
s = device_action_to_string(action);
|
||||
if (!s)
|
||||
return -EINVAL;
|
||||
|
||||
/* This uses the simple no-UUID interface of kernel < 4.13 */
|
||||
return sd_device_set_sysattr_value(device, "uevent", s);
|
||||
return sd_device_trigger_with_uuid(device, action, NULL);
|
||||
}
|
||||
|
||||
_public_ int sd_device_trigger_with_uuid(
|
||||
@@ -2644,10 +2721,6 @@ _public_ int sd_device_trigger_with_uuid(
|
||||
|
||||
assert_return(device, -EINVAL);
|
||||
|
||||
/* If no one wants to know the UUID, use the simple interface from pre-4.13 times */
|
||||
if (!ret_uuid)
|
||||
return sd_device_trigger(device, action);
|
||||
|
||||
s = device_action_to_string(action);
|
||||
if (!s)
|
||||
return -EINVAL;
|
||||
@@ -2662,6 +2735,7 @@ _public_ int sd_device_trigger_with_uuid(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_uuid)
|
||||
*ret_uuid = u;
|
||||
return 0;
|
||||
}
|
||||
|
@@ -2,12 +2,21 @@
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "event-source.h"
|
||||
#include "event-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
|
||||
#define SI_FLAG_FORWARD (INT32_C(1) << 30)
|
||||
#define SI_FLAG_POSITIVE (INT32_C(1) << 29)
|
||||
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
event_source_hash_ops,
|
||||
void, trivial_hash_func, trivial_compare_func,
|
||||
sd_event_source, sd_event_source_disable_unref);
|
||||
|
||||
int event_reset_time(
|
||||
sd_event *e,
|
||||
sd_event_source **s,
|
||||
@@ -154,19 +163,73 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
|
||||
|
||||
int event_add_child_pidref(
|
||||
sd_event *e,
|
||||
sd_event_source **s,
|
||||
sd_event_source **ret,
|
||||
const PidRef *pid,
|
||||
int options,
|
||||
sd_event_child_handler_t callback,
|
||||
void *userdata) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(e);
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
return -ESRCH;
|
||||
|
||||
if (pid->fd >= 0)
|
||||
return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
|
||||
if (pidref_is_remote(pid))
|
||||
return -EREMOTE;
|
||||
|
||||
return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
|
||||
if (pid->fd < 0)
|
||||
return sd_event_add_child(e, ret, pid->pid, options, callback, userdata);
|
||||
|
||||
_cleanup_close_ int copy_fd = fcntl(pid->fd, F_DUPFD_CLOEXEC, 3);
|
||||
if (copy_fd < 0)
|
||||
return -errno;
|
||||
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
|
||||
r = sd_event_add_child_pidfd(e, &s, copy_fd, options, callback, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_child_pidfd_own(s, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TAKE_FD(copy_fd);
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(s);
|
||||
else {
|
||||
r = sd_event_source_set_floating(s, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_source_get_child_pidref(sd_event_source *s, PidRef *ret) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
|
||||
pid_t pid;
|
||||
r = sd_event_source_get_child_pid(s, &pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
int pidfd = sd_event_source_get_child_pidfd(s);
|
||||
if (pidfd < 0)
|
||||
return pidfd;
|
||||
|
||||
/* Note, we don't actually duplicate the fd here, i.e. we do not pass ownership of this PidRef to the caller */
|
||||
*ret = (PidRef) {
|
||||
.pid = pid,
|
||||
.fd = pidfd,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
|
||||
@@ -177,3 +240,90 @@ dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
|
||||
assert_se(sd_event_now(e, CLOCK_MONOTONIC, &ts->monotonic) >= 0);
|
||||
return ts;
|
||||
}
|
||||
|
||||
void event_source_unref_many(sd_event_source **array, size_t n) {
|
||||
FOREACH_ARRAY(v, array, n)
|
||||
sd_event_source_unref(*v);
|
||||
|
||||
free(array);
|
||||
}
|
||||
|
||||
static int event_forward_signal_callback(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) {
|
||||
sd_event_source *child = ASSERT_PTR(userdata);
|
||||
|
||||
assert(ssi);
|
||||
|
||||
siginfo_t si = {
|
||||
.si_signo = ssi->ssi_signo,
|
||||
/* We include some extra information to indicate the signal was forwarded and originally a positive
|
||||
* value since we can only set negative values ourselves as positive values are prohibited by the
|
||||
* kernel. */
|
||||
.si_code = (ssi->ssi_code & (SI_FLAG_FORWARD|SI_FLAG_POSITIVE)) ? INT_MIN :
|
||||
(ssi->ssi_code >= 0 ? (-ssi->ssi_code - 1) | SI_FLAG_POSITIVE | SI_FLAG_FORWARD : ssi->ssi_code | SI_FLAG_FORWARD),
|
||||
.si_errno = ssi->ssi_errno,
|
||||
};
|
||||
|
||||
/* The following fields are implemented as macros, hence we cannot use compound initialization for them. */
|
||||
si.si_pid = ssi->ssi_pid;
|
||||
si.si_uid = ssi->ssi_uid;
|
||||
si.si_int = ssi->ssi_int;
|
||||
si.si_ptr = UINT64_TO_PTR(ssi->ssi_ptr);
|
||||
|
||||
return sd_event_source_send_child_signal(child, ssi->ssi_signo, &si, /* flags = */ 0);
|
||||
}
|
||||
|
||||
static void event_forward_signal_destroy(void *userdata) {
|
||||
sd_event_source *child = ASSERT_PTR(userdata);
|
||||
sd_event_source_unref(child);
|
||||
}
|
||||
|
||||
int event_forward_signals(
|
||||
sd_event *e,
|
||||
sd_event_source *child,
|
||||
const int *signals,
|
||||
size_t n_signals,
|
||||
sd_event_source ***ret_sources,
|
||||
size_t *ret_n_sources) {
|
||||
|
||||
sd_event_source **sources = NULL;
|
||||
size_t n_sources = 0;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(sources, n_sources, event_source_unref_many);
|
||||
|
||||
assert(e);
|
||||
assert(child);
|
||||
assert(child->type == SOURCE_CHILD);
|
||||
assert(signals || n_signals == 0);
|
||||
assert(ret_sources);
|
||||
assert(ret_n_sources);
|
||||
|
||||
if (n_signals == 0) {
|
||||
*ret_sources = NULL;
|
||||
*ret_n_sources = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sources = new0(sd_event_source*, n_signals);
|
||||
if (!sources)
|
||||
return -ENOMEM;
|
||||
|
||||
FOREACH_ARRAY(sig, signals, n_signals) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
|
||||
r = sd_event_add_signal(e, &s, *sig | SD_EVENT_SIGNAL_PROCMASK, event_forward_signal_callback, child);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_destroy_callback(s, event_forward_signal_destroy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sd_event_source_ref(child);
|
||||
sources[n_sources++] = TAKE_PTR(s);
|
||||
}
|
||||
|
||||
*ret_sources = TAKE_PTR(sources);
|
||||
*ret_n_sources = n_sources;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -5,8 +5,11 @@
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "hash-funcs.h"
|
||||
#include "pidref.h"
|
||||
|
||||
extern const struct hash_ops event_source_hash_ops;
|
||||
|
||||
int event_reset_time(
|
||||
sd_event *e,
|
||||
sd_event_source **s,
|
||||
@@ -37,4 +40,10 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
|
||||
|
||||
int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
|
||||
|
||||
int event_source_get_child_pidref(sd_event_source *s, PidRef *ret);
|
||||
|
||||
dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts);
|
||||
|
||||
void event_source_unref_many(sd_event_source **array, size_t n);
|
||||
|
||||
int event_forward_signals(sd_event *e, sd_event_source *child, const int *signals, size_t n_signals, sd_event_source ***ret_sources, size_t *ret_n_sources);
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include "sd-daemon.h"
|
||||
#include "sd-event.h"
|
||||
@@ -24,12 +25,11 @@
|
||||
#include "memory-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_threads.h"
|
||||
#include "missing_wait.h"
|
||||
#include "origin-id.h"
|
||||
#include "path-util.h"
|
||||
#include "prioq.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "prioq.h"
|
||||
#include "process-util.h"
|
||||
#include "psi-util.h"
|
||||
#include "set.h"
|
||||
@@ -43,11 +43,10 @@
|
||||
|
||||
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
|
||||
|
||||
static bool EVENT_SOURCE_WATCH_PIDFD(sd_event_source *s) {
|
||||
static bool EVENT_SOURCE_WATCH_PIDFD(const sd_event_source *s) {
|
||||
/* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
|
||||
return s &&
|
||||
s->type == SOURCE_CHILD &&
|
||||
s->child.pidfd >= 0 &&
|
||||
s->child.options == WEXITED;
|
||||
}
|
||||
|
||||
@@ -432,7 +431,7 @@ _public_ int sd_event_new(sd_event** ret) {
|
||||
|
||||
if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
|
||||
log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 %s 2^63 us will be logged every 5s.",
|
||||
special_glyph(SPECIAL_GLYPH_ELLIPSIS));
|
||||
glyph(GLYPH_ELLIPSIS));
|
||||
e->profile_delays = true;
|
||||
}
|
||||
|
||||
@@ -988,7 +987,7 @@ static void source_disconnect(sd_event_source *s) {
|
||||
s->event->n_online_child_sources--;
|
||||
}
|
||||
|
||||
(void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
|
||||
assert_se(hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)));
|
||||
}
|
||||
|
||||
if (EVENT_SOURCE_WATCH_PIDFD(s))
|
||||
@@ -1088,12 +1087,11 @@ static sd_event_source* source_free(sd_event_source *s) {
|
||||
/* Eventually the kernel will do this automatically for us, but for now let's emulate this (unreliably) in userspace. */
|
||||
|
||||
if (s->child.process_owned) {
|
||||
assert(s->child.pid > 0);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (!s->child.exited) {
|
||||
if (s->child.pidfd >= 0)
|
||||
r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
|
||||
else
|
||||
r = RET_NERRNO(kill(s->child.pid, SIGKILL));
|
||||
if (r < 0 && r != -ESRCH)
|
||||
log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
|
||||
s->child.pid);
|
||||
@@ -1103,10 +1101,7 @@ static sd_event_source* source_free(sd_event_source *s) {
|
||||
siginfo_t si = {};
|
||||
|
||||
/* Reap the child if we can */
|
||||
if (s->child.pidfd >= 0)
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
|
||||
else
|
||||
(void) waitid(P_PID, s->child.pid, &si, WEXITED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1896,15 +1891,15 @@ _public_ int sd_event_trim_memory(void) {
|
||||
LOG_MESSAGE("Memory trimming took %s, returned %s to OS.",
|
||||
FORMAT_TIMESPAN(period, 0),
|
||||
FORMAT_BYTES(l)),
|
||||
"MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR,
|
||||
"TRIMMED_BYTES=%zu", l,
|
||||
"TRIMMED_USEC=" USEC_FMT, period);
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
|
||||
LOG_ITEM("TRIMMED_BYTES=%zu", l),
|
||||
LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
|
||||
#else
|
||||
log_struct(LOG_DEBUG,
|
||||
LOG_MESSAGE("Memory trimming took %s.",
|
||||
FORMAT_TIMESPAN(period, 0)),
|
||||
"MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR,
|
||||
"TRIMMED_USEC=" USEC_FMT, period);
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
|
||||
LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@@ -2730,9 +2725,11 @@ _public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t *ret) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(s->type == SOURCE_IO, -EDOM);
|
||||
assert_return(s->pending, -ENODATA);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
|
||||
if (!s->pending)
|
||||
return -ENODATA;
|
||||
|
||||
*ret = s->io.revents;
|
||||
return 0;
|
||||
}
|
||||
@@ -2999,13 +2996,13 @@ static int event_source_online(
|
||||
|
||||
case SOURCE_CHILD:
|
||||
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
|
||||
/* yes, we have pidfd */
|
||||
/* yes, we can rely on pidfd */
|
||||
|
||||
r = source_child_pidfd_register(s, enabled);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
/* no pidfd, or something other to watch for than WEXITED */
|
||||
/* something other to watch for than WEXITED */
|
||||
|
||||
r = event_make_signal_data(s->event, SIGCHLD, NULL);
|
||||
if (r < 0) {
|
||||
@@ -3198,9 +3195,6 @@ _public_ int sd_event_source_get_child_pidfd(sd_event_source *s) {
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
|
||||
if (s->child.pidfd < 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return s->child.pidfd;
|
||||
}
|
||||
|
||||
@@ -3209,51 +3203,26 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
assert_return(SIGNAL_VALID(sig), -EINVAL);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
/* If we already have seen indication the process exited refuse sending a signal early. This way we
|
||||
* can be sure we don't accidentally kill the wrong process on PID reuse when pidfds are not
|
||||
* available. */
|
||||
/* If we already have seen indication the process exited refuse sending a signal early. */
|
||||
if (s->child.exited)
|
||||
return -ESRCH;
|
||||
assert(!s->child.waited);
|
||||
|
||||
if (s->child.pidfd >= 0) {
|
||||
/* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the structure here. */
|
||||
siginfo_t copy;
|
||||
|
||||
/* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the
|
||||
* structure here */
|
||||
if (si)
|
||||
copy = *si;
|
||||
|
||||
if (pidfd_send_signal(s->child.pidfd, sig, si ? © : NULL, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flags are only supported for pidfd_send_signal(), not for rt_sigqueueinfo(), hence let's refuse
|
||||
* this here. */
|
||||
if (flags != 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (si) {
|
||||
/* We use rt_sigqueueinfo() only if siginfo_t is specified. */
|
||||
siginfo_t copy = *si;
|
||||
|
||||
if (rt_sigqueueinfo(s->child.pid, sig, ©) < 0)
|
||||
return -errno;
|
||||
} else if (kill(s->child.pid, sig) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? © : NULL, flags));
|
||||
}
|
||||
|
||||
_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
|
||||
if (s->child.pidfd < 0)
|
||||
return -EOPNOTSUPP;
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
return s->child.pidfd_owned;
|
||||
}
|
||||
@@ -3262,9 +3231,7 @@ _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
|
||||
assert_return(s, -EINVAL);
|
||||
assert_return(s->type == SOURCE_CHILD, -EDOM);
|
||||
assert_return(!event_origin_changed(s->event), -ECHILD);
|
||||
|
||||
if (s->child.pidfd < 0)
|
||||
return -EOPNOTSUPP;
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
s->child.pidfd_owned = own;
|
||||
return 0;
|
||||
@@ -3723,9 +3690,9 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
||||
|
||||
e->need_process_child = false;
|
||||
|
||||
/* So, this is ugly. We iteratively invoke waitid() with P_PID + WNOHANG for each PID we wait
|
||||
* for, instead of using P_ALL. This is because we only want to get child information of very
|
||||
* specific child processes, and not all of them. We might not have processed the SIGCHLD event
|
||||
/* So, this is ugly. We iteratively invoke waitid() + WNOHANG with each child process we shall wait for,
|
||||
* instead of using P_ALL. This is because we only want to get child information of very specific
|
||||
* child processes, and not all of them. We might not have processed the SIGCHLD event
|
||||
* of a previous invocation and we don't want to maintain a unbounded *per-child* event queue,
|
||||
* hence we really don't want anything flushed out of the kernel's queue that we don't care
|
||||
* about. Since this is O(n) this means that if you have a lot of processes you probably want
|
||||
@@ -3736,6 +3703,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
||||
|
||||
HASHMAP_FOREACH(s, e->child_sources) {
|
||||
assert(s->type == SOURCE_CHILD);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (s->priority > threshold)
|
||||
continue;
|
||||
@@ -3755,23 +3723,21 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
|
||||
continue;
|
||||
|
||||
zero(s->child.siginfo);
|
||||
if (waitid(P_PID, s->child.pid, &s->child.siginfo,
|
||||
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo,
|
||||
WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
|
||||
return negative_errno();
|
||||
|
||||
if (s->child.siginfo.si_pid != 0) {
|
||||
bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
|
||||
bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
|
||||
|
||||
if (zombie)
|
||||
s->child.exited = true;
|
||||
|
||||
if (!zombie && (s->child.options & WEXITED)) {
|
||||
/* If the child isn't dead then let's immediately remove the state
|
||||
* change from the queue, since there's no benefit in leaving it
|
||||
* queued. */
|
||||
else if (s->child.options & WEXITED) {
|
||||
/* If the child isn't dead then let's immediately remove the state change
|
||||
* from the queue, since there's no benefit in leaving it queued. */
|
||||
|
||||
assert(s->child.options & (WSTOPPED|WCONTINUED));
|
||||
(void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
|
||||
}
|
||||
|
||||
r = source_set_pending(s, true);
|
||||
@@ -3792,6 +3758,7 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
|
||||
assert(e);
|
||||
assert(s);
|
||||
assert(s->type == SOURCE_CHILD);
|
||||
assert(s->child.pidfd >= 0);
|
||||
|
||||
if (s->pending)
|
||||
return 0;
|
||||
@@ -3802,14 +3769,19 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
|
||||
if (!EVENT_SOURCE_WATCH_PIDFD(s))
|
||||
return 0;
|
||||
|
||||
/* Note that pidfd would also generate EPOLLHUP when the process gets reaped. But at this point we
|
||||
* only permit EPOLLIN, under the assumption that upon EPOLLHUP the child source should already
|
||||
* be set to pending, and we would have returned early above. */
|
||||
assert(!s->child.exited);
|
||||
|
||||
zero(s->child.siginfo);
|
||||
if (waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
|
||||
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
|
||||
return -errno;
|
||||
|
||||
if (s->child.siginfo.si_pid == 0)
|
||||
return 0;
|
||||
|
||||
if (IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED))
|
||||
if (SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code))
|
||||
s->child.exited = true;
|
||||
|
||||
return source_set_pending(s, true);
|
||||
@@ -4222,15 +4194,13 @@ static int source_dispatch(sd_event_source *s) {
|
||||
break;
|
||||
|
||||
case SOURCE_CHILD: {
|
||||
bool zombie;
|
||||
|
||||
zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
|
||||
bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
|
||||
|
||||
r = s->child.callback(s, &s->child.siginfo, s->userdata);
|
||||
|
||||
/* Now, reap the PID for good. */
|
||||
if (zombie) {
|
||||
(void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
|
||||
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED);
|
||||
s->child.waited = true;
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "namespace-util.h"
|
||||
#include "process-util.h"
|
||||
#include "sha256.h"
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-id128.h"
|
||||
@@ -14,15 +15,15 @@
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
#include "keyring-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_threads.h"
|
||||
#include "path-util.h"
|
||||
#include "random-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
|
||||
_public_ char *sd_id128_to_string(sd_id128_t id, char s[static SD_ID128_STRING_MAX]) {
|
||||
size_t k = 0;
|
||||
|
||||
assert_return(s, NULL);
|
||||
@@ -38,7 +39,7 @@ _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID12
|
||||
return s;
|
||||
}
|
||||
|
||||
_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]) {
|
||||
_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[static SD_ID128_UUID_STRING_MAX]) {
|
||||
size_t k = 0;
|
||||
|
||||
assert_return(s, NULL);
|
||||
@@ -214,8 +215,10 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
|
||||
|
||||
key = request_key("user", "invocation_id", NULL, 0);
|
||||
if (key == -1) {
|
||||
/* Keyring support not available? No invocation key stored? */
|
||||
if (IN_SET(errno, ENOSYS, ENOKEY))
|
||||
/* Keyring support not available? Keyring access locked down? No invocation key stored? */
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) ||
|
||||
ERRNO_IS_PRIVILEGE(errno) ||
|
||||
errno == ENOKEY)
|
||||
return -ENXIO;
|
||||
|
||||
return -errno;
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
/* This is a private header; never even think of including this directly! */
|
||||
|
||||
#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__)
|
||||
#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__) && !defined(__clang_analyzer__)
|
||||
# error "Do not include _sd-common.h directly; it is a private header."
|
||||
#endif
|
||||
|
||||
|
@@ -23,11 +23,10 @@
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "_sd-common.h"
|
||||
#include "sd-event.h"
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
typedef struct sd_device sd_device;
|
||||
|
@@ -23,14 +23,13 @@
|
||||
#include <net/ethernet.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "_sd-common.h"
|
||||
#include "sd-device.h"
|
||||
#include "sd-dhcp-duid.h"
|
||||
#include "sd-dhcp6-lease.h"
|
||||
#include "sd-dhcp6-option.h"
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
enum {
|
||||
|
@@ -23,9 +23,8 @@
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-dhcp6-option.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
#include "sd-dhcp6-option.h"
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
|
@@ -20,9 +20,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-dhcp6-protocol.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
#include "sd-dhcp6-protocol.h"
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
|
@@ -25,14 +25,13 @@
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "_sd-common.h"
|
||||
#include "sd-event.h"
|
||||
#include "sd-ndisc-neighbor.h"
|
||||
#include "sd-ndisc-protocol.h"
|
||||
#include "sd-ndisc-redirect.h"
|
||||
#include "sd-ndisc-router.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
typedef struct sd_ndisc sd_ndisc;
|
||||
|
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
|
||||
void* memdup(const void *p, size_t l) {
|
||||
void *ret;
|
||||
|
@@ -7,7 +7,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "assert-util.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
|
||||
#if HAS_FEATURE_MEMORY_SANITIZER
|
||||
# include <sanitizer/msan_interface.h>
|
||||
@@ -119,15 +121,6 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t s
|
||||
return malloc(size * need ?: 1);
|
||||
}
|
||||
|
||||
#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 ?: 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
|
||||
if (size_multiply_overflow(size, need))
|
||||
return NULL;
|
||||
@@ -275,5 +268,3 @@ _alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
#include "memory-util.h"
|
||||
|
30
src/libnm-systemd-shared/src/basic/assert-util.h
Normal file
30
src/libnm-systemd-shared/src/basic/assert-util.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "assert-fundamental.h"
|
||||
#include "macro.h"
|
||||
|
||||
/* Logging for various assertions */
|
||||
|
||||
void log_set_assert_return_is_critical(bool b);
|
||||
bool log_get_assert_return_is_critical(void) _pure_;
|
||||
|
||||
void log_assert_failed_return(const char *text, const char *file, int line, const char *func);
|
||||
|
||||
#define assert_log(expr, message) ((_likely_(expr)) \
|
||||
? (true) \
|
||||
: (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
|
||||
|
||||
#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)
|
@@ -7,6 +7,7 @@
|
||||
#define _INDEX_TO_MASK(type, i, uniq) \
|
||||
({ \
|
||||
int UNIQ_T(_i, uniq) = (i); \
|
||||
assert(UNIQ_T(_i, uniq) >= 0); \
|
||||
assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \
|
||||
((type)1) << UNIQ_T(_i, uniq); \
|
||||
})
|
||||
|
@@ -263,8 +263,7 @@ int cg_get_attribute_as_bool(const char *controller, const char *path, const cha
|
||||
int cg_get_owner(const char *path, uid_t *ret_uid);
|
||||
|
||||
int cg_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags);
|
||||
int cg_get_xattr(const char *path, const char *name, void *value, size_t size);
|
||||
int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
|
||||
int cg_get_xattr_malloc(const char *path, const char *name, char **ret, size_t *ret_size);
|
||||
/* Returns negative on error, and 0 or 1 on success for the bool value */
|
||||
int cg_get_xattr_bool(const char *path, const char *name);
|
||||
int cg_remove_xattr(const char *path, const char *name);
|
||||
@@ -312,10 +311,6 @@ int cg_mask_supported_subtree(const char *root, CGroupMask *ret);
|
||||
int cg_mask_from_string(const char *s, CGroupMask *ret);
|
||||
int cg_mask_to_string(CGroupMask mask, char **ret);
|
||||
|
||||
int cg_kernel_controllers(Set **controllers);
|
||||
|
||||
bool cg_ns_supported(void);
|
||||
bool cg_freezer_supported(void);
|
||||
bool cg_kill_supported(void);
|
||||
|
||||
int cg_all_unified(void);
|
||||
@@ -329,9 +324,6 @@ static inline int cg_unified(void) {
|
||||
const char* cgroup_controller_to_string(CGroupController c) _const_;
|
||||
CGroupController cgroup_controller_from_string(const char *s) _pure_;
|
||||
|
||||
bool is_cgroup_fs(const struct statfs *s);
|
||||
bool fd_is_cgroup_fs(int fd);
|
||||
|
||||
typedef enum ManagedOOMMode {
|
||||
MANAGED_OOM_AUTO,
|
||||
MANAGED_OOM_KILL,
|
||||
|
@@ -4,13 +4,13 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "bitfield.h"
|
||||
#include "chattr-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
|
||||
|
@@ -2,7 +2,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -41,14 +40,14 @@ typedef enum ChattrApplyFlags {
|
||||
} ChattrApplyFlags;
|
||||
|
||||
int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
|
||||
static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *previous) {
|
||||
return chattr_full(dir_fd, path, value, mask, previous, NULL, 0);
|
||||
static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask) {
|
||||
return chattr_full(dir_fd, path, value, mask, NULL, NULL, 0);
|
||||
}
|
||||
static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
|
||||
return chattr_full(fd, NULL, value, mask, previous, NULL, 0);
|
||||
static inline int chattr_fd(int fd, unsigned value, unsigned mask) {
|
||||
return chattr_full(fd, NULL, value, mask, NULL, NULL, 0);
|
||||
}
|
||||
static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) {
|
||||
return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0);
|
||||
static inline int chattr_path(const char *path, unsigned value, unsigned mask) {
|
||||
return chattr_full(AT_FDCWD, path, value, mask, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
int read_attr_fd(int fd, unsigned *ret);
|
||||
|
@@ -9,6 +9,9 @@
|
||||
|
||||
int parse_devnum(const char *s, dev_t *ret);
|
||||
|
||||
#define DEVNUM_MAJOR_MAX ((UINT32_C(1) << 12) - 1U)
|
||||
#define DEVNUM_MINOR_MAX ((UINT32_C(1) << 20) - 1U)
|
||||
|
||||
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
|
||||
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
|
||||
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
|
||||
@@ -18,14 +21,14 @@ int parse_devnum(const char *s, dev_t *ret);
|
||||
#define DEVICE_MAJOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 12); \
|
||||
_x >= _y && _x <= DEVNUM_MAJOR_MAX; \
|
||||
\
|
||||
})
|
||||
|
||||
#define DEVICE_MINOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 20); \
|
||||
_x >= _y && _x <= DEVNUM_MINOR_MAX; \
|
||||
})
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
|
||||
@@ -54,3 +57,6 @@ static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
|
||||
static inline bool devnum_is_zero(dev_t d) {
|
||||
return major(d) == 0 && minor(d) == 0;
|
||||
}
|
||||
|
||||
#define DEVNUM_TO_PTR(u) ((void*) (uintptr_t) (u))
|
||||
#define PTR_TO_DEVNUM(p) ((dev_t) ((uintptr_t) (p)))
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "extract-word.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
@@ -546,7 +547,7 @@ char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlag
|
||||
return NULL;
|
||||
|
||||
t = strndupa_safe(name, k);
|
||||
return getenv(t);
|
||||
return secure_getenv(t);
|
||||
};
|
||||
|
||||
return NULL;
|
||||
@@ -695,7 +696,7 @@ int replace_env_full(
|
||||
_cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
|
||||
const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
|
||||
_cleanup_free_ char *s = NULL;
|
||||
char ***pu, ***pb, *k;
|
||||
char ***pu, ***pb;
|
||||
size_t i, len = 0; /* len is initialized to appease gcc */
|
||||
int nest = 0, r;
|
||||
|
||||
@@ -717,33 +718,24 @@ int replace_env_full(
|
||||
|
||||
case CURLY:
|
||||
if (*e == '{') {
|
||||
k = strnappend(s, word, e-word-1);
|
||||
if (!k)
|
||||
if (!strextendn(&s, word, e-word-1))
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(s, k);
|
||||
|
||||
word = e-1;
|
||||
state = VARIABLE;
|
||||
nest++;
|
||||
|
||||
} else if (*e == '$') {
|
||||
k = strnappend(s, word, e-word);
|
||||
if (!k)
|
||||
if (!strextendn(&s, word, e-word))
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(s, k);
|
||||
|
||||
word = e+1;
|
||||
state = WORD;
|
||||
|
||||
} else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
|
||||
k = strnappend(s, word, e-word-1);
|
||||
if (!k)
|
||||
if (!strextendn(&s, word, e-word-1))
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(s, k);
|
||||
|
||||
word = e-1;
|
||||
state = VARIABLE_RAW;
|
||||
|
||||
@@ -1114,7 +1106,7 @@ int getenv_steal_erase(const char *name, char **ret) {
|
||||
* it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for
|
||||
* testing, and given that people are likely going to misuse this, be thorough) */
|
||||
|
||||
e = getenv(name);
|
||||
e = secure_getenv(name);
|
||||
if (!e) {
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
@@ -1138,25 +1130,6 @@ int getenv_steal_erase(const char *name, char **ret) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int set_full_environment(char **env) {
|
||||
int r;
|
||||
|
||||
clearenv();
|
||||
|
||||
STRV_FOREACH(e, env) {
|
||||
_cleanup_free_ char *k = NULL, *v = NULL;
|
||||
|
||||
r = split_pair(*e, "=", &k, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (setenv(k, v, /* overwrite= */ true) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
va_list ap;
|
||||
|
@@ -82,6 +82,4 @@ int getenv_path_list(const char *name, char ***ret_paths);
|
||||
|
||||
int getenv_steal_erase(const char *name, char **ret);
|
||||
|
||||
int set_full_environment(char **env);
|
||||
|
||||
int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "assert-util.h"
|
||||
#include "macro.h"
|
||||
|
||||
/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */
|
||||
|
@@ -80,10 +80,15 @@ char* cescape_length(const char *s, size_t n) {
|
||||
const char *f;
|
||||
char *r, *t;
|
||||
|
||||
/* Does C style string escaping. May be reversed with cunescape(). */
|
||||
|
||||
assert(s || n == 0);
|
||||
|
||||
/* Does C style string escaping. May be reversed with
|
||||
* cunescape(). */
|
||||
if (n == SIZE_MAX)
|
||||
n = strlen(s);
|
||||
|
||||
if (n > (SIZE_MAX - 1) / 4)
|
||||
return NULL;
|
||||
|
||||
r = new(char, n*4 + 1);
|
||||
if (!r)
|
||||
@@ -97,12 +102,6 @@ char* cescape_length(const char *s, size_t n) {
|
||||
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, bool accept_nul) {
|
||||
int r = 1;
|
||||
|
||||
@@ -449,7 +448,7 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
|
||||
char* octescape(const char *s, size_t len) {
|
||||
char *buf, *t;
|
||||
|
||||
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */
|
||||
/* Escapes \ and " chars, in \nnn style escaping. */
|
||||
|
||||
assert(s || len == 0);
|
||||
|
||||
@@ -479,13 +478,19 @@ char* octescape(const char *s, size_t len) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
char* decescape(const char *s, const char *bad, size_t len) {
|
||||
char* decescape(const char *s, size_t len, const char *bad) {
|
||||
char *buf, *t;
|
||||
|
||||
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
|
||||
|
||||
assert(s || len == 0);
|
||||
|
||||
if (len == SIZE_MAX)
|
||||
len = strlen(s);
|
||||
|
||||
if (len > (SIZE_MAX - 1) / 4)
|
||||
return NULL;
|
||||
|
||||
t = buf = new(char, len * 4 + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
@@ -8,7 +8,6 @@
|
||||
#include <uchar.h>
|
||||
|
||||
#include "string-util.h"
|
||||
#include "missing_type.h"
|
||||
|
||||
/* What characters are special in the shell? */
|
||||
/* must be escaped outside and inside double-quotes */
|
||||
@@ -41,9 +40,11 @@ typedef enum ShellEscapeFlags {
|
||||
SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */
|
||||
} ShellEscapeFlags;
|
||||
|
||||
char* cescape(const char *s);
|
||||
char* cescape_length(const char *s, size_t n);
|
||||
int cescape_char(char c, char *buf);
|
||||
char* cescape_length(const char *s, size_t n) _nonnull_if_nonzero_(1, 2);
|
||||
static inline char* cescape(const char *s) {
|
||||
return cescape_length(s, SIZE_MAX);
|
||||
}
|
||||
|
||||
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
|
||||
|
||||
@@ -65,7 +66,7 @@ static inline char* xescape(const char *s, const char *bad) {
|
||||
return xescape_full(s, bad, SIZE_MAX, 0);
|
||||
}
|
||||
char* octescape(const char *s, size_t len);
|
||||
char* decescape(const char *s, const char *bad, size_t len);
|
||||
char* decescape(const char *s, size_t len, const char *bad) _nonnull_if_nonzero_(1, 2);
|
||||
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
|
||||
|
||||
char* shell_escape(const char *s, const char *bad);
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "ether-addr-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
|
||||
@@ -85,22 +86,6 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret) {
|
||||
char *buf;
|
||||
|
||||
assert(addr);
|
||||
assert(ret);
|
||||
|
||||
buf = new(char, ETHER_ADDR_TO_STRING_MAX);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ether_addr_to_string(addr, buf);
|
||||
|
||||
*ret = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
|
||||
return memcmp(a, b, ETH_ALEN);
|
||||
}
|
||||
|
@@ -72,7 +72,6 @@ extern const struct hash_ops hw_addr_hash_ops_free;
|
||||
|
||||
#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_to_string_alloc(const struct ether_addr *addr, char **ret);
|
||||
/* Use only as function argument, never stand-alone! */
|
||||
#define ETHER_ADDR_TO_STR(addr) ether_addr_to_string((addr), (char[ETHER_ADDR_TO_STRING_MAX]){})
|
||||
|
||||
|
@@ -2,9 +2,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#if WANT_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
#include <linux/kcmp.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
@@ -17,6 +15,7 @@
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_fcntl.h"
|
||||
#include "missing_fs.h"
|
||||
@@ -29,7 +28,6 @@
|
||||
#include "sort-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
/* The maximum number of iterations in the loop to close descriptors in the fallback case
|
||||
* when /proc/self/fd/ is inaccessible. */
|
||||
@@ -1003,13 +1001,13 @@ int fd_verify_safe_flags_full(int fd, int extra_flags) {
|
||||
if (flags < 0)
|
||||
return -errno;
|
||||
|
||||
unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
|
||||
unexpected_flags = flags & ~(O_ACCMODE_STRICT|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
|
||||
if (unexpected_flags != 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
|
||||
"Unexpected flags set for extrinsic fd: 0%o",
|
||||
(unsigned) unexpected_flags);
|
||||
|
||||
return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */
|
||||
return flags & (O_ACCMODE_STRICT | extra_flags); /* return the flags variable, but remove the noise */
|
||||
}
|
||||
|
||||
int read_nr_open(void) {
|
||||
@@ -1086,30 +1084,27 @@ int path_is_root_at(int dir_fd, const char *path) {
|
||||
}
|
||||
|
||||
int fds_are_same_mount(int fd1, int fd2) {
|
||||
STRUCT_NEW_STATX_DEFINE(st1);
|
||||
STRUCT_NEW_STATX_DEFINE(st2);
|
||||
struct statx sx1 = {}, sx2 = {}; /* explicitly initialize the struct to make msan silent. */
|
||||
int r;
|
||||
|
||||
assert(fd1 >= 0);
|
||||
assert(fd2 >= 0);
|
||||
|
||||
r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (statx(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
|
||||
return -errno;
|
||||
|
||||
r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (statx(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2) < 0)
|
||||
return -errno;
|
||||
|
||||
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
|
||||
if (!statx_inode_same(&st1.sx, &st2.sx))
|
||||
if (!statx_inode_same(&sx1, &sx2))
|
||||
return false;
|
||||
|
||||
/* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
|
||||
* kernel is used. In that case, let's assume that we do not have such spurious mount points in an
|
||||
* early boot stage, and silently skip the following check. */
|
||||
|
||||
if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) {
|
||||
if (!FLAGS_SET(sx1.stx_mask, STATX_MNT_ID)) {
|
||||
int mntid;
|
||||
|
||||
r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
|
||||
@@ -1117,11 +1112,11 @@ int fds_are_same_mount(int fd1, int fd2) {
|
||||
return r;
|
||||
assert(mntid >= 0);
|
||||
|
||||
st1.nsx.stx_mnt_id = mntid;
|
||||
st1.nsx.stx_mask |= STATX_MNT_ID;
|
||||
sx1.stx_mnt_id = mntid;
|
||||
sx1.stx_mask |= STATX_MNT_ID;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
|
||||
if (!FLAGS_SET(sx2.stx_mask, STATX_MNT_ID)) {
|
||||
int mntid;
|
||||
|
||||
r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
|
||||
@@ -1129,15 +1124,15 @@ int fds_are_same_mount(int fd1, int fd2) {
|
||||
return r;
|
||||
assert(mntid >= 0);
|
||||
|
||||
st2.nsx.stx_mnt_id = mntid;
|
||||
st2.nsx.stx_mask |= STATX_MNT_ID;
|
||||
sx2.stx_mnt_id = mntid;
|
||||
sx2.stx_mask |= STATX_MNT_ID;
|
||||
}
|
||||
|
||||
return statx_mount_same(&st1.nsx, &st2.nsx);
|
||||
return statx_mount_same(&sx1, &sx2);
|
||||
}
|
||||
|
||||
const char* accmode_to_string(int flags) {
|
||||
switch (flags & O_ACCMODE) {
|
||||
switch (flags & O_ACCMODE_STRICT) {
|
||||
case O_RDONLY:
|
||||
return "ro";
|
||||
case O_WRONLY:
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_fcntl.h"
|
||||
#include "stdio-util.h"
|
||||
|
||||
|
@@ -244,17 +244,13 @@ static int write_string_file_atomic_at(
|
||||
}
|
||||
|
||||
r = fopen_temporary_at(dir_fd, fn, &f, &p);
|
||||
if (call_label_ops_post)
|
||||
/* If fopen_temporary_at() failed in the above, propagate the error code, and ignore failures
|
||||
* in label_ops_post(). */
|
||||
RET_GATHER(r, label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (call_label_ops_post) {
|
||||
call_label_ops_post = false;
|
||||
|
||||
r = label_ops_post(fileno(f), /* path= */ NULL, /* created= */ true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = write_string_stream_full(f, line, flags, ts);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@@ -277,9 +273,6 @@ static int write_string_file_atomic_at(
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (call_label_ops_post)
|
||||
(void) label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f);
|
||||
|
||||
if (f)
|
||||
(void) unlinkat(dir_fd, p, 0);
|
||||
return r;
|
||||
@@ -293,24 +286,27 @@ int write_string_file_full(
|
||||
const struct timespec *ts,
|
||||
const char *label_fn) {
|
||||
|
||||
bool call_label_ops_post = false, made_file = false;
|
||||
bool made_file = false;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert(fn);
|
||||
assert(dir_fd == AT_FDCWD || dir_fd >= 0);
|
||||
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_MKDIR_0755) {
|
||||
assert(fn);
|
||||
|
||||
r = mkdirat_parents(dir_fd, fn, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (flags & WRITE_STRING_FILE_ATOMIC) {
|
||||
assert(fn);
|
||||
assert(flags & WRITE_STRING_FILE_CREATE);
|
||||
|
||||
r = write_string_file_atomic_at(dir_fd, fn, line, flags, ts);
|
||||
@@ -320,7 +316,15 @@ int write_string_file_full(
|
||||
return r;
|
||||
}
|
||||
|
||||
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
|
||||
if (isempty(fn))
|
||||
r = fd = fd_reopen(
|
||||
ASSERT_FD(dir_fd), O_CLOEXEC | O_NOCTTY |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY));
|
||||
else {
|
||||
mode_t mode = write_string_file_flags_to_mode(flags);
|
||||
bool call_label_ops_post = false;
|
||||
|
||||
if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
|
||||
r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
|
||||
@@ -330,8 +334,7 @@ int write_string_file_full(
|
||||
call_label_ops_post = true;
|
||||
}
|
||||
|
||||
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
|
||||
fd = openat_report_new(
|
||||
r = fd = openat_report_new(
|
||||
dir_fd, fn, O_CLOEXEC | O_NOCTTY |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
|
||||
@@ -339,18 +342,13 @@ int write_string_file_full(
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
|
||||
mode,
|
||||
&made_file);
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto fail;
|
||||
if (call_label_ops_post)
|
||||
/* If openat_report_new() failed in the above, propagate the error code, and ignore
|
||||
* failures in label_ops_post(). */
|
||||
RET_GATHER(r, label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file));
|
||||
}
|
||||
|
||||
if (call_label_ops_post) {
|
||||
call_label_ops_post = false;
|
||||
|
||||
r = label_ops_post(fd, /* path= */ NULL, made_file);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = take_fdopen_unlocked(&fd, "w", &f);
|
||||
if (r < 0)
|
||||
@@ -366,9 +364,6 @@ int write_string_file_full(
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (call_label_ops_post)
|
||||
(void) label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file);
|
||||
|
||||
if (made_file)
|
||||
(void) unlinkat(dir_fd, fn, 0);
|
||||
|
||||
@@ -380,7 +375,7 @@ fail:
|
||||
|
||||
/* OK, the operation failed, but let's see if the right contents in place already. If so, eat up the
|
||||
* error. */
|
||||
if (verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
|
||||
if (verify_file_at(dir_fd, fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
|
||||
return 0;
|
||||
|
||||
return r;
|
||||
@@ -442,7 +437,6 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
|
||||
size_t l, k;
|
||||
int r;
|
||||
|
||||
assert(fn);
|
||||
assert(blob);
|
||||
|
||||
l = strlen(blob);
|
||||
@@ -454,7 +448,7 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
r = fopen_unlocked_at(dir_fd, fn, "re", 0, &f);
|
||||
r = fopen_unlocked_at(dir_fd, strempty(fn), "re", 0, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -474,7 +468,13 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
|
||||
int read_virtual_file_at(
|
||||
int dir_fd,
|
||||
const char *filename,
|
||||
size_t max_size,
|
||||
char **ret_contents,
|
||||
size_t *ret_size) {
|
||||
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t n, size;
|
||||
int n_retries;
|
||||
@@ -491,11 +491,23 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
|
||||
* max_size specifies a limit on the bytes read. If max_size is SIZE_MAX, the full file is read. If
|
||||
* the full file is too large to read, an error is returned. For other values of max_size, *partial
|
||||
* contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
|
||||
* partial success, 1 if untruncated contents were read. */
|
||||
* partial success, 1 if untruncated contents were read.
|
||||
*
|
||||
* Rule: for kernfs files using "seq_file" → use regular read_full_file_at()
|
||||
* for kernfs files using "raw" → use read_virtual_file_at()
|
||||
*/
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX);
|
||||
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
if (isempty(filename))
|
||||
fd = fd_reopen(ASSERT_FD(dir_fd), O_RDONLY | O_NOCTTY | O_CLOEXEC);
|
||||
else
|
||||
fd = RET_NERRNO(openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC));
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
|
||||
n_retries = 3;
|
||||
|
||||
@@ -619,31 +631,6 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
|
||||
return !truncated;
|
||||
}
|
||||
|
||||
int read_virtual_file_at(
|
||||
int dir_fd,
|
||||
const char *filename,
|
||||
size_t max_size,
|
||||
char **ret_contents,
|
||||
size_t *ret_size) {
|
||||
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
|
||||
if (!filename) {
|
||||
if (dir_fd == AT_FDCWD)
|
||||
return -EBADF;
|
||||
|
||||
return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size);
|
||||
}
|
||||
|
||||
fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return read_virtual_file_fd(fd, max_size, ret_contents, ret_size);
|
||||
}
|
||||
|
||||
int read_full_stream_full(
|
||||
FILE *f,
|
||||
const char *filename,
|
||||
@@ -723,7 +710,7 @@ int read_full_stream_full(
|
||||
size_t k;
|
||||
|
||||
/* If we shall fail when reading overly large data, then read exactly one byte more than the
|
||||
* specified size at max, since that'll tell us if there's anymore data beyond the limit*/
|
||||
* specified size at max, since that'll tell us if there's anymore data beyond the limit. */
|
||||
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
|
||||
n_next = size + 1;
|
||||
|
||||
@@ -902,74 +889,46 @@ int script_get_shebang_interpreter(const char *path, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
int get_proc_field(const char *path, const char *key, char **ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(terminator);
|
||||
assert(filename);
|
||||
assert(pattern);
|
||||
assert(field);
|
||||
/* Retrieve one field from a file like /proc/self/status. "key" matches the beginning of the line
|
||||
* and should not include whitespace or the delimiter (':').
|
||||
* Whitespaces after the ':' will be skipped. Only the first element is returned
|
||||
* (i.e. for /proc/meminfo line "MemTotal: 1024 kB" -> return "1024"). */
|
||||
|
||||
r = read_full_virtual_file(filename, &status, NULL);
|
||||
assert(path);
|
||||
assert(key);
|
||||
|
||||
r = fopen_unlocked(path, "re", &f);
|
||||
if (r == -ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
t = status;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
|
||||
do {
|
||||
bool pattern_ok;
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -ENODATA;
|
||||
|
||||
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--;
|
||||
}
|
||||
|
||||
f = strdupcspn(t, terminator);
|
||||
if (!f)
|
||||
char *l = startswith(line, key);
|
||||
if (l && *l == ':') {
|
||||
if (ret) {
|
||||
char *s = strdupcspn(skip_leading_chars(l + 1, " \t"), WHITESPACE);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*field = f;
|
||||
*ret = s;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DIR* xopendirat(int dir_fd, const char *name, int flags) {
|
||||
|
@@ -56,6 +56,9 @@ int write_string_file_full(int dir_fd, const char *fn, const char *line, WriteSt
|
||||
static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) {
|
||||
return write_string_file_full(dir_fd, fn, line, flags, NULL, NULL);
|
||||
}
|
||||
static inline int write_string_file_fd(int dir_fd, const char *line, WriteStringFileFlags flags) {
|
||||
return write_string_file_at(dir_fd, NULL, line, flags);
|
||||
}
|
||||
static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
|
||||
return write_string_file_at(AT_FDCWD, fn, line, flags);
|
||||
}
|
||||
@@ -75,8 +78,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
|
||||
return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
|
||||
}
|
||||
|
||||
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
|
||||
int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
|
||||
static inline int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
|
||||
return read_virtual_file_at(fd, NULL, max_size, ret_contents, ret_size);
|
||||
}
|
||||
static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
|
||||
return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size);
|
||||
}
|
||||
@@ -90,13 +95,10 @@ static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_siz
|
||||
}
|
||||
|
||||
int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_extra_nl);
|
||||
static inline int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
|
||||
return verify_file_at(AT_FDCWD, fn, blob, accept_extra_nl);
|
||||
}
|
||||
|
||||
int script_get_shebang_interpreter(const char *path, char **ret);
|
||||
|
||||
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
|
||||
int get_proc_field(const char *path, const char *key, char **ret);
|
||||
|
||||
DIR* xopendirat(int dir_fd, const char *name, int flags);
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "format-ifname.h"
|
||||
#include "log.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
|
||||
|
@@ -62,5 +62,4 @@ char* format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
|
||||
finish:
|
||||
buf[l-1] = 0;
|
||||
return buf;
|
||||
|
||||
}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/magic.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/file.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/magic.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
@@ -77,6 +77,11 @@ int rmdir_parents(const char *path, const char *stop) {
|
||||
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
|
||||
int r;
|
||||
|
||||
assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
|
||||
assert(oldpath);
|
||||
assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
|
||||
assert(newpath);
|
||||
|
||||
/* Try the ideal approach first */
|
||||
if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
|
||||
return 0;
|
||||
@@ -783,7 +788,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */
|
||||
/* Don't deallocate if there's nothing to deallocate or if the file is linked elsewhere */
|
||||
if (st.st_blocks == 0 || st.st_nlink > 0)
|
||||
return 0;
|
||||
|
||||
@@ -1031,7 +1036,7 @@ int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_
|
||||
|
||||
if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
|
||||
return -EINVAL;
|
||||
if ((flags & O_ACCMODE) != O_RDONLY)
|
||||
if ((flags & O_ACCMODE_STRICT) != O_RDONLY)
|
||||
return -EINVAL;
|
||||
|
||||
/* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
|
||||
@@ -1254,7 +1259,7 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
}
|
||||
|
||||
if (FLAGS_SET(xopen_flags, XO_NOCOW)) {
|
||||
r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
|
||||
r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL);
|
||||
if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
|
||||
goto error;
|
||||
}
|
||||
|
@@ -172,3 +172,12 @@ static inline int at_flags_normalize_nofollow(int flags) {
|
||||
flags |= AT_SYMLINK_NOFOLLOW;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline int at_flags_normalize_follow(int flags) {
|
||||
if (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW)) {
|
||||
assert(!FLAGS_SET(flags, AT_SYMLINK_FOLLOW));
|
||||
flags &= ~AT_SYMLINK_NOFOLLOW;
|
||||
} else
|
||||
flags |= AT_SYMLINK_FOLLOW;
|
||||
return flags;
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ bool emoji_enabled(void) {
|
||||
return cached_emoji_enabled;
|
||||
}
|
||||
|
||||
const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
|
||||
const char* glyph_full(Glyph code, bool force_utf) {
|
||||
|
||||
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
|
||||
* conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
|
||||
@@ -32,133 +32,139 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
|
||||
* http://git.altlinux.org/people/legion/packages/kbd.git?p=kbd.git;a=blob;f=data/consolefonts/README.eurlatgr
|
||||
*/
|
||||
|
||||
static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = {
|
||||
static const char* const draw_table[2][_GLYPH_MAX] = {
|
||||
/* ASCII fallback */
|
||||
[false] = {
|
||||
[SPECIAL_GLYPH_TREE_VERTICAL] = "| ",
|
||||
[SPECIAL_GLYPH_TREE_BRANCH] = "|-",
|
||||
[SPECIAL_GLYPH_TREE_RIGHT] = "`-",
|
||||
[SPECIAL_GLYPH_TREE_SPACE] = " ",
|
||||
[SPECIAL_GLYPH_TREE_TOP] = ",-",
|
||||
[SPECIAL_GLYPH_VERTICAL_DOTTED] = ":",
|
||||
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = "-",
|
||||
[SPECIAL_GLYPH_HORIZONTAL_FAT] = "=",
|
||||
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
|
||||
[SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
|
||||
[SPECIAL_GLYPH_WHITE_CIRCLE] = "*",
|
||||
[SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "x",
|
||||
[SPECIAL_GLYPH_CIRCLE_ARROW] = "*",
|
||||
[SPECIAL_GLYPH_BULLET] = "*",
|
||||
[SPECIAL_GLYPH_MU] = "u",
|
||||
[SPECIAL_GLYPH_CHECK_MARK] = "+",
|
||||
[SPECIAL_GLYPH_CROSS_MARK] = "-",
|
||||
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
|
||||
[SPECIAL_GLYPH_DARK_SHADE] = "X",
|
||||
[SPECIAL_GLYPH_FULL_BLOCK] = "#",
|
||||
[SPECIAL_GLYPH_SIGMA] = "S",
|
||||
[SPECIAL_GLYPH_ARROW_UP] = "^",
|
||||
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
|
||||
[SPECIAL_GLYPH_ARROW_LEFT] = "<-",
|
||||
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
|
||||
[SPECIAL_GLYPH_ELLIPSIS] = "...",
|
||||
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]",
|
||||
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
|
||||
[SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}",
|
||||
[SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)",
|
||||
[SPECIAL_GLYPH_NEUTRAL_SMILEY] = ":-|",
|
||||
[SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
|
||||
[SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{",
|
||||
[SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[",
|
||||
[SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,",
|
||||
[SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
|
||||
[SPECIAL_GLYPH_RECYCLING] = "~",
|
||||
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
|
||||
[SPECIAL_GLYPH_SPARKLES] = "*",
|
||||
[SPECIAL_GLYPH_LOW_BATTERY] = "!",
|
||||
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
|
||||
[SPECIAL_GLYPH_RED_CIRCLE] = "o",
|
||||
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
|
||||
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
|
||||
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
|
||||
[SPECIAL_GLYPH_SUPERHERO] = "S",
|
||||
[SPECIAL_GLYPH_IDCARD] = "@",
|
||||
[GLYPH_SPACE] = " ",
|
||||
[GLYPH_TREE_VERTICAL] = "| ",
|
||||
[GLYPH_TREE_BRANCH] = "|-",
|
||||
[GLYPH_TREE_RIGHT] = "`-",
|
||||
[GLYPH_TREE_SPACE] = " ",
|
||||
[GLYPH_TREE_TOP] = ",-",
|
||||
[GLYPH_VERTICAL_DOTTED] = ":",
|
||||
[GLYPH_HORIZONTAL_DOTTED] = "-",
|
||||
[GLYPH_HORIZONTAL_FAT] = "=",
|
||||
[GLYPH_TRIANGULAR_BULLET] = ">",
|
||||
[GLYPH_BLACK_CIRCLE] = "*",
|
||||
[GLYPH_WHITE_CIRCLE] = "*",
|
||||
[GLYPH_MULTIPLICATION_SIGN] = "x",
|
||||
[GLYPH_CIRCLE_ARROW] = "*",
|
||||
[GLYPH_BULLET] = "*",
|
||||
[GLYPH_MU] = "u",
|
||||
[GLYPH_CHECK_MARK] = "+",
|
||||
[GLYPH_CROSS_MARK] = "-",
|
||||
[GLYPH_LIGHT_SHADE] = "-",
|
||||
[GLYPH_DARK_SHADE] = "X",
|
||||
[GLYPH_FULL_BLOCK] = "#",
|
||||
[GLYPH_SIGMA] = "S",
|
||||
[GLYPH_ARROW_UP] = "^",
|
||||
[GLYPH_ARROW_DOWN] = "v",
|
||||
[GLYPH_ARROW_LEFT] = "<-",
|
||||
[GLYPH_ARROW_RIGHT] = "->",
|
||||
[GLYPH_ELLIPSIS] = "...",
|
||||
[GLYPH_EXTERNAL_LINK] = "[LNK]",
|
||||
[GLYPH_ECSTATIC_SMILEY] = ":-]",
|
||||
[GLYPH_HAPPY_SMILEY] = ":-}",
|
||||
[GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)",
|
||||
[GLYPH_NEUTRAL_SMILEY] = ":-|",
|
||||
[GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
|
||||
[GLYPH_UNHAPPY_SMILEY] = ":-{",
|
||||
[GLYPH_DEPRESSED_SMILEY] = ":-[",
|
||||
[GLYPH_LOCK_AND_KEY] = "o-,",
|
||||
[GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
|
||||
[GLYPH_RECYCLING] = "~",
|
||||
[GLYPH_DOWNLOAD] = "\\",
|
||||
[GLYPH_SPARKLES] = "*",
|
||||
[GLYPH_LOW_BATTERY] = "!",
|
||||
[GLYPH_WARNING_SIGN] = "!",
|
||||
[GLYPH_RED_CIRCLE] = "o",
|
||||
[GLYPH_YELLOW_CIRCLE] = "o",
|
||||
[GLYPH_BLUE_CIRCLE] = "o",
|
||||
[GLYPH_GREEN_CIRCLE] = "o",
|
||||
[GLYPH_SUPERHERO] = "S",
|
||||
[GLYPH_IDCARD] = "@",
|
||||
[GLYPH_HOME] = "^",
|
||||
},
|
||||
|
||||
/* UTF-8 */
|
||||
[true] = {
|
||||
/* This exists to allow more consistent handling of optional whitespace */
|
||||
[GLYPH_SPACE] = " ",
|
||||
|
||||
/* The following are multiple glyphs in both ASCII and in UNICODE */
|
||||
[SPECIAL_GLYPH_TREE_VERTICAL] = u8"│ ",
|
||||
[SPECIAL_GLYPH_TREE_BRANCH] = u8"├─",
|
||||
[SPECIAL_GLYPH_TREE_RIGHT] = u8"└─",
|
||||
[SPECIAL_GLYPH_TREE_SPACE] = u8" ",
|
||||
[SPECIAL_GLYPH_TREE_TOP] = u8"┌─",
|
||||
[GLYPH_TREE_VERTICAL] = UTF8("│ "),
|
||||
[GLYPH_TREE_BRANCH] = UTF8("├─"),
|
||||
[GLYPH_TREE_RIGHT] = UTF8("└─"),
|
||||
[GLYPH_TREE_SPACE] = UTF8(" "),
|
||||
[GLYPH_TREE_TOP] = UTF8("┌─"),
|
||||
|
||||
/* Single glyphs in both cases */
|
||||
[SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"┆",
|
||||
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = u8"┄",
|
||||
[SPECIAL_GLYPH_HORIZONTAL_FAT] = u8"━",
|
||||
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"‣",
|
||||
[SPECIAL_GLYPH_BLACK_CIRCLE] = u8"●",
|
||||
[SPECIAL_GLYPH_WHITE_CIRCLE] = u8"○",
|
||||
[SPECIAL_GLYPH_MULTIPLICATION_SIGN] = u8"×",
|
||||
[SPECIAL_GLYPH_CIRCLE_ARROW] = u8"↻",
|
||||
[SPECIAL_GLYPH_BULLET] = u8"•",
|
||||
[SPECIAL_GLYPH_MU] = u8"μ", /* actually called: GREEK SMALL LETTER MU */
|
||||
[SPECIAL_GLYPH_CHECK_MARK] = u8"✓",
|
||||
[SPECIAL_GLYPH_CROSS_MARK] = u8"✗", /* actually called: BALLOT X */
|
||||
[SPECIAL_GLYPH_LIGHT_SHADE] = u8"░",
|
||||
[SPECIAL_GLYPH_DARK_SHADE] = u8"▒",
|
||||
[SPECIAL_GLYPH_FULL_BLOCK] = u8"█",
|
||||
[SPECIAL_GLYPH_SIGMA] = u8"Σ",
|
||||
[SPECIAL_GLYPH_ARROW_UP] = u8"↑", /* actually called: UPWARDS ARROW */
|
||||
[SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */
|
||||
[GLYPH_VERTICAL_DOTTED] = UTF8("┆"),
|
||||
[GLYPH_HORIZONTAL_DOTTED] = UTF8("┄"),
|
||||
[GLYPH_HORIZONTAL_FAT] = UTF8("━"),
|
||||
[GLYPH_TRIANGULAR_BULLET] = UTF8("‣"),
|
||||
[GLYPH_BLACK_CIRCLE] = UTF8("●"),
|
||||
[GLYPH_WHITE_CIRCLE] = UTF8("○"),
|
||||
[GLYPH_MULTIPLICATION_SIGN] = UTF8("×"),
|
||||
[GLYPH_CIRCLE_ARROW] = UTF8("↻"),
|
||||
[GLYPH_BULLET] = UTF8("•"),
|
||||
[GLYPH_MU] = UTF8("μ"), /* actually called: GREEK SMALL LETTER MU */
|
||||
[GLYPH_CHECK_MARK] = UTF8("✓"),
|
||||
[GLYPH_CROSS_MARK] = UTF8("✗"), /* actually called: BALLOT X */
|
||||
[GLYPH_LIGHT_SHADE] = UTF8("░"),
|
||||
[GLYPH_DARK_SHADE] = UTF8("▒"),
|
||||
[GLYPH_FULL_BLOCK] = UTF8("█"),
|
||||
[GLYPH_SIGMA] = UTF8("Σ"),
|
||||
[GLYPH_ARROW_UP] = UTF8("↑"), /* actually called: UPWARDS ARROW */
|
||||
[GLYPH_ARROW_DOWN] = UTF8("↓"), /* actually called: DOWNWARDS ARROW */
|
||||
|
||||
/* Single glyph in Unicode, two in ASCII */
|
||||
[SPECIAL_GLYPH_ARROW_LEFT] = u8"←", /* actually called: LEFTWARDS ARROW */
|
||||
[SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */
|
||||
[GLYPH_ARROW_LEFT] = UTF8("←"), /* actually called: LEFTWARDS ARROW */
|
||||
[GLYPH_ARROW_RIGHT] = UTF8("→"), /* actually called: RIGHTWARDS ARROW */
|
||||
|
||||
/* Single glyph in Unicode, three in ASCII */
|
||||
[SPECIAL_GLYPH_ELLIPSIS] = u8"…", /* actually called: HORIZONTAL ELLIPSIS */
|
||||
[GLYPH_ELLIPSIS] = UTF8("…"), /* actually called: HORIZONTAL ELLIPSIS */
|
||||
|
||||
/* Three glyphs in Unicode, five in ASCII */
|
||||
[SPECIAL_GLYPH_EXTERNAL_LINK] = u8"[🡕]", /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */
|
||||
[GLYPH_EXTERNAL_LINK] = UTF8("[🡕]"), /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */
|
||||
|
||||
/* These smileys are a single glyph in Unicode, and three in ASCII */
|
||||
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = u8"😇", /* actually called: SMILING FACE WITH HALO */
|
||||
[SPECIAL_GLYPH_HAPPY_SMILEY] = u8"😀", /* actually called: GRINNING FACE */
|
||||
[SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = u8"🙂", /* actually called: SLIGHTLY SMILING FACE */
|
||||
[SPECIAL_GLYPH_NEUTRAL_SMILEY] = u8"😐", /* actually called: NEUTRAL FACE */
|
||||
[SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = u8"🙁", /* actually called: SLIGHTLY FROWNING FACE */
|
||||
[SPECIAL_GLYPH_UNHAPPY_SMILEY] = u8"😨", /* actually called: FEARFUL FACE */
|
||||
[SPECIAL_GLYPH_DEPRESSED_SMILEY] = u8"🤢", /* actually called: NAUSEATED FACE */
|
||||
[GLYPH_ECSTATIC_SMILEY] = UTF8("😇"), /* actually called: SMILING FACE WITH HALO */
|
||||
[GLYPH_HAPPY_SMILEY] = UTF8("😀"), /* actually called: GRINNING FACE */
|
||||
[GLYPH_SLIGHTLY_HAPPY_SMILEY] = UTF8("🙂"), /* actually called: SLIGHTLY SMILING FACE */
|
||||
[GLYPH_NEUTRAL_SMILEY] = UTF8("😐"), /* actually called: NEUTRAL FACE */
|
||||
[GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = UTF8("🙁"), /* actually called: SLIGHTLY FROWNING FACE */
|
||||
[GLYPH_UNHAPPY_SMILEY] = UTF8("😨"), /* actually called: FEARFUL FACE */
|
||||
[GLYPH_DEPRESSED_SMILEY] = UTF8("🤢"), /* actually called: NAUSEATED FACE */
|
||||
|
||||
/* This emoji is a single character cell glyph in Unicode, and three in ASCII */
|
||||
[SPECIAL_GLYPH_LOCK_AND_KEY] = u8"🔐", /* actually called: CLOSED LOCK WITH KEY */
|
||||
[GLYPH_LOCK_AND_KEY] = UTF8("🔐"), /* actually called: CLOSED LOCK WITH KEY */
|
||||
|
||||
/* This emoji is a single character cell glyph in Unicode, and two in ASCII */
|
||||
[SPECIAL_GLYPH_TOUCH] = u8"👆", /* actually called: BACKHAND INDEX POINTING UP */
|
||||
[GLYPH_TOUCH] = UTF8("👆"), /* actually called: BACKHAND INDEX POINTING UP */
|
||||
|
||||
/* These four emojis are single character cell glyphs in Unicode and also in ASCII. */
|
||||
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */
|
||||
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
|
||||
[SPECIAL_GLYPH_SPARKLES] = u8"✨",
|
||||
[SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫",
|
||||
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
|
||||
[SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
|
||||
[SPECIAL_GLYPH_WORLD] = u8"🌍",
|
||||
[GLYPH_RECYCLING] = UTF8("♻️"), /* actually called: UNIVERSAL RECYCLNG SYMBOL */
|
||||
[GLYPH_DOWNLOAD] = UTF8("⤵️"), /* actually called: RIGHT ARROW CURVING DOWN */
|
||||
[GLYPH_SPARKLES] = UTF8("✨"),
|
||||
[GLYPH_LOW_BATTERY] = UTF8("🪫"),
|
||||
[GLYPH_WARNING_SIGN] = UTF8("⚠️"),
|
||||
[GLYPH_COMPUTER_DISK] = UTF8("💽"),
|
||||
[GLYPH_WORLD] = UTF8("🌍"),
|
||||
|
||||
[SPECIAL_GLYPH_RED_CIRCLE] = u8"🔴",
|
||||
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
|
||||
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
|
||||
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
|
||||
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
|
||||
[SPECIAL_GLYPH_IDCARD] = u8"🪪",
|
||||
[GLYPH_RED_CIRCLE] = UTF8("🔴"),
|
||||
[GLYPH_YELLOW_CIRCLE] = UTF8("🟡"),
|
||||
[GLYPH_BLUE_CIRCLE] = UTF8("🔵"),
|
||||
[GLYPH_GREEN_CIRCLE] = UTF8("🟢"),
|
||||
[GLYPH_SUPERHERO] = UTF8("🦸"),
|
||||
[GLYPH_IDCARD] = UTF8("🪪"),
|
||||
[GLYPH_HOME] = UTF8("🏠"),
|
||||
},
|
||||
};
|
||||
|
||||
if (code < 0)
|
||||
return NULL;
|
||||
|
||||
assert(code < _SPECIAL_GLYPH_MAX);
|
||||
return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
|
||||
assert(code < _GLYPH_MAX);
|
||||
return draw_table[force_utf || (code >= _GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
|
||||
}
|
||||
|
@@ -6,73 +6,79 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
typedef enum SpecialGlyph {
|
||||
SPECIAL_GLYPH_TREE_VERTICAL,
|
||||
SPECIAL_GLYPH_TREE_BRANCH,
|
||||
SPECIAL_GLYPH_TREE_RIGHT,
|
||||
SPECIAL_GLYPH_TREE_SPACE,
|
||||
SPECIAL_GLYPH_TREE_TOP,
|
||||
SPECIAL_GLYPH_VERTICAL_DOTTED,
|
||||
SPECIAL_GLYPH_HORIZONTAL_DOTTED,
|
||||
SPECIAL_GLYPH_HORIZONTAL_FAT,
|
||||
SPECIAL_GLYPH_TRIANGULAR_BULLET,
|
||||
SPECIAL_GLYPH_BLACK_CIRCLE,
|
||||
SPECIAL_GLYPH_WHITE_CIRCLE,
|
||||
SPECIAL_GLYPH_MULTIPLICATION_SIGN,
|
||||
SPECIAL_GLYPH_CIRCLE_ARROW,
|
||||
SPECIAL_GLYPH_BULLET,
|
||||
SPECIAL_GLYPH_MU,
|
||||
SPECIAL_GLYPH_CHECK_MARK,
|
||||
SPECIAL_GLYPH_CROSS_MARK,
|
||||
SPECIAL_GLYPH_LIGHT_SHADE,
|
||||
SPECIAL_GLYPH_DARK_SHADE,
|
||||
SPECIAL_GLYPH_FULL_BLOCK,
|
||||
SPECIAL_GLYPH_SIGMA,
|
||||
SPECIAL_GLYPH_ARROW_UP,
|
||||
SPECIAL_GLYPH_ARROW_DOWN,
|
||||
SPECIAL_GLYPH_ARROW_LEFT,
|
||||
SPECIAL_GLYPH_ARROW_RIGHT,
|
||||
SPECIAL_GLYPH_ELLIPSIS,
|
||||
SPECIAL_GLYPH_EXTERNAL_LINK,
|
||||
_SPECIAL_GLYPH_FIRST_EMOJI,
|
||||
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
|
||||
SPECIAL_GLYPH_HAPPY_SMILEY,
|
||||
SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY,
|
||||
SPECIAL_GLYPH_NEUTRAL_SMILEY,
|
||||
SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
|
||||
SPECIAL_GLYPH_UNHAPPY_SMILEY,
|
||||
SPECIAL_GLYPH_DEPRESSED_SMILEY,
|
||||
SPECIAL_GLYPH_LOCK_AND_KEY,
|
||||
SPECIAL_GLYPH_TOUCH,
|
||||
SPECIAL_GLYPH_RECYCLING,
|
||||
SPECIAL_GLYPH_DOWNLOAD,
|
||||
SPECIAL_GLYPH_SPARKLES,
|
||||
SPECIAL_GLYPH_LOW_BATTERY,
|
||||
SPECIAL_GLYPH_WARNING_SIGN,
|
||||
SPECIAL_GLYPH_COMPUTER_DISK,
|
||||
SPECIAL_GLYPH_WORLD,
|
||||
SPECIAL_GLYPH_RED_CIRCLE,
|
||||
SPECIAL_GLYPH_YELLOW_CIRCLE,
|
||||
SPECIAL_GLYPH_BLUE_CIRCLE,
|
||||
SPECIAL_GLYPH_GREEN_CIRCLE,
|
||||
SPECIAL_GLYPH_SUPERHERO,
|
||||
SPECIAL_GLYPH_IDCARD,
|
||||
_SPECIAL_GLYPH_MAX,
|
||||
_SPECIAL_GLYPH_INVALID = -EINVAL,
|
||||
} SpecialGlyph;
|
||||
typedef enum Glyph {
|
||||
GLYPH_SPACE,
|
||||
GLYPH_TREE_VERTICAL,
|
||||
GLYPH_TREE_BRANCH,
|
||||
GLYPH_TREE_RIGHT,
|
||||
GLYPH_TREE_SPACE,
|
||||
GLYPH_TREE_TOP,
|
||||
GLYPH_VERTICAL_DOTTED,
|
||||
GLYPH_HORIZONTAL_DOTTED,
|
||||
GLYPH_HORIZONTAL_FAT,
|
||||
GLYPH_TRIANGULAR_BULLET,
|
||||
GLYPH_BLACK_CIRCLE,
|
||||
GLYPH_WHITE_CIRCLE,
|
||||
GLYPH_MULTIPLICATION_SIGN,
|
||||
GLYPH_CIRCLE_ARROW,
|
||||
GLYPH_BULLET,
|
||||
GLYPH_MU,
|
||||
GLYPH_CHECK_MARK,
|
||||
GLYPH_CROSS_MARK,
|
||||
GLYPH_LIGHT_SHADE,
|
||||
GLYPH_DARK_SHADE,
|
||||
GLYPH_FULL_BLOCK,
|
||||
GLYPH_SIGMA,
|
||||
GLYPH_ARROW_UP,
|
||||
GLYPH_ARROW_DOWN,
|
||||
GLYPH_ARROW_LEFT,
|
||||
GLYPH_ARROW_RIGHT,
|
||||
GLYPH_ELLIPSIS,
|
||||
GLYPH_EXTERNAL_LINK,
|
||||
_GLYPH_FIRST_EMOJI,
|
||||
GLYPH_ECSTATIC_SMILEY = _GLYPH_FIRST_EMOJI,
|
||||
GLYPH_HAPPY_SMILEY,
|
||||
GLYPH_SLIGHTLY_HAPPY_SMILEY,
|
||||
GLYPH_NEUTRAL_SMILEY,
|
||||
GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
|
||||
GLYPH_UNHAPPY_SMILEY,
|
||||
GLYPH_DEPRESSED_SMILEY,
|
||||
GLYPH_LOCK_AND_KEY,
|
||||
GLYPH_TOUCH,
|
||||
GLYPH_RECYCLING,
|
||||
GLYPH_DOWNLOAD,
|
||||
GLYPH_SPARKLES,
|
||||
GLYPH_LOW_BATTERY,
|
||||
GLYPH_WARNING_SIGN,
|
||||
GLYPH_COMPUTER_DISK,
|
||||
GLYPH_WORLD,
|
||||
GLYPH_RED_CIRCLE,
|
||||
GLYPH_YELLOW_CIRCLE,
|
||||
GLYPH_BLUE_CIRCLE,
|
||||
GLYPH_GREEN_CIRCLE,
|
||||
GLYPH_SUPERHERO,
|
||||
GLYPH_IDCARD,
|
||||
GLYPH_HOME,
|
||||
_GLYPH_MAX,
|
||||
_GLYPH_INVALID = -EINVAL,
|
||||
} Glyph;
|
||||
|
||||
bool emoji_enabled(void);
|
||||
|
||||
const char* special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
|
||||
const char* glyph_full(Glyph code, bool force_utf) _const_;
|
||||
|
||||
static inline const char* special_glyph(SpecialGlyph code) {
|
||||
return special_glyph_full(code, false);
|
||||
static inline const char* glyph(Glyph code) {
|
||||
return glyph_full(code, false);
|
||||
}
|
||||
|
||||
static inline const char* special_glyph_check_mark(bool b) {
|
||||
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
|
||||
static inline const char* optional_glyph(Glyph code) {
|
||||
return emoji_enabled() ? glyph(code) : "";
|
||||
}
|
||||
|
||||
static inline const char* special_glyph_check_mark_space(bool b) {
|
||||
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " ";
|
||||
static inline const char* glyph_check_mark(bool b) {
|
||||
return b ? glyph(GLYPH_CHECK_MARK) : glyph(GLYPH_CROSS_MARK);
|
||||
}
|
||||
|
||||
static inline const char* glyph_check_mark_space(bool b) {
|
||||
return b ? glyph(GLYPH_CHECK_MARK) : " ";
|
||||
}
|
||||
|
@@ -10,13 +10,21 @@ void string_hash_func(const char *p, struct siphash *state) {
|
||||
siphash24_compress(p, strlen(p) + 1, state);
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
|
||||
DEFINE_HASH_OPS(string_hash_ops,
|
||||
char, string_hash_func, string_compare_func);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
string_hash_ops_free,
|
||||
char, string_hash_func, string_compare_func, free);
|
||||
DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
string_hash_ops_value_free,
|
||||
char, string_hash_func, string_compare_func,
|
||||
void, free);
|
||||
DEFINE_HASH_OPS_FULL(
|
||||
string_hash_ops_free_free,
|
||||
char, string_hash_func, string_compare_func, free,
|
||||
void, free);
|
||||
DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
|
||||
DEFINE_HASH_OPS_FULL(
|
||||
string_hash_ops_free_strv_free,
|
||||
char, string_hash_func, string_compare_func, free,
|
||||
char*, strv_free);
|
||||
|
||||
@@ -59,10 +67,13 @@ void path_hash_func(const char *q, struct siphash *state) {
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
|
||||
DEFINE_HASH_OPS(path_hash_ops,
|
||||
char, path_hash_func, path_compare);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
path_hash_ops_free,
|
||||
char, path_hash_func, path_compare, free);
|
||||
DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
|
||||
DEFINE_HASH_OPS_FULL(
|
||||
path_hash_ops_free_free,
|
||||
char, path_hash_func, path_compare, free,
|
||||
void, free);
|
||||
|
||||
@@ -74,23 +85,19 @@ 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,
|
||||
};
|
||||
|
||||
const struct hash_ops trivial_hash_ops_free = {
|
||||
.hash = trivial_hash_func,
|
||||
.compare = trivial_compare_func,
|
||||
.free_key = free,
|
||||
};
|
||||
|
||||
const struct hash_ops trivial_hash_ops_free_free = {
|
||||
.hash = trivial_hash_func,
|
||||
.compare = trivial_compare_func,
|
||||
.free_key = free,
|
||||
.free_value = free,
|
||||
};
|
||||
DEFINE_HASH_OPS(trivial_hash_ops,
|
||||
void, trivial_hash_func, trivial_compare_func);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
trivial_hash_ops_free,
|
||||
void, trivial_hash_func, trivial_compare_func, free);
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
trivial_hash_ops_value_free,
|
||||
void, trivial_hash_func, trivial_compare_func,
|
||||
void, free);
|
||||
DEFINE_HASH_OPS_FULL(
|
||||
trivial_hash_ops_free_free,
|
||||
void, trivial_hash_func, trivial_compare_func, free,
|
||||
void, free);
|
||||
|
||||
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
|
||||
siphash24_compress_typesafe(*p, state);
|
||||
@@ -100,7 +107,12 @@ int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
|
||||
return CMP(*a, *b);
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func);
|
||||
DEFINE_HASH_OPS(uint64_hash_ops,
|
||||
uint64_t, uint64_hash_func, uint64_compare_func);
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
uint64_hash_ops_value_free,
|
||||
uint64_t, uint64_hash_func, uint64_compare_func,
|
||||
void, free);
|
||||
|
||||
#if SIZEOF_DEV_T != 8
|
||||
void devt_hash_func(const dev_t *p, struct siphash *state) {
|
||||
|
@@ -77,6 +77,7 @@ void string_hash_func(const char *p, struct siphash *state);
|
||||
#define string_compare_func strcmp
|
||||
extern const struct hash_ops string_hash_ops;
|
||||
extern const struct hash_ops string_hash_ops_free;
|
||||
extern const struct hash_ops string_hash_ops_value_free;
|
||||
extern const struct hash_ops string_hash_ops_free_free;
|
||||
extern const struct hash_ops string_hash_ops_free_strv_free;
|
||||
|
||||
@@ -91,6 +92,7 @@ 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;
|
||||
extern const struct hash_ops trivial_hash_ops_free;
|
||||
extern const struct hash_ops trivial_hash_ops_value_free;
|
||||
extern const struct hash_ops trivial_hash_ops_free_free;
|
||||
|
||||
/* 32-bit values we can always just embed in the pointer itself, but in order to support 32-bit archs we need store 64-bit
|
||||
@@ -98,6 +100,7 @@ extern const struct hash_ops trivial_hash_ops_free_free;
|
||||
void uint64_hash_func(const uint64_t *p, struct siphash *state);
|
||||
int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
|
||||
extern const struct hash_ops uint64_hash_ops;
|
||||
extern const struct hash_ops uint64_hash_ops_value_free;
|
||||
|
||||
/* On some archs dev_t is 32-bit, and on others 64-bit. And sometimes it's 64-bit on 32-bit archs, and sometimes 32-bit on
|
||||
* 64-bit archs. Yuck! */
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hashmap.h"
|
||||
#include "log.h"
|
||||
#include "logarithm.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
@@ -912,24 +913,20 @@ static void hashmap_free_no_clear(HashmapBase *h) {
|
||||
free(h);
|
||||
}
|
||||
|
||||
HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
|
||||
HashmapBase* _hashmap_free(HashmapBase *h) {
|
||||
if (h) {
|
||||
_hashmap_clear(h, default_free_key, default_free_value);
|
||||
_hashmap_clear(h);
|
||||
hashmap_free_no_clear(h);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
|
||||
free_func_t free_key, free_value;
|
||||
void _hashmap_clear(HashmapBase *h) {
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
free_key = h->hash_ops->free_key ?: default_free_key;
|
||||
free_value = h->hash_ops->free_value ?: default_free_value;
|
||||
|
||||
if (free_key || free_value) {
|
||||
if (h->hash_ops->free_key || h->hash_ops->free_value) {
|
||||
|
||||
/* If destructor calls are defined, let's destroy things defensively: let's take the item out of the
|
||||
* hash table, and only then call the destructor functions. If these destructors then try to unregister
|
||||
@@ -941,11 +938,11 @@ void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t de
|
||||
|
||||
v = _hashmap_first_key_and_value(h, true, &k);
|
||||
|
||||
if (free_key)
|
||||
free_key(k);
|
||||
if (h->hash_ops->free_key)
|
||||
h->hash_ops->free_key(k);
|
||||
|
||||
if (free_value)
|
||||
free_value(v);
|
||||
if (h->hash_ops->free_value)
|
||||
h->hash_ops->free_value(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1780,7 +1777,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) {
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return _hashmap_free(copy, NULL, NULL);
|
||||
return _hashmap_free(copy);
|
||||
|
||||
return copy;
|
||||
}
|
||||
@@ -1805,6 +1802,23 @@ char** _hashmap_get_strv(HashmapBase *h) {
|
||||
return sv;
|
||||
}
|
||||
|
||||
char** set_to_strv(Set **s) {
|
||||
assert(s);
|
||||
|
||||
/* This is similar to set_get_strv(), but invalidates the set on success. */
|
||||
|
||||
char **v = new(char*, set_size(*s) + 1);
|
||||
if (!v)
|
||||
return NULL;
|
||||
|
||||
for (char **p = v; (*p = set_steal_first(*s)); p++)
|
||||
;
|
||||
|
||||
assert(set_isempty(*s));
|
||||
*s = set_free(*s);
|
||||
return v;
|
||||
}
|
||||
|
||||
void* ordered_hashmap_next(OrderedHashmap *h, const void *key) {
|
||||
struct ordered_hashmap_entry *e;
|
||||
unsigned hash, idx;
|
||||
|
@@ -90,34 +90,15 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DE
|
||||
|
||||
#define hashmap_free_and_replace(a, b) \
|
||||
free_and_replace_full(a, b, hashmap_free)
|
||||
#define ordered_hashmap_free_and_replace(a, b) \
|
||||
free_and_replace_full(a, b, ordered_hashmap_free)
|
||||
|
||||
HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
|
||||
HashmapBase* _hashmap_free(HashmapBase *h);
|
||||
static inline Hashmap* hashmap_free(Hashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h));
|
||||
}
|
||||
static inline OrderedHashmap* ordered_hashmap_free(OrderedHashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
|
||||
}
|
||||
|
||||
static inline Hashmap* hashmap_free_free(Hashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
static inline OrderedHashmap* ordered_hashmap_free_free(OrderedHashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
|
||||
static inline Hashmap* hashmap_free_free_key(Hashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
static inline OrderedHashmap* ordered_hashmap_free_free_key(OrderedHashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
|
||||
static inline Hashmap* hashmap_free_free_free(Hashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
|
||||
}
|
||||
static inline OrderedHashmap* ordered_hashmap_free_free_free(OrderedHashmap *h) {
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
|
||||
return (void*) _hashmap_free(HASHMAP_BASE(h));
|
||||
}
|
||||
|
||||
IteratedCache* iterated_cache_free(IteratedCache *cache);
|
||||
@@ -285,33 +266,12 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void
|
||||
return _hashmap_iterate(HASHMAP_BASE(h), i, value, key);
|
||||
}
|
||||
|
||||
void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
|
||||
void _hashmap_clear(HashmapBase *h);
|
||||
static inline void hashmap_clear(Hashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
|
||||
_hashmap_clear(HASHMAP_BASE(h));
|
||||
}
|
||||
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
|
||||
}
|
||||
|
||||
static inline void hashmap_clear_free(Hashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), NULL, free);
|
||||
}
|
||||
|
||||
static inline void hashmap_clear_free_key(Hashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), free, NULL);
|
||||
}
|
||||
|
||||
static inline void hashmap_clear_free_free(Hashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), free, free);
|
||||
}
|
||||
static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
|
||||
_hashmap_clear(HASHMAP_BASE(h), free, free);
|
||||
_hashmap_clear(HASHMAP_BASE(h));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -371,27 +331,6 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
|
||||
return _hashmap_first_key(HASHMAP_BASE(h), false);
|
||||
}
|
||||
|
||||
#define hashmap_clear_with_destructor(h, f) \
|
||||
({ \
|
||||
Hashmap *_h = (h); \
|
||||
void *_item; \
|
||||
while ((_item = hashmap_steal_first(_h))) \
|
||||
f(_item); \
|
||||
_h; \
|
||||
})
|
||||
#define hashmap_free_with_destructor(h, f) \
|
||||
hashmap_free(hashmap_clear_with_destructor(h, f))
|
||||
#define ordered_hashmap_clear_with_destructor(h, f) \
|
||||
({ \
|
||||
OrderedHashmap *_h = (h); \
|
||||
void *_item; \
|
||||
while ((_item = ordered_hashmap_steal_first(_h))) \
|
||||
f(_item); \
|
||||
_h; \
|
||||
})
|
||||
#define ordered_hashmap_free_with_destructor(h, f) \
|
||||
ordered_hashmap_free(ordered_hashmap_clear_with_destructor(h, f))
|
||||
|
||||
/* no hashmap_next */
|
||||
void* ordered_hashmap_next(OrderedHashmap *h, const void *key);
|
||||
|
||||
@@ -459,20 +398,10 @@ static inline int ordered_hashmap_dump_keys_sorted(OrderedHashmap *h, void ***re
|
||||
_ORDERED_HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key);
|
||||
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_key);
|
||||
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);
|
||||
|
||||
|
@@ -17,8 +17,8 @@ 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_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
|
||||
char* hexmem(const void *p, size_t l) _nonnull_if_nonzero_(1, 2);
|
||||
int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2);
|
||||
static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
|
||||
return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
|
||||
}
|
||||
@@ -30,10 +30,10 @@ char base64char(int x) _const_;
|
||||
char urlsafe_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);
|
||||
char* base32hexmem(const void *p, size_t l, bool padding) _nonnull_if_nonzero_(1, 2);
|
||||
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len) _nonnull_if_nonzero_(1, 2);
|
||||
|
||||
ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
|
||||
ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret) _nonnull_if_nonzero_(1, 2);
|
||||
static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
|
||||
return base64mem_full(p, l, SIZE_MAX, ret);
|
||||
}
|
||||
@@ -45,9 +45,9 @@ ssize_t base64_append(
|
||||
size_t l,
|
||||
size_t margin,
|
||||
size_t width);
|
||||
int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
|
||||
int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2);
|
||||
static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
|
||||
return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
|
||||
}
|
||||
|
||||
void hexdump(FILE *f, const void *p, size_t s);
|
||||
void hexdump(FILE *f, const void *p, size_t s) _nonnull_if_nonzero_(2, 3);
|
||||
|
@@ -10,17 +10,21 @@
|
||||
#include "alloc-util.h"
|
||||
#include "env-file.h"
|
||||
#include "hostname-util.h"
|
||||
#include "log.h"
|
||||
#include "os-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
char* get_default_hostname(void) {
|
||||
char* get_default_hostname_raw(void) {
|
||||
int r;
|
||||
|
||||
/* Returns the default hostname, and leaves any ??? in place. */
|
||||
|
||||
const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
|
||||
if (e) {
|
||||
if (hostname_is_valid(e, 0))
|
||||
if (hostname_is_valid(e, VALID_HOSTNAME_QUESTION_MARK))
|
||||
return strdup(e);
|
||||
|
||||
log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e);
|
||||
}
|
||||
|
||||
@@ -29,49 +33,15 @@ char* get_default_hostname(void) {
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse os-release, ignoring: %m");
|
||||
else if (f) {
|
||||
if (hostname_is_valid(f, 0))
|
||||
if (hostname_is_valid(f, VALID_HOSTNAME_QUESTION_MARK))
|
||||
return TAKE_PTR(f);
|
||||
|
||||
log_debug("Invalid hostname in os-release, ignoring: %s", f);
|
||||
}
|
||||
|
||||
return strdup(FALLBACK_HOSTNAME);
|
||||
}
|
||||
|
||||
int gethostname_full(GetHostnameFlags flags, char **ret) {
|
||||
_cleanup_free_ char *buf = NULL, *fallback = NULL;
|
||||
struct utsname u;
|
||||
const char *s;
|
||||
|
||||
assert(ret);
|
||||
|
||||
assert_se(uname(&u) >= 0);
|
||||
|
||||
s = u.nodename;
|
||||
if (isempty(s) || streq(s, "(none)") ||
|
||||
(!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
|
||||
(FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
|
||||
if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
|
||||
return -ENXIO;
|
||||
|
||||
s = fallback = get_default_hostname();
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
|
||||
buf = strdupcspn(s, ".");
|
||||
else
|
||||
buf = strdup(s);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = TAKE_PTR(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool valid_ldh_char(char c) {
|
||||
/* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
|
||||
|
||||
@@ -116,7 +86,7 @@ bool hostname_is_valid(const char *s, ValidHostnameFlags flags) {
|
||||
hyphen = true;
|
||||
|
||||
} else {
|
||||
if (!valid_ldh_char(*p))
|
||||
if (!valid_ldh_char(*p) && (*p != '?' || !FLAGS_SET(flags, VALID_HOSTNAME_QUESTION_MARK)))
|
||||
return false;
|
||||
|
||||
dot = false;
|
||||
@@ -158,7 +128,7 @@ char* hostname_cleanup(char *s) {
|
||||
dot = false;
|
||||
hyphen = true;
|
||||
|
||||
} else if (valid_ldh_char(*p)) {
|
||||
} else if (valid_ldh_char(*p) || *p == '?') {
|
||||
*(d++) = *p;
|
||||
dot = false;
|
||||
hyphen = false;
|
||||
|
@@ -7,42 +7,14 @@
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
|
||||
typedef enum GetHostnameFlags {
|
||||
GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 0, /* accepts "localhost" or friends. */
|
||||
GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */
|
||||
GET_HOSTNAME_SHORT = 1 << 2, /* kills the FQDN part if present. */
|
||||
} GetHostnameFlags;
|
||||
|
||||
int gethostname_full(GetHostnameFlags flags, char **ret);
|
||||
static inline int gethostname_strict(char **ret) {
|
||||
return gethostname_full(0, ret);
|
||||
}
|
||||
|
||||
static inline char* gethostname_malloc(void) {
|
||||
char *s;
|
||||
|
||||
if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
|
||||
return NULL;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline char* gethostname_short_malloc(void) {
|
||||
char *s;
|
||||
|
||||
if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
|
||||
return NULL;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
char* get_default_hostname(void);
|
||||
char* get_default_hostname_raw(void);
|
||||
|
||||
bool valid_ldh_char(char c) _const_;
|
||||
|
||||
typedef enum ValidHostnameFlags {
|
||||
VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */
|
||||
VALID_HOSTNAME_DOT_HOST = 1 << 1, /* Accept ".host" as valid hostname */
|
||||
VALID_HOSTNAME_QUESTION_MARK = 1 << 2, /* Accept "?" as place holder for hashed machine ID value */
|
||||
} ValidHostnameFlags;
|
||||
|
||||
bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_;
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "in-addr-util.h"
|
||||
#include "logarithm.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "random-util.h"
|
||||
#include "stdio-util.h"
|
||||
@@ -28,7 +29,7 @@ bool in4_addr_is_null(const struct in_addr *a) {
|
||||
bool in6_addr_is_null(const struct in6_addr *a) {
|
||||
assert(a);
|
||||
|
||||
return IN6_IS_ADDR_UNSPECIFIED(a);
|
||||
return eqzero(a->in6_u.u6_addr32);
|
||||
}
|
||||
|
||||
int in_addr_is_null(int family, const union in_addr_union *u) {
|
||||
@@ -66,7 +67,7 @@ bool in4_addr_is_link_local_dynamic(const struct in_addr *a) {
|
||||
bool in6_addr_is_link_local(const struct in6_addr *a) {
|
||||
assert(a);
|
||||
|
||||
return IN6_IS_ADDR_LINKLOCAL(a);
|
||||
return (a->in6_u.u6_addr32[0] & htobe32(0xffc00000)) == htobe32(0xfe800000);
|
||||
}
|
||||
|
||||
int in_addr_is_link_local(int family, const union in_addr_union *u) {
|
||||
@@ -100,7 +101,7 @@ bool in4_addr_is_multicast(const struct in_addr *a) {
|
||||
bool in6_addr_is_multicast(const struct in6_addr *a) {
|
||||
assert(a);
|
||||
|
||||
return IN6_IS_ADDR_MULTICAST(a);
|
||||
return a->in6_u.u6_addr8[0] == 0xff;
|
||||
}
|
||||
|
||||
int in_addr_is_multicast(int family, const union in_addr_union *u) {
|
||||
@@ -136,6 +137,10 @@ bool in4_addr_is_non_local(const struct in_addr *a) {
|
||||
!in4_addr_is_localhost(a);
|
||||
}
|
||||
|
||||
static bool in6_addr_is_loopback(const struct in6_addr *a) {
|
||||
return memcmp(a, &(struct in6_addr) IN6ADDR_LOOPBACK_INIT, sizeof(struct in6_addr)) == 0;
|
||||
}
|
||||
|
||||
int in_addr_is_localhost(int family, const union in_addr_union *u) {
|
||||
assert(u);
|
||||
|
||||
@@ -143,7 +148,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
|
||||
return in4_addr_is_localhost(&u->in);
|
||||
|
||||
if (family == AF_INET6)
|
||||
return IN6_IS_ADDR_LOOPBACK(&u->in6);
|
||||
return in6_addr_is_loopback(&u->in6);
|
||||
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
@@ -156,7 +161,7 @@ int in_addr_is_localhost_one(int family, const union in_addr_union *u) {
|
||||
return be32toh(u->in.s_addr) == UINT32_C(0x7F000001);
|
||||
|
||||
if (family == AF_INET6)
|
||||
return IN6_IS_ADDR_LOOPBACK(&u->in6);
|
||||
return in6_addr_is_loopback(&u->in6);
|
||||
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
@@ -178,7 +183,7 @@ bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
return IN6_ARE_ADDR_EQUAL(a, b);
|
||||
return memcmp(a, b, sizeof(struct in6_addr)) == 0;
|
||||
}
|
||||
|
||||
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
|
||||
@@ -942,7 +947,6 @@ int in_addr_prefix_from_string_auto_full(
|
||||
*ret_prefixlen = k;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
|
||||
|
9
src/libnm-systemd-shared/src/basic/include/net/if.h
Normal file
9
src/libnm-systemd-shared/src/basic/include/net/if.h
Normal file
@@ -0,0 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <linux/if.h>
|
||||
|
||||
#define IF_NAMESIZE 16
|
||||
|
||||
extern unsigned int if_nametoindex(const char *__ifname) __THROW;
|
||||
extern char *if_indextoname(unsigned int __ifindex, char __ifname[IF_NAMESIZE]) __THROW;
|
19
src/libnm-systemd-shared/src/basic/include/netinet/in.h
Normal file
19
src/libnm-systemd-shared/src/basic/include/netinet/in.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#define INET6_ADDRSTRLEN 46
|
||||
|
||||
extern const struct in6_addr in6addr_any; /* :: */
|
||||
extern const struct in6_addr in6addr_loopback; /* ::1 */
|
||||
#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
|
||||
#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
|
||||
|
||||
typedef uint32_t in_addr_t;
|
@@ -86,6 +86,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
|
||||
return n;
|
||||
|
||||
assert((size_t) k <= nbytes);
|
||||
assert(k <= SSIZE_MAX - n);
|
||||
|
||||
p += k;
|
||||
nbytes -= k;
|
||||
@@ -188,7 +189,7 @@ int pipe_eof(int fd) {
|
||||
return !!(r & POLLHUP);
|
||||
}
|
||||
|
||||
int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
||||
int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) {
|
||||
int r;
|
||||
|
||||
assert(fds || nfds == 0);
|
||||
@@ -208,10 +209,10 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
||||
* to handle signals, such as signalfd() or signal handlers. ⚠️ ⚠️ ⚠️
|
||||
*/
|
||||
|
||||
if (nfds == 0)
|
||||
if (nfds == 0 && timeout == 0)
|
||||
return 0;
|
||||
|
||||
r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL);
|
||||
r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), ss);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
if (r == 0)
|
||||
|
@@ -15,14 +15,18 @@ 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_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
|
||||
int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) _nonnull_if_nonzero_(2, 3);
|
||||
static inline int loop_write(int fd, const void *buf, size_t nbytes) {
|
||||
return loop_write_full(fd, buf, nbytes, 0);
|
||||
}
|
||||
|
||||
int pipe_eof(int fd);
|
||||
|
||||
int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout);
|
||||
int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) _nonnull_if_nonzero_(1, 2);
|
||||
_nonnull_if_nonzero_(1, 2) static inline int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
||||
return ppoll_usec_full(fds, nfds, timeout, NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -42,5 +46,4 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
|
||||
return true;
|
||||
|
||||
return FILE_SIZE_VALID(l);
|
||||
|
||||
}
|
||||
|
@@ -12,9 +12,9 @@
|
||||
extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */
|
||||
extern const struct iovec iovec_empty; /* Points to an empty, but valid (i.e. non-NULL) pointer */
|
||||
|
||||
size_t iovec_total_size(const struct iovec *iovec, size_t n);
|
||||
size_t iovec_total_size(const struct iovec *iovec, size_t n) _nonnull_if_nonzero_(1, 2);
|
||||
|
||||
bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
|
||||
bool iovec_increment(struct iovec *iovec, size_t n, size_t k) _nonnull_if_nonzero_(1, 2);
|
||||
|
||||
static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
|
||||
assert(iovec);
|
||||
@@ -42,7 +42,7 @@ static inline void iovec_done_erase(struct iovec *iovec) {
|
||||
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
|
||||
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
|
||||
|
||||
void iovec_array_free(struct iovec *iovec, size_t n_iovec);
|
||||
void iovec_array_free(struct iovec *iovec, size_t n_iovec) _nonnull_if_nonzero_(1, 2);
|
||||
|
||||
static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
|
||||
|
||||
@@ -55,7 +55,7 @@ static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
|
||||
b ? b->iov_len : 0);
|
||||
}
|
||||
|
||||
static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
|
||||
static inline struct iovec* iovec_memdup(const struct iovec *source, struct iovec *ret) {
|
||||
assert(ret);
|
||||
|
||||
if (!iovec_is_set(source))
|
||||
|
@@ -1,6 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* 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) \
|
||||
@@ -203,7 +205,3 @@
|
||||
free_func(elem); \
|
||||
head; \
|
||||
})
|
||||
|
||||
/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
|
||||
* it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
|
||||
#include "macro.h"
|
||||
|
@@ -17,15 +17,16 @@
|
||||
#include "fileio.h"
|
||||
#include "hashmap.h"
|
||||
#include "locale-util.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "log.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "set.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
static char *normalize_locale(const char *name) {
|
||||
static char* normalize_locale(const char *name) {
|
||||
const char *e;
|
||||
|
||||
/* Locale names are weird: glibc has some magic rules when looking for the charset name on disk: it
|
||||
@@ -93,18 +94,15 @@ static int add_locales_from_archive(Set *locales) {
|
||||
uint32_t locrec_offset;
|
||||
};
|
||||
|
||||
const struct locarhead *h;
|
||||
const struct namehashent *e;
|
||||
const void *p = MAP_FAILED;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
size_t sz = 0;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
assert(locales);
|
||||
|
||||
_cleanup_close_ int fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return errno == ENOENT ? 0 : -errno;
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
@@ -117,11 +115,12 @@ static int add_locales_from_archive(Set *locales) {
|
||||
if (file_offset_beyond_memory_size(st.st_size))
|
||||
return -EFBIG;
|
||||
|
||||
p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
void *p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (p == MAP_FAILED)
|
||||
return -errno;
|
||||
|
||||
h = (const struct locarhead *) p;
|
||||
const struct namehashent *e;
|
||||
const struct locarhead *h = p;
|
||||
if (h->magic != 0xde020109 ||
|
||||
h->namehash_offset + h->namehash_size > st.st_size ||
|
||||
h->string_offset + h->string_size > st.st_size ||
|
||||
@@ -154,9 +153,9 @@ static int add_locales_from_archive(Set *locales) {
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
finish:
|
||||
if (p != MAP_FAILED)
|
||||
munmap((void*) p, sz);
|
||||
munmap((void*) p, st.st_size);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -165,6 +164,8 @@ static int add_locales_from_libdir(Set *locales) {
|
||||
_cleanup_closedir_ DIR *dir = NULL;
|
||||
int r;
|
||||
|
||||
assert(locales);
|
||||
|
||||
dir = opendir("/usr/lib/locale");
|
||||
if (!dir)
|
||||
return errno == ENOENT ? 0 : -errno;
|
||||
@@ -180,7 +181,7 @@ static int add_locales_from_libdir(Set *locales) {
|
||||
return -ENOMEM;
|
||||
|
||||
r = set_consume(locales, z);
|
||||
if (r < 0 && r != -EEXIST)
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -188,11 +189,10 @@ static int add_locales_from_libdir(Set *locales) {
|
||||
}
|
||||
|
||||
int get_locales(char ***ret) {
|
||||
_cleanup_set_free_free_ Set *locales = NULL;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_set_free_ Set *locales = NULL;
|
||||
int r;
|
||||
|
||||
locales = set_new(&string_hash_ops);
|
||||
locales = set_new(&string_hash_ops_free);
|
||||
if (!locales)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -213,31 +213,25 @@ int get_locales(char ***ret) {
|
||||
free(set_remove(locales, locale));
|
||||
}
|
||||
|
||||
l = set_get_strv(locales);
|
||||
_cleanup_strv_free_ char **l = set_to_strv(&locales);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Now, all elements are owned by strv 'l'. Hence, do not call set_free_free(). */
|
||||
locales = set_free(locales);
|
||||
|
||||
r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
|
||||
if (IN_SET(r, -ENXIO, 0)) {
|
||||
char **a, **b;
|
||||
if (r <= 0) {
|
||||
if (!IN_SET(r, -ENXIO, 0))
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean, ignoring: %m");
|
||||
|
||||
/* Filter out non-UTF-8 locales, because it's 2019, by default */
|
||||
for (a = b = l; *a; a++) {
|
||||
|
||||
if (endswith(*a, "UTF-8") ||
|
||||
strstr(*a, ".UTF-8@"))
|
||||
char **b = l;
|
||||
STRV_FOREACH(a, l)
|
||||
if (endswith(*a, "UTF-8") || strstr(*a, ".UTF-8@"))
|
||||
*(b++) = *a;
|
||||
else
|
||||
free(*a);
|
||||
}
|
||||
|
||||
*b = NULL;
|
||||
|
||||
} else if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean");
|
||||
}
|
||||
|
||||
strv_sort(l);
|
||||
|
||||
@@ -283,64 +277,48 @@ int locale_is_installed(const char *name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_locale_utf8(void) {
|
||||
static int cached_answer = -1;
|
||||
static bool is_locale_utf8_impl(void) {
|
||||
const char *set;
|
||||
int r;
|
||||
|
||||
/* Note that we default to 'true' here, since today UTF8 is
|
||||
* pretty much supported everywhere. */
|
||||
|
||||
if (cached_answer >= 0)
|
||||
goto out;
|
||||
/* Note that we default to 'true' here, since today UTF8 is pretty much supported everywhere. */
|
||||
|
||||
r = secure_getenv_bool("SYSTEMD_UTF8");
|
||||
if (r >= 0) {
|
||||
cached_answer = r;
|
||||
goto out;
|
||||
} else if (r != -ENXIO)
|
||||
if (r >= 0)
|
||||
return r;
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
|
||||
|
||||
/* This function may be called from libsystemd, and setlocale() is not thread safe. Assuming yes. */
|
||||
if (gettid() != raw_getpid()) {
|
||||
cached_answer = true;
|
||||
goto out;
|
||||
}
|
||||
if (!is_main_thread())
|
||||
return true;
|
||||
|
||||
if (!setlocale(LC_ALL, "")) {
|
||||
cached_answer = true;
|
||||
goto out;
|
||||
}
|
||||
if (!setlocale(LC_ALL, ""))
|
||||
return true;
|
||||
|
||||
set = nl_langinfo(CODESET);
|
||||
if (!set) {
|
||||
cached_answer = true;
|
||||
goto out;
|
||||
}
|
||||
if (!set || streq(set, "UTF-8"))
|
||||
return true;
|
||||
|
||||
if (streq(set, "UTF-8")) {
|
||||
cached_answer = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* For LC_CTYPE=="C" return true, because CTYPE is effectively
|
||||
* unset and everything can do to UTF-8 nowadays. */
|
||||
set = setlocale(LC_CTYPE, NULL);
|
||||
if (!set) {
|
||||
cached_answer = true;
|
||||
goto out;
|
||||
}
|
||||
if (!set)
|
||||
return true;
|
||||
|
||||
/* Check result, but ignore the result if C was set
|
||||
* explicitly. */
|
||||
cached_answer =
|
||||
STR_IN_SET(set, "C", "POSIX") &&
|
||||
/* Unless LC_CTYPE is explicitly overridden, return true. Because here CTYPE is effectively unset
|
||||
* and everything can do to UTF-8 nowadays. */
|
||||
return STR_IN_SET(set, "C", "POSIX") &&
|
||||
!getenv("LC_ALL") &&
|
||||
!getenv("LC_CTYPE") &&
|
||||
!getenv("LANG");
|
||||
}
|
||||
|
||||
out:
|
||||
return (bool) cached_answer;
|
||||
bool is_locale_utf8(void) {
|
||||
static int cached = -1;
|
||||
|
||||
if (cached < 0)
|
||||
cached = is_locale_utf8_impl();
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
|
||||
|
@@ -5,6 +5,8 @@
|
||||
/* Include here so consumers have LOCK_{EX,SH,NB} available. */
|
||||
#include <sys/file.h>
|
||||
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct LockFile {
|
||||
int dir_fd;
|
||||
char *path;
|
||||
|
@@ -7,10 +7,7 @@
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
#include "ratelimit.h"
|
||||
#include "stdio-util.h"
|
||||
|
||||
/* Some structures we reference but don't want to pull in headers for */
|
||||
struct iovec;
|
||||
@@ -82,9 +79,6 @@ int log_show_tid_from_string(const char *e);
|
||||
* environment should not be called from library code — this is always a job
|
||||
* for the application itself. */
|
||||
|
||||
assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
|
||||
#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
|
||||
|
||||
bool stderr_is_journal(void);
|
||||
int log_open(void);
|
||||
void log_close(void);
|
||||
@@ -153,7 +147,7 @@ int log_struct_internal(
|
||||
const char *file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *format, ...) _printf_(6,0) _sentinel_;
|
||||
const char *format, ...) _sentinel_;
|
||||
|
||||
int log_oom_internal(
|
||||
int level,
|
||||
@@ -188,24 +182,6 @@ int log_dump_internal(
|
||||
const char *func,
|
||||
char *buffer);
|
||||
|
||||
/* Logging for various assertions */
|
||||
_noreturn_ void log_assert_failed(
|
||||
const char *text,
|
||||
const char *file,
|
||||
int line,
|
||||
const char *func);
|
||||
|
||||
_noreturn_ void log_assert_failed_unreachable(
|
||||
const char *file,
|
||||
int line,
|
||||
const char *func);
|
||||
|
||||
void log_assert_failed_return(
|
||||
const char *text,
|
||||
const char *file,
|
||||
int line,
|
||||
const char *func);
|
||||
|
||||
#define log_dispatch(level, error, buffer) \
|
||||
log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
|
||||
|
||||
@@ -314,11 +290,15 @@ bool log_on_console(void) _pure_;
|
||||
/* Do a fake formatting of the message string to let the scanner verify the arguments against the format
|
||||
* message. The variable will never be set to true, but we don't tell the compiler that :) */
|
||||
extern bool _log_message_dummy;
|
||||
# define LOG_MESSAGE(fmt, ...) "MESSAGE=%.0d" fmt, (_log_message_dummy && printf(fmt, ##__VA_ARGS__)), ##__VA_ARGS__
|
||||
# define LOG_ITEM(fmt, ...) "%.0d" fmt, (_log_message_dummy && printf(fmt, ##__VA_ARGS__)), ##__VA_ARGS__
|
||||
# define LOG_MESSAGE(fmt, ...) LOG_ITEM("MESSAGE=" fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
# define LOG_ITEM(fmt, ...) fmt, ##__VA_ARGS__
|
||||
# define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
|
||||
#endif
|
||||
|
||||
#define LOG_MESSAGE_ID(id) LOG_ITEM("MESSAGE_ID=" id)
|
||||
|
||||
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 */
|
||||
@@ -335,9 +315,6 @@ void log_set_open_when_needed(bool b);
|
||||
* stderr, the console or kmsg */
|
||||
void log_set_prohibit_ipc(bool b);
|
||||
|
||||
void log_set_assert_return_is_critical(bool b);
|
||||
bool log_get_assert_return_is_critical(void) _pure_;
|
||||
|
||||
int log_dup_console(void);
|
||||
|
||||
int log_syntax_internal(
|
||||
@@ -399,58 +376,6 @@ int log_syntax_parse_error_internal(
|
||||
|
||||
void log_setup(void);
|
||||
|
||||
typedef struct LogRateLimit {
|
||||
int error;
|
||||
int level;
|
||||
RateLimit ratelimit;
|
||||
} LogRateLimit;
|
||||
|
||||
#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...) \
|
||||
({ \
|
||||
int _log_ratelimit_error = (_error); \
|
||||
int _log_ratelimit_level = (_level); \
|
||||
static LogRateLimit _log_ratelimit = { \
|
||||
.ratelimit = (_ratelimit), \
|
||||
}; \
|
||||
unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
|
||||
if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
|
||||
ratelimit_reset(&_log_ratelimit.ratelimit); \
|
||||
_log_ratelimit.error = _log_ratelimit_error; \
|
||||
_log_ratelimit.level = _log_ratelimit_level; \
|
||||
} \
|
||||
if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \
|
||||
_log_ratelimit_error = _num_dropped_errors > 0 \
|
||||
? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \
|
||||
: log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \
|
||||
_log_ratelimit_error; \
|
||||
})
|
||||
|
||||
#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...) \
|
||||
({ \
|
||||
int _level = (level), _e = (error); \
|
||||
_e = (log_get_max_level() >= LOG_PRI(_level)) \
|
||||
? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
|
||||
: -ERRNO_VALUE(_e); \
|
||||
_e < 0 ? _e : -ESTRPIPE; \
|
||||
})
|
||||
|
||||
#define log_ratelimit_full(level, _ratelimit, format, ...) \
|
||||
log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__)
|
||||
|
||||
/* Normal logging */
|
||||
#define log_ratelimit_info(...) log_ratelimit_full(LOG_INFO, __VA_ARGS__)
|
||||
#define log_ratelimit_notice(...) log_ratelimit_full(LOG_NOTICE, __VA_ARGS__)
|
||||
#define log_ratelimit_warning(...) log_ratelimit_full(LOG_WARNING, __VA_ARGS__)
|
||||
#define log_ratelimit_error(...) log_ratelimit_full(LOG_ERR, __VA_ARGS__)
|
||||
#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__)
|
||||
|
||||
/* Logging triggered by an errno-like error */
|
||||
#define log_ratelimit_info_errno(error, ...) log_ratelimit_full_errno(LOG_INFO, error, __VA_ARGS__)
|
||||
#define log_ratelimit_notice_errno(error, ...) log_ratelimit_full_errno(LOG_NOTICE, error, __VA_ARGS__)
|
||||
#define log_ratelimit_warning_errno(error, ...) log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
|
||||
#define log_ratelimit_error_errno(error, ...) log_ratelimit_full_errno(LOG_ERR, error, __VA_ARGS__)
|
||||
#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
|
||||
|
||||
const char* _log_set_prefix(const char *prefix, bool force);
|
||||
static inline const char* _log_unset_prefixp(const char **p) {
|
||||
assert(p);
|
||||
@@ -460,112 +385,3 @@ static inline const char* _log_unset_prefixp(const char **p) {
|
||||
|
||||
#define LOG_SET_PREFIX(prefix) \
|
||||
_cleanup_(_log_unset_prefixp) _unused_ const char *CONCATENATE(_cleanup_log_unset_prefix_, UNIQ) = _log_set_prefix(prefix, false);
|
||||
|
||||
/*
|
||||
* The log context allows attaching extra metadata to log messages written to the journal via log.h. We keep
|
||||
* track of a thread local log context onto which we can push extra metadata fields that should be logged.
|
||||
*
|
||||
* LOG_CONTEXT_PUSH() will add the provided field to the log context and will remove it again when the
|
||||
* current block ends. LOG_CONTEXT_PUSH_STRV() will do the same but for all fields in the given strv.
|
||||
* LOG_CONTEXT_PUSHF() is like LOG_CONTEXT_PUSH() but takes a format string and arguments.
|
||||
*
|
||||
* Using the macros is as simple as putting them anywhere inside a block to add a field to all following log
|
||||
* messages logged from inside that block.
|
||||
*
|
||||
* void myfunction(...) {
|
||||
* ...
|
||||
*
|
||||
* LOG_CONTEXT_PUSHF("MYMETADATA=%s", "abc");
|
||||
*
|
||||
* // Every journal message logged will now have the MYMETADATA=abc
|
||||
* // field included.
|
||||
* }
|
||||
*
|
||||
* One special case to note is async code, where we use callbacks that are invoked to continue processing
|
||||
* when some event occurs. For async code, there's usually an associated "userdata" struct containing all the
|
||||
* information associated with the async operation. In this "userdata" struct, we can store a log context
|
||||
* allocated with log_context_new() and freed with log_context_free(). We can then add and remove fields to
|
||||
* the `fields` member of the log context object and all those fields will be logged along with each log
|
||||
* message.
|
||||
*/
|
||||
|
||||
typedef struct LogContext LogContext;
|
||||
|
||||
bool log_context_enabled(void);
|
||||
|
||||
LogContext* log_context_new(const char *key, const char *value);
|
||||
LogContext* log_context_new_strv(char **fields, bool owned);
|
||||
LogContext* log_context_new_iov(struct iovec *input_iovec, size_t n_input_iovec, bool owned);
|
||||
|
||||
/* Same as log_context_new(), but frees the given fields strv/iovec on failure. */
|
||||
LogContext* log_context_new_strv_consume(char **fields);
|
||||
LogContext* log_context_new_iov_consume(struct iovec *input_iovec, size_t n_input_iovec);
|
||||
|
||||
LogContext *log_context_ref(LogContext *c);
|
||||
LogContext *log_context_unref(LogContext *c);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_unref);
|
||||
|
||||
/* Returns the number of attached log context objects. */
|
||||
size_t log_context_num_contexts(void);
|
||||
/* Returns the number of fields in all attached log contexts. */
|
||||
size_t log_context_num_fields(void);
|
||||
|
||||
static inline void _reset_log_level(int *saved_log_level) {
|
||||
assert(saved_log_level);
|
||||
|
||||
log_set_max_level(*saved_log_level);
|
||||
}
|
||||
|
||||
#define LOG_CONTEXT_SET_LOG_LEVEL(level) \
|
||||
_cleanup_(_reset_log_level) _unused_ int _saved_log_level_ = log_set_max_level(level);
|
||||
|
||||
#define LOG_CONTEXT_PUSH(...) \
|
||||
LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))
|
||||
|
||||
#define LOG_CONTEXT_PUSHF(...) \
|
||||
LOG_CONTEXT_PUSH(snprintf_ok((char[LINE_MAX]) {}, LINE_MAX, __VA_ARGS__))
|
||||
|
||||
#define _LOG_CONTEXT_PUSH_KEY_VALUE(key, value, c) \
|
||||
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new(key, value);
|
||||
|
||||
#define LOG_CONTEXT_PUSH_KEY_VALUE(key, value) \
|
||||
_LOG_CONTEXT_PUSH_KEY_VALUE(key, value, UNIQ_T(c, UNIQ))
|
||||
|
||||
#define _LOG_CONTEXT_PUSH_STRV(strv, c) \
|
||||
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv(strv, /*owned=*/ false);
|
||||
|
||||
#define LOG_CONTEXT_PUSH_STRV(strv) \
|
||||
_LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ))
|
||||
|
||||
#define _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, c) \
|
||||
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov(input_iovec, n_input_iovec, /*owned=*/ false);
|
||||
|
||||
#define LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec) \
|
||||
_LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))
|
||||
|
||||
/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV()/LOG_CONTEXT_CONSUME_IOV() are identical to
|
||||
* LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV()/LOG_CONTEXT_PUSH_IOV() except they take ownership of the
|
||||
* given str/strv argument.
|
||||
*/
|
||||
|
||||
#define _LOG_CONTEXT_CONSUME_STR(s, c, strv) \
|
||||
_unused_ _cleanup_strv_free_ strv = strv_new(s); \
|
||||
if (!strv) \
|
||||
free(s); \
|
||||
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(TAKE_PTR(strv))
|
||||
|
||||
#define LOG_CONTEXT_CONSUME_STR(s) \
|
||||
_LOG_CONTEXT_CONSUME_STR(s, UNIQ_T(c, UNIQ), UNIQ_T(sv, UNIQ))
|
||||
|
||||
#define _LOG_CONTEXT_CONSUME_STRV(strv, c) \
|
||||
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(strv);
|
||||
|
||||
#define LOG_CONTEXT_CONSUME_STRV(strv) \
|
||||
_LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ))
|
||||
|
||||
#define _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, c) \
|
||||
_unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov_consume(input_iovec, n_input_iovec);
|
||||
|
||||
#define LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec) \
|
||||
_LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))
|
||||
|
@@ -103,76 +103,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
|
||||
(type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \
|
||||
})
|
||||
|
||||
#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 void __coverity_check__(int condition) {
|
||||
if (!condition)
|
||||
__coverity_panic__();
|
||||
}
|
||||
|
||||
static inline int __coverity_check_and_return__(int condition) {
|
||||
return condition;
|
||||
}
|
||||
|
||||
#define assert_message_se(expr, message) __coverity_check__(!!(expr))
|
||||
|
||||
#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
|
||||
|
||||
#else /* ! __COVERITY__ */
|
||||
|
||||
#define assert_message_se(expr, message) \
|
||||
do { \
|
||||
if (_unlikely_(!(expr))) \
|
||||
log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
|
||||
} while (false)
|
||||
|
||||
#define assert_log(expr, message) ((_likely_(expr)) \
|
||||
? (true) \
|
||||
: (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), 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) ({ if (!(expr)) __builtin_unreachable(); })
|
||||
#else
|
||||
#define assert(expr) assert_message_se(expr, #expr)
|
||||
#endif
|
||||
|
||||
#define assert_not_reached() \
|
||||
log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
|
||||
|
||||
#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 return_with_errno(r, err) \
|
||||
do { \
|
||||
errno = abs(err); \
|
||||
@@ -251,59 +181,6 @@ static inline int __coverity_check_and_return__(int condition) {
|
||||
/* Pointers range from NULL to POINTER_MAX */
|
||||
#define POINTER_MAX ((void*) UINTPTR_MAX)
|
||||
|
||||
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
|
||||
scope type *name##_ref(type *p) { \
|
||||
if (!p) \
|
||||
return NULL; \
|
||||
\
|
||||
/* For type check. */ \
|
||||
unsigned *q = &p->n_ref; \
|
||||
assert(*q > 0); \
|
||||
assert_se(*q < UINT_MAX); \
|
||||
\
|
||||
(*q)++; \
|
||||
return p; \
|
||||
}
|
||||
|
||||
#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
|
||||
scope type *name##_unref(type *p) { \
|
||||
if (!p) \
|
||||
return NULL; \
|
||||
\
|
||||
assert(p->n_ref > 0); \
|
||||
p->n_ref--; \
|
||||
if (p->n_ref > 0) \
|
||||
return NULL; \
|
||||
\
|
||||
return free_func(p); \
|
||||
}
|
||||
|
||||
#define DEFINE_TRIVIAL_REF_FUNC(type, name) \
|
||||
_DEFINE_TRIVIAL_REF_FUNC(type, name,)
|
||||
#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name) \
|
||||
_DEFINE_TRIVIAL_REF_FUNC(type, name, static)
|
||||
#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name) \
|
||||
_DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
|
||||
|
||||
#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
|
||||
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
|
||||
#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
|
||||
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
|
||||
#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func) \
|
||||
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
|
||||
|
||||
#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
|
||||
DEFINE_TRIVIAL_REF_FUNC(type, name); \
|
||||
DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
|
||||
|
||||
#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
|
||||
DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name); \
|
||||
DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
|
||||
|
||||
#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
|
||||
DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \
|
||||
DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
|
||||
|
||||
/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from
|
||||
* memory and want to make sure the compiler won't optimize away the destination variable for us. It's not
|
||||
* supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not
|
||||
@@ -328,12 +205,6 @@ static inline size_t size_add(size_t x, size_t y) {
|
||||
return saturate_add(x, y, SIZE_MAX);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int _empty[0];
|
||||
} dummy_t;
|
||||
|
||||
assert_cc(sizeof(dummy_t) == 0);
|
||||
|
||||
/* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for
|
||||
* loops that count down from a high pointer until some base. A naive loop would implement this like this:
|
||||
*
|
||||
@@ -358,5 +229,3 @@ assert_cc(sizeof(dummy_t) == 0);
|
||||
for (typeof(entry) _va_sentinel_[1] = {}, _entries_[] = { __VA_ARGS__ __VA_OPT__(,) _va_sentinel_[0] }, *_current_ = _entries_; \
|
||||
((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \
|
||||
_current_++)
|
||||
|
||||
#include "log.h"
|
||||
|
@@ -1,9 +1,10 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_threads.h"
|
||||
|
||||
size_t page_size(void) {
|
||||
static thread_local size_t pgsz = 0;
|
||||
@@ -40,7 +41,7 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
|
||||
return memcmp(data, p + 16, length) == 0;
|
||||
}
|
||||
|
||||
void *memdup_reverse(const void *mem, size_t size) {
|
||||
void* memdup_reverse(const void *mem, size_t size) {
|
||||
assert(mem);
|
||||
assert(size != 0);
|
||||
|
||||
@@ -55,3 +56,14 @@ void *memdup_reverse(const void *mem, size_t size) {
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void* erase_and_free(void *p) {
|
||||
size_t l;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
l = MALLOC_SIZEOF_SAFE(p);
|
||||
explicit_bzero_safe(p, l);
|
||||
return mfree(p);
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util-fundamental.h"
|
||||
|
||||
@@ -35,13 +34,16 @@ static inline void* mempcpy_safe(void *dst, const void *src, size_t n) {
|
||||
return mempcpy(dst, src, n);
|
||||
}
|
||||
|
||||
#define mempcpy_typesafe(dst, src, n) \
|
||||
#define _mempcpy_typesafe(dst, src, n, sz) \
|
||||
({ \
|
||||
size_t _sz_; \
|
||||
assert_se(MUL_SAFE(&_sz_, sizeof((dst)[0]), n)); \
|
||||
(typeof((dst)[0])*) mempcpy_safe(dst, src, _sz_); \
|
||||
size_t sz; \
|
||||
assert_se(MUL_SAFE(&sz, sizeof((dst)[0]), n)); \
|
||||
(typeof((dst)[0])*) mempcpy_safe(dst, src, sz); \
|
||||
})
|
||||
|
||||
#define mempcpy_typesafe(dst, src, n) \
|
||||
_mempcpy_typesafe(dst, src, n, UNIQ_T(sz, UNIQ))
|
||||
|
||||
/* 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)
|
||||
@@ -59,19 +61,19 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
|
||||
|
||||
#define zero(x) (memzero(&(x), sizeof(x)))
|
||||
|
||||
bool memeqbyte(uint8_t byte, const void *data, size_t length);
|
||||
bool memeqbyte(uint8_t byte, const void *data, size_t length) _nonnull_if_nonzero_(2, 3);
|
||||
|
||||
#define memeqzero(data, length) memeqbyte(0x00, data, length)
|
||||
|
||||
#define eqzero(x) memeqzero(x, sizeof(x))
|
||||
|
||||
static inline void *mempset(void *s, int c, size_t n) {
|
||||
static inline void* mempset(void *s, int c, size_t n) {
|
||||
memset(s, c, n);
|
||||
return (uint8_t*)s + n;
|
||||
return (uint8_t*) s + n;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
static inline void* memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
|
||||
|
||||
if (needlelen <= 0)
|
||||
return (void*) haystack;
|
||||
@@ -85,7 +87,7 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
|
||||
return memmem(haystack, haystacklen, needle, needlelen);
|
||||
}
|
||||
|
||||
static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
|
||||
static inline void* mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
|
||||
const uint8_t *p;
|
||||
|
||||
p = memmem_safe(haystack, haystacklen, needle, needlelen);
|
||||
@@ -95,16 +97,7 @@ static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const
|
||||
return (uint8_t*) p + needlelen;
|
||||
}
|
||||
|
||||
static inline void* erase_and_free(void *p) {
|
||||
size_t l;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
l = MALLOC_SIZEOF_SAFE(p);
|
||||
explicit_bzero_safe(p, l);
|
||||
return mfree(p);
|
||||
}
|
||||
void* erase_and_free(void *p);
|
||||
|
||||
static inline void erase_and_freep(void *p) {
|
||||
erase_and_free(*(void**) p);
|
||||
@@ -116,4 +109,57 @@ static inline void erase_char(char *p) {
|
||||
}
|
||||
|
||||
/* Makes a copy of the buffer with reversed order of bytes */
|
||||
void *memdup_reverse(const void *mem, size_t size);
|
||||
void* memdup_reverse(const void *mem, size_t size);
|
||||
|
||||
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
|
||||
scope type *name##_ref(type *p) { \
|
||||
if (!p) \
|
||||
return NULL; \
|
||||
\
|
||||
/* For type check. */ \
|
||||
unsigned *q = &p->n_ref; \
|
||||
assert(*q > 0); \
|
||||
assert_se(*q < UINT_MAX); \
|
||||
\
|
||||
(*q)++; \
|
||||
return p; \
|
||||
}
|
||||
|
||||
#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
|
||||
scope type *name##_unref(type *p) { \
|
||||
if (!p) \
|
||||
return NULL; \
|
||||
\
|
||||
assert(p->n_ref > 0); \
|
||||
p->n_ref--; \
|
||||
if (p->n_ref > 0) \
|
||||
return NULL; \
|
||||
\
|
||||
return free_func(p); \
|
||||
}
|
||||
|
||||
#define DEFINE_TRIVIAL_REF_FUNC(type, name) \
|
||||
_DEFINE_TRIVIAL_REF_FUNC(type, name,)
|
||||
#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name) \
|
||||
_DEFINE_TRIVIAL_REF_FUNC(type, name, static)
|
||||
#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name) \
|
||||
_DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
|
||||
|
||||
#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
|
||||
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
|
||||
#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func) \
|
||||
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
|
||||
#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func) \
|
||||
_DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
|
||||
|
||||
#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
|
||||
DEFINE_TRIVIAL_REF_FUNC(type, name); \
|
||||
DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
|
||||
|
||||
#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
|
||||
DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name); \
|
||||
DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
|
||||
|
||||
#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func) \
|
||||
DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name); \
|
||||
DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "format-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "mempool.h"
|
||||
|
@@ -3,74 +3,16 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef F_LINUX_SPECIFIC_BASE
|
||||
#define F_LINUX_SPECIFIC_BASE 1024
|
||||
#endif
|
||||
|
||||
/* This is defined since glibc-2.41. */
|
||||
#ifndef F_DUPFD_QUERY
|
||||
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
|
||||
#endif
|
||||
|
||||
#ifndef F_SETPIPE_SZ
|
||||
#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
|
||||
#endif
|
||||
|
||||
#ifndef F_GETPIPE_SZ
|
||||
#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
|
||||
#endif
|
||||
|
||||
#ifndef F_ADD_SEALS
|
||||
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
|
||||
#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
|
||||
|
||||
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
|
||||
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
|
||||
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
|
||||
#define F_SEAL_WRITE 0x0008 /* prevent writes */
|
||||
#endif
|
||||
|
||||
#ifndef F_SEAL_FUTURE_WRITE
|
||||
#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */
|
||||
#define F_DUPFD_QUERY 1027
|
||||
#endif
|
||||
|
||||
/* This is defined since glibc-2.39. */
|
||||
#ifndef F_SEAL_EXEC
|
||||
#define F_SEAL_EXEC 0x0020 /* prevent chmod modifying exec bits */
|
||||
#endif
|
||||
|
||||
#ifndef F_OFD_GETLK
|
||||
#define F_OFD_GETLK 36
|
||||
#define F_OFD_SETLK 37
|
||||
#define F_OFD_SETLKW 38
|
||||
#endif
|
||||
|
||||
#ifndef MAX_HANDLE_SZ
|
||||
#define MAX_HANDLE_SZ 128
|
||||
#endif
|
||||
|
||||
/* The precise definition of __O_TMPFILE is arch specific; use the
|
||||
* values defined by the kernel (note: some are hexa, some are octal,
|
||||
* duplicated as-is from the kernel definitions):
|
||||
* - alpha, parisc, sparc: each has a specific value;
|
||||
* - others: they use the "generic" value.
|
||||
*/
|
||||
|
||||
#ifndef __O_TMPFILE
|
||||
#if defined(__alpha__)
|
||||
#define __O_TMPFILE 0100000000
|
||||
#elif defined(__parisc__) || defined(__hppa__)
|
||||
#define __O_TMPFILE 0400000000
|
||||
#elif defined(__sparc__) || defined(__sparc64__)
|
||||
#define __O_TMPFILE 0x2000000
|
||||
#else
|
||||
#define __O_TMPFILE 020000000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* a horrid kludge trying to make sure that this will fail on old kernels */
|
||||
#ifndef O_TMPFILE
|
||||
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
|
||||
#endif
|
||||
|
||||
/* So O_LARGEFILE is generally implied by glibc, and defined to zero hence, because we only build in LFS
|
||||
* mode. However, when invoking fcntl(F_GETFL) the flag is ORed into the result anyway — glibc does not mask
|
||||
* it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves.
|
||||
@@ -97,6 +39,13 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* This is defined since glibc-2.39. */
|
||||
#ifndef AT_HANDLE_FID
|
||||
#define AT_HANDLE_FID AT_REMOVEDIR
|
||||
#endif
|
||||
|
||||
/* On musl, O_ACCMODE is defined as (03|O_SEARCH), unlike glibc which defines it as
|
||||
* (O_RDONLY|O_WRONLY|O_RDWR). Additionally, O_SEARCH is simply defined as O_PATH. This changes the behaviour
|
||||
* of O_ACCMODE in certain situations, which we don't want. This definition is copied from glibc and works
|
||||
* around the problems with musl's definition. */
|
||||
#define O_ACCMODE_STRICT (O_RDONLY|O_WRONLY|O_RDWR)
|
||||
|
14
src/libnm-systemd-shared/src/basic/missing_fs.h
Normal file
14
src/libnm-systemd-shared/src/basic/missing_fs.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
/* Not exposed yet. Defined at fs/ext4/ext4.h */
|
||||
#ifndef EXT4_IOC_RESIZE_FS
|
||||
#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
|
||||
#endif
|
||||
|
||||
/* linux/exportfs.h (33c5ac9175195c36a0b7005aaf503a2e81f117a1, 5.5) */
|
||||
#ifndef FILEID_KERNFS
|
||||
#define FILEID_KERNFS 0xfe
|
||||
#endif
|
@@ -1,26 +1,11 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <sys/random.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
#if HAVE_GETRANDOM
|
||||
# include <sys/random.h>
|
||||
#else
|
||||
# include <linux/random.h>
|
||||
#endif
|
||||
|
||||
#ifndef GRND_NONBLOCK
|
||||
# define GRND_NONBLOCK 0x0001
|
||||
#else
|
||||
assert_cc(GRND_NONBLOCK == 0x0001);
|
||||
#endif
|
||||
|
||||
#ifndef GRND_RANDOM
|
||||
# define GRND_RANDOM 0x0002
|
||||
#else
|
||||
assert_cc(GRND_RANDOM == 0x0002);
|
||||
#endif
|
||||
|
||||
/* Defined since glibc-2.32. */
|
||||
#ifndef GRND_INSECURE
|
||||
# define GRND_INSECURE 0x0004
|
||||
#else
|
||||
|
@@ -3,75 +3,36 @@
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifndef AF_VSOCK
|
||||
#define AF_VSOCK 40
|
||||
#endif
|
||||
|
||||
#ifndef SO_REUSEPORT
|
||||
#define SO_REUSEPORT 15
|
||||
#endif
|
||||
|
||||
#ifndef SO_PEERGROUPS
|
||||
#define SO_PEERGROUPS 59
|
||||
#endif
|
||||
|
||||
/* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89) */
|
||||
#ifndef SO_PASSPIDFD
|
||||
#define SO_PASSPIDFD 76
|
||||
#endif
|
||||
|
||||
/* Supported since kernel v6.5 (7b26952a91cf65ff1cc867a2382a8964d8c0ee7d) */
|
||||
#ifndef SO_PEERPIDFD
|
||||
#define SO_PEERPIDFD 77
|
||||
#endif
|
||||
|
||||
#ifndef SO_BINDTOIFINDEX
|
||||
#define SO_BINDTOIFINDEX 62
|
||||
#endif
|
||||
|
||||
#ifndef SOL_NETLINK
|
||||
#define SOL_NETLINK 270
|
||||
#endif
|
||||
|
||||
#ifndef SOL_ALG
|
||||
#define SOL_ALG 279
|
||||
#endif
|
||||
|
||||
/* Not exposed yet. Defined in include/linux/socket.h. */
|
||||
#ifndef SOL_SCTP
|
||||
#define SOL_SCTP 132
|
||||
#endif
|
||||
|
||||
/* Supported since kernel v2.6.17 (2c7946a7bf45ae86736ab3b43d0085e43947945c).
|
||||
* Defined since glibc-2.39 */
|
||||
#ifndef SCM_SECURITY
|
||||
#define SCM_SECURITY 0x03
|
||||
#endif
|
||||
|
||||
/* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89).
|
||||
* Defined since glibc-2.39 */
|
||||
#ifndef SCM_PIDFD
|
||||
#define SCM_PIDFD 0x04
|
||||
#endif
|
||||
|
||||
/* netinet/in.h */
|
||||
#ifndef IP_FREEBIND
|
||||
#define IP_FREEBIND 15
|
||||
#endif
|
||||
|
||||
#ifndef IP_TRANSPARENT
|
||||
#define IP_TRANSPARENT 19
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_FREEBIND
|
||||
#define IPV6_FREEBIND 78
|
||||
#endif
|
||||
|
||||
#ifndef IP_RECVFRAGSIZE
|
||||
#define IP_RECVFRAGSIZE 25
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_RECVFRAGSIZE
|
||||
#define IPV6_RECVFRAGSIZE 77
|
||||
#endif
|
||||
|
||||
/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant, but very much
|
||||
* useful for userspace too. It's documented in unix(7) these days, hence should be fairly reliable to define
|
||||
* here. */
|
||||
/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant defined in
|
||||
* include/net/scm.h, but very much useful for userspace too. It's documented in unix(7) these days, hence
|
||||
* should be fairly reliable to define here. */
|
||||
#ifndef SCM_MAX_FD
|
||||
#define SCM_MAX_FD 253U
|
||||
#endif
|
||||
|
@@ -1,135 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if WANT_LINUX_STAT_H
|
||||
#include <linux/stat.h>
|
||||
#endif
|
||||
|
||||
/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
|
||||
#define STATX_DEFINITION { \
|
||||
__u32 stx_mask; \
|
||||
__u32 stx_blksize; \
|
||||
__u64 stx_attributes; \
|
||||
__u32 stx_nlink; \
|
||||
__u32 stx_uid; \
|
||||
__u32 stx_gid; \
|
||||
__u16 stx_mode; \
|
||||
__u16 __spare0[1]; \
|
||||
__u64 stx_ino; \
|
||||
__u64 stx_size; \
|
||||
__u64 stx_blocks; \
|
||||
__u64 stx_attributes_mask; \
|
||||
struct statx_timestamp stx_atime; \
|
||||
struct statx_timestamp stx_btime; \
|
||||
struct statx_timestamp stx_ctime; \
|
||||
struct statx_timestamp stx_mtime; \
|
||||
__u32 stx_rdev_major; \
|
||||
__u32 stx_rdev_minor; \
|
||||
__u32 stx_dev_major; \
|
||||
__u32 stx_dev_minor; \
|
||||
__u64 stx_mnt_id; \
|
||||
__u64 __spare2; \
|
||||
__u64 __spare3[12]; \
|
||||
}
|
||||
|
||||
#if !HAVE_STRUCT_STATX
|
||||
struct statx_timestamp {
|
||||
__s64 tv_sec;
|
||||
__u32 tv_nsec;
|
||||
__s32 __reserved;
|
||||
};
|
||||
|
||||
struct statx STATX_DEFINITION;
|
||||
#endif
|
||||
|
||||
/* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc
|
||||
* defines an older definition */
|
||||
struct new_statx STATX_DEFINITION;
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef AT_STATX_SYNC_AS_STAT
|
||||
#define AT_STATX_SYNC_AS_STAT 0x0000
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef AT_STATX_FORCE_SYNC
|
||||
#define AT_STATX_FORCE_SYNC 0x2000
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef AT_STATX_DONT_SYNC
|
||||
#define AT_STATX_DONT_SYNC 0x4000
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_TYPE
|
||||
#define STATX_TYPE 0x00000001U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_MODE
|
||||
#define STATX_MODE 0x00000002U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_NLINK
|
||||
#define STATX_NLINK 0x00000004U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_UID
|
||||
#define STATX_UID 0x00000008U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_GID
|
||||
#define STATX_GID 0x00000010U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_ATIME
|
||||
#define STATX_ATIME 0x00000020U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_MTIME
|
||||
#define STATX_MTIME 0x00000040U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_CTIME
|
||||
#define STATX_CTIME 0x00000080U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_INO
|
||||
#define STATX_INO 0x00000100U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_SIZE
|
||||
#define STATX_SIZE 0x00000200U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_BLOCKS
|
||||
#define STATX_BLOCKS 0x00000400U
|
||||
#endif
|
||||
|
||||
/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
|
||||
#ifndef STATX_BTIME
|
||||
#define STATX_BTIME 0x00000800U
|
||||
#endif
|
||||
|
||||
/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */
|
||||
#ifndef STATX_MNT_ID
|
||||
#define STATX_MNT_ID 0x00001000U
|
||||
#endif
|
||||
|
||||
/* 80340fe3605c0e78cfe496c3b3878be828cfdbfe (5.8) */
|
||||
#ifndef STATX_ATTR_MOUNT_ROOT
|
||||
#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */
|
||||
#endif
|
@@ -4,12 +4,7 @@
|
||||
/* Missing glibc definitions to access certain kernel APIs */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#if HAVE_LINUX_TIME_TYPES_H
|
||||
/* This header defines __kernel_timespec for us, but is only available since Linux 5.1, hence conditionally
|
||||
* include this. */
|
||||
#include <linux/time_types.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
@@ -23,24 +18,14 @@
|
||||
#include "macro.h"
|
||||
#include "missing_keyctl.h"
|
||||
#include "missing_sched.h"
|
||||
#include "missing_stat.h"
|
||||
#include "missing_syscall_def.h"
|
||||
|
||||
/* linux/kcmp.h */
|
||||
#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */
|
||||
#define KCMP_FILE 0
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_FCHMODAT2
|
||||
/* since kernel v6.6 (78252deb023cf0879256fcfbafe37022c390762b) */
|
||||
static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
|
||||
# ifdef __NR_fchmodat2
|
||||
return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define fchmodat2 missing_fchmodat2
|
||||
@@ -78,93 +63,6 @@ static inline int missing_ioprio_set(int which, int who, int ioprio) {
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_MEMFD_CREATE
|
||||
static inline int missing_memfd_create(const char *name, unsigned int flags) {
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
|
||||
# define memfd_create missing_memfd_create
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_GETRANDOM
|
||||
/* glibc says getrandom() returns ssize_t */
|
||||
static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
|
||||
return syscall(__NR_getrandom, buffer, count, flags);
|
||||
}
|
||||
|
||||
# define getrandom missing_getrandom
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
/* The syscall has been defined since forever, but the glibc wrapper was missing. */
|
||||
#if !HAVE_GETTID
|
||||
static inline pid_t missing_gettid(void) {
|
||||
# if defined __NR_gettid && __NR_gettid >= 0
|
||||
return (pid_t) syscall(__NR_gettid);
|
||||
# else
|
||||
# error "__NR_gettid not defined"
|
||||
# endif
|
||||
}
|
||||
|
||||
# define gettid missing_gettid
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_NAME_TO_HANDLE_AT
|
||||
struct file_handle {
|
||||
unsigned int handle_bytes;
|
||||
int handle_type;
|
||||
unsigned char f_handle[0];
|
||||
};
|
||||
|
||||
static inline int missing_name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
|
||||
# ifdef __NR_name_to_handle_at
|
||||
return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define name_to_handle_at missing_name_to_handle_at
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_SETNS
|
||||
static inline int missing_setns(int fd, int nstype) {
|
||||
return syscall(__NR_setns, fd, nstype);
|
||||
}
|
||||
|
||||
# define setns missing_setns
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
static inline pid_t raw_getpid(void) {
|
||||
#if defined(__alpha__)
|
||||
return (pid_t) syscall(__NR_getxpid);
|
||||
#else
|
||||
return (pid_t) syscall(__NR_getpid);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_RENAMEAT2
|
||||
static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
|
||||
return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
|
||||
}
|
||||
|
||||
# define renameat2 missing_renameat2
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_KCMP
|
||||
static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
|
||||
return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
|
||||
@@ -182,12 +80,16 @@ static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg
|
||||
# define keyctl missing_keyctl
|
||||
}
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
|
||||
return syscall(__NR_add_key, type, description, payload, plen, ringid);
|
||||
|
||||
# define add_key missing_add_key
|
||||
}
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
|
||||
return syscall(__NR_request_key, type, description, callout_info, destringid);
|
||||
|
||||
@@ -197,34 +99,11 @@ static inline key_serial_t missing_request_key(const char *type, const char *des
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_COPY_FILE_RANGE
|
||||
static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
|
||||
int fd_out, loff_t *off_out,
|
||||
size_t len,
|
||||
unsigned int flags) {
|
||||
# ifdef __NR_copy_file_range
|
||||
return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define copy_file_range missing_copy_file_range
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_BPF
|
||||
union bpf_attr;
|
||||
|
||||
static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
|
||||
#ifdef __NR_bpf
|
||||
return (int) syscall(__NR_bpf, cmd, attr, size);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
# define bpf missing_bpf
|
||||
@@ -232,28 +111,6 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_STATX
|
||||
struct statx;
|
||||
|
||||
static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) {
|
||||
# ifdef __NR_statx
|
||||
return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This typedef is supposed to be always defined. */
|
||||
typedef struct statx struct_statx;
|
||||
|
||||
#if !HAVE_STATX
|
||||
# define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer)
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_SET_MEMPOLICY
|
||||
enum {
|
||||
MPOL_DEFAULT,
|
||||
@@ -265,14 +122,7 @@ enum {
|
||||
|
||||
static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask,
|
||||
unsigned long maxnode) {
|
||||
long i;
|
||||
# if defined __NR_set_mempolicy && __NR_set_mempolicy >= 0
|
||||
i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
i = -1;
|
||||
# endif
|
||||
return i;
|
||||
return syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
|
||||
}
|
||||
|
||||
# define set_mempolicy missing_set_mempolicy
|
||||
@@ -282,14 +132,7 @@ static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask
|
||||
static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
|
||||
unsigned long maxnode, void *addr,
|
||||
unsigned long flags) {
|
||||
long i;
|
||||
# if defined __NR_get_mempolicy && __NR_get_mempolicy >= 0
|
||||
i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
i = -1;
|
||||
# endif
|
||||
return i;
|
||||
return syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
|
||||
}
|
||||
|
||||
# define get_mempolicy missing_get_mempolicy
|
||||
@@ -298,6 +141,7 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_PIDFD_SEND_SIGNAL
|
||||
/* since kernel v5.1 (3eb39f47934f9d5a3027fe00d906a45fe3a15fad) */
|
||||
static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
|
||||
return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
|
||||
}
|
||||
@@ -305,7 +149,10 @@ static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, un
|
||||
# define pidfd_send_signal missing_pidfd_send_signal
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_PIDFD_OPEN
|
||||
/* since kernel v5.3 (7615d9e1780e26e0178c93c55b73309a5dc093d7) */
|
||||
static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
|
||||
return syscall(__NR_pidfd_open, pid, flags);
|
||||
}
|
||||
@@ -315,27 +162,9 @@ static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_RT_SIGQUEUEINFO
|
||||
static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) {
|
||||
# if defined __NR_rt_sigqueueinfo && __NR_rt_sigqueueinfo >= 0
|
||||
return syscall(__NR_rt_sigqueueinfo, tgid, sig, info);
|
||||
# else
|
||||
# error "__NR_rt_sigqueueinfo not defined"
|
||||
# endif
|
||||
}
|
||||
|
||||
# define rt_sigqueueinfo missing_rt_sigqueueinfo
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_RT_TGSIGQUEUEINFO
|
||||
static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) {
|
||||
# if defined __NR_rt_tgsigqueueinfo && __NR_rt_tgsigqueueinfo >= 0
|
||||
return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info);
|
||||
# else
|
||||
# error "__NR_rt_tgsigqueueinfo not defined"
|
||||
# endif
|
||||
}
|
||||
|
||||
# define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo
|
||||
@@ -344,27 +173,21 @@ static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, sigi
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_EXECVEAT
|
||||
/* since kernel v3.19 (51f39a1f0cea1cacf8c787f652f26dfee9611874) */
|
||||
static inline int missing_execveat(int dirfd, const char *pathname,
|
||||
char *const argv[], char *const envp[],
|
||||
int flags) {
|
||||
# if defined __NR_execveat && __NR_execveat >= 0
|
||||
return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# undef AT_EMPTY_PATH
|
||||
# define AT_EMPTY_PATH 0x1000
|
||||
# define execveat missing_execveat
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_CLOSE_RANGE
|
||||
/* since kernel v5.9 (9b4feb630e8e9801603f3cab3a36369e3c1cf88d) */
|
||||
static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsigned flags) {
|
||||
# ifdef __NR_close_range
|
||||
/* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while
|
||||
* userspace exclusively uses signed integers for fds. glibc chose to expose it 1:1 however, hence we
|
||||
* do so here too, even if we end up passing signed fds to it most of the time. */
|
||||
@@ -372,10 +195,6 @@ static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsign
|
||||
first_fd,
|
||||
end_fd,
|
||||
flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define close_range missing_close_range
|
||||
@@ -383,243 +202,8 @@ static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsign
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_MOUNT_SETATTR
|
||||
|
||||
#if !HAVE_STRUCT_MOUNT_ATTR
|
||||
struct mount_attr {
|
||||
uint64_t attr_set;
|
||||
uint64_t attr_clr;
|
||||
uint64_t propagation;
|
||||
uint64_t userns_fd;
|
||||
};
|
||||
#else
|
||||
struct mount_attr;
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_RDONLY
|
||||
#define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_NOSUID
|
||||
#define MOUNT_ATTR_NOSUID 0x00000002 /* Ignore suid and sgid bits */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_NODEV
|
||||
#define MOUNT_ATTR_NODEV 0x00000004 /* Disallow access to device special files */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_NOEXEC
|
||||
#define MOUNT_ATTR_NOEXEC 0x00000008 /* Disallow program execution */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR__ATIME
|
||||
#define MOUNT_ATTR__ATIME 0x00000070 /* Setting on how atime should be updated */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_RELATIME
|
||||
#define MOUNT_ATTR_RELATIME 0x00000000 /* - Update atime relative to mtime/ctime. */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_NOATIME
|
||||
#define MOUNT_ATTR_NOATIME 0x00000010 /* - Do not update access times. */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_STRICTATIME
|
||||
#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_NODIRATIME
|
||||
#define MOUNT_ATTR_NODIRATIME 0x00000080 /* Do not update directory access times */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_IDMAP
|
||||
#define MOUNT_ATTR_IDMAP 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_NOSYMFOLLOW
|
||||
#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks */
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_SIZE_VER0
|
||||
#define MOUNT_ATTR_SIZE_VER0 32 /* sizeof first published struct */
|
||||
#endif
|
||||
|
||||
#ifndef AT_RECURSIVE
|
||||
#define AT_RECURSIVE 0x8000
|
||||
#endif
|
||||
|
||||
static inline int missing_mount_setattr(
|
||||
int dfd,
|
||||
const char *path,
|
||||
unsigned flags,
|
||||
struct mount_attr *attr,
|
||||
size_t size) {
|
||||
|
||||
# if defined __NR_mount_setattr && __NR_mount_setattr >= 0
|
||||
return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define mount_setattr missing_mount_setattr
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_OPEN_TREE
|
||||
|
||||
#ifndef OPEN_TREE_CLONE
|
||||
#define OPEN_TREE_CLONE 1
|
||||
#endif
|
||||
|
||||
#ifndef OPEN_TREE_CLOEXEC
|
||||
#define OPEN_TREE_CLOEXEC O_CLOEXEC
|
||||
#endif
|
||||
|
||||
static inline int missing_open_tree(
|
||||
int dfd,
|
||||
const char *filename,
|
||||
unsigned flags) {
|
||||
|
||||
# if defined __NR_open_tree && __NR_open_tree >= 0
|
||||
return syscall(__NR_open_tree, dfd, filename, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define open_tree missing_open_tree
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#ifndef MOVE_MOUNT_BENEATH
|
||||
#define MOVE_MOUNT_BENEATH 0x00000200
|
||||
#endif
|
||||
|
||||
#if !HAVE_MOVE_MOUNT
|
||||
|
||||
#ifndef MOVE_MOUNT_F_EMPTY_PATH
|
||||
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
|
||||
#endif
|
||||
|
||||
#ifndef MOVE_MOUNT_T_EMPTY_PATH
|
||||
#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
|
||||
#endif
|
||||
|
||||
static inline int missing_move_mount(
|
||||
int from_dfd,
|
||||
const char *from_pathname,
|
||||
int to_dfd,
|
||||
const char *to_pathname,
|
||||
unsigned flags) {
|
||||
|
||||
# if defined __NR_move_mount && __NR_move_mount >= 0
|
||||
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define move_mount missing_move_mount
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_FSOPEN
|
||||
|
||||
#ifndef FSOPEN_CLOEXEC
|
||||
#define FSOPEN_CLOEXEC 0x00000001
|
||||
#endif
|
||||
|
||||
static inline int missing_fsopen(const char *fsname, unsigned flags) {
|
||||
# if defined __NR_fsopen && __NR_fsopen >= 0
|
||||
return syscall(__NR_fsopen, fsname, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define fsopen missing_fsopen
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_FSCONFIG
|
||||
|
||||
#ifndef FSCONFIG_SET_FLAG
|
||||
#define FSCONFIG_SET_FLAG 0 /* Set parameter, supplying no value */
|
||||
#endif
|
||||
|
||||
#ifndef FSCONFIG_SET_STRING
|
||||
#define FSCONFIG_SET_STRING 1 /* Set parameter, supplying a string value */
|
||||
#endif
|
||||
|
||||
#ifndef FSCONFIG_SET_FD
|
||||
#define FSCONFIG_SET_FD 5 /* Set parameter, supplying an object by fd */
|
||||
#endif
|
||||
|
||||
#ifndef FSCONFIG_CMD_CREATE
|
||||
#define FSCONFIG_CMD_CREATE 6 /* Invoke superblock creation */
|
||||
#endif
|
||||
|
||||
static inline int missing_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux) {
|
||||
# if defined __NR_fsconfig && __NR_fsconfig >= 0
|
||||
return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define fsconfig missing_fsconfig
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_FSMOUNT
|
||||
|
||||
#ifndef FSMOUNT_CLOEXEC
|
||||
#define FSMOUNT_CLOEXEC 0x00000001
|
||||
#endif
|
||||
|
||||
static inline int missing_fsmount(int fd, unsigned flags, unsigned ms_flags) {
|
||||
# if defined __NR_fsmount && __NR_fsmount >= 0
|
||||
return syscall(__NR_fsmount, fd, flags, ms_flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define fsmount missing_fsmount
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_GETDENTS64
|
||||
|
||||
static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
|
||||
# if defined __NR_getdents64 && __NR_getdents64 >= 0
|
||||
return syscall(__NR_getdents64, fd, buffer, length);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define getdents64 missing_getdents64
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_SCHED_SETATTR
|
||||
|
||||
/* since kernel 3.14 (e6cfc0295c7d51b008999a8b13a44fb43f8685ea) */
|
||||
static inline ssize_t missing_sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) {
|
||||
return syscall(__NR_sched_setattr, pid, attr, flags);
|
||||
}
|
||||
@@ -644,15 +228,38 @@ int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags,
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_QUOTACTL_FD
|
||||
|
||||
/* since kernel v5.14 (64c2c2c62f92339b176ea24403d8db16db36f9e6) */
|
||||
static inline int missing_quotactl_fd(int fd, int cmd, int id, void *addr) {
|
||||
#if defined __NR_quotactl_fd
|
||||
return syscall(__NR_quotactl_fd, fd, cmd, id, addr);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
# define quotactl_fd missing_quotactl_fd
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_SETXATTRAT
|
||||
/* since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394) */
|
||||
struct xattr_args {
|
||||
_align_(8) uint64_t value;
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static inline int missing_setxattrat(int fd, const char *path, int at_flags, const char *name, const struct xattr_args *args, size_t size) {
|
||||
return syscall(__NR_setxattrat, fd, path, at_flags, name, args, size);
|
||||
}
|
||||
|
||||
# define setxattrat missing_setxattrat
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_REMOVEXATTRAT
|
||||
/* since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394) */
|
||||
static inline int missing_removexattrat(int fd, const char *path, int at_flags, const char *name) {
|
||||
return syscall(__NR_removexattrat, fd, path, at_flags, name);
|
||||
}
|
||||
|
||||
# define removexattrat missing_removexattrat
|
||||
#endif
|
||||
|
@@ -1,13 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
/* If threads.h doesn't exist, then define our own thread_local to match C11's thread_local. */
|
||||
#if HAVE_THREADS_H
|
||||
# include <threads.h>
|
||||
#elif !(defined(thread_local))
|
||||
# ifndef __STDC_NO_THREADS__
|
||||
# define thread_local _Thread_local
|
||||
# else
|
||||
# define thread_local __thread
|
||||
# endif
|
||||
#endif
|
@@ -1,12 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <uchar.h>
|
||||
|
||||
#if !HAVE_CHAR32_T
|
||||
# define char32_t uint32_t
|
||||
#endif
|
||||
|
||||
#if !HAVE_CHAR16_T
|
||||
# define char16_t uint16_t
|
||||
#endif
|
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* since glibc-2.36 */
|
||||
#ifndef P_PIDFD
|
||||
# define P_PIDFD 3
|
||||
#else
|
||||
|
819
src/libnm-systemd-shared/src/basic/mountpoint-util.c
Normal file
819
src/libnm-systemd-shared/src/basic/mountpoint-util.c
Normal file
@@ -0,0 +1,819 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "filesystems.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "missing_fcntl.h"
|
||||
#include "missing_fs.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "mkdir.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
|
||||
* any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
|
||||
* is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
|
||||
* EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
|
||||
* from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
|
||||
* with large file handles anyway. */
|
||||
#define ORIGINAL_MAX_HANDLE_SZ 128
|
||||
|
||||
bool is_name_to_handle_at_fatal_error(int err) {
|
||||
/* name_to_handle_at() can return "acceptable" errors that are due to the context. For example
|
||||
* the file system does not support name_to_handle_at() (EOPNOTSUPP), or the syscall was blocked
|
||||
* (EACCES/EPERM; maybe through seccomp, because we are running inside of a container), or
|
||||
* the mount point is not triggered yet (EOVERFLOW, think autofs+nfs4), or some general name_to_handle_at()
|
||||
* flakiness (EINVAL). However other errors are not supposed to happen and therefore are considered
|
||||
* fatal ones. */
|
||||
|
||||
assert(err < 0);
|
||||
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(err))
|
||||
return false;
|
||||
if (ERRNO_IS_NEG_PRIVILEGE(err))
|
||||
return false;
|
||||
|
||||
return !IN_SET(err, -EOVERFLOW, -EINVAL);
|
||||
}
|
||||
|
||||
int name_to_handle_at_loop(
|
||||
int fd,
|
||||
const char *path,
|
||||
struct file_handle **ret_handle,
|
||||
int *ret_mnt_id,
|
||||
int flags) {
|
||||
|
||||
size_t n = ORIGINAL_MAX_HANDLE_SZ;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|AT_HANDLE_FID)) == 0);
|
||||
|
||||
/* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
|
||||
* buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
|
||||
* start value, it is not an upper bound on the buffer size required.
|
||||
*
|
||||
* This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
|
||||
* as NULL if there's no interest in either. */
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ struct file_handle *h = NULL;
|
||||
int mnt_id = -1;
|
||||
|
||||
h = malloc0(offsetof(struct file_handle, f_handle) + n);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
h->handle_bytes = n;
|
||||
|
||||
if (name_to_handle_at(fd, strempty(path), h, &mnt_id, flags) >= 0) {
|
||||
|
||||
if (ret_handle)
|
||||
*ret_handle = TAKE_PTR(h);
|
||||
|
||||
if (ret_mnt_id)
|
||||
*ret_mnt_id = mnt_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (errno != EOVERFLOW)
|
||||
return -errno;
|
||||
|
||||
if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
|
||||
|
||||
/* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
|
||||
* buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
|
||||
* be filled in, and the caller was interested in only the mount ID an nothing else. */
|
||||
|
||||
*ret_mnt_id = mnt_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by
|
||||
* something else (apparently EOVERFLOW is returned for untriggered nfs4 autofs mounts
|
||||
* sometimes), not by the too small buffer. In that case propagate EOVERFLOW */
|
||||
if (h->handle_bytes <= n)
|
||||
return -EOVERFLOW;
|
||||
|
||||
/* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
|
||||
n = h->handle_bytes;
|
||||
|
||||
/* paranoia: check for overflow (note that .handle_bytes is unsigned only) */
|
||||
if (n > UINT_MAX - offsetof(struct file_handle, f_handle))
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
int name_to_handle_at_try_fid(
|
||||
int fd,
|
||||
const char *path,
|
||||
struct file_handle **ret_handle,
|
||||
int *ret_mnt_id,
|
||||
int flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
|
||||
/* First issues name_to_handle_at() with AT_HANDLE_FID. If this fails and this is not a fatal error
|
||||
* we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID
|
||||
* (i.e. older than Linux 6.5). */
|
||||
|
||||
r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags | AT_HANDLE_FID);
|
||||
if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags & ~AT_HANDLE_FID);
|
||||
}
|
||||
|
||||
static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mnt_id) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_ int subfd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
|
||||
assert(ret_mnt_id);
|
||||
|
||||
if ((flags & AT_EMPTY_PATH) && isempty(filename))
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
else {
|
||||
subfd = openat(fd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_FOLLOW ? 0 : O_NOFOLLOW));
|
||||
if (subfd < 0)
|
||||
return -errno;
|
||||
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", subfd);
|
||||
}
|
||||
|
||||
_cleanup_free_ char *p = NULL;
|
||||
r = get_proc_field(path, "mnt_id", &p);
|
||||
if (r == -ENOENT)
|
||||
return -EBADF;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return safe_atoi(p, ret_mnt_id);
|
||||
}
|
||||
|
||||
static bool filename_possibly_with_slash_suffix(const char *s) {
|
||||
const char *slash, *copied;
|
||||
|
||||
/* Checks whether the specified string is either file name, or a filename with a suffix of
|
||||
* slashes. But nothing else.
|
||||
*
|
||||
* this is OK: foo, bar, foo/, bar/, foo//, bar///
|
||||
* this is not OK: "", "/", "/foo", "foo/bar", ".", ".." … */
|
||||
|
||||
slash = strchr(s, '/');
|
||||
if (!slash)
|
||||
return filename_is_valid(s);
|
||||
|
||||
if (slash - s > PATH_MAX) /* We want to allocate on the stack below, hence do a size check first */
|
||||
return false;
|
||||
|
||||
if (slash[strspn(slash, "/")] != 0) /* Check that the suffix consist only of one or more slashes */
|
||||
return false;
|
||||
|
||||
copied = strndupa_safe(s, slash - s);
|
||||
return filename_is_valid(copied);
|
||||
}
|
||||
|
||||
bool file_handle_equal(const struct file_handle *a, const struct file_handle *b) {
|
||||
if (a == b)
|
||||
return true;
|
||||
if (!a != !b)
|
||||
return false;
|
||||
if (a->handle_type != b->handle_type)
|
||||
return false;
|
||||
|
||||
return memcmp_nn(a->f_handle, a->handle_bytes, b->f_handle, b->handle_bytes) == 0;
|
||||
}
|
||||
|
||||
int is_mount_point_at(int fd, const char *filename, int flags) {
|
||||
bool fd_is_self;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
|
||||
|
||||
if (isempty(filename)) {
|
||||
if (fd == AT_FDCWD)
|
||||
filename = ".";
|
||||
else {
|
||||
/* If the file name is empty we'll see if the specified 'fd' is a mount point.
|
||||
* That's only supported by statx(), or if the inode specified via 'fd' refers to a
|
||||
* directory. Otherwise, we'll have to fail (ENOTDIR), because we have no kernel API
|
||||
* to query the information we need. */
|
||||
flags |= AT_EMPTY_PATH;
|
||||
filename = "";
|
||||
}
|
||||
|
||||
fd_is_self = true;
|
||||
} else if (STR_IN_SET(filename, ".", "./"))
|
||||
fd_is_self = true;
|
||||
else {
|
||||
/* Insist that the specified filename is actually a filename, and not a path, i.e. some inode
|
||||
* further up or down the tree then immediately below the specified directory fd. */
|
||||
if (!filename_possibly_with_slash_suffix(filename))
|
||||
return -EINVAL;
|
||||
|
||||
fd_is_self = false;
|
||||
}
|
||||
|
||||
/* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
|
||||
* since kernel 5.8.
|
||||
*
|
||||
* If that fails, our second try is the name_to_handle_at() syscall, which tells us the mount id and
|
||||
* an opaque file "handle". It is not supported everywhere though (kernel compile-time option, not
|
||||
* all file systems are hooked up). If it works the mount id is usually good enough to tell us
|
||||
* whether something is a mount point.
|
||||
*
|
||||
* If that didn't work we will try to read the mount id from /proc/self/fdinfo/<fd>. This is almost
|
||||
* as good as name_to_handle_at(), however, does not return the opaque file handle. The opaque file
|
||||
* handle is pretty useful to detect the root directory, which we should always consider a mount
|
||||
* point. Hence we use this only as fallback.
|
||||
*
|
||||
* Note that traditionally the check is done via fstat()-based st_dev comparisons. However, various
|
||||
* file systems don't guarantee same st_dev across single fs anymore, e.g. unionfs exposes file systems
|
||||
* with a variety of st_dev reported. Also, btrfs subvolumes have different st_dev, even though
|
||||
* they aren't real mounts of their own. */
|
||||
|
||||
struct statx sx = {}; /* explicitly initialize the struct to make msan silent. */
|
||||
if (statx(fd, filename,
|
||||
at_flags_normalize_nofollow(flags) |
|
||||
AT_NO_AUTOMOUNT | /* don't trigger automounts – mounts are a local concept, hence no need to trigger automounts to determine STATX_ATTR_MOUNT_ROOT */
|
||||
AT_STATX_DONT_SYNC, /* don't go to the network for this – for similar reasons */
|
||||
STATX_TYPE,
|
||||
&sx) < 0)
|
||||
return -errno;
|
||||
|
||||
if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) /* yay! */
|
||||
return FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
|
||||
|
||||
_cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
|
||||
int mount_id = -1, mount_id_parent = -1;
|
||||
bool nosupp = false;
|
||||
|
||||
r = name_to_handle_at_try_fid(fd, filename, &h, &mount_id, flags);
|
||||
if (r < 0) {
|
||||
if (is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(r))
|
||||
goto fallback_fdinfo;
|
||||
|
||||
/* This file system does not support name_to_handle_at(), hence let's see if the upper fs
|
||||
* supports it (in which case it is a mount point), otherwise fall back to the fdinfo logic. */
|
||||
nosupp = true;
|
||||
}
|
||||
|
||||
if (fd_is_self)
|
||||
r = name_to_handle_at_try_fid(fd, "..", &h_parent, &mount_id_parent, 0); /* can't work for non-directories 😢 */
|
||||
else
|
||||
r = name_to_handle_at_try_fid(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
|
||||
if (r < 0) {
|
||||
if (is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(r))
|
||||
goto fallback_fdinfo;
|
||||
if (nosupp)
|
||||
/* Both the parent and the directory can't do name_to_handle_at() */
|
||||
goto fallback_fdinfo;
|
||||
|
||||
/* The parent can't do name_to_handle_at() but the directory we are
|
||||
* interested in can? If so, it must be a mount point. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The parent can do name_to_handle_at() but the directory we are interested in can't? If
|
||||
* so, it must be a mount point. */
|
||||
if (nosupp)
|
||||
return 1;
|
||||
|
||||
/* If the file handle for the directory we are interested in and its parent are identical,
|
||||
* we assume this is the root directory, which is a mount point. */
|
||||
if (file_handle_equal(h_parent, h))
|
||||
return 1;
|
||||
|
||||
return mount_id != mount_id_parent;
|
||||
|
||||
fallback_fdinfo:
|
||||
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (fd_is_self)
|
||||
r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); /* can't work for non-directories 😢 */
|
||||
else
|
||||
r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mount_id != mount_id_parent)
|
||||
return 1;
|
||||
|
||||
/* Hmm, so, the mount ids are the same. This leaves one special case though for the root file
|
||||
* system. For that, let's see if the parent directory has the same inode as we are interested
|
||||
* in. */
|
||||
|
||||
struct stat a, b;
|
||||
|
||||
/* yay for fstatat() taking a different set of flags than the other _at() above */
|
||||
if (fstatat(fd, filename, &a, at_flags_normalize_nofollow(flags)) < 0)
|
||||
return -errno;
|
||||
|
||||
if (fd_is_self)
|
||||
r = fstatat(fd, "..", &b, 0);
|
||||
else
|
||||
r = fstatat(fd, "", &b, AT_EMPTY_PATH);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
/* A directory with same device and inode as its parent must be the root directory. Otherwise
|
||||
* not a mount point.
|
||||
*
|
||||
* NB: we avoid inode_same_at() here because it internally attempts name_to_handle_at_try_fid() first,
|
||||
* which is redundant. */
|
||||
return stat_inode_same(&a, &b);
|
||||
}
|
||||
|
||||
/* flags can be AT_SYMLINK_FOLLOW or 0 */
|
||||
int path_is_mount_point_full(const char *path, const char *root, int flags) {
|
||||
_cleanup_close_ int dfd = -EBADF;
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
|
||||
assert(path);
|
||||
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
|
||||
|
||||
if (path_equal(path, "/"))
|
||||
return 1;
|
||||
|
||||
/* we need to resolve symlinks manually, we can't just rely on is_mount_point_at() to do that for us;
|
||||
* if we have a structure like /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
|
||||
* look at needs to be /usr, not /. */
|
||||
dfd = chase_and_open_parent(path, root,
|
||||
CHASE_TRAIL_SLASH|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : CHASE_NOFOLLOW),
|
||||
&fn);
|
||||
if (dfd < 0)
|
||||
return dfd;
|
||||
|
||||
return is_mount_point_at(dfd, fn, flags);
|
||||
}
|
||||
|
||||
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
|
||||
int r;
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
assert(ret);
|
||||
|
||||
r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
|
||||
if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
|
||||
}
|
||||
|
||||
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
|
||||
struct statx sx;
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
assert(ret);
|
||||
|
||||
if (statx(dir_fd,
|
||||
strempty(path),
|
||||
(isempty(path) ? AT_EMPTY_PATH : AT_SYMLINK_NOFOLLOW) |
|
||||
AT_NO_AUTOMOUNT | /* don't trigger automounts, mnt_id is a local concept */
|
||||
AT_STATX_DONT_SYNC, /* don't go to the network, mnt_id is a local concept */
|
||||
STATX_MNT_ID,
|
||||
&sx) < 0)
|
||||
return -errno;
|
||||
|
||||
if (FLAGS_SET(sx.stx_mask, STATX_MNT_ID)) {
|
||||
*ret = sx.stx_mnt_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return path_get_mnt_id_at_fallback(dir_fd, path, ret);
|
||||
}
|
||||
|
||||
bool fstype_is_network(const char *fstype) {
|
||||
const char *x;
|
||||
|
||||
x = startswith(fstype, "fuse.");
|
||||
if (x)
|
||||
fstype = x;
|
||||
|
||||
if (nulstr_contains(filesystem_sets[FILESYSTEM_SET_NETWORK].value, fstype))
|
||||
return true;
|
||||
|
||||
/* Filesystems not present in the internal database */
|
||||
return STR_IN_SET(fstype,
|
||||
"davfs",
|
||||
"glusterfs",
|
||||
"lustre",
|
||||
"sshfs");
|
||||
}
|
||||
|
||||
bool fstype_needs_quota(const char *fstype) {
|
||||
/* 1. quotacheck needs to be run for some filesystems after they are mounted
|
||||
* if the filesystem was not unmounted cleanly.
|
||||
* 2. You may need to run quotaon to enable quota usage tracking and/or
|
||||
* enforcement.
|
||||
* ext2 - needs 1) and 2)
|
||||
* ext3 - needs 2) if configured using usrjquota/grpjquota mount options
|
||||
* ext4 - needs 1) if created without journal, needs 2) if created without QUOTA
|
||||
* filesystem feature
|
||||
* reiserfs - needs 2).
|
||||
* jfs - needs 2)
|
||||
* f2fs - needs 2) if configured using usrjquota/grpjquota/prjjquota mount options
|
||||
* xfs - nothing needed
|
||||
* gfs2 - nothing needed
|
||||
* ocfs2 - nothing needed
|
||||
* btrfs - nothing needed
|
||||
* for reference see filesystem and quota manpages */
|
||||
return STR_IN_SET(fstype,
|
||||
"ext2",
|
||||
"ext3",
|
||||
"ext4",
|
||||
"reiserfs",
|
||||
"jfs",
|
||||
"f2fs");
|
||||
}
|
||||
|
||||
bool fstype_is_api_vfs(const char *fstype) {
|
||||
assert(fstype);
|
||||
|
||||
const FilesystemSet *fs;
|
||||
FOREACH_ARGUMENT(fs,
|
||||
filesystem_sets + FILESYSTEM_SET_BASIC_API,
|
||||
filesystem_sets + FILESYSTEM_SET_AUXILIARY_API,
|
||||
filesystem_sets + FILESYSTEM_SET_PRIVILEGED_API,
|
||||
filesystem_sets + FILESYSTEM_SET_TEMPORARY)
|
||||
if (nulstr_contains(fs->value, fstype))
|
||||
return true;
|
||||
|
||||
/* Filesystems not present in the internal database */
|
||||
return STR_IN_SET(fstype,
|
||||
"autofs",
|
||||
"cpuset",
|
||||
"devtmpfs");
|
||||
}
|
||||
|
||||
bool fstype_is_blockdev_backed(const char *fstype) {
|
||||
const char *x;
|
||||
|
||||
x = startswith(fstype, "fuse.");
|
||||
if (x)
|
||||
fstype = x;
|
||||
|
||||
return !streq(fstype, "9p") && !fstype_is_network(fstype) && !fstype_is_api_vfs(fstype);
|
||||
}
|
||||
|
||||
bool fstype_is_ro(const char *fstype) {
|
||||
/* All Linux file systems that are necessarily read-only */
|
||||
return STR_IN_SET(fstype,
|
||||
"DM_verity_hash",
|
||||
"cramfs",
|
||||
"erofs",
|
||||
"iso9660",
|
||||
"squashfs");
|
||||
}
|
||||
|
||||
bool fstype_can_discard(const char *fstype) {
|
||||
assert(fstype);
|
||||
|
||||
/* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
|
||||
* not be allowed in our MAC context. */
|
||||
if (STR_IN_SET(fstype, "btrfs", "f2fs", "ext4", "vfat", "xfs"))
|
||||
return true;
|
||||
|
||||
/* On new kernels we can just ask the kernel */
|
||||
return mount_option_supported(fstype, "discard", NULL) > 0;
|
||||
}
|
||||
|
||||
const char* fstype_norecovery_option(const char *fstype) {
|
||||
int r;
|
||||
|
||||
assert(fstype);
|
||||
|
||||
/* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
|
||||
* not be allowed in our MAC context. */
|
||||
if (STR_IN_SET(fstype, "ext3", "ext4", "xfs"))
|
||||
return "norecovery";
|
||||
|
||||
/* btrfs dropped support for the "norecovery" option in 6.8
|
||||
* (https://github.com/torvalds/linux/commit/a1912f712188291f9d7d434fba155461f1ebef66) and replaced
|
||||
* it with rescue=nologreplay, so we check for the new name first and fall back to checking for the
|
||||
* old name if the new name doesn't work. */
|
||||
if (streq(fstype, "btrfs")) {
|
||||
r = mount_option_supported(fstype, "rescue=nologreplay", NULL);
|
||||
if (r == -EAGAIN) {
|
||||
log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming old kernel with 'norecovery': %m");
|
||||
return "norecovery";
|
||||
}
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming it is not supported: %m");
|
||||
if (r > 0)
|
||||
return "rescue=nologreplay";
|
||||
}
|
||||
|
||||
/* On new kernels we can just ask the kernel */
|
||||
return mount_option_supported(fstype, "norecovery", NULL) > 0 ? "norecovery" : NULL;
|
||||
}
|
||||
|
||||
bool fstype_can_fmask_dmask(const char *fstype) {
|
||||
assert(fstype);
|
||||
|
||||
/* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
|
||||
* not be allowed in our MAC context. If we don't know ourselves, on new kernels we can just ask the
|
||||
* kernel. */
|
||||
return streq(fstype, "vfat") || (mount_option_supported(fstype, "fmask", "0177") > 0 && mount_option_supported(fstype, "dmask", "0077") > 0);
|
||||
}
|
||||
|
||||
bool fstype_can_uid_gid(const char *fstype) {
|
||||
/* All file systems that have a uid=/gid= mount option that fixates the owners of all files and
|
||||
* directories, current and future. Note that this does *not* ask the kernel via
|
||||
* mount_option_supported() here because the uid=/gid= setting of various file systems mean different
|
||||
* things: some apply it only to the root dir inode, others to all inodes in the file system. Thus we
|
||||
* maintain the curated list below. 😢 */
|
||||
|
||||
return STR_IN_SET(fstype,
|
||||
"adfs",
|
||||
"exfat",
|
||||
"fat",
|
||||
"hfs",
|
||||
"hpfs",
|
||||
"iso9660",
|
||||
"msdos",
|
||||
"ntfs",
|
||||
"vfat");
|
||||
}
|
||||
|
||||
int dev_is_devtmpfs(void) {
|
||||
_cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
|
||||
int mount_id, r;
|
||||
char *e;
|
||||
|
||||
r = path_get_mnt_id("/dev", &mount_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fopen_unlocked("/proc/self/mountinfo", "re", &proc_self_mountinfo);
|
||||
if (r == -ENOENT)
|
||||
return proc_mounted() > 0 ? -ENOENT : -ENOSYS;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
int mid;
|
||||
|
||||
r = read_line(proc_self_mountinfo, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (sscanf(line, "%i", &mid) != 1)
|
||||
continue;
|
||||
|
||||
if (mid != mount_id)
|
||||
continue;
|
||||
|
||||
e = strstrafter(line, " - ");
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
/* accept any name that starts with the currently expected type */
|
||||
if (startswith(e, "devtmpfs"))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mount_fd(
|
||||
const char *source,
|
||||
int target_fd,
|
||||
const char *filesystemtype,
|
||||
unsigned long mountflags,
|
||||
const void *data) {
|
||||
|
||||
assert(target_fd >= 0);
|
||||
|
||||
if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* ENOENT can mean two things: either that the source is missing, or that /proc/ isn't
|
||||
* mounted. Check for the latter to generate better error messages. */
|
||||
if (proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mount_nofollow(
|
||||
const char *source,
|
||||
const char *target,
|
||||
const char *filesystemtype,
|
||||
unsigned long mountflags,
|
||||
const void *data) {
|
||||
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
|
||||
assert(target);
|
||||
|
||||
/* In almost all cases we want to manipulate the mount table without following symlinks, hence
|
||||
* mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is
|
||||
* not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early
|
||||
* initialization of namespacing/container stuff where /proc is not yet mounted (and maybe even the
|
||||
* fs to mount) we can only use traditional mount() directly.
|
||||
*
|
||||
* Note that this disables following only for the final component of the target, i.e symlinks within
|
||||
* the path of the target are honoured, as are symlinks in the source path everywhere. */
|
||||
|
||||
fd = open(target, O_PATH|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return mount_fd(source, fd, filesystemtype, mountflags, data);
|
||||
}
|
||||
|
||||
const char* mount_propagation_flag_to_string(unsigned long flags) {
|
||||
|
||||
switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
|
||||
case 0:
|
||||
return "";
|
||||
case MS_SHARED:
|
||||
return "shared";
|
||||
case MS_SLAVE:
|
||||
return "slave";
|
||||
case MS_PRIVATE:
|
||||
return "private";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int mount_propagation_flag_from_string(const char *name, unsigned long *ret) {
|
||||
|
||||
if (isempty(name))
|
||||
*ret = 0;
|
||||
else if (streq(name, "shared"))
|
||||
*ret = MS_SHARED;
|
||||
else if (streq(name, "slave"))
|
||||
*ret = MS_SLAVE;
|
||||
else if (streq(name, "private"))
|
||||
*ret = MS_PRIVATE;
|
||||
else
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool mount_propagation_flag_is_valid(unsigned long flag) {
|
||||
return IN_SET(flag, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE);
|
||||
}
|
||||
|
||||
bool mount_new_api_supported(void) {
|
||||
static int cache = -1;
|
||||
int r;
|
||||
|
||||
if (cache >= 0)
|
||||
return cache;
|
||||
|
||||
/* This is the newer API among the ones we use, so use it as boundary */
|
||||
r = RET_NERRNO(mount_setattr(-EBADF, NULL, 0, NULL, 0));
|
||||
if (r == 0 || ERRNO_IS_NOT_SUPPORTED(r)) /* This should return an error if it is working properly */
|
||||
return (cache = false);
|
||||
|
||||
return (cache = true);
|
||||
}
|
||||
|
||||
unsigned long ms_nosymfollow_supported(void) {
|
||||
_cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
|
||||
static int cache = -1;
|
||||
|
||||
/* Returns MS_NOSYMFOLLOW if it is supported, zero otherwise. */
|
||||
|
||||
if (cache >= 0)
|
||||
return cache ? MS_NOSYMFOLLOW : 0;
|
||||
|
||||
if (!mount_new_api_supported())
|
||||
goto not_supported;
|
||||
|
||||
/* Checks if MS_NOSYMFOLLOW is supported (which was added in 5.10). We use the new mount API's
|
||||
* mount_setattr() call for that, which was added in 5.12, which is close enough. */
|
||||
|
||||
fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
|
||||
if (fsfd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to open superblock context for tmpfs: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to create tmpfs superblock: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
|
||||
if (mntfd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to turn superblock fd into mount fd: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mount_setattr(mntfd, "", AT_EMPTY_PATH|AT_RECURSIVE,
|
||||
&(struct mount_attr) {
|
||||
.attr_set = MOUNT_ATTR_NOSYMFOLLOW,
|
||||
}, sizeof(struct mount_attr)) < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
goto not_supported;
|
||||
|
||||
log_debug_errno(errno, "Failed to set MOUNT_ATTR_NOSYMFOLLOW mount attribute: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache = true;
|
||||
return MS_NOSYMFOLLOW;
|
||||
|
||||
not_supported:
|
||||
cache = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mount_option_supported(const char *fstype, const char *key, const char *value) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
/* Checks if the specified file system supports a mount option. Returns > 0 if it supports it, == 0 if
|
||||
* it does not. Return -EAGAIN if we can't determine it. And any other error otherwise. */
|
||||
|
||||
assert(fstype);
|
||||
assert(key);
|
||||
|
||||
fd = fsopen(fstype, FSOPEN_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype);
|
||||
|
||||
/* Various file systems support fs context only in recent kernels (e.g. btrfs). For older kernels
|
||||
* fsconfig() with FSCONFIG_SET_STRING/FSCONFIG_SET_FLAG never fail. Which sucks, because we want to
|
||||
* use it for testing support, after all. Let's hence do a check if the file system got converted yet
|
||||
* first. */
|
||||
if (fsconfig(fd, FSCONFIG_SET_FD, "adefinitelynotexistingmountoption", NULL, fd) < 0) {
|
||||
/* If FSCONFIG_SET_FD is not supported for the fs, then the file system was not converted to
|
||||
* the new mount API yet. If it returns EINVAL the mount option doesn't exist, but the fstype
|
||||
* is converted. */
|
||||
if (errno == EOPNOTSUPP)
|
||||
return -EAGAIN; /* fs not converted to new mount API → don't know */
|
||||
if (errno != EINVAL)
|
||||
return log_debug_errno(errno, "Failed to check if file system '%s' has been converted to new mount API: %m", fstype);
|
||||
|
||||
/* So FSCONFIG_SET_FD worked, but the option didn't exist (we got EINVAL), this means the fs
|
||||
* is converted. Let's now ask the actual question we wonder about. */
|
||||
} else
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "FSCONFIG_SET_FD worked unexpectedly for '%s', whoa!", fstype);
|
||||
|
||||
if (value)
|
||||
r = fsconfig(fd, FSCONFIG_SET_STRING, key, value, 0);
|
||||
else
|
||||
r = fsconfig(fd, FSCONFIG_SET_FLAG, key, NULL, 0);
|
||||
if (r < 0) {
|
||||
if (errno == EINVAL)
|
||||
return false; /* EINVAL means option not supported. */
|
||||
|
||||
return log_debug_errno(errno, "Failed to set '%s%s%s' on '%s' superblock context: %m",
|
||||
key, value ? "=" : "", strempty(value), fstype);
|
||||
}
|
||||
|
||||
return true; /* works! */
|
||||
}
|
||||
|
||||
bool path_below_api_vfs(const char *p) {
|
||||
assert(p);
|
||||
|
||||
/* API VFS are either directly mounted on any of these three paths, or below it. */
|
||||
return PATH_STARTSWITH_SET(p, "/dev", "/sys", "/proc");
|
||||
}
|
87
src/libnm-systemd-shared/src/basic/mountpoint-util.h
Normal file
87
src/libnm-systemd-shared/src/basic/mountpoint-util.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
|
||||
* consume any space and udev isn't supposed to create regular file either. There's no limit on the
|
||||
* max number of inodes since such limit is hard to guess especially on large storage array
|
||||
* systems. */
|
||||
#define TMPFS_LIMITS_DEV ",size=4m"
|
||||
|
||||
/* The limit used for /dev in private namespaces. 4MB for contents of regular files. The number of
|
||||
* inodes should be relatively low in private namespaces but for now use a 64k limit. */
|
||||
#define TMPFS_LIMITS_PRIVATE_DEV ",size=4m,nr_inodes=64k"
|
||||
|
||||
/* Very little, if any use expected */
|
||||
#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
|
||||
#define TMPFS_LIMITS_SYS TMPFS_LIMITS_EMPTY_OR_ALMOST
|
||||
#define TMPFS_LIMITS_SYS_FS_CGROUP TMPFS_LIMITS_EMPTY_OR_ALMOST
|
||||
|
||||
/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
|
||||
* PID1 because 16MB of free space is required. */
|
||||
#define TMPFS_LIMITS_RUN ",size=20%,nr_inodes=800k"
|
||||
|
||||
/* The limit used for various nested tmpfs mounts, in particular for guests started by systemd-nspawn.
|
||||
* 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
|
||||
* translates to 1M inodes.
|
||||
* (On the host, /tmp is configured through a .mount unit file.) */
|
||||
#define NESTED_TMPFS_LIMITS ",size=10%,nr_inodes=400k"
|
||||
|
||||
/* More space for volatile root and /var */
|
||||
#define TMPFS_LIMITS_VAR ",size=25%,nr_inodes=1m"
|
||||
#define TMPFS_LIMITS_ROOTFS TMPFS_LIMITS_VAR
|
||||
#define TMPFS_LIMITS_VOLATILE_STATE TMPFS_LIMITS_VAR
|
||||
|
||||
bool is_name_to_handle_at_fatal_error(int err);
|
||||
|
||||
int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
|
||||
int name_to_handle_at_try_fid(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
|
||||
|
||||
bool file_handle_equal(const struct file_handle *a, const struct file_handle *b);
|
||||
|
||||
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret);
|
||||
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
|
||||
static inline int path_get_mnt_id(const char *path, int *ret) {
|
||||
return path_get_mnt_id_at(AT_FDCWD, path, ret);
|
||||
}
|
||||
|
||||
int is_mount_point_at(int fd, const char *filename, int flags);
|
||||
int path_is_mount_point_full(const char *path, const char *root, int flags);
|
||||
static inline int path_is_mount_point(const char *path) {
|
||||
return path_is_mount_point_full(path, NULL, 0);
|
||||
}
|
||||
|
||||
bool fstype_is_network(const char *fstype);
|
||||
bool fstype_needs_quota(const char *fstype);
|
||||
bool fstype_is_api_vfs(const char *fstype);
|
||||
bool fstype_is_blockdev_backed(const char *fstype);
|
||||
bool fstype_is_ro(const char *fsype);
|
||||
bool fstype_can_discard(const char *fstype);
|
||||
bool fstype_can_uid_gid(const char *fstype);
|
||||
bool fstype_can_fmask_dmask(const char *fstype);
|
||||
|
||||
const char* fstype_norecovery_option(const char *fstype);
|
||||
|
||||
int dev_is_devtmpfs(void);
|
||||
|
||||
int mount_nofollow(
|
||||
const char *source,
|
||||
const char *target,
|
||||
const char *filesystemtype,
|
||||
unsigned long mountflags,
|
||||
const void *data);
|
||||
|
||||
const char* mount_propagation_flag_to_string(unsigned long flags);
|
||||
int mount_propagation_flag_from_string(const char *name, unsigned long *ret);
|
||||
bool mount_propagation_flag_is_valid(unsigned long flag);
|
||||
|
||||
bool mount_new_api_supported(void);
|
||||
unsigned long ms_nosymfollow_supported(void);
|
||||
|
||||
int mount_option_supported(const char *fstype, const char *key, const char *value);
|
||||
|
||||
bool path_below_api_vfs(const char *p);
|
@@ -28,6 +28,8 @@ extern const struct namespace_info {
|
||||
|
||||
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag);
|
||||
|
||||
bool namespace_type_supported(NamespaceType type);
|
||||
|
||||
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type);
|
||||
int namespace_open_by_type(NamespaceType type);
|
||||
|
||||
@@ -86,7 +88,8 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
|
||||
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
|
||||
|
||||
int userns_acquire_empty(void);
|
||||
int userns_acquire(const char *uid_map, const char *gid_map);
|
||||
int userns_acquire(const char *uid_map, const char *gid_map, bool setgroups_deny);
|
||||
int userns_acquire_self_root(void);
|
||||
int userns_enter_and_pin(int userns_fd, pid_t *ret_pid);
|
||||
|
||||
int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);
|
||||
|
@@ -22,18 +22,10 @@ static inline void ordered_set_clear(OrderedSet *s) {
|
||||
return ordered_hashmap_clear((OrderedHashmap*) s);
|
||||
}
|
||||
|
||||
static inline void ordered_set_clear_free(OrderedSet *s) {
|
||||
return ordered_hashmap_clear_free((OrderedHashmap*) s);
|
||||
}
|
||||
|
||||
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
|
||||
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
|
||||
}
|
||||
|
||||
static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
|
||||
return (OrderedSet*) ordered_hashmap_free_free((OrderedHashmap*) s);
|
||||
}
|
||||
|
||||
static inline int ordered_set_contains(OrderedSet *s, const void *p) {
|
||||
return ordered_hashmap_contains((OrderedHashmap*) s, p);
|
||||
}
|
||||
@@ -91,19 +83,6 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s);
|
||||
#define ORDERED_SET_FOREACH(e, s) \
|
||||
_ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ))
|
||||
|
||||
#define ordered_set_clear_with_destructor(s, f) \
|
||||
({ \
|
||||
OrderedSet *_s = (s); \
|
||||
void *_item; \
|
||||
while ((_item = ordered_set_steal_first(_s))) \
|
||||
f(_item); \
|
||||
_s; \
|
||||
})
|
||||
#define ordered_set_free_with_destructor(s, f) \
|
||||
ordered_set_free(ordered_set_clear_with_destructor(s, f))
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
|
||||
|
||||
#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
|
||||
#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep)
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include "errno-list.h"
|
||||
#include "extract-word.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_network.h"
|
||||
#include "parse-util.h"
|
||||
@@ -527,7 +529,7 @@ int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int safe_atolli(const char *s, long long int *ret_lli) {
|
||||
int safe_atolli(const char *s, long long *ret_lli) {
|
||||
unsigned base = 0;
|
||||
char *x = NULL;
|
||||
long long l;
|
||||
@@ -777,18 +779,14 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
|
||||
/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
|
||||
* https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
|
||||
bool nft_identifier_valid(const char *id) {
|
||||
if (!id)
|
||||
if (isempty(id))
|
||||
return false;
|
||||
|
||||
size_t len = strlen(id);
|
||||
if (len == 0 || len > 31)
|
||||
if (strlen(id) >= NFT_NAME_MAXLEN)
|
||||
return false;
|
||||
|
||||
if (!ascii_isalpha(id[0]))
|
||||
return false;
|
||||
|
||||
for (size_t i = 1; i < len; i++)
|
||||
if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
|
||||
return false;
|
||||
return true;
|
||||
return in_charset(id + 1, ALPHANUMERICAL "/\\_.");
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ static inline int safe_atou(const char *s, unsigned *ret_u) {
|
||||
int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret);
|
||||
|
||||
int safe_atoi(const char *s, int *ret_i);
|
||||
int safe_atolli(const char *s, long long int *ret_i);
|
||||
int safe_atolli(const char *s, long long *ret_i);
|
||||
|
||||
int safe_atou8_full(const char *s, unsigned base, uint8_t *ret);
|
||||
|
||||
@@ -87,8 +87,8 @@ static inline int safe_atou64(const char *s, uint64_t *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);
|
||||
assert_cc(sizeof(int64_t) == sizeof(long long));
|
||||
return safe_atolli(s, (long long*) ret_i);
|
||||
}
|
||||
|
||||
static inline int safe_atoux64(const char *s, uint64_t *ret) {
|
||||
@@ -101,8 +101,8 @@ static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *r
|
||||
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
|
||||
return safe_atou_full(s, base, (unsigned*) ret_u);
|
||||
}
|
||||
static inline int safe_atoli(const char *s, long int *ret_u) {
|
||||
assert_cc(sizeof(long int) == sizeof(int));
|
||||
static inline int safe_atoli(const char *s, long *ret_u) {
|
||||
assert_cc(sizeof(long) == sizeof(int));
|
||||
return safe_atoi(s, (int*) ret_u);
|
||||
}
|
||||
#else
|
||||
@@ -110,9 +110,9 @@ static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *r
|
||||
assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
|
||||
return safe_atollu_full(s, base, (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);
|
||||
static inline int safe_atoli(const char *s, long *ret_u) {
|
||||
assert_cc(sizeof(long) == sizeof(long long));
|
||||
return safe_atolli(s, (long long*) ret_u);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -613,37 +613,9 @@ char* path_extend_internal(char **x, ...) {
|
||||
return nx;
|
||||
}
|
||||
|
||||
static int check_x_access(const char *path, int *ret_fd) {
|
||||
int open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
/* We need to use O_PATH because there may be executables for which we have only exec
|
||||
* permissions, but not read (usually suid executables). */
|
||||
fd = open(path, O_PATH|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
r = fd_verify_regular(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = access_fd(fd, X_OK);
|
||||
if (r == -ENOSYS) {
|
||||
/* /proc is not mounted. Fallback to access(). */
|
||||
if (access(path, X_OK) < 0)
|
||||
return -errno;
|
||||
} else if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_fd)
|
||||
*ret_fd = TAKE_FD(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
_cleanup_free_ char *path_name = NULL;
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
@@ -654,23 +626,40 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
|
||||
* needed to avoid unforeseen regression or other complicated changes. */
|
||||
if (root) {
|
||||
/* prefix root to name in case full paths are not specified */
|
||||
r = chase(name, root, CHASE_PREFIX_ROOT, &path_name, /* ret_fd= */ NULL);
|
||||
r = chase(name, root, CHASE_PREFIX_ROOT, &resolved, &fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
name = path_name;
|
||||
name = resolved;
|
||||
} else {
|
||||
/* We need to use O_PATH because there may be executables for which we have only exec permissions,
|
||||
* but not read (usually suid executables). */
|
||||
fd = open(name, O_PATH|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = check_x_access(name, ret_fd ? &fd : NULL);
|
||||
r = fd_verify_regular(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_filename) {
|
||||
r = path_make_absolute_cwd(name, ret_filename);
|
||||
r = access_fd(fd, X_OK);
|
||||
if (r == -ENOSYS)
|
||||
/* /proc/ is not mounted. Fall back to access(). */
|
||||
r = RET_NERRNO(access(name, X_OK));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path_simplify(*ret_filename);
|
||||
if (ret_path) {
|
||||
if (resolved)
|
||||
*ret_path = TAKE_PTR(resolved);
|
||||
else {
|
||||
r = path_make_absolute_cwd(name, ret_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path_simplify(*ret_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_fd)
|
||||
@@ -692,7 +681,7 @@ int find_executable_full(
|
||||
assert(name);
|
||||
|
||||
if (is_path(name))
|
||||
return find_executable_impl(name, root, ret_filename, ret_fd);
|
||||
return open_and_check_executable(name, root, ret_filename, ret_fd);
|
||||
|
||||
if (exec_search_path) {
|
||||
STRV_FOREACH(element, exec_search_path) {
|
||||
@@ -707,7 +696,7 @@ int find_executable_full(
|
||||
if (!full_path)
|
||||
return -ENOMEM;
|
||||
|
||||
r = find_executable_impl(full_path, root, ret_filename, ret_fd);
|
||||
r = open_and_check_executable(full_path, root, ret_filename, ret_fd);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
if (r != -EACCES)
|
||||
@@ -743,7 +732,7 @@ int find_executable_full(
|
||||
if (!path_extend(&element, name))
|
||||
return -ENOMEM;
|
||||
|
||||
r = find_executable_impl(element, root, ret_filename, ret_fd);
|
||||
r = open_and_check_executable(element, root, ret_filename, ret_fd);
|
||||
if (r >= 0) /* Found it! */
|
||||
return 0;
|
||||
/* PATH entries which we don't have access to are ignored, as per tradition. */
|
||||
|
@@ -115,6 +115,7 @@ 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 open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd);
|
||||
int find_executable_full(
|
||||
const char *name,
|
||||
const char *root,
|
||||
|
@@ -1,15 +1,18 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_fs.h"
|
||||
#include "missing_magic.h"
|
||||
#include "missing_threads.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidfd-util.h"
|
||||
@@ -86,26 +89,21 @@ static int pidfd_get_info(int fd, struct pidfd_info *info) {
|
||||
|
||||
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *fdinfo = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
|
||||
r = read_full_virtual_file(path, &fdinfo, NULL);
|
||||
r = get_proc_field(path, "Pid", &p);
|
||||
if (r == -ENOENT)
|
||||
return proc_fd_enoent_errno();
|
||||
return -EBADF;
|
||||
if (r == -ENODATA) /* not a pidfd? */
|
||||
return -ENOTTY;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
char *p = find_line_startswith(fdinfo, "Pid:");
|
||||
if (!p)
|
||||
return -ENOTTY; /* not a pidfd? */
|
||||
|
||||
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||
p[strcspn(p, WHITESPACE)] = 0;
|
||||
|
||||
if (streq(p, "0"))
|
||||
return -EREMOTE; /* PID is in foreign PID namespace? */
|
||||
if (streq(p, "-1"))
|
||||
@@ -226,6 +224,7 @@ int pidfd_get_cgroupid(int fd, uint64_t *ret) {
|
||||
}
|
||||
|
||||
int pidfd_get_inode_id(int fd, uint64_t *ret) {
|
||||
static bool file_handle_supported = true;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
@@ -236,6 +235,30 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
|
||||
if (r == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (file_handle_supported) {
|
||||
union {
|
||||
struct file_handle file_handle;
|
||||
uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
|
||||
} fh = {
|
||||
.file_handle.handle_bytes = sizeof(uint64_t),
|
||||
.file_handle.handle_type = FILEID_KERNFS,
|
||||
};
|
||||
int mnt_id;
|
||||
|
||||
r = RET_NERRNO(name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH));
|
||||
if (r >= 0) {
|
||||
if (ret)
|
||||
*ret = *(uint64_t*) fh.file_handle.f_handle;
|
||||
return 0;
|
||||
}
|
||||
assert(r != -EOVERFLOW);
|
||||
if (is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
file_handle_supported = false;
|
||||
}
|
||||
|
||||
#if SIZEOF_INO_T == 8
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
@@ -243,6 +266,16 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
|
||||
if (ret)
|
||||
*ret = (uint64_t) st.st_ino;
|
||||
return 0;
|
||||
|
||||
#elif SIZEOF_INO_T == 4
|
||||
/* On 32-bit systems (where sizeof(ino_t) == 4), the inode id returned by fstat() cannot be used to
|
||||
* reliably identify the process, nor can we communicate the origin of the id with the clients.
|
||||
* Hence let's just refuse to acquire pidfdid through fstat() here. All clients shall also insist on
|
||||
* the 64-bit id from name_to_handle_at(). */
|
||||
return -EOPNOTSUPP;
|
||||
#else
|
||||
# error Unsupported ino_t size
|
||||
#endif
|
||||
}
|
||||
|
||||
int pidfd_get_inode_id_self_cached(uint64_t *ret) {
|
||||
|
@@ -1,10 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
typedef struct PidRef PidRef;
|
||||
|
||||
#include "macro.h"
|
||||
#include "process-util.h"
|
||||
#include "memory-util.h"
|
||||
|
||||
/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes
|
||||
* continuously. This combines a PID, a modern Linux pidfd and the 64bit inode number of the pidfd into one
|
||||
@@ -29,22 +27,22 @@ typedef struct PidRef PidRef;
|
||||
* process. Moreover, most operations will fail with -EREMOTE. Only PidRef structures that are not marked
|
||||
* *unset* can be marked *remote*.
|
||||
*/
|
||||
struct PidRef {
|
||||
pid_t pid; /* > 0 if the PidRef is set, otherwise set to PID_AUTOMATIC if automatic mode is
|
||||
* desired, or 0 otherwise. */
|
||||
typedef struct PidRef {
|
||||
pid_t pid; /* > 0 if the PidRef is set, otherwise set to INT_MIN (PID_AUTOMATIC) if automatic
|
||||
* mode is desired, or 0 otherwise. */
|
||||
int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd. If we
|
||||
* know that the PID is not from the local machine we set this to -EREMOTE, otherwise
|
||||
* we use -EBADF as indicator the fd is invalid. */
|
||||
uint64_t fd_id; /* the inode number of pidfd. only useful in kernel 6.9+ where pidfds live in
|
||||
their own pidfs and each process comes with a unique inode number */
|
||||
};
|
||||
} PidRef;
|
||||
|
||||
#define PIDREF_NULL (PidRef) { .fd = -EBADF }
|
||||
|
||||
/* A special pidref value that we are using when a PID shall be automatically acquired from some surrounding
|
||||
* context, for example connection peer. Much like PIDREF_NULL it will be considered unset by
|
||||
* pidref_is_set().*/
|
||||
#define PIDREF_AUTOMATIC (const PidRef) { .pid = PID_AUTOMATIC, .fd = -EBADF }
|
||||
* pidref_is_set(). */
|
||||
#define PIDREF_AUTOMATIC (const PidRef) { .pid = (pid_t) INT_MIN, .fd = -EBADF }
|
||||
|
||||
/* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to
|
||||
* pidref_set_pid() which does so *with* acquiring one, see below) */
|
||||
@@ -70,7 +68,7 @@ bool pidref_equal(PidRef *a, PidRef *b);
|
||||
int pidref_set_pid(PidRef *pidref, pid_t pid);
|
||||
int pidref_set_pidstr(PidRef *pidref, const char *pid);
|
||||
int pidref_set_pidfd(PidRef *pidref, int fd);
|
||||
int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
|
||||
int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success */
|
||||
int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
|
||||
int pidref_set_parent(PidRef *ret);
|
||||
static inline int pidref_set_self(PidRef *pidref) {
|
||||
@@ -108,5 +106,9 @@ int pidref_verify(const PidRef *pidref);
|
||||
|
||||
#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
|
||||
|
||||
struct siphash;
|
||||
void pidref_hash_func(const PidRef *pidref, struct siphash *state);
|
||||
int pidref_compare_func(const PidRef *a, const PidRef *b);
|
||||
|
||||
extern const struct hash_ops pidref_hash_ops;
|
||||
extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */
|
||||
|
@@ -46,6 +46,11 @@ Prioq* prioq_free(Prioq *q) {
|
||||
if (!q)
|
||||
return NULL;
|
||||
|
||||
/* Invalidate the index fields of any remaining objects */
|
||||
FOREACH_ARRAY(item, q->items, q->n_items)
|
||||
if (item->idx)
|
||||
*(item->idx) = PRIOQ_IDX_NULL;
|
||||
|
||||
free(q->items);
|
||||
return mfree(q);
|
||||
}
|
||||
@@ -178,6 +183,11 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
|
||||
assert(q);
|
||||
assert(i);
|
||||
|
||||
/* Let's invalidate the index pointer stored in the user's object to indicate the item is now removed
|
||||
* from the priority queue */
|
||||
if (i->idx)
|
||||
*(i->idx) = PRIOQ_IDX_NULL;
|
||||
|
||||
l = q->items + q->n_items - 1;
|
||||
|
||||
if (i == l)
|
||||
@@ -189,6 +199,7 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
|
||||
/* Not last entry, let's replace the last entry with
|
||||
* this one, and reshuffle */
|
||||
|
||||
assert(i >= q->items);
|
||||
k = i - q->items;
|
||||
|
||||
i->data = l->data;
|
||||
@@ -252,6 +263,7 @@ void prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
|
||||
if (!i)
|
||||
return;
|
||||
|
||||
assert(i >= q->items);
|
||||
k = i - q->items;
|
||||
k = shuffle_down(q, k);
|
||||
shuffle_up(q, k);
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <syslog.h>
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
#if HAVE_VALGRIND_VALGRIND_H
|
||||
#include <valgrind/valgrind.h>
|
||||
@@ -36,13 +37,13 @@
|
||||
#include "fs-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_sched.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_threads.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "nulstr-util.h"
|
||||
@@ -53,6 +54,7 @@
|
||||
#include "raw-clone.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
@@ -505,48 +507,10 @@ int get_process_exe(pid_t pid, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
const char *p;
|
||||
int pid_get_uid(pid_t pid, uid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(field);
|
||||
assert(ret);
|
||||
|
||||
if (pid < 0)
|
||||
return -EINVAL;
|
||||
|
||||
p = procfs_file_alloca(pid, "status");
|
||||
r = fopen_unlocked(p, "re", &f);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
char *l;
|
||||
|
||||
r = read_stripped_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
l = startswith(line, field);
|
||||
if (l) {
|
||||
l += strspn(l, WHITESPACE);
|
||||
|
||||
l[strcspn(l, WHITESPACE)] = 0;
|
||||
|
||||
return parse_uid(l, ret);
|
||||
}
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int pid_get_uid(pid_t pid, uid_t *ret) {
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (pid == 0 || pid == getpid_cached()) {
|
||||
@@ -554,7 +518,14 @@ int pid_get_uid(pid_t pid, uid_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return get_process_id(pid, "Uid:", ret);
|
||||
_cleanup_free_ char *v = NULL;
|
||||
r = procfs_file_get_field(pid, "status", "Uid", &v);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return parse_uid(v, ret);
|
||||
}
|
||||
|
||||
int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
||||
@@ -587,14 +558,24 @@ int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
||||
}
|
||||
|
||||
int get_process_gid(pid_t pid, gid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (pid == 0 || pid == getpid_cached()) {
|
||||
*ret = getgid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
||||
return get_process_id(pid, "Gid:", ret);
|
||||
_cleanup_free_ char *v = NULL;
|
||||
r = procfs_file_get_field(pid, "status", "Gid", &v);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return parse_gid(v, ret);
|
||||
}
|
||||
|
||||
int get_process_cwd(pid_t pid, char **ret) {
|
||||
@@ -860,15 +841,12 @@ int pidref_get_start_time(const PidRef *pid, usec_t *ret) {
|
||||
|
||||
int get_process_umask(pid_t pid, mode_t *ret) {
|
||||
_cleanup_free_ char *m = NULL;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
p = procfs_file_alloca(pid, "status");
|
||||
|
||||
r = get_proc_field(p, "Umask", WHITESPACE, &m);
|
||||
r = procfs_file_get_field(pid, "status", "Umask", &m);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
@@ -877,27 +855,8 @@ int get_process_umask(pid_t pid, mode_t *ret) {
|
||||
return parse_mode(m, ret);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
int wait_for_terminate(pid_t pid, siginfo_t *ret) {
|
||||
return pidref_wait_for_terminate(&PIDREF_MAKE_FROM_PID(pid), ret);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -914,24 +873,29 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
|
||||
* 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) {
|
||||
int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFlags flags) {
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
return -ESRCH;
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
if (pidref->pid == 1 || pidref_is_self(pidref))
|
||||
return -ECHILD;
|
||||
|
||||
_cleanup_free_ char *buffer = NULL;
|
||||
siginfo_t status;
|
||||
int r, prio;
|
||||
|
||||
assert(pid > 1);
|
||||
|
||||
if (!name) {
|
||||
r = pid_get_comm(pid, &buffer);
|
||||
r = pidref_get_comm(pidref, &buffer);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
|
||||
log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pidref->pid);
|
||||
else
|
||||
name = buffer;
|
||||
}
|
||||
|
||||
prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
|
||||
int prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
|
||||
|
||||
r = wait_for_terminate(pid, &status);
|
||||
siginfo_t status;
|
||||
r = pidref_wait_for_terminate(pidref, &status);
|
||||
if (r < 0)
|
||||
return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
|
||||
|
||||
@@ -954,6 +918,10 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
|
||||
return pidref_wait_for_terminate_and_check(name, &PIDREF_MAKE_FROM_PID(pid), flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return values:
|
||||
*
|
||||
@@ -1379,8 +1347,8 @@ void valgrind_summary_hack(void) {
|
||||
if (pid < 0)
|
||||
log_struct_errno(
|
||||
LOG_EMERG, errno,
|
||||
"MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
|
||||
LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_VALGRIND_HELPER_FORK_STR),
|
||||
LOG_MESSAGE("Failed to fork off valgrind helper: %m"));
|
||||
else if (pid == 0)
|
||||
exit(EXIT_SUCCESS);
|
||||
else {
|
||||
@@ -1439,7 +1407,7 @@ pid_t getpid_cached(void) {
|
||||
case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
|
||||
pid_t new_pid;
|
||||
|
||||
new_pid = raw_getpid();
|
||||
new_pid = getpid();
|
||||
|
||||
if (!installed) {
|
||||
/* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's
|
||||
@@ -1460,7 +1428,7 @@ pid_t getpid_cached(void) {
|
||||
}
|
||||
|
||||
case CACHED_PID_BUSY: /* Somebody else is currently initializing */
|
||||
return raw_getpid();
|
||||
return getpid();
|
||||
|
||||
default: /* Properly initialized */
|
||||
return current_value;
|
||||
@@ -1525,25 +1493,28 @@ static int fork_flags_to_signal(ForkFlags flags) {
|
||||
SIGKILL;
|
||||
}
|
||||
|
||||
int safe_fork_full(
|
||||
int pidref_safe_fork_full(
|
||||
const char *name,
|
||||
const int stdio_fds[3],
|
||||
int except_fds[],
|
||||
size_t n_except_fds,
|
||||
ForkFlags flags,
|
||||
pid_t *ret_pid) {
|
||||
PidRef *ret_pid) {
|
||||
|
||||
pid_t original_pid, pid;
|
||||
sigset_t saved_ss, ss;
|
||||
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
|
||||
bool block_signals = false, block_all = false, intermediary = false;
|
||||
_cleanup_close_pair_ int pidref_transport_fds[2] = EBADF_PAIR;
|
||||
int prio, r;
|
||||
|
||||
assert(!FLAGS_SET(flags, FORK_WAIT|FORK_FREEZE));
|
||||
assert(!FLAGS_SET(flags, FORK_DETACH) ||
|
||||
(!ret_pid && (flags & (FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) == 0));
|
||||
(flags & (FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) == 0);
|
||||
|
||||
/* 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. */
|
||||
/* A wrapper around fork(), that does a couple of important initializations in addition to mere
|
||||
* forking. If provided, ret_pid is initialized in both the parent and the child process, both times
|
||||
* referencing the child process. Returns == 0 in the child and > 0 in the parent. */
|
||||
|
||||
prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
|
||||
|
||||
@@ -1586,14 +1557,43 @@ int safe_fork_full(
|
||||
if (!r) {
|
||||
/* Not a reaper process, hence do a double fork() so we are reparented to one */
|
||||
|
||||
if (ret_pid && socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pidref_transport_fds) < 0)
|
||||
return log_full_errno(prio, errno, "Failed to allocate pidref socket: %m");
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
|
||||
if (pid > 0) {
|
||||
log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid);
|
||||
|
||||
pidref_transport_fds[1] = safe_close(pidref_transport_fds[1]);
|
||||
|
||||
if (pidref_transport_fds[0] >= 0) {
|
||||
/* Wait for the intermediary child to exit so the caller can be certain the actual child
|
||||
* process has been reparented by the time this function returns. */
|
||||
r = wait_for_terminate_and_check(name, pid, FLAGS_SET(flags, FORK_LOG) ? WAIT_LOG : 0);
|
||||
if (r < 0)
|
||||
return log_full_errno(prio, r, "Failed to wait for intermediary process: %m");
|
||||
if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
|
||||
return -EPROTO;
|
||||
|
||||
int pidfd;
|
||||
ssize_t n = receive_one_fd_iov(
|
||||
pidref_transport_fds[0],
|
||||
&IOVEC_MAKE(&pid, sizeof(pid)),
|
||||
/* iovlen= */ 1,
|
||||
/* flags= */ 0,
|
||||
&pidfd);
|
||||
if (n < 0)
|
||||
return log_full_errno(prio, n, "Failed to receive child pidref: %m");
|
||||
|
||||
*ret_pid = (PidRef) { .pid = pid, .fd = pidfd };
|
||||
}
|
||||
|
||||
return 1; /* return in the parent */
|
||||
}
|
||||
|
||||
pidref_transport_fds[0] = safe_close(pidref_transport_fds[0]);
|
||||
intermediary = true;
|
||||
}
|
||||
}
|
||||
@@ -1611,8 +1611,30 @@ int safe_fork_full(
|
||||
if (pid > 0) {
|
||||
|
||||
/* If we are in the intermediary process, exit now */
|
||||
if (intermediary)
|
||||
if (intermediary) {
|
||||
if (pidref_transport_fds[1] >= 0) {
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
|
||||
r = pidref_set_pid(&pidref, pid);
|
||||
if (r < 0) {
|
||||
log_full_errno(prio, r, "Failed to open reference to PID "PID_FMT": %m", pid);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
r = send_one_fd_iov(
|
||||
pidref_transport_fds[1],
|
||||
pidref.fd,
|
||||
&IOVEC_MAKE(&pidref.pid, sizeof(pidref.pid)),
|
||||
/* iovlen= */ 1,
|
||||
/* flags= */ 0);
|
||||
if (r < 0) {
|
||||
log_full_errno(prio, r, "Failed to send child pidref: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* We are in the parent process */
|
||||
log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
|
||||
@@ -1630,16 +1652,31 @@ int safe_fork_full(
|
||||
return r;
|
||||
if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
|
||||
return -EPROTO;
|
||||
|
||||
/* If we are in the parent and successfully waited, then the process doesn't exist anymore. */
|
||||
if (ret_pid)
|
||||
*ret_pid = PIDREF_NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ret_pid)
|
||||
*ret_pid = pid;
|
||||
if (ret_pid) {
|
||||
if (FLAGS_SET(flags, FORK_PID_ONLY))
|
||||
*ret_pid = PIDREF_MAKE_FROM_PID(pid);
|
||||
else {
|
||||
r = pidref_set_pid(ret_pid, pid);
|
||||
if (r < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
|
||||
*ret_pid = PIDREF_MAKE_FROM_PID(pid);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* We are in the child process */
|
||||
|
||||
pidref_transport_fds[1] = safe_close(pidref_transport_fds[1]);
|
||||
|
||||
/* Restore signal mask manually */
|
||||
saved_ssp = NULL;
|
||||
|
||||
@@ -1801,36 +1838,41 @@ int safe_fork_full(
|
||||
if (FLAGS_SET(flags, FORK_FREEZE))
|
||||
freeze();
|
||||
|
||||
if (ret_pid)
|
||||
*ret_pid = getpid_cached();
|
||||
if (ret_pid) {
|
||||
if (FLAGS_SET(flags, FORK_PID_ONLY))
|
||||
*ret_pid = PIDREF_MAKE_FROM_PID(getpid_cached());
|
||||
else {
|
||||
r = pidref_set_self(ret_pid);
|
||||
if (r < 0) {
|
||||
log_full_errno(prio, r, "Failed to acquire PID reference on ourselves: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidref_safe_fork_full(
|
||||
int safe_fork_full(
|
||||
const char *name,
|
||||
const int stdio_fds[3],
|
||||
int except_fds[],
|
||||
size_t n_except_fds,
|
||||
ForkFlags flags,
|
||||
PidRef *ret_pid) {
|
||||
pid_t *ret_pid) {
|
||||
|
||||
pid_t pid;
|
||||
int r, q;
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
|
||||
/* Getting the detached child process pid without pidfd is racy, so don't allow it if not returning
|
||||
* a pidref to the caller. */
|
||||
assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
|
||||
|
||||
r = pidref_safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags|FORK_PID_ONLY, ret_pid ? &pidref : NULL);
|
||||
if (r < 0 || !ret_pid)
|
||||
return r;
|
||||
|
||||
if (r > 0 && FLAGS_SET(flags, FORK_WAIT)) {
|
||||
/* If we are in the parent and successfully waited, then the process doesn't exist anymore */
|
||||
*ret_pid = PIDREF_NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
q = pidref_set_pid(ret_pid, pid);
|
||||
if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
|
||||
*ret_pid = PIDREF_MAKE_FROM_PID(pid);
|
||||
*ret_pid = pidref.pid;
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -2005,17 +2047,14 @@ _noreturn_ void freeze(void) {
|
||||
|
||||
int get_process_threads(pid_t pid) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const char *p;
|
||||
int n, r;
|
||||
|
||||
if (pid < 0)
|
||||
return -EINVAL;
|
||||
|
||||
p = procfs_file_alloca(pid, "status");
|
||||
|
||||
r = get_proc_field(p, "Threads", WHITESPACE, &t);
|
||||
r = procfs_file_get_field(pid, "status", "Threads", &t);
|
||||
if (r == -ENOENT)
|
||||
return proc_mounted() == 0 ? -ENOSYS : -ESRCH;
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@@ -12,6 +12,8 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "assert-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "macro.h"
|
||||
#include "pidref.h"
|
||||
@@ -22,16 +24,20 @@
|
||||
pid_t _pid_ = (pid); \
|
||||
const char *_field_ = (field); \
|
||||
char *_r_; \
|
||||
if (_pid_ == 0) { \
|
||||
_r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \
|
||||
strcpy(stpcpy(_r_, "/proc/self/"), _field_); \
|
||||
} else { \
|
||||
if (_pid_ == 0) \
|
||||
_r_ = strjoina("/proc/self/", _field_); \
|
||||
else { \
|
||||
assert(_pid_ > 0); \
|
||||
_r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \
|
||||
sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \
|
||||
} \
|
||||
(const char*) _r_; \
|
||||
})
|
||||
|
||||
static inline int procfs_file_get_field(pid_t pid, const char *name, const char *key, char **ret) {
|
||||
return get_proc_field(procfs_file_alloca(pid, name), key, ret);
|
||||
}
|
||||
|
||||
typedef enum ProcessCmdlineFlags {
|
||||
PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
|
||||
PROCESS_CMDLINE_USE_LOCALE = 1 << 1,
|
||||
@@ -61,7 +67,11 @@ int get_process_umask(pid_t pid, mode_t *ret);
|
||||
|
||||
int container_get_leader(const char *machine, pid_t *pid);
|
||||
|
||||
int wait_for_terminate(pid_t pid, siginfo_t *status);
|
||||
static inline bool SIGINFO_CODE_IS_DEAD(int code) {
|
||||
return IN_SET(code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
|
||||
}
|
||||
|
||||
int wait_for_terminate(pid_t pid, siginfo_t *ret);
|
||||
|
||||
typedef enum WaitFlags {
|
||||
WAIT_LOG_ABNORMAL = 1 << 0,
|
||||
@@ -71,7 +81,9 @@ typedef enum WaitFlags {
|
||||
WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
|
||||
} WaitFlags;
|
||||
|
||||
int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFlags flags);
|
||||
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);
|
||||
@@ -193,20 +205,9 @@ typedef enum ForkFlags {
|
||||
FORK_NEW_NETNS = 1 << 20, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
|
||||
FORK_NEW_PIDNS = 1 << 21, /* Run child in its own PID namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
|
||||
FORK_FREEZE = 1 << 22, /* Don't return in child, just call freeze() instead */
|
||||
FORK_PID_ONLY = 1 << 23, /* Don't open a pidfd referencing the child process */
|
||||
} ForkFlags;
|
||||
|
||||
int safe_fork_full(
|
||||
const char *name,
|
||||
const int stdio_fds[3],
|
||||
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, NULL, 0, flags, ret_pid);
|
||||
}
|
||||
|
||||
int pidref_safe_fork_full(
|
||||
const char *name,
|
||||
const int stdio_fds[3],
|
||||
@@ -219,6 +220,18 @@ static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *re
|
||||
return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
|
||||
}
|
||||
|
||||
int safe_fork_full(
|
||||
const char *name,
|
||||
const int stdio_fds[3],
|
||||
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, NULL, 0, flags, ret_pid);
|
||||
}
|
||||
|
||||
int namespace_fork(
|
||||
const char *outer_name,
|
||||
const char *inner_name,
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "env-util.h"
|
||||
@@ -19,9 +20,9 @@
|
||||
#include "fileio.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "log.h"
|
||||
#include "missing_random.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_threads.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
|
@@ -6,8 +6,10 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
void random_bytes(void *p, size_t n); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */
|
||||
int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */
|
||||
#include "macro.h"
|
||||
|
||||
void random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */
|
||||
int crypto_random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns secure random bytes after waiting for the RNG to initialize. */
|
||||
int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret);
|
||||
|
||||
static inline uint64_t random_u64(void) {
|
||||
@@ -29,6 +31,6 @@ static inline uint32_t random_u32(void) {
|
||||
|
||||
size_t random_pool_size(void);
|
||||
|
||||
int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
|
||||
int random_write_entropy(int fd, const void *seed, size_t size, bool credit) _nonnull_if_nonzero_(2, 3);
|
||||
|
||||
uint64_t random_u64_range(uint64_t max);
|
||||
|
@@ -28,3 +28,55 @@ unsigned ratelimit_num_dropped(const RateLimit *rl);
|
||||
|
||||
usec_t ratelimit_end(const RateLimit *rl);
|
||||
usec_t ratelimit_left(const RateLimit *rl);
|
||||
|
||||
typedef struct LogRateLimit {
|
||||
int error;
|
||||
int level;
|
||||
RateLimit ratelimit;
|
||||
} LogRateLimit;
|
||||
|
||||
#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...) \
|
||||
({ \
|
||||
int _log_ratelimit_error = (_error); \
|
||||
int _log_ratelimit_level = (_level); \
|
||||
static LogRateLimit _log_ratelimit = { \
|
||||
.ratelimit = (_ratelimit), \
|
||||
}; \
|
||||
unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
|
||||
if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
|
||||
ratelimit_reset(&_log_ratelimit.ratelimit); \
|
||||
_log_ratelimit.error = _log_ratelimit_error; \
|
||||
_log_ratelimit.level = _log_ratelimit_level; \
|
||||
} \
|
||||
if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \
|
||||
_log_ratelimit_error = _num_dropped_errors > 0 \
|
||||
? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \
|
||||
: log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \
|
||||
_log_ratelimit_error; \
|
||||
})
|
||||
|
||||
#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...) \
|
||||
({ \
|
||||
int _level = (level), _e = (error); \
|
||||
_e = (log_get_max_level() >= LOG_PRI(_level)) \
|
||||
? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
|
||||
: -ERRNO_VALUE(_e); \
|
||||
_e < 0 ? _e : -ESTRPIPE; \
|
||||
})
|
||||
|
||||
#define log_ratelimit_full(level, _ratelimit, format, ...) \
|
||||
log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__)
|
||||
|
||||
/* Normal logging */
|
||||
#define log_ratelimit_info(...) log_ratelimit_full(LOG_INFO, __VA_ARGS__)
|
||||
#define log_ratelimit_notice(...) log_ratelimit_full(LOG_NOTICE, __VA_ARGS__)
|
||||
#define log_ratelimit_warning(...) log_ratelimit_full(LOG_WARNING, __VA_ARGS__)
|
||||
#define log_ratelimit_error(...) log_ratelimit_full(LOG_ERR, __VA_ARGS__)
|
||||
#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__)
|
||||
|
||||
/* Logging triggered by an errno-like error */
|
||||
#define log_ratelimit_info_errno(error, ...) log_ratelimit_full_errno(LOG_INFO, error, __VA_ARGS__)
|
||||
#define log_ratelimit_notice_errno(error, ...) log_ratelimit_full_errno(LOG_NOTICE, error, __VA_ARGS__)
|
||||
#define log_ratelimit_warning_errno(error, ...) log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
|
||||
#define log_ratelimit_error_errno(error, ...) log_ratelimit_full_errno(LOG_ERR, error, __VA_ARGS__)
|
||||
#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
|
||||
|
@@ -12,15 +12,9 @@ Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
|
||||
#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
||||
static inline Set* set_free(Set *s) {
|
||||
return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL);
|
||||
return (Set*) _hashmap_free(HASHMAP_BASE(s));
|
||||
}
|
||||
|
||||
static inline Set* set_free_free(Set *s) {
|
||||
return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL);
|
||||
}
|
||||
|
||||
/* no set_free_free_free */
|
||||
|
||||
#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s) HASHMAP_DEBUG_SRC_ARGS))
|
||||
|
||||
int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
|
||||
@@ -77,30 +71,13 @@ static inline bool set_iterate(const Set *s, Iterator *i, void **value) {
|
||||
}
|
||||
|
||||
static inline void set_clear(Set *s) {
|
||||
_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
|
||||
_hashmap_clear(HASHMAP_BASE(s));
|
||||
}
|
||||
|
||||
static inline void set_clear_free(Set *s) {
|
||||
_hashmap_clear(HASHMAP_BASE(s), free, NULL);
|
||||
}
|
||||
|
||||
/* no set_clear_free_free */
|
||||
|
||||
static inline void *set_steal_first(Set *s) {
|
||||
return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
|
||||
}
|
||||
|
||||
#define set_clear_with_destructor(s, f) \
|
||||
({ \
|
||||
Set *_s = (s); \
|
||||
void *_item; \
|
||||
while ((_item = set_steal_first(_s))) \
|
||||
f(_item); \
|
||||
_s; \
|
||||
})
|
||||
#define set_free_with_destructor(s, f) \
|
||||
set_free(set_clear_with_destructor(s, f))
|
||||
|
||||
/* no set_steal_first_key */
|
||||
/* no set_first_key */
|
||||
|
||||
@@ -114,6 +91,8 @@ static inline char **set_get_strv(Set *s) {
|
||||
return _hashmap_get_strv(HASHMAP_BASE(s));
|
||||
}
|
||||
|
||||
char** set_to_strv(Set **s);
|
||||
|
||||
int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key HASHMAP_DEBUG_PARAMS);
|
||||
#define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
||||
@@ -143,10 +122,8 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags
|
||||
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)
|
||||
|
||||
int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret);
|
||||
|
||||
|
@@ -2,11 +2,11 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_threads.h"
|
||||
#include "parse-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "stdio-util.h"
|
||||
@@ -280,13 +280,13 @@ void propagate_signal(int sig, siginfo_t *siginfo) {
|
||||
|
||||
/* To be called from a signal handler. Will raise the same signal again, in our process + in our threads.
|
||||
*
|
||||
* Note that we use raw_getpid() instead of getpid_cached(). We might have forked with raw_clone()
|
||||
* Note that we use getpid() instead of getpid_cached(). We might have forked with raw_clone()
|
||||
* earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not
|
||||
* performance sensitive code.
|
||||
*
|
||||
* Note that we use kill() rather than raise() as fallback, for similar reasons. */
|
||||
|
||||
p = raw_getpid();
|
||||
p = getpid();
|
||||
|
||||
if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
|
||||
assert_se(kill(p, sig) >= 0);
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "assert-util.h"
|
||||
#include "macro.h"
|
||||
|
||||
int reset_all_signal_handlers(void);
|
||||
|
@@ -1,10 +1,11 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
/* Make sure the net/if.h header is included before any linux/ one */
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <poll.h>
|
||||
@@ -14,7 +15,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "random-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
@@ -1350,6 +1351,53 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
|
||||
return memcpy_safe(buf, CMSG_DATA(cmsg), buf_len);
|
||||
}
|
||||
|
||||
size_t sockaddr_ll_len(const struct sockaddr_ll *sa) {
|
||||
/* Certain hardware address types (e.g Infiniband) do not fit into sll_addr
|
||||
* (8 bytes) and run over the structure. This function returns the correct size that
|
||||
* must be passed to kernel. */
|
||||
|
||||
assert(sa->sll_family == AF_PACKET);
|
||||
|
||||
size_t mac_len = sizeof(sa->sll_addr);
|
||||
|
||||
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);
|
||||
|
||||
return offsetof(struct sockaddr_ll, sll_addr) + mac_len;
|
||||
}
|
||||
|
||||
size_t sockaddr_un_len(const struct sockaddr_un *sa) {
|
||||
/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
|
||||
|
||||
assert(sa->sun_family == AF_UNIX);
|
||||
|
||||
return 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))+1);
|
||||
}
|
||||
|
||||
size_t sockaddr_len(const union sockaddr_union *sa) {
|
||||
switch (sa->sa.sa_family) {
|
||||
case AF_INET:
|
||||
return sizeof(struct sockaddr_in);
|
||||
case AF_INET6:
|
||||
return sizeof(struct sockaddr_in6);
|
||||
case AF_UNIX:
|
||||
return sockaddr_un_len(&sa->un);
|
||||
case AF_PACKET:
|
||||
return sockaddr_ll_len(&sa->ll);
|
||||
case AF_NETLINK:
|
||||
return sizeof(struct sockaddr_nl);
|
||||
case AF_VSOCK:
|
||||
return sizeof(struct sockaddr_vm);
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
int socket_ioctl_fd(void) {
|
||||
int fd;
|
||||
|
||||
@@ -1445,25 +1493,43 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
|
||||
}
|
||||
|
||||
int socket_bind_to_ifindex(int fd, int ifindex) {
|
||||
char ifname[IF_NAMESIZE];
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (ifindex <= 0)
|
||||
/* Drop binding */
|
||||
return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
|
||||
|
||||
r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
|
||||
if (r != -ENOPROTOOPT)
|
||||
return r;
|
||||
return setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
|
||||
}
|
||||
|
||||
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
|
||||
r = format_ifname(ifindex, ifname);
|
||||
int socket_autobind(int fd, char **ret_name) {
|
||||
_cleanup_free_ char *name = NULL;
|
||||
uint64_t random;
|
||||
int r;
|
||||
|
||||
/* Generate a random abstract socket name and bind fd to it. This is modeled after the kernel
|
||||
* "autobind" feature, but uses 64-bit random number internally. */
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret_name);
|
||||
|
||||
random = random_u64();
|
||||
|
||||
if (asprintf(&name, "@%" PRIu64, random) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
union sockaddr_union sa;
|
||||
assert_cc(DECIMAL_STR_MAX(uint64_t) < sizeof(sa.un.sun_path));
|
||||
|
||||
r = sockaddr_un_set_path(&sa.un, name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return socket_bind_to_ifname(fd, ifname);
|
||||
if (bind(fd, &sa.sa, r) < 0)
|
||||
return -errno;
|
||||
|
||||
*ret_name = TAKE_PTR(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
|
||||
@@ -1800,8 +1866,6 @@ int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* This returns ENOPROTOOPT if the kernel is older than 4.2. */
|
||||
|
||||
if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0)
|
||||
return -errno;
|
||||
|
||||
|
@@ -6,12 +6,12 @@
|
||||
#include <linux/if_infiniband.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <sys/socket.h> /* linux/vms_sockets.h requires 'struct sockaddr' */
|
||||
#include <linux/vm_sockets.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
@@ -238,62 +238,11 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
|
||||
(size) == CMSG_ALIGN(size) ? 1 : -1]; \
|
||||
}
|
||||
|
||||
/*
|
||||
* 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; \
|
||||
})
|
||||
size_t sockaddr_ll_len(const struct sockaddr_ll *sa);
|
||||
|
||||
/* 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))+1); \
|
||||
})
|
||||
size_t sockaddr_un_len(const struct sockaddr_un *sa);
|
||||
|
||||
#define SOCKADDR_LEN(saddr) \
|
||||
({ \
|
||||
const union sockaddr_union *__sa = &(saddr); \
|
||||
size_t _len; \
|
||||
switch (__sa->sa.sa_family) { \
|
||||
case AF_INET: \
|
||||
_len = sizeof(struct sockaddr_in); \
|
||||
break; \
|
||||
case AF_INET6: \
|
||||
_len = sizeof(struct sockaddr_in6); \
|
||||
break; \
|
||||
case AF_UNIX: \
|
||||
_len = SOCKADDR_UN_LEN(__sa->un); \
|
||||
break; \
|
||||
case AF_PACKET: \
|
||||
_len = SOCKADDR_LL_LEN(__sa->ll); \
|
||||
break; \
|
||||
case AF_NETLINK: \
|
||||
_len = sizeof(struct sockaddr_nl); \
|
||||
break; \
|
||||
case AF_VSOCK: \
|
||||
_len = sizeof(struct sockaddr_vm); \
|
||||
break; \
|
||||
default: \
|
||||
assert_not_reached(); \
|
||||
} \
|
||||
_len; \
|
||||
})
|
||||
size_t sockaddr_len(const union sockaddr_union *sa);
|
||||
|
||||
int socket_ioctl_fd(void);
|
||||
|
||||
@@ -322,6 +271,8 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
|
||||
int socket_bind_to_ifname(int fd, const char *ifname);
|
||||
int socket_bind_to_ifindex(int fd, int ifindex);
|
||||
|
||||
int socket_autobind(int fd, char **ret_name);
|
||||
|
||||
/* Define a 64-bit version of timeval/timespec in any case, even on 32-bit userspace. */
|
||||
struct timeval_large {
|
||||
uint64_t tvl_sec, tvl_usec;
|
||||
|
@@ -16,10 +16,10 @@
|
||||
#include "filesystems.h"
|
||||
#include "fs-util.h"
|
||||
#include "hash-funcs.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_fs.h"
|
||||
#include "missing_magic.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
@@ -172,7 +172,7 @@ int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup)
|
||||
struct dirent *de;
|
||||
ssize_t n;
|
||||
|
||||
n = getdents64(fd, buf, m);
|
||||
n = posix_getdents(fd, buf, m, /* flags = */ 0);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n == 0)
|
||||
@@ -298,7 +298,7 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
|
||||
flags |= AT_EMPTY_PATH;
|
||||
}
|
||||
|
||||
int ntha_flags = (flags & AT_EMPTY_PATH) | (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? 0 : AT_SYMLINK_FOLLOW);
|
||||
int ntha_flags = at_flags_normalize_follow(flags) & (AT_EMPTY_PATH|AT_SYMLINK_FOLLOW);
|
||||
_cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
|
||||
int mntida = -1, mntidb = -1;
|
||||
|
||||
@@ -470,8 +470,8 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) {
|
||||
a->stx_ino == b->stx_ino;
|
||||
}
|
||||
|
||||
bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
|
||||
if (!new_statx_is_set(a) || !new_statx_is_set(b))
|
||||
bool statx_mount_same(const struct statx *a, const struct statx *b) {
|
||||
if (!statx_is_set(a) || !statx_is_set(b))
|
||||
return false;
|
||||
|
||||
/* if we have the mount ID, that's all we need */
|
||||
@@ -483,87 +483,20 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
|
||||
a->stx_dev_minor == b->stx_dev_minor;
|
||||
}
|
||||
|
||||
static bool is_statx_fatal_error(int err, int flags) {
|
||||
assert(err < 0);
|
||||
|
||||
/* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
|
||||
* let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
|
||||
* fs access issues, which we should propagate. */
|
||||
if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
|
||||
return false;
|
||||
|
||||
/* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
|
||||
* See statx_generic() in glibc. */
|
||||
if (err != -EINVAL)
|
||||
return true;
|
||||
|
||||
if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
|
||||
return false; /* Unsupported flags are specified. Let's try to use our implementation. */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
|
||||
static bool avoid_statx = false;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
if (!avoid_statx) {
|
||||
r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
|
||||
if (r >= 0 || is_statx_fatal_error(r, flags))
|
||||
return r;
|
||||
|
||||
avoid_statx = true;
|
||||
}
|
||||
|
||||
/* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
|
||||
* OK to ignore */
|
||||
if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
|
||||
AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
|
||||
return -errno;
|
||||
|
||||
*sx = (struct statx) {
|
||||
.stx_mask = STATX_TYPE|STATX_MODE|
|
||||
STATX_NLINK|STATX_UID|STATX_GID|
|
||||
STATX_ATIME|STATX_MTIME|STATX_CTIME|
|
||||
STATX_INO|STATX_SIZE|STATX_BLOCKS,
|
||||
.stx_blksize = st.st_blksize,
|
||||
.stx_nlink = st.st_nlink,
|
||||
.stx_uid = st.st_uid,
|
||||
.stx_gid = st.st_gid,
|
||||
.stx_mode = st.st_mode,
|
||||
.stx_ino = st.st_ino,
|
||||
.stx_size = st.st_size,
|
||||
.stx_blocks = st.st_blocks,
|
||||
.stx_rdev_major = major(st.st_rdev),
|
||||
.stx_rdev_minor = minor(st.st_rdev),
|
||||
.stx_dev_major = major(st.st_dev),
|
||||
.stx_dev_minor = minor(st.st_dev),
|
||||
.stx_atime.tv_sec = st.st_atim.tv_sec,
|
||||
.stx_atime.tv_nsec = st.st_atim.tv_nsec,
|
||||
.stx_mtime.tv_sec = st.st_mtim.tv_sec,
|
||||
.stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
|
||||
.stx_ctime.tv_sec = st.st_ctim.tv_sec,
|
||||
.stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
assert(ret);
|
||||
|
||||
if (!isempty(path)) {
|
||||
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
dir_fd = fd;
|
||||
}
|
||||
|
||||
return RET_NERRNO(fstatfs(fd, ret));
|
||||
return RET_NERRNO(fstatfs(dir_fd, ret));
|
||||
}
|
||||
|
||||
void inode_hash_func(const struct stat *q, struct siphash *state) {
|
||||
|
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing_stat.h"
|
||||
#include "siphash24.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@@ -90,31 +89,10 @@ bool stat_inode_same(const struct stat *a, const struct stat *b);
|
||||
bool stat_inode_unmodified(const struct stat *a, const struct stat *b);
|
||||
|
||||
bool statx_inode_same(const struct statx *a, const struct statx *b);
|
||||
bool statx_mount_same(const struct new_statx *a, const struct new_statx *b);
|
||||
|
||||
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx);
|
||||
bool statx_mount_same(const struct statx *a, const struct statx *b);
|
||||
|
||||
int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
|
||||
|
||||
#if HAS_FEATURE_MEMORY_SANITIZER
|
||||
# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
|
||||
# define STRUCT_STATX_DEFINE(var) \
|
||||
struct statx var = {}
|
||||
# define STRUCT_NEW_STATX_DEFINE(var) \
|
||||
union { \
|
||||
struct statx sx; \
|
||||
struct new_statx nsx; \
|
||||
} var = {}
|
||||
#else
|
||||
# define STRUCT_STATX_DEFINE(var) \
|
||||
struct statx var
|
||||
# define STRUCT_NEW_STATX_DEFINE(var) \
|
||||
union { \
|
||||
struct statx sx; \
|
||||
struct new_statx nsx; \
|
||||
} var
|
||||
#endif
|
||||
|
||||
static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
|
||||
return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
|
||||
}
|
||||
@@ -140,6 +118,3 @@ static inline bool stat_is_set(const struct stat *st) {
|
||||
static inline bool statx_is_set(const struct statx *sx) {
|
||||
return sx && sx->stx_mask != 0;
|
||||
}
|
||||
static inline bool new_statx_is_set(const struct new_statx *sx) {
|
||||
return sx && sx->stx_mask != 0;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user