systemd: update code from upstream (2023-02-07)

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=9eba03c7b10fe98e0443508402b3f9804230453e

(
  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-identifier.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.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/network-internal.h"
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-util.h"
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-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-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/async.h"
nm_copy_sd_shared "src/basic/cgroup-util.h"
nm_copy_sd_shared "src/basic/constants.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-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/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/log.h"
nm_copy_sd_shared "src/basic/logarithm.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_random.h"
nm_copy_sd_shared "src/basic/missing_socket.h"
nm_copy_sd_shared "src/basic/missing_stat.h"
nm_copy_sd_shared "src/basic/missing_syscall.h"
nm_copy_sd_shared "src/basic/missing_type.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/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/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/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.h"
nm_copy_sd_shared "src/basic/utf8.c"
nm_copy_sd_shared "src/basic/utf8.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.c"
nm_copy_sd_shared "src/fundamental/sha256.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:
Thomas Haller 2022-10-24 22:35:04 +02:00
parent 5afa09e966
commit a300809f3c
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
91 changed files with 2160 additions and 1711 deletions

View file

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdint.h>
static inline uint16_t unaligned_read_ne16(const void *_u) {
const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
return u->x;
}
static inline uint32_t unaligned_read_ne32(const void *_u) {
const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
return u->x;
}
static inline uint64_t unaligned_read_ne64(const void *_u) {
const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
return u->x;
}
static inline void unaligned_write_ne16(void *_u, uint16_t a) {
struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u;
u->x = a;
}
static inline void unaligned_write_ne32(void *_u, uint32_t a) {
struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u;
u->x = a;
}
static inline void unaligned_write_ne64(void *_u, uint64_t a) {
struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u;
u->x = a;
}

View file

@ -4,6 +4,8 @@
#include <endian.h>
#include <stdint.h>
#include "unaligned-fundamental.h"
/* BE */
static inline uint16_t unaligned_read_be16(const void *_u) {
@ -79,21 +81,3 @@ static inline void unaligned_write_le64(void *_u, uint64_t a) {
u->x = le64toh(a);
}
#if __BYTE_ORDER == __BIG_ENDIAN
#define unaligned_read_ne16 unaligned_read_be16
#define unaligned_read_ne32 unaligned_read_be32
#define unaligned_read_ne64 unaligned_read_be64
#define unaligned_write_ne16 unaligned_write_be16
#define unaligned_write_ne32 unaligned_write_be32
#define unaligned_write_ne64 unaligned_write_be64
#else
#define unaligned_read_ne16 unaligned_read_le16
#define unaligned_read_ne32 unaligned_read_le32
#define unaligned_read_ne64 unaligned_read_le64
#define unaligned_write_ne16 unaligned_write_le16
#define unaligned_write_ne32 unaligned_write_le32
#define unaligned_write_ne64 unaligned_write_le64
#endif

View file

@ -1,140 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/***
Copyright © 2014 Axis Communications AB. All rights reserved.
***/
#include <arpa/inet.h>
#include <linux/filter.h>
#include <netinet/if_ether.h>
#include "arp-util.h"
#include "ether-addr-util.h"
#include "fd-util.h"
#include "in-addr-util.h"
#include "unaligned.h"
#include "util.h"
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Hardware Address must be different from our own */
BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 4), /* A == X ? */
BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
BPF_STMT(BPF_LDX + BPF_IMM, htobe32(a->s_addr)), /* X <- clients IP */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
};
struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
.filter = (struct sock_filter*) filter,
};
assert(fd >= 0);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
return -errno;
return 0;
}
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac) {
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
_cleanup_close_ int s = -1;
int r;
assert(ifindex > 0);
assert(mac);
s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (s < 0)
return -errno;
r = arp_update_filter(s, a, mac);
if (r < 0)
return r;
if (bind(s, &link.sa, sizeof(link.ll)) < 0)
return -errno;
return TAKE_FD(s);
}
int arp_send_packet(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha,
bool announce) {
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP),
.ll.sll_ifindex = ifindex,
.ll.sll_halen = ETH_ALEN,
.ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
struct ether_arp arp = {
.ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
.ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
.ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
.ea_hdr.ar_pln = sizeof(struct in_addr), /* PLEN */
.ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
};
ssize_t n;
assert(fd >= 0);
assert(ifindex > 0);
assert(pa);
assert(in4_addr_is_set(pa));
assert(ha);
assert(!ether_addr_is_null(ha));
memcpy(&arp.arp_sha, ha, ETH_ALEN);
memcpy(&arp.arp_tpa, pa, sizeof(struct in_addr));
if (announce)
memcpy(&arp.arp_spa, pa, sizeof(struct in_addr));
n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
if (n < 0)
return -errno;
if (n != sizeof(struct ether_arp))
return -EIO;
return 0;
}

View file

@ -1,36 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/***
Copyright © 2014 Axis Communications AB. All rights reserved.
***/
#include <net/ethernet.h>
#include <netinet/in.h>
#include "socket-util.h"
#include "sparse-endian.h"
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac);
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac);
int arp_send_packet(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha,
bool announce);
static inline int arp_send_probe(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, false);
}
static inline int arp_send_announcement(
int fd,
int ifindex,
const struct in_addr *pa,
const struct ether_addr *ha) {
return arp_send_packet(fd, ifindex, pa, ha, true);
}

View file

@ -4,15 +4,11 @@
#include <net/ethernet.h>
#include <net/if_arp.h>
#include "sd-device.h"
#include "sd-id128.h"
#include "dhcp-identifier.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "string-table.h"
#include "udev-util.h"
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
@ -207,48 +203,20 @@ int dhcp_identifier_set_duid(
}
int dhcp_identifier_set_iaid(
int ifindex,
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
bool use_mac,
void *ret) {
/* name is a pointer to memory in the sd_device struct, so must
* have the same scope */
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
const char *name = NULL;
uint32_t id32;
uint64_t id;
int r;
assert(ifindex > 0);
assert(hw_addr);
assert(ret);
if (udev_available() && !use_mac) {
/* udev should be around */
r = sd_device_new_from_ifindex(&device, ifindex);
if (r < 0)
return r;
r = sd_device_get_is_initialized(device);
if (r < 0)
return r;
if (r == 0)
/* not yet ready */
return -EBUSY;
r = device_is_renaming(device);
if (r < 0)
return r;
if (r > 0)
/* device is under renaming */
return -EBUSY;
name = net_get_persistent_name(device);
}
if (dev)
name = net_get_persistent_name(dev);
if (name)
id = siphash24(name, strlen(name), HASH_KEY.bytes);
else

View file

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-device.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
@ -66,10 +67,9 @@ int dhcp_identifier_set_duid(
struct duid *ret_duid,
size_t *ret_len);
int dhcp_identifier_set_iaid(
int ifindex,
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
bool use_mac,
void *ret);
const char *duid_type_to_string(DUIDType t) _const_;

View file

@ -1,90 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/***
Copyright © 2013 Intel Corporation. All rights reserved.
***/
#include "sd-dhcp-client.h"
#include "dhcp-internal.h"
#include "dhcp-protocol.h"
#include "list.h"
#include "util.h"
struct sd_dhcp_route {
struct in_addr dst_addr;
struct in_addr gw_addr;
unsigned char dst_prefixlen;
};
struct sd_dhcp_raw_option {
LIST_FIELDS(struct sd_dhcp_raw_option, options);
uint8_t tag;
uint8_t length;
void *data;
};
struct sd_dhcp_lease {
unsigned n_ref;
/* each 0 if unset */
uint32_t t1;
uint32_t t2;
uint32_t lifetime;
/* each 0 if unset */
be32_t address;
be32_t server_address;
be32_t next_server;
bool have_subnet_mask;
be32_t subnet_mask;
bool have_broadcast;
be32_t broadcast;
struct in_addr *router;
size_t router_size;
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
struct sd_dhcp_route *static_routes;
size_t n_static_routes;
struct sd_dhcp_route *classless_routes;
size_t n_classless_routes;
uint16_t mtu; /* 0 if unset */
char *domainname;
char **search_domains;
char *hostname;
char *root_path;
void *client_id;
size_t client_id_len;
void *vendor_specific;
size_t vendor_specific_len;
char *timezone;
uint8_t sixrd_ipv4masklen;
uint8_t sixrd_prefixlen;
struct in6_addr sixrd_prefix;
struct in_addr *sixrd_br_addresses;
size_t sixrd_n_br_addresses;
LIST_HEAD(struct sd_dhcp_raw_option, private_options);
};
int dhcp_lease_new(sd_dhcp_lease **ret);
int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata);
int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains);
int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len);
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);

View file

@ -48,6 +48,8 @@ struct sd_dhcp6_client {
int event_priority;
int fd;
sd_device *dev;
DHCP6State state;
bool information_request;
usec_t information_request_time_usec;
@ -77,8 +79,9 @@ struct sd_dhcp6_client {
sd_dhcp6_client_callback_t callback;
void *userdata;
bool send_release;
/* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
/* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
bool test_mode;
};

View file

@ -23,7 +23,7 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
.in6.sin6_scope_id = ifindex,
};
_cleanup_close_ int s = -1;
_cleanup_close_ int s = -EBADF;
int r;
assert(ifindex > 0);

View file

@ -495,13 +495,18 @@ int dhcp6_option_parse(
}
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
DHCP6Status status;
assert(data || data_len == 0);
if (data_len < sizeof(uint16_t))
return -EBADMSG;
status = unaligned_read_be16(data);
if (ret_status_message) {
char *msg;
_cleanup_free_ char *msg = NULL;
const char *s;
/* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
* Let's escape unsafe characters for safety. */
@ -509,10 +514,14 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
if (!msg)
return -ENOMEM;
*ret_status_message = msg;
s = dhcp6_message_status_to_string(status);
if (s && !strextend_with_separator(&msg, ": ", s))
return -ENOMEM;
*ret_status_message = TAKE_PTR(msg);
}
return unaligned_read_be16(data);
return status;
}
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
@ -538,9 +547,8 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
return r;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address or PD prefix option with non-zero status: %s%s%s",
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
"Received an IA address or PD prefix option with non-zero status%s%s",
isempty(msg) ? "." : ": ", strempty(msg));
if (r < 0)
/* Let's log but ignore the invalid status option. */
log_dhcp6_client_errno(client, r,
@ -746,9 +754,8 @@ int dhcp6_option_parse_ia(
return r;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with non-zero status: %s%s%s",
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
"Received an IA option with non-zero status%s%s",
isempty(msg) ? "." : ": ", strempty(msg));
if (r < 0)
log_dhcp6_client_errno(client, r,
"Received an IA option with an invalid status sub option, ignoring: %m");

View file

@ -11,6 +11,7 @@ static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = {
[DHCP6_STATE_BOUND] = "bound",
[DHCP6_STATE_RENEW] = "renew",
[DHCP6_STATE_REBIND] = "rebind",
[DHCP6_STATE_STOPPING] = "stopping",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State);

View file

@ -58,6 +58,7 @@ typedef enum DHCP6State {
DHCP6_STATE_BOUND,
DHCP6_STATE_RENEW,
DHCP6_STATE_REBIND,
DHCP6_STATE_STOPPING,
_DHCP6_STATE_MAX,
_DHCP6_STATE_INVALID = -EINVAL,
} DHCP6State;

View file

@ -11,6 +11,7 @@
#include "sd-dhcp6-client.h"
#include "alloc-util.h"
#include "device-util.h"
#include "dhcp-identifier.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
@ -299,9 +300,8 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
if (client->iaid_set)
return 0;
r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
/* legacy_unstable_byteorder = */ true,
/* use_mac = */ client->test_mode,
&iaid);
if (r < 0)
return r;
@ -498,6 +498,14 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
return 0;
}
int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
client->send_release = enable;
return 0;
}
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
@ -586,7 +594,8 @@ static int client_append_common_options_in_managed_mode(
DHCP6_STATE_SOLICITATION,
DHCP6_STATE_REQUEST,
DHCP6_STATE_RENEW,
DHCP6_STATE_REBIND));
DHCP6_STATE_REBIND,
DHCP6_STATE_STOPPING));
assert(buf);
assert(*buf);
assert(offset);
@ -603,9 +612,11 @@ static int client_append_common_options_in_managed_mode(
return r;
}
r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
if (r < 0)
return r;
if (client->state != DHCP6_STATE_STOPPING) {
r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
if (r < 0)
return r;
}
r = dhcp6_option_append_user_class(buf, offset, client->user_class);
if (r < 0)
@ -636,6 +647,8 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client)
return DHCP6_MESSAGE_RENEW;
case DHCP6_STATE_REBIND:
return DHCP6_MESSAGE_REBIND;
case DHCP6_STATE_STOPPING:
return DHCP6_MESSAGE_RELEASE;
default:
assert_not_reached();
}
@ -679,6 +692,9 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off
req_opts = p;
break;
case DHCP6_STATE_STOPPING:
return 0;
default:
n = client->n_req_opts;
req_opts = client->req_opts;
@ -690,6 +706,22 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off
return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
}
static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
assert(client);
assert(buf);
assert(*buf);
assert(offset);
if (!client->mudurl)
return 0;
if (client->state == DHCP6_STATE_STOPPING)
return 0;
return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_MUD_URL_V6,
strlen(client->mudurl), client->mudurl);
}
int dhcp6_client_send_message(sd_dhcp6_client *client) {
_cleanup_free_ uint8_t *buf = NULL;
struct in6_addr all_servers =
@ -735,7 +767,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_STOPPING:
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
client->lease->serverid);
@ -753,18 +785,15 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
return r;
break;
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
case DHCP6_STATE_STOPPED:
default:
assert_not_reached();
}
if (client->mudurl) {
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6,
strlen(client->mudurl), client->mudurl);
if (r < 0)
return r;
}
r = client_append_mudurl(client, &buf, &offset);
if (r < 0)
return r;
r = client_append_oro(client, &buf, &offset);
if (r < 0)
@ -856,6 +885,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
break;
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_STOPPING:
case DHCP6_STATE_BOUND:
default:
assert_not_reached();
@ -911,6 +941,7 @@ static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) {
assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW));
break;
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_STOPPING:
case DHCP6_STATE_BOUND:
default:
assert_not_reached();
@ -1293,7 +1324,7 @@ static int client_receive_message(
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
triple_timestamp_from_realtime(&t, timeval_load(CMSG_TYPED_DATA(cmsg, struct timeval)));
}
if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
@ -1319,6 +1350,7 @@ static int client_receive_message(
case DHCP6_STATE_BOUND:
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_STOPPING:
default:
assert_not_reached();
}
@ -1326,10 +1358,37 @@ static int client_receive_message(
return 0;
}
static int client_send_release(sd_dhcp6_client *client) {
sd_dhcp6_lease *lease;
assert(client);
if (!client->send_release)
return 0;
if (sd_dhcp6_client_get_lease(client, &lease) < 0)
return 0;
if (!lease->ia_na && !lease->ia_pd)
return 0;
client_set_state(client, DHCP6_STATE_STOPPING);
return dhcp6_client_send_message(client);
}
int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
int r;
if (!client)
return 0;
/* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
* engine is about to release its UDP socket unconditionally. */
r = client_send_release(client);
if (r < 0)
log_dhcp6_client_errno(client, r,
"Failed to send DHCP6 release message, ignoring: %m");
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
client->receive_message = sd_event_source_unref(client->receive_message);
@ -1446,6 +1505,12 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
return client->event;
}
int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev) {
assert_return(client, -EINVAL);
return device_unref_and_replace(client->dev, dev);
}
static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
if (!client)
return NULL;
@ -1461,6 +1526,8 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
client->fd = safe_close(client->fd);
sd_device_unref(client->dev);
free(client->req_opts);
free(client->fqdn);
free(client->mudurl);
@ -1491,7 +1558,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
.ia_pd.type = SD_DHCP6_OPTION_IA_PD,
.ifindex = -1,
.request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
.fd = -1,
.fd = -EBADF,
.rapid_commit = true,
};

View file

@ -510,13 +510,11 @@ static int dhcp6_lease_parse_message(
r = dhcp6_option_parse_status(optval, optlen, &msg);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
if (r > 0)
return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
"Received %s message with non-zero status: %s%s%s",
"Received %s message with non-zero status%s%s",
dhcp6_message_type_to_string(message->type),
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
isempty(msg) ? "." : ": ", strempty(msg));
break;
}
case SD_DHCP6_OPTION_IA_NA: {
@ -619,7 +617,7 @@ static int dhcp6_lease_parse_message(
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received information refresh time option with an invalid length (%zu).", optlen);
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
irt = unaligned_read_be32(optval) * USEC_PER_SEC;
break;
}
}

View file

@ -0,0 +1,103 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "sd-device.h"
#include "log.h"
#include "macro.h"
#define device_unref_and_replace(a, b) \
unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (key = sd_device_get_property_first(device, &(value)); \
key; \
key = sd_device_get_property_next(device, &(value)))
#define FOREACH_DEVICE_TAG(device, tag) \
for (tag = sd_device_get_tag_first(device); \
tag; \
tag = sd_device_get_tag_next(device))
#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \
for (tag = sd_device_get_current_tag_first(device); \
tag; \
tag = sd_device_get_current_tag_next(device))
#define FOREACH_DEVICE_SYSATTR(device, attr) \
for (attr = sd_device_get_sysattr_first(device); \
attr; \
attr = sd_device_get_sysattr_next(device))
#define FOREACH_DEVICE_DEVLINK(device, devlink) \
for (devlink = sd_device_get_devlink_first(device); \
devlink; \
devlink = sd_device_get_devlink_next(device))
#define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr) \
for (child = sd_device_get_child_first(device, suffix_ptr); \
child; \
child = sd_device_get_child_next(device, suffix_ptr))
#define FOREACH_DEVICE_CHILD(device, child) \
_FOREACH_DEVICE_CHILD(device, child, NULL)
#define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix) \
_FOREACH_DEVICE_CHILD(device, child, &suffix)
#define FOREACH_DEVICE(enumerator, device) \
for (device = sd_device_enumerator_get_device_first(enumerator); \
device; \
device = sd_device_enumerator_get_device_next(enumerator))
#define FOREACH_SUBSYSTEM(enumerator, device) \
for (device = sd_device_enumerator_get_subsystem_first(enumerator); \
device; \
device = sd_device_enumerator_get_subsystem_next(enumerator))
#define log_device_full_errno_zerook(device, level, error, ...) \
({ \
const char *_sysname = NULL; \
sd_device *_d = (device); \
int _level = (level), _e = (error); \
\
if (_d && _unlikely_(log_get_max_level() >= LOG_PRI(_level))) \
(void) sd_device_get_sysname(_d, &_sysname); \
log_object_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, \
_sysname ? "DEVICE=" : NULL, _sysname, \
NULL, NULL, __VA_ARGS__); \
})
#define log_device_full_errno(device, level, error, ...) \
({ \
int _error = (error); \
ASSERT_NON_ZERO(_error); \
log_device_full_errno_zerook(device, level, _error, __VA_ARGS__); \
})
#define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__)
#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__)
#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
int devname_from_devnum(mode_t mode, dev_t devnum, char **ret);
static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
assert(st);
return devname_from_devnum(st->st_mode, st->st_rdev, ret);
}
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
char** device_make_log_fields(sd_device *device);

View file

