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:
Jan Vaclav
2025-01-22 18:45:03 +01:00
parent 9282f93f70
commit 3ae6505d7d
119 changed files with 4603 additions and 2885 deletions

View File

@@ -8,8 +8,8 @@
#include <net/ethernet.h> #include <net/ethernet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include "sd-event.h"
#include "sd-dhcp6-client.h" #include "sd-dhcp6-client.h"
#include "sd-event.h"
#include "dhcp-duid-internal.h" #include "dhcp-duid-internal.h"
#include "dhcp6-client-internal.h" #include "dhcp6-client-internal.h"

View File

@@ -8,10 +8,10 @@
#include <inttypes.h> #include <inttypes.h>
#include "sd-dhcp6-lease.h" #include "sd-dhcp6-lease.h"
#include "dns-resolver-internal.h"
#include "dhcp6-option.h" #include "dhcp6-option.h"
#include "dhcp6-protocol.h" #include "dhcp6-protocol.h"
#include "dns-resolver-internal.h"
#include "macro.h" #include "macro.h"
#include "set.h" #include "set.h"
#include "time-util.h" #include "time-util.h"

View File

@@ -4,13 +4,13 @@
***/ ***/
#include <errno.h> #include <errno.h>
#include <linux/if_packet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <linux/if_packet.h>
#include "dhcp6-internal.h" #include "dhcp6-internal.h"
#include "dhcp6-protocol.h" #include "dhcp6-protocol.h"

View File

@@ -4,9 +4,9 @@
***/ ***/
#include <errno.h> #include <errno.h>
#include <sys/ioctl.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/if_infiniband.h> #include <linux/if_infiniband.h>
#include <sys/ioctl.h>
#include "sd-dhcp6-client.h" #include "sd-dhcp6-client.h"
@@ -1289,7 +1289,8 @@ static int client_receive_message(
sd_dhcp6_client *client = ASSERT_PTR(userdata); sd_dhcp6_client *client = ASSERT_PTR(userdata);
DHCP6_CLIENT_DONT_DESTROY(client); 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 = {}; CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
struct iovec iov; struct iovec iov;
union sockaddr_union sa = {}; union sockaddr_union sa = {};

View File

@@ -13,6 +13,43 @@
struct sd_device { struct sd_device {
unsigned n_ref; 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. /* The database version indicates the supported features by the udev database.
* This is saved and parsed in V field. * This is saved and parsed in V field.
* *
@@ -21,68 +58,38 @@ struct sd_device {
*/ */
unsigned database_version; 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; Iterator properties_iterator;
uint64_t properties_generation; /* changes whenever the properties are changed */ uint64_t properties_generation; /* changes whenever the properties are changed */
uint64_t properties_iterator_generation; /* generation when iteration was started */ 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 */ /* TAG keyword */
OrderedHashmap *properties_db;
Hashmap *sysattr_values; /* cached sysattr values */
Set *sysattrs; /* names of sysattrs */
Iterator sysattrs_iterator;
Set *all_tags, *current_tags; Set *all_tags, *current_tags;
Iterator all_tags_iterator, current_tags_iterator; Iterator all_tags_iterator, current_tags_iterator;
uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */ 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 */ uint64_t tags_generation; /* changes whenever the tags are changed */
/* SYMLINK keyword */
Set *devlinks; Set *devlinks;
Iterator devlinks_iterator; Iterator devlinks_iterator;
uint64_t devlinks_generation; /* changes whenever the devlinks are changed */ uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
uint64_t devlinks_iterator_generation; /* generation when iteration was started */ uint64_t devlinks_iterator_generation; /* generation when iteration was started */
int devlink_priority; int devlink_priority;
/* parent and child devices */
sd_device *parent;
Hashmap *children; Hashmap *children;
Iterator children_iterator; Iterator children_iterator;
bool children_enumerated; 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 parent_set:1; /* no need to try to reload parent */
bool sysattrs_read:1; /* don't try to re-read sysattrs once read */ 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 */ 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 driver_set:1; /* don't reread driver */
bool uevent_loaded:1; /* don't reread uevent */ bool uevent_loaded:1; /* don't reread uevent */
bool db_loaded; /* don't reread db */ bool db_loaded; /* don't reread db */
bool is_initialized:1; bool is_initialized:1;
bool sealed:1; /* don't read more information from uevent/db */ 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 */ 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_syspath(sd_device *device, const char *_syspath, bool verify);
int device_set_ifindex(sd_device *device, const char *ifindex); 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_devmode(sd_device *device, const char *devmode);
int device_set_devname(sd_device *device, const char *devname); int device_set_devname(sd_device *device, const char *devname);
int device_set_devtype(sd_device *device, const char *devtype); int device_set_devtype(sd_device *device, const char *devtype);

View File

@@ -116,6 +116,10 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret) {
assert(device); assert(device);
r = device_read_uevent_file(device);
if (r < 0)
return r;
r = device_read_db(device); r = device_read_db(device);
if (r < 0) if (r < 0)
return r; return r;
@@ -134,6 +138,10 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
assert(device); assert(device);
r = device_read_uevent_file(device);
if (r < 0)
return r;
r = device_read_db(device); r = device_read_db(device);
if (r < 0) if (r < 0)
return r; return r;
@@ -147,7 +155,7 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
return 0; 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; uid_t u;
int r; int r;
@@ -172,6 +180,10 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
assert(device); assert(device);
r = device_read_uevent_file(device);
if (r < 0)
return r;
r = device_read_db(device); r = device_read_db(device);
if (r < 0) if (r < 0)
return r; return r;
@@ -185,7 +197,7 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
return 0; 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; gid_t g;
int r; int r;
@@ -429,10 +441,11 @@ static int device_verify(sd_device *device) {
return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
"sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum."); "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); r = device_set_drivers_subsystem(device);
if (r < 0) if (r < 0)
return r; return log_device_debug_errno(device, r,
"sd-device: Failed to set driver subsystem: %m");
} }
device->sealed = true; 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) { void device_cleanup_tags(sd_device *device) {
assert(device); assert(device);
device->all_tags = set_free_free(device->all_tags); device->all_tags = set_free(device->all_tags);
device->current_tags = set_free_free(device->current_tags); device->current_tags = set_free(device->current_tags);
device->property_tags_outdated = true; device->property_tags_outdated = true;
device->tags_generation++; device->tags_generation++;
} }
@@ -688,7 +701,7 @@ void device_cleanup_tags(sd_device *device) {
void device_cleanup_devlinks(sd_device *device) { void device_cleanup_devlinks(sd_device *device) {
assert(device); assert(device);
set_free_free(device->devlinks); set_free(device->devlinks);
device->devlinks = NULL; device->devlinks = NULL;
device->property_devlinks_outdated = true; device->property_devlinks_outdated = true;
device->devlinks_generation++; 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); 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);
}

View File

@@ -9,6 +9,7 @@
#include "sd-device.h" #include "sd-device.h"
#include "chase.h"
#include "macro.h" #include "macro.h"
int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum); 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_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_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret); 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_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); 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) { 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_uid(sd_device *device, uid_t *ret);
int device_get_devnode_gid(sd_device *device, gid_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); void device_clear_sysattr_cache(sd_device *device);
int device_cache_sysattr_value(sd_device *device, const char *key, char *value); int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error);
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
void device_seal(sd_device *device); void device_seal(sd_device *device);
void device_set_is_initialized(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); int device_set_action(sd_device *device, sd_device_action_t a);
sd_device_action_t device_action_from_string(const char *s) _pure_; sd_device_action_t device_action_from_string(const char *s) _pure_;
const char* device_action_to_string(sd_device_action_t a) _const_; const char* device_action_to_string(sd_device_action_t a) _const_;
void dump_device_action_table(void);

View 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);
}

View File

@@ -110,6 +110,19 @@ bool device_is_devtype(sd_device *device, const char *devtype);
static inline bool device_property_can_set(const char *property) { static inline bool device_property_can_set(const char *property) {
return property && return property &&
!STR_IN_SET(property, !STR_IN_SET(property,
"ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER", /* basic properties set by kernel, only in netlink event */
"IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS"); "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_");
} }

View File

@@ -366,7 +366,7 @@ _public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return(ifindex > 0, -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) if (r < 0)
return r; 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. */ 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); r = device_new_from_path_join(&device, subsystem, subsys, "drivers", "/sys/bus/", subsys, "/drivers", NULL);
else 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) if (r < 0)
return r; return r;
} }
@@ -766,16 +766,24 @@ static int handle_uevent_line(
assert(major); assert(major);
assert(minor); assert(minor);
if (streq(key, "SUBSYSTEM"))
return device_set_subsystem(device, value);
if (streq(key, "DEVTYPE")) if (streq(key, "DEVTYPE"))
return device_set_devtype(device, value); return device_set_devtype(device, value);
if (streq(key, "IFINDEX")) if (streq(key, "IFINDEX"))
return device_set_ifindex(device, value); return device_set_ifindex(device, value);
if (streq(key, "DEVNAME")) if (streq(key, "DEVNAME"))
return device_set_devname(device, value); 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")) if (streq(key, "DEVMODE"))
return device_set_devmode(device, value); return device_set_devmode(device, value);
if (streq(key, "DISKSEQ")) if (streq(key, "DISKSEQ"))
return device_set_diskseq(device, value); return device_set_diskseq(device, value);
if (streq(key, "DRIVER"))
return device_set_driver(device, value);
if (streq(key, "MAJOR")) if (streq(key, "MAJOR"))
*major = value; *major = value;
else if (streq(key, "MINOR")) else if (streq(key, "MINOR"))
@@ -787,89 +795,59 @@ static int handle_uevent_line(
} }
int device_read_uevent_file(sd_device *device) { 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; int r;
enum {
PRE_KEY,
KEY,
PRE_VALUE,
VALUE,
INVALID_LINE,
} state = PRE_KEY;
assert(device); assert(device);
if (device->uevent_loaded || device->sealed) if (device->uevent_loaded || device->sealed)
return 0; return 0;
r = sd_device_get_syspath(device, &syspath);
if (r < 0)
return r;
device->uevent_loaded = true; device->uevent_loaded = true;
path = strjoina(syspath, "/uevent"); const char *uevent;
r = sd_device_get_sysattr_value(device, "uevent", &uevent);
r = read_full_virtual_file(path, &uevent, &uevent_len); if (ERRNO_IS_NEG_PRIVILEGE(r) || ERRNO_IS_NEG_DEVICE_ABSENT(r))
if (r == -EACCES || ERRNO_IS_NEG_DEVICE_ABSENT(r))
/* The uevent files may be write-only, the device may be already removed, or the device /* The uevent files may be write-only, the device may be already removed, or the device
* may not have the uevent file. */ * may not have the uevent file. */
return 0; return 0;
if (r < 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++) _cleanup_strv_free_ char **v = NULL;
switch (state) { r = strv_split_newlines_full(&v, uevent, EXTRACT_RETAIN_ESCAPE);
case PRE_KEY: if (r < 0)
if (!strchr(NEWLINE, uevent[i])) { return log_device_debug_errno(device, r, "sd-device: Failed to parse uevent file: %m");
key = &uevent[i];
state = KEY; const char *major = NULL, *minor = NULL;
} STRV_FOREACH(s, v) {
char *eq = strchr(*s, '=');
break; if (!eq) {
case KEY: log_device_debug(device, "sd-device: Invalid uevent line, ignoring: %s", *s);
if (uevent[i] == '=') { continue;
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);
if (r < 0)
log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value);
state = PRE_KEY;
}
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) { if (major) {
r = device_set_devnum(device, major, minor); r = device_set_devnum(device, major, minor);
if (r < 0) 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; return 0;
@@ -893,6 +871,21 @@ _public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
return 0; 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) { _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
int r; int r;
@@ -1214,36 +1207,29 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
assert_return(device, -EINVAL); assert_return(device, -EINVAL);
r = device_read_uevent_file(device);
if (r < 0)
return r;
if (!device->subsystem_set) { if (!device->subsystem_set) {
_cleanup_free_ char *subsystem = NULL; const char *subsystem;
const char *syspath;
char *path;
r = sd_device_get_syspath(device, &syspath); r = sd_device_get_sysattr_value(device, "subsystem", &subsystem);
if (r < 0)
return r;
/* read 'subsystem' link */
path = strjoina(syspath, "/subsystem");
r = readlink_value(path, &subsystem);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return log_device_debug_errno(device, r, return log_device_debug_errno(device, r,
"sd-device: Failed to read subsystem for %s: %m", "sd-device: Failed to read subsystem for %s: %m",
device->devpath); device->devpath);
if (r >= 0)
if (subsystem)
r = device_set_subsystem(device, subsystem); r = device_set_subsystem(device, subsystem);
/* use implicit names */ /* use implicit names */
else if (!isempty(path_startswith(device->devpath, "/module/"))) else if (!isempty(path_startswith(device->devpath, "/module/")))
r = device_set_subsystem(device, "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); r = device_set_drivers_subsystem(device);
else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/"))) else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/")))
r = device_set_subsystem(device, "subsystem"); r = device_set_subsystem(device, "subsystem");
else { else
device->subsystem_set = true; r = device_set_subsystem(device, NULL);
r = 0;
}
if (r < 0) if (r < 0)
return log_device_debug_errno(device, r, return log_device_debug_errno(device, r,
"sd-device: Failed to set subsystem for %s: %m", "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) { _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
int r;
assert_return(device, -EINVAL); assert_return(device, -EINVAL);
r = device_read_uevent_file(device);
if (r < 0)
return r;
if (!device->driver_set) { if (!device->driver_set) {
_cleanup_free_ char *driver = NULL; const char *driver = NULL;
const char *syspath;
char *path;
int r;
r = sd_device_get_syspath(device, &syspath); r = sd_device_get_sysattr_value(device, "driver", &driver);
if (r < 0)
return r;
path = strjoina(syspath, "/driver");
r = readlink_value(path, &driver);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return log_device_debug_errno(device, r, 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); r = device_set_driver(device, driver);
if (r < 0) if (r < 0)
@@ -1476,6 +1460,26 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
return 0; 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) { _public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) {
assert_return(device, -EINVAL); 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) { if (!device->device_id) {
_cleanup_free_ char *id = NULL; _cleanup_free_ char *id = NULL;
const char *subsystem;
dev_t devnum; dev_t devnum;
int ifindex, r; int ifindex, r;
r = sd_device_get_subsystem(device, &subsystem);
if (r < 0)
return r;
if (sd_device_get_devnum(device, &devnum) >= 0) { if (sd_device_get_devnum(device, &devnum) >= 0) {
/* use dev_t — b259:131072, c254:0 */ /* use dev_t — b259:131072, c254:0 */
if (asprintf(&id, "%c" DEVNUM_FORMAT_STR, if (asprintf(&id, "%c" DEVNUM_FORMAT_STR,
streq(subsystem, "block") ? 'b' : 'c', device_in_subsystem(device, "block") ? 'b' : 'c',
DEVNUM_FORMAT_VAL(devnum)) < 0) DEVNUM_FORMAT_VAL(devnum)) < 0)
return -ENOMEM; return -ENOMEM;
} else if (sd_device_get_ifindex(device, &ifindex) >= 0) { } 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) if (r == O_DIRECTORY)
return -EINVAL; return -EINVAL;
if (streq(subsystem, "drivers")) { if (device_in_subsystem(device, "drivers"))
/* the 'drivers' pseudo-subsystem is special, and needs the real /* the 'drivers' pseudo-subsystem is special, and needs the real
* subsystem encoded as well */ * subsystem encoded as well */
assert(device->driver_subsystem); id = strjoin("+drivers:", ASSERT_PTR(device->driver_subsystem), ":", sysname);
id = strjoin("+drivers:", device->driver_subsystem, ":", sysname); else {
} else const char *subsystem;
r = sd_device_get_subsystem(device, &subsystem);
if (r < 0)
return r;
id = strjoin("+", subsystem, ":", sysname); id = strjoin("+", subsystem, ":", sysname);
}
if (!id) if (!id)
return -ENOMEM; return -ENOMEM;
} }
@@ -2326,134 +2330,224 @@ void device_clear_sysattr_cache(sd_device *device) {
device->sysattr_values = hashmap_free(device->sysattr_values); device->sysattr_values = hashmap_free(device->sysattr_values);
} }
int device_cache_sysattr_value(sd_device *device, const char *key, char *value) { typedef struct SysAttrCacheEntry {
_unused_ _cleanup_free_ char *old_value = NULL; char *key;
_cleanup_free_ char *new_key = NULL; 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; int r;
assert(device); assert(device);
assert(key); assert(key);
assert(value || error > 0);
/* This takes the reference of the input value. The input value may be NULL. /* This takes the reference of the input arguments when cached, hence the caller must not free them
* This replaces the value if it already exists. */ * 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. */ if (ignore_uevent && streq(last_path_component(key), "uevent"))
old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key); return 0; /* not cached */
if (!new_key) {
new_key = strdup(key);
if (!new_key)
return -ENOMEM;
}
r = hashmap_ensure_put(&device->sysattr_values, &path_hash_ops_free_free, new_key, value); /* 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;
*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) if (r < 0)
return r; return r;
TAKE_PTR(new_key); TAKE_PTR(entry);
return 1; /* cached */
return 0;
} }
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) {
const char *k = NULL, *value; 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(device);
assert(key); assert(key);
value = hashmap_get2(device->sysattr_values, key, (void **) &k); entry = hashmap_get(device->sysattr_values, key);
if (!k) if (!entry)
return -ESTALE; /* We have not read the attribute. */ return -ENOANO; /* We have not read the attribute. */
if (!value) if (!entry->value) {
return -ENOENT; /* We have looked up the attribute before and it did not exist. */ /* We have looked up the attribute before and failed. Return the cached error code. */
assert(entry->error > 0);
return -entry->error;
}
if (ret_value) if (ret_value)
*ret_value = value; *ret_value = entry->value;
return 0; return 0;
} }
/* We cache all sysattr lookups. If an attribute does not exist, it is stored int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **ret_resolved, int *ret_fd) {
* with a NULL value in the cache, otherwise the returned string is stored */ int r;
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
_cleanup_free_ char *value = NULL, *path = NULL; assert(device);
assert(path);
const char *syspath; 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; int r;
assert_return(device, -EINVAL); assert_return(device, -EINVAL);
assert_return(sysattr, -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); r = device_get_cached_sysattr_value(device, sysattr, ret_value);
if (r != -ESTALE) if (r != -ENOANO)
return r; return r;
r = sd_device_get_syspath(device, &syspath); /* Special cases: read the symlink and return the last component of the value. Some core links return
if (r < 0) * only the last element of the target path, these are just values, the paths should not be exposed. */
return r; if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
_cleanup_free_ char *prefixed = NULL;
const char *syspath;
path = path_join(syspath, sysattr); r = sd_device_get_syspath(device, &syspath);
if (!path)
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) if (r < 0)
return r; return r;
/* drop trailing newlines */ prefixed = path_join(syspath, sysattr);
while (size > 0 && strchr(NEWLINE, value[--size])) if (!prefixed)
value[size] = '\0'; return -ENOMEM;
r = readlink_value(prefixed, &value);
if (r != -EINVAL) /* -EINVAL means the path is not a symlink. */
goto cache_result;
} }
/* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching r = device_chase(device, sysattr, CHASE_PREFIX_ROOT, &resolved, &fd);
* sysattr value is critical unlike the other places. */ if (r < 0)
r = device_cache_sysattr_value(device, sysattr, value); goto cache_result;
if (r < 0) {
log_device_debug_errno(device, r,
"sd-device: failed to cache attribute '%s' with '%s'%s: %m",
sysattr, value, ret_value ? "" : ", ignoring");
if (ret_value)
return r;
return 0; /* 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);
} }
if (ret_value) 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. */
log_device_debug_errno(device, k,
"sd-device: failed to cache attribute '%s' with '%s'%s: %m",
resolved, value, ret_value ? "" : ", ignoring");
if (ret_value)
return k;
}
return r;
}
assert(k > 0);
if (ret_value && r >= 0)
*ret_value = value; *ret_value = value;
/* device_cache_sysattr_value_full() takes 'resolved' and 'value' on success. */
TAKE_PTR(resolved);
TAKE_PTR(value); TAKE_PTR(value);
return 0; return r;
} }
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) { 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); return parse_boolean(value);
} }
static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) { static int device_remove_cached_sysattr_value(sd_device *device, const char *sysattr) {
_cleanup_free_ char *key = NULL; int r;
assert(device); 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) { _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;
int r; int r;
assert_return(device, -EINVAL); 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. */ /* 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. */ /* If input value is NULL, then clear cache and not write anything. */
device_remove_cached_sysattr_value(device, sysattr); return device_remove_cached_sysattr_value(device, sysattr);
return 0;
}
r = sd_device_get_syspath(device, &syspath); _cleanup_free_ char *resolved = NULL;
if (r < 0) _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; 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 */ /* value length is limited to 4k */
if (len > 4096) _cleanup_free_ char *copied = strndup(value, 4096);
return -EINVAL; if (!copied)
value = strndup(_value, len);
if (!value)
return -ENOMEM; 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) { if (r < 0) {
/* On failure, clear cache entry, as we do not know how it fails. */ /* 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; return r;
} }
/* Do not cache action string written into uevent file. */ r = device_cache_sysattr_value(device, resolved, copied, 0);
if (streq(sysattr, "uevent"))
return 0;
r = device_cache_sysattr_value(device, sysattr, value);
if (r < 0) if (r < 0)
log_device_debug_errno(device, r, log_device_debug_errno(device, r,
"sd-device: failed to cache attribute '%s' with '%s', ignoring: %m", "sd-device: failed to cache written attribute '%s' with '%s', ignoring: %m",
sysattr, value); resolved, copied);
else else if (r > 0) {
TAKE_PTR(value); TAKE_PTR(resolved);
TAKE_PTR(copied);
}
return 0; 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(device, -EINVAL);
assert_return(sysattr, -EINVAL); assert_return(sysattr, -EINVAL);
if (!format) { if (!format)
device_remove_cached_sysattr_value(device, sysattr); return device_remove_cached_sysattr_value(device, sysattr);
return 0;
}
va_start(ap, format); va_start(ap, format);
r = vasprintf(&value, format, ap); 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) { _public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
const char *s; return sd_device_trigger_with_uuid(device, action, NULL);
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);
} }
_public_ int sd_device_trigger_with_uuid( _public_ int sd_device_trigger_with_uuid(
@@ -2644,10 +2721,6 @@ _public_ int sd_device_trigger_with_uuid(
assert_return(device, -EINVAL); 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); s = device_action_to_string(action);
if (!s) if (!s)
return -EINVAL; return -EINVAL;
@@ -2662,7 +2735,8 @@ _public_ int sd_device_trigger_with_uuid(
if (r < 0) if (r < 0)
return r; return r;
*ret_uuid = u; if (ret_uuid)
*ret_uuid = u;
return 0; return 0;
} }

View File

@@ -2,12 +2,21 @@
#include <errno.h> #include <errno.h>
#include "errno-util.h"
#include "event-source.h" #include "event-source.h"
#include "event-util.h" #include "event-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "log.h" #include "log.h"
#include "string-util.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( int event_reset_time(
sd_event *e, sd_event *e,
sd_event_source **s, 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( int event_add_child_pidref(
sd_event *e, sd_event *e,
sd_event_source **s, sd_event_source **ret,
const PidRef *pid, const PidRef *pid,
int options, int options,
sd_event_child_handler_t callback, sd_event_child_handler_t callback,
void *userdata) { void *userdata) {
int r;
assert(e);
if (!pidref_is_set(pid)) if (!pidref_is_set(pid))
return -ESRCH; return -ESRCH;
if (pid->fd >= 0) if (pidref_is_remote(pid))
return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata); 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) { 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); assert_se(sd_event_now(e, CLOCK_MONOTONIC, &ts->monotonic) >= 0);
return ts; 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;
}

View File

@@ -5,8 +5,11 @@
#include "sd-event.h" #include "sd-event.h"
#include "hash-funcs.h"
#include "pidref.h" #include "pidref.h"
extern const struct hash_ops event_source_hash_ops;
int event_reset_time( int event_reset_time(
sd_event *e, sd_event *e,
sd_event_source **s, 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_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); 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);

View File

@@ -3,6 +3,7 @@
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/timerfd.h> #include <sys/timerfd.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <threads.h>
#include "sd-daemon.h" #include "sd-daemon.h"
#include "sd-event.h" #include "sd-event.h"
@@ -24,12 +25,11 @@
#include "memory-util.h" #include "memory-util.h"
#include "missing_magic.h" #include "missing_magic.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h"
#include "missing_wait.h" #include "missing_wait.h"
#include "origin-id.h" #include "origin-id.h"
#include "path-util.h" #include "path-util.h"
#include "prioq.h"
#include "pidfd-util.h" #include "pidfd-util.h"
#include "prioq.h"
#include "process-util.h" #include "process-util.h"
#include "psi-util.h" #include "psi-util.h"
#include "set.h" #include "set.h"
@@ -43,11 +43,10 @@
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) #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 */ /* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
return s && return s &&
s->type == SOURCE_CHILD && s->type == SOURCE_CHILD &&
s->child.pidfd >= 0 &&
s->child.options == WEXITED; s->child.options == WEXITED;
} }
@@ -432,7 +431,7 @@ _public_ int sd_event_new(sd_event** ret) {
if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) { 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.", 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; e->profile_delays = true;
} }
@@ -988,7 +987,7 @@ static void source_disconnect(sd_event_source *s) {
s->event->n_online_child_sources--; 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)) 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. */ /* Eventually the kernel will do this automatically for us, but for now let's emulate this (unreliably) in userspace. */
if (s->child.process_owned) { if (s->child.process_owned) {
assert(s->child.pid > 0);
assert(s->child.pidfd >= 0);
if (!s->child.exited) { if (!s->child.exited) {
if (s->child.pidfd >= 0) r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 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) if (r < 0 && r != -ESRCH)
log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m", log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
s->child.pid); s->child.pid);
@@ -1103,10 +1101,7 @@ static sd_event_source* source_free(sd_event_source *s) {
siginfo_t si = {}; siginfo_t si = {};
/* Reap the child if we can */ /* Reap the child if we can */
if (s->child.pidfd >= 0) (void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
(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.", LOG_MESSAGE("Memory trimming took %s, returned %s to OS.",
FORMAT_TIMESPAN(period, 0), FORMAT_TIMESPAN(period, 0),
FORMAT_BYTES(l)), FORMAT_BYTES(l)),
"MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR, LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
"TRIMMED_BYTES=%zu", l, LOG_ITEM("TRIMMED_BYTES=%zu", l),
"TRIMMED_USEC=" USEC_FMT, period); LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
#else #else
log_struct(LOG_DEBUG, log_struct(LOG_DEBUG,
LOG_MESSAGE("Memory trimming took %s.", LOG_MESSAGE("Memory trimming took %s.",
FORMAT_TIMESPAN(period, 0)), FORMAT_TIMESPAN(period, 0)),
"MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR, LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
"TRIMMED_USEC=" USEC_FMT, period); LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
#endif #endif
return 0; 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(s, -EINVAL);
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_IO, -EDOM); assert_return(s->type == SOURCE_IO, -EDOM);
assert_return(s->pending, -ENODATA);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
if (!s->pending)
return -ENODATA;
*ret = s->io.revents; *ret = s->io.revents;
return 0; return 0;
} }
@@ -2999,13 +2996,13 @@ static int event_source_online(
case SOURCE_CHILD: case SOURCE_CHILD:
if (EVENT_SOURCE_WATCH_PIDFD(s)) { if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* yes, we have pidfd */ /* yes, we can rely on pidfd */
r = source_child_pidfd_register(s, enabled); r = source_child_pidfd_register(s, enabled);
if (r < 0) if (r < 0)
return r; return r;
} else { } 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); r = event_make_signal_data(s->event, SIGCHLD, NULL);
if (r < 0) { 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(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
if (s->child.pidfd < 0)
return -EOPNOTSUPP;
return s->child.pidfd; 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(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
assert_return(SIGNAL_VALID(sig), -EINVAL); 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 /* If we already have seen indication the process exited refuse sending a signal early. */
* can be sure we don't accidentally kill the wrong process on PID reuse when pidfds are not
* available. */
if (s->child.exited) if (s->child.exited)
return -ESRCH; 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; siginfo_t copy;
if (si)
copy = *si;
/* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, flags));
* structure here */
if (si)
copy = *si;
if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : 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, &copy) < 0)
return -errno;
} else if (kill(s->child.pid, sig) < 0)
return -errno;
return 0;
} }
_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) { _public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
assert_return(s, -EINVAL); assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM); assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
assert(s->child.pidfd >= 0);
if (s->child.pidfd < 0)
return -EOPNOTSUPP;
return s->child.pidfd_owned; 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, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM); assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD); assert_return(!event_origin_changed(s->event), -ECHILD);
assert(s->child.pidfd >= 0);
if (s->child.pidfd < 0)
return -EOPNOTSUPP;
s->child.pidfd_owned = own; s->child.pidfd_owned = own;
return 0; 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; e->need_process_child = false;
/* So, this is ugly. We iteratively invoke waitid() with P_PID + WNOHANG for each PID we wait /* So, this is ugly. We iteratively invoke waitid() + WNOHANG with each child process we shall wait for,
* for, instead of using P_ALL. This is because we only want to get child information of very * instead of using P_ALL. This is because we only want to get child information of very specific
* specific child processes, and not all of them. We might not have processed the SIGCHLD event * 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, * 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 * 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 * 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) { HASHMAP_FOREACH(s, e->child_sources) {
assert(s->type == SOURCE_CHILD); assert(s->type == SOURCE_CHILD);
assert(s->child.pidfd >= 0);
if (s->priority > threshold) if (s->priority > threshold)
continue; continue;
@@ -3755,23 +3723,21 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
continue; continue;
zero(s->child.siginfo); 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) WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
return negative_errno(); return negative_errno();
if (s->child.siginfo.si_pid != 0) { 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) if (zombie)
s->child.exited = true; s->child.exited = true;
else if (s->child.options & WEXITED) {
if (!zombie && (s->child.options & WEXITED)) { /* If the child isn't dead then let's immediately remove the state change
/* If the child isn't dead then let's immediately remove the state * from the queue, since there's no benefit in leaving it queued. */
* change from the queue, since there's no benefit in leaving it
* queued. */
assert(s->child.options & (WSTOPPED|WCONTINUED)); 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); 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(e);
assert(s); assert(s);
assert(s->type == SOURCE_CHILD); assert(s->type == SOURCE_CHILD);
assert(s->child.pidfd >= 0);
if (s->pending) if (s->pending)
return 0; 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)) if (!EVENT_SOURCE_WATCH_PIDFD(s))
return 0; 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); 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; return -errno;
if (s->child.siginfo.si_pid == 0) if (s->child.siginfo.si_pid == 0)
return 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; s->child.exited = true;
return source_set_pending(s, true); return source_set_pending(s, true);
@@ -4222,15 +4194,13 @@ static int source_dispatch(sd_event_source *s) {
break; break;
case SOURCE_CHILD: { case SOURCE_CHILD: {
bool zombie; bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
r = s->child.callback(s, &s->child.siginfo, s->userdata); r = s->child.callback(s, &s->child.siginfo, s->userdata);
/* Now, reap the PID for good. */ /* Now, reap the PID for good. */
if (zombie) { 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; s->child.waited = true;
} }

View File

@@ -9,6 +9,7 @@
#include "hexdecoct.h" #include "hexdecoct.h"
#include "id128-util.h" #include "id128-util.h"
#include "io-util.h" #include "io-util.h"
#include "log.h"
#include "namespace-util.h" #include "namespace-util.h"
#include "process-util.h" #include "process-util.h"
#include "sha256.h" #include "sha256.h"

View File

@@ -2,6 +2,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <threads.h>
#include <unistd.h> #include <unistd.h>
#include "sd-id128.h" #include "sd-id128.h"
@@ -14,15 +15,15 @@
#include "id128-util.h" #include "id128-util.h"
#include "io-util.h" #include "io-util.h"
#include "keyring-util.h" #include "keyring-util.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h"
#include "path-util.h" #include "path-util.h"
#include "random-util.h" #include "random-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "user-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; size_t k = 0;
assert_return(s, NULL); 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; 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; size_t k = 0;
assert_return(s, NULL); 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); key = request_key("user", "invocation_id", NULL, 0);
if (key == -1) { if (key == -1) {
/* Keyring support not available? No invocation key stored? */ /* Keyring support not available? Keyring access locked down? No invocation key stored? */
if (IN_SET(errno, ENOSYS, ENOKEY)) if (ERRNO_IS_NOT_SUPPORTED(errno) ||
ERRNO_IS_PRIVILEGE(errno) ||
errno == ENOKEY)
return -ENXIO; return -ENXIO;
return -errno; return -errno;

View File

@@ -19,7 +19,7 @@
/* This is a private header; never even think of including this directly! */ /* 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." # error "Do not include _sd-common.h directly; it is a private header."
#endif #endif

View File

@@ -23,11 +23,10 @@
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#include <sys/types.h> #include <sys/types.h>
#include "_sd-common.h"
#include "sd-event.h" #include "sd-event.h"
#include "sd-id128.h" #include "sd-id128.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS; _SD_BEGIN_DECLARATIONS;
typedef struct sd_device sd_device; typedef struct sd_device sd_device;

View File

@@ -23,14 +23,13 @@
#include <net/ethernet.h> #include <net/ethernet.h>
#include <sys/types.h> #include <sys/types.h>
#include "_sd-common.h"
#include "sd-device.h" #include "sd-device.h"
#include "sd-dhcp-duid.h" #include "sd-dhcp-duid.h"
#include "sd-dhcp6-lease.h" #include "sd-dhcp6-lease.h"
#include "sd-dhcp6-option.h" #include "sd-dhcp6-option.h"
#include "sd-event.h" #include "sd-event.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS; _SD_BEGIN_DECLARATIONS;
enum { enum {

View File

@@ -23,9 +23,8 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/types.h> #include <sys/types.h>
#include "sd-dhcp6-option.h"
#include "_sd-common.h" #include "_sd-common.h"
#include "sd-dhcp6-option.h"
_SD_BEGIN_DECLARATIONS; _SD_BEGIN_DECLARATIONS;

View File

@@ -20,9 +20,8 @@
#include <inttypes.h> #include <inttypes.h>
#include <sys/types.h> #include <sys/types.h>
#include "sd-dhcp6-protocol.h"
#include "_sd-common.h" #include "_sd-common.h"
#include "sd-dhcp6-protocol.h"
_SD_BEGIN_DECLARATIONS; _SD_BEGIN_DECLARATIONS;

View File

@@ -25,14 +25,13 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/types.h> #include <sys/types.h>
#include "_sd-common.h"
#include "sd-event.h" #include "sd-event.h"
#include "sd-ndisc-neighbor.h" #include "sd-ndisc-neighbor.h"
#include "sd-ndisc-protocol.h" #include "sd-ndisc-protocol.h"
#include "sd-ndisc-redirect.h" #include "sd-ndisc-redirect.h"
#include "sd-ndisc-router.h" #include "sd-ndisc-router.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS; _SD_BEGIN_DECLARATIONS;
typedef struct sd_ndisc sd_ndisc; typedef struct sd_ndisc sd_ndisc;

View File

@@ -6,7 +6,6 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h"
void* memdup(const void *p, size_t l) { void* memdup(const void *p, size_t l) {
void *ret; void *ret;

View File

@@ -7,7 +7,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "assert-util.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h"
#if HAS_FEATURE_MEMORY_SANITIZER #if HAS_FEATURE_MEMORY_SANITIZER
# include <sanitizer/msan_interface.h> # 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); 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) { _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need)) if (size_multiply_overflow(size, need))
return NULL; return NULL;
@@ -275,5 +268,3 @@ _alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
return q; return q;
} }
#include "memory-util.h"

View 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)

View File

@@ -7,6 +7,7 @@
#define _INDEX_TO_MASK(type, i, uniq) \ #define _INDEX_TO_MASK(type, i, uniq) \
({ \ ({ \
int UNIQ_T(_i, uniq) = (i); \ int UNIQ_T(_i, uniq) = (i); \
assert(UNIQ_T(_i, uniq) >= 0); \
assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \ assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \
((type)1) << UNIQ_T(_i, uniq); \ ((type)1) << UNIQ_T(_i, uniq); \
}) })

View File

@@ -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_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_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, size_t *ret_size);
int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
/* Returns negative on error, and 0 or 1 on success for the bool value */ /* 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_get_xattr_bool(const char *path, const char *name);
int cg_remove_xattr(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_from_string(const char *s, CGroupMask *ret);
int cg_mask_to_string(CGroupMask mask, char **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); bool cg_kill_supported(void);
int cg_all_unified(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_; const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_; 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 { typedef enum ManagedOOMMode {
MANAGED_OOM_AUTO, MANAGED_OOM_AUTO,
MANAGED_OOM_KILL, MANAGED_OOM_KILL,

View File

@@ -4,13 +4,13 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <linux/fs.h>
#include "bitfield.h" #include "bitfield.h"
#include "chattr-util.h" #include "chattr-util.h"
#include "errno-util.h" #include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "string-util.h" #include "string-util.h"

View File

@@ -2,7 +2,6 @@
#pragma once #pragma once
#include <fcntl.h> #include <fcntl.h>
#include <linux/fs.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
@@ -41,14 +40,14 @@ typedef enum ChattrApplyFlags {
} ChattrApplyFlags; } ChattrApplyFlags;
int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags); 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) { static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask) {
return chattr_full(dir_fd, path, value, mask, previous, NULL, 0); return chattr_full(dir_fd, path, value, mask, NULL, NULL, 0);
} }
static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) { static inline int chattr_fd(int fd, unsigned value, unsigned mask) {
return chattr_full(fd, NULL, value, mask, previous, NULL, 0); return chattr_full(fd, NULL, value, mask, NULL, NULL, 0);
} }
static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) { static inline int chattr_path(const char *path, unsigned value, unsigned mask) {
return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0); return chattr_full(AT_FDCWD, path, value, mask, NULL, NULL, 0);
} }
int read_attr_fd(int fd, unsigned *ret); int read_attr_fd(int fd, unsigned *ret);

View File

@@ -9,6 +9,9 @@
int parse_devnum(const char *s, dev_t *ret); 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 /* 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 * 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 * 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) \ #define DEVICE_MAJOR_VALID(x) \
({ \ ({ \
typeof(x) _x = (x), _y = 0; \ typeof(x) _x = (x), _y = 0; \
_x >= _y && _x < (UINT32_C(1) << 12); \ _x >= _y && _x <= DEVNUM_MAJOR_MAX; \
\ \
}) })
#define DEVICE_MINOR_VALID(x) \ #define DEVICE_MINOR_VALID(x) \
({ \ ({ \
typeof(x) _x = (x), _y = 0; \ 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); 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) { static inline bool devnum_is_zero(dev_t d) {
return major(d) == 0 && minor(d) == 0; 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)))

View File

@@ -7,6 +7,7 @@
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "log.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"

View File

@@ -11,6 +11,7 @@
#include "errno-util.h" #include "errno-util.h"
#include "escape.h" #include "escape.h"
#include "extract-word.h" #include "extract-word.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-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; return NULL;
t = strndupa_safe(name, k); t = strndupa_safe(name, k);
return getenv(t); return secure_getenv(t);
}; };
return NULL; return NULL;
@@ -695,7 +696,7 @@ int replace_env_full(
_cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL; _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 */ const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
char ***pu, ***pb, *k; char ***pu, ***pb;
size_t i, len = 0; /* len is initialized to appease gcc */ size_t i, len = 0; /* len is initialized to appease gcc */
int nest = 0, r; int nest = 0, r;
@@ -717,33 +718,24 @@ int replace_env_full(
case CURLY: case CURLY:
if (*e == '{') { if (*e == '{') {
k = strnappend(s, word, e-word-1); if (!strextendn(&s, word, e-word-1))
if (!k)
return -ENOMEM; return -ENOMEM;
free_and_replace(s, k);
word = e-1; word = e-1;
state = VARIABLE; state = VARIABLE;
nest++; nest++;
} else if (*e == '$') { } else if (*e == '$') {
k = strnappend(s, word, e-word); if (!strextendn(&s, word, e-word))
if (!k)
return -ENOMEM; return -ENOMEM;
free_and_replace(s, k);
word = e+1; word = e+1;
state = WORD; state = WORD;
} else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) { } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
k = strnappend(s, word, e-word-1); if (!strextendn(&s, word, e-word-1))
if (!k)
return -ENOMEM; return -ENOMEM;
free_and_replace(s, k);
word = e-1; word = e-1;
state = VARIABLE_RAW; 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 * 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) */ * testing, and given that people are likely going to misuse this, be thorough) */
e = getenv(name); e = secure_getenv(name);
if (!e) { if (!e) {
if (ret) if (ret)
*ret = NULL; *ret = NULL;
@@ -1138,25 +1130,6 @@ int getenv_steal_erase(const char *name, char **ret) {
return 1; 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, ...) { int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
_cleanup_free_ char *value = NULL; _cleanup_free_ char *value = NULL;
va_list ap; va_list ap;

View File

@@ -82,6 +82,4 @@ int getenv_path_list(const char *name, char ***ret_paths);
int getenv_steal_erase(const char *name, char **ret); 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); int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);

View File

@@ -5,6 +5,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "assert-util.h"
#include "macro.h" #include "macro.h"
/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */ /* strerror(3) says that glibc uses a maximum length of 1024 bytes. */

View File

@@ -80,10 +80,15 @@ char* cescape_length(const char *s, size_t n) {
const char *f; const char *f;
char *r, *t; char *r, *t;
/* Does C style string escaping. May be reversed with cunescape(). */
assert(s || n == 0); assert(s || n == 0);
/* Does C style string escaping. May be reversed with if (n == SIZE_MAX)
* cunescape(). */ n = strlen(s);
if (n > (SIZE_MAX - 1) / 4)
return NULL;
r = new(char, n*4 + 1); r = new(char, n*4 + 1);
if (!r) if (!r)
@@ -97,12 +102,6 @@ char* cescape_length(const char *s, size_t n) {
return r; 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 cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) {
int r = 1; 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* octescape(const char *s, size_t len) {
char *buf, *t; 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); assert(s || len == 0);
@@ -479,13 +478,19 @@ char* octescape(const char *s, size_t len) {
return buf; 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; char *buf, *t;
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */ /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
assert(s || len == 0); 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); t = buf = new(char, len * 4 + 1);
if (!buf) if (!buf)
return NULL; return NULL;

View File

@@ -8,7 +8,6 @@
#include <uchar.h> #include <uchar.h>
#include "string-util.h" #include "string-util.h"
#include "missing_type.h"
/* What characters are special in the shell? */ /* What characters are special in the shell? */
/* must be escaped outside and inside double-quotes */ /* must be escaped outside and inside double-quotes */
@@ -41,9 +40,11 @@ typedef enum ShellEscapeFlags {
SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */ SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */
} ShellEscapeFlags; } ShellEscapeFlags;
char* cescape(const char *s);
char* cescape_length(const char *s, size_t n);
int cescape_char(char c, char *buf); 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); 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); return xescape_full(s, bad, SIZE_MAX, 0);
} }
char* octescape(const char *s, size_t len); 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* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
char* shell_escape(const char *s, const char *bad); char* shell_escape(const char *s, const char *bad);