@ -111,7 +111,7 @@ int event_reset_time_relative(
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) {
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
int r;
assert(e);

View file

@ -16,6 +16,7 @@
#include "glyph-util.h"
#include "hashmap.h"
#include "list.h"
#include "logarithm.h"
#include "macro.h"
#include "memory-util.h"
#include "missing_syscall.h"
@ -377,22 +378,22 @@ _public_ int sd_event_new(sd_event** ret) {
*e = (sd_event) {
.n_ref = 1,
.epoll_fd = -1,
.watchdog_fd = -1,
.epoll_fd = -EBADF,
.watchdog_fd = -EBADF,
.realtime.wakeup = WAKEUP_CLOCK_DATA,
.realtime.fd = -1,
.realtime.fd = -EBADF,
.realtime.next = USEC_INFINITY,
.boottime.wakeup = WAKEUP_CLOCK_DATA,
.boottime.fd = -1,
.boottime.fd = -EBADF,
.boottime.next = USEC_INFINITY,
.monotonic.wakeup = WAKEUP_CLOCK_DATA,
.monotonic.fd = -1,
.monotonic.fd = -EBADF,
.monotonic.next = USEC_INFINITY,
.realtime_alarm.wakeup = WAKEUP_CLOCK_DATA,
.realtime_alarm.fd = -1,
.realtime_alarm.fd = -EBADF,
.realtime_alarm.next = USEC_INFINITY,
.boottime_alarm.wakeup = WAKEUP_CLOCK_DATA,
.boottime_alarm.fd = -1,
.boottime_alarm.fd = -EBADF,
.boottime_alarm.next = USEC_INFINITY,
.perturb = USEC_INFINITY,
.original_pid = getpid_cached(),
@ -642,7 +643,7 @@ static int event_make_signal_data(
*d = (struct signal_data) {
.wakeup = WAKEUP_SIGNAL_DATA,
.fd = -1,
.fd = -EBADF,
.priority = priority,
};
@ -658,7 +659,9 @@ static int event_make_signal_data(
ss_copy = d->sigset;
assert_se(sigaddset(&ss_copy, sig) >= 0);
r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC);
r = signalfd(d->fd >= 0 ? d->fd : -1, /* the first arg must be -1 or a valid signalfd */
&ss_copy,
SFD_NONBLOCK|SFD_CLOEXEC);
if (r < 0) {
r = -errno;
goto fail;
@ -1177,7 +1180,7 @@ static int event_setup_timer_fd(
if (_likely_(d->fd >= 0))
return 0;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC);
if (fd < 0)
@ -1513,7 +1516,7 @@ _public_ int sd_event_add_child(
} else
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
} else
s->child.pidfd = -1;
s->child.pidfd = -EBADF;
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* We have a pidfd and we only want to watch for exit */
@ -1776,7 +1779,7 @@ static int event_make_inotify_data(
int64_t priority,
struct inotify_data **ret) {
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
struct inotify_data *d;
int r;
@ -1973,7 +1976,7 @@ static int event_make_inode_data(
.dev = dev,
.ino = ino,
.wd = -1,
.fd = -1,
.fd = -EBADF,
.inotify_data = inotify_data,
};
@ -2069,7 +2072,7 @@ static int event_add_inotify_fd_internal(
sd_event_inotify_handler_t callback,
void *userdata) {
_cleanup_close_ int donated_fd = donate ? fd : -1;
_cleanup_close_ int donated_fd = donate ? fd : -EBADF;
_cleanup_(source_freep) sd_event_source *s = NULL;
struct inotify_data *inotify_data = NULL;
struct inode_data *inode_data = NULL;
@ -2171,9 +2174,9 @@ _public_ int sd_event_add_inotify(
assert_return(path, -EINVAL);
fd = open(path, O_PATH|O_CLOEXEC|
(mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
(mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
fd = open(path, O_PATH | O_CLOEXEC |
(mask & IN_ONLYDIR ? O_DIRECTORY : 0) |
(mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
if (fd < 0)
return -errno;
@ -2723,6 +2726,9 @@ _public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec
assert_return(s, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
if (usec == USEC_INFINITY)
return sd_event_source_set_time(s, USEC_INFINITY);
r = sd_event_now(s->event, event_source_type_to_clock(s->type), &t);
if (r < 0)
return r;
@ -3176,7 +3182,7 @@ static int event_arm_timer(
assert_se(d->fd >= 0);
if (t == 0) {
/* We don' want to disarm here, just mean some time looooong ago. */
/* We don't want to disarm here, just mean some time looooong ago. */
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 1;
} else
@ -3773,12 +3779,9 @@ static int event_prepare(sd_event *e) {
break;
s->prepare_iteration = e->iteration;
r = prioq_reshuffle(e->prepare, s, &s->prepare_index);
if (r < 0)
return r;
prioq_reshuffle(e->prepare, s, &s->prepare_index);
assert(s->prepare);
s->dispatching = true;
r = s->prepare(s, s->userdata);
s->dispatching = false;
@ -3968,15 +3971,18 @@ static int epoll_wait_usec(
usec_t timeout) {
int msec;
#if 0
/* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. */
#if HAVE_EPOLL_PWAIT2
static bool epoll_pwait2_absent = false;
int r;
/* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not.
*
* FIXME: this is temporarily disabled until epoll_pwait2() becomes more widely available.
* See https://github.com/systemd/systemd/pull/18973 and
* https://github.com/systemd/systemd/issues/19052. */
/* epoll_pwait2() was added to Linux 5.11 (2021-02-14) and to glibc in 2.35 (2022-02-03). In contrast
* to other syscalls we don't bother with our own fallback syscall wrappers on old libcs, since this
* is not that obvious to implement given the libc and kernel definitions differ in the last
* argument. Moreover, the only reason to use it is the more accurate time-outs (which is not a
* biggie), let's hence rely on glibc's definitions, and fallback to epoll_pwait() when that's
* missing. */
if (!epoll_pwait2_absent && timeout != USEC_INFINITY) {
r = epoll_pwait2(fd,

View file

@ -13,59 +13,49 @@
#include "sync-util.h"
bool id128_is_valid(const char *s) {
size_t i, l;
size_t l;
assert(s);
l = strlen(s);
if (l == 32) {
if (l == SD_ID128_STRING_MAX - 1)
/* Plain formatted 128bit hex string */
return in_charset(s, HEXDIGITS);
for (i = 0; i < l; i++) {
char c = s[i];
if (!ascii_isdigit(c) &&
!(c >= 'a' && c <= 'f') &&
!(c >= 'A' && c <= 'F'))
return false;
}
} else if (l == 36) {
if (l == SD_ID128_UUID_STRING_MAX - 1) {
/* Formatted UUID */
for (i = 0; i < l; i++) {
for (size_t i = 0; i < l; i++) {
char c = s[i];
if (IN_SET(i, 8, 13, 18, 23)) {
if (c != '-')
return false;
} else {
if (!ascii_isdigit(c) &&
!(c >= 'a' && c <= 'f') &&
!(c >= 'A' && c <= 'F'))
return false;
}
} else if (!ascii_ishex(c))
return false;
}
return true;
}
} else
return false;
return true;
return false;
}
int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
char buffer[36 + 2];
int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret) {
char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
ssize_t l;
int r;
assert(fd >= 0);
assert(f < _ID128_FORMAT_MAX);
/* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
* optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
* aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
* accept". */
* accept".
*
* This returns the following:
* -ENOMEDIUM: an empty string,
* -ENOPKG: "uninitialized" or "uninitialized\n",
* -EUCLEAN: other invalid strings. */
l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
if (l < 0)
@ -75,44 +65,44 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) {
switch (l) {
case 13:
case 14:
/* Treat an "uninitialized" id file like an empty one */
return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL;
case STRLEN("uninitialized"):
case STRLEN("uninitialized\n"):
return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL;
case 33: /* plain UUID with trailing newline */
if (buffer[32] != '\n')
return -EINVAL;
case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */
if (buffer[SD_ID128_STRING_MAX-1] != '\n')
return -EUCLEAN;
_fallthrough_;
case 32: /* plain UUID without trailing newline */
if (f == ID128_UUID)
return -EINVAL;
case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */
if (!FLAGS_SET(f, ID128_FORMAT_PLAIN))
return -EUCLEAN;
buffer[32] = 0;
buffer[SD_ID128_STRING_MAX-1] = 0;
break;
case 37: /* RFC UUID with trailing newline */
if (buffer[36] != '\n')
return -EINVAL;
case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */
if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n')
return -EUCLEAN;
_fallthrough_;
case 36: /* RFC UUID without trailing newline */
if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT))
return -EINVAL;
case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */
if (!FLAGS_SET(f, ID128_FORMAT_UUID))
return -EUCLEAN;
buffer[36] = 0;
buffer[SD_ID128_UUID_STRING_MAX-1] = 0;
break;
default:
return -EINVAL;
return -EUCLEAN;
}
return sd_id128_from_string(buffer, ret);
r = sd_id128_from_string(buffer, ret);
return r == -EINVAL ? -EUCLEAN : r;
}
int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
_cleanup_close_ int fd = -1;
int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret) {
_cleanup_close_ int fd = -EBADF;
fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
@ -121,29 +111,28 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret) {
return id128_read_fd(fd, f, ret);
}
int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
char buffer[36 + 2];
int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id) {
char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
size_t sz;
int r;
assert(fd >= 0);
assert(f < _ID128_FORMAT_MAX);
assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID));
if (f != ID128_UUID) {
if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) {
assert_se(sd_id128_to_string(id, buffer));
buffer[SD_ID128_STRING_MAX - 1] = '\n';
sz = SD_ID128_STRING_MAX;
} else {
assert_se(sd_id128_to_uuid_string(id, buffer));
buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n';
sz = SD_ID128_UUID_STRING_MAX;
}
buffer[sz - 1] = '\n';
r = loop_write(fd, buffer, sz, false);
if (r < 0)
return r;
if (do_sync) {
if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) {
r = fsync_full(fd);
if (r < 0)
return r;
@ -152,14 +141,14 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
return 0;
}
int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
_cleanup_close_ int fd = -1;
int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id) {
_cleanup_close_ int fd = -EBADF;
fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444);
if (fd < 0)
return -errno;
return id128_write_fd(fd, f, id, do_sync);
return id128_write_fd(fd, f, id);
}
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
@ -184,6 +173,7 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
}
DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free);
int id128_get_product(sd_id128_t *ret) {
sd_id128_t uuid;
@ -194,9 +184,9 @@ int id128_get_product(sd_id128_t *ret) {
/* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
* particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
if (r == -ENOENT)
r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
if (r < 0)
return r;

View file

@ -10,27 +10,32 @@
bool id128_is_valid(const char *s) _pure_;
typedef enum Id128Format {
ID128_ANY,
ID128_PLAIN, /* formatted as 32 hex chars as-is */
ID128_PLAIN_OR_UNINIT, /* formatted as 32 hex chars as-is; allow special "uninitialized"
* value when reading from file (id128_read() and id128_read_fd()).
*
* This format should be used when reading a machine-id file. */
ID128_UUID, /* formatted as 36 character uuid string */
_ID128_FORMAT_MAX,
} Id128Format;
typedef enum Id128FormatFlag {
ID128_FORMAT_PLAIN = 1 << 0, /* formatted as 32 hex chars as-is */
ID128_FORMAT_UUID = 1 << 1, /* formatted as 36 character uuid string */
ID128_FORMAT_ANY = ID128_FORMAT_PLAIN | ID128_FORMAT_UUID,
int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret);
int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
ID128_SYNC_ON_WRITE = 1 << 2, /* Sync the file after write. Used only when writing an ID. */
} Id128FormatFlag;
int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret);
int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret);
int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id);
int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id);
void id128_hash_func(const sd_id128_t *p, struct siphash *state);
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
extern const struct hash_ops id128_hash_ops;
extern const struct hash_ops id128_hash_ops_free;
sd_id128_t id128_make_v4_uuid(sd_id128_t id);
int id128_get_product(sd_id128_t *ret);
/* A helper to check for the three relevant cases of "machine ID not initialized" */
#define ERRNO_IS_MACHINE_ID_UNSET(r) \
IN_SET(abs(r), \
ENOENT, \
ENOMEDIUM, \
ENOPKG)

View file

@ -15,18 +15,21 @@
#include "macro.h"
#include "missing_syscall.h"
#include "random-util.h"
#include "stat-util.h"
#include "user-util.h"
#include "util.h"
_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
size_t k = 0;
assert_return(s, NULL);
for (size_t n = 0; n < 16; n++) {
s[n*2] = hexchar(id.bytes[n] >> 4);
s[n*2+1] = hexchar(id.bytes[n] & 0xF);
for (size_t n = 0; n < sizeof(sd_id128_t); n++) {
s[k++] = hexchar(id.bytes[n] >> 4);
s[k++] = hexchar(id.bytes[n] & 0xF);
}
s[SD_ID128_STRING_MAX-1] = 0;
assert(k == SD_ID128_STRING_MAX - 1);
s[k] = 0;
return s;
}
@ -38,7 +41,7 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD
/* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
for (size_t n = 0; n < 16; n++) {
for (size_t n = 0; n < sizeof(sd_id128_t); n++) {
if (IN_SET(n, 4, 6, 8, 10))
s[k++] = '-';
@ -53,14 +56,14 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD
return s;
}
_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
unsigned n, i;
_public_ int sd_id128_from_string(const char *s, sd_id128_t *ret) {
size_t n, i;
sd_id128_t t;
bool is_guid = false;
assert_return(s, -EINVAL);
for (n = 0, i = 0; n < 16;) {
for (n = 0, i = 0; n < sizeof(sd_id128_t);) {
int a, b;
if (s[i] == '-') {
@ -90,7 +93,7 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
t.bytes[n++] = (a << 4) | b;
}
if (i != (is_guid ? 36 : 32))
if (i != (is_guid ? SD_ID128_UUID_STRING_MAX : SD_ID128_STRING_MAX) - 1)
return -EINVAL;
if (s[i] != 0)
@ -121,10 +124,8 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
static thread_local sd_id128_t saved_machine_id = {};
int r;
assert_return(ret, -EINVAL);
if (sd_id128_is_null(saved_machine_id)) {
r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN, &saved_machine_id);
if (r < 0)
return r;
@ -132,7 +133,8 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) {
return -ENOMEDIUM;
}
*ret = saved_machine_id;
if (ret)
*ret = saved_machine_id;
return 0;
}
@ -140,15 +142,19 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local sd_id128_t saved_boot_id = {};
int r;
assert_return(ret, -EINVAL);
if (sd_id128_is_null(saved_boot_id)) {
r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID, &saved_boot_id);
if (r == -ENOENT && proc_mounted() == 0)
return -ENOSYS;
if (r < 0)
return r;
if (sd_id128_is_null(saved_boot_id))
return -ENOMEDIUM;
}
*ret = saved_boot_id;
if (ret)
*ret = saved_boot_id;
return 0;
}
@ -198,22 +204,22 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
/* Chop off the final description string */
d = strrchr(description, ';');
if (!d)
return -EIO;
return -EUCLEAN;
*d = 0;
/* Look for the permissions */
p = strrchr(description, ';');
if (!p)
return -EIO;
return -EUCLEAN;
errno = 0;
perms = strtoul(p + 1, &e, 16);
if (errno > 0)
return -errno;
if (e == p + 1) /* Read at least one character */
return -EIO;
return -EUCLEAN;
if (e != d) /* Must reached the end */
return -EIO;
return -EUCLEAN;
if ((perms & ~MAX_PERMS) != 0)
return -EPERM;
@ -223,7 +229,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
/* Look for the group ID */
g = strrchr(description, ';');
if (!g)
return -EIO;
return -EUCLEAN;
r = parse_gid(g + 1, &gid);
if (r < 0)
return r;
@ -234,7 +240,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
/* Look for the user ID */
u = strrchr(description, ';');
if (!u)
return -EIO;
return -EUCLEAN;
r = parse_uid(u + 1, &uid);
if (r < 0)
return r;
@ -245,13 +251,14 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
if (c < 0)
return -errno;
if (c != sizeof(sd_id128_t))
return -EIO;
return -EUCLEAN;
return 0;
}
static int get_invocation_from_environment(sd_id128_t *ret) {
const char *e;
int r;
assert(ret);
@ -259,33 +266,31 @@ static int get_invocation_from_environment(sd_id128_t *ret) {
if (!e)
return -ENXIO;
return sd_id128_from_string(e, ret);
r = sd_id128_from_string(e, ret);
return r == -EINVAL ? -EUCLEAN : r;
}
_public_ int sd_id128_get_invocation(sd_id128_t *ret) {
static thread_local sd_id128_t saved_invocation_id = {};
int r;
assert_return(ret, -EINVAL);
if (sd_id128_is_null(saved_invocation_id)) {
/* We first check the environment. The environment variable is primarily relevant for user
* services, and sufficiently safe as long as no privilege boundary is involved. */
r = get_invocation_from_environment(&saved_invocation_id);
if (r >= 0) {
*ret = saved_invocation_id;
return 0;
} else if (r != -ENXIO)
return r;
/* The kernel keyring is relevant for system services (as for user services we don't store
* the invocation ID in the keyring, as there'd be no trust benefit in that). */
r = get_invocation_from_keyring(&saved_invocation_id);
if (r == -ENXIO)
/* The kernel keyring is relevant for system services (as for user services we don't
* store the invocation ID in the keyring, as there'd be no trust benefit in that). */
r = get_invocation_from_keyring(&saved_invocation_id);
if (r < 0)
return r;
if (sd_id128_is_null(saved_invocation_id))
return -ENOMEDIUM;
}
*ret = saved_invocation_id;
if (ret)
*ret = saved_invocation_id;
return 0;
}

View file

@ -0,0 +1,167 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosddevicehfoo
#define foosddevicehfoo
/***
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <https://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include "sd-event.h"
#include "sd-id128.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
typedef struct sd_device sd_device;
typedef struct sd_device_enumerator sd_device_enumerator;
typedef struct sd_device_monitor sd_device_monitor;
__extension__ typedef enum sd_device_action_t {
SD_DEVICE_ADD,
SD_DEVICE_REMOVE,
SD_DEVICE_CHANGE,
SD_DEVICE_MOVE,
SD_DEVICE_ONLINE,
SD_DEVICE_OFFLINE,
SD_DEVICE_BIND,
SD_DEVICE_UNBIND,
_SD_DEVICE_ACTION_MAX,
_SD_DEVICE_ACTION_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(DEVICE_ACTION)
} sd_device_action_t;
/* callback */
typedef int (*sd_device_monitor_handler_t)(sd_device_monitor *m, sd_device *device, void *userdata);
/* device */
sd_device *sd_device_ref(sd_device *device);
sd_device *sd_device_unref(sd_device *device);
int sd_device_new_from_syspath(sd_device **ret, const char *syspath);
int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum);
int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname);
int sd_device_new_from_device_id(sd_device **ret, const char *id);
int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st);
int sd_device_new_from_devname(sd_device **ret, const char *devname);
int sd_device_new_from_path(sd_device **ret, const char *path);
int sd_device_new_from_ifname(sd_device **ret, const char *ifname);
int sd_device_new_from_ifindex(sd_device **ret, int ifindex);
int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix);
int sd_device_get_parent(sd_device *child, sd_device **ret);
int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret);
int sd_device_get_syspath(sd_device *device, const char **ret);
int sd_device_get_subsystem(sd_device *device, const char **ret);
int sd_device_get_devtype(sd_device *device, const char **ret);
int sd_device_get_devnum(sd_device *device, dev_t *devnum);
int sd_device_get_ifindex(sd_device *device, int *ifindex);
int sd_device_get_driver(sd_device *device, const char **ret);
int sd_device_get_devpath(sd_device *device, const char **ret);
int sd_device_get_devname(sd_device *device, const char **ret);
int sd_device_get_sysname(sd_device *device, const char **ret);
int sd_device_get_sysnum(sd_device *device, const char **ret);
int sd_device_get_action(sd_device *device, sd_device_action_t *ret);
int sd_device_get_seqnum(sd_device *device, uint64_t *ret);
int sd_device_get_diskseq(sd_device *device, uint64_t *ret);
int sd_device_get_is_initialized(sd_device *device);
int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret);
int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret);
const char *sd_device_get_tag_first(sd_device *device);
const char *sd_device_get_tag_next(sd_device *device);
const char *sd_device_get_current_tag_first(sd_device *device);
const char *sd_device_get_current_tag_next(sd_device *device);
const char *sd_device_get_devlink_first(sd_device *device);
const char *sd_device_get_devlink_next(sd_device *device);
const char *sd_device_get_property_first(sd_device *device, const char **value);
const char *sd_device_get_property_next(sd_device *device, const char **value);
const char *sd_device_get_sysattr_first(sd_device *device);
const char *sd_device_get_sysattr_next(sd_device *device);
sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix);
sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix);
int sd_device_has_tag(sd_device *device, const char *tag);
int sd_device_has_current_tag(sd_device *device, const char *tag);
int sd_device_get_property_value(sd_device *device, const char *key, const char **value);
int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret);
int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value);
int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value);
int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4);
int sd_device_trigger(sd_device *device, sd_device_action_t action);
int sd_device_trigger_with_uuid(sd_device *device, sd_device_action_t action, sd_id128_t *ret_uuid);
int sd_device_open(sd_device *device, int flags);
/* device enumerator */
int sd_device_enumerator_new(sd_device_enumerator **ret);
sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator);
sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator);
sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator);
sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator);
sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator);
sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator);
int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match);
int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match);
int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value);
int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
/* device monitor */
int sd_device_monitor_new(sd_device_monitor **ret);
sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m);
sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m);
int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size);
int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event);
int sd_device_monitor_detach_event(sd_device_monitor *m);
sd_event *sd_device_monitor_get_event(sd_device_monitor *m);
sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m);
int sd_device_monitor_set_description(sd_device_monitor *m, const char *description);
int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret);
int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata);
int sd_device_monitor_stop(sd_device_monitor *m);
int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype);
int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag);
int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, const char *sysattr, const char *value, int match);
int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match);
int sd_device_monitor_filter_update(sd_device_monitor *m);
int sd_device_monitor_filter_remove(sd_device_monitor *m);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device, sd_device_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_enumerator, sd_device_enumerator_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_monitor, sd_device_monitor_unref);
_SD_END_DECLARATIONS;
#endif