View File

@@ -8,6 +8,7 @@
#include "ether-addr-util.h" #include "ether-addr-util.h"
#include "hexdecoct.h" #include "hexdecoct.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "string-util.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; 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) { int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
return memcmp(a, b, ETH_ALEN); return memcmp(a, b, ETH_ALEN);
} }

View File

@@ -72,7 +72,6 @@ extern const struct hash_ops hw_addr_hash_ops_free;
#define ETHER_ADDR_TO_STRING_MAX (3*6) #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]); 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! */ /* 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]){}) #define ETHER_ADDR_TO_STR(addr) ether_addr_to_string((addr), (char[ETHER_ADDR_TO_STRING_MAX]){})

View File

@@ -2,9 +2,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#if WANT_LINUX_FS_H #include <linux/kcmp.h>
#include <linux/fs.h>
#endif
#include <linux/magic.h> #include <linux/magic.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/resource.h> #include <sys/resource.h>
@@ -17,6 +15,7 @@
#include "fileio.h" #include "fileio.h"
#include "fs-util.h" #include "fs-util.h"
#include "io-util.h" #include "io-util.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "missing_fcntl.h" #include "missing_fcntl.h"
#include "missing_fs.h" #include "missing_fs.h"
@@ -29,7 +28,6 @@
#include "sort-util.h" #include "sort-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "stdio-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 /* The maximum number of iterations in the loop to close descriptors in the fallback case
* when /proc/self/fd/ is inaccessible. */ * when /proc/self/fd/ is inaccessible. */
@@ -1003,13 +1001,13 @@ int fd_verify_safe_flags_full(int fd, int extra_flags) {
if (flags < 0) if (flags < 0)
return -errno; 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) if (unexpected_flags != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO), return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
"Unexpected flags set for extrinsic fd: 0%o", "Unexpected flags set for extrinsic fd: 0%o",
(unsigned) unexpected_flags); (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) { 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) { int fds_are_same_mount(int fd1, int fd2) {
STRUCT_NEW_STATX_DEFINE(st1); struct statx sx1 = {}, sx2 = {}; /* explicitly initialize the struct to make msan silent. */
STRUCT_NEW_STATX_DEFINE(st2);
int r; int r;
assert(fd1 >= 0); assert(fd1 >= 0);
assert(fd2 >= 0); assert(fd2 >= 0);
r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx); if (statx(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
if (r < 0) return -errno;
return r;
r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx); if (statx(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2) < 0)
if (r < 0) return -errno;
return r;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */ /* 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; return false;
/* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old /* 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 * 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. */ * 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; int mntid;
r = path_get_mnt_id_at_fallback(fd1, "", &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; return r;
assert(mntid >= 0); assert(mntid >= 0);
st1.nsx.stx_mnt_id = mntid; sx1.stx_mnt_id = mntid;
st1.nsx.stx_mask |= STATX_MNT_ID; 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; int mntid;
r = path_get_mnt_id_at_fallback(fd2, "", &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; return r;
assert(mntid >= 0); assert(mntid >= 0);
st2.nsx.stx_mnt_id = mntid; sx2.stx_mnt_id = mntid;
st2.nsx.stx_mask |= STATX_MNT_ID; 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) { const char* accmode_to_string(int flags) {
switch (flags & O_ACCMODE) { switch (flags & O_ACCMODE_STRICT) {
case O_RDONLY: case O_RDONLY:
return "ro"; return "ro";
case O_WRONLY: case O_WRONLY:

View File

@@ -8,6 +8,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include "macro.h" #include "macro.h"
#include "memory-util.h"
#include "missing_fcntl.h" #include "missing_fcntl.h"
#include "stdio-util.h" #include "stdio-util.h"

View File

@@ -244,17 +244,13 @@ static int write_string_file_atomic_at(
} }
r = fopen_temporary_at(dir_fd, fn, &f, &p); 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) if (r < 0)
goto fail; 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); r = write_string_stream_full(f, line, flags, ts);
if (r < 0) if (r < 0)
goto fail; goto fail;
@@ -277,9 +273,6 @@ static int write_string_file_atomic_at(
return 0; return 0;
fail: fail:
if (call_label_ops_post)
(void) label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f);
if (f) if (f)
(void) unlinkat(dir_fd, p, 0); (void) unlinkat(dir_fd, p, 0);
return r; return r;
@@ -293,24 +286,27 @@ int write_string_file_full(
const struct timespec *ts, const struct timespec *ts,
const char *label_fn) { const char *label_fn) {
bool call_label_ops_post = false, made_file = false; bool made_file = false;
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
int r; int r;
assert(fn); assert(dir_fd == AT_FDCWD || dir_fd >= 0);
assert(line); assert(line);
/* We don't know how to verify whether the file contents was already on-disk. */ /* 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))); assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
if (flags & WRITE_STRING_FILE_MKDIR_0755) { if (flags & WRITE_STRING_FILE_MKDIR_0755) {
assert(fn);
r = mkdirat_parents(dir_fd, fn, 0755); r = mkdirat_parents(dir_fd, fn, 0755);
if (r < 0) if (r < 0)
return r; return r;
} }
if (flags & WRITE_STRING_FILE_ATOMIC) { if (flags & WRITE_STRING_FILE_ATOMIC) {
assert(fn);
assert(flags & WRITE_STRING_FILE_CREATE); assert(flags & WRITE_STRING_FILE_CREATE);
r = write_string_file_atomic_at(dir_fd, fn, line, flags, ts); r = write_string_file_atomic_at(dir_fd, fn, line, flags, ts);
@@ -320,37 +316,39 @@ int write_string_file_full(
return r; return r;
} }
mode_t mode = write_string_file_flags_to_mode(flags);
if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
if (r < 0)
goto fail;
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. */ /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
fd = openat_report_new( if (isempty(fn))
dir_fd, fn, O_CLOEXEC | O_NOCTTY | r = fd = fd_reopen(
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) | ASSERT_FD(dir_fd), O_CLOEXEC | O_NOCTTY |
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY));
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY), else {
mode, mode_t mode = write_string_file_flags_to_mode(flags);
&made_file); bool call_label_ops_post = false;
if (fd < 0) {
r = fd; if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
if (r < 0)
goto fail;
call_label_ops_post = true;
}
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) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
mode,
&made_file);
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 (r < 0)
goto fail; goto fail;
}
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); r = take_fdopen_unlocked(&fd, "w", &f);
if (r < 0) if (r < 0)
@@ -366,9 +364,6 @@ int write_string_file_full(
return 0; return 0;
fail: fail:
if (call_label_ops_post)
(void) label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file);
if (made_file) if (made_file)
(void) unlinkat(dir_fd, fn, 0); (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 /* OK, the operation failed, but let's see if the right contents in place already. If so, eat up the
* error. */ * 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 0;
return r; 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; size_t l, k;
int r; int r;
assert(fn);
assert(blob); assert(blob);
l = strlen(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) if (!buf)
return -ENOMEM; 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) if (r < 0)
return r; return r;
@@ -474,7 +468,13 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
return 1; 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; _cleanup_free_ char *buf = NULL;
size_t n, size; size_t n, size;
int n_retries; 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 * 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 * 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 * 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); 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(). */ /* Limit the number of attempts to read the number of bytes returned by fstat(). */
n_retries = 3; 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; 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( int read_full_stream_full(
FILE *f, FILE *f,
const char *filename, const char *filename,
@@ -723,7 +710,7 @@ int read_full_stream_full(
size_t k; size_t k;
/* If we shall fail when reading overly large data, then read exactly one byte more than the /* 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) if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
n_next = size + 1; n_next = size + 1;
@@ -902,74 +889,46 @@ int script_get_shebang_interpreter(const char *path, char **ret) {
return 0; return 0;
} }
/** int get_proc_field(const char *path, const char *key, char **ret) {
* Retrieve one field from a file like /proc/self/status. pattern _cleanup_fclose_ FILE *f = NULL;
* 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 r; int r;
assert(terminator); /* Retrieve one field from a file like /proc/self/status. "key" matches the beginning of the line
assert(filename); * and should not include whitespace or the delimiter (':').
assert(pattern); * Whitespaces after the ':' will be skipped. Only the first element is returned
assert(field); * (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) if (r < 0)
return r; return r;
t = status; for (;;) {
_cleanup_free_ char *line = NULL;
do { r = read_line(f, LONG_LINE_MAX, &line);
bool pattern_ok; if (r < 0)
return r;
if (r == 0)
return -ENODATA;
do { char *l = startswith(line, key);
t = strstr(t, pattern); if (l && *l == ':') {
if (!t) if (ret) {
return -ENOENT; char *s = strdupcspn(skip_leading_chars(l + 1, " \t"), WHITESPACE);
if (!s)
return -ENOMEM;
/* Check that pattern occurs in beginning of line. */ *ret = s;
pattern_ok = (t == status || t[-1] == '\n'); }
t += strlen(pattern); return 0;
}
} 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)
return -ENOMEM;
*field = f;
return 0;
} }
DIR* xopendirat(int dir_fd, const char *name, int flags) { DIR* xopendirat(int dir_fd, const char *name, int flags) {

View File

@@ -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) { 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); 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) { static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_at(AT_FDCWD, fn, line, 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); 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); 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) { 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); 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); 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 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); DIR* xopendirat(int dir_fd, const char *name, int flags);

View File

@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "format-ifname.h" #include "format-ifname.h"
#include "log.h"
#include "stdio-util.h"
#include "string-util.h" #include "string-util.h"
assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE); assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);

View File

@@ -62,5 +62,4 @@ char* format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
finish: finish:
buf[l-1] = 0; buf[l-1] = 0;
return buf; return buf;
} }

View File

@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h> #include <errno.h>
#include <linux/falloc.h>
#include <linux/magic.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/file.h> #include <sys/file.h>
#include <linux/falloc.h>
#include <linux/magic.h>
#include <unistd.h> #include <unistd.h>
#include "alloc-util.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 rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
int r; int r;
assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
assert(oldpath);
assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
assert(newpath);
/* Try the ideal approach first */ /* Try the ideal approach first */
if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0) if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
return 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) if (st.st_blocks == 0 || st.st_nlink > 0)
return 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)) if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
return -EINVAL; return -EINVAL;
if ((flags & O_ACCMODE) != O_RDONLY) if ((flags & O_ACCMODE_STRICT) != O_RDONLY)
return -EINVAL; return -EINVAL;
/* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following /* 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)) { 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)) if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
goto error; goto error;
} }

View File

@@ -172,3 +172,12 @@ static inline int at_flags_normalize_nofollow(int flags) {
flags |= AT_SYMLINK_NOFOLLOW; flags |= AT_SYMLINK_NOFOLLOW;
return flags; 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;
}

View File

@@ -23,7 +23,7 @@ bool emoji_enabled(void) {
return cached_emoji_enabled; 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 /* 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 * 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 * 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 */ /* ASCII fallback */
[false] = { [false] = {
[SPECIAL_GLYPH_TREE_VERTICAL] = "| ", [GLYPH_SPACE] = " ",
[SPECIAL_GLYPH_TREE_BRANCH] = "|-", [GLYPH_TREE_VERTICAL] = "| ",
[SPECIAL_GLYPH_TREE_RIGHT] = "`-", [GLYPH_TREE_BRANCH] = "|-",
[SPECIAL_GLYPH_TREE_SPACE] = " ", [GLYPH_TREE_RIGHT] = "`-",
[SPECIAL_GLYPH_TREE_TOP] = ",-", [GLYPH_TREE_SPACE] = " ",
[SPECIAL_GLYPH_VERTICAL_DOTTED] = ":", [GLYPH_TREE_TOP] = ",-",
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = "-", [GLYPH_VERTICAL_DOTTED] = ":",
[SPECIAL_GLYPH_HORIZONTAL_FAT] = "=", [GLYPH_HORIZONTAL_DOTTED] = "-",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">", [GLYPH_HORIZONTAL_FAT] = "=",
[SPECIAL_GLYPH_BLACK_CIRCLE] = "*", [GLYPH_TRIANGULAR_BULLET] = ">",
[SPECIAL_GLYPH_WHITE_CIRCLE] = "*", [GLYPH_BLACK_CIRCLE] = "*",
[SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "x", [GLYPH_WHITE_CIRCLE] = "*",
[SPECIAL_GLYPH_CIRCLE_ARROW] = "*", [GLYPH_MULTIPLICATION_SIGN] = "x",
[SPECIAL_GLYPH_BULLET] = "*", [GLYPH_CIRCLE_ARROW] = "*",
[SPECIAL_GLYPH_MU] = "u", [GLYPH_BULLET] = "*",
[SPECIAL_GLYPH_CHECK_MARK] = "+", [GLYPH_MU] = "u",
[SPECIAL_GLYPH_CROSS_MARK] = "-", [GLYPH_CHECK_MARK] = "+",
[SPECIAL_GLYPH_LIGHT_SHADE] = "-", [GLYPH_CROSS_MARK] = "-",
[SPECIAL_GLYPH_DARK_SHADE] = "X", [GLYPH_LIGHT_SHADE] = "-",
[SPECIAL_GLYPH_FULL_BLOCK] = "#", [GLYPH_DARK_SHADE] = "X",
[SPECIAL_GLYPH_SIGMA] = "S", [GLYPH_FULL_BLOCK] = "#",
[SPECIAL_GLYPH_ARROW_UP] = "^", [GLYPH_SIGMA] = "S",
[SPECIAL_GLYPH_ARROW_DOWN] = "v", [GLYPH_ARROW_UP] = "^",
[SPECIAL_GLYPH_ARROW_LEFT] = "<-", [GLYPH_ARROW_DOWN] = "v",
[SPECIAL_GLYPH_ARROW_RIGHT] = "->", [GLYPH_ARROW_LEFT] = "<-",
[SPECIAL_GLYPH_ELLIPSIS] = "...", [GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]", [GLYPH_ELLIPSIS] = "...",
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]", [GLYPH_EXTERNAL_LINK] = "[LNK]",
[SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}", [GLYPH_ECSTATIC_SMILEY] = ":-]",
[SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)", [GLYPH_HAPPY_SMILEY] = ":-}",
[SPECIAL_GLYPH_NEUTRAL_SMILEY] = ":-|", [GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)",
[SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(", [GLYPH_NEUTRAL_SMILEY] = ":-|",
[SPECIAL_GLYPH_UNHAPPY_SMILEY] = ":-{", [GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
[SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[", [GLYPH_UNHAPPY_SMILEY] = ":-{",
[SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,", [GLYPH_DEPRESSED_SMILEY] = ":-[",
[SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */ [GLYPH_LOCK_AND_KEY] = "o-,",
[SPECIAL_GLYPH_RECYCLING] = "~", [GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
[SPECIAL_GLYPH_DOWNLOAD] = "\\", [GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_SPARKLES] = "*", [GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_LOW_BATTERY] = "!", [GLYPH_SPARKLES] = "*",
[SPECIAL_GLYPH_WARNING_SIGN] = "!", [GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_RED_CIRCLE] = "o", [GLYPH_WARNING_SIGN] = "!",
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o", [GLYPH_RED_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o", [GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o", [GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_SUPERHERO] = "S", [GLYPH_GREEN_CIRCLE] = "o",
[SPECIAL_GLYPH_IDCARD] = "@", [GLYPH_SUPERHERO] = "S",
[GLYPH_IDCARD] = "@",
[GLYPH_HOME] = "^",
}, },
/* UTF-8 */ /* UTF-8 */
[true] = { [true] = {
/* This exists to allow more consistent handling of optional whitespace */
[GLYPH_SPACE] = " ",
/* The following are multiple glyphs in both ASCII and in UNICODE */ /* The following are multiple glyphs in both ASCII and in UNICODE */
[SPECIAL_GLYPH_TREE_VERTICAL] = u8"", [GLYPH_TREE_VERTICAL] = UTF8(""),
[SPECIAL_GLYPH_TREE_BRANCH] = u8"├─", [GLYPH_TREE_BRANCH] = UTF8("├─"),
[SPECIAL_GLYPH_TREE_RIGHT] = u8"└─", [GLYPH_TREE_RIGHT] = UTF8("└─"),
[SPECIAL_GLYPH_TREE_SPACE] = u8" ", [GLYPH_TREE_SPACE] = UTF8(" "),
[SPECIAL_GLYPH_TREE_TOP] = u8"┌─", [GLYPH_TREE_TOP] = UTF8("┌─"),
/* Single glyphs in both cases */ /* Single glyphs in both cases */
[SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"", [GLYPH_VERTICAL_DOTTED] = UTF8(""),
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = u8"", [GLYPH_HORIZONTAL_DOTTED] = UTF8(""),
[SPECIAL_GLYPH_HORIZONTAL_FAT] = u8"", [GLYPH_HORIZONTAL_FAT] = UTF8(""),
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"", [GLYPH_TRIANGULAR_BULLET] = UTF8(""),
[SPECIAL_GLYPH_BLACK_CIRCLE] = u8"", [GLYPH_BLACK_CIRCLE] = UTF8(""),
[SPECIAL_GLYPH_WHITE_CIRCLE] = u8"", [GLYPH_WHITE_CIRCLE] = UTF8(""),
[SPECIAL_GLYPH_MULTIPLICATION_SIGN] = u8"×", [GLYPH_MULTIPLICATION_SIGN] = UTF8("×"),
[SPECIAL_GLYPH_CIRCLE_ARROW] = u8"", [GLYPH_CIRCLE_ARROW] = UTF8(""),
[SPECIAL_GLYPH_BULLET] = u8"", [GLYPH_BULLET] = UTF8(""),
[SPECIAL_GLYPH_MU] = u8"μ", /* actually called: GREEK SMALL LETTER MU */ [GLYPH_MU] = UTF8("μ"), /* actually called: GREEK SMALL LETTER MU */
[SPECIAL_GLYPH_CHECK_MARK] = u8"", [GLYPH_CHECK_MARK] = UTF8(""),
[SPECIAL_GLYPH_CROSS_MARK] = u8"", /* actually called: BALLOT X */ [GLYPH_CROSS_MARK] = UTF8(""), /* actually called: BALLOT X */
[SPECIAL_GLYPH_LIGHT_SHADE] = u8"", [GLYPH_LIGHT_SHADE] = UTF8(""),
[SPECIAL_GLYPH_DARK_SHADE] = u8"", [GLYPH_DARK_SHADE] = UTF8(""),
[SPECIAL_GLYPH_FULL_BLOCK] = u8"", [GLYPH_FULL_BLOCK] = UTF8(""),
[SPECIAL_GLYPH_SIGMA] = u8"Σ", [GLYPH_SIGMA] = UTF8("Σ"),
[SPECIAL_GLYPH_ARROW_UP] = u8"", /* actually called: UPWARDS ARROW */ [GLYPH_ARROW_UP] = UTF8(""), /* actually called: UPWARDS ARROW */
[SPECIAL_GLYPH_ARROW_DOWN] = u8"", /* actually called: DOWNWARDS ARROW */ [GLYPH_ARROW_DOWN] = UTF8(""), /* actually called: DOWNWARDS ARROW */
/* Single glyph in Unicode, two in ASCII */ /* Single glyph in Unicode, two in ASCII */
[SPECIAL_GLYPH_ARROW_LEFT] = u8"", /* actually called: LEFTWARDS ARROW */ [GLYPH_ARROW_LEFT] = UTF8(""), /* actually called: LEFTWARDS ARROW */
[SPECIAL_GLYPH_ARROW_RIGHT] = u8"", /* actually called: RIGHTWARDS ARROW */ [GLYPH_ARROW_RIGHT] = UTF8(""), /* actually called: RIGHTWARDS ARROW */
/* Single glyph in Unicode, three in ASCII */ /* 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 */ /* 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 */ /* These smileys are a single glyph in Unicode, and three in ASCII */
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = u8"😇", /* actually called: SMILING FACE WITH HALO */ [GLYPH_ECSTATIC_SMILEY] = UTF8("😇"), /* actually called: SMILING FACE WITH HALO */
[SPECIAL_GLYPH_HAPPY_SMILEY] = u8"😀", /* actually called: GRINNING FACE */ [GLYPH_HAPPY_SMILEY] = UTF8("😀"), /* actually called: GRINNING FACE */
[SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = u8"🙂", /* actually called: SLIGHTLY SMILING FACE */ [GLYPH_SLIGHTLY_HAPPY_SMILEY] = UTF8("🙂"), /* actually called: SLIGHTLY SMILING FACE */
[SPECIAL_GLYPH_NEUTRAL_SMILEY] = u8"😐", /* actually called: NEUTRAL FACE */ [GLYPH_NEUTRAL_SMILEY] = UTF8("😐"), /* actually called: NEUTRAL FACE */
[SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = u8"🙁", /* actually called: SLIGHTLY FROWNING FACE */ [GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = UTF8("🙁"), /* actually called: SLIGHTLY FROWNING FACE */
[SPECIAL_GLYPH_UNHAPPY_SMILEY] = u8"😨", /* actually called: FEARFUL FACE */ [GLYPH_UNHAPPY_SMILEY] = UTF8("😨"), /* actually called: FEARFUL FACE */
[SPECIAL_GLYPH_DEPRESSED_SMILEY] = u8"🤢", /* actually called: NAUSEATED FACE */ [GLYPH_DEPRESSED_SMILEY] = UTF8("🤢"), /* actually called: NAUSEATED FACE */
/* This emoji is a single character cell glyph in Unicode, and three in ASCII */ /* 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 */ /* 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. */ /* These four emojis are single character cell glyphs in Unicode and also in ASCII. */
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */ [GLYPH_RECYCLING] = UTF8("♻️"), /* actually called: UNIVERSAL RECYCLNG SYMBOL */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */ [GLYPH_DOWNLOAD] = UTF8("⤵️"), /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"", [GLYPH_SPARKLES] = UTF8(""),
[SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫", [GLYPH_LOW_BATTERY] = UTF8("🪫"),
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️", [GLYPH_WARNING_SIGN] = UTF8("⚠️"),
[SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽", [GLYPH_COMPUTER_DISK] = UTF8("💽"),
[SPECIAL_GLYPH_WORLD] = u8"🌍", [GLYPH_WORLD] = UTF8("🌍"),
[SPECIAL_GLYPH_RED_CIRCLE] = u8"🔴", [GLYPH_RED_CIRCLE] = UTF8("🔴"),
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡", [GLYPH_YELLOW_CIRCLE] = UTF8("🟡"),
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵", [GLYPH_BLUE_CIRCLE] = UTF8("🔵"),
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢", [GLYPH_GREEN_CIRCLE] = UTF8("🟢"),
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸", [GLYPH_SUPERHERO] = UTF8("🦸"),
[SPECIAL_GLYPH_IDCARD] = u8"🪪", [GLYPH_IDCARD] = UTF8("🪪"),
[GLYPH_HOME] = UTF8("🏠"),
}, },
}; };
if (code < 0) if (code < 0)
return NULL; return NULL;
assert(code < _SPECIAL_GLYPH_MAX); assert(code < _GLYPH_MAX);
return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code]; return draw_table[force_utf || (code >= _GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
} }

View File

@@ -6,73 +6,79 @@
#include "macro.h" #include "macro.h"
typedef enum SpecialGlyph { typedef enum Glyph {
SPECIAL_GLYPH_TREE_VERTICAL, GLYPH_SPACE,
SPECIAL_GLYPH_TREE_BRANCH, GLYPH_TREE_VERTICAL,
SPECIAL_GLYPH_TREE_RIGHT, GLYPH_TREE_BRANCH,
SPECIAL_GLYPH_TREE_SPACE, GLYPH_TREE_RIGHT,
SPECIAL_GLYPH_TREE_TOP, GLYPH_TREE_SPACE,
SPECIAL_GLYPH_VERTICAL_DOTTED, GLYPH_TREE_TOP,
SPECIAL_GLYPH_HORIZONTAL_DOTTED, GLYPH_VERTICAL_DOTTED,
SPECIAL_GLYPH_HORIZONTAL_FAT, GLYPH_HORIZONTAL_DOTTED,
SPECIAL_GLYPH_TRIANGULAR_BULLET, GLYPH_HORIZONTAL_FAT,
SPECIAL_GLYPH_BLACK_CIRCLE, GLYPH_TRIANGULAR_BULLET,
SPECIAL_GLYPH_WHITE_CIRCLE, GLYPH_BLACK_CIRCLE,
SPECIAL_GLYPH_MULTIPLICATION_SIGN, GLYPH_WHITE_CIRCLE,
SPECIAL_GLYPH_CIRCLE_ARROW, GLYPH_MULTIPLICATION_SIGN,
SPECIAL_GLYPH_BULLET, GLYPH_CIRCLE_ARROW,
SPECIAL_GLYPH_MU, GLYPH_BULLET,
SPECIAL_GLYPH_CHECK_MARK, GLYPH_MU,
SPECIAL_GLYPH_CROSS_MARK, GLYPH_CHECK_MARK,
SPECIAL_GLYPH_LIGHT_SHADE, GLYPH_CROSS_MARK,
SPECIAL_GLYPH_DARK_SHADE, GLYPH_LIGHT_SHADE,
SPECIAL_GLYPH_FULL_BLOCK, GLYPH_DARK_SHADE,
SPECIAL_GLYPH_SIGMA, GLYPH_FULL_BLOCK,
SPECIAL_GLYPH_ARROW_UP, GLYPH_SIGMA,
SPECIAL_GLYPH_ARROW_DOWN, GLYPH_ARROW_UP,
SPECIAL_GLYPH_ARROW_LEFT, GLYPH_ARROW_DOWN,
SPECIAL_GLYPH_ARROW_RIGHT, GLYPH_ARROW_LEFT,
SPECIAL_GLYPH_ELLIPSIS, GLYPH_ARROW_RIGHT,
SPECIAL_GLYPH_EXTERNAL_LINK, GLYPH_ELLIPSIS,
_SPECIAL_GLYPH_FIRST_EMOJI, GLYPH_EXTERNAL_LINK,
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI, _GLYPH_FIRST_EMOJI,
SPECIAL_GLYPH_HAPPY_SMILEY, GLYPH_ECSTATIC_SMILEY = _GLYPH_FIRST_EMOJI,
SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY, GLYPH_HAPPY_SMILEY,
SPECIAL_GLYPH_NEUTRAL_SMILEY, GLYPH_SLIGHTLY_HAPPY_SMILEY,
SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY, GLYPH_NEUTRAL_SMILEY,
SPECIAL_GLYPH_UNHAPPY_SMILEY, GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
SPECIAL_GLYPH_DEPRESSED_SMILEY, GLYPH_UNHAPPY_SMILEY,
SPECIAL_GLYPH_LOCK_AND_KEY, GLYPH_DEPRESSED_SMILEY,
SPECIAL_GLYPH_TOUCH, GLYPH_LOCK_AND_KEY,
SPECIAL_GLYPH_RECYCLING, GLYPH_TOUCH,
SPECIAL_GLYPH_DOWNLOAD, GLYPH_RECYCLING,
SPECIAL_GLYPH_SPARKLES, GLYPH_DOWNLOAD,
SPECIAL_GLYPH_LOW_BATTERY, GLYPH_SPARKLES,
SPECIAL_GLYPH_WARNING_SIGN, GLYPH_LOW_BATTERY,
SPECIAL_GLYPH_COMPUTER_DISK, GLYPH_WARNING_SIGN,
SPECIAL_GLYPH_WORLD, GLYPH_COMPUTER_DISK,
SPECIAL_GLYPH_RED_CIRCLE, GLYPH_WORLD,
SPECIAL_GLYPH_YELLOW_CIRCLE, GLYPH_RED_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE, GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE, GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_SUPERHERO, GLYPH_GREEN_CIRCLE,
SPECIAL_GLYPH_IDCARD, GLYPH_SUPERHERO,
_SPECIAL_GLYPH_MAX, GLYPH_IDCARD,
_SPECIAL_GLYPH_INVALID = -EINVAL, GLYPH_HOME,
} SpecialGlyph; _GLYPH_MAX,
_GLYPH_INVALID = -EINVAL,
} Glyph;
bool emoji_enabled(void); 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) { static inline const char* glyph(Glyph code) {
return special_glyph_full(code, false); return glyph_full(code, false);
} }
static inline const char* special_glyph_check_mark(bool b) { static inline const char* optional_glyph(Glyph code) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK); return emoji_enabled() ? glyph(code) : "";
} }
static inline const char* special_glyph_check_mark_space(bool b) { static inline const char* glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " "; 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) : " ";
} }

View File

@@ -10,15 +10,23 @@ void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state); siphash24_compress(p, strlen(p) + 1, state);
} }
DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func); DEFINE_HASH_OPS(string_hash_ops,
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, char, string_hash_func, string_compare_func);
char, string_hash_func, string_compare_func, free); DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, string_hash_ops_free,
char, string_hash_func, string_compare_func, free, char, string_hash_func, string_compare_func, free);
void, free); DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free, string_hash_ops_value_free,
char, string_hash_func, string_compare_func, free, char, string_hash_func, string_compare_func,
char*, strv_free); 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,
char, string_hash_func, string_compare_func, free,
char*, strv_free);
void path_hash_func(const char *q, struct siphash *state) { void path_hash_func(const char *q, struct siphash *state) {
bool add_slash = false; bool add_slash = false;
@@ -59,12 +67,15 @@ 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(path_hash_ops,
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free, char, path_hash_func, path_compare);
char, path_hash_func, path_compare, free); DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
DEFINE_HASH_OPS_FULL(path_hash_ops_free_free, path_hash_ops_free,
char, path_hash_func, path_compare, free, char, path_hash_func, path_compare, free);
void, free); DEFINE_HASH_OPS_FULL(
path_hash_ops_free_free,
char, path_hash_func, path_compare, free,
void, free);
void trivial_hash_func(const void *p, struct siphash *state) { void trivial_hash_func(const void *p, struct siphash *state) {
siphash24_compress_typesafe(p, state); siphash24_compress_typesafe(p, state);
@@ -74,23 +85,19 @@ int trivial_compare_func(const void *a, const void *b) {
return CMP(a, b); return CMP(a, b);
} }
const struct hash_ops trivial_hash_ops = { DEFINE_HASH_OPS(trivial_hash_ops,
.hash = trivial_hash_func, void, trivial_hash_func, trivial_compare_func);
.compare = trivial_compare_func, DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
}; trivial_hash_ops_free,
void, trivial_hash_func, trivial_compare_func, free);
const struct hash_ops trivial_hash_ops_free = { DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
.hash = trivial_hash_func, trivial_hash_ops_value_free,
.compare = trivial_compare_func, void, trivial_hash_func, trivial_compare_func,
.free_key = free, void, free);
}; DEFINE_HASH_OPS_FULL(
trivial_hash_ops_free_free,
const struct hash_ops trivial_hash_ops_free_free = { void, trivial_hash_func, trivial_compare_func, free,
.hash = trivial_hash_func, void, free);
.compare = trivial_compare_func,
.free_key = free,
.free_value = free,
};
void uint64_hash_func(const uint64_t *p, struct siphash *state) { void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress_typesafe(*p, 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); 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 #if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state) { void devt_hash_func(const dev_t *p, struct siphash *state) {

View File

@@ -77,6 +77,7 @@ void string_hash_func(const char *p, struct siphash *state);
#define string_compare_func strcmp #define string_compare_func strcmp
extern const struct hash_ops string_hash_ops; 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_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_free;
extern const struct hash_ops string_hash_ops_free_strv_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_; 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;
extern const struct hash_ops trivial_hash_ops_free; 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; 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 /* 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); void uint64_hash_func(const uint64_t *p, struct siphash *state);
int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_; 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;
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 /* 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! */ * 64-bit archs. Yuck! */

View File

@@ -12,6 +12,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "fileio.h" #include "fileio.h"
#include "hashmap.h" #include "hashmap.h"
#include "log.h"
#include "logarithm.h" #include "logarithm.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h" #include "memory-util.h"
@@ -912,24 +913,20 @@ static void hashmap_free_no_clear(HashmapBase *h) {
free(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) { if (h) {
_hashmap_clear(h, default_free_key, default_free_value); _hashmap_clear(h);
hashmap_free_no_clear(h); hashmap_free_no_clear(h);
} }
return NULL; return NULL;
} }
void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) { void _hashmap_clear(HashmapBase *h) {
free_func_t free_key, free_value;
if (!h) if (!h)
return; return;
free_key = h->hash_ops->free_key ?: default_free_key; if (h->hash_ops->free_key || h->hash_ops->free_value) {
free_value = h->hash_ops->free_value ?: default_free_value;
if (free_key || free_value) {
/* If destructor calls are defined, let's destroy things defensively: let's take the item out of the /* 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 * 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); v = _hashmap_first_key_and_value(h, true, &k);
if (free_key) if (h->hash_ops->free_key)
free_key(k); h->hash_ops->free_key(k);
if (free_value) if (h->hash_ops->free_value)
free_value(v); h->hash_ops->free_value(v);
} }
} }
@@ -1780,7 +1777,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) {
} }
if (r < 0) if (r < 0)
return _hashmap_free(copy, NULL, NULL); return _hashmap_free(copy);
return copy; return copy;
} }
@@ -1805,6 +1802,23 @@ char** _hashmap_get_strv(HashmapBase *h) {
return sv; 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) { void* ordered_hashmap_next(OrderedHashmap *h, const void *key) {
struct ordered_hashmap_entry *e; struct ordered_hashmap_entry *e;
unsigned hash, idx; unsigned hash, idx;

View File

@@ -88,36 +88,17 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DE
#define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) #define hashmap_new(ops) _hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define hashmap_free_and_replace(a, b) \ #define hashmap_free_and_replace(a, b) \
free_and_replace_full(a, b, hashmap_free) 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) { 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) { static inline OrderedHashmap* ordered_hashmap_free(OrderedHashmap *h) {
return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL); return (void*) _hashmap_free(HASHMAP_BASE(h));
}
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);
} }
IteratedCache* iterated_cache_free(IteratedCache *cache); 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); 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) { 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) { static inline void ordered_hashmap_clear(OrderedHashmap *h) {
_hashmap_clear(HASHMAP_BASE(h), NULL, NULL); _hashmap_clear(HASHMAP_BASE(h));
}
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);
} }
/* /*
@@ -371,27 +331,6 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
return _hashmap_first_key(HASHMAP_BASE(h), false); 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 */ /* no hashmap_next */
void* ordered_hashmap_next(OrderedHashmap *h, const void *key); 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)) _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);
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);
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_ _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_ _cleanup_(ordered_hashmap_freep)
#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free); DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free);