View file

@ -23,6 +23,7 @@
#include <net/ethernet.h>
#include <sys/types.h>
#include "sd-device.h"
#include "sd-dhcp6-lease.h"
#include "sd-dhcp6-option.h"
#include "sd-event.h"
@ -263,6 +264,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
sd_dhcp6_option *v);
int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable);
int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,
@ -279,6 +281,7 @@ int sd_dhcp6_client_attach_event(
int64_t priority);
int sd_dhcp6_client_detach_event(sd_dhcp6_client *client);
sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client);
int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev);
sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client);
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client);
int sd_dhcp6_client_new(sd_dhcp6_client **ret);

View file

@ -102,3 +102,7 @@ void* greedy_realloc0(
return q;
}
void *expand_to_usable(void *ptr, size_t newsize _unused_) {
return ptr;
}

View file

@ -2,6 +2,7 @@
#pragma once
#include <alloca.h>
#include <malloc.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@ -184,17 +185,35 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
# define msan_unpoison(r, s)
#endif
/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), in a way that
* is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the
* object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of
* malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by
* both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the
* size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory,
* too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
* __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner
* case. */
/* Dummy allocator to tell the compiler that the new size of p is newsize. The implementation returns the
* pointer as is; the only reason for its existence is as a conduit for the _alloc_ attribute. This must not
* be inlined (hence a non-static function with _noinline_ because LTO otherwise tries to inline it) because
* gcc then loses the attributes on the function.
* See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96503 */
void *expand_to_usable(void *p, size_t newsize) _alloc_(2) _returns_nonnull_ _noinline_;
static inline size_t malloc_sizeof_safe(void **xp) {
if (_unlikely_(!xp || !*xp))
return 0;
size_t sz = malloc_usable_size(*xp);
*xp = expand_to_usable(*xp, sz);
/* GCC doesn't see the _returns_nonnull_ when built with ubsan, so yet another hint to make it doubly
* clear that expand_to_usable won't return NULL.
* See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79265 */
if (!*xp)
assert_not_reached();
return sz;
}
/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), which may
* return a value larger than the size that was actually allocated. Access to that additional memory is
* discouraged because it violates the C standard; a compiler cannot see that this as valid. To help the
* compiler out, the MALLOC_SIZEOF_SAFE macro 'allocates' the usable size using a dummy allocator function
* expand_to_usable. There is a possibility of malloc_usable_size() returning different values during the
* lifetime of an object, which may cause problems, but the glibc allocator does not do that at the moment. */
#define MALLOC_SIZEOF_SAFE(x) \
MIN(malloc_usable_size(x), __builtin_object_size(x, 0))
malloc_sizeof_safe((void**) &__builtin_choose_expr(__builtin_constant_p(x), (void*) { NULL }, (x)))
/* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items
* that fit into the specified memory block */

View file

@ -9,7 +9,7 @@
#include <sys/statfs.h>
#include <sys/types.h>
#include "def.h"
#include "constants.h"
#include "set.h"
#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"

View file