View File

@@ -17,8 +17,8 @@ int undecchar(char c) _const_;
char hexchar(int x) _const_; char hexchar(int x) _const_;
int unhexchar(char c) _const_; int unhexchar(char c) _const_;
char* hexmem(const void *p, size_t l); 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); 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) { 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); 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_; char urlsafe_base64char(int x) _const_;
int unbase64char(char c) _const_; int unbase64char(char c) _const_;
char* base32hexmem(const void *p, size_t l, bool padding); 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); 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) { static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
return base64mem_full(p, l, SIZE_MAX, ret); return base64mem_full(p, l, SIZE_MAX, ret);
} }
@@ -45,9 +45,9 @@ ssize_t base64_append(
size_t l, size_t l,
size_t margin, size_t margin,
size_t width); 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) { 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); 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);

View File

@@ -10,17 +10,21 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "env-file.h" #include "env-file.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "log.h"
#include "os-util.h" #include "os-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
char* get_default_hostname(void) { char* get_default_hostname_raw(void) {
int r; int r;
/* Returns the default hostname, and leaves any ??? in place. */
const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME"); const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
if (e) { if (e) {
if (hostname_is_valid(e, 0)) if (hostname_is_valid(e, VALID_HOSTNAME_QUESTION_MARK))
return strdup(e); return strdup(e);
log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e); log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e);
} }
@@ -29,49 +33,15 @@ char* get_default_hostname(void) {
if (r < 0) if (r < 0)
log_debug_errno(r, "Failed to parse os-release, ignoring: %m"); log_debug_errno(r, "Failed to parse os-release, ignoring: %m");
else if (f) { else if (f) {
if (hostname_is_valid(f, 0)) if (hostname_is_valid(f, VALID_HOSTNAME_QUESTION_MARK))
return TAKE_PTR(f); return TAKE_PTR(f);
log_debug("Invalid hostname in os-release, ignoring: %s", f); log_debug("Invalid hostname in os-release, ignoring: %s", f);
} }
return strdup(FALLBACK_HOSTNAME); 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) { bool valid_ldh_char(char c) {
/* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */ /* "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; hyphen = true;
} else { } else {
if (!valid_ldh_char(*p)) if (!valid_ldh_char(*p) && (*p != '?' || !FLAGS_SET(flags, VALID_HOSTNAME_QUESTION_MARK)))
return false; return false;
dot = false; dot = false;
@@ -158,7 +128,7 @@ char* hostname_cleanup(char *s) {
dot = false; dot = false;
hyphen = true; hyphen = true;
} else if (valid_ldh_char(*p)) { } else if (valid_ldh_char(*p) || *p == '?') {
*(d++) = *p; *(d++) = *p;
dot = false; dot = false;
hyphen = false; hyphen = false;

View File

@@ -7,42 +7,14 @@
#include "macro.h" #include "macro.h"
#include "strv.h" #include "strv.h"
typedef enum GetHostnameFlags { char* get_default_hostname_raw(void);
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);
bool valid_ldh_char(char c) _const_; bool valid_ldh_char(char c) _const_;
typedef enum ValidHostnameFlags { typedef enum ValidHostnameFlags {
VALID_HOSTNAME_TRAILING_DOT = 1 << 0, /* Accept trailing dot on multi-label names */ 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_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; } ValidHostnameFlags;
bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_; bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_;

View File

@@ -13,6 +13,7 @@
#include "in-addr-util.h" #include "in-addr-util.h"
#include "logarithm.h" #include "logarithm.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "random-util.h" #include "random-util.h"
#include "stdio-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) { bool in6_addr_is_null(const struct in6_addr *a) {
assert(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) { 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) { bool in6_addr_is_link_local(const struct in6_addr *a) {
assert(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) { 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) { bool in6_addr_is_multicast(const struct in6_addr *a) {
assert(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) { 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); !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) { int in_addr_is_localhost(int family, const union in_addr_union *u) {
assert(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); return in4_addr_is_localhost(&u->in);
if (family == AF_INET6) if (family == AF_INET6)
return IN6_IS_ADDR_LOOPBACK(&u->in6); return in6_addr_is_loopback(&u->in6);
return -EAFNOSUPPORT; 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); return be32toh(u->in.s_addr) == UINT32_C(0x7F000001);
if (family == AF_INET6) if (family == AF_INET6)
return IN6_IS_ADDR_LOOPBACK(&u->in6); return in6_addr_is_loopback(&u->in6);
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
} }
@@ -178,7 +183,7 @@ bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b) {
assert(a); assert(a);
assert(b); 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) { 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; *ret_prefixlen = k;
return 0; return 0;
} }
void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) { void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {

View 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;

View 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;

View File

@@ -86,6 +86,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
return n; return n;
assert((size_t) k <= nbytes); assert((size_t) k <= nbytes);
assert(k <= SSIZE_MAX - n);
p += k; p += k;
nbytes -= k; nbytes -= k;
@@ -188,7 +189,7 @@ int pipe_eof(int fd) {
return !!(r & POLLHUP); 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; int r;
assert(fds || nfds == 0); 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. ⚠️ ⚠️ ⚠️ * to handle signals, such as signalfd() or signal handlers. ⚠️ ⚠️ ⚠️
*/ */
if (nfds == 0) if (nfds == 0 && timeout == 0)
return 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) if (r < 0)
return -errno; return -errno;
if (r == 0) if (r == 0)

View File

@@ -15,14 +15,18 @@ int flush_fd(int fd);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); 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_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) { static inline int loop_write(int fd, const void *buf, size_t nbytes) {
return loop_write_full(fd, buf, nbytes, 0); return loop_write_full(fd, buf, nbytes, 0);
} }
int pipe_eof(int fd); 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); 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); 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 true;
return FILE_SIZE_VALID(l); return FILE_SIZE_VALID(l);
} }

View File

@@ -12,9 +12,9 @@
extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */ 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 */ 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) { static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
assert(iovec); 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(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); 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) { 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); 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); assert(ret);
if (!iovec_is_set(source)) if (!iovec_is_set(source))

View File

@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include "macro.h"
/* The head of the linked list. Use this in the structure that shall /* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */ * contain the head of the linked list */
#define LIST_HEAD(t,name) \ #define LIST_HEAD(t,name) \
@@ -203,7 +205,3 @@
free_func(elem); \ free_func(elem); \
head; \ 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"

View File

@@ -17,15 +17,16 @@
#include "fileio.h" #include "fileio.h"
#include "hashmap.h" #include "hashmap.h"
#include "locale-util.h" #include "locale-util.h"
#include "missing_syscall.h" #include "log.h"
#include "path-util.h" #include "path-util.h"
#include "process-util.h"
#include "set.h" #include "set.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "utf8.h" #include "utf8.h"
static char *normalize_locale(const char *name) { static char* normalize_locale(const char *name) {
const char *e; const char *e;
/* Locale names are weird: glibc has some magic rules when looking for the charset name on disk: it /* 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; 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; 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) if (fd < 0)
return errno == ENOENT ? 0 : -errno; return errno == ENOENT ? 0 : -errno;
struct stat st;
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
return -errno; return -errno;
@@ -117,11 +115,12 @@ static int add_locales_from_archive(Set *locales) {
if (file_offset_beyond_memory_size(st.st_size)) if (file_offset_beyond_memory_size(st.st_size))
return -EFBIG; 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) if (p == MAP_FAILED)
return -errno; return -errno;
h = (const struct locarhead *) p; const struct namehashent *e;
const struct locarhead *h = p;
if (h->magic != 0xde020109 || if (h->magic != 0xde020109 ||
h->namehash_offset + h->namehash_size > st.st_size || h->namehash_offset + h->namehash_size > st.st_size ||
h->string_offset + h->string_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; r = 0;
finish: finish:
if (p != MAP_FAILED) if (p != MAP_FAILED)
munmap((void*) p, sz); munmap((void*) p, st.st_size);
return r; return r;
} }
@@ -165,6 +164,8 @@ static int add_locales_from_libdir(Set *locales) {
_cleanup_closedir_ DIR *dir = NULL; _cleanup_closedir_ DIR *dir = NULL;
int r; int r;
assert(locales);
dir = opendir("/usr/lib/locale"); dir = opendir("/usr/lib/locale");
if (!dir) if (!dir)
return errno == ENOENT ? 0 : -errno; return errno == ENOENT ? 0 : -errno;
@@ -180,7 +181,7 @@ static int add_locales_from_libdir(Set *locales) {
return -ENOMEM; return -ENOMEM;
r = set_consume(locales, z); r = set_consume(locales, z);
if (r < 0 && r != -EEXIST) if (r < 0)
return r; return r;
} }
@@ -188,11 +189,10 @@ static int add_locales_from_libdir(Set *locales) {
} }
int get_locales(char ***ret) { int get_locales(char ***ret) {
_cleanup_set_free_free_ Set *locales = NULL; _cleanup_set_free_ Set *locales = NULL;
_cleanup_strv_free_ char **l = NULL;
int r; int r;
locales = set_new(&string_hash_ops); locales = set_new(&string_hash_ops_free);
if (!locales) if (!locales)
return -ENOMEM; return -ENOMEM;
@@ -213,31 +213,25 @@ int get_locales(char ***ret) {
free(set_remove(locales, locale)); free(set_remove(locales, locale));
} }
l = set_get_strv(locales); _cleanup_strv_free_ char **l = set_to_strv(&locales);
if (!l) if (!l)
return -ENOMEM; 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"); r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
if (IN_SET(r, -ENXIO, 0)) { if (r <= 0) {
char **a, **b; 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 */ /* Filter out non-UTF-8 locales, because it's 2019, by default */
for (a = b = l; *a; a++) { char **b = l;
STRV_FOREACH(a, l)
if (endswith(*a, "UTF-8") || if (endswith(*a, "UTF-8") || strstr(*a, ".UTF-8@"))
strstr(*a, ".UTF-8@"))
*(b++) = *a; *(b++) = *a;
else else
free(*a); free(*a);
}
*b = NULL; *b = NULL;
}
} else if (r < 0)
log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean");
strv_sort(l); strv_sort(l);
@@ -283,64 +277,48 @@ int locale_is_installed(const char *name) {
return true; return true;
} }
bool is_locale_utf8(void) { static bool is_locale_utf8_impl(void) {
static int cached_answer = -1;
const char *set; const char *set;
int r; int r;
/* Note that we default to 'true' here, since today UTF8 is /* Note that we default to 'true' here, since today UTF8 is pretty much supported everywhere. */
* pretty much supported everywhere. */
if (cached_answer >= 0)
goto out;
r = secure_getenv_bool("SYSTEMD_UTF8"); r = secure_getenv_bool("SYSTEMD_UTF8");
if (r >= 0) { if (r >= 0)
cached_answer = r; return r;
goto out; if (r != -ENXIO)
} else if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m"); 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. */ /* This function may be called from libsystemd, and setlocale() is not thread safe. Assuming yes. */
if (gettid() != raw_getpid()) { if (!is_main_thread())
cached_answer = true; return true;
goto out;
}
if (!setlocale(LC_ALL, "")) { if (!setlocale(LC_ALL, ""))
cached_answer = true; return true;
goto out;
}
set = nl_langinfo(CODESET); set = nl_langinfo(CODESET);
if (!set) { if (!set || streq(set, "UTF-8"))
cached_answer = true; return true;
goto out;
}
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); set = setlocale(LC_CTYPE, NULL);
if (!set) { if (!set)
cached_answer = true; return true;
goto out;
}
/* Check result, but ignore the result if C was set /* Unless LC_CTYPE is explicitly overridden, return true. Because here CTYPE is effectively unset
* explicitly. */ * and everything can do to UTF-8 nowadays. */
cached_answer = return STR_IN_SET(set, "C", "POSIX") &&
STR_IN_SET(set, "C", "POSIX") &&
!getenv("LC_ALL") && !getenv("LC_ALL") &&
!getenv("LC_CTYPE") && !getenv("LC_CTYPE") &&
!getenv("LANG"); !getenv("LANG");
}
out: bool is_locale_utf8(void) {
return (bool) cached_answer; static int cached = -1;
if (cached < 0)
cached = is_locale_utf8_impl();
return cached;
} }
void locale_variables_free(char *l[_VARIABLE_LC_MAX]) { void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {

View File

@@ -5,6 +5,8 @@
/* Include here so consumers have LOCK_{EX,SH,NB} available. */ /* Include here so consumers have LOCK_{EX,SH,NB} available. */
#include <sys/file.h> #include <sys/file.h>
#include "time-util.h"
typedef struct LockFile { typedef struct LockFile {
int dir_fd; int dir_fd;
char *path; char *path;

View File

@@ -7,10 +7,7 @@
#include <string.h> #include <string.h>
#include <syslog.h> #include <syslog.h>
#include "list.h"
#include "macro.h" #include "macro.h"
#include "ratelimit.h"
#include "stdio-util.h"
/* Some structures we reference but don't want to pull in headers for */ /* Some structures we reference but don't want to pull in headers for */
struct iovec; 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 * environment should not be called from library code — this is always a job
* for the application itself. */ * 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); bool stderr_is_journal(void);
int log_open(void); int log_open(void);
void log_close(void); void log_close(void);
@@ -153,7 +147,7 @@ int log_struct_internal(
const char *file, const char *file,
int line, int line,
const char *func, const char *func,
const char *format, ...) _printf_(6,0) _sentinel_; const char *format, ...) _sentinel_;
int log_oom_internal( int log_oom_internal(
int level, int level,
@@ -188,24 +182,6 @@ int log_dump_internal(
const char *func, const char *func,
char *buffer); 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) \ #define log_dispatch(level, error, buffer) \
log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, 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 /* 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 :) */ * message. The variable will never be set to true, but we don't tell the compiler that :) */
extern bool _log_message_dummy; 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 #else
# define LOG_ITEM(fmt, ...) fmt, ##__VA_ARGS__
# define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ # define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
#endif #endif
#define LOG_MESSAGE_ID(id) LOG_ITEM("MESSAGE_ID=" id)
void log_received_signal(int level, const struct signalfd_siginfo *si); 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 */ /* 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 */ * stderr, the console or kmsg */
void log_set_prohibit_ipc(bool b); 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_dup_console(void);
int log_syntax_internal( int log_syntax_internal(
@@ -399,58 +376,6 @@ int log_syntax_parse_error_internal(
void log_setup(void); 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); const char* _log_set_prefix(const char *prefix, bool force);
static inline const char* _log_unset_prefixp(const char **p) { static inline const char* _log_unset_prefixp(const char **p) {
assert(p); assert(p);
@@ -460,112 +385,3 @@ static inline const char* _log_unset_prefixp(const char **p) {
#define LOG_SET_PREFIX(prefix) \ #define LOG_SET_PREFIX(prefix) \
_cleanup_(_log_unset_prefixp) _unused_ const char *CONCATENATE(_cleanup_log_unset_prefix_, UNIQ) = _log_set_prefix(prefix, false); _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))

View File

@@ -103,76 +103,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
(type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \ (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) \ #define return_with_errno(r, err) \
do { \ do { \
errno = abs(err); \ errno = abs(err); \
@@ -251,59 +181,6 @@ static inline int __coverity_check_and_return__(int condition) {
/* Pointers range from NULL to POINTER_MAX */ /* Pointers range from NULL to POINTER_MAX */
#define POINTER_MAX ((void*) UINTPTR_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 /* 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 * 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 * 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); 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 /* 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: * 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_; \ 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; }); \ ((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \
_current_++) _current_++)
#include "log.h"

View File

@@ -1,9 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <threads.h>
#include <unistd.h> #include <unistd.h>
#include "alloc-util.h"
#include "memory-util.h" #include "memory-util.h"
#include "missing_threads.h"
size_t page_size(void) { size_t page_size(void) {
static thread_local size_t pgsz = 0; 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; 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(mem);
assert(size != 0); assert(size != 0);
@@ -55,3 +56,14 @@ void *memdup_reverse(const void *mem, size_t size) {
return p; 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);
}

View File

@@ -7,7 +7,6 @@
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include "alloc-util.h"
#include "macro.h" #include "macro.h"
#include "memory-util-fundamental.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); return mempcpy(dst, src, n);
} }
#define mempcpy_typesafe(dst, src, n) \ #define _mempcpy_typesafe(dst, src, n, sz) \
({ \ ({ \
size_t _sz_; \ size_t sz; \
assert_se(MUL_SAFE(&_sz_, sizeof((dst)[0]), n)); \ assert_se(MUL_SAFE(&sz, sizeof((dst)[0]), n)); \
(typeof((dst)[0])*) mempcpy_safe(dst, src, _sz_); \ (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. */ /* 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) { static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
if (n == 0) 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))) #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 memeqzero(data, length) memeqbyte(0x00, data, length)
#define eqzero(x) memeqzero(x, sizeof(x)) #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); 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 */ /* 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) if (needlelen <= 0)
return (void*) haystack; 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); 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; const uint8_t *p;
p = memmem_safe(haystack, haystacklen, needle, needlelen); 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; return (uint8_t*) p + needlelen;
} }
static inline void* erase_and_free(void *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);
}
static inline void erase_and_freep(void *p) { static inline void erase_and_freep(void *p) {
erase_and_free(*(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 */ /* 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);

View File

@@ -4,6 +4,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "format-util.h" #include "format-util.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h" #include "memory-util.h"
#include "mempool.h" #include "mempool.h"

View File

@@ -3,74 +3,16 @@
#include <fcntl.h> #include <fcntl.h>
#ifndef F_LINUX_SPECIFIC_BASE /* This is defined since glibc-2.41. */
#define F_LINUX_SPECIFIC_BASE 1024
#endif
#ifndef F_DUPFD_QUERY #ifndef F_DUPFD_QUERY
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3) #define F_DUPFD_QUERY 1027
#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 */
#endif #endif
/* This is defined since glibc-2.39. */
#ifndef F_SEAL_EXEC #ifndef F_SEAL_EXEC
#define F_SEAL_EXEC 0x0020 /* prevent chmod modifying exec bits */ #define F_SEAL_EXEC 0x0020 /* prevent chmod modifying exec bits */
#endif #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 /* 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 * 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. * it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves.
@@ -97,6 +39,13 @@
#endif #endif
#endif #endif
/* This is defined since glibc-2.39. */
#ifndef AT_HANDLE_FID #ifndef AT_HANDLE_FID
#define AT_HANDLE_FID AT_REMOVEDIR #define AT_HANDLE_FID AT_REMOVEDIR
#endif #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)

View 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

View File

@@ -1,26 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include <sys/random.h>
#include "macro.h" #include "macro.h"
#if HAVE_GETRANDOM /* Defined since glibc-2.32. */
# 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
#ifndef GRND_INSECURE #ifndef GRND_INSECURE
# define GRND_INSECURE 0x0004 # define GRND_INSECURE 0x0004
#else #else

View File

@@ -3,75 +3,36 @@
#include <sys/socket.h> #include <sys/socket.h>
#ifndef AF_VSOCK /* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89) */
#define AF_VSOCK 40
#endif
#ifndef SO_REUSEPORT
#define SO_REUSEPORT 15
#endif
#ifndef SO_PEERGROUPS
#define SO_PEERGROUPS 59
#endif
#ifndef SO_PASSPIDFD #ifndef SO_PASSPIDFD
#define SO_PASSPIDFD 76 #define SO_PASSPIDFD 76
#endif #endif
/* Supported since kernel v6.5 (7b26952a91cf65ff1cc867a2382a8964d8c0ee7d) */
#ifndef SO_PEERPIDFD #ifndef SO_PEERPIDFD
#define SO_PEERPIDFD 77 #define SO_PEERPIDFD 77
#endif #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. */ /* Not exposed yet. Defined in include/linux/socket.h. */
#ifndef SOL_SCTP #ifndef SOL_SCTP
#define SOL_SCTP 132 #define SOL_SCTP 132
#endif #endif
/* Supported since kernel v2.6.17 (2c7946a7bf45ae86736ab3b43d0085e43947945c).
* Defined since glibc-2.39 */
#ifndef SCM_SECURITY #ifndef SCM_SECURITY
#define SCM_SECURITY 0x03 #define SCM_SECURITY 0x03
#endif #endif
/* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89).
* Defined since glibc-2.39 */
#ifndef SCM_PIDFD #ifndef SCM_PIDFD
#define SCM_PIDFD 0x04 #define SCM_PIDFD 0x04
#endif #endif
/* netinet/in.h */ /* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant defined in
#ifndef IP_FREEBIND * include/net/scm.h, but very much useful for userspace too. It's documented in unix(7) these days, hence
#define IP_FREEBIND 15 * should be fairly reliable to define here. */
#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. */
#ifndef SCM_MAX_FD #ifndef SCM_MAX_FD
#define SCM_MAX_FD 253U #define SCM_MAX_FD 253U
#endif #endif

View File

@@ -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

View File

@@ -4,12 +4,7 @@
/* Missing glibc definitions to access certain kernel APIs */ /* Missing glibc definitions to access certain kernel APIs */
#include <errno.h> #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> #include <linux/time_types.h>
#endif
#include <signal.h> #include <signal.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/types.h> #include <sys/types.h>
@@ -23,24 +18,14 @@
#include "macro.h" #include "macro.h"
#include "missing_keyctl.h" #include "missing_keyctl.h"
#include "missing_sched.h" #include "missing_sched.h"
#include "missing_stat.h"
#include "missing_syscall_def.h" #include "missing_syscall_def.h"
/* linux/kcmp.h */
#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */
#define KCMP_FILE 0
#endif
/* ======================================================================= */ /* ======================================================================= */
#if !HAVE_FCHMODAT2 #if !HAVE_FCHMODAT2
/* since kernel v6.6 (78252deb023cf0879256fcfbafe37022c390762b) */
static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) { 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); return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define fchmodat2 missing_fchmodat2 # 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 #if !HAVE_KCMP
static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { 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); 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 # 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) { 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); return syscall(__NR_add_key, type, description, payload, plen, ringid);
# define add_key missing_add_key # 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) { 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); 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 #if !HAVE_BPF
union bpf_attr; union bpf_attr;
static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { 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); return (int) syscall(__NR_bpf, cmd, attr, size);
#else
errno = ENOSYS;
return -1;
#endif
} }
# define bpf missing_bpf # 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 #if !HAVE_SET_MEMPOLICY
enum { enum {
MPOL_DEFAULT, MPOL_DEFAULT,
@@ -265,14 +122,7 @@ enum {
static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask, static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask,
unsigned long maxnode) { unsigned long maxnode) {
long i; return syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
# 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;
} }
# define set_mempolicy missing_set_mempolicy # 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, static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
unsigned long maxnode, void *addr, unsigned long maxnode, void *addr,
unsigned long flags) { unsigned long flags) {
long i; return syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
# 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;
} }
# define get_mempolicy missing_get_mempolicy # 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 #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) { 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); 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 # define pidfd_send_signal missing_pidfd_send_signal
#endif #endif
/* ======================================================================= */
#if !HAVE_PIDFD_OPEN #if !HAVE_PIDFD_OPEN
/* since kernel v5.3 (7615d9e1780e26e0178c93c55b73309a5dc093d7) */
static inline int missing_pidfd_open(pid_t pid, unsigned flags) { static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
return syscall(__NR_pidfd_open, pid, 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 #if !HAVE_RT_TGSIGQUEUEINFO
static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) { 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); return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info);
# else
# error "__NR_rt_tgsigqueueinfo not defined"
# endif
} }
# define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo # 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 #if !HAVE_EXECVEAT
/* since kernel v3.19 (51f39a1f0cea1cacf8c787f652f26dfee9611874) */
static inline int missing_execveat(int dirfd, const char *pathname, static inline int missing_execveat(int dirfd, const char *pathname,
char *const argv[], char *const envp[], char *const argv[], char *const envp[],
int flags) { int flags) {
# if defined __NR_execveat && __NR_execveat >= 0
return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); 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 # define execveat missing_execveat
#endif #endif
/* ======================================================================= */ /* ======================================================================= */
#if !HAVE_CLOSE_RANGE #if !HAVE_CLOSE_RANGE
/* since kernel v5.9 (9b4feb630e8e9801603f3cab3a36369e3c1cf88d) */
static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsigned flags) { 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 /* 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 * 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. */ * 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, first_fd,
end_fd, end_fd,
flags); flags);
# else
errno = ENOSYS;
return -1;
# endif
} }
# define close_range missing_close_range # 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 #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) { 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); 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 #if !HAVE_QUOTACTL_FD
/* since kernel v5.14 (64c2c2c62f92339b176ea24403d8db16db36f9e6) */
static inline int missing_quotactl_fd(int fd, int cmd, int id, void *addr) { 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); return syscall(__NR_quotactl_fd, fd, cmd, id, addr);
#else
errno = ENOSYS;
return -1;
#endif
} }
# define quotactl_fd missing_quotactl_fd # define quotactl_fd missing_quotactl_fd
#endif #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