@ -0,0 +1,111 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# define HAS_FEATURE_MEMORY_SANITIZER 1
# endif
# endif
# if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# define HAS_FEATURE_MEMORY_SANITIZER 0
# endif
#endif
#if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
# ifdef __SANITIZE_ADDRESS__
# define HAS_FEATURE_ADDRESS_SANITIZER 1
# elif defined(__has_feature)
# if __has_feature(address_sanitizer)
# define HAS_FEATURE_ADDRESS_SANITIZER 1
# endif
# endif
# if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
# define HAS_FEATURE_ADDRESS_SANITIZER 0
# endif
#endif
#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC)
/* Many different things, but also system unit start/stop */
#define DEFAULT_TIMEOUT_USEC (DEFAULT_TIMEOUT_SEC*USEC_PER_SEC)
/* User unit start/stop */
#define DEFAULT_USER_TIMEOUT_USEC (DEFAULT_USER_TIMEOUT_SEC*USEC_PER_SEC)
/* Timeout for user confirmation on the console */
#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC)
/* We use an extra-long timeout for the reload. This is because a reload or reexec means generators are rerun
* which are timed out after DEFAULT_TIMEOUT_USEC. Let's use twice that time here, so that the generators can
* have their timeout, and for everything else there's the same time budget in place. */
#define DAEMON_RELOAD_TIMEOUT_SEC (DEFAULT_TIMEOUT_USEC * 2)
#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC)
#define DEFAULT_START_LIMIT_BURST 5
/* The default time after which exit-on-idle services exit. This
* should be kept lower than the watchdog timeout, because otherwise
* the watchdog pings will keep the loop busy. */
#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC)
/* The default value for the net.unix.max_dgram_qlen sysctl */
#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
#define NOTIFY_FD_MAX 768
#define NOTIFY_BUFFER_MAX PIPE_BUF
#if HAVE_SPLIT_USR
# define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0"
# define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n
#else
# define _CONF_PATHS_SPLIT_USR_NULSTR(n)
# define _CONF_PATHS_SPLIT_USR(n)
#endif
/* Return a nulstr for a standard cascade of configuration paths,
* suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
* to implement drop-in directories for extending configuration
* files. */
#define CONF_PATHS_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" \
_CONF_PATHS_SPLIT_USR_NULSTR(n)
#define CONF_PATHS_USR(n) \
"/etc/" n, \
"/run/" n, \
"/usr/local/lib/" n, \
"/usr/lib/" n
#define CONF_PATHS(n) \
CONF_PATHS_USR(n) \
_CONF_PATHS_SPLIT_USR(n)
#define CONF_PATHS_USR_STRV(n) \
STRV_MAKE(CONF_PATHS_USR(n))
#define CONF_PATHS_STRV(n) \
STRV_MAKE(CONF_PATHS(n))
/* The limit for PID 1 itself (which is not inherited to children) */
#define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL)
/* Since kernel 5.16 the kernel default limit was raised to 8M. Let's adjust things on old kernels too, and
* in containers so that our children inherit that. */
#define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL)
#define PLYMOUTH_SOCKET { \
.un.sun_family = AF_UNIX, \
.un.sun_path = "\0/org/freedesktop/plymouthd", \
}
/* Path where PID1 listens for varlink subscriptions from systemd-oomd to notify of changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.system.ManagedOOM"
/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM"
#define KERNEL_BASELINE_VERSION "4.15"

View file

@ -12,11 +12,17 @@
#include "tmpfile-util.h"
#include "utf8.h"
typedef int (*push_env_func_t)(
const char *filename,
unsigned line,
const char *key,
char *value,
void *userdata);
static int parse_env_file_internal(
FILE *f,
const char *fname,
int (*push) (const char *filename, unsigned line,
const char *key, char *value, void *userdata),
push_env_func_t push,
void *userdata) {
size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX;
@ -37,6 +43,9 @@ static int parse_env_file_internal(
COMMENT_ESCAPE
} state = PRE_KEY;
assert(f || fname);
assert(push);
if (f)
r = read_full_stream(f, &contents, NULL);
else
@ -274,6 +283,8 @@ static int check_utf8ness_and_warn(
const char *filename, unsigned line,
const char *key, char *value) {
assert(key);
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p = NULL;
@ -304,6 +315,8 @@ static int parse_env_file_push(
va_list aq, *ap = userdata;
int r;
assert(key);
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
@ -338,6 +351,8 @@ int parse_env_filev(
int r;
va_list aq;
assert(f || fname);
va_copy(aq, ap);
r = parse_env_file_internal(f, fname, parse_env_file_push, &aq);
va_end(aq);
@ -352,6 +367,37 @@ int parse_env_file_sentinel(
va_list ap;
int r;
assert(f || fname);
va_start(ap, fname);
r = parse_env_filev(f, fname, ap);
va_end(ap);
return r;
}
int parse_env_file_fd_sentinel(
int fd,
const char *fname, /* only used for logging */
...) {
_cleanup_close_ int fd_ro = -EBADF;
_cleanup_fclose_ FILE *f = NULL;
va_list ap;
int r;
assert(fd >= 0);
fd_ro = fd_reopen(fd, O_CLOEXEC | O_RDONLY);
if (fd_ro < 0)
return fd_ro;
f = fdopen(fd_ro, "re");
if (!f)
return -errno;
TAKE_FD(fd_ro);
va_start(ap, fname);
r = parse_env_filev(f, fname, ap);
va_end(ap);
@ -363,10 +409,13 @@ static int load_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata) {
char ***m = userdata;
char *p;
int r;
assert(key);
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
@ -383,15 +432,18 @@ static int load_env_file_push(
return 0;
}
int load_env_file(FILE *f, const char *fname, char ***rl) {
int load_env_file(FILE *f, const char *fname, char ***ret) {
_cleanup_strv_free_ char **m = NULL;
int r;
assert(f || fname);
assert(ret);
r = parse_env_file_internal(f, fname, load_env_file_push, &m);
if (r < 0)
return r;
*rl = TAKE_PTR(m);
*ret = TAKE_PTR(m);
return 0;
}
@ -403,6 +455,8 @@ static int load_env_file_push_pairs(
char ***m = ASSERT_PTR(userdata);
int r;
assert(key);
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
@ -426,15 +480,17 @@ static int load_env_file_push_pairs(
return strv_extend(m, "");
}
int load_env_file_pairs(FILE *f, const char *fname, char ***rl) {
int load_env_file_pairs(FILE *f, const char *fname, char ***ret) {
_cleanup_strv_free_ char **m = NULL;
int r;
assert(f || fname);
r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m);
if (r < 0)
return r;
*rl = TAKE_PTR(m);
*ret = TAKE_PTR(m);
return 0;
}
@ -446,6 +502,8 @@ static int merge_env_file_push(
char ***env = ASSERT_PTR(userdata);
char *expanded_value;
assert(key);
if (!value) {
log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
return 0;
@ -476,6 +534,9 @@ int merge_env_file(
FILE *f,
const char *fname) {
assert(env);
assert(f || fname);
/* NOTE: this function supports braceful and braceless variable expansions,
* plus "extended" substitutions, unlike other exported parsing functions.
*/
@ -486,6 +547,9 @@ int merge_env_file(
static void write_env_var(FILE *f, const char *v) {
const char *p;
assert(f);
assert(v);
p = strchr(v, '=');
if (!p) {
/* Fallback */

View file

@ -9,8 +9,10 @@
int parse_env_filev(FILE *f, const char *fname, va_list ap);
int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_;
#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL)
int load_env_file(FILE *f, const char *fname, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, char ***l);
int parse_env_file_fd_sentinel(int fd, const char *fname, ...) _sentinel_;
#define parse_env_file_fd(fd, fname, ...) parse_env_file_fd_sentinel(fd, fname, __VA_ARGS__, NULL)
int load_env_file(FILE *f, const char *fname, char ***ret);
int load_env_file_pairs(FILE *f, const char *fname, char ***ret);
int merge_env_file(char ***env, FILE *f, const char *fname);

View file

@ -445,31 +445,30 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
}
char* octescape(const char *s, size_t len) {
char *r, *t;
const char *f;
char *buf, *t;
/* Escapes all chars in bad, in addition to \ and " chars,
* in \nnn style escaping. */
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */
r = new(char, len * 4 + 1);
if (!r)
assert(s || len == 0);
t = buf = new(char, len * 4 + 1);
if (!buf)
return NULL;
for (f = s, t = r; f < s + len; f++) {
for (size_t i = 0; i < len; i++) {
uint8_t u = (uint8_t) s[i];
if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"')) {
*(t++) = '\\';
*(t++) = '0' + (*f >> 6);
*(t++) = '0' + ((*f >> 3) & 8);
*(t++) = '0' + (*f & 8);
*(t++) = '0' + (u >> 6);
*(t++) = '0' + ((u >> 3) & 7);
*(t++) = '0' + (u & 7);
} else
*(t++) = *f;
*(t++) = u;
}
*t = 0;
return r;
return buf;
}
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {

View file

@ -29,7 +29,6 @@
#include "stat-util.h"
#include "stdio-util.h"
#include "tmpfile-util.h"
#include "util.h"
/* The maximum number of iterations in the loop to close descriptors in the fallback case
* when /proc/self/fd/ is inaccessible. */
@ -57,11 +56,9 @@ int close_nointr(int fd) {
}
int safe_close(int fd) {
/*
* Like close_nointr() but cannot fail. Guarantees errno is
* unchanged. Is a NOP with negative fds passed, and returns
* -1, so that it can be used in this syntax:
* Like close_nointr() but cannot fail. Guarantees errno is unchanged. Is a noop for negative fds,
* and returns -EBADF, so that it can be used in this syntax:
*
* fd = safe_close(fd);
*/
@ -77,7 +74,7 @@ int safe_close(int fd) {
assert_se(close_nointr(fd) != -EBADF);
}
return -1;
return -EBADF;
}
void safe_close_pair(int p[static 2]) {
@ -174,12 +171,35 @@ int fd_cloexec(int fd, bool cloexec) {
return RET_NERRNO(fcntl(fd, F_SETFD, nflags));
}
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
int ret = 0, r;
assert(n_fds == 0 || fds);
for (size_t i = 0; i < n_fds; i++) {
if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
continue;
r = fd_cloexec(fds[i], cloexec);
if (r < 0 && ret >= 0) /* Continue going, but return first error */
ret = r;
else
ret = 1; /* report if we did anything */
}
return ret;
}
_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
assert(n_fdset == 0 || fdset);
for (size_t i = 0; i < n_fdset; i++)
for (size_t i = 0; i < n_fdset; i++) {
if (fdset[i] < 0)
continue;
if (fdset[i] == fd)
return true;
}
return false;
}
@ -226,7 +246,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
"Refusing to loop over %d potential fds.",
max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
int q;
if (fd_in_set(fd, except, n_except))
@ -252,6 +272,10 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
if (!have_close_range)
return 0;
if (n_except == 1 && except[0] < 0) /* Minor optimization: if we only got one fd, and it's invalid,
* we got none */
n_except = 0;
switch (n_except) {
case 0:
@ -386,7 +410,7 @@ int close_all_fds(const int except[], size_t n_except) {
return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
FOREACH_DIRENT(de, d, return -errno) {
int fd = -1, q;
int fd = -EBADF, q;
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
continue;
@ -475,7 +499,8 @@ void cmsg_close_all(struct msghdr *mh) {
CMSG_FOREACH(cmsg, mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
close_many(CMSG_TYPED_DATA(cmsg, int),
(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
}
bool fdname_is_valid(const char *s) {
@ -604,25 +629,23 @@ int fd_move_above_stdio(int fd) {
}
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) {
int fd[3] = { /* Put together an array of fds we work on */
original_input_fd,
original_output_fd,
original_error_fd
};
int r, i,
null_fd = -1, /* if we open /dev/null, we store the fd to it here */
copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */
int fd[3] = { original_input_fd, /* Put together an array of fds we work on */
original_output_fd,
original_error_fd },
null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */
copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here
* temporarily, and hence need to close at the end. */
r;
bool null_readable, null_writable;
/* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is
* specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as
* itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be
* on.
/* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors
* is specified as -EBADF it will be connected with /dev/null instead. If any of the file descriptors
* is passed as itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is
* turned off should it be on.
*
* Note that if any of the passed file descriptors are > 2 they will be closed both on success and on
* failure! Thus, callers should assume that when this function returns the input fds are invalidated.
* Note that if any of the passed file descriptors are > 2 they will be closed both on success and
* on failure! Thus, callers should assume that when this function returns the input fds are
* invalidated.
*
* Note that when this function fails stdin/stdout/stderr might remain half set up!
*
@ -658,7 +681,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
}
/* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */
for (i = 0; i < 3; i++) {
for (int i = 0; i < 3; i++) {
if (fd[i] < 0)
fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */
@ -674,10 +697,10 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
}
}
/* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we
* have freedom to move them around. If the fds already were at the right places then the specific fds are
* -1. Let's now move them to the right places. This is the point of no return. */
for (i = 0; i < 3; i++) {
/* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that
* we have freedom to move them around. If the fds already were at the right places then the specific
* fds are -EBADF. Let's now move them to the right places. This is the point of no return. */
for (int i = 0; i < 3; i++) {
if (fd[i] == i) {
@ -708,7 +731,7 @@ finish:
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
for (i = 0; i < 3; i++)
for (int i = 0; i < 3; i++)
safe_close(copy_fd[i]);
/* Close our null fd, if it's > 2 */
@ -754,6 +777,37 @@ int fd_reopen(int fd, int flags) {
return new_fd;
}
int fd_reopen_condition(
int fd,
int flags,
int mask,
int *ret_new_fd) {
int r, new_fd;
assert(fd >= 0);
/* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
* flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
* needed, but only then. */
r = fcntl(fd, F_GETFL);
if (r < 0)
return -errno;
if ((r & mask) == (flags & mask)) {
*ret_new_fd = -EBADF;
return fd;
}
new_fd = fd_reopen(fd, flags);
if (new_fd < 0)
return new_fd;
*ret_new_fd = new_fd;
return new_fd;
}
int read_nr_open(void) {
_cleanup_free_ char *nr_open = NULL;
int r;

View file

@ -15,14 +15,15 @@
/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
#define PIPE_EBADF { -EBADF, -EBADF }
int close_nointr(int fd);
int safe_close(int fd);
void safe_close_pair(int p[static 2]);
static inline int safe_close_above_stdio(int fd) {
if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */
return -1;
if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -EBADF. */
return -EBADF;
return safe_close(fd);
}
@ -56,6 +57,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
int fd_nonblock(int fd, bool nonblock);
int fd_cloexec(int fd, bool cloexec);
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);
int get_max_fd(void);
@ -85,17 +87,11 @@ int fd_move_above_stdio(int fd);
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
static inline int make_null_stdio(void) {
return rearrange_stdio(-1, -1, -1);
return rearrange_stdio(-EBADF, -EBADF, -EBADF);
}
/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */
#define TAKE_FD(fd) \
({ \
int *_fd_ = &(fd); \
int _ret_ = *_fd_; \
*_fd_ = -1; \
_ret_; \
})
/* Like TAKE_PTR() but for file descriptors, resetting them to -EBADF */
#define TAKE_FD(fd) TAKE_GENERIC(fd, int, -EBADF)
/* Like free_and_replace(), but for file descriptors */
#define close_and_replace(a, b) \
@ -107,6 +103,7 @@ static inline int make_null_stdio(void) {
})
int fd_reopen(int fd, int flags);
int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);

View file

@ -21,6 +21,7 @@
#include "log.h"
#include "macro.h"
#include "mkdir.h"
#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
@ -43,16 +44,17 @@
* can detect EOFs. */
#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U)
int fopen_unlocked(const char *path, const char *options, FILE **ret) {
int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret) {
int r;
assert(ret);
FILE *f = fopen(path, options);
if (!f)
return -errno;
r = xfopenat(dir_fd, path, options, flags, ret);
if (r < 0)
return r;
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
(void) __fsetlocking(*ret, FSETLOCKING_BYCALLER);
*ret = f;
return 0;
}
@ -78,7 +80,7 @@ int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) {
if (r < 0)
return r;
*fd = -1;
*fd = -EBADF;
return 0;
}
@ -90,7 +92,7 @@ FILE* take_fdopen(int *fd, const char *options) {
if (!f)
return NULL;
*fd = -1;
*fd = -EBADF;
return f;
}
@ -102,7 +104,7 @@ DIR* take_fdopendir(int *dfd) {
if (!d)
return NULL;
*dfd = -1;
*dfd = -EBADF;
return d;
}
@ -134,7 +136,7 @@ int write_string_stream_ts(
const struct timespec *ts) {
bool needs_nl;
int r, fd = -1;
int r, fd = -EBADF;
assert(f);
assert(line);
@ -209,7 +211,8 @@ int write_string_stream_ts(
return 0;
}
static int write_string_file_atomic(
static int write_string_file_atomic_at(
int dir_fd,
const char *fn,
const char *line,
WriteStringFileFlags flags,
@ -225,7 +228,7 @@ static int write_string_file_atomic(
/* Note that we'd really like to use O_TMPFILE here, but can't really, since we want replacement
* semantics here, and O_TMPFILE can't offer that. i.e. rename() replaces but linkat() doesn't. */
r = fopen_temporary(fn, &f, &p);
r = fopen_temporary_at(dir_fd, fn, &f, &p);
if (r < 0)
return r;
@ -237,7 +240,7 @@ static int write_string_file_atomic(
if (r < 0)
goto fail;
if (rename(p, fn) < 0) {
if (renameat(dir_fd, p, dir_fd, fn) < 0) {
r = -errno;
goto fail;
}
@ -252,11 +255,12 @@ static int write_string_file_atomic(
return 0;
fail:
(void) unlink(p);
(void) unlinkat(dir_fd, p, 0);
return r;
}
int write_string_file_ts(
int write_string_file_ts_at(
int dir_fd,
const char *fn,
const char *line,
WriteStringFileFlags flags,
@ -272,7 +276,7 @@ int write_string_file_ts(
assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
if (flags & WRITE_STRING_FILE_MKDIR_0755) {
r = mkdir_parents(fn, 0755);
r = mkdirat_parents(dir_fd, fn, 0755);
if (r < 0)
return r;
}
@ -280,7 +284,7 @@ int write_string_file_ts(
if (flags & WRITE_STRING_FILE_ATOMIC) {
assert(flags & WRITE_STRING_FILE_CREATE);
r = write_string_file_atomic(fn, line, flags, ts);
r = write_string_file_atomic_at(dir_fd, fn, line, flags, ts);
if (r < 0)
goto fail;
@ -289,12 +293,12 @@ int write_string_file_ts(
assert(!ts);
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
fd = open(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),
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
fd = openat(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),
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
if (fd < 0) {
r = -errno;
goto fail;
@ -364,7 +368,7 @@ int read_one_line_file(const char *fn, char **line) {
return read_line(f, LONG_LINE_MAX, line);
}
int verify_file(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) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *buf = NULL;
size_t l, k;
@ -382,7 +386,7 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
if (!buf)
return -ENOMEM;
r = fopen_unlocked(fn, "re", &f);
r = fopen_unlocked_at(dir_fd, fn, "re", 0, &f);
if (r < 0)
return r;
@ -554,7 +558,7 @@ int read_virtual_file_at(
char **ret_contents,
size_t *ret_size) {
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
@ -763,7 +767,7 @@ int read_full_file_full(
r = xfopenat(dir_fd, filename, "re", 0, &f);
if (r < 0) {
_cleanup_close_ int sk = -1;
_cleanup_close_ int sk = -EBADF;
/* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */
if (r != -ENXIO)
@ -858,7 +862,6 @@ int executable_is_script(const char *path, char **interpreter) {
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
_cleanup_free_ char *status = NULL;
char *t, *f;
size_t len;
int r;
assert(terminator);
@ -910,9 +913,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
t--;
}
len = strcspn(t, terminator);
f = strndup(t, len);
f = strdupcspn(t, terminator);
if (!f)
return -ENOMEM;
@ -1245,7 +1246,7 @@ typedef enum EndOfLineMarker {
static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) {
if (!IN_SET(flags, READ_LINE_ONLY_NUL)) {
if (!FLAGS_SET(flags, READ_LINE_ONLY_NUL)) {
if (c == '\n')
return EOL_TEN;
if (c == '\r')

View file

@ -43,7 +43,10 @@ typedef enum {
READ_FULL_FILE_FAIL_WHEN_LARGER = 1 << 5, /* fail loading if file is larger than specified size */
} ReadFullFileFlags;
int fopen_unlocked(const char *path, const char *options, FILE **ret);
int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret);
static inline int fopen_unlocked(const char *path, const char *options, FILE **ret) {
return fopen_unlocked_at(AT_FDCWD, path, options, 0, ret);
}
int fdopen_unlocked(int fd, const char *options, FILE **ret);
int take_fdopen_unlocked(int *fd, const char *options, FILE **ret);
FILE* take_fdopen(int *fd, const char *options);
@ -55,7 +58,13 @@ int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags
static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) {
return write_string_stream_ts(f, line, flags, NULL);
}
int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
int write_string_file_ts_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
static inline int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts) {
return write_string_file_ts_at(AT_FDCWD, fn, line, flags, ts);
}
static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_ts_at(dir_fd, fn, line, flags, NULL);
}
static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
return write_string_file_ts(fn, line, flags, NULL);
}
@ -64,6 +73,9 @@ int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *f
int read_one_line_file(const char *filename, char **line);
int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size);
static inline int read_full_file_at(int dir_fd, const char *filename, char **ret_contents, size_t *ret_size) {
return read_full_file_full(dir_fd, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
}
static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
}
@ -82,7 +94,10 @@ static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_siz
return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size);
}
int verify_file(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 executable_is_script(const char *path, char **interpreter);

View file

@ -32,7 +32,6 @@
#include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
int unlink_noerrno(const char *path) {
PROTECT_ERRNO;
@ -175,37 +174,41 @@ int readlink_value(const char *p, char **ret) {
return 0;
}
int readlink_and_make_absolute(const char *p, char **r) {
int readlink_and_make_absolute(const char *p, char **ret) {
_cleanup_free_ char *target = NULL;
char *k;
int j;
int r;
assert(p);
assert(r);
assert(ret);
j = readlink_malloc(p, &target);
if (j < 0)
return j;
r = readlink_malloc(p, &target);
if (r < 0)
return r;
k = file_in_same_dir(p, target);
if (!k)
return -ENOMEM;
*r = k;
return 0;
return file_in_same_dir(p, target, ret);
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -EBADF;
assert(path);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change
* mode/owner on the same file */
if (fd < 0)
return -errno;
if (path) {
/* Let's acquire an O_PATH fd, as precaution to change mode/owner on the same file */
fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
return -errno;
dir_fd = fd;
return fchmod_and_chown(fd, mode, uid, gid);
} else if (dir_fd == AT_FDCWD) {
/* Let's acquire an O_PATH fd of the current directory */
fd = openat(dir_fd, ".", O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY);
if (fd < 0)
return -errno;
dir_fd = fd;
}
return fchmod_and_chown(dir_fd, mode, uid, gid);
}
int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
@ -351,7 +354,7 @@ int fd_warn_permissions(const char *path, int fd) {
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
int r, ret;
assert(path);
@ -675,7 +678,7 @@ void unlink_tempfilep(char (*p)[]) {
}
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
_cleanup_close_ int truncate_fd = -1;
_cleanup_close_ int truncate_fd = -EBADF;
struct stat st;
off_t l, bs;
@ -806,7 +809,7 @@ int conservative_renameat(
int olddirfd, const char *oldpath,
int newdirfd, const char *newpath) {
_cleanup_close_ int old_fd = -1, new_fd = -1;
_cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF;
struct stat old_stat, new_stat;
/* Renames the old path to thew new path, much like renameat() — except if both are regular files and
@ -902,7 +905,7 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
/* On EINTR try a couple of times more, but protect against busy looping
* (not more than 16 times per 10s) */
rl = (RateLimit) { 10 * USEC_PER_SEC, 16 };
rl = (const RateLimit) { 10 * USEC_PER_SEC, 16 };
while (ratelimit_below(&rl)) {
r = posix_fallocate(fd, offset, size);
if (r != EINTR)
@ -988,7 +991,7 @@ int parse_cifs_service(
}
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
_cleanup_close_ int fd = -1, parent_fd = -1;
_cleanup_close_ int fd = -EBADF, parent_fd = -EBADF;
_cleanup_free_ char *fname = NULL;
bool made;
int r;

View file

@ -33,7 +33,10 @@ int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid);
static inline int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
return chmod_and_chown_at(AT_FDCWD, path, mode, uid, gid);
}
int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid);
static inline int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
return fchmod_and_chown_with_fallback(fd, NULL, mode, uid, gid); /* no fallback */

View file

@ -9,15 +9,15 @@ bool emoji_enabled(void) {
static int cached_emoji_enabled = -1;
if (cached_emoji_enabled < 0) {
int val;
int val = getenv_bool("SYSTEMD_EMOJI");
if (val >= 0)
return (cached_emoji_enabled = val);
val = getenv_bool("SYSTEMD_EMOJI");
if (val < 0)
cached_emoji_enabled =
is_locale_utf8() &&
!STRPTR_IN_SET(getenv("TERM"), "dumb", "linux");
else
cached_emoji_enabled = val;
const char *term = getenv("TERM");
if (!term || STR_IN_SET(term, "dumb", "linux"))
return (cached_emoji_enabled = false);
cached_emoji_enabled = is_locale_utf8();
}
return cached_emoji_enabled;
@ -71,6 +71,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_SPARKLES] = "*",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
},
/* UTF-8 */
@ -124,10 +125,11 @@ const char *special_glyph(SpecialGlyph code) {
/* This emoji is a single character cell glyph in Unicode, and two in ASCII */
[SPECIAL_GLYPH_TOUCH] = u8"👆", /* actually called: BACKHAND INDEX POINTING UP */
/* These three 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 */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"",
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
},
};

View file

@ -44,6 +44,7 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_RECYCLING,
SPECIAL_GLYPH_DOWNLOAD,
SPECIAL_GLYPH_SPARKLES,
SPECIAL_GLYPH_WARNING_SIGN,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;

View file

@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "fileio.h"
#include "hashmap.h"
#include "logarithm.h"
#include "macro.h"
#include "memory-util.h"
#include "mempool.h"
@ -372,8 +373,9 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
}
static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) {
return (struct hashmap_base_entry*)
((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
return CAST_ALIGN_PTR(
struct hashmap_base_entry,
(uint8_t *) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
}
static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) {
@ -772,7 +774,7 @@ static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enu
HashmapBase *h;
const struct hashmap_type_info *hi = &hashmap_type_info[type];
bool use_pool = mempool_enabled && mempool_enabled();
bool use_pool = mempool_enabled && mempool_enabled(); /* mempool_enabled is a weak symbol */
h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
if (!h)
@ -1751,7 +1753,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) {
}
if (r < 0)
return _hashmap_free(copy, false, false);
return _hashmap_free(copy, NULL, NULL);
return copy;
}

View file

@ -7,7 +7,6 @@
#include "hash-funcs.h"
#include "macro.h"
#include "util.h"
/*
* A hash table implementation. As a minor optimization a NULL hashmap object

View file

@ -59,11 +59,13 @@ char *hexmem(const void *p, size_t l) {
const uint8_t *x;
char *r, *z;
assert(p || l == 0);
z = r = new(char, l * 2 + 1);
if (!r)
return NULL;
for (x = p; x < (const uint8_t*) p + l; x++) {
for (x = p; x && x < (const uint8_t*) p + l; x++) {
*(z++) = hexchar(*x >> 4);
*(z++) = hexchar(*x & 15);
}
@ -108,12 +110,17 @@ static int unhex_next(const char **p, size_t *l) {
return r;
}
int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) {
int unhexmem_full(
const char *p,
size_t l,
bool secure,
void **ret,
size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
size_t buf_size;
const char *x;
uint8_t *z;
int r;
assert(p || l == 0);
@ -126,22 +133,20 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_
if (!buf)
return -ENOMEM;
CLEANUP_ERASE_PTR(secure ? &buf : NULL, buf_size);
for (x = p, z = buf;;) {
int a, b;
a = unhex_next(&x, &l);
if (a == -EPIPE) /* End of string */
break;
if (a < 0) {
r = a;
goto on_failure;
}
if (a < 0)
return a;
b = unhex_next(&x, &l);
if (b < 0) {
r = b;
goto on_failure;
}
if (b < 0)
return b;
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}
@ -154,12 +159,6 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_
*ret = TAKE_PTR(buf);
return 0;
on_failure:
if (secure)
explicit_bzero_safe(buf, buf_size);
return r;
}
/* https://tools.ietf.org/html/rfc4648#section-6
@ -586,121 +585,136 @@ ssize_t base64mem_full(
const void *p,
size_t l,
size_t line_break,
char **out) {
char **ret) {
const uint8_t *x;
char *r, *z;
char *b, *z;
size_t m;
assert(p || l == 0);
assert(out);
assert(line_break > 0);
assert(ret);
/* three input bytes makes four output bytes, padding is added so we must round up */
m = 4 * (l + 2) / 3 + 1;
if (line_break != SIZE_MAX)
m += m / line_break;
z = r = malloc(m);
if (!r)
z = b = malloc(m);
if (!b)
return -ENOMEM;
for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
for (x = p; x && x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
}
switch (l % 3) {
case 2:
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
maybe_line_break(&z, r, line_break);
maybe_line_break(&z, b, line_break);
*(z++) = '=';
break;
case 1:
maybe_line_break(&z, r, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
maybe_line_break(&z, r, line_break);
*(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
maybe_line_break(&z, r, line_break);
*(z++) = '=';
maybe_line_break(&z, r, line_break);
*(z++) = '=';
case 1:
maybe_line_break(&z, b, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
maybe_line_break(&z, b, line_break);
*(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
maybe_line_break(&z, b, line_break);
*(z++) = '=';
maybe_line_break(&z, b, line_break);
*(z++) = '=';
break;
}
*z = 0;
*out = r;
assert(z >= r); /* Let static analyzers know that the answer is non-negative. */
return z - r;
*ret = b;
assert(z >= b); /* Let static analyzers know that the answer is non-negative. */
return z - b;
}
static int base64_append_width(
char **prefix, int plen,
char sep, int indent,
const void *p, size_t l,
int width) {
static ssize_t base64_append_width(
char **prefix,
size_t plen,
char sep,
size_t indent,
const void *p,
size_t l,
size_t width) {
_cleanup_free_ char *x = NULL;
char *t, *s;
ssize_t len, avail, line, lines;
size_t lines;
ssize_t len;
assert(prefix);
assert(*prefix || plen == 0);
assert(p || l == 0);
len = base64mem(p, l, &x);
if (len <= 0)
if (len < 0)
return len;
if (len == 0)
return plen;
lines = DIV_ROUND_UP(len, width);
if ((size_t) plen >= SSIZE_MAX - 1 - 1 ||
if (plen >= SSIZE_MAX - 1 - 1 ||
lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1))
return -ENOMEM;
t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines);
t = realloc(*prefix, plen + 1 + 1 + (indent + width + 1) * lines);
if (!t)
return -ENOMEM;
t[plen] = sep;
s = t + plen;
for (size_t line = 0; line < lines; line++) {
size_t act = MIN(width, (size_t) len);
for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) {
int act = MIN(width, avail);
if (line > 0)
sep = '\n';
if (line > 0 || sep == '\n') {
memset(s, ' ', indent);
s += indent;
if (s > t) {
*s++ = sep;
if (sep == '\n')
s = mempset(s, ' ', indent);
}
s = mempcpy(s, x + width * line, act);
*(s++) = line < lines - 1 ? '\n' : '\0';
avail -= act;
len -= act;
}
assert(avail == 0);
assert(len == 0);
*s = '\0';
*prefix = t;
return 0;
return s - t;
}
int base64_append(
char **prefix, int plen,
const void *p, size_t l,
int indent, int width) {
ssize_t base64_append(
char **prefix,
size_t plen,
const void *p,
size_t l,
size_t indent,
size_t width) {
if (plen > width / 2 || plen + indent > width)
/* leave indent on the left, keep last column free */
return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1);
return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent);
else
/* leave plen on the left, keep last column free */
return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1);
@ -748,12 +762,17 @@ static int unbase64_next(const char **p, size_t *l) {
return ret;
}
int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) {
int unbase64mem_full(
const char *p,
size_t l,
bool secure,
void **ret,
size_t *ret_size) {
_cleanup_free_ uint8_t *buf = NULL;
const char *x;
uint8_t *z;
size_t len;
int r;
assert(p || l == 0);
@ -768,60 +787,44 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
if (!buf)
return -ENOMEM;
CLEANUP_ERASE_PTR(secure ? &buf : NULL, len);
for (x = p, z = buf;;) {
int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
a = unbase64_next(&x, &l);
if (a == -EPIPE) /* End of string */
break;
if (a < 0) {
r = a;
goto on_failure;
}
if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */
r = -EINVAL;
goto on_failure;
}
if (a < 0)
return a;
if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
return -EINVAL;
b = unbase64_next(&x, &l);
if (b < 0) {
r = b;
goto on_failure;
}
if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */
r = -EINVAL;
goto on_failure;
}
if (b < 0)
return b;
if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
return -EINVAL;
c = unbase64_next(&x, &l);
if (c < 0) {
r = c;
goto on_failure;
}
if (c < 0)
return c;
d = unbase64_next(&x, &l);
if (d < 0) {
r = d;
goto on_failure;
}
if (d < 0)
return d;
if (c == INT_MAX) { /* Padding at the third character */
if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */
r = -EINVAL;
goto on_failure;
}
if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
return -EINVAL;
/* b == 00YY0000 */
if (b & 15) {
r = -EINVAL;
goto on_failure;
}
if (b & 15)
return -EINVAL;
if (l > 0) { /* Trailing rubbish? */
r = -ENAMETOOLONG;
goto on_failure;
}
if (l > 0) /* Trailing rubbish? */
return -ENAMETOOLONG;
*(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
break;
@ -829,15 +832,11 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
if (d == INT_MAX) {
/* c == 00ZZZZ00 */
if (c & 3) {
r = -EINVAL;
goto on_failure;
}
if (c & 3)
return -EINVAL;
if (l > 0) { /* Trailing rubbish? */
r = -ENAMETOOLONG;
goto on_failure;
}
if (l > 0) /* Trailing rubbish? */
return -ENAMETOOLONG;
*(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
*(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
@ -851,18 +850,14 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
*z = 0;
assert((size_t) (z - buf) <= len);
if (ret_size)
*ret_size = (size_t) (z - buf);
if (ret)
*ret = TAKE_PTR(buf);
return 0;
on_failure:
if (secure)
explicit_bzero_safe(buf, len);
return r;
}
void hexdump(FILE *f, const void *p, size_t s) {

View file

@ -38,9 +38,13 @@ static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
return base64mem_full(p, l, SIZE_MAX, ret);
}
int base64_append(char **prefix, int plen,
const void *p, size_t l,
int margin, int width);
ssize_t base64_append(
char **prefix,
size_t plen,
const void *p,
size_t l,
size_t margin,
size_t width);
int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
return unbase64mem_full(p, l, false, mem, len);

View file

@ -62,7 +62,7 @@ int gethostname_full(GetHostnameFlags flags, char **ret) {
}
if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
buf = strndup(s, strcspn(s, "."));
buf = strdupcspn(s, ".");
else
buf = strdup(s);
if (!buf)

View file

@ -60,4 +60,12 @@ static inline bool is_outbound_hostname(const char *hostname) {
return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
}
static inline bool is_dns_stub_hostname(const char *hostname) {
return STRCASE_IN_SET(hostname, "_localdnsstub", "_localdnsstub.");
}
static inline bool is_dns_proxy_stub_hostname(const char *hostname) {
return STRCASE_IN_SET(hostname, "_localdnsproxy", "_localdnsproxy.");
}
int get_pretty_hostname(char **ret);

View file

@ -11,13 +11,13 @@
#include "alloc-util.h"
#include "errno-util.h"
#include "in-addr-util.h"
#include "logarithm.h"
#include "macro.h"
#include "parse-util.h"
#include "random-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strxcpyx.h"
#include "util.h"
bool in4_addr_is_null(const struct in_addr *a) {
assert(a);
@ -898,14 +898,6 @@ int in_addr_prefix_from_string_auto_internal(
break;
case PREFIXLEN_REFUSE:
return -ENOANO; /* To distinguish this error from others. */
case PREFIXLEN_LEGACY:
if (family == AF_INET) {
r = in4_addr_default_prefixlen(&buffer.in, &k);
if (r < 0)
return r;
} else
k = 0;
break;
default:
assert_not_reached();
}
@ -921,7 +913,7 @@ int in_addr_prefix_from_string_auto_internal(
}
static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
assert(a);
assert(state);
@ -929,7 +921,7 @@ static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
}
static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
int r;
assert(x);
@ -942,7 +934,18 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
}
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
DEFINE_HASH_OPS(
in_addr_data_hash_ops,
struct in_addr_data,
in_addr_data_hash_func,
in_addr_data_compare_func);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
in_addr_data_hash_ops_free,
struct in_addr_data,
in_addr_data_hash_func,
in_addr_data_compare_func,
free);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
@ -958,7 +961,12 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
return memcmp(a, b, sizeof(*a));
}
DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
DEFINE_HASH_OPS(
in6_addr_hash_ops,
struct in6_addr,
in6_addr_hash_func,
in6_addr_compare_func);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
in6_addr_hash_ops_free,
struct in6_addr,

View file

@ -8,7 +8,6 @@
#include "hash-funcs.h"
#include "macro.h"
#include "util.h"
union in_addr_union {
struct in_addr in;
@ -154,7 +153,6 @@ int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *r
typedef enum InAddrPrefixLenMode {
PREFIXLEN_FULL, /* Default to prefixlen of address size, 32 for IPv4 or 128 for IPv6, if not specified. */
PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */
PREFIXLEN_LEGACY, /* Default to legacy default prefixlen calculation from address if not specified. */
} InAddrPrefixLenMode;
int in_addr_prefix_from_string_auto_internal(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
@ -178,10 +176,13 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
extern const struct hash_ops in_addr_data_hash_ops;
extern const struct hash_ops in_addr_data_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;

View file

@ -161,6 +161,21 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
assert(fds || nfds == 0);
/* This is a wrapper around ppoll() that does primarily two things:
*
* Takes a usec_t instead of a struct timespec
*
* Guarantees that if an invalid fd is specified we return EBADF (i.e. converts POLLNVAL to
* EBADF). This is done because EBADF is a programming error usually, and hence should bubble up
* as error, and not be eaten up as non-error POLLNVAL event.
*
* Note that this function does not add any special handling for EINTR. Don't forget
* poll()/ppoll() will return with EINTR on any received signal always, there is no automatic
* restarting via SA_RESTART available. Thus, typically you want to handle EINTR not as an error,
* but just as reason to restart things, under the assumption you use a more appropriate mechanism
* to handle signals, such as signalfd() or signal handlers.
*/
if (nfds == 0)
return 0;
@ -188,6 +203,9 @@ int fd_wait_for_event(int fd, int event, usec_t timeout) {
};
int r;
/* ⚠️ ⚠️ ⚠️ Keep in mind you almost certainly want to handle -EINTR gracefully in the caller, see
* ppoll_usec() above! */
r = ppoll_usec(&pollfd, 1, timeout);
if (r <= 0)
return r;

View file

@ -91,7 +91,16 @@ struct iovec_wrapper *iovw_new(void);
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
/* Move data into iovw or free on error */
int r = iovw_put(iovw, data, len);
if (r < 0)
free(data);
return r;
}
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);

View file

@ -1,8 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "macro.h"
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define LIST_HEAD(t,name) \
@ -28,26 +26,27 @@
/* Prepend an item to the list */
#define LIST_PREPEND(name,head,item) \
do { \
({ \
typeof(*(head)) **_head = &(head), *_item = (item); \
assert(_item); \
if ((_item->name##_next = *_head)) \
_item->name##_next->name##_prev = _item; \
_item->name##_prev = NULL; \
*_head = _item; \
} while (false)
_item; \
})
/* Append an item to the list */
#define LIST_APPEND(name,head,item) \
do { \
({ \
typeof(*(head)) **_hhead = &(head), *_tail; \
LIST_FIND_TAIL(name, *_hhead, _tail); \
_tail = LIST_FIND_TAIL(name, *_hhead); \
LIST_INSERT_AFTER(name, *_hhead, _tail, item); \
} while (false)
})
/* Remove an item from the list */
#define LIST_REMOVE(name,head,item) \
do { \
({ \
typeof(*(head)) **_head = &(head), *_item = (item); \
assert(_item); \
if (_item->name##_next) \
@ -59,37 +58,30 @@
*_head = _item->name##_next; \
} \
_item->name##_next = _item->name##_prev = NULL; \
} while (false)
_item; \
})
/* Find the head of the list */
#define LIST_FIND_HEAD(name,item,head) \
do { \
#define LIST_FIND_HEAD(name,item) \
({ \
typeof(*(item)) *_item = (item); \
if (!_item) \
(head) = NULL; \
else { \
while (_item->name##_prev) \
_item = _item->name##_prev; \
(head) = _item; \
} \
} while (false)
while (_item && _item->name##_prev) \
_item = _item->name##_prev; \
_item; \
})
/* Find the tail of the list */
#define LIST_FIND_TAIL(name,item,tail) \
do { \
#define LIST_FIND_TAIL(name,item) \
({ \
typeof(*(item)) *_item = (item); \
if (!_item) \
(tail) = NULL; \
else { \
while (_item->name##_next) \
_item = _item->name##_next; \
(tail) = _item; \
} \
} while (false)
while (_item && _item->name##_next) \
_item = _item->name##_next; \
_item; \
})
/* Insert an item after another one (a = where, b = what) */
#define LIST_INSERT_AFTER(name,head,a,b) \
do { \
({ \
typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
assert(_b); \
if (!_a) { \
@ -103,11 +95,12 @@
_b->name##_prev = _a; \
_a->name##_next = _b; \
} \
} while (false)
_b; \
})
/* Insert an item before another one (a = where, b = what) */
#define LIST_INSERT_BEFORE(name,head,a,b) \
do { \
({ \
typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \
assert(_b); \
if (!_a) { \
@ -131,7 +124,8 @@
_b->name##_next = _a; \
_a->name##_prev = _b; \
} \
} while (false)
_b; \
})
#define LIST_JUST_US(name,item) \
(!(item)->name##_prev && !(item)->name##_next)
@ -172,18 +166,19 @@
/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
#define LIST_JOIN(name,a,b) \
do { \
({ \
assert(b); \
if (!(a)) \
(a) = (b); \
else { \
typeof(*(a)) *_head = (b), *_tail; \
LIST_FIND_TAIL(name, (a), _tail); \
_tail = LIST_FIND_TAIL(name, (a)); \
_tail->name##_next = _head; \
_head->name##_prev = _tail; \
} \
(b) = NULL; \
} while (false)
a; \
})
#define LIST_POP(name, a) \
({ \
@ -193,3 +188,7 @@
LIST_REMOVE(name, *_a, _p); \
_p; \
})
/* 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

@ -10,7 +10,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include "def.h"
#include "constants.h"
#include "dirent-util.h"
#include "env-util.h"
#include "fd-util.h"
@ -95,7 +95,7 @@ static int add_locales_from_archive(Set *locales) {
const struct locarhead *h;
const struct namehashent *e;
const void *p = MAP_FAILED;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
size_t sz = 0;
struct stat st;
int r;
@ -286,8 +286,9 @@ void init_gettext(void) {
}
bool is_locale_utf8(void) {
const char *set;
static int cached_answer = -1;
const char *set;
int r;
/* Note that we default to 'true' here, since today UTF8 is
* pretty much supported everywhere. */
@ -295,6 +296,13 @@ bool is_locale_utf8(void) {
if (cached_answer >= 0)
goto out;
r = getenv_bool_secure("SYSTEMD_UTF8");
if (r >= 0) {
cached_answer = r;
goto out;
} else if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
if (!setlocale(LC_ALL, "")) {
cached_answer = true;
goto out;

View file

@ -7,8 +7,10 @@
#include <string.h>
#include <syslog.h>
#include "list.h"
#include "macro.h"
#include "ratelimit.h"
#include "stdio-util.h"
/* Some structures we reference but don't want to pull in headers for */
struct iovec;
@ -296,6 +298,7 @@ int log_emergency_level(void);
#define log_oom() log_oom_internal(LOG_ERR, PROJECT_FILE, __LINE__, __func__)
#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__)
#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__)
bool log_on_console(void) _pure_;
@ -375,15 +378,12 @@ typedef struct LogRateLimit {
RateLimit ratelimit;
} LogRateLimit;
#define log_ratelimit_internal(_level, _error, _format, _file, _line, _func, ...) \
#define log_ratelimit_internal(_level, _error, _ratelimit, _format, _file, _line, _func, ...) \
({ \
int _log_ratelimit_error = (_error); \
int _log_ratelimit_level = (_level); \
static LogRateLimit _log_ratelimit = { \
.ratelimit = { \
.interval = 1 * USEC_PER_SEC, \
.burst = 1, \
}, \
.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) { \
@ -391,18 +391,115 @@ typedef struct LogRateLimit {
_log_ratelimit.error = _log_ratelimit_error; \
_log_ratelimit.level = _log_ratelimit_level; \
} \
if (ratelimit_below(&_log_ratelimit.ratelimit)) \
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_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, format, ...) \
#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, format, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
? log_ratelimit_internal(_level, _e, _ratelimit, format, PROJECT_FILE, __LINE__, __func__, ##__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__)
/*
* 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_attach(LogContext *c);
LogContext* log_context_detach(LogContext *c);
LogContext* log_context_new(char **fields, bool owned);
LogContext* log_context_free(LogContext *c);
/* Same as log_context_new(), but frees the given fields strv on failure. */
LogContext* log_context_new_consume(char **fields);
/* 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);
DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_detach);
DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_free);
#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_STRV(strv, c) \
_unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new(strv, /*owned=*/ false);
#define LOG_CONTEXT_PUSH_STRV(strv) \
_LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ))
/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV() are identical to
* LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV() 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_freep) LogContext *c = log_context_new_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_freep) LogContext *c = log_context_new_consume(strv);
#define LOG_CONTEXT_CONSUME_STRV(strv) \
_LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ))

View file

@ -5,27 +5,6 @@
#include "macro.h"
extern int saved_argc;
extern char **saved_argv;
static inline void save_argc_argv(int argc, char **argv) {
/* Protect against CVE-2021-4034 style attacks */
assert_se(argc > 0);
assert_se(argv);
assert_se(argv[0]);
saved_argc = argc;
saved_argv = argv;
}
bool kexec_loaded(void);
int prot_from_flags(int flags) _const_;
bool in_initrd(void);
void in_initrd_force(bool value);
/* Note: log2(0) == log2(1) == 0 here and below. */
#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0)
@ -72,9 +51,3 @@ static inline unsigned log2u_round_up(unsigned x) {
return log2u(x - 1) + 1;
}
int container_get_leader(const char *machine, pid_t *pid);
int version(void);
void disable_coredumps(void);

View file

@ -9,32 +9,9 @@
#include <sys/sysmacros.h>
#include <sys/types.h>
#include "constants.h"
#include "macro-fundamental.h"
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# define HAS_FEATURE_MEMORY_SANITIZER 1
# endif
# endif
# if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# define HAS_FEATURE_MEMORY_SANITIZER 0
# endif
#endif
#if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
# ifdef __SANITIZE_ADDRESS__
# define HAS_FEATURE_ADDRESS_SANITIZER 1
# elif defined(__has_feature)
# if __has_feature(address_sanitizer)
# define HAS_FEATURE_ADDRESS_SANITIZER 1
# endif
# endif
# if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
# define HAS_FEATURE_ADDRESS_SANITIZER 0
# endif
#endif
/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global
* variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since
* our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be
@ -87,10 +64,14 @@
_Pragma("GCC diagnostic push")
#endif
#define DISABLE_WARNING_TYPE_LIMITS \
#define DISABLE_WARNING_TYPE_LIMITS \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
#define DISABLE_WARNING_ADDRESS \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Waddress\"")
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
@ -194,12 +175,12 @@ static inline int __coverity_check_and_return__(int condition) {
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
log_assert_failed(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
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__, __PRETTY_FUNCTION__), false))
: (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
#endif /* __COVERITY__ */
@ -214,7 +195,7 @@ static inline int __coverity_check_and_return__(int condition) {
#endif
#define assert_not_reached() \
log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
#define assert_return(expr, r) \
do { \
@ -341,10 +322,14 @@ static inline int __coverity_check_and_return__(int condition) {
*p = func(*p); \
}
/* When func() doesn't return the appropriate type, set variable to empty afterwards */
/* When func() doesn't return the appropriate type, set variable to empty afterwards.
* The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
static inline void func##p(type *p) { \
if (*p != (empty)) { \
DISABLE_WARNING_ADDRESS; \
assert(func); \
REENABLE_WARNING; \
func(*p); \
*p = (empty); \
} \

View file

@ -38,21 +38,3 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
/* Now we know first 16 bytes match, memcmp() with self. */
return memcmp(data, p + 16, length) == 0;
}
#if !HAVE_EXPLICIT_BZERO
/*
* The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that
* it points to any function in particular (such as memset(), which it then might further "optimize"). This
* approach is inspired by openssl's crypto/mem_clr.c.
*/
typedef void *(*memset_t)(void *,int,size_t);
static volatile memset_t memset_func = memset;
void* explicit_bzero_safe(void *p, size_t l) {
if (l > 0)
memset_func(p, '\0', l);
return p;
}
#endif

View file

@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "macro.h"
#include "memory-util-fundamental.h"
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
@ -91,17 +92,6 @@ static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const
return (uint8_t*) p + needlelen;
}
#if HAVE_EXPLICIT_BZERO
static inline void* explicit_bzero_safe(void *p, size_t l) {
if (l > 0)
explicit_bzero(p, l);
return p;
}
#else
void *explicit_bzero_safe(void *p, size_t l);
#endif
static inline void* erase_and_free(void *p) {
size_t l;

View file

@ -363,6 +363,20 @@ static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info)
/* ======================================================================= */
#if !HAVE_RT_TGSIGQUEUEINFO
static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) {
# if defined __NR_rt_tgsigqueueinfo && __NR_rt_tgsigqueueinfo >= 0
return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info);
# else
# error "__NR_rt_tgsigqueueinfo not defined"
# endif
}
# define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo
#endif
/* ======================================================================= */
#if !HAVE_EXECVEAT
static inline int missing_execveat(int dirfd, const char *pathname,
char *const argv[], char *const envp[],
@ -412,44 +426,6 @@ static inline int missing_close_range(int first_fd, int end_fd, unsigned flags)
/* ======================================================================= */
#if !HAVE_EPOLL_PWAIT2
/* Defined to be equivalent to the kernel's _NSIG_WORDS, i.e. the size of the array of longs that is
* encapsulated by sigset_t. */
#define KERNEL_NSIG_WORDS (64 / (sizeof(long) * 8))
#define KERNEL_NSIG_BYTES (KERNEL_NSIG_WORDS * sizeof(long))
struct epoll_event;
static inline int missing_epoll_pwait2(
int fd,
struct epoll_event *events,
int maxevents,
const struct timespec *timeout,
const sigset_t *sigset) {
# if defined(__NR_epoll_pwait2) && HAVE_LINUX_TIME_TYPES_H
if (timeout) {
/* Convert from userspace timespec to kernel timespec */
struct __kernel_timespec ts = {
.tv_sec = timeout->tv_sec,
.tv_nsec = timeout->tv_nsec,
};
return syscall(__NR_epoll_pwait2, fd, events, maxevents, &ts, sigset, sigset ? KERNEL_NSIG_BYTES : 0);
} else
return syscall(__NR_epoll_pwait2, fd, events, maxevents, NULL, sigset, sigset ? KERNEL_NSIG_BYTES : 0);
# else
errno = ENOSYS;
return -1;
# endif
}
# define epoll_pwait2 missing_epoll_pwait2
#endif
/* ======================================================================= */
#if !HAVE_MOUNT_SETATTR
#if !HAVE_STRUCT_MOUNT_ATTR
@ -593,6 +569,46 @@ static inline int missing_move_mount(
/* ======================================================================= */
#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_STRING
#define FSCONFIG_SET_STRING 1 /* Set parameter, supplying a string value */
#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_GETDENTS64
static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {

View file

@ -256,6 +256,26 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
return 0;
}
int parse_sector_size(const char *t, uint64_t *ret) {
int r;
assert(t);
assert(ret);
uint64_t ss;
r = safe_atou64(t, &ss);
if (r < 0)
return log_error_errno(r, "Failed to parse sector size parameter %s", t);
if (ss < 512 || ss > 4096) /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Sector size not between 512 and 4096: %s", t);
if (!ISPOWEROF2(ss))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size not power of 2: %s", t);
*ret = ss;
return 0;
}
int parse_range(const char *t, unsigned *lower, unsigned *upper) {
_cleanup_free_ char *word = NULL;
unsigned l, u;

View file

@ -18,6 +18,7 @@ int parse_ifindex(const char *s);
int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size);
int parse_sector_size(const char *t, uint64_t *ret);
int parse_range(const char *t, unsigned *lower, unsigned *upper);
int parse_errno(const char *t);

View file

@ -519,17 +519,17 @@ char* path_extend_internal(char **x, ...) {
va_list ap;
bool slash;
/* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
* already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
* removed. The string returned is hence always equal to or longer than the sum of the lengths of each
* individual string.
/* Joins all listed strings until the sentinel and places a "/" between them unless the strings
* end/begin already with one so that it is unnecessary. Note that slashes which are already
* duplicate won't be removed. The string returned is hence always equal to or longer than the sum of
* the lengths of the individual strings.
*
* The first argument may be an already allocated string that is extended via realloc() if
* non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the
* first parameter to distinguish the two operations.
*
* Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
* are optional.
* Note: any listed empty string is simply skipped. This can be useful for concatenating strings of
* which some are optional.
*
* Examples:
*
@ -587,7 +587,7 @@ char* path_extend_internal(char **x, ...) {
}
static int check_x_access(const char *path, int *ret_fd) {
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
int r;
/* We need to use O_PATH because there may be executables for which we have only exec
@ -615,7 +615,7 @@ static int check_x_access(const char *path, int *ret_fd) {
}
static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *path_name = NULL;
int r;
@ -1164,31 +1164,35 @@ bool path_is_normalized(const char *p) {
return true;
}
char *file_in_same_dir(const char *path, const char *filename) {
char *e, *ret;
size_t k;
int file_in_same_dir(const char *path, const char *filename, char **ret) {
_cleanup_free_ char *b = NULL;
int r;
assert(path);
assert(filename);
assert(ret);
/* This removes the last component of path and appends
* filename, unless the latter is absolute anyway or the
* former isn't */
/* This removes the last component of path and appends filename, unless the latter is absolute anyway
* or the former isn't */
if (path_is_absolute(filename))
return strdup(filename);
b = strdup(filename);
else {
_cleanup_free_ char *dn = NULL;
e = strrchr(path, '/');
if (!e)
return strdup(filename);
r = path_extract_directory(path, &dn);
if (r == -EDESTADDRREQ) /* no path prefix */
b = strdup(filename);
else if (r < 0)
return r;
else
b = path_join(dn, filename);
}
if (!b)
return -ENOMEM;
k = strlen(filename);
ret = new(char, (e + 1 - path) + k + 1);
if (!ret)
return NULL;
memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
return ret;
*ret = TAKE_PTR(b);
return 0;
}
bool hidden_or_backup_file(const char *filename) {

View file

@ -130,6 +130,7 @@ int fsck_exists_for_fstype(const char *fstype);
/* Similar to path_join(), but only works for two components, and only the first one may be NULL and returns
* an alloca() buffer, or possibly a const pointer into the path parameter. */
/* DEPRECATED: use path_join() instead */
#define prefix_roota(root, path) \
({ \
const char* _path = (path), *_root = (root), *_ret; \
@ -169,7 +170,7 @@ static inline bool path_is_safe(const char *p) {
}
bool path_is_normalized(const char *p) _pure_;
char *file_in_same_dir(const char *path, const char *filename);
int file_in_same_dir(const char *path, const char *filename, char **ret);
bool hidden_or_backup_file(const char *filename) _pure_;

View file

@ -253,7 +253,7 @@ int prioq_remove(Prioq *q, void *data, unsigned *idx) {
return 1;
}
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
void prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
unsigned k;
@ -261,12 +261,11 @@ int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
i = find_item(q, data, idx);
if (!i)
return 0;
return;
k = i - q->items;
k = shuffle_down(q, k);
shuffle_up(q, k);
return 1;
}
void *prioq_peek_by_index(Prioq *q, unsigned idx) {

View file

@ -18,7 +18,7 @@ int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
int prioq_put(Prioq *q, void *data, unsigned *idx);
int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx);
int prioq_remove(Prioq *q, void *data, unsigned *idx);
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
void prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
void *prioq_peek_by_index(Prioq *q, unsigned idx) _pure_;
static inline void *prioq_peek(Prioq *q) {

View file

@ -8,7 +8,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
@ -22,19 +21,25 @@
#include "alloc-util.h"
#include "architecture.h"
#include "argv-util.h"
#include "env-file.h"
#include "env-util.h"
#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
#include "missing_sched.h"
#include "missing_syscall.h"
#include "mountpoint-util.h"
#include "namespace-util.h"
#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "raw-clone.h"
@ -253,151 +258,47 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
return 0;
}
static int update_argv(const char name[], size_t l) {
static int can_do = -1;
if (can_do == 0)
return 0;
can_do = false; /* We'll set it to true only if the whole process works */
/* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the
* CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is
* present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if
* PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but
* mmap() is not. */
if (geteuid() != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
"Skipping PR_SET_MM, as we don't have privileges.");
static size_t mm_size = 0;
static char *mm = NULL;
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;
pid_t leader;
int r;
if (mm_size < l+1) {
size_t nn_size;
char *nn;
assert(machine);
assert(pid);
nn_size = PAGE_ALIGN(l+1);
nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (nn == MAP_FAILED)
return log_debug_errno(errno, "mmap() failed: %m");
strncpy(nn, name, nn_size);
/* Now, let's tell the kernel about this new memory */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
if (ERRNO_IS_PRIVILEGE(errno))
return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
/* HACK: prctl() API is kind of dumb on this point. The existing end address may already be
* below the desired start address, in which case the kernel may have kicked this back due
* to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
* action). The proper solution would be to have a prctl() API that could set both start+end
* simultaneously, or at least let us query the existing address to anticipate this condition
* and respond accordingly. For now, we can only guess at the cause of this failure and try
* a workaround--which will briefly expand the arg space to something potentially huge before
* resizing it to what we want. */
log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
(void) munmap(nn, nn_size);
return r;
}
if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0)
return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
} else {
/* And update the end pointer to the new end, too. If this fails, we don't really know what
* to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
* and continue. */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
}
if (mm)
(void) munmap(mm, mm_size);
mm = nn;
mm_size = nn_size;
} else {
strncpy(mm, name, mm_size);
/* Update the end pointer, continuing regardless of any failure. */
if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0)
log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
if (streq(machine, ".host")) {
*pid = 1;
return 0;
}
can_do = true;
if (!hostname_is_valid(machine, 0))
return -EINVAL;
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(NULL, p,
"LEADER", &s,
"CLASS", &class);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
return r;
if (!s)
return -EIO;
if (!streq_ptr(class, "container"))
return -EIO;
r = parse_pid(s, &leader);
if (r < 0)
return r;
if (leader <= 1)
return -EIO;
*pid = leader;
return 0;
}
int rename_process(const char name[]) {
bool truncated = false;
/* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
* internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
* many cases one of 10 (i.e. length of "/sbin/init") however if we have CAP_SYS_RESOURCES it is unbounded;
* to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
* truncated.
*
* Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
if (isempty(name))
return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
if (!is_main_thread())
return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
* cache things without locking, and we make assumptions that PR_SET_NAME sets the
* process name that isn't correct on any other threads */
size_t l = strlen(name);
/* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
* can use PR_SET_NAME, which sets the thread name for the calling thread. */
if (prctl(PR_SET_NAME, name) < 0)
log_debug_errno(errno, "PR_SET_NAME failed: %m");
if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
truncated = true;
/* Second step, change glibc's ID of the process name. */
if (program_invocation_name) {
size_t k;
k = strlen(program_invocation_name);
strncpy(program_invocation_name, name, k);
if (l > k)
truncated = true;
}
/* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
* has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
* the end. This is the best option for changing /proc/self/cmdline. */
(void) update_argv(name, l);
/* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
* it still looks here */
if (saved_argc > 0) {
if (saved_argv[0]) {
size_t k;
k = strlen(saved_argv[0]);
strncpy(saved_argv[0], name, k);
if (l > k)
truncated = true;
}
for (int i = 1; i < saved_argc; i++) {
if (!saved_argv[i])
break;
memzero(saved_argv[i], strlen(saved_argv[i]));
}
}
return !truncated;
}
int is_kernel_thread(pid_t pid) {
_cleanup_free_ char *line = NULL;
unsigned long long flags;
@ -864,6 +765,23 @@ void sigterm_wait(pid_t pid) {
(void) wait_for_terminate(pid, NULL);
}
void sigkill_nowait(pid_t pid) {
assert(pid > 1);
(void) kill(pid, SIGKILL);
}
void sigkill_nowaitp(pid_t *pid) {
PROTECT_ERRNO;
if (!pid)
return;
if (*pid <= 1)
return;
sigkill_nowait(*pid);
}
int kill_and_sigcont(pid_t pid, int sig) {
int r;
@ -1352,15 +1270,26 @@ int safe_fork_full(
}
if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) {
/* Optionally, make sure we never propagate mounts to the host. */
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m");
_exit(EXIT_FAILURE);
}
}
if (FLAGS_SET(flags, FORK_PRIVATE_TMP)) {
assert(FLAGS_SET(flags, FORK_NEW_MOUNTNS));
/* Optionally, overmount new tmpfs instance on /tmp/. */
r = mount_nofollow("tmpfs", "/tmp", "tmpfs",
MS_NOSUID|MS_NODEV,
"mode=01777" TMPFS_LIMITS_RUN);
if (r < 0) {
log_full_errno(prio, r, "Failed to overmount /tmp/: %m");
_exit(EXIT_FAILURE);
}
}
if (flags & FORK_CLOSE_ALL_FDS) {
/* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
log_close();
@ -1372,6 +1301,14 @@ int safe_fork_full(
}
}
if (flags & FORK_CLOEXEC_OFF) {
r = fd_cloexec_many(except_fds, n_except_fds, false);
if (r < 0) {
log_full_errno(prio, r, "Failed to turn off O_CLOEXEC on file descriptors: %m");
_exit(EXIT_FAILURE);
}
}
/* When we were asked to reopen the logs, do so again now */
if (flags & FORK_REOPEN_LOG) {
log_open();
@ -1519,6 +1456,20 @@ int pidfd_get_pid(int fd, pid_t *ret) {
return parse_pid(p, ret);
}
int pidfd_verify_pid(int pidfd, pid_t pid) {
pid_t current_pid;
int r;
assert(pidfd >= 0);
assert(pid > 0);
r = pidfd_get_pid(pidfd, &current_pid);
if (r < 0)
return r;
return current_pid != pid ? -ESRCH : 0;
}
static int rlimit_to_nice(rlim_t limit) {
if (limit <= 1)
return PRIO_MAX-1; /* i.e. 19 */
@ -1575,40 +1526,6 @@ int setpriority_closest(int priority) {
return 0;
}
bool invoked_as(char *argv[], const char *token) {
if (!argv || isempty(argv[0]))
return false;
if (isempty(token))
return false;
return strstr(last_path_component(argv[0]), token);
}
bool invoked_by_systemd(void) {
int r;
/* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
* or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
const char *e = getenv("SYSTEMD_EXEC_PID");
if (!e)
return false;
if (streq(e, "*"))
/* For testing. */
return true;
pid_t p;
r = parse_pid(e, &p);
if (r < 0) {
/* We know that systemd sets the variable correctly. Something else must have set it. */
log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
return false;
}
return getpid_cached() == p;
}
_noreturn_ void freeze(void) {
log_close();
@ -1630,31 +1547,6 @@ _noreturn_ void freeze(void) {
pause();
}
bool argv_looks_like_help(int argc, char **argv) {
char **l;
/* Scans the command line for indications the user asks for help. This is supposed to be called by
* tools that do not implement getopt() style command line parsing because they are not primarily
* user-facing. Detects four ways of asking for help:
*
* 1. Passing zero arguments
* 2. Passing "help" as first argument
* 3. Passing --help as any argument
* 4. Passing -h as any argument
*/
if (argc <= 1)
return true;
if (streq_ptr(argv[1], "help"))
return true;
l = strv_skip(argv, 1);
return strv_contains(l, "--help") ||
strv_contains(l, "-h");
}
static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited",
[CLD_KILLED] = "killed",

View file

@ -50,6 +50,8 @@ int get_process_environ(pid_t pid, char **ret);
int get_process_ppid(pid_t pid, pid_t *ret);
int get_process_umask(pid_t pid, mode_t *ret);
int container_get_leader(const char *machine, pid_t *pid);
int wait_for_terminate(pid_t pid, siginfo_t *status);
typedef enum WaitFlags {
@ -66,10 +68,11 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
void sigkill_wait(pid_t pid);
void sigkill_waitp(pid_t *pid);
void sigterm_wait(pid_t pid);
void sigkill_nowait(pid_t pid);
void sigkill_nowaitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
int rename_process(const char name[]);
int is_kernel_thread(pid_t pid);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
@ -146,10 +149,12 @@ typedef enum ForkFlags {
FORK_WAIT = 1 << 7, /* Wait until child exited */
FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */
FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */
FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */
FORK_FLUSH_STDIO = 1 << 12, /* fflush() stdout (and stderr) before forking */
FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */
FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */
FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace */
FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
@ -175,23 +180,12 @@ int get_oom_score_adjust(int *ret);
assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */
#define TAKE_PID(pid) \
({ \
pid_t *_ppid_ = &(pid); \
pid_t _pid_ = *_ppid_; \
*_ppid_ = 0; \
_pid_; \
})
/* Like TAKE_PTR() but for pid_t, resetting them to 0 */
#define TAKE_PID(pid) TAKE_GENERIC(pid, pid_t, 0)
int pidfd_get_pid(int fd, pid_t *ret);
int pidfd_verify_pid(int pidfd, pid_t pid);
int setpriority_closest(int priority);
bool invoked_as(char *argv[], const char *token);
bool invoked_by_systemd(void);
_noreturn_ void freeze(void);
bool argv_looks_like_help(int argc, char **argv);

View file

@ -75,7 +75,7 @@ static void fallback_random_bytes(void *p, size_t n) {
void random_bytes(void *p, size_t n) {
static bool have_getrandom = true, have_grndinsecure = true;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
if (n == 0)
return;
@ -117,7 +117,7 @@ void random_bytes(void *p, size_t n) {
int crypto_random_bytes(void *p, size_t n) {
static bool have_getrandom = true, seen_initialized = false;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
if (n == 0)
return 0;
@ -145,7 +145,7 @@ int crypto_random_bytes(void *p, size_t n) {
}
if (!seen_initialized) {
_cleanup_close_ int ready_fd = -1;
_cleanup_close_ int ready_fd = -EBADF;
int r;
ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
@ -187,7 +187,7 @@ size_t random_pool_size(void) {
}
int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
_cleanup_close_ int opened_fd = -1;
_cleanup_close_ int opened_fd = -EBADF;
int r;
assert(seed || size == 0);

View file

@ -23,6 +23,7 @@ static inline uint32_t random_u32(void) {
/* Some limits on the pool sizes when we deal with the kernel random pool */
#define RANDOM_POOL_SIZE_MIN 32U
#define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
#define RANDOM_EFI_SEED_SIZE 32U
size_t random_pool_size(void);

View file

@ -10,6 +10,7 @@
bool ratelimit_below(RateLimit *r) {
usec_t ts;
bool good = false;
assert(r);
@ -24,18 +25,12 @@ bool ratelimit_below(RateLimit *r) {
/* Reset counter */
r->num = 0;
goto good;
}
if (r->num < r->burst)
goto good;
good = true;
} else if (r->num < r->burst)
good = true;
r->num++;
return false;
good:
r->num++;
return true;
return good;
}
unsigned ratelimit_num_dropped(RateLimit *r) {

View file

@ -5,6 +5,7 @@
#include "errno-util.h"
#include "macro.h"
#include "missing_syscall.h"
#include "parse-util.h"
#include "signal-util.h"
#include "stdio-util.h"
@ -282,3 +283,20 @@ int pop_pending_signal_internal(int sig, ...) {
return r; /* Returns the signal popped */
}
void propagate_signal(int sig, siginfo_t *siginfo) {
pid_t p;
/* 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()
* earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not
* performance sensitive code.
*
* Note that we use kill() rather than raise() as fallback, for similar reasons. */
p = raw_getpid();
if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
assert_se(kill(p, sig) >= 0);
}

View file

@ -65,3 +65,5 @@ int signal_is_blocked(int sig);
int pop_pending_signal_internal(int sig, ...);
#define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1)
void propagate_signal(int sig, siginfo_t *siginfo);

View file

@ -1049,7 +1049,7 @@ ssize_t receive_one_fd_iov(
if (found)
*ret_fd = *(int*) CMSG_DATA(found);
else
*ret_fd = -1;
*ret_fd = -EBADF;
return k;
}
@ -1426,7 +1426,7 @@ int socket_get_mtu(int fd, int af, size_t *ret) {
}
int connect_unix_path(int fd, int dir_fd, const char *path) {
_cleanup_close_ int inode_fd = -1;
_cleanup_close_ int inode_fd = -EBADF;
union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
};
@ -1472,3 +1472,71 @@ int connect_unix_path(int fd, int dir_fd, const char *path) {
return RET_NERRNO(connect(fd, &sa.sa, salen));
}
int socket_address_parse_unix(SocketAddress *ret_address, const char *s) {
struct sockaddr_un un;
int r;
assert(ret_address);
assert(s);
if (!IN_SET(*s, '/', '@'))
return -EPROTO;
r = sockaddr_un_set_path(&un, s);
if (r < 0)
return r;
*ret_address = (SocketAddress) {
.sockaddr.un = un,
.size = r,
};
return 0;
}
int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) {
/* AF_VSOCK socket in vsock:cid:port notation */
_cleanup_free_ char *n = NULL;
char *e, *cid_start;
unsigned port, cid;
int r;
assert(ret_address);
assert(s);
cid_start = startswith(s, "vsock:");
if (!cid_start)
return -EPROTO;
e = strchr(cid_start, ':');
if (!e)
return -EINVAL;
r = safe_atou(e+1, &port);
if (r < 0)
return r;
n = strndup(cid_start, e - cid_start);
if (!n)
return -ENOMEM;
if (isempty(n))
cid = VMADDR_CID_ANY;
else {
r = safe_atou(n, &cid);
if (r < 0)
return r;
}
*ret_address = (SocketAddress) {
.sockaddr.vm = {
.svm_cid = cid,
.svm_family = AF_VSOCK,
.svm_port = port,
},
.size = sizeof(struct sockaddr_vm),
};
return 0;
}

View file

@ -175,15 +175,17 @@ int flush_accept(int fd);
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
#define CMSG_TYPED_DATA(cmsg, type) \
({ \
struct cmsghdr *_cmsg = cmsg; \
_cmsg ? CAST_ALIGN_PTR(type, CMSG_DATA(_cmsg)) : (type*) NULL; \
})
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
/* Type-safe, dereferencing version of cmsg_find() */
#define CMSG_FIND_DATA(mh, level, type, ctype) \
({ \
struct cmsghdr *_found; \
_found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \
(ctype*) (_found ? CMSG_DATA(_found) : NULL); \
})
#define CMSG_FIND_DATA(mh, level, type, ctype) \
CMSG_TYPED_DATA(cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))), ctype)
/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type
* itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr"
@ -334,3 +336,9 @@ int socket_get_mtu(int fd, int af, size_t *ret);
#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }
int connect_unix_path(int fd, int dir_fd, const char *path);
/* Parses AF_UNIX and AF_VSOCK addresses. AF_INET[6] require some netlink calls, so it cannot be in
* src/basic/ and is done from 'socket_local_address from src/shared/. Return -EPROTO in case of
* protocol mismatch. */
int socket_address_parse_unix(SocketAddress *ret_address, const char *s);
int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);

View file

@ -15,6 +15,7 @@
#include "fileio.h"
#include "filesystems.h"
#include "fs-util.h"
#include "hash-funcs.h"
#include "macro.h"
#include "missing_fs.h"
#include "missing_magic.h"
@ -64,7 +65,7 @@ int is_device_node(const char *path) {
}
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
struct dirent *buf;
size_t m;
@ -162,26 +163,37 @@ int null_or_empty_fd(int fd) {
return null_or_empty(&st);
}
int path_is_read_only_fs(const char *path) {
static int fd_is_read_only_fs(int fd) {
struct statvfs st;
assert(path);
assert(fd >= 0);
if (statvfs(path, &st) < 0)
if (fstatvfs(fd, &st) < 0)
return -errno;
if (st.f_flag & ST_RDONLY)
return true;
/* On NFS, statvfs() might not reflect whether we can actually
* write to the remote share. Let's try again with
* access(W_OK) which is more reliable, at least sometimes. */
if (access(path, W_OK) < 0 && errno == EROFS)
/* On NFS, fstatvfs() might not reflect whether we can actually write to the remote share. Let's try
* again with access(W_OK) which is more reliable, at least sometimes. */
if (access_fd(fd, W_OK) == -EROFS)
return true;
return false;
}
int path_is_read_only_fs(const char *path) {
_cleanup_close_ int fd = -EBADF;
assert(path);
fd = open(path, O_CLOEXEC | O_PATH);
if (fd < 0)
return -errno;
return fd_is_read_only_fs(fd);
}
int files_same(const char *filea, const char *fileb, int flags) {
struct stat a, b;
@ -441,3 +453,20 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
return 0;
}
void inode_hash_func(const struct stat *q, struct siphash *state) {
siphash24_compress(&q->st_dev, sizeof(q->st_dev), state);
siphash24_compress(&q->st_ino, sizeof(q->st_ino), state);
}
int inode_compare_func(const struct stat *a, const struct stat *b) {
int r;
r = CMP(a->st_dev, b->st_dev);
if (r != 0)
return r;
return CMP(a->st_ino, b->st_ino);
}
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);

View file

@ -11,6 +11,7 @@
#include "macro.h"
#include "missing_stat.h"
#include "siphash24.h"
int is_symlink(const char *path);
int is_dir_full(int atfd, const char *fname, bool follow);
@ -96,3 +97,7 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
struct new_statx nsx; \
} var
#endif
void inode_hash_func(const struct stat *q, struct siphash *state);
int inode_compare_func(const struct stat *a, const struct stat *b);
extern const struct hash_ops inode_hash_ops;

View file

@ -7,15 +7,20 @@
#include <sys/types.h>
#include "macro.h"
#include "memory-util.h"
#define snprintf_ok(buf, len, fmt, ...) \
({ \
char *_buf = (buf); \
size_t _len = (len); \
int _snpf = snprintf(_buf, _len, (fmt), ##__VA_ARGS__); \
_snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \
})
_printf_(3, 4)
static inline char *snprintf_ok(char *buf, size_t len, const char *format, ...) {
va_list ap;
int r;
va_start(ap, format);
DISABLE_WARNING_FORMAT_NONLITERAL;
r = vsnprintf(buf, len, format, ap);
REENABLE_WARNING;
va_end(ap);
return r >= 0 && (size_t) r < len ? buf : NULL;
}
#define xsprintf(buf, fmt, ...) \
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
@ -26,7 +31,7 @@ do { \
size_t _i, _k; \
/* See https://github.com/google/sanitizers/issues/992 */ \
if (HAS_FEATURE_MEMORY_SANITIZER) \
zero(_argtypes); \
memset(_argtypes, 0, sizeof(_argtypes)); \
_k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
assert(_k < ELEMENTSOF(_argtypes)); \
for (_i = 0; _i < _k; _i++) { \

View file

@ -18,7 +18,6 @@
#include "strv.h"
#include "terminal-util.h"
#include "utf8.h"
#include "util.h"
char* first_word(const char *s, const char *word) {
size_t sl, wl;
@ -1188,6 +1187,49 @@ char *string_replace_char(char *str, char old_char, char new_char) {
return str;
}
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
char *b;
assert(s || n == 0);
assert(mode >= 0);
assert(mode < _MAKE_CSTRING_MODE_MAX);
/* Converts a sized character buffer into a NUL-terminated NUL string, refusing if there are embedded
* NUL bytes. Whether to expect a trailing NUL byte can be specified via 'mode' */
if (n == 0) {
if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
return -EINVAL;
if (!ret)
return 0;
b = new0(char, 1);
} else {
const char *nul;
nul = memchr(s, 0, n);
if (nul) {
if (nul < s + n - 1 || /* embedded NUL? */
mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
return -EINVAL;
n--;
} else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
return -EINVAL;
if (!ret)
return 0;
b = memdup_suffix0(s, n);
}
if (!b)
return -ENOMEM;
*ret = b;
return 0;
}
size_t strspn_from_end(const char *str, const char *accept) {
size_t n = 0;
@ -1202,3 +1244,19 @@ size_t strspn_from_end(const char *str, const char *accept) {
return n;
}
char *strdupspn(const char *a, const char *accept) {
if (isempty(a) || isempty(accept))
return strdup("");
return strndup(a, strspn(a, accept));
}
char *strdupcspn(const char *a, const char *reject) {
if (isempty(a))
return strdup("");
if (isempty(reject))
return strdup(a);
return strndup(a, strcspn(a, reject));
}

View file

@ -53,9 +53,13 @@ static inline const char* enable_disable(bool b) {
return b ? "enable" : "disable";
}
static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p;
}
/* This macro's return pointer will have the "const" qualifier set or unset the same way as the input
* pointer. */
#define empty_to_null(p) \
({ \
const char *_p = (p); \
(typeof(p)) (isempty(_p) ? NULL : _p); \
})
static inline const char *empty_to_na(const char *p) {
return isempty(p) ? "n/a" : p;
@ -74,6 +78,11 @@ static inline bool empty_or_dash(const char *str) {
static inline const char *empty_or_dash_to_null(const char *p) {
return empty_or_dash(p) ? NULL : p;
}
#define empty_or_dash_to_null(p) \
({ \
const char *_p = (p); \
(typeof(p)) (empty_or_dash(_p) ? NULL : _p); \
})
char *first_word(const char *s, const char *word) _pure_;
@ -230,4 +239,17 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
char *string_replace_char(char *str, char old_char, char new_char);
typedef enum MakeCStringMode {
MAKE_CSTRING_REFUSE_TRAILING_NUL,
MAKE_CSTRING_ALLOW_TRAILING_NUL,
MAKE_CSTRING_REQUIRE_TRAILING_NUL,
_MAKE_CSTRING_MODE_MAX,
_MAKE_CSTRING_MODE_INVALID = -1,
} MakeCStringMode;
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret);
size_t strspn_from_end(const char *str, const char *accept);
char *strdupspn(const char *a, const char *accept);
char *strdupcspn(const char *a, const char *reject);

View file

@ -623,121 +623,6 @@ char** strv_remove(char **l, const char *s) {
return l;
}
char** strv_parse_nulstr(const char *s, size_t l) {
/* l is the length of the input data, which will be split at NULs into
* elements of the resulting strv. Hence, the number of items in the resulting strv
* will be equal to one plus the number of NUL bytes in the l bytes starting at s,
* unless s[l-1] is NUL, in which case the final empty string is not stored in
* the resulting strv, and length is equal to the number of NUL bytes.
*
* Note that contrary to a normal nulstr which cannot contain empty strings, because
* the input data is terminated by any two consequent NUL bytes, this parser accepts
* empty strings in s.
*/
size_t c = 0, i = 0;
char **v;
assert(s || l <= 0);
if (l <= 0)
return new0(char*, 1);
for (const char *p = s; p < s + l; p++)
if (*p == 0)
c++;
if (s[l-1] != 0)
c++;
v = new0(char*, c+1);
if (!v)
return NULL;
for (const char *p = s; p < s + l; ) {
const char *e;
e = memchr(p, 0, s + l - p);
v[i] = strndup(p, e ? e - p : s + l - p);
if (!v[i]) {
strv_free(v);
return NULL;
}
i++;
if (!e)
break;
p = e + 1;
}
assert(i == c);
return v;
}
char** strv_split_nulstr(const char *s) {
const char *i;
char **r = NULL;
NULSTR_FOREACH(i, s)
if (strv_extend(&r, i) < 0) {
strv_free(r);
return NULL;
}
if (!r)
return strv_new(NULL);
return r;
}
int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
/* A valid nulstr with two NULs at the end will be created, but
* q will be the length without the two trailing NULs. Thus the output
* string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
* and can also be parsed by strv_parse_nulstr as long as the length
* is provided separately.
*/
_cleanup_free_ char *m = NULL;
size_t n = 0;
assert(ret);
assert(ret_size);
STRV_FOREACH(i, l) {
size_t z;
z = strlen(*i);
if (!GREEDY_REALLOC(m, n + z + 2))
return -ENOMEM;
memcpy(m + n, *i, z + 1);
n += z + 1;
}
if (!m) {
m = new0(char, 1);
if (!m)
return -ENOMEM;
n = 1;
} else
/* make sure there is a second extra NUL at the end of resulting nulstr */
m[n] = '\0';
assert(n > 0);
*ret = m;
*ret_size = n - 1;
m = NULL;
return 0;
}
bool strv_overlap(char * const *a, char * const *b) {
STRV_FOREACH(i, a)
if (strv_contains(b, *i))
@ -904,6 +789,22 @@ rollback:
return -ENOMEM;
}
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) {
char *j;
assert(l);
assert(lhs);
if (!rhs) /* value is optional, in which case we suppress the field */
return 0;
j = strjoin(lhs, "=", rhs);
if (!j)
return -ENOMEM;
return strv_consume(l, j);
}
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
bool b = false;
int r;

View file

@ -45,7 +45,7 @@ static inline int strv_extend(char ***l, const char *value) {
return strv_extend_with_size(l, NULL, value);
}
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
int strv_extendf(char ***l, const char *format, ...) _printf_(2,3);
int strv_extend_front(char ***l, const char *value);
int strv_push_with_size(char ***l, size_t *n, char *value);
@ -124,20 +124,6 @@ static inline char *strv_join(char * const *l, const char *separator) {
return strv_join_full(l, separator, NULL, false);
}
char** strv_parse_nulstr(const char *s, size_t l);
char** strv_split_nulstr(const char *s);
int strv_make_nulstr(char * const *l, char **p, size_t *n);
static inline int strv_from_nulstr(char ***a, const char *nulstr) {
char **t;
t = strv_split_nulstr(nulstr);
if (!t)
return -ENOMEM;
*a = t;
return 0;
}
bool strv_overlap(char * const *a, char * const *b) _pure_;
#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \
@ -255,6 +241,8 @@ char** strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);
int strv_extend_assignment(char ***l, const char *lhs, const char *rhs);
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
#define strv_free_and_replace(a, b) \

View file

@ -308,54 +308,48 @@ char *format_timestamp_style(
};
struct tm tm;
bool utc, us;
time_t sec;
size_t n;
bool utc = false, us = false;
int r;
assert(buf);
assert(style >= 0);
assert(style < _TIMESTAMP_STYLE_MAX);
switch (style) {
case TIMESTAMP_PRETTY:
case TIMESTAMP_UNIX:
break;
case TIMESTAMP_US:
us = true;
break;
case TIMESTAMP_UTC:
utc = true;
break;
case TIMESTAMP_US_UTC:
us = true;
utc = true;
break;
default:
return NULL;
}
if (l < (size_t) (3 + /* week day */
1 + 10 + /* space and date */
1 + 8 + /* space and time */
(us ? 1 + 6 : 0) + /* "." and microsecond part */
1 + 1 + /* space and shortest possible zone */
1))
return NULL; /* Not enough space even for the shortest form. */
if (!timestamp_is_set(t))
return NULL; /* Timestamp is unset */
if (style == TIMESTAMP_UNIX) {
r = snprintf(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */
if (r < 0 || (size_t) r >= l)
return NULL; /* Doesn't fit */
if (l < (size_t) (1 + 1 + 1))
return NULL; /* not enough space for even the shortest of forms */
return buf;
return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */
}
utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
us = IN_SET(style, TIMESTAMP_US, TIMESTAMP_US_UTC);
if (l < (size_t) (3 + /* week day */
1 + 10 + /* space and date */
style == TIMESTAMP_DATE ? 0 :
(1 + 8 + /* space and time */
(us ? 1 + 6 : 0) + /* "." and microsecond part */
1 + (utc ? 3 : 1)) + /* space and shortest possible zone */
1))
return NULL; /* Not enough space even for the shortest form. */
/* Let's not format times with years > 9999 */
if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1);
strcpy(buf, "--- XXXX-XX-XX XX:XX:XX");
return buf;
static const char* const xxx[_TIMESTAMP_STYLE_MAX] = {
[TIMESTAMP_PRETTY] = "--- XXXX-XX-XX XX:XX:XX",
[TIMESTAMP_US] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX",
[TIMESTAMP_UTC] = "--- XXXX-XX-XX XX:XX:XX UTC",
[TIMESTAMP_US_UTC] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC",
[TIMESTAMP_DATE] = "--- XXXX-XX-XX",
};
assert(l >= strlen(xxx[style]) + 1);
return strcpy(buf, xxx[style]);
}
sec = (time_t) (t / USEC_PER_SEC); /* Round down */
@ -367,6 +361,14 @@ char *format_timestamp_style(
assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
memcpy(buf, weekdays[tm.tm_wday], 4);
if (style == TIMESTAMP_DATE) {
/* Special format string if only date should be shown. */
if (strftime(buf + 3, l - 3, " %Y-%m-%d", &tm) <= 0)
return NULL; /* Doesn't fit */
return buf;
}
/* Add the main components */
if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
return NULL; /* Doesn't fit */
@ -1395,7 +1397,7 @@ int get_timezones(char ***ret) {
int verify_timezone(const char *name, int log_level) {
bool slash = false;
const char *p, *t;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
char buf[4];
int r;
@ -1568,7 +1570,7 @@ int time_change_fd(void) {
.it_value.tv_sec = TIME_T_MAX,
};
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));

View file

@ -35,6 +35,7 @@ typedef enum TimestampStyle {
TIMESTAMP_UTC,
TIMESTAMP_US_UTC,
TIMESTAMP_UNIX,
TIMESTAMP_DATE,
_TIMESTAMP_STYLE_MAX,
_TIMESTAMP_STYLE_INVALID = -EINVAL,
} TimestampStyle;

View file

@ -19,29 +19,15 @@
#include "tmpfile-util.h"
#include "umask-util.h"
int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
static int fopen_temporary_internal(int dir_fd, const char *path, FILE **ret_file) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *t = NULL;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
int r;
if (path) {
r = tempfn_xxxxxx(path, NULL, &t);
if (r < 0)
return r;
} else {
const char *d;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
r = tmp_dir(&d);
if (r < 0)
return r;
t = path_join(d, "XXXXXX");
if (!t)
return -ENOMEM;
}
fd = mkostemp_safe(t);
fd = openat(dir_fd, path, O_CLOEXEC|O_NOCTTY|O_RDWR|O_CREAT|O_EXCL, 0600);
if (fd < 0)
return -errno;
@ -50,15 +36,59 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
r = take_fdopen_unlocked(&fd, "w", &f);
if (r < 0) {
(void) unlink(t);
(void) unlinkat(dir_fd, path, 0);
return r;
}
if (ret_f)
*ret_f = TAKE_PTR(f);
if (ret_file)
*ret_file = TAKE_PTR(f);
if (ret_temp_path)
*ret_temp_path = TAKE_PTR(t);
return 0;
}
int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
_cleanup_free_ char *t = NULL;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
r = tempfn_random(path, NULL, &t);
if (r < 0)
return r;
r = fopen_temporary_internal(dir_fd, t, ret_file);
if (r < 0)
return r;
if (ret_path)
*ret_path = TAKE_PTR(t);
return 0;
}
int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) {
_cleanup_free_ char *t = NULL;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
if (!path) {
r = tmp_dir(&path);
if (r < 0)
return r;
}
r = tempfn_random_child(path, NULL, &t);
if (r < 0)
return r;
r = fopen_temporary_internal(dir_fd, t, ret_file);
if (r < 0)
return r;
if (ret_path)
*ret_path = TAKE_PTR(t);
return 0;
}
@ -71,7 +101,7 @@ int mkostemp_safe(char *pattern) {
}
int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
FILE *f;
fd = mkostemp_safe(pattern);
@ -279,7 +309,7 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) {
_cleanup_free_ char *path = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
_cleanup_close_ int fd = -EBADF;
assert(target);
assert(ret_file);
@ -358,3 +388,23 @@ int mkdtemp_malloc(const char *template, char **ret) {
*ret = TAKE_PTR(p);
return 0;
}
int mkdtemp_open(const char *template, int flags, char **ret) {
_cleanup_free_ char *p = NULL;
int fd, r;
r = mkdtemp_malloc(template, &p);
if (r < 0)
return r;
fd = RET_NERRNO(open(p, O_DIRECTORY|O_CLOEXEC|flags));
if (fd < 0) {
(void) rmdir(p);
return fd;
}
if (ret)
*ret = TAKE_PTR(p);
return fd;
}

View file

@ -1,9 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <fcntl.h>
#include <stdio.h>
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path);
static inline int fopen_temporary(const char *path, FILE **ret_file, char **ret_path) {
return fopen_temporary_at(AT_FDCWD, path, ret_file, ret_path);
}
int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path);
static inline int fopen_temporary_child(const char *path, FILE **ret_file, char **ret_path) {
return fopen_temporary_child_at(AT_FDCWD, path, ret_file, ret_path);
}
int mkostemp_safe(char *pattern);
int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
@ -19,3 +29,4 @@ int link_tmpfile(int fd, const char *path, const char *target);
int flink_tmpfile(FILE *f, const char *path, const char *target);
int mkdtemp_malloc(const char *template, char **ret);
int mkdtemp_open(const char *template, int flags, char **ret);

View file

@ -15,12 +15,12 @@ static inline void umaskp(mode_t *u) {
/* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although
* mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to
* distinguish the first and the second iteration of the RUN_WITH_UMASK() loop, so that we can run the first
* one, and exit on the second. */
* distinguish the first and the second iteration of the WITH_UMASK() loop, so that we can run the first one,
* and exit on the second. */
assert_cc((S_IFMT & 0777) == 0);
#define RUN_WITH_UMASK(mask) \
#define WITH_UMASK(mask) \
for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
FLAGS_SET(_saved_umask_, S_IFMT); \
_saved_umask_ &= 0777)

View file

@ -1,174 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "alloc-util.h"
#include "build.h"
#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "util.h"
#include "virt.h"
int saved_argc = 0;
char **saved_argv = NULL;
static int saved_in_initrd = -1;
bool kexec_loaded(void) {
_cleanup_free_ char *s = NULL;
if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0)
return false;
return s[0] == '1';
}
int prot_from_flags(int flags) {
switch (flags & O_ACCMODE) {
case O_RDONLY:
return PROT_READ;
case O_WRONLY:
return PROT_WRITE;
case O_RDWR:
return PROT_READ|PROT_WRITE;
default:
return -EINVAL;
}
}
bool in_initrd(void) {
int r;
const char *e;
bool lenient = false;
if (saved_in_initrd >= 0)
return saved_in_initrd;
/* We have two checks here:
*
* 1. the flag file /etc/initrd-release must exist
* 2. the root file system must be a memory file system
*
* The second check is extra paranoia, since misdetecting an
* initrd can have bad consequences due the initrd
* emptying when transititioning to the main systemd.
*
* If env var $SYSTEMD_IN_INITRD is not set or set to "auto",
* both checks are used. If it's set to "lenient", only check
* 1 is used. If set to a boolean value, then the boolean
* value is returned.
*/
e = secure_getenv("SYSTEMD_IN_INITRD");
if (e) {
if (streq(e, "lenient"))
lenient = true;
else if (!streq(e, "auto")) {
r = parse_boolean(e);
if (r >= 0) {
saved_in_initrd = r > 0;
return saved_in_initrd;
}
log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
}
}
if (!lenient) {
r = path_is_temporary_fs("/");
if (r < 0)
log_debug_errno(r, "Couldn't determine if / is a temporary file system: %m");
saved_in_initrd = r > 0;
}
r = access("/etc/initrd-release", F_OK);
if (r >= 0) {
if (saved_in_initrd == 0)
log_debug("/etc/initrd-release exists, but it's not an initrd.");
else
saved_in_initrd = 1;
} else {
if (errno != ENOENT)
log_debug_errno(errno, "Failed to test if /etc/initrd-release exists: %m");
saved_in_initrd = 0;
}
return saved_in_initrd;
}
void in_initrd_force(bool value) {
saved_in_initrd = value;
}
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;
pid_t leader;
int r;
assert(machine);
assert(pid);
if (streq(machine, ".host")) {
*pid = 1;
return 0;
}
if (!hostname_is_valid(machine, 0))
return -EINVAL;
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(NULL, p,
"LEADER", &s,
"CLASS", &class);
if (r == -ENOENT)
return -EHOSTDOWN;
if (r < 0)
return r;
if (!s)
return -EIO;
if (!streq_ptr(class, "container"))
return -EIO;
r = parse_pid(s, &leader);
if (r < 0)
return r;
if (leader <= 1)
return -EIO;
*pid = leader;
return 0;
}
int version(void) {
printf("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n%s\n",
systemd_features);
return 0;
}
/* Turn off core dumps but only if we're running outside of a container. */
void disable_coredumps(void) {
int r;
if (detect_container() > 0)
return;
r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
}

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#ifndef SD_BOOT
#if !SD_BOOT
# include <assert.h>
#endif
@ -20,6 +20,7 @@
#define _hidden_ __attribute__((__visibility__("hidden")))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _malloc_ __attribute__((__malloc__))
#define _noinline_ __attribute__((noinline))
#define _noreturn_ _Noreturn
#define _packed_ __attribute__((__packed__))
#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
@ -66,18 +67,18 @@
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#ifdef SD_BOOT
#if SD_BOOT
_noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
#ifdef NDEBUG
#define assert(expr)
#define assert_not_reached() __builtin_unreachable()
#else
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
#define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
#define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
#endif
#define static_assert _Static_assert
#define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
#define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
#endif
/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
@ -251,47 +252,44 @@
(UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \
})
#define CASE_F(X) case X:
#define CASE_F_1(CASE, X) CASE_F(X)
#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__)
#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__)
#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__)
#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__)
#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__)
#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__)
#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__)
#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__)
#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__)
#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__)
#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__)
#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__)
#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__)
#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__)
#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__)
#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__)
#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__)
#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__)
#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__)
#define CASE_F_1(X) case X:
#define CASE_F_2(X, ...) case X: CASE_F_1( __VA_ARGS__)
#define CASE_F_3(X, ...) case X: CASE_F_2( __VA_ARGS__)
#define CASE_F_4(X, ...) case X: CASE_F_3( __VA_ARGS__)
#define CASE_F_5(X, ...) case X: CASE_F_4( __VA_ARGS__)
#define CASE_F_6(X, ...) case X: CASE_F_5( __VA_ARGS__)
#define CASE_F_7(X, ...) case X: CASE_F_6( __VA_ARGS__)
#define CASE_F_8(X, ...) case X: CASE_F_7( __VA_ARGS__)
#define CASE_F_9(X, ...) case X: CASE_F_8( __VA_ARGS__)
#define CASE_F_10(X, ...) case X: CASE_F_9( __VA_ARGS__)
#define CASE_F_11(X, ...) case X: CASE_F_10( __VA_ARGS__)
#define CASE_F_12(X, ...) case X: CASE_F_11( __VA_ARGS__)
#define CASE_F_13(X, ...) case X: CASE_F_12( __VA_ARGS__)
#define CASE_F_14(X, ...) case X: CASE_F_13( __VA_ARGS__)
#define CASE_F_15(X, ...) case X: CASE_F_14( __VA_ARGS__)
#define CASE_F_16(X, ...) case X: CASE_F_15( __VA_ARGS__)
#define CASE_F_17(X, ...) case X: CASE_F_16( __VA_ARGS__)
#define CASE_F_18(X, ...) case X: CASE_F_17( __VA_ARGS__)
#define CASE_F_19(X, ...) case X: CASE_F_18( __VA_ARGS__)
#define CASE_F_20(X, ...) case X: CASE_F_19( __VA_ARGS__)
#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
#define FOR_EACH_MAKE_CASE(...) \
GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \
CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
(CASE_F,__VA_ARGS__)
(__VA_ARGS__)
#define IN_SET(x, ...) \
#define IN_SET(x, first, ...) \
({ \
bool _found = false; \
/* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \
* type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
* the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \
* doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \
static const long double __assert_in_set[] _unused_ = { __VA_ARGS__ }; \
/* If the build breaks in the line below, you need to extend the case macros. We use typeof(+x) \
* here to widen the type of x if it is a bit-field as this would otherwise be illegal. */ \
static const typeof(+x) __assert_in_set[] _unused_ = { first, __VA_ARGS__ }; \
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
switch (x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
FOR_EACH_MAKE_CASE(first, __VA_ARGS__) \
_found = true; \
break; \
break; \
default: \
break; \
} \
@ -300,13 +298,18 @@
/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time
* resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
#define TAKE_PTR(ptr) \
({ \
typeof(ptr) *_pptr_ = &(ptr); \
typeof(ptr) _ptr_ = *_pptr_; \
*_pptr_ = NULL; \
_ptr_; \
#define TAKE_GENERIC(var, type, nullvalue) \
({ \
type *_pvar_ = &(var); \
type _var_ = *_pvar_; \
type _nullvalue_ = nullvalue; \
*_pvar_ = _nullvalue_; \
_var_; \
})
#define TAKE_PTR_TYPE(ptr, type) TAKE_GENERIC(ptr, type, NULL)
#define TAKE_PTR(ptr) TAKE_PTR_TYPE(ptr, typeof(ptr))
#define TAKE_STRUCT_TYPE(s, type) TAKE_GENERIC(s, type, {})
#define TAKE_STRUCT(s) TAKE_STRUCT_TYPE(s, typeof(s))
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
@ -330,14 +333,23 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
return ((l + ali - 1) & ~(ali - 1));
}
#define ALIGN2(l) ALIGN_TO(l, 2)
#define ALIGN4(l) ALIGN_TO(l, 4)
#define ALIGN8(l) ALIGN_TO(l, 8)
#ifndef SD_BOOT
#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
#if !SD_BOOT
/* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */
#define ALIGN(l) ALIGN_TO(l, sizeof(void*))
#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
#endif
/* Checks if the specified pointer is aligned as appropriate for the specific type */
#define IS_ALIGNED16(p) (((uintptr_t) p) % __alignof__(uint16_t) == 0)
#define IS_ALIGNED32(p) (((uintptr_t) p) % __alignof__(uint32_t) == 0)
#define IS_ALIGNED64(p) (((uintptr_t) p) % __alignof__(uint64_t) == 0)
/* Same as ALIGN_TO but callable in constant contexts. */
#define CONST_ALIGN_TO(l, ali) \
__builtin_choose_expr( \
@ -348,9 +360,31 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
((l) + (ali) - 1) & ~((ali) - 1), \
VOID_0)
/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
* alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
* warning or if you want to assert that the cast gives a pointer of suitable alignment. */
#define CAST_ALIGN_PTR(t, p) \
({ \
const void *_p = (p); \
assert(((uintptr_t) _p) % __alignof__(t) == 0); \
(t *) _p; \
})
#define UPDATE_FLAG(orig, flag, b) \
((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
#define SET_FLAG(v, flag, b) \
(v) = UPDATE_FLAG(v, flag, b)
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)
/* Declare a flexible array usable in a union.
* This is essentially a work-around for a pointless constraint in C99
* and might go away in some future version of the standard.
*
* See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3080ea5553cc909b000d1f1d964a9041962f2c5b
*/
#define DECLARE_FLEX_ARRAY(type, name) \
struct { \
dummy_t __empty__ ## name; \
type name[]; \
}

View file

@ -0,0 +1,66 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stddef.h>
#if SD_BOOT
# include "efi-string.h"
#else
# include <string.h>
#endif
#include "macro-fundamental.h"
#if !SD_BOOT && HAVE_EXPLICIT_BZERO
static inline void *explicit_bzero_safe(void *p, size_t l) {
if (p && l > 0)
explicit_bzero(p, l);
return p;
}
#else
static inline void *explicit_bzero_safe(void *p, size_t l) {
if (p && l > 0) {
memset(p, 0, l);
__asm__ __volatile__("" : : "r"(p) : "memory");
}
return p;
}
#endif
struct VarEraser {
/* NB: This is a pointer to memory to erase in case of CLEANUP_ERASE(). Pointer to pointer to memory
* to erase in case of CLEANUP_ERASE_PTR() */
void *p;
size_t size;
};
static inline void erase_var(struct VarEraser *e) {
explicit_bzero_safe(e->p, e->size);
}
/* Mark var to be erased when leaving scope. */
#define CLEANUP_ERASE(var) \
_cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
.p = &(var), \
.size = sizeof(var), \
}
static inline void erase_varp(struct VarEraser *e) {
/* Very similar to erase_var(), but assumes `p` is a pointer to a pointer whose memory shall be destructed. */
if (!e->p)
return;
explicit_bzero_safe(*(void**) e->p, e->size);
}
/* Mark pointer so that memory pointed to is erased when leaving scope. Note: this takes a pointer to the
* specified pointer, instead of just a copy of it. This is to allow callers to invalidate the pointer after
* use, if they like, disabling our automatic erasure (for example because they succeeded with whatever they
* wanted to do and now intend to return the allocated buffer to their caller without it being erased). */
#define CLEANUP_ERASE_PTR(ptr, sz) \
_cleanup_(erase_varp) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \
.p = (ptr), \
.size = (sz), \
}

View file

@ -22,7 +22,7 @@
<https://www.gnu.org/licenses/>. */
#include <stdbool.h>
#ifdef SD_BOOT
#if SD_BOOT
# include "efi-string.h"
#else
# include <string.h>
@ -30,6 +30,7 @@
#include "macro-fundamental.h"
#include "sha256.h"
#include "unaligned-fundamental.h"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define SWAP(n) \
@ -48,14 +49,6 @@
# define SWAP64(n) (n)
#endif
/* The condition below is from glibc's string/string-inline.c.
* See definition of _STRING_INLINE_unaligned. */
#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__)
# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__(uint32_t) != 0)
#else
# define UNALIGNED_P(p) false
#endif
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. (FIPS 180-2:5.1.1) */
static const uint8_t fillbuf[64] = {
@ -128,11 +121,7 @@ uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_
/* Put result from CTX in first 32 bytes following RESBUF. */
for (size_t i = 0; i < 8; ++i)
if (UNALIGNED_P(resbuf))
memcpy(resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
else
((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
unaligned_write_ne32(resbuf + i * sizeof(uint32_t), SWAP(ctx->H[i]));
return resbuf;
}
@ -165,18 +154,17 @@ void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx
/* Process available complete blocks. */
if (len >= 64) {
if (UNALIGNED_P(buffer))
if (IS_ALIGNED32(buffer)) {
sha256_process_block(buffer, len & ~63, ctx);
buffer = (const char *) buffer + (len & ~63);
len &= 63;
} else
while (len > 64) {
memcpy(ctx->buffer, buffer, 64);
sha256_process_block(ctx->buffer, 64, ctx);
buffer = (const char *) buffer + 64;
len -= 64;
}
else {
sha256_process_block(buffer, len & ~63, ctx);
buffer = (const char *) buffer + (len & ~63);
len &= 63;
}
}
/* Move remaining bytes into internal buffer. */

View file

@ -1,7 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "stdint.h"
#include <stddef.h>
#include <stdint.h>
#define SHA256_DIGEST_SIZE 32
@ -28,6 +29,11 @@ void sha256_init_ctx(struct sha256_ctx *ctx);
uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]);
void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx);
static inline void sha256_process_bytes_and_size(const void *buffer, size_t len, struct sha256_ctx *ctx) {
sha256_process_bytes(&len, sizeof(len), ctx);
sha256_process_bytes(buffer, len, ctx);
}
uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]);
#define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {})

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef SD_BOOT
#if !SD_BOOT
# include <ctype.h>
#endif
@ -20,7 +20,7 @@ sd_char *startswith(const sd_char *s, const sd_char *prefix) {
return (sd_char*) s + l;
}
#ifndef SD_BOOT
#if !SD_BOOT
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
size_t l;

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#ifdef SD_BOOT
#if SD_BOOT
# include <efi.h>
# include <efilib.h>
# include "efi-string.h"
@ -11,7 +11,7 @@
#include "macro-fundamental.h"
#ifdef SD_BOOT
#if SD_BOOT
# define strlen strlen16
# define strcmp strcmp16
# define strncmp strncmp16
@ -59,7 +59,7 @@ static inline size_t strlen_ptr(const sd_char *s) {
}
sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
#ifndef SD_BOOT
#if !SD_BOOT
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
#endif
sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
@ -110,6 +110,10 @@ static inline bool ascii_isdigit(sd_char a) {
return a >= '0' && a <= '9';
}
static inline bool ascii_ishex(sd_char a) {
return ascii_isdigit(a) || (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F');
}
static inline bool ascii_isalpha(sd_char a) {
/* A pure ASCII, locale independent version of isalpha() */
return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');

View file

@ -297,7 +297,6 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) {
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
_cleanup_free_ uint32_t *input = NULL;
size_t input_size, l;
const char *p;
bool contains_8bit = false;
char buffer[DNS_LABEL_MAX+1];
int r;
@ -314,7 +313,7 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
if (encoded_size <= 0)
return -EINVAL;
for (p = encoded; p < encoded + encoded_size; p++)
for (const char *p = encoded; p < encoded + encoded_size; p++)
if ((uint8_t) *p > 127)
contains_8bit = true;
@ -527,7 +526,18 @@ int dns_name_compare_func(const char *a, const char *b) {
}
}
DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
DEFINE_HASH_OPS(
dns_name_hash_ops,
char,
dns_name_hash_func,
dns_name_compare_func);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
dns_name_hash_ops_free,
char,
dns_name_hash_func,
dns_name_compare_func,
free);
int dns_name_equal(const char *x, const char *y) {
int r, q;
@ -745,9 +755,8 @@ int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_ad
return r;
if (r > 0) {
uint8_t a[4];
unsigned i;
for (i = 0; i < ELEMENTSOF(a); i++) {
for (size_t i = 0; i < ELEMENTSOF(a); i++) {
char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, label, sizeof label, 0);
@ -781,9 +790,8 @@ int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_ad
return r;
if (r > 0) {
struct in6_addr a;
unsigned i;
for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
for (size_t i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
char label[DNS_LABEL_MAX+1];
int x, y;
@ -824,7 +832,6 @@ int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_ad
}
bool dns_name_is_root(const char *name) {
assert(name);
/* There are exactly two ways to encode the root domain name:
@ -895,8 +902,6 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo
}
static bool srv_type_label_is_valid(const char *label, size_t n) {
size_t k;
assert(label);
if (n < 2) /* Label needs to be at least 2 chars long */
@ -910,12 +915,11 @@ static bool srv_type_label_is_valid(const char *label, size_t n) {
return false;
/* Third and further chars must be alphanumeric or a hyphen */
for (k = 2; k < n; k++) {
for (size_t k = 2; k < n; k++)
if (!ascii_isalpha(label[k]) &&
!ascii_isdigit(label[k]) &&
label[k] != '-')
return false;
}
return true;
}
@ -1111,14 +1115,12 @@ finish:
}
static int dns_name_build_suffix_table(const char *name, const char *table[]) {
const char *p;
const char *p = ASSERT_PTR(name);
unsigned n = 0;
int r;
assert(name);
assert(table);
p = name;
for (;;) {
if (n > DNS_N_LABELS_MAX)
return -EINVAL;

View file

@ -60,13 +60,10 @@ static inline int dns_name_is_valid_ldh(const char *s) {
return 1;
}
static inline bool dns_name_is_empty(const char *s) {
return isempty(s) || streq(s, ".");
}
void dns_name_hash_func(const char *s, struct siphash *state);
int dns_name_compare_func(const char *a, const char *b);
extern const struct hash_ops dns_name_hash_ops;
extern const struct hash_ops dns_name_hash_ops_free;
int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);