View File

@@ -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

View File

@@ -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

View File

@@ -5,6 +5,7 @@
#include "macro.h" #include "macro.h"
/* since glibc-2.36 */
#ifndef P_PIDFD #ifndef P_PIDFD
# define P_PIDFD 3 # define P_PIDFD 3
#else #else

View 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");
}

View 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);

View File

@@ -28,6 +28,8 @@ extern const struct namespace_info {
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag); 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 pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type);
int namespace_open_by_type(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 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_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_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); int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);

View File

@@ -22,18 +22,10 @@ static inline void ordered_set_clear(OrderedSet *s) {
return ordered_hashmap_clear((OrderedHashmap*) 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) { static inline OrderedSet* ordered_set_free(OrderedSet *s) {
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) 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) { static inline int ordered_set_contains(OrderedSet *s, const void *p) {
return ordered_hashmap_contains((OrderedHashmap*) s, 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) \ #define ORDERED_SET_FOREACH(e, s) \
_ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ)) _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);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep) #define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep)

View File

@@ -3,6 +3,7 @@
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/netfilter/nf_tables.h>
#include <net/if.h> #include <net/if.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -12,6 +13,7 @@
#include "errno-list.h" #include "errno-list.h"
#include "extract-word.h" #include "extract-word.h"
#include "locale-util.h" #include "locale-util.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "missing_network.h" #include "missing_network.h"
#include "parse-util.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; 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; unsigned base = 0;
char *x = NULL; char *x = NULL;
long long l; 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 /* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
* https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */ * https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
bool nft_identifier_valid(const char *id) { bool nft_identifier_valid(const char *id) {
if (!id) if (isempty(id))
return false; return false;
size_t len = strlen(id); if (strlen(id) >= NFT_NAME_MAXLEN)
if (len == 0 || len > 31)
return false; return false;
if (!ascii_isalpha(id[0])) if (!ascii_isalpha(id[0]))
return false; return false;
for (size_t i = 1; i < len; i++) return in_charset(id + 1, ALPHANUMERICAL "/\\_.");
if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
return false;
return true;
} }

View File

@@ -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_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret);
int safe_atoi(const char *s, int *ret_i); 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); 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) { static inline int safe_atoi64(const char *s, int64_t *ret_i) {
assert_cc(sizeof(int64_t) == sizeof(long long int)); assert_cc(sizeof(int64_t) == sizeof(long long));
return safe_atolli(s, (long long int*) ret_i); return safe_atolli(s, (long long*) ret_i);
} }
static inline int safe_atoux64(const char *s, uint64_t *ret) { 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)); assert_cc(sizeof(unsigned long) == sizeof(unsigned));
return safe_atou_full(s, base, (unsigned*) ret_u); return safe_atou_full(s, base, (unsigned*) ret_u);
} }
static inline int safe_atoli(const char *s, long int *ret_u) { static inline int safe_atoli(const char *s, long *ret_u) {
assert_cc(sizeof(long int) == sizeof(int)); assert_cc(sizeof(long) == sizeof(int));
return safe_atoi(s, (int*) ret_u); return safe_atoi(s, (int*) ret_u);
} }
#else #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)); assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
return safe_atollu_full(s, base, (unsigned long long*) ret_u); return safe_atollu_full(s, base, (unsigned long long*) ret_u);
} }
static inline int safe_atoli(const char *s, long int *ret_u) { static inline int safe_atoli(const char *s, long *ret_u) {
assert_cc(sizeof(long int) == sizeof(long long int)); assert_cc(sizeof(long) == sizeof(long long));
return safe_atolli(s, (long long int*) ret_u); return safe_atolli(s, (long long*) ret_u);
} }
#endif #endif

View File

@@ -613,37 +613,9 @@ char* path_extend_internal(char **x, ...) {
return nx; 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; _cleanup_close_ int fd = -EBADF;
int r; _cleanup_free_ char *resolved = NULL;
/* 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;
int r; int r;
assert(name); 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. */ * needed to avoid unforeseen regression or other complicated changes. */
if (root) { if (root) {
/* prefix root to name in case full paths are not specified */ /* 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) if (r < 0)
return r; 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) if (r < 0)
return r; return r;
if (ret_filename) { r = access_fd(fd, X_OK);
r = path_make_absolute_cwd(name, ret_filename); if (r == -ENOSYS)
if (r < 0) /* /proc/ is not mounted. Fall back to access(). */
return r; 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) if (ret_fd)
@@ -692,7 +681,7 @@ int find_executable_full(
assert(name); assert(name);
if (is_path(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) { if (exec_search_path) {
STRV_FOREACH(element, exec_search_path) { STRV_FOREACH(element, exec_search_path) {
@@ -707,7 +696,7 @@ int find_executable_full(
if (!full_path) if (!full_path)
return -ENOMEM; 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) if (r >= 0)
return 0; return 0;
if (r != -EACCES) if (r != -EACCES)
@@ -743,7 +732,7 @@ int find_executable_full(
if (!path_extend(&element, name)) if (!path_extend(&element, name))
return -ENOMEM; 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! */ if (r >= 0) /* Found it! */
return 0; return 0;
/* PATH entries which we don't have access to are ignored, as per tradition. */ /* PATH entries which we don't have access to are ignored, as per tradition. */

View File

@@ -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(char **l, const char *root);
char** path_strv_resolve_uniq(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( int find_executable_full(
const char *name, const char *name,
const char *root, const char *root,

View File

@@ -1,15 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <threads.h>
#include <unistd.h> #include <unistd.h>
#include "errno-util.h" #include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h" #include "memory-util.h"
#include "missing_fs.h"
#include "missing_magic.h" #include "missing_magic.h"
#include "missing_threads.h" #include "mountpoint-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
#include "pidfd-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) { static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL; _cleanup_free_ char *p = NULL;
int r; int r;
assert(fd >= 0); assert(fd >= 0);
xsprintf(path, "/proc/self/fdinfo/%i", fd); 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) if (r == -ENOENT)
return proc_fd_enoent_errno(); return -EBADF;
if (r == -ENODATA) /* not a pidfd? */
return -ENOTTY;
if (r < 0) if (r < 0)
return r; 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")) if (streq(p, "0"))
return -EREMOTE; /* PID is in foreign PID namespace? */ return -EREMOTE; /* PID is in foreign PID namespace? */
if (streq(p, "-1")) 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) { int pidfd_get_inode_id(int fd, uint64_t *ret) {
static bool file_handle_supported = true;
int r; int r;
assert(fd >= 0); assert(fd >= 0);
@@ -236,6 +235,30 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
if (r == 0) if (r == 0)
return -EOPNOTSUPP; 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; struct stat st;
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
return -errno; return -errno;
@@ -243,6 +266,16 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
if (ret) if (ret)
*ret = (uint64_t) st.st_ino; *ret = (uint64_t) st.st_ino;
return 0; 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) { int pidfd_get_inode_id_self_cached(uint64_t *ret) {

View File

@@ -1,10 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
typedef struct PidRef PidRef;
#include "macro.h" #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 /* 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 * 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 * process. Moreover, most operations will fail with -EREMOTE. Only PidRef structures that are not marked
* *unset* can be marked *remote*. * *unset* can be marked *remote*.
*/ */
struct PidRef { typedef struct PidRef {
pid_t pid; /* > 0 if the PidRef is set, otherwise set to PID_AUTOMATIC if automatic mode is pid_t pid; /* > 0 if the PidRef is set, otherwise set to INT_MIN (PID_AUTOMATIC) if automatic
* desired, or 0 otherwise. */ * 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 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 * 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. */ * 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 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 */ their own pidfs and each process comes with a unique inode number */
}; } PidRef;
#define PIDREF_NULL (PidRef) { .fd = -EBADF } #define PIDREF_NULL (PidRef) { .fd = -EBADF }
/* A special pidref value that we are using when a PID shall be automatically acquired from some surrounding /* 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 * context, for example connection peer. Much like PIDREF_NULL it will be considered unset by
* pidref_is_set().*/ * pidref_is_set(). */
#define PIDREF_AUTOMATIC (const PidRef) { .pid = PID_AUTOMATIC, .fd = -EBADF } #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 /* 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) */ * 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_pid(PidRef *pidref, pid_t pid);
int pidref_set_pidstr(PidRef *pidref, const char *pid); int pidref_set_pidstr(PidRef *pidref, const char *pid);
int pidref_set_pidfd(PidRef *pidref, int fd); 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_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
int pidref_set_parent(PidRef *ret); int pidref_set_parent(PidRef *ret);
static inline int pidref_set_self(PidRef *pidref) { 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) #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;
extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */ extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */

View File

@@ -46,6 +46,11 @@ Prioq* prioq_free(Prioq *q) {
if (!q) if (!q)
return NULL; 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); free(q->items);
return mfree(q); return mfree(q);
} }
@@ -178,6 +183,11 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
assert(q); assert(q);
assert(i); 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; l = q->items + q->n_items - 1;
if (i == l) 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 /* Not last entry, let's replace the last entry with
* this one, and reshuffle */ * this one, and reshuffle */
assert(i >= q->items);
k = i - q->items; k = i - q->items;
i->data = l->data; i->data = l->data;
@@ -252,6 +263,7 @@ void prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
if (!i) if (!i)
return; return;
assert(i >= q->items);
k = i - q->items; k = i - q->items;
k = shuffle_down(q, k); k = shuffle_down(q, k);
shuffle_up(q, k); shuffle_up(q, k);

View File

@@ -15,6 +15,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <syslog.h> #include <syslog.h>
#include <threads.h>
#include <unistd.h> #include <unistd.h>
#if HAVE_VALGRIND_VALGRIND_H #if HAVE_VALGRIND_VALGRIND_H
#include <valgrind/valgrind.h> #include <valgrind/valgrind.h>
@@ -36,13 +37,13 @@
#include "fs-util.h" #include "fs-util.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "io-util.h" #include "io-util.h"
#include "iovec-util.h"
#include "locale-util.h" #include "locale-util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h" #include "memory-util.h"
#include "missing_sched.h" #include "missing_sched.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h"
#include "mountpoint-util.h" #include "mountpoint-util.h"
#include "namespace-util.h" #include "namespace-util.h"
#include "nulstr-util.h" #include "nulstr-util.h"
@@ -53,6 +54,7 @@
#include "raw-clone.h" #include "raw-clone.h"
#include "rlimit-util.h" #include "rlimit-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "stdio-util.h" #include "stdio-util.h"
#include "string-table.h" #include "string-table.h"
@@ -505,48 +507,10 @@ int get_process_exe(pid_t pid, char **ret) {
return 0; return 0;
} }
static int get_process_id(pid_t pid, const char *field, uid_t *ret) { int pid_get_uid(pid_t pid, uid_t *ret) {
_cleanup_fclose_ FILE *f = NULL;
const char *p;
int r; int r;
assert(field); assert(pid >= 0);
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(ret); assert(ret);
if (pid == 0 || pid == getpid_cached()) { if (pid == 0 || pid == getpid_cached()) {
@@ -554,7 +518,14 @@ int pid_get_uid(pid_t pid, uid_t *ret) {
return 0; 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) { 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 get_process_gid(pid_t pid, gid_t *ret) {
int r;
assert(pid >= 0);
assert(ret);
if (pid == 0 || pid == getpid_cached()) { if (pid == 0 || pid == getpid_cached()) {
*ret = getgid(); *ret = getgid();
return 0; return 0;
} }
assert_cc(sizeof(uid_t) == sizeof(gid_t)); _cleanup_free_ char *v = NULL;
return get_process_id(pid, "Gid:", ret); 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) { 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) { int get_process_umask(pid_t pid, mode_t *ret) {
_cleanup_free_ char *m = NULL; _cleanup_free_ char *m = NULL;
const char *p;
int r; int r;
assert(pid >= 0); assert(pid >= 0);
assert(ret); assert(ret);
p = procfs_file_alloca(pid, "status"); r = procfs_file_get_field(pid, "status", "Umask", &m);
r = get_proc_field(p, "Umask", WHITESPACE, &m);
if (r == -ENOENT) if (r == -ENOENT)
return -ESRCH; return -ESRCH;
if (r < 0) if (r < 0)
@@ -877,27 +855,8 @@ int get_process_umask(pid_t pid, mode_t *ret) {
return parse_mode(m, ret); return parse_mode(m, ret);
} }
int wait_for_terminate(pid_t pid, siginfo_t *status) { int wait_for_terminate(pid_t pid, siginfo_t *ret) {
siginfo_t dummy; return pidref_wait_for_terminate(&PIDREF_MAKE_FROM_PID(pid), ret);
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;
}
} }
/* /*
@@ -914,24 +873,29 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
* A warning is emitted if the process terminates abnormally, * A warning is emitted if the process terminates abnormally,
* and also if it returns non-zero unless check_exit_code is true. * 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; _cleanup_free_ char *buffer = NULL;
siginfo_t status;
int r, prio;
assert(pid > 1);
if (!name) { if (!name) {
r = pid_get_comm(pid, &buffer); r = pidref_get_comm(pidref, &buffer);
if (r < 0) 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 else
name = buffer; 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) if (r < 0)
return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); 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; 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: * Return values:
* *
@@ -1379,8 +1347,8 @@ void valgrind_summary_hack(void) {
if (pid < 0) if (pid < 0)
log_struct_errno( log_struct_errno(
LOG_EMERG, errno, LOG_EMERG, errno,
"MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR, LOG_MESSAGE_ID(SD_MESSAGE_VALGRIND_HELPER_FORK_STR),
LOG_MESSAGE( "Failed to fork off valgrind helper: %m")); LOG_MESSAGE("Failed to fork off valgrind helper: %m"));
else if (pid == 0) else if (pid == 0)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
else { else {
@@ -1439,7 +1407,7 @@ pid_t getpid_cached(void) {
case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
pid_t new_pid; pid_t new_pid;
new_pid = raw_getpid(); new_pid = getpid();
if (!installed) { if (!installed) {
/* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's /* __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 */ case CACHED_PID_BUSY: /* Somebody else is currently initializing */
return raw_getpid(); return getpid();
default: /* Properly initialized */ default: /* Properly initialized */
return current_value; return current_value;
@@ -1525,25 +1493,28 @@ static int fork_flags_to_signal(ForkFlags flags) {
SIGKILL; SIGKILL;
} }
int safe_fork_full( int pidref_safe_fork_full(
const char *name, const char *name,
const int stdio_fds[3], const int stdio_fds[3],
int except_fds[], int except_fds[],
size_t n_except_fds, size_t n_except_fds,
ForkFlags flags, ForkFlags flags,
pid_t *ret_pid) { PidRef *ret_pid) {
pid_t original_pid, pid; pid_t original_pid, pid;
sigset_t saved_ss, ss; sigset_t saved_ss, ss;
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; _unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
bool block_signals = false, block_all = false, intermediary = false; bool block_signals = false, block_all = false, intermediary = false;
_cleanup_close_pair_ int pidref_transport_fds[2] = EBADF_PAIR;
int prio, r; int prio, r;
assert(!FLAGS_SET(flags, FORK_WAIT|FORK_FREEZE));
assert(!FLAGS_SET(flags, FORK_DETACH) || 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 /* A wrapper around fork(), that does a couple of important initializations in addition to mere
* returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ * 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; prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
@@ -1586,14 +1557,43 @@ int safe_fork_full(
if (!r) { if (!r) {
/* Not a reaper process, hence do a double fork() so we are reparented to one */ /* 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(); pid = fork();
if (pid < 0) if (pid < 0)
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name)); return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
if (pid > 0) { if (pid > 0) {
log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid); 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 */ return 1; /* return in the parent */
} }
pidref_transport_fds[0] = safe_close(pidref_transport_fds[0]);
intermediary = true; intermediary = true;
} }
} }
@@ -1611,8 +1611,30 @@ int safe_fork_full(
if (pid > 0) { if (pid > 0) {
/* If we are in the intermediary process, exit now */ /* 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); _exit(EXIT_SUCCESS);
}
/* We are in the parent process */ /* We are in the parent process */
log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
@@ -1630,16 +1652,31 @@ int safe_fork_full(
return r; return r;
if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */ if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
return -EPROTO; 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) if (ret_pid) {
*ret_pid = 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; return 1;
} }
/* We are in the child process */ /* We are in the child process */
pidref_transport_fds[1] = safe_close(pidref_transport_fds[1]);
/* Restore signal mask manually */ /* Restore signal mask manually */
saved_ssp = NULL; saved_ssp = NULL;
@@ -1801,36 +1838,41 @@ int safe_fork_full(
if (FLAGS_SET(flags, FORK_FREEZE)) if (FLAGS_SET(flags, FORK_FREEZE))
freeze(); freeze();
if (ret_pid) if (ret_pid) {
*ret_pid = getpid_cached(); 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; return 0;
} }
int pidref_safe_fork_full( int safe_fork_full(
const char *name, const char *name,
const int stdio_fds[3], const int stdio_fds[3],
int except_fds[], int except_fds[],
size_t n_except_fds, size_t n_except_fds,
ForkFlags flags, ForkFlags flags,
PidRef *ret_pid) { pid_t *ret_pid) {
pid_t pid; _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
int r, q; 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) if (r < 0 || !ret_pid)
return r; return r;
if (r > 0 && FLAGS_SET(flags, FORK_WAIT)) { *ret_pid = pidref.pid;
/* 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);
return r; return r;
} }
@@ -2005,17 +2047,14 @@ _noreturn_ void freeze(void) {
int get_process_threads(pid_t pid) { int get_process_threads(pid_t pid) {
_cleanup_free_ char *t = NULL; _cleanup_free_ char *t = NULL;
const char *p;
int n, r; int n, r;
if (pid < 0) if (pid < 0)
return -EINVAL; return -EINVAL;
p = procfs_file_alloca(pid, "status"); r = procfs_file_get_field(pid, "status", "Threads", &t);
r = get_proc_field(p, "Threads", WHITESPACE, &t);
if (r == -ENOENT) if (r == -ENOENT)
return proc_mounted() == 0 ? -ENOSYS : -ESRCH; return -ESRCH;
if (r < 0) if (r < 0)
return r; return r;

View File

@@ -12,6 +12,8 @@
#include <sys/types.h> #include <sys/types.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "assert-util.h"
#include "fileio.h"
#include "format-util.h" #include "format-util.h"
#include "macro.h" #include "macro.h"
#include "pidref.h" #include "pidref.h"
@@ -22,16 +24,20 @@
pid_t _pid_ = (pid); \ pid_t _pid_ = (pid); \
const char *_field_ = (field); \ const char *_field_ = (field); \
char *_r_; \ char *_r_; \
if (_pid_ == 0) { \ if (_pid_ == 0) \
_r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \ _r_ = strjoina("/proc/self/", _field_); \
strcpy(stpcpy(_r_, "/proc/self/"), _field_); \ else { \
} else { \ assert(_pid_ > 0); \
_r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \ _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \
sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \ sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \
} \ } \
(const char*) _r_; \ (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 { typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0, PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
PROCESS_CMDLINE_USE_LOCALE = 1 << 1, 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 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 { typedef enum WaitFlags {
WAIT_LOG_ABNORMAL = 1 << 0, WAIT_LOG_ABNORMAL = 1 << 0,
@@ -71,7 +81,9 @@ typedef enum WaitFlags {
WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
} WaitFlags; } 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_and_check(const char *name, pid_t pid, WaitFlags flags);
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
void sigkill_wait(pid_t pid); 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_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_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_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; } 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( int pidref_safe_fork_full(
const char *name, const char *name,
const int stdio_fds[3], 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); 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( int namespace_fork(
const char *outer_name, const char *outer_name,
const char *inner_name, const char *inner_name,

View File

@@ -11,6 +11,7 @@
#include <sys/auxv.h> #include <sys/auxv.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/time.h> #include <sys/time.h>
#include <threads.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "env-util.h" #include "env-util.h"
@@ -19,9 +20,9 @@
#include "fileio.h" #include "fileio.h"
#include "io-util.h" #include "io-util.h"
#include "iovec-util.h" #include "iovec-util.h"
#include "log.h"
#include "missing_random.h" #include "missing_random.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h"
#include "parse-util.h" #include "parse-util.h"
#include "pidfd-util.h" #include "pidfd-util.h"
#include "process-util.h" #include "process-util.h"

View File

@@ -6,8 +6,10 @@
#include <stdint.h> #include <stdint.h>
#include <sys/uio.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. */ #include "macro.h"
int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */
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); int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret);
static inline uint64_t random_u64(void) { static inline uint64_t random_u64(void) {
@@ -29,6 +31,6 @@ static inline uint32_t random_u32(void) {
size_t random_pool_size(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); uint64_t random_u64_range(uint64_t max);

View File

@@ -28,3 +28,55 @@ unsigned ratelimit_num_dropped(const RateLimit *rl);
usec_t ratelimit_end(const RateLimit *rl); usec_t ratelimit_end(const RateLimit *rl);
usec_t ratelimit_left(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__)

View File

@@ -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) #define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set* set_free(Set *s) { 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)) #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); 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) { 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) { static inline void *set_steal_first(Set *s) {
return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); 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_steal_first_key */
/* no set_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)); 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); 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) #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; }); ) 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);
DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
#define _cleanup_set_free_ _cleanup_(set_freep) #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); int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret);

View File

@@ -2,11 +2,11 @@
#include <errno.h> #include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <threads.h>
#include "errno-util.h" #include "errno-util.h"
#include "macro.h" #include "macro.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "missing_threads.h"
#include "parse-util.h" #include "parse-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "stdio-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. /* 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 * earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not
* performance sensitive code. * performance sensitive code.
* *
* Note that we use kill() rather than raise() as fallback, for similar reasons. */ * 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) if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
assert_se(kill(p, sig) >= 0); assert_se(kill(p, sig) >= 0);

View File

@@ -3,6 +3,7 @@
#include <signal.h> #include <signal.h>
#include "assert-util.h"
#include "macro.h" #include "macro.h"
int reset_all_signal_handlers(void); int reset_all_signal_handlers(void);

View File

@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* 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 <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <net/if.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <poll.h> #include <poll.h>
@@ -14,7 +15,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <linux/if.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "errno-util.h" #include "errno-util.h"
@@ -28,6 +28,7 @@
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
#include "process-util.h" #include "process-util.h"
#include "random-util.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.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); 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 socket_ioctl_fd(void) {
int fd; 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) { int socket_bind_to_ifindex(int fd, int ifindex) {
char ifname[IF_NAMESIZE];
int r;
assert(fd >= 0); assert(fd >= 0);
if (ifindex <= 0) if (ifindex <= 0)
/* Drop binding */ /* Drop binding */
return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0)); return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex); return setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
if (r != -ENOPROTOOPT) }
return r;
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */ int socket_autobind(int fd, char **ret_name) {
r = format_ifname(ifindex, ifname); _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) if (r < 0)
return r; 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) { 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); 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) if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0)
return -errno; return -errno;

View File

@@ -6,12 +6,12 @@
#include <linux/if_infiniband.h> #include <linux/if_infiniband.h>
#include <linux/if_packet.h> #include <linux/if_packet.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <sys/socket.h> /* linux/vms_sockets.h requires 'struct sockaddr' */
#include <linux/vm_sockets.h> #include <linux/vm_sockets.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/un.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]; \ (size) == CMSG_ALIGN(size) ? 1 : -1]; \
} }
/* 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 macro returns the correct size that
* must be passed to kernel.
*/
#define SOCKADDR_LL_LEN(sa) \
({ \
const struct sockaddr_ll *_sa = &(sa); \
size_t _mac_len = sizeof(_sa->sll_addr); \
assert(_sa->sll_family == AF_PACKET); \
if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \
_mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \
if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \
_mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \
offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \
})
/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ size_t sockaddr_un_len(const struct sockaddr_un *sa);
#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); \
})
#define SOCKADDR_LEN(saddr) \ size_t sockaddr_len(const union sockaddr_union *sa);
({ \
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; \
})
int socket_ioctl_fd(void); 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_ifname(int fd, const char *ifname);
int socket_bind_to_ifindex(int fd, int ifindex); 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. */ /* Define a 64-bit version of timeval/timespec in any case, even on 32-bit userspace. */
struct timeval_large { struct timeval_large {
uint64_t tvl_sec, tvl_usec; uint64_t tvl_sec, tvl_usec;

View File

@@ -16,10 +16,10 @@
#include "filesystems.h" #include "filesystems.h"
#include "fs-util.h" #include "fs-util.h"
#include "hash-funcs.h" #include "hash-funcs.h"
#include "log.h"
#include "macro.h" #include "macro.h"
#include "missing_fs.h" #include "missing_fs.h"
#include "missing_magic.h" #include "missing_magic.h"
#include "missing_syscall.h"
#include "mountpoint-util.h" #include "mountpoint-util.h"
#include "nulstr-util.h" #include "nulstr-util.h"
#include "parse-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; struct dirent *de;
ssize_t n; ssize_t n;
n = getdents64(fd, buf, m); n = posix_getdents(fd, buf, m, /* flags = */ 0);
if (n < 0) if (n < 0)
return -errno; return -errno;
if (n == 0) 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; 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; _cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
int mntida = -1, mntidb = -1; 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; a->stx_ino == b->stx_ino;
} }
bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) { bool statx_mount_same(const struct statx *a, const struct statx *b) {
if (!new_statx_is_set(a) || !new_statx_is_set(b)) if (!statx_is_set(a) || !statx_is_set(b))
return false; return false;
/* if we have the mount ID, that's all we need */ /* 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; 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) { int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD); assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(ret); assert(ret);
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY); if (!isempty(path)) {
if (fd < 0) fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
return fd; 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) { void inode_hash_func(const struct stat *q, struct siphash *state) {

View File

@@ -11,7 +11,6 @@
#include "fs-util.h" #include "fs-util.h"
#include "macro.h" #include "macro.h"
#include "missing_stat.h"
#include "siphash24.h" #include "siphash24.h"
#include "time-util.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 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_inode_same(const struct statx *a, const struct statx *b);
bool statx_mount_same(const struct new_statx *a, const struct new_statx *b); bool statx_mount_same(const struct statx *a, const struct statx *b);
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx);
int xstatfsat(int dir_fd, const char *path, struct statfs *ret); 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) { 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 }); 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) { static inline bool statx_is_set(const struct statx *sx) {
return sx && sx->stx_mask != 0; 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