systemd: merge branch systemd into master

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/799
This commit is contained in:
Thomas Haller 2021-04-01 18:18:43 +02:00
commit 5f7f81a6a0
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
84 changed files with 2295 additions and 1996 deletions

View file

@ -16,7 +16,7 @@ typedef enum DUIDType {
DUID_TYPE_LL = 3,
DUID_TYPE_UUID = 4,
_DUID_TYPE_MAX,
_DUID_TYPE_INVALID = -1,
_DUID_TYPE_INVALID = -EINVAL,
} DUIDType;
/* RFC 3315 section 9.1:

View file

@ -40,11 +40,14 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
size_t total = 0;
char **s;
if (strv_isempty((char **) optval))
return -EINVAL;
STRV_FOREACH(s, (char **) optval) {
size_t len = strlen(*s);
if (len > 255)
return -ENAMETOOLONG;
if (len > 255 || len == 0)
return -EINVAL;
total += 1 + len;
}
@ -53,14 +56,13 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
return -ENOBUFS;
options[*offset] = code;
options[*offset + 1] = total;
options[*offset + 1] = total;
*offset += 2;
STRV_FOREACH(s, (char **) optval) {
size_t len = strlen(*s);
options[*offset] = len;
memcpy(&options[*offset + 1], *s, len);
*offset += 1 + len;
}
@ -80,11 +82,11 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
break;
case SD_DHCP_OPTION_VENDOR_SPECIFIC: {
OrderedHashmap *s = (OrderedHashmap *) optval;
OrderedSet *s = (OrderedSet *) optval;
struct sd_dhcp_option *p;
size_t l = 0;
ORDERED_HASHMAP_FOREACH(p, s)
ORDERED_SET_FOREACH(p, s)
l += p->length + 2;
if (*offset + l + 2 > size)
@ -95,7 +97,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
*offset += 2;
ORDERED_HASHMAP_FOREACH(p, s) {
ORDERED_SET_FOREACH(p, s) {
options[*offset] = p->option;
options[*offset + 1] = p->length;
memcpy(&options[*offset + 2], p->data, p->length);

View file

@ -97,10 +97,10 @@ typedef struct DHCP6IA DHCP6IA;
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix);
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);

View file

@ -43,14 +43,15 @@ typedef struct DHCP6PDPrefixOption {
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
size_t optlen) {
DHCP6Option *option = (DHCP6Option*) *buf;
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
DHCP6Option *option;
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
option = (DHCP6Option*) *buf;
if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
return -ENOBUFS;
@ -114,10 +115,13 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
}
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
uint16_t len;
uint8_t *ia_hdr;
size_t iaid_offset, ia_buflen, ia_addrlen = 0;
size_t ia_buflen, ia_addrlen = 0;
struct ia_na ia_na;
struct ia_ta ia_ta;
DHCP6Address *addr;
uint8_t *ia_hdr;
uint16_t len;
void *p;
int r;
assert_return(buf, -EINVAL);
@ -125,15 +129,23 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
assert_return(buflen, -EINVAL);
assert_return(ia, -EINVAL);
/* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
switch (ia->type) {
case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
iaid_offset = offsetof(DHCP6IA, ia_na);
ia_na = (struct ia_na) {
.id = ia->ia_na.id,
};
p = &ia_na;
break;
case SD_DHCP6_OPTION_IA_TA:
len = DHCP6_OPTION_IA_TA_LEN;
iaid_offset = offsetof(DHCP6IA, ia_ta);
ia_ta = (struct ia_ta) {
.id = ia->ia_ta.id,
};
p = &ia_ta;
break;
default:
@ -149,30 +161,113 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
*buf += offsetof(DHCP6Option, data);
*buflen -= offsetof(DHCP6Option, data);
memcpy(*buf, (char*) ia + iaid_offset, len);
memcpy(*buf, p, len);
*buf += len;
*buflen -= len;
LIST_FOREACH(addresses, addr, ia->addresses) {
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
sizeof(addr->iaaddr));
struct iaaddr a = {
.address = addr->iaaddr.address,
};
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr));
if (r < 0)
return r;
memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
memcpy(*buf, &a, sizeof(struct iaaddr));
*buf += sizeof(addr->iaaddr);
*buflen -= sizeof(addr->iaaddr);
*buf += sizeof(struct iaaddr);
*buflen -= sizeof(struct iaaddr);
ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr);
ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr);
}
r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
}
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) {
struct iapdprefix p;
int r;
assert(buf);
assert(*buf);
assert(buflen);
assert(prefix);
if (prefix->iapdprefix.prefixlen == 0)
return -EINVAL;
/* Do not append T1 and T2. */
p = (struct iapdprefix) {
.prefixlen = prefix->iapdprefix.prefixlen,
.address = prefix->iapdprefix.address,
};
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix));
if (r < 0)
return r;
return 0;
memcpy(*buf, &p, sizeof(struct iapdprefix));
*buf += sizeof(struct iapdprefix);
*buflen -= sizeof(struct iapdprefix);
return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix);
}
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) {
struct ia_pd ia_pd;
size_t len, pd_buflen;
uint8_t *pd_hdr;
int r;
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
assert_return(pd, -EINVAL);
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
/* Do not set T1 and T2. */
ia_pd = (struct ia_pd) {
.id = pd->ia_pd.id,
};
len = sizeof(struct ia_pd);
if (*buflen < offsetof(DHCP6Option, data) + len)
return -ENOBUFS;
pd_hdr = *buf;
pd_buflen = *buflen;
/* The header will be written at the end of this function. */
*buf += offsetof(DHCP6Option, data);
*buflen -= offsetof(DHCP6Option, data);
memcpy(*buf, &ia_pd, len);
*buf += sizeof(struct ia_pd);
*buflen -= sizeof(struct ia_pd);
DHCP6Address *prefix;
LIST_FOREACH(addresses, prefix, pd->addresses) {
r = option_append_pd_prefix(buf, buflen, prefix);
if (r < 0)
return r;
len += r;
}
if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
r = option_append_pd_prefix(buf, buflen, hint_pd_prefix);
if (r < 0)
return r;
len += r;
}
return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len);
}
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
@ -202,19 +297,22 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
return r;
}
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) {
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) {
_cleanup_free_ uint8_t *p = NULL;
size_t total = 0, offset = 0;
char **s;
char * const *s;
assert_return(buf && *buf && buflen && user_class, -EINVAL);
assert(buf);
assert(*buf);
assert(buflen);
assert(!strv_isempty(user_class));
STRV_FOREACH(s, user_class) {
size_t len = strlen(*s);
uint8_t *q;
if (len > 0xffff)
return -ENAMETOOLONG;
if (len > 0xffff || len == 0)
return -EINVAL;
q = realloc(p, total + len + 2);
if (!q)
return -ENOMEM;
@ -231,16 +329,16 @@ int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_cl
return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p);
}
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) {
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class) {
_cleanup_free_ uint8_t *p = NULL;
uint32_t enterprise_identifier;
size_t total, offset;
char **s;
char * const *s;
assert(buf);
assert(*buf);
assert(buflen);
assert(vendor_class);
assert(!strv_isempty(vendor_class));
enterprise_identifier = htobe32(SYSTEMD_PEN);
@ -255,6 +353,9 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendo
size_t len = strlen(*s);
uint8_t *q;
if (len > UINT16_MAX || len == 0)
return -EINVAL;
q = realloc(p, total + len + 2);
if (!q)
return -ENOMEM;
@ -271,51 +372,6 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendo
return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
}
int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
DHCP6Option *option = (DHCP6Option *)buf;
size_t i = sizeof(*option) + sizeof(pd->ia_pd);
DHCP6PDPrefixOption *prefix_opt;
DHCP6Address *prefix;
assert_return(buf, -EINVAL);
assert_return(pd, -EINVAL);
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
if (len < i)
return -ENOBUFS;
option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
LIST_FOREACH(addresses, prefix, pd->addresses) {
if (len < i + sizeof(*prefix_opt))
return -ENOBUFS;
prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, sizeof(struct iapdprefix));
i += sizeof(*prefix_opt);
}
if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
if (len < i + sizeof(*prefix_opt))
return -ENOBUFS;
prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
memcpy(&prefix_opt->iapdprefix, &hint_pd_prefix->iapdprefix, sizeof(struct iapdprefix));
i += sizeof(*prefix_opt);
}
option->len = htobe16(i - sizeof(*option));
return i;
}
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
DHCP6Option *option = (DHCP6Option*) *buf;
uint16_t len;
@ -371,8 +427,7 @@ int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
return be16toh(statusopt->status);
}
static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
uint32_t *lifetime_valid) {
static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
DHCP6Address *addr;
uint32_t lt_valid, lt_pref;
@ -385,16 +440,22 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
if (lt_valid == 0 || lt_pref > lt_valid) {
log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
log_dhcp6_client(client,
"Valid lifetime of an IA address is zero or "
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
lt_pref, lt_valid);
return 0;
return -EINVAL;
}
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
if (r != 0)
return r < 0 ? r: 0;
if (r < 0)
return r;
if (r > 0) {
log_dhcp6_client(client, "Non-zero status code '%s' for address is received",
dhcp6_message_status_to_string(r));
return -EINVAL;
}
}
addr = new0(DHCP6Address, 1);
@ -406,13 +467,12 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
LIST_PREPEND(addresses, ia->addresses, addr);
*lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
*ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
return 0;
}
static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
uint32_t *lifetime_valid) {
static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
DHCP6Address *prefix;
uint32_t lt_valid, lt_pref;
@ -425,16 +485,22 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
if (lt_valid == 0 || lt_pref > lt_valid) {
log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
log_dhcp6_client(client,
"Valid lifetieme of a PD prefix is zero or "
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
lt_pref, lt_valid);
return 0;
return -EINVAL;
}
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
if (r != 0)
return r < 0 ? r: 0;
if (r < 0)
return r;
if (r > 0) {
log_dhcp6_client(client, "Non-zero status code '%s' for PD prefix is received",
dhcp6_message_status_to_string(r));
return -EINVAL;
}
}
prefix = new0(DHCP6Address, 1);
@ -446,7 +512,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
LIST_PREPEND(addresses, ia->addresses, prefix);
*lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
*ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
return 0;
}
@ -478,8 +544,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
lt_t2 = be32toh(ia->ia_na.lifetime_t2);
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
lt_t1, lt_t2);
log_dhcp6_client(client, "IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec", lt_t1, lt_t2);
return -EINVAL;
}
@ -497,8 +562,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
lt_t1, lt_t2);
log_dhcp6_client(client, "IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec", lt_t1, lt_t2);
return -EINVAL;
}
@ -538,10 +602,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
}
r = dhcp6_option_parse_address(option, ia, &lt_valid);
if (r < 0)
if (r < 0 && r != -EINVAL)
return r;
if (lt_valid < lt_min)
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
break;
@ -554,10 +617,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
}
r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
if (r < 0)
if (r < 0 && r != -EINVAL)
return r;
if (lt_valid < lt_min)
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
break;
@ -590,26 +652,26 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
switch(iatype) {
case SD_DHCP6_OPTION_IA_NA:
if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
ia->ia_na.lifetime_t1 = htobe32(lt_t1);
ia->ia_na.lifetime_t2 = htobe32(lt_t2);
log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero",
log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}
break;
case SD_DHCP6_OPTION_IA_PD:
if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero",
log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}

View file

@ -35,5 +35,5 @@ struct sd_lldp {
#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
const char* lldp_event_to_string(sd_lldp_event e) _const_;
sd_lldp_event lldp_event_from_string(const char *s) _pure_;
const char* lldp_event_to_string(sd_lldp_event_t e) _const_;
sd_lldp_event_t lldp_event_from_string(const char *s) _pure_;

View file

@ -24,7 +24,7 @@ int lldp_network_bind_raw_socket(int ifindex) {
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */
};
static const struct sock_fprog fprog = {

View file

@ -276,7 +276,6 @@ int sd_dhcp_client_set_request_address(
}
int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
assert_return(ifindex > 0, -EINVAL);
@ -350,13 +349,14 @@ int sd_dhcp_client_get_client_id(
assert_return(data, -EINVAL);
assert_return(data_len, -EINVAL);
*type = 0;
*data = NULL;
*data_len = 0;
if (client->client_id_len) {
*type = client->client_id.type;
*data = client->client_id.raw.data;
*data_len = client->client_id_len - sizeof(client->client_id.type);
} else {
*type = 0;
*data = NULL;
*data_len = 0;
}
return 0;
@ -578,22 +578,26 @@ int sd_dhcp_client_set_mud_url(
int sd_dhcp_client_set_user_class(
sd_dhcp_client *client,
const char* const* user_class) {
char * const *user_class) {
_cleanup_strv_free_ char **s = NULL;
char **p;
char * const *p;
char **s = NULL;
STRV_FOREACH(p, (char **) user_class)
if (strlen(*p) > 255)
return -ENAMETOOLONG;
assert_return(client, -EINVAL);
assert_return(!strv_isempty(user_class), -EINVAL);
s = strv_copy((char **) user_class);
STRV_FOREACH(p, user_class) {
size_t n = strlen(*p);
if (n > 255 || n == 0)
return -EINVAL;
}
s = strv_copy(user_class);
if (!s)
return -ENOMEM;
client->user_class = TAKE_PTR(s);
return 0;
return strv_free_and_replace(client->user_class, s);
}
int sd_dhcp_client_set_client_port(
@ -630,11 +634,7 @@ int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) {
assert_return(client, -EINVAL);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp_option_hash_ops, UINT_TO_PTR(v->option), v);
if (r < 0)
return r;
@ -2234,7 +2234,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
.mtu = DHCP_DEFAULT_MIN_SIZE,
.port = DHCP_PORT_CLIENT,
.anonymize = !!anonymize,
.max_attempts = (uint64_t) -1,
.max_attempts = UINT64_MAX,
.ip_service_type = -1,
};
/* NOTE: this could be moved to a function. */

View file

@ -21,6 +21,7 @@
#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
@ -100,7 +101,7 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
int sd_dhcp_lease_get_servers(
sd_dhcp_lease *lease,
sd_dhcp_lease_server_type what,
sd_dhcp_lease_server_type_t what,
const struct in_addr **addr) {
assert_return(lease, -EINVAL);
@ -282,7 +283,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
free(lease->hostname);
free(lease->domainname);
for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
free(lease->servers[i].addr);
free(lease->static_route);
@ -870,7 +871,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
}
int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
struct sd_dhcp_raw_option *option;
struct in_addr address;
@ -890,7 +891,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
r = fopen_temporary(lease_file, &f, &temp_path);
if (r < 0)
goto fail;
return r;
(void) fchmod(fileno(f), 0644);
@ -993,10 +994,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_free_ char *client_id_hex = NULL;
client_id_hex = hexmem(client_id, client_id_len);
if (!client_id_hex) {
r = -ENOMEM;
goto fail;
}
if (!client_id_hex)
return -ENOMEM;
fprintf(f, "CLIENTID=%s\n", client_id_hex);
}
@ -1005,10 +1004,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_free_ char *option_hex = NULL;
option_hex = hexmem(data, data_len);
if (!option_hex) {
r = -ENOMEM;
goto fail;
}
if (!option_hex)
return -ENOMEM;
fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex);
}
@ -1018,29 +1015,23 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
xsprintf(key, "OPTION_%" PRIu8, option->tag);
r = serialize_dhcp_option(f, key, option->data, option->length);
if (r < 0)
goto fail;
return r;
}
r = fflush_and_check(f);
if (r < 0)
goto fail;
return r;
if (rename(temp_path, lease_file) < 0) {
r = -errno;
goto fail;
}
r = conservative_rename(temp_path, lease_file);
if (r < 0)
return r;
temp_path = mfree(temp_path);
return 0;
fail:
if (temp_path)
(void) unlink(temp_path);
return log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
}
int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
_cleanup_free_ char
*address = NULL,
@ -1268,13 +1259,13 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
if (client_id_hex) {
r = unhexmem(client_id_hex, (size_t) -1, &lease->client_id, &lease->client_id_len);
r = unhexmem(client_id_hex, SIZE_MAX, &lease->client_id, &lease->client_id_len);
if (r < 0)
log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
}
if (vendor_specific_hex) {
r = unhexmem(vendor_specific_hex, (size_t) -1, &lease->vendor_specific, &lease->vendor_specific_len);
r = unhexmem(vendor_specific_hex, SIZE_MAX, &lease->vendor_specific, &lease->vendor_specific_len);
if (r < 0)
log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
}
@ -1286,7 +1277,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (!options[i])
continue;
r = unhexmem(options[i], (size_t) -1, &data, &len);
r = unhexmem(options[i], SIZE_MAX, &data, &len);
if (r < 0) {
log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
continue;

View file

@ -163,7 +163,6 @@ int sd_dhcp6_client_set_callback(
}
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
@ -178,7 +177,7 @@ int sd_dhcp6_client_set_local_address(
assert_return(client, -EINVAL);
assert_return(local_address, -EINVAL);
assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
@ -241,11 +240,7 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *
assert_return(client, -EINVAL);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(client->vendor_options, v, v);
r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v);
if (r < 0)
return r;
@ -361,6 +356,7 @@ int sd_dhcp6_client_duid_as_string(
assert_return(client, -EINVAL);
assert_return(client->duid_len > 0, -ENODATA);
assert_return(duid, -EINVAL);
v = dhcp6_duid_type_to_string(be16toh(client->duid.type));
if (v) {
@ -464,7 +460,6 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
}
int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
assert_return(mudurl, -EINVAL);
@ -474,47 +469,48 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud
return free_and_strdup(&client->mudurl, mudurl);
}
int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) {
_cleanup_strv_free_ char **s = NULL;
char **p;
int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) {
char * const *p;
char **s;
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
assert_return(!strv_isempty(user_class), -EINVAL);
assert_return(user_class, -EINVAL);
STRV_FOREACH(p, user_class) {
size_t len = strlen(*p);
STRV_FOREACH(p, user_class)
if (strlen(*p) > UINT16_MAX)
return -ENAMETOOLONG;
if (len > UINT16_MAX || len == 0)
return -EINVAL;
}
s = strv_copy(user_class);
if (!s)
return -ENOMEM;
client->user_class = TAKE_PTR(s);
return 0;
return strv_free_and_replace(client->user_class, s);
}
int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) {
_cleanup_strv_free_ char **s = NULL;
char **p;
int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) {
char * const *p;
char **s;
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
assert_return(vendor_class, -EINVAL);
assert_return(!strv_isempty(vendor_class), -EINVAL);
STRV_FOREACH(p, vendor_class)
if (strlen(*p) > UINT8_MAX)
return -ENAMETOOLONG;
STRV_FOREACH(p, vendor_class) {
size_t len = strlen(*p);
if (len > UINT16_MAX || len == 0)
return -EINVAL;
}
s = strv_copy(vendor_class);
if (!s)
return -ENOMEM;
client->vendor_class = TAKE_PTR(s);
return 0;
return strv_free_and_replace(client->vendor_class, s);
}
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
@ -577,11 +573,7 @@ int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
assert_return(client, -EINVAL);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp6_option_hash_ops, UINT_TO_PTR(v->option), v);
if (r < 0)
return r;
@ -716,12 +708,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0)
return r;
opt += r;
optlen -= r;
}
break;
@ -780,12 +769,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
opt += r;
optlen -= r;
}
break;
@ -832,12 +818,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
opt += r;
optlen -= r;
}
break;
@ -1711,7 +1694,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->ifindex > 0, -EINVAL);
assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
return -EBUSY;

View file

@ -48,7 +48,7 @@ typedef enum IPv4ACDState {
IPV4ACD_STATE_ANNOUNCING,
IPV4ACD_STATE_RUNNING,
_IPV4ACD_STATE_MAX,
_IPV4ACD_STATE_INVALID = -1
_IPV4ACD_STATE_INVALID = -EINVAL,
} IPv4ACDState;
struct sd_ipv4acd {

View file

@ -188,7 +188,7 @@ int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
static bool ipv4ll_address_is_valid(const struct in_addr *address) {
assert(address);
if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address))
if (!in4_addr_is_link_local(address))
return false;
return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);

View file

@ -23,13 +23,13 @@
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
[SD_LLDP_EVENT_ADDED] = "added",
[SD_LLDP_EVENT_REMOVED] = "removed",
[SD_LLDP_EVENT_ADDED] = "added",
[SD_LLDP_EVENT_REMOVED] = "removed",
[SD_LLDP_EVENT_UPDATED] = "updated",
[SD_LLDP_EVENT_REFRESHED] = "refreshed",
};
DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event_t);
static void lldp_flush_neighbors(sd_lldp *lldp) {
assert(lldp);
@ -37,7 +37,7 @@ static void lldp_flush_neighbors(sd_lldp *lldp) {
hashmap_clear(lldp->neighbor_by_id);
}
static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
static void lldp_callback(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n) {
assert(lldp);
assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
@ -375,7 +375,7 @@ _public_ int sd_lldp_new(sd_lldp **ret) {
.n_ref = 1,
.fd = -1,
.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
.capability_mask = (uint16_t) -1,
.capability_mask = UINT16_MAX,
};
lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);

View file

@ -28,7 +28,7 @@ typedef enum EventSourceType {
SOURCE_WATCHDOG,
SOURCE_INOTIFY,
_SOURCE_EVENT_SOURCE_TYPE_MAX,
_SOURCE_EVENT_SOURCE_TYPE_INVALID = -1
_SOURCE_EVENT_SOURCE_TYPE_INVALID = -EINVAL,
} EventSourceType;
/* All objects we use in epoll events start with this value, so that
@ -40,7 +40,7 @@ typedef enum WakeupType {
WAKEUP_SIGNAL_DATA,
WAKEUP_INOTIFY_DATA,
_WAKEUP_TYPE_MAX,
_WAKEUP_TYPE_INVALID = -1,
_WAKEUP_TYPE_INVALID = -EINVAL,
} WakeupType;
struct inode_data;
@ -56,7 +56,7 @@ struct sd_event_source {
char *description;
EventSourceType type:5;
EventSourceType type;
signed int enabled:3;
bool pending:1;
bool dispatching:1;

View file

@ -409,7 +409,7 @@ _public_ int sd_event_new(sd_event** ret) {
e->epoll_fd = fd_move_above_stdio(e->epoll_fd);
if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s.");
log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 2^63 us will be logged every 5s.");
e->profile_delays = true;
}
@ -631,10 +631,6 @@ static int event_make_signal_data(
return 0;
}
} else {
r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops);
if (r < 0)
return r;
d = new(struct signal_data, 1);
if (!d)
return -ENOMEM;
@ -645,7 +641,7 @@ static int event_make_signal_data(
.priority = priority,
};
r = hashmap_put(e->signal_data, &d->priority, d);
r = hashmap_ensure_put(&e->signal_data, &uint64_hash_ops, &d->priority, d);
if (r < 0) {
free(d);
return r;
@ -945,7 +941,7 @@ static void source_disconnect(sd_event_source *s) {
sd_event_unref(event);
}
static void source_free(sd_event_source *s) {
static sd_event_source* source_free(sd_event_source *s) {
assert(s);
source_disconnect(s);
@ -995,7 +991,7 @@ static void source_free(sd_event_source *s) {
s->destroy_callback(s->userdata);
free(s->description);
free(s);
return mfree(s);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, source_free);
@ -1045,7 +1041,7 @@ static int source_set_pending(sd_event_source *s, bool b) {
}
}
return 0;
return 1;
}
static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) {
@ -1242,7 +1238,7 @@ _public_ int sd_event_add_time(
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(accuracy != (uint64_t) -1, -EINVAL);
assert_return(accuracy != UINT64_MAX, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@ -1731,10 +1727,6 @@ static int event_make_inotify_data(
fd = fd_move_above_stdio(fd);
r = hashmap_ensure_allocated(&e->inotify_data, &uint64_hash_ops);
if (r < 0)
return r;
d = new(struct inotify_data, 1);
if (!d)
return -ENOMEM;
@ -1745,7 +1737,7 @@ static int event_make_inotify_data(
.priority = priority,
};
r = hashmap_put(e->inotify_data, &d->priority, d);
r = hashmap_ensure_put(&e->inotify_data, &uint64_hash_ops, &d->priority, d);
if (r < 0) {
d->fd = safe_close(d->fd);
free(d);
@ -2606,10 +2598,11 @@ _public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec
if (r < 0)
return r;
if (usec >= USEC_INFINITY - t)
usec = usec_add(t, usec);
if (usec == USEC_INFINITY)
return -EOVERFLOW;
return sd_event_source_set_time(s, t + usec);
return sd_event_source_set_time(s, usec);
}
_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) {
@ -2626,7 +2619,7 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec
int r;
assert_return(s, -EINVAL);
assert_return(usec != (uint64_t) -1, -EINVAL);
assert_return(usec != UINT64_MAX, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(s->event), -ECHILD);
@ -3127,11 +3120,19 @@ static int process_timer(
return 0;
}
static int process_child(sd_event *e) {
static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) {
int64_t min_priority = threshold;
bool something_new = false;
sd_event_source *s;
int r;
assert(e);
assert(ret_min_priority);
if (!e->need_process_child) {
*ret_min_priority = min_priority;
return 0;
}
e->need_process_child = false;
@ -3156,6 +3157,9 @@ static int process_child(sd_event *e) {
HASHMAP_FOREACH(s, e->child_sources) {
assert(s->type == SOURCE_CHILD);
if (s->priority > threshold)
continue;
if (s->pending)
continue;
@ -3192,10 +3196,15 @@ static int process_child(sd_event *e) {
r = source_set_pending(s, true);
if (r < 0)
return r;
if (r > 0) {
something_new = true;
min_priority = MIN(min_priority, s->priority);
}
}
}
return 0;
*ret_min_priority = min_priority;
return something_new;
}
static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
@ -3225,13 +3234,13 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
return source_set_pending(s, true);
}
static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
bool read_one = false;
static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, int64_t *min_priority) {
int r;
assert(e);
assert(d);
assert_return(events == EPOLLIN, -EIO);
assert(min_priority);
/* If there's a signal queued on this priority and SIGCHLD is
on this priority too, then make sure to recheck the
@ -3257,7 +3266,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
n = read(d->fd, &si, sizeof(si));
if (n < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
return read_one;
return 0;
return -errno;
}
@ -3267,8 +3276,6 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
assert(SIGNAL_VALID(si.ssi_signo));
read_one = true;
if (e->signal_sources)
s = e->signal_sources[si.ssi_signo];
if (!s)
@ -3282,12 +3289,16 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
r = source_set_pending(s, true);
if (r < 0)
return r;
if (r > 0 && *min_priority >= s->priority) {
*min_priority = s->priority;
return 1; /* an event source with smaller priority is queued. */
}
return 1;
return 0;
}
}
static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents) {
static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents, int64_t threshold) {
ssize_t n;
assert(e);
@ -3303,6 +3314,9 @@ static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t
if (d->buffer_filled > 0)
return 0;
if (d->priority > threshold)
return 0;
n = read(d->fd, &d->buffer, sizeof(d->buffer));
if (n < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
@ -3792,44 +3806,103 @@ pending:
return r;
}
_public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
size_t event_queue_max;
int r, m, i;
static int epoll_wait_usec(
int fd,
struct epoll_event *events,
int maxevents,
usec_t timeout) {
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
int r, msec;
#if 0
static bool epoll_pwait2_absent = false;
if (e->exit_requested) {
e->state = SD_EVENT_PENDING;
return 1;
/* 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. */
if (!epoll_pwait2_absent && timeout != USEC_INFINITY) {
struct timespec ts;
r = epoll_pwait2(fd,
events,
maxevents,
timespec_store(&ts, timeout),
NULL);
if (r >= 0)
return r;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno; /* Only fallback to old epoll_wait() if the syscall is masked or not
* supported. */
epoll_pwait2_absent = true;
}
#endif
if (timeout == USEC_INFINITY)
msec = -1;
else {
usec_t k;
k = DIV_ROUND_UP(timeout, USEC_PER_MSEC);
if (k >= INT_MAX)
msec = INT_MAX; /* Saturate */
else
msec = (int) k;
}
event_queue_max = MAX(e->n_sources, 1u);
if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, event_queue_max))
r = epoll_wait(fd,
events,
maxevents,
msec);
if (r < 0)
return -errno;
return r;
}
static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) {
int64_t min_priority = threshold;
bool something_new = false;
size_t n_event_queue, m;
int r;
assert(e);
assert(ret_min_priority);
n_event_queue = MAX(e->n_sources, 1u);
if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, n_event_queue))
return -ENOMEM;
/* If we still have inotify data buffered, then query the other fds, but don't wait on it */
if (e->inotify_data_buffered)
timeout = 0;
m = epoll_wait(e->epoll_fd, e->event_queue, event_queue_max,
timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC));
if (m < 0) {
if (errno == EINTR) {
e->state = SD_EVENT_PENDING;
return 1;
}
for (;;) {
r = epoll_wait_usec(e->epoll_fd, e->event_queue, e->event_queue_allocated, timeout);
if (r < 0)
return r;
r = -errno;
goto finish;
m = (size_t) r;
if (m < e->event_queue_allocated)
break;
if (e->event_queue_allocated >= n_event_queue * 10)
break;
if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, e->event_queue_allocated + n_event_queue))
return -ENOMEM;
timeout = 0;
}
triple_timestamp_get(&e->timestamp);
/* Set timestamp only when this is called first time. */
if (threshold == INT64_MAX)
triple_timestamp_get(&e->timestamp);
for (i = 0; i < m; i++) {
for (size_t i = 0; i < m; i++) {
if (e->event_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG))
r = flush_timer(e, e->watchdog_fd, e->event_queue[i].events, NULL);
@ -3843,6 +3916,11 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
assert(s);
if (s->priority > threshold)
continue;
min_priority = MIN(min_priority, s->priority);
switch (s->type) {
case SOURCE_IO:
@ -3870,19 +3948,75 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
}
case WAKEUP_SIGNAL_DATA:
r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events);
r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events, &min_priority);
break;
case WAKEUP_INOTIFY_DATA:
r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events);
r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events, threshold);
break;
default:
assert_not_reached("Invalid wake-up pointer");
}
}
if (r < 0)
return r;
if (r > 0)
something_new = true;
}
*ret_min_priority = min_priority;
return something_new;
}
_public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
int r;
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_ARMED, -EBUSY);
if (e->exit_requested) {
e->state = SD_EVENT_PENDING;
return 1;
}
for (int64_t threshold = INT64_MAX; ; threshold--) {
int64_t epoll_min_priority, child_min_priority;
/* There may be a possibility that new epoll (especially IO) and child events are
* triggered just after process_epoll() call but before process_child(), and the new IO
* events may have higher priority than the child events. To salvage these events,
* let's call epoll_wait() again, but accepts only events with higher priority than the
* previous. See issue https://github.com/systemd/systemd/issues/18190 and comments
* https://github.com/systemd/systemd/pull/18750#issuecomment-785801085
* https://github.com/systemd/systemd/pull/18922#issuecomment-792825226 */
r = process_epoll(e, timeout, threshold, &epoll_min_priority);
if (r == -EINTR) {
e->state = SD_EVENT_PENDING;
return 1;
}
if (r < 0)
goto finish;
if (r == 0 && threshold < INT64_MAX)
/* No new epoll event. */
break;
r = process_child(e, threshold, &child_min_priority);
if (r < 0)
goto finish;
if (r == 0)
/* No new child event. */
break;
threshold = MIN(epoll_min_priority, child_min_priority);
if (threshold == INT64_MIN)
break;
timeout = 0;
}
r = process_watchdog(e);
@ -3909,19 +4043,12 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0)
goto finish;
if (e->need_process_child) {
r = process_child(e);
if (r < 0)
goto finish;
}
r = process_inotify(e);
if (r < 0)
goto finish;
if (event_next_pending(e)) {
e->state = SD_EVENT_PENDING;
return 1;
}
@ -4033,7 +4160,7 @@ _public_ int sd_event_loop(sd_event *e) {
_unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
while (e->state != SD_EVENT_FINISHED) {
r = sd_event_run(e, (uint64_t) -1);
r = sd_event_run(e, UINT64_MAX);
if (r < 0)
return r;
}

View file

@ -99,4 +99,10 @@ typedef void (*_sd_destroy_t)(void *userdata);
} \
struct _sd_useless_struct_to_allow_trailing_semicolon_
/* The following macro should be used in all public enums, to force 64bit wideness on them, so that we can
* freely extend them later on, without breaking compatibility. */
#define _SD_ENUM_FORCE_S64(id) \
_SD_##id##_INT64_MIN = INT64_MIN, \
_SD_##id##_INT64_MAX = INT64_MAX
#endif

View file

@ -181,7 +181,7 @@ int sd_dhcp_client_set_mud_url(
const char *mudurl);
int sd_dhcp_client_set_user_class(
sd_dhcp_client *client,
const char* const *user_class);
char * const *user_class);
int sd_dhcp_client_get_lease(
sd_dhcp_client *client,
sd_dhcp_lease **ret);

View file

@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <inttypes.h>
#include <net/ethernet.h>
#include <netinet/in.h>
@ -33,7 +34,7 @@ typedef struct sd_dhcp_route sd_dhcp_route;
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
typedef enum sd_dhcp_lease_server_type {
typedef enum sd_dhcp_lease_server_type_t {
SD_DHCP_LEASE_DNS,
SD_DHCP_LEASE_NTP,
SD_DHCP_LEASE_SIP,
@ -41,8 +42,9 @@ typedef enum sd_dhcp_lease_server_type {
SD_DHCP_LEASE_SMTP,
SD_DHCP_LEASE_LPR,
_SD_DHCP_LEASE_SERVER_TYPE_MAX,
_SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1,
} sd_dhcp_lease_server_type;
_SD_DHCP_LEASE_SERVER_TYPE_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(DHCP_LEASE_SERVER_TYPE),
} sd_dhcp_lease_server_type_t;
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
@ -53,7 +55,7 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr);
int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type_t what, const struct in_addr **addr);
int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);

View file

@ -133,10 +133,10 @@ int sd_dhcp6_client_set_request_mud_url(
const char *mudurl);
int sd_dhcp6_client_set_request_user_class(
sd_dhcp6_client *client,
char** user_class);
char * const *user_class);
int sd_dhcp6_client_set_request_vendor_class(
sd_dhcp6_client *client,
char** vendor_class);
char * const *vendor_class);
int sd_dhcp6_client_set_prefix_delegation_hint(
sd_dhcp6_client *client,
uint8_t prefixlen,

View file

@ -17,6 +17,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <inttypes.h>
#include <net/ethernet.h>
#include <sys/types.h>
@ -80,7 +81,7 @@ enum {
SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
};
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1)
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL UINT16_MAX
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \
((uint16_t) \
@ -121,16 +122,17 @@ enum {
typedef struct sd_lldp sd_lldp;
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
typedef enum sd_lldp_event {
typedef enum sd_lldp_event_t {
SD_LLDP_EVENT_ADDED,
SD_LLDP_EVENT_REMOVED,
SD_LLDP_EVENT_UPDATED,
SD_LLDP_EVENT_REFRESHED,
_SD_LLDP_EVENT_MAX,
_SD_LLDP_EVENT_INVALID = -1,
} sd_lldp_event;
_SD_LLDP_EVENT_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(LLDP_EVENT),
} sd_lldp_event_t;
typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata);
typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata);
int sd_lldp_new(sd_lldp **ret);
sd_lldp* sd_lldp_ref(sd_lldp *lldp);

View file

@ -19,6 +19,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <inttypes.h>
#include <net/ethernet.h>
#include <netinet/in.h>
@ -54,14 +55,15 @@ enum {
typedef struct sd_ndisc sd_ndisc;
typedef struct sd_ndisc_router sd_ndisc_router;
typedef enum sd_ndisc_event {
typedef enum sd_ndisc_event_t {
SD_NDISC_EVENT_TIMEOUT,
SD_NDISC_EVENT_ROUTER,
_SD_NDISC_EVENT_MAX,
_SD_NDISC_EVENT_INVALID = -1,
} sd_ndisc_event;
_SD_NDISC_EVENT_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(NDISC_EVENT),
} sd_ndisc_event_t;
typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata);
typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata);
int sd_ndisc_new(sd_ndisc **ret);
sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);

View file

@ -80,7 +80,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
})
static inline void freep(void *p) {
free(*(void**) p);
*(void**)p = mfree(*(void**) p);
}
#define _cleanup_free_ _cleanup_(freep)
@ -158,15 +158,6 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
(void*)memset(_new_, 0, _xsize_); \
})
/* 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) _ptr_ = (ptr); \
(ptr) = NULL; \
_ptr_; \
})
#if HAS_FEATURE_MEMORY_SANITIZER
# define msan_unpoison(r, s) __msan_unpoison(r, s)
#else

View file

@ -32,7 +32,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_DEVICES,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -1,
_CGROUP_CONTROLLER_INVALID = -EINVAL,
} CGroupController;
#define CGROUP_CONTROLLER_TO_MASK(c) (1U << (c))
@ -75,13 +75,13 @@ CGroupMask get_cpu_accounting_mask(void);
bool cpu_accounting_is_cheap(void);
/* Special values for all weight knobs on unified hierarchy */
#define CGROUP_WEIGHT_INVALID ((uint64_t) -1)
#define CGROUP_WEIGHT_INVALID UINT64_MAX
#define CGROUP_WEIGHT_MIN UINT64_C(1)
#define CGROUP_WEIGHT_MAX UINT64_C(10000)
#define CGROUP_WEIGHT_DEFAULT UINT64_C(100)
#define CGROUP_LIMIT_MIN UINT64_C(0)
#define CGROUP_LIMIT_MAX ((uint64_t) -1)
#define CGROUP_LIMIT_MAX UINT64_MAX
static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) {
return
@ -97,7 +97,7 @@ typedef enum CGroupIOLimitType {
CGROUP_IO_WIOPS_MAX,
_CGROUP_IO_LIMIT_TYPE_MAX,
_CGROUP_IO_LIMIT_TYPE_INVALID = -1
_CGROUP_IO_LIMIT_TYPE_INVALID = -EINVAL,
} CGroupIOLimitType;
extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX];
@ -106,7 +106,7 @@ const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_;
CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_;
/* Special values for the cpu.shares attribute */
#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1)
#define CGROUP_CPU_SHARES_INVALID UINT64_MAX
#define CGROUP_CPU_SHARES_MIN UINT64_C(2)
#define CGROUP_CPU_SHARES_MAX UINT64_C(262144)
#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024)
@ -118,7 +118,7 @@ static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) {
}
/* Special values for the blkio.weight attribute */
#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1)
#define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX
#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10)
#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000)
#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500)
@ -212,10 +212,13 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret);
int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid);
int cg_get_owner(const char *controller, const char *path, uid_t *ret_uid);
int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret);
/* Returns negative on error, and 0 or 1 on success for the bool value */
int cg_get_xattr_bool(const char *controller, const char *path, const char *name);
int cg_remove_xattr(const char *controller, const char *path, const char *name);
int cg_install_release_agent(const char *controller, const char *agent);
@ -257,6 +260,7 @@ int cg_slice_to_path(const char *unit, char **ret);
typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata);
int cg_mask_supported(CGroupMask *ret);
int cg_mask_supported_subtree(const char *root, CGroupMask *ret);
int cg_mask_from_string(const char *s, CGroupMask *ret);
int cg_mask_to_string(CGroupMask mask, char **ret);
@ -283,8 +287,19 @@ typedef enum ManagedOOMMode {
MANAGED_OOM_AUTO,
MANAGED_OOM_KILL,
_MANAGED_OOM_MODE_MAX,
_MANAGED_OOM_MODE_INVALID = -1,
_MANAGED_OOM_MODE_INVALID = -EINVAL,
} ManagedOOMMode;
const char* managed_oom_mode_to_string(ManagedOOMMode m) _const_;
ManagedOOMMode managed_oom_mode_from_string(const char *s) _pure_;
typedef enum ManagedOOMPreference {
MANAGED_OOM_PREFERENCE_NONE = 0,
MANAGED_OOM_PREFERENCE_AVOID = 1,
MANAGED_OOM_PREFERENCE_OMIT = 2,
_MANAGED_OOM_PREFERENCE_MAX,
_MANAGED_OOM_PREFERENCE_INVALID = -EINVAL,
} ManagedOOMPreference;
const char* managed_oom_preference_to_string(ManagedOOMPreference a) _const_;
ManagedOOMPreference managed_oom_preference_from_string(const char *s) _pure_;

View file

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */
#define DNS_LABEL_MAX 63
/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */
#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */
#define DNS_HOSTNAME_MAX 253
/* Maximum length of a full hostname, on the wire, including the final NUL byte */
#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255
/* Maximum number of labels per valid hostname */
#define DNS_N_LABELS_MAX 127

View file

@ -22,7 +22,7 @@ static int parse_env_file_internal(
void *userdata,
int *n_pushed) {
size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX;
_cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
unsigned line = 1;
char *p;
@ -58,7 +58,7 @@ static int parse_env_file_internal(
state = COMMENT;
else if (!strchr(WHITESPACE, c)) {
state = KEY;
last_key_whitespace = (size_t) -1;
last_key_whitespace = SIZE_MAX;
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
return -ENOMEM;
@ -74,11 +74,11 @@ static int parse_env_file_internal(
n_key = 0;
} else if (c == '=') {
state = PRE_VALUE;
last_value_whitespace = (size_t) -1;
last_value_whitespace = SIZE_MAX;
} else {
if (!strchr(WHITESPACE, c))
last_key_whitespace = (size_t) -1;
else if (last_key_whitespace == (size_t) -1)
last_key_whitespace = SIZE_MAX;
else if (last_key_whitespace == SIZE_MAX)
last_key_whitespace = n_key;
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
@ -99,7 +99,7 @@ static int parse_env_file_internal(
value[n_value] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
if (last_key_whitespace != SIZE_MAX)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
@ -138,11 +138,11 @@ static int parse_env_file_internal(
value[n_value] = 0;
/* Chomp off trailing whitespace from value */
if (last_value_whitespace != (size_t) -1)
if (last_value_whitespace != SIZE_MAX)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
if (last_key_whitespace != SIZE_MAX)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
@ -155,11 +155,11 @@ static int parse_env_file_internal(
} else if (c == '\\') {
state = VALUE_ESCAPE;
last_value_whitespace = (size_t) -1;
last_value_whitespace = SIZE_MAX;
} else {
if (!strchr(WHITESPACE, c))
last_value_whitespace = (size_t) -1;
else if (last_value_whitespace == (size_t) -1)
last_value_whitespace = SIZE_MAX;
else if (last_value_whitespace == SIZE_MAX)
last_value_whitespace = n_value;
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
@ -257,11 +257,11 @@ static int parse_env_file_internal(
value[n_value] = 0;
if (state == VALUE)
if (last_value_whitespace != (size_t) -1)
if (last_value_whitespace != SIZE_MAX)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
if (last_key_whitespace != SIZE_MAX)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
@ -388,11 +388,9 @@ static int load_env_file_push(
if (!p)
return -ENOMEM;
r = strv_env_replace(m, p);
if (r < 0) {
free(p);
r = strv_env_replace_consume(m, p);
if (r < 0)
return r;
}
if (n_pushed)
(*n_pushed)++;

View file

@ -14,6 +14,9 @@
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "utf8.h"
@ -25,8 +28,6 @@
"_"
static bool env_name_is_valid_n(const char *e, size_t n) {
const char *p;
if (!e)
return false;
@ -44,7 +45,7 @@ static bool env_name_is_valid_n(const char *e, size_t n) {
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false;
for (p = e; p < e + n; p++)
for (const char *p = e; p < e + n; p++)
if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p))
return false;
@ -62,16 +63,13 @@ bool env_value_is_valid(const char *e) {
if (!utf8_is_valid(e))
return false;
/* bash allows tabs and newlines in environment variables, and so
* should we */
if (string_has_cc(e, "\t\n"))
return false;
/* Note that variable *values* may contain control characters, in particular NL, TAB, BS, DEL, ESC…
* When printing those variables with show-environment, we'll escape them. Make sure to print
* environment variables carefully! */
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the shortest possible variable name of
* length 1, the equal sign and trailing NUL this hence leaves
* ARG_MAX-3 as longest possible variable value. */
/* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
* hence cannot be either. Discounting the shortest possible variable name of length 1, the equal
* sign and trailing NUL this hence leaves ARG_MAX-3 as longest possible variable value. */
if (strlen(e) > sc_arg_max() - 3)
return false;
@ -91,10 +89,8 @@ bool env_assignment_is_valid(const char *e) {
if (!env_value_is_valid(eq + 1))
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, hence the individual variable assignments
* cannot be either, but let's leave room for one trailing NUL
* byte. */
/* POSIX says the overall size of the environment block cannot be > ARG_MAX, hence the individual
* variable assignments cannot be either, but let's leave room for one trailing NUL byte. */
if (strlen(e) > sc_arg_max() - 1)
return false;
@ -192,14 +188,14 @@ static int env_append(char **r, char ***k, char **a) {
char **strv_env_merge(size_t n_lists, ...) {
_cleanup_strv_free_ char **ret = NULL;
size_t n = 0, i;
size_t n = 0;
char **l, **k;
va_list ap;
/* Merges an arbitrary number of environment sets */
va_start(ap, n_lists);
for (i = 0; i < n_lists; i++) {
for (size_t i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
n += strv_length(l);
}
@ -213,7 +209,7 @@ char **strv_env_merge(size_t n_lists, ...) {
k = ret;
va_start(ap, n_lists);
for (i = 0; i < n_lists; i++) {
for (size_t i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
if (env_append(ret, &k, l) < 0) {
va_end(ap);
@ -279,10 +275,8 @@ char **strv_env_delete(char **x, size_t n_lists, ...) {
return NULL;
STRV_FOREACH(k, x) {
size_t v;
va_start(ap, n_lists);
for (v = 0; v < n_lists; v++) {
for (size_t v = 0; v < n_lists; v++) {
char **l, **j;
l = va_arg(ap, char**);
@ -313,7 +307,6 @@ char **strv_env_delete(char **x, size_t n_lists, ...) {
}
char **strv_env_unset(char **l, const char *p) {
char **f, **t;
if (!l)
@ -374,20 +367,23 @@ char **strv_env_unset_many(char **l, ...) {
return l;
}
int strv_env_replace(char ***l, char *p) {
int strv_env_replace_consume(char ***l, char *p) {
const char *t, *name;
char **f;
int r;
assert(p);
/* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
* in-place. Does not copy p. p must be a valid key=value assignment.
*/
/* Replace first occurrence of the env var or add a new one in the string list. Drop other
* occurrences. Edits in-place. Does not copy p and CONSUMES p EVEN ON FAILURE.
*
* p must be a valid key=value assignment. */
t = strchr(p, '=');
if (!t)
if (!t) {
free(p);
return -EINVAL;
}
name = strndupa(p, t - p);
@ -399,39 +395,39 @@ int strv_env_replace(char ***l, char *p) {
}
/* We didn't find a match, we need to append p or create a new strv */
r = strv_push(l, p);
r = strv_consume(l, p);
if (r < 0)
return r;
return 1;
}
char **strv_env_set(char **x, const char *p) {
_cleanup_strv_free_ char **ret = NULL;
size_t n, m;
char **k;
int strv_env_replace_strdup(char ***l, const char *assignment) {
/* Like strv_env_replace_consume(), but copies the argument. */
/* Overrides the env var setting of p, returns a new copy */
char *p = strdup(assignment);
if (!p)
return -ENOMEM;
n = strv_length(x);
m = n + 2;
if (m < n) /* overflow? */
return NULL;
return strv_env_replace_consume(l, p);
}
ret = new(char*, m);
if (!ret)
return NULL;
int strv_env_assign(char ***l, const char *key, const char *value) {
if (!env_name_is_valid(key))
return -EINVAL;
*ret = NULL;
k = ret;
/* NULL removes assignment, "" creates an empty assignment. */
if (env_append(ret, &k, x) < 0)
return NULL;
if (!value) {
strv_env_unset(*l, key);
return 0;
}
if (env_append(ret, &k, STRV_MAKE(p)) < 0)
return NULL;
char *p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
return TAKE_PTR(ret);
return strv_env_replace_consume(l, p);
}
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
@ -463,6 +459,18 @@ char *strv_env_get(char **l, const char *name) {
return strv_env_get_n(l, name, strlen(name), 0);
}
char *strv_env_pairs_get(char **l, const char *name) {
char **key, **value, *result = NULL;
assert(name);
STRV_FOREACH_PAIR(key, value, l)
if (streq(*key, name))
result = *value;
return result;
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
char **p, **q;
int k = 0;
@ -764,4 +772,79 @@ int set_unset_env(const char *name, const char *value, bool overwrite) {
return -errno;
return 0;
}
int putenv_dup(const char *assignment, bool override) {
const char *e, *n;
e = strchr(assignment, '=');
if (!e)
return -EINVAL;
n = strndupa(assignment, e - assignment);
/* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */
if (setenv(n, e + 1, override) < 0)
return -errno;
return 0;
}
int setenv_systemd_exec_pid(bool update_only) {
char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
e = secure_getenv("SYSTEMD_EXEC_PID");
if (!e && update_only)
return 0;
if (streq_ptr(e, "*"))
return 0;
xsprintf(str, PID_FMT, getpid_cached());
if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
return -errno;
return 1;
}
int getenv_path_list(const char *name, char ***ret_paths) {
_cleanup_strv_free_ char **l = NULL;
const char *e;
char **p;
int r;
assert(name);
assert(ret_paths);
e = secure_getenv(name);
if (!e)
return -ENXIO;
r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_debug_errno(r, "Failed to parse $%s: %m", name);
STRV_FOREACH(p, l) {
if (!path_is_absolute(*p))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Path '%s' is not absolute, refusing.", *p);
if (!path_is_normalized(*p))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Path '%s' is not normalized, refusing.", *p);
if (path_equal(*p, "/"))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Path '%s' is the root fs, refusing.", *p);
}
if (strv_isempty(l))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"No paths specified, refusing.");
*ret_paths = TAKE_PTR(l);
return 1;
}
#endif /* NM_IGNORED */

View file

@ -42,16 +42,27 @@ bool strv_env_name_or_assignment_is_valid(char **l);
char **strv_env_merge(size_t n_lists, ...);
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char **strv_env_set(char **x, const char *p); /* New copy ... */
char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace(char ***l, char *p); /* In place ... */
int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
char *strv_env_pairs_get(char **l, const char *name) _pure_;
int getenv_bool(const char *p);
int getenv_bool_secure(const char *p);
/* Like setenv, but calls unsetenv if value == NULL. */
int set_unset_env(const char *name, const char *value, bool overwrite);
/* Like putenv, but duplicates the memory like setenv. */
int putenv_dup(const char *assignment, bool override);
int setenv_systemd_exec_pid(bool update_only);
/* Parses and does sanity checks on an environment variable containing
* PATH-like colon-separated absolute paths */
int getenv_path_list(const char *name, char ***ret_paths);

View file

@ -116,7 +116,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
* instead be copied directly.
*/
if (length != (size_t) -1 && length < 1)
if (length != SIZE_MAX && length < 1)
return -EINVAL;
switch (p[0]) {
@ -161,7 +161,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
/* hexadecimal encoding */
int a, b;
if (length != (size_t) -1 && length < 3)
if (length != SIZE_MAX && length < 3)
return -EINVAL;
a = unhexchar(p[1]);
@ -189,7 +189,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
size_t i;
uint32_t c;
if (length != (size_t) -1 && length < 5)
if (length != SIZE_MAX && length < 5)
return -EINVAL;
for (i = 0; i < 4; i++) {
@ -216,7 +216,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
size_t i;
char32_t c;
if (length != (size_t) -1 && length < 9)
if (length != SIZE_MAX && length < 9)
return -EINVAL;
for (i = 0; i < 8; i++) {
@ -253,7 +253,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
int a, b, c;
char32_t m;
if (length != (size_t) -1 && length < 3)
if (length != SIZE_MAX && length < 3)
return -EINVAL;
a = unoctchar(p[0]);

View file

@ -22,11 +22,10 @@
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
char c;
int r;
char quote = 0; /* 0 or ' or " */
bool backslash = false; /* whether we've just seen a backslash */
char c;
int r;
assert(p);
assert(ret);
@ -72,14 +71,14 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
return -ENOMEM;
if (c == 0) {
if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
(!quote || flags & EXTRACT_RELAX)) {
if ((flags & EXTRACT_UNESCAPE_RELAX) &&
(quote == 0 || flags & EXTRACT_RELAX)) {
/* If we find an unquoted trailing backslash and we're in
* EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
* EXTRACT_UNESCAPE_RELAX mode, keep it verbatim in the
* output.
*
* Unbalanced quotes will only be allowed in EXTRACT_RELAX
* mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
* mode, EXTRACT_UNESCAPE_RELAX mode does not allow them.
*/
s[sz++] = '\\';
goto finish_force_terminate;
@ -94,7 +93,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
char32_t u;
if ((flags & EXTRACT_CUNESCAPE) &&
(r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false)) >= 0) {
(r = cunescape_one(*p, SIZE_MAX, &u, &eight_bit, false)) >= 0) {
/* A valid escaped sequence */
assert(r >= 1);
@ -105,10 +104,10 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
else
sz += utf8_encode_unichar(s + sz, u);
} else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) &&
strchr(separators, **p))
/* An escaped separator char */
(strchr(separators, **p) || **p == '\\'))
/* An escaped separator char or the escape char itself */
s[sz++] = c;
else if (flags & EXTRACT_CUNESCAPE_RELAX) {
else if (flags & EXTRACT_UNESCAPE_RELAX) {
s[sz++] = '\\';
s[sz++] = c;
} else
@ -118,7 +117,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
backslash = false;
} else if (quote) { /* inside either single or double quotes */
} else if (quote != 0) { /* inside either single or double quotes */
for (;; (*p)++, c = **p) {
if (c == 0) {
if (flags & EXTRACT_RELAX)
@ -200,7 +199,7 @@ int extract_first_word_and_warn(
const char *rvalue) {
/* Try to unquote it, if it fails, warn about it and try again
* but this time using EXTRACT_CUNESCAPE_RELAX to keep the
* but this time using EXTRACT_UNESCAPE_RELAX to keep the
* backslashes verbatim in invalid escape sequences. */
const char *save;
@ -211,11 +210,11 @@ int extract_first_word_and_warn(
if (r >= 0)
return r;
if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
if (r == -EINVAL && !(flags & EXTRACT_UNESCAPE_RELAX)) {
/* Retry it with EXTRACT_CUNESCAPE_RELAX. */
/* Retry it with EXTRACT_UNESCAPE_RELAX. */
*p = save;
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
r = extract_first_word(p, ret, separators, flags|EXTRACT_UNESCAPE_RELAX);
if (r >= 0) {
/* It worked this time, hence it must have been an invalid escape sequence. */
log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);

View file

@ -4,13 +4,15 @@
#include "macro.h"
typedef enum ExtractFlags {
EXTRACT_RELAX = 1 << 0,
EXTRACT_CUNESCAPE = 1 << 1,
EXTRACT_CUNESCAPE_RELAX = 1 << 2,
EXTRACT_UNESCAPE_SEPARATORS = 1 << 3,
EXTRACT_UNQUOTE = 1 << 4,
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5,
EXTRACT_RETAIN_ESCAPE = 1 << 6,
EXTRACT_RELAX = 1 << 0, /* Allow unbalanced quote and eat up trailing backslash. */
EXTRACT_CUNESCAPE = 1 << 1, /* Unescape known escape sequences. */
EXTRACT_UNESCAPE_RELAX = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */
EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */
EXTRACT_UNQUOTE = 1 << 4, /* Remove quoting with "" and ''. */
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */
EXTRACT_RETAIN_ESCAPE = 1 << 6, /* Treat escape character '\' as any other character without special meaning */
/* Note that if no flags are specified, escaped escape characters will be silently stripped. */
} ExtractFlags;
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);

View file

@ -93,11 +93,9 @@ void safe_close_pair(int p[static 2]) {
}
void close_many(const int fds[], size_t n_fd) {
size_t i;
assert(fds || n_fd <= 0);
for (i = 0; i < n_fd; i++)
for (size_t i = 0; i < n_fd; i++)
safe_close(fds[i]);
}
@ -182,11 +180,9 @@ int fd_cloexec(int fd, bool cloexec) {
#if 0 /* NM_IGNORED */
_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
size_t i;
assert(n_fdset == 0 || fdset);
for (i = 0; i < n_fdset; i++)
for (size_t i = 0; i < n_fdset; i++)
if (fdset[i] == fd)
return true;

View file

@ -41,8 +41,8 @@ static inline void fclosep(FILE **f) {
safe_fclose(*f);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose);
DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
#define _cleanup_close_ _cleanup_(closep)
#define _cleanup_fclose_ _cleanup_(fclosep)

View file

@ -29,7 +29,8 @@
#include "string-util.h"
#include "tmpfile-util.h"
#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
/* The maximum size of the file we'll read in one go. */
#define READ_FULL_BYTES_MAX (4U*1024U*1024U - 1)
int fopen_unlocked(const char *path, const char *options, FILE **ret) {
assert(ret);
@ -372,7 +373,6 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
struct stat st;
size_t n, size;
int n_retries;
char *p;
assert(ret_contents);
@ -389,8 +389,12 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
if (fd < 0)
return -errno;
/* Start size for files in /proc which usually report a file size of 0. */
size = LINE_MAX / 2;
/* Start size for files in /proc/ which usually report a file size of 0. (Files in /sys/ report a
* file size of 4K, which is probably OK for sizing our initial buffer, and sysfs attributes can't be
* larger anyway.)
*
* It's one less than 4k, so that the malloc() below allocates exactly 4k. */
size = 4095;
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
n_retries = 3;
@ -406,19 +410,27 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
return -EBADF;
/* Be prepared for files from /proc which generally report a file size of 0. */
assert_cc(READ_FULL_BYTES_MAX < SSIZE_MAX);
if (st.st_size > 0) {
if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
size = st.st_size;
n_retries--;
} else
size = size * 2;
} else {
/* Double the buffer size */
if (size >= READ_FULL_BYTES_MAX)
return -E2BIG;
if (size > READ_FULL_BYTES_MAX / 2 - 1)
size = READ_FULL_BYTES_MAX; /* clamp to max */
else
size = size * 2 + 1; /* Stay always one less than page size, so we malloc evenly */
}
if (size > READ_FULL_BYTES_MAX)
return -E2BIG;
p = realloc(buf, size + 1);
if (!p)
buf = malloc(size + 1);
if (!buf)
return -ENOMEM;
buf = TAKE_PTR(p);
size = malloc_usable_size(buf) - 1; /* Use a bigger allocation if we got it anyway */
for (;;) {
ssize_t k;
@ -447,25 +459,28 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
if (lseek(fd, 0, SEEK_SET) < 0)
return -errno;
buf = mfree(buf);
}
if (n < size) {
char *p;
/* Return rest of the buffer to libc */
p = realloc(buf, n + 1);
if (!p)
return -ENOMEM;
buf = TAKE_PTR(p);
}
if (!ret_size) {
/* Safety check: if the caller doesn't want to know the size of what we
* just read it will rely on the trailing NUL byte. But if there's an
* embedded NUL byte, then we should refuse operation as otherwise
* there'd be ambiguity about what we just read. */
if (memchr(buf, 0, n))
return -EBADMSG;
} else
if (ret_size)
*ret_size = n;
else if (memchr(buf, 0, n))
/* Safety check: if the caller doesn't want to know the size of what we just read it will
* rely on the trailing NUL byte. But if there's an embedded NUL byte, then we should refuse
* operation as otherwise there'd be ambiguity about what we just read. */
return -EBADMSG;
buf[n] = 0;
*ret_contents = TAKE_PTR(buf);
@ -550,7 +565,9 @@ int read_full_stream_full(
}
buf = t;
n = n_next;
/* Unless a size has been explicitly specified, try to read as much as fits into the memory
* we allocated (minus 1, to leave one byte for the safety NUL byte) */
n = size == SIZE_MAX ? malloc_usable_size(buf) - 1 : n_next;
errno = 0;
k = fread(buf + l, 1, n - l, f);
@ -1157,7 +1174,7 @@ static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) {
return EOL_NONE;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, funlockfile, NULL);
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
size_t n = 0, allocated = 0, count = 0;
@ -1336,15 +1353,6 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
}
#if 0 /* NM_IGNORED */
int sync_rights(int from, int to) {
struct stat st;
if (fstat(from, &st) < 0)
return -errno;
return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid);
}
int rename_and_apply_smack_floor_label(const char *from, const char *to) {
int r = 0;
if (rename(from, to) < 0)

View file

@ -122,6 +122,4 @@ int safe_fgetc(FILE *f, char *ret);
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
int sync_rights(int from, int to);
int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path);

View file

@ -45,17 +45,17 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
{ "K", UINT64_C(1000) },
};
const suffix_table *table;
size_t n, i;
size_t n;
assert_cc(ELEMENTSOF(table_iec) == ELEMENTSOF(table_si));
if (t == (uint64_t) -1)
if (t == UINT64_MAX)
return NULL;
table = flag & FORMAT_BYTES_USE_IEC ? table_iec : table_si;
n = ELEMENTSOF(table_iec);
for (i = 0; i < n; i++)
for (size_t i = 0; i < n; i++)
if (t >= table[i].factor) {
if (flag & FORMAT_BYTES_BELOW_POINT) {
snprintf(buf, l,

View file

@ -103,7 +103,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
/* renameat2() exists since Linux 3.15, btrfs and FAT added support for it later. If it is not implemented,
* fall back to a different method. */
if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY))
if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL)
return -errno;
/* Let's try to use linkat()+unlinkat() as fallback. This doesn't work on directories and on some file systems
@ -120,7 +120,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
return 0;
}
if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !IN_SET(errno, EINVAL, EPERM)) /* FAT returns EPERM on link()… */
return -errno;
/* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fall back to the racy TOCTOU
@ -139,34 +139,34 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
#endif /* NM_IGNORED */
int readlinkat_malloc(int fd, const char *p, char **ret) {
size_t l = FILENAME_MAX+1;
int r;
size_t l = PATH_MAX;
assert(p);
assert(ret);
for (;;) {
char *c;
_cleanup_free_ char *c = NULL;
ssize_t n;
c = new(char, l);
c = new(char, l+1);
if (!c)
return -ENOMEM;
n = readlinkat(fd, p, c, l-1);
if (n < 0) {
r = -errno;
free(c);
return r;
}
n = readlinkat(fd, p, c, l);
if (n < 0)
return -errno;
if ((size_t) n < l-1) {
if ((size_t) n < l) {
c[n] = 0;
*ret = c;
*ret = TAKE_PTR(c);
return 0;
}
free(c);
if (l > (SSIZE_MAX-1)/2) /* readlinkat() returns an ssize_t, and we want an extra byte for a
* trailing NUL, hence do an overflow check relative to SSIZE_MAX-1
* here */
return -EFBIG;
l *= 2;
}
}
@ -433,9 +433,9 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) {
if (make_relative) {
_cleanup_free_ char *parent = NULL;
parent = dirname_malloc(to);
if (!parent)
return -ENOMEM;
r = path_extract_directory(to, &parent);
if (r < 0)
return r;
r = path_make_relative(parent, from, &relpath);
if (r < 0)
@ -1425,33 +1425,45 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
int fsync_directory_of_file(int fd) {
_cleanup_free_ char *path = NULL;
_cleanup_close_ int dfd = -1;
struct stat st;
int r;
r = fd_verify_regular(fd);
if (r < 0)
return r;
assert(fd >= 0);
r = fd_get_path(fd, &path);
if (r < 0) {
log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
fd,
r == -ENOSYS ? ", ignoring" : "");
/* We only reasonably can do this for regular files and directories, hence check for that */
if (fstat(fd, &st) < 0)
return -errno;
if (r == -ENOSYS)
/* If /proc is not available, we're most likely running in some
* chroot environment, and syncing the directory is not very
* important in that case. Let's just silently do nothing. */
return 0;
if (S_ISREG(st.st_mode)) {
return r;
}
r = fd_get_path(fd, &path);
if (r < 0) {
log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
fd,
r == -ENOSYS ? ", ignoring" : "");
if (!path_is_absolute(path))
return -EINVAL;
if (r == -ENOSYS)
/* If /proc is not available, we're most likely running in some
* chroot environment, and syncing the directory is not very
* important in that case. Let's just silently do nothing. */
return 0;
dfd = open_parent(path, O_CLOEXEC, 0);
if (dfd < 0)
return dfd;
return r;
}
if (!path_is_absolute(path))
return -EINVAL;
dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0);
if (dfd < 0)
return dfd;
} else if (S_ISDIR(st.st_mode)) {
dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
if (dfd < 0)
return -errno;
} else
return -ENOTTY;
if (fsync(dfd) < 0)
return -errno;
@ -1466,9 +1478,14 @@ int fsync_full(int fd) {
/* Sync both the file and the directory */
r = fsync(fd) < 0 ? -errno : 0;
q = fsync_directory_of_file(fd);
return r < 0 ? r : q;
q = fsync_directory_of_file(fd);
if (r < 0) /* Return earlier error */
return r;
if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a
* parent dir */
return 0;
return q;
}
int fsync_path_at(int at_fd, const char *path) {
@ -1485,8 +1502,7 @@ int fsync_path_at(int at_fd, const char *path) {
} else
fd = at_fd;
} else {
opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC);
opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (opened_fd < 0)
return -errno;
@ -1516,16 +1532,11 @@ int syncfs_path(int atfd, const char *path) {
int open_parent(const char *path, int flags, mode_t mode) {
_cleanup_free_ char *parent = NULL;
int fd;
int fd, r;
if (isempty(path))
return -EINVAL;
if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */
return -EINVAL;
parent = dirname_malloc(path);
if (!parent)
return -ENOMEM;
r = path_extract_directory(path, &parent);
if (r < 0)
return r;
/* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an
* O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */
@ -1627,7 +1638,7 @@ int path_is_encrypted(const char *path) {
return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
}
int conservative_rename(
int conservative_renameat(
int olddirfd, const char *oldpath,
int newdirfd, const char *newpath) {
@ -1669,23 +1680,37 @@ int conservative_rename(
goto do_rename;
for (;;) {
char buf1[16*1024];
char buf2[sizeof(buf1) + 1];
uint8_t buf1[16*1024];
uint8_t buf2[sizeof(buf1)];
ssize_t l1, l2;
l1 = read(old_fd, buf1, sizeof(buf1));
if (l1 < 0)
goto do_rename;
l2 = read(new_fd, buf2, l1 + 1);
if (l1 != l2)
goto do_rename;
if (l1 == sizeof(buf1))
/* Read the full block, hence read a full block in the other file too */
if (l1 == 0) /* EOF on both! And everything's the same so far, yay! */
break;
l2 = read(new_fd, buf2, l1);
else {
assert((size_t) l1 < sizeof(buf1));
/* Short read. This hence was the last block in the first file, and then came
* EOF. Read one byte more in the second file, so that we can verify we hit EOF there
* too. */
assert((size_t) (l1 + 1) <= sizeof(buf2));
l2 = read(new_fd, buf2, l1 + 1);
}
if (l2 != l1)
goto do_rename;
if (memcmp(buf1, buf2, l1) != 0)
goto do_rename;
if ((size_t) l1 < sizeof(buf1)) /* We hit EOF on the first file, and the second file too, hence exit
* now. */
break;
}
is_same:

View file

@ -11,6 +11,7 @@
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
#include "errno-util.h"
#include "time-util.h"
@ -43,7 +44,8 @@ int futimens_opath(int fd, const struct timespec ts[2]);
int fd_warn_permissions(const char *path, int fd);
int stat_warn_permissions(const char *path, const struct stat *st);
#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
#define laccess(path, mode) \
(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) < 0 ? -errno : 0)
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
int touch(const char *path);
@ -99,16 +101,23 @@ int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chas
int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
static inline void rmdir_and_free(char *p) {
static inline char *rmdir_and_free(char *p) {
PROTECT_ERRNO;
if (!p)
return NULL;
(void) rmdir(p);
free(p);
return mfree(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
static inline void unlink_and_free(char *p) {
static inline char* unlink_and_free(char *p) {
if (!p)
return NULL;
(void) unlink_noerrno(p);
free(p);
return mfree(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
@ -133,4 +142,7 @@ int open_parent(const char *path, int flags, mode_t mode);
int path_is_encrypted(const char *path);
int conservative_rename(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
static inline int conservative_rename(const char *oldpath, const char *newpath) {
return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
}

View file

@ -847,6 +847,16 @@ int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBU
return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
}
int _hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) {
int r;
r = _hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
return hashmap_put(*h, key, value);
}
int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) {
int r;
@ -2030,3 +2040,35 @@ int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **
*ret = TAKE_PTR(str);
return 0;
}
bool set_equal(Set *a, Set *b) {
void *p;
/* Checks whether each entry of 'a' is also in 'b' and vice versa, i.e. the two sets contain the same
* entries */
if (a == b)
return true;
if (set_isempty(a) && set_isempty(b))
return true;
if (set_size(a) != set_size(b)) /* Cheap check that hopefully catches a lot of inequality cases
* already */
return false;
SET_FOREACH(p, a)
if (!set_contains(b, p))
return false;
/* If we have the same hashops, then we don't need to check things backwards given we compared the
* size and that all of a is in b. */
if (a->b.hash_ops == b->b.hash_ops)
return true;
SET_FOREACH(p, b)
if (!set_contains(a, p))
return false;
return true;
}

View file

@ -133,8 +133,11 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS);
#define ordered_hashmap_copy(h) ((OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS))
int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
int _hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS);
int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
#define hashmap_ensure_put(s, ops, key, value) _hashmap_ensure_put(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS);

View file

@ -121,7 +121,7 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_
assert(ret_len);
assert(p || l == 0);
if (l == (size_t) -1)
if (l == SIZE_MAX)
l = strlen(p);
/* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */
@ -312,7 +312,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
assert(mem);
assert(_len);
if (l == (size_t) -1)
if (l == SIZE_MAX)
l = strlen(p);
/* padding ensures any base32hex input has input divisible by 8 */
@ -714,7 +714,7 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
assert(ret);
assert(ret_size);
if (l == (size_t) -1)
if (l == SIZE_MAX)
l = strlen(p);
/* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra

View file

@ -5,15 +5,40 @@
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "alloc-util.h"
#include "hostname-util.h"
#include "os-util.h"
#include "string-util.h"
#include "strv.h"
#if 0 /* NM_IGNORED */
char* get_default_hostname(void) {
int r;
const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
if (e) {
if (hostname_is_valid(e, 0))
return strdup(e);
log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e);
}
_cleanup_free_ char *f = NULL;
r = parse_os_release(NULL, "DEFAULT_HOSTNAME", &f);
if (r < 0)
log_debug_errno(r, "Failed to parse os-release, ignoring: %m");
else if (f) {
if (hostname_is_valid(f, 0))
return TAKE_PTR(f);
log_debug("Invalid hostname in os-release, ignoring: %s", f);
}
return strdup(FALLBACK_HOSTNAME);
}
char* gethostname_malloc(void) {
struct utsname u;
const char *s;
@ -26,7 +51,7 @@ char* gethostname_malloc(void) {
s = u.nodename;
if (isempty(s) || streq(s, "(none)"))
s = FALLBACK_HOSTNAME;
return get_default_hostname();
return strdup(s);
}
@ -34,6 +59,7 @@ char* gethostname_malloc(void) {
char* gethostname_short_malloc(void) {
struct utsname u;
const char *s;
_cleanup_free_ char *f = NULL;
/* Like above, but kills the FQDN part if present. */
@ -41,7 +67,10 @@ char* gethostname_short_malloc(void) {
s = u.nodename;
if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
s = FALLBACK_HOSTNAME;
s = f = get_default_hostname();
if (!s)
return NULL;
assert(s[0] != '.');
}

View file

@ -7,6 +7,7 @@
#include "macro.h"
#include "strv.h"
char* get_default_hostname(void);
char* gethostname_malloc(void);
char* gethostname_short_malloc(void);
int gethostname_strict(char **ret);

View file

@ -26,6 +26,12 @@ bool in4_addr_is_null(const struct in_addr *a) {
return a->s_addr == 0;
}
bool in6_addr_is_null(const struct in6_addr *a) {
assert(a);
return IN6_IS_ADDR_UNSPECIFIED(a);
}
int in_addr_is_null(int family, const union in_addr_union *u) {
assert(u);
@ -33,7 +39,7 @@ int in_addr_is_null(int family, const union in_addr_union *u) {
return in4_addr_is_null(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_UNSPECIFIED(&u->in6);
return in6_addr_is_null(&u->in6);
return -EAFNOSUPPORT;
}
@ -44,6 +50,12 @@ bool in4_addr_is_link_local(const struct in_addr *a) {
return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
}
bool in6_addr_is_link_local(const struct in6_addr *a) {
assert(a);
return IN6_IS_ADDR_LINKLOCAL(a);
}
int in_addr_is_link_local(int family, const union in_addr_union *u) {
assert(u);
@ -51,7 +63,7 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
return in4_addr_is_link_local(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_LINKLOCAL(&u->in6);
return in6_addr_is_link_local(&u->in6);
return -EAFNOSUPPORT;
}
@ -118,6 +130,13 @@ bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b) {
return a->s_addr == b->s_addr;
}
bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b) {
assert(a);
assert(b);
return IN6_ARE_ADDR_EQUAL(a, b);
}
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
assert(a);
assert(b);
@ -126,7 +145,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_
return in4_addr_equal(&a->in, &b->in);
if (family == AF_INET6)
return IN6_ARE_ADDR_EQUAL(&a->in6, &b->in6);
return in6_addr_equal(&a->in6, &b->in6);
return -EAFNOSUPPORT;
}
@ -192,8 +211,8 @@ int in_addr_prefix_intersect(
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) {
assert(u);
/* Increases the network part of an address by one. Returns
* positive if that succeeds, or -ERANGE if this overflows. */
/* Increases the network part of an address by one. Returns 0 if that succeeds, or -ERANGE if
* this overflows. */
return in_addr_prefix_nth(family, u, prefixlen, 1);
}
@ -201,19 +220,17 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen)
/*
* Calculates the nth prefix of size prefixlen starting from the address denoted by u.
*
* On success 1 will be returned and the calculated prefix will be available in
* u. In the case nth == 0 the input will be left unchanged and 1 will be returned.
* In case the calculation cannot be performed (invalid prefix length,
* On success 0 will be returned and the calculated prefix will be available in
* u. In case the calculation cannot be performed (invalid prefix length,
* overflows would occur) -ERANGE is returned. If the address family given isn't
* supported -EAFNOSUPPORT will be returned.
*
*
* Examples:
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 0, writes 192.168.2.0 to u
* - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 0, no data written
* - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written
* - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written
* - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u
* - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 0, writes 2001:0db8:0000:ff00:: to u
*/
int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) {
assert(u);
@ -221,13 +238,11 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u
if (prefixlen <= 0)
return -ERANGE;
if (nth == 0)
return 1;
if (family == AF_INET) {
uint32_t c, n, t;
if (prefixlen > 32)
prefixlen = 32;
return -ERANGE;
c = be32toh(u->in.s_addr);
@ -241,44 +256,40 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u
n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen);
u->in.s_addr = htobe32(n);
return 1;
return 0;
}
if (family == AF_INET6) {
struct in6_addr result = {};
uint8_t overflow = 0;
uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */
unsigned start_byte = (prefixlen - 1) / 8;
bool overflow = false;
if (prefixlen > 128)
prefixlen = 128;
/* First calculate what we have to add */
delta = nth << ((128 - prefixlen) % 8);
for (unsigned i = 16; i > 0; i--) {
unsigned j = i - 1;
unsigned d = 0;
if (j <= start_byte) {
int16_t t;
d = delta & 0xFF;
delta >>= 8;
t = u->in6.s6_addr[j] + d + overflow;
overflow = t > UINT8_MAX ? t - UINT8_MAX : 0;
result.s6_addr[j] = (uint8_t)t;
} else
result.s6_addr[j] = u->in6.s6_addr[j];
}
if (overflow || delta != 0)
return -ERANGE;
u->in6 = result;
return 1;
for (unsigned i = 16; i > 0; i--) {
unsigned t, j = i - 1, p = j * 8;
if (p >= prefixlen) {
u->in6.s6_addr[j] = 0;
continue;
}
if (prefixlen - p < 8) {
u->in6.s6_addr[j] &= 0xff << (8 - (prefixlen - p));
t = u->in6.s6_addr[j] + ((nth & 0xff) << (8 - (prefixlen - p)));
nth >>= prefixlen - p;
} else {
t = u->in6.s6_addr[j] + (nth & 0xff) + overflow;
nth >>= 8;
}
overflow = t > UINT8_MAX;
u->in6.s6_addr[j] = (uint8_t) (t & 0xff);
}
if (overflow || nth != 0)
return -ERANGE;
return 0;
}
return -EAFNOSUPPORT;
@ -362,6 +373,43 @@ int in_addr_random_prefix(
}
#endif /* NM_IGNORED */
int in_addr_prefix_range(
int family,
const union in_addr_union *in,
unsigned prefixlen,
union in_addr_union *ret_start,
union in_addr_union *ret_end) {
union in_addr_union start, end;
int r;
assert(in);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
if (ret_start) {
start = *in;
r = in_addr_prefix_nth(family, &start, prefixlen, 0);
if (r < 0)
return r;
}
if (ret_end) {
end = *in;
r = in_addr_prefix_nth(family, &end, prefixlen, 1);
if (r < 0)
return r;
}
if (ret_start)
*ret_start = start;
if (ret_end)
*ret_end = end;
return 0;
}
int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
_cleanup_free_ char *x = NULL;
size_t l;

View file

@ -21,11 +21,29 @@ struct in_addr_data {
};
bool in4_addr_is_null(const struct in_addr *a);
static inline bool in4_addr_is_set(const struct in_addr *a) {
return !in4_addr_is_null(a);
}
bool in6_addr_is_null(const struct in6_addr *a);
static inline bool in6_addr_is_set(const struct in6_addr *a) {
return !in6_addr_is_null(a);
}
int in_addr_is_null(int family, const union in_addr_union *u);
static inline bool in_addr_is_set(int family, const union in_addr_union *u) {
return in_addr_is_null(family, u) == 0;
}
static inline int in_addr_data_is_null(const struct in_addr_data *a) {
assert(a);
return in_addr_is_null(a->family, &a->address);
}
static inline bool in_addr_data_is_set(const struct in_addr_data *a) {
return in_addr_data_is_null(a);
}
int in_addr_is_multicast(int family, const union in_addr_union *u);
bool in4_addr_is_link_local(const struct in_addr *a);
bool in6_addr_is_link_local(const struct in6_addr *a);
int in_addr_is_link_local(int family, const union in_addr_union *u);
bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
@ -36,11 +54,18 @@ bool in4_addr_is_local_multicast(const struct in_addr *a);
bool in4_addr_is_non_local(const struct in_addr *a);
bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b);
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth);
int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
int in_addr_prefix_range(
int family,
const union in_addr_union *in,
unsigned prefixlen,
union in_addr_union *ret_start,
union in_addr_union *ret_end);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);

View file

@ -4,7 +4,6 @@
#include <errno.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <unistd.h>
@ -165,24 +164,42 @@ int pipe_eof(int fd) {
}
#endif /* NM_IGNORED */
int fd_wait_for_event(int fd, int event, usec_t t) {
struct pollfd pollfd = {
.fd = fd,
.events = event,
};
int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
struct timespec ts;
int r;
r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL);
assert(fds || nfds == 0);
if (nfds == 0)
return 0;
r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL);
if (r < 0)
return -errno;
if (r == 0)
return 0;
if (pollfd.revents & POLLNVAL)
return -EBADF;
for (size_t i = 0, n = r; i < nfds && n > 0; i++) {
if (fds[i].revents == 0)
continue;
if (fds[i].revents & POLLNVAL)
return -EBADF;
n--;
}
return r;
}
int fd_wait_for_event(int fd, int event, usec_t timeout) {
struct pollfd pollfd = {
.fd = fd,
.events = event,
};
int r;
r = ppoll_usec(&pollfd, 1, timeout);
if (r <= 0)
return r;
return pollfd.revents;
}
@ -326,16 +343,14 @@ int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, ch
}
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
size_t i;
for (i = 0; i < iovw->count; i++)
for (size_t i = 0; i < iovw->count; i++)
iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
}
size_t iovw_size(struct iovec_wrapper *iovw) {
size_t n = 0, i;
size_t n = 0;
for (i = 0; i < iovw->count; i++)
for (size_t i = 0; i < iovw->count; i++)
n += iovw->iovec[i].iov_len;
return n;

View file

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <poll.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@ -18,6 +19,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
int pipe_eof(int fd);
int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout);
int fd_wait_for_event(int fd, int event, usec_t timeout);
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
@ -60,7 +62,7 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
/* Same as above, but allows one extra value: -1 as indication for infinity. */
if (l == (uint64_t) -1)
if (l == UINT64_MAX)
return true;
return FILE_SIZE_VALID(l);

View file

@ -12,16 +12,6 @@
struct iovec;
struct signalfd_siginfo;
typedef enum LogRealm {
LOG_REALM_SYSTEMD,
LOG_REALM_UDEV,
_LOG_REALM_MAX,
} LogRealm;
#ifndef LOG_REALM
# define LOG_REALM LOG_REALM_SYSTEMD
#endif
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
LOG_TARGET_CONSOLE_PREFIXED,
@ -33,35 +23,26 @@ typedef enum LogTarget{
LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1
_LOG_TARGET_INVALID = -EINVAL,
} LogTarget;
/* Note to readers: << and >> have lower precedence than & and | */
#define LOG_REALM_PLUS_LEVEL(realm, level) ((realm) << 10 | (level))
#define LOG_REALM_REMOVE_LEVEL(realm_level) ((realm_level) >> 10)
#define SYNTHETIC_ERRNO(num) (1 << 30 | (num))
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255)
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
void log_set_target(LogTarget target);
int log_set_target_from_string(const char *e);
LogTarget log_get_target(void) _pure_;
void log_set_max_level_realm(LogRealm realm, int level);
#define log_set_max_level(level) \
log_set_max_level_realm(LOG_REALM, (level))
static inline void log_set_max_level_all_realms(int level) {
for (LogRealm realm = 0; realm < _LOG_REALM_MAX; realm++)
log_set_max_level_realm(realm, level);
}
void log_set_max_level(int level);
int log_set_max_level_from_string(const char *e);
int log_get_max_level(void) _pure_;
void log_set_facility(int facility);
int log_set_target_from_string(const char *e);
int log_set_max_level_from_string_realm(LogRealm realm, const char *e);
#define log_set_max_level_from_string(e) \
log_set_max_level_from_string_realm(LOG_REALM, (e))
void log_show_color(bool b);
bool log_get_show_color(void) _pure_;
void log_show_location(bool b);
@ -76,17 +57,9 @@ int log_show_location_from_string(const char *e);
int log_show_time_from_string(const char *e);
int log_show_tid_from_string(const char *e);
LogTarget log_get_target(void) _pure_;
#if 0 /* NM_IGNORED */
int log_get_max_level_realm(LogRealm realm) _pure_;
#endif /* NM_IGNORED */
#define log_get_max_level() \
log_get_max_level_realm(LOG_REALM)
/* Functions below that open and close logs or configure logging based on the
* environment should not be called from library code this is always a job
* for the application itself.
*/
* for the application itself. */
#if 0 /* NM_IGNORED */
assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
@ -98,12 +71,7 @@ int log_open(void);
void log_close(void);
void log_forget_fds(void);
void log_parse_environment_realm(LogRealm realm);
void log_parse_environment_cli_realm(LogRealm realm);
#define log_parse_environment() \
log_parse_environment_realm(LOG_REALM)
#define log_parse_environment_cli() \
log_parse_environment_cli_realm(LOG_REALM)
void log_parse_environment(void);
#if 0 /* NM_IGNORED */
int log_dispatch_internal(
@ -118,7 +86,7 @@ int log_dispatch_internal(
const char *extra_field,
char *buffer);
int log_internal_realm(
int log_internal(
int level,
int error,
const char *file,
@ -126,36 +94,9 @@ int log_internal_realm(
const char *func,
const char *format, ...) _printf_(6,7);
#endif /* NM_IGNORED */
#define log_internal(level, ...) \
log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__)
#define log_object_internal(level, \
error, \
file, \
line, \
func, \
object_field, \
object, \
extra_field, \
extra, \
format, \
...) \
({ \
const char *const _object = (object); \
\
log_internal_realm((level), \
(error), \
file, \
(line), \
(func), \
"%s%s" format, \
_object ?: "", \
_object ? ": " : "", \
##__VA_ARGS__); \
})
#if 0 /* NM_IGNORED */
int log_internalv_realm(
int log_internalv(
int level,
int error,
const char *file,
@ -163,10 +104,7 @@ int log_internalv_realm(
const char *func,
const char *format,
va_list ap) _printf_(6,0);
#define log_internalv(level, ...) \
log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__)
/* Realm is fixed to LOG_REALM_SYSTEMD for those */
int log_object_internalv(
int level,
int error,
@ -239,50 +177,37 @@ int log_dump_internal(
char *buffer);
/* Logging for various assertions */
_noreturn_ void log_assert_failed_realm(
LogRealm realm,
_noreturn_ void log_assert_failed(
const char *text,
const char *file,
int line,
const char *func);
#define log_assert_failed(text, ...) \
log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__)
_noreturn_ void log_assert_failed_unreachable_realm(
LogRealm realm,
_noreturn_ void log_assert_failed_unreachable(
const char *text,
const char *file,
int line,
const char *func);
#define log_assert_failed_unreachable(text, ...) \
log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__)
void log_assert_failed_return_realm(
LogRealm realm,
void log_assert_failed_return(
const char *text,
const char *file,
int line,
const char *func);
#define log_assert_failed_return(text, ...) \
log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__)
#define log_dispatch(level, error, buffer) \
log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
#endif /* NM_IGNORED */
/* Logging with level */
#define log_full_errno_realm(realm, level, error, ...) \
#define log_full_errno(level, error, ...) \
({ \
int _level = (level), _e = (error), _realm = (realm); \
(log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
int _level = (level), _e = (error); \
(log_get_max_level() >= LOG_PRI(_level)) \
? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
: -ERRNO_VALUE(_e); \
})
#define log_full_errno(level, error, ...) \
log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__)
#define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__)
int log_emergency_level(void);
@ -303,36 +228,30 @@ int log_emergency_level(void);
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__)
#ifdef LOG_TRACE
#if LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
#else
# define log_trace(...) do {} while (0)
#endif
/* Structured logging */
#define log_struct_errno(level, error, ...) \
log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
error, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__, NULL)
#define log_struct_errno(level, error, ...) \
log_struct_internal(level, error, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__, NULL)
#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__)
#define log_struct_iovec_errno(level, error, iovec, n_iovec) \
log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
error, PROJECT_FILE, __LINE__, __func__, iovec, n_iovec)
log_struct_iovec_internal(level, error, PROJECT_FILE, __LINE__, __func__, iovec, n_iovec)
#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec)
/* This modifies the buffer passed! */
#define log_dump(level, buffer) \
log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
0, PROJECT_FILE, __LINE__, __func__, buffer)
#define log_dump(level, buffer) \
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer)
#define log_oom() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_ERR), PROJECT_FILE, __LINE__, __func__)
#define log_oom_debug() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_DEBUG), PROJECT_FILE, __LINE__, __func__)
#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__)
bool log_on_console(void) _pure_;
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
/* Helper to prepare various field for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
@ -397,5 +316,4 @@ int log_syntax_invalid_utf8_internal(
#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
void log_setup_service(void);
void log_setup_cli(void);
void log_setup(void);

View file

@ -9,6 +9,8 @@
#include <sys/sysmacros.h>
#include <sys/types.h>
#include "macro-fundamental.h"
#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
#ifdef __clang__
# define _alloc_(...)
@ -18,10 +20,7 @@
#define _sentinel_ __attribute__((__sentinel__))
#define _section_(x) __attribute__((__section__(x)))
#define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__))
#define _destructor_ __attribute__((__destructor__))
#define _pure_ __attribute__((__pure__))
#define _const_ __attribute__((__const__))
#define _deprecated_ __attribute__((__deprecated__))
#define _packed_ __attribute__((__packed__))
#define _malloc_ __attribute__((__malloc__))
@ -34,7 +33,6 @@
#define _align_(x) __attribute__((__aligned__(x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _cleanup_(x) __attribute__((__cleanup__(x)))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
@ -152,12 +150,6 @@
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
#define UNIQ __COUNTER__
/* builtins */
#if __SIZEOF_INT__ == 4
#define BUILTIN_FFS_U32(x) __builtin_ffs(x);
@ -231,18 +223,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
return m;
}
#ifndef __COVERITY__
# define VOID_0 ((void)0)
#else
# define VOID_0 ((void*)0)
#endif
#define ELEMENTSOF(x) \
(__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
sizeof(x)/sizeof((x)[0]), \
VOID_0))
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
@ -263,106 +243,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
(type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \
})
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define CONST_MAX(_A, _B) \
(__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) > (_B)) ? (_A) : (_B), \
VOID_0))
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
#define MAX3(x, y, z) \
({ \
const typeof(x) _c = MAX(x, y); \
MAX(_c, z); \
})
#define MAX4(x, y, z, a) \
({ \
const typeof(x) _d = MAX3(x, y, z); \
MAX(_d, a); \
})
#undef MIN
#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
#define __MIN(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define CONST_MIN(_A, _B) \
(__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) < (_B)) ? (_A) : (_B), \
VOID_0))
#define MIN3(x, y, z) \
({ \
const typeof(x) _c = MIN(x, y); \
MIN(_c, z); \
})
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \
})
#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b))
#define __CMP(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \
})
#undef CLAMP
#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
#define __CLAMP(xq, x, lowq, low, highq, high) \
({ \
const typeof(x) UNIQ_T(X, xq) = (x); \
const typeof(low) UNIQ_T(LOW, lowq) = (low); \
const typeof(high) UNIQ_T(HIGH, highq) = (high); \
UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \
UNIQ_T(HIGH, highq) : \
UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \
UNIQ_T(LOW, lowq) : \
UNIQ_T(X, xq); \
})
/* [(x + y - 1) / y] suffers from an integer overflow, even though the
* computation should be possible in the given type. Therefore, we use
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
* quotient and the remainder, so both should be equally fast. */
#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
#define __DIV_ROUND_UP(xq, x, yq, y) \
({ \
const typeof(x) UNIQ_T(X, xq) = (x); \
const typeof(y) UNIQ_T(Y, yq) = (y); \
(UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \
})
#ifdef __COVERITY__
/* Use special definitions of assertion macros in order to prevent
@ -419,16 +299,6 @@ static inline int __coverity_check_and_return__(int condition) {
#define assert_not_reached(t) \
log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
#if defined(static_assert)
#define assert_cc(expr) \
static_assert(expr, #expr)
#else
#define assert_cc(expr) \
struct CONCATENATE(_assert_struct_, __COUNTER__) { \
char x[(expr) ? 0 : -1]; \
}
#endif
#define assert_return(expr, r) \
do { \
if (!assert_log(expr, #expr)) \
@ -507,53 +377,6 @@ static inline int __coverity_check_and_return__(int condition) {
#define FLAGS_SET(v, flags) \
((~(v) & (flags)) == 0)
#define CASE_F(X) case X:
#define CASE_F_1(CASE, X) CASE_F(X)
#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__)
#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__)
#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__)
#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__)
#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__)
#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__)
#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__)
#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__)
#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__)
#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__)
#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__)
#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__)
#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__)
#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__)
#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__)
#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__)
#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__)
#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__)
#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__)
#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
#define FOR_EACH_MAKE_CASE(...) \
GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \
CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
(CASE_F,__VA_ARGS__)
#define IN_SET(x, ...) \
({ \
bool _found = false; \
/* If the build breaks in the line below, you need to extend the case macros. (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__ }; \
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
switch(x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
_found = true; \
break; \
default: \
break; \
} \
_found; \
})
#define SWAP_TWO(x, y) do { \
typeof(x) _t = (x); \
(x) = (y); \
@ -562,6 +385,7 @@ static inline int __coverity_check_and_return__(int condition) {
#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
#define STRV_MAKE_CONST(...) ((const char* const*) ((const char*[]) { __VA_ARGS__, NULL }))
/* Pointers range from NULL to POINTER_MAX */
#define POINTER_MAX ((void*) UINTPTR_MAX)
@ -591,10 +415,20 @@ static inline int __coverity_check_and_return__(int condition) {
func(p); \
}
/* When func() returns the void value (NULL, -1, …) of the appropriate type */
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
*p = func(*p); \
}
/* When func() doesn't return the appropriate type, set variable to empty afterwards */
#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
static inline void func##p(type *p) { \
if (*p != (empty)) { \
func(*p); \
*p = (empty); \
} \
}
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \

View file

@ -5,6 +5,11 @@
#include <errno.h>
#include <fcntl.h>
#if HAVE_LINUX_TIME_TYPES_H
/* This header defines __kernel_timespec for us, but is only available since Linux 5.1, hence conditionally
* include this. */
#include <linux/time_types.h>
#endif
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
@ -15,28 +20,9 @@
#include <asm/sgidefs.h>
#endif
#if defined(__alpha__)
# define systemd_SC_arch_bias(x) (110 + (x))
#elif defined(__ia64__)
# define systemd_SC_arch_bias(x) (1024 + (x))
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_SC_arch_bias(x) (4000 + (x))
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_SC_arch_bias(x) (6000 + (x))
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_SC_arch_bias(x) (5000 + (x))
# else
# error "Unknown MIPS ABI"
# endif
#elif defined(__x86_64__) && defined(__ILP32__)
# define systemd_SC_arch_bias(x) ((x) | /* __X32_SYSCALL_BIT */ 0x40000000)
#else
# define systemd_SC_arch_bias(x) (x)
#endif
#include "missing_keyctl.h"
#include "missing_stat.h"
#include "missing_syscall_def.h"
#if 0 /* NM_IGNORED */
@ -45,6 +31,8 @@
#define KCMP_FILE 0
#endif
/* ======================================================================= */
#if !HAVE_PIVOT_ROOT
static inline int missing_pivot_root(const char *new_root, const char *put_old) {
return syscall(__NR_pivot_root, new_root, put_old);
@ -55,54 +43,6 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old)
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_memfd_create 279
#elif defined(__alpha__)
# define systemd_NR_memfd_create 512
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_memfd_create 279
#elif defined(__arm__)
# define systemd_NR_memfd_create 385
#elif defined(__i386__)
# define systemd_NR_memfd_create 356
#elif defined(__ia64__)
# define systemd_NR_memfd_create systemd_SC_arch_bias(316)
#elif defined(__m68k__)
# define systemd_NR_memfd_create 353
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_memfd_create systemd_SC_arch_bias(354)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_memfd_create systemd_SC_arch_bias(318)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_memfd_create systemd_SC_arch_bias(314)
# endif
#elif defined(__powerpc__)
# define systemd_NR_memfd_create 360
#elif defined(__s390__)
# define systemd_NR_memfd_create 350
#elif defined(__sparc__)
# define systemd_NR_memfd_create 348
#elif defined(__x86_64__)
# define systemd_NR_memfd_create systemd_SC_arch_bias(319)
#else
# warning "memfd_create() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_memfd_create && __NR_memfd_create >= 0
# if defined systemd_NR_memfd_create
assert_cc(__NR_memfd_create == systemd_NR_memfd_create);
# endif
#else
# if defined __NR_memfd_create
# undef __NR_memfd_create
# endif
# if defined systemd_NR_memfd_create
# define __NR_memfd_create systemd_NR_memfd_create
# endif
#endif
#if !HAVE_MEMFD_CREATE
static inline int missing_memfd_create(const char *name, unsigned int flags) {
# ifdef __NR_memfd_create
@ -118,54 +58,6 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_getrandom 278
#elif defined(__alpha__)
# define systemd_NR_getrandom 511
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_getrandom 278
#elif defined(__arm__)
# define systemd_NR_getrandom 384
#elif defined(__i386__)
# define systemd_NR_getrandom 355
#elif defined(__ia64__)
# define systemd_NR_getrandom systemd_SC_arch_bias(318)
#elif defined(__m68k__)
# define systemd_NR_getrandom 352
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_getrandom systemd_SC_arch_bias(353)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_getrandom systemd_SC_arch_bias(317)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_getrandom systemd_SC_arch_bias(313)
# endif
#elif defined(__powerpc__)
# define systemd_NR_getrandom 359
#elif defined(__s390__)
# define systemd_NR_getrandom 349
#elif defined(__sparc__)
# define systemd_NR_getrandom 347
#elif defined(__x86_64__)
# define systemd_NR_getrandom systemd_SC_arch_bias(318)
#else
# warning "getrandom() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_getrandom && __NR_getrandom >= 0
# if defined systemd_NR_getrandom
assert_cc(__NR_getrandom == systemd_NR_getrandom);
# endif
#else
# if defined __NR_getrandom
# undef __NR_getrandom
# endif
# if defined systemd_NR_getrandom
# define __NR_getrandom systemd_NR_getrandom
# endif
#endif
#if !HAVE_GETRANDOM
static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) {
# ifdef __NR_getrandom
@ -196,54 +88,6 @@ static inline pid_t missing_gettid(void) {
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_name_to_handle_at 264
#elif defined(__alpha__)
# define systemd_NR_name_to_handle_at 497
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_name_to_handle_at 264
#elif defined(__arm__)
# define systemd_NR_name_to_handle_at 370
#elif defined(__i386__)
# define systemd_NR_name_to_handle_at 341
#elif defined(__ia64__)
# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(302)
#elif defined(__m68k__)
# define systemd_NR_name_to_handle_at 340
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(339)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(298)
# endif
#elif defined(__powerpc__)
# define systemd_NR_name_to_handle_at 345
#elif defined(__s390__)
# define systemd_NR_name_to_handle_at 335
#elif defined(__sparc__)
# define systemd_NR_name_to_handle_at 332
#elif defined(__x86_64__)
# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303)
#else
# warning "name_to_handle_at() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0
# if defined systemd_NR_name_to_handle_at
assert_cc(__NR_name_to_handle_at == systemd_NR_name_to_handle_at);
# endif
#else
# if defined __NR_name_to_handle_at
# undef __NR_name_to_handle_at
# endif
# if defined systemd_NR_name_to_handle_at
# define __NR_name_to_handle_at systemd_NR_name_to_handle_at
# endif
#endif
#if !HAVE_NAME_TO_HANDLE_AT
struct file_handle {
unsigned int handle_bytes;
@ -265,54 +109,6 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_setns 268
#elif defined(__alpha__)
# define systemd_NR_setns 501
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_setns 268
#elif defined(__arm__)
# define systemd_NR_setns 375
#elif defined(__i386__)
# define systemd_NR_setns 346
#elif defined(__ia64__)
# define systemd_NR_setns systemd_SC_arch_bias(306)
#elif defined(__m68k__)
# define systemd_NR_setns 344
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_setns systemd_SC_arch_bias(344)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_setns systemd_SC_arch_bias(308)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_setns systemd_SC_arch_bias(303)
# endif
#elif defined(__powerpc__)
# define systemd_NR_setns 350
#elif defined(__s390__)
# define systemd_NR_setns 339
#elif defined(__sparc__)
# define systemd_NR_setns 337
#elif defined(__x86_64__)
# define systemd_NR_setns systemd_SC_arch_bias(308)
#else
# warning "setns() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_setns && __NR_setns >= 0
# if defined systemd_NR_setns
assert_cc(__NR_setns == systemd_NR_setns);
# endif
#else
# if defined __NR_setns
# undef __NR_setns
# endif
# if defined systemd_NR_setns
# define __NR_setns systemd_NR_setns
# endif
#endif
#if !HAVE_SETNS
static inline int missing_setns(int fd, int nstype) {
# ifdef __NR_setns
@ -338,54 +134,6 @@ static inline pid_t raw_getpid(void) {
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_renameat2 276
#elif defined(__alpha__)
# define systemd_NR_renameat2 510
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_renameat2 276
#elif defined(__arm__)
# define systemd_NR_renameat2 382
#elif defined(__i386__)
# define systemd_NR_renameat2 353
#elif defined(__ia64__)
# define systemd_NR_renameat2 systemd_SC_arch_bias(314)
#elif defined(__m68k__)
# define systemd_NR_renameat2 351
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_renameat2 systemd_SC_arch_bias(351)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_renameat2 systemd_SC_arch_bias(315)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_renameat2 systemd_SC_arch_bias(311)
# endif
#elif defined(__powerpc__)
# define systemd_NR_renameat2 357
#elif defined(__s390__)
# define systemd_NR_renameat2 347
#elif defined(__sparc__)
# define systemd_NR_renameat2 345
#elif defined(__x86_64__)
# define systemd_NR_renameat2 systemd_SC_arch_bias(316)
#else
# warning "renameat2() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_renameat2 && __NR_renameat2 >= 0
# if defined systemd_NR_renameat2
assert_cc(__NR_renameat2 == systemd_NR_renameat2);
# endif
#else
# if defined __NR_renameat2
# undef __NR_renameat2
# endif
# if defined systemd_NR_renameat2
# define __NR_renameat2 systemd_NR_renameat2
# endif
#endif
#if !HAVE_RENAMEAT2
static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
# ifdef __NR_renameat2
@ -453,54 +201,6 @@ static inline key_serial_t missing_request_key(const char *type, const char *des
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_copy_file_range 285
#elif defined(__alpha__)
# define systemd_NR_copy_file_range 519
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_copy_file_range 285
#elif defined(__arm__)
# define systemd_NR_copy_file_range 391
#elif defined(__i386__)
# define systemd_NR_copy_file_range 377
#elif defined(__ia64__)
# define systemd_NR_copy_file_range systemd_SC_arch_bias(323)
#elif defined(__m68k__)
# define systemd_NR_copy_file_range 376
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_copy_file_range systemd_SC_arch_bias(360)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_copy_file_range systemd_SC_arch_bias(324)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_copy_file_range systemd_SC_arch_bias(320)
# endif
#elif defined(__powerpc__)
# define systemd_NR_copy_file_range 379
#elif defined(__s390__)
# define systemd_NR_copy_file_range 375
#elif defined(__sparc__)
# define systemd_NR_copy_file_range 357
#elif defined(__x86_64__)
# define systemd_NR_copy_file_range systemd_SC_arch_bias(326)
#else
# warning "copy_file_range() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_copy_file_range && __NR_copy_file_range >= 0
# if defined systemd_NR_copy_file_range
assert_cc(__NR_copy_file_range == systemd_NR_copy_file_range);
# endif
#else
# if defined __NR_copy_file_range
# undef __NR_copy_file_range
# endif
# if defined systemd_NR_copy_file_range
# define __NR_copy_file_range systemd_NR_copy_file_range
# endif
#endif
#if !HAVE_COPY_FILE_RANGE
static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
int fd_out, loff_t *off_out,
@ -519,54 +219,6 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_bpf 280
#elif defined(__alpha__)
# define systemd_NR_bpf 515
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_bpf 280
#elif defined(__arm__)
# define systemd_NR_bpf 386
#elif defined(__i386__)
# define systemd_NR_bpf 357
#elif defined(__ia64__)
# define systemd_NR_bpf systemd_SC_arch_bias(317)
#elif defined(__m68k__)
# define systemd_NR_bpf 354
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_bpf systemd_SC_arch_bias(355)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_bpf systemd_SC_arch_bias(319)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_bpf systemd_SC_arch_bias(315)
# endif
#elif defined(__powerpc__)
# define systemd_NR_bpf 361
#elif defined(__s390__)
# define systemd_NR_bpf 351
#elif defined(__sparc__)
# define systemd_NR_bpf 349
#elif defined(__x86_64__)
# define systemd_NR_bpf systemd_SC_arch_bias(321)
#else
# warning "bpf() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_bpf && __NR_bpf >= 0
# if defined systemd_NR_bpf
assert_cc(__NR_bpf == systemd_NR_bpf);
# endif
#else
# if defined __NR_bpf
# undef __NR_bpf
# endif
# if defined systemd_NR_bpf
# define __NR_bpf systemd_NR_bpf
# endif
#endif
#if !HAVE_BPF
union bpf_attr;
@ -584,106 +236,6 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
/* ======================================================================= */
#ifndef __IGNORE_pkey_mprotect
# if defined(__aarch64__)
# define systemd_NR_pkey_mprotect 288
# elif defined(__alpha__)
# define systemd_NR_pkey_mprotect 524
# elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_pkey_mprotect 226
# elif defined(__arm__)
# define systemd_NR_pkey_mprotect 394
# elif defined(__i386__)
# define systemd_NR_pkey_mprotect 380
# elif defined(__ia64__)
# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(330)
# elif defined(__m68k__)
# define systemd_NR_pkey_mprotect 381
# elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(363)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(327)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(323)
# endif
# elif defined(__powerpc__)
# define systemd_NR_pkey_mprotect 386
# elif defined(__s390__)
# define systemd_NR_pkey_mprotect 384
# elif defined(__sparc__)
# define systemd_NR_pkey_mprotect 362
# elif defined(__x86_64__)
# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(329)
# else
# warning "pkey_mprotect() syscall number is unknown for your architecture"
# endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
# if defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0
# if defined systemd_NR_pkey_mprotect
assert_cc(__NR_pkey_mprotect == systemd_NR_pkey_mprotect);
# endif
# else
# if defined __NR_pkey_mprotect
# undef __NR_pkey_mprotect
# endif
# if defined systemd_NR_pkey_mprotect
# define __NR_pkey_mprotect systemd_NR_pkey_mprotect
# endif
# endif
#endif
/* ======================================================================= */
#if defined(__aarch64__)
# define systemd_NR_statx 291
#elif defined(__alpha__)
# define systemd_NR_statx 522
#elif defined(__arc__) || defined(__tilegx__)
# define systemd_NR_statx 291
#elif defined(__arm__)
# define systemd_NR_statx 397
#elif defined(__i386__)
# define systemd_NR_statx 383
#elif defined(__ia64__)
# define systemd_NR_statx systemd_SC_arch_bias(326)
#elif defined(__m68k__)
# define systemd_NR_statx 379
#elif defined(_MIPS_SIM)
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define systemd_NR_statx systemd_SC_arch_bias(366)
# elif _MIPS_SIM == _MIPS_SIM_NABI32
# define systemd_NR_statx systemd_SC_arch_bias(330)
# elif _MIPS_SIM == _MIPS_SIM_ABI64
# define systemd_NR_statx systemd_SC_arch_bias(326)
# endif
#elif defined(__powerpc__)
# define systemd_NR_statx 383
#elif defined(__s390__)
# define systemd_NR_statx 379
#elif defined(__sparc__)
# define systemd_NR_statx 360
#elif defined(__x86_64__)
# define systemd_NR_statx systemd_SC_arch_bias(332)
#else
# warning "statx() syscall number is unknown for your architecture"
#endif
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_statx && __NR_statx >= 0
# if defined systemd_NR_statx
assert_cc(__NR_statx == systemd_NR_statx);
# endif
#else
# if defined __NR_statx
# undef __NR_statx
# endif
# if defined systemd_NR_statx
# define __NR_statx systemd_NR_statx
# endif
#endif
#if !HAVE_STATX
struct statx;
@ -751,21 +303,6 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
/* ======================================================================= */
/* should be always defined, see kernel 39036cd2727395c3369b1051005da74059a85317 */
#define systemd_NR_pidfd_send_signal systemd_SC_arch_bias(424)
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0
# if defined systemd_NR_pidfd_send_signal
assert_cc(__NR_pidfd_send_signal == systemd_NR_pidfd_send_signal);
# endif
#else
# if defined __NR_pidfd_send_signal
# undef __NR_pidfd_send_signal
# endif
# define __NR_pidfd_send_signal systemd_NR_pidfd_send_signal
#endif
#if !HAVE_PIDFD_SEND_SIGNAL
static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
# ifdef __NR_pidfd_send_signal
@ -779,21 +316,6 @@ static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, un
# define pidfd_send_signal missing_pidfd_send_signal
#endif
/* should be always defined, see kernel 7615d9e1780e26e0178c93c55b73309a5dc093d7 */
#define systemd_NR_pidfd_open systemd_SC_arch_bias(434)
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_pidfd_open && __NR_pidfd_open >= 0
# if defined systemd_NR_pidfd_open
assert_cc(__NR_pidfd_open == systemd_NR_pidfd_open);
# endif
#else
# if defined __NR_pidfd_open
# undef __NR_pidfd_open
# endif
# define __NR_pidfd_open systemd_NR_pidfd_open
#endif
#if !HAVE_PIDFD_OPEN
static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
# ifdef __NR_pidfd_open
@ -843,22 +365,6 @@ static inline int missing_execveat(int dirfd, const char *pathname,
/* ======================================================================= */
#define systemd_NR_close_range systemd_SC_arch_bias(436)
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
#if defined __NR_close_range && __NR_close_range >= 0
# if defined systemd_NR_close_range
assert_cc(__NR_close_range == systemd_NR_close_range);
# endif
#else
# if defined __NR_close_range
# undef __NR_close_range
# endif
# if defined systemd_NR_close_range
# define __NR_close_range systemd_NR_close_range
# endif
#endif
#if !HAVE_CLOSE_RANGE
static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) {
# ifdef __NR_close_range
@ -886,4 +392,42 @@ static inline int missing_close_range(int first_fd, int end_fd, unsigned flags)
# define close_range missing_close_range
#endif
/* ======================================================================= */
#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
#endif /* NM_IGNORED */

View file

@ -0,0 +1,101 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "fileio.h"
#include "ordered-set.h"
#include "strv.h"
int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HASHMAP_DEBUG_PARAMS) {
if (*s)
return 0;
*s = _ordered_set_new(ops HASHMAP_DEBUG_PASS_ARGS);
if (!*s)
return -ENOMEM;
return 0;
}
int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS) {
int r;
r = _ordered_set_ensure_allocated(s, ops HASHMAP_DEBUG_PASS_ARGS);
if (r < 0)
return r;
return ordered_set_put(*s, p);
}
int ordered_set_consume(OrderedSet *s, void *p) {
int r;
r = ordered_set_put(s, p);
if (r <= 0)
free(p);
return r;
}
int ordered_set_put_strdup(OrderedSet *s, const char *p) {
char *c;
int r;
assert(s);
assert(p);
c = strdup(p);
if (!c)
return -ENOMEM;
r = ordered_set_consume(s, c);
if (r == -EEXIST)
return 0;
return r;
}
int ordered_set_put_strdupv(OrderedSet *s, char **l) {
int n = 0, r;
char **i;
STRV_FOREACH(i, l) {
r = ordered_set_put_strdup(s, *i);
if (r < 0)
return r;
n += r;
}
return n;
}
int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l) {
int n = 0, r;
char *p;
/* Like ordered_set_put_strv, but for an OrderedSet of strings */
ORDERED_SET_FOREACH(p, l) {
r = ordered_set_put_strdup(s, p);
if (r < 0)
return r;
n += r;
}
return n;
}
void ordered_set_print(FILE *f, const char *field, OrderedSet *s) {
bool space = false;
char *p;
if (ordered_set_isempty(s))
return;
fputs(field, f);
ORDERED_SET_FOREACH(p, s)
fputs_with_space(f, p, NULL, &space);
fputc('\n', f);
}

View file

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdio.h>
#include "hashmap.h"
typedef struct OrderedSet OrderedSet;
static inline OrderedSet* _ordered_set_new(const struct hash_ops *ops HASHMAP_DEBUG_PARAMS) {
return (OrderedSet*) _ordered_hashmap_new(ops HASHMAP_DEBUG_PASS_ARGS);
}
#define ordered_set_new(ops) _ordered_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HASHMAP_DEBUG_PARAMS);
#define ordered_set_ensure_allocated(s, ops) _ordered_set_ensure_allocated(s, ops HASHMAP_DEBUG_SRC_ARGS)
int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS);
#define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS)
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
}
static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
return (OrderedSet*) ordered_hashmap_free_free((OrderedHashmap*) s);
}
static inline int ordered_set_put(OrderedSet *s, void *p) {
return ordered_hashmap_put((OrderedHashmap*) s, p, p);
}
static inline unsigned ordered_set_size(OrderedSet *s) {
return ordered_hashmap_size((OrderedHashmap*) s);
}
static inline bool ordered_set_isempty(OrderedSet *s) {
return ordered_hashmap_isempty((OrderedHashmap*) s);
}
static inline bool ordered_set_iterate(OrderedSet *s, Iterator *i, void **value) {
return ordered_hashmap_iterate((OrderedHashmap*) s, i, value, NULL);
}
static inline void* ordered_set_remove(OrderedSet *s, void *p) {
return ordered_hashmap_remove((OrderedHashmap*) s, p);
}
static inline void* ordered_set_first(OrderedSet *s) {
return ordered_hashmap_first((OrderedHashmap*) s);
}
static inline void* ordered_set_steal_first(OrderedSet *s) {
return ordered_hashmap_steal_first((OrderedHashmap*) s);
}
static inline char** ordered_set_get_strv(OrderedSet *s) {
return _hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s));
}
int ordered_set_consume(OrderedSet *s, void *p);
int ordered_set_put_strdup(OrderedSet *s, const char *p);
int ordered_set_put_strdupv(OrderedSet *s, char **l);
int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l);
void ordered_set_print(FILE *f, const char *field, OrderedSet *s);
#define _ORDERED_SET_FOREACH(e, s, i) \
for (Iterator i = ITERATOR_FIRST; ordered_set_iterate((s), &i, (void**)&(e)); )
#define ORDERED_SET_FOREACH(e, s) \
_ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ))
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep)

View file

@ -18,9 +18,6 @@
#include "missing_network.h"
#include "parse-util.h"
#include "process-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@ -319,46 +316,6 @@ int parse_errno(const char *t) {
return e;
}
#if HAVE_SECCOMP
int parse_syscall_and_errno(const char *in, char **name, int *error) {
_cleanup_free_ char *n = NULL;
char *p;
int e = -1;
assert(in);
assert(name);
assert(error);
/*
* This parse "syscall:errno" like "uname:EILSEQ", "@sync:255".
* If errno is omitted, then error is set to -1.
* Empty syscall name is not allowed.
* Here, we do not check that the syscall name is valid or not.
*/
p = strchr(in, ':');
if (p) {
e = seccomp_parse_errno_or_action(p + 1);
if (e < 0)
return e;
n = strndup(in, p - in);
} else
n = strdup(in);
if (!n)
return -ENOMEM;
if (isempty(n))
return -EINVAL;
*error = e;
*name = TAKE_PTR(n);
return 0;
}
#endif
#endif /* NM_IGNORED */
static const char *mangle_base(const char *s, unsigned *base) {
@ -641,14 +598,13 @@ int safe_atod(const char *s, double *ret_d) {
}
int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
size_t i;
unsigned val = 0;
const char *s;
s = *p;
/* accept any number of digits, strtoull is limited to 19 */
for (i=0; i < digits; i++,s++) {
for (size_t i = 0; i < digits; i++,s++) {
if (*s < '0' || *s > '9') {
if (i == 0)
return -EINVAL;
@ -676,87 +632,6 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
return 0;
}
int parse_percent_unbounded(const char *p) {
const char *pc, *n;
int r, v;
pc = endswith(p, "%");
if (!pc)
return -EINVAL;
n = strndupa(p, pc - p);
r = safe_atoi(n, &v);
if (r < 0)
return r;
if (v < 0)
return -ERANGE;
return v;
}
int parse_percent(const char *p) {
int v;
v = parse_percent_unbounded(p);
if (v > 100)
return -ERANGE;
return v;
}
int parse_permille_unbounded(const char *p) {
const char *pc, *pm, *dot, *n;
int r, q, v;
pm = endswith(p, "");
if (pm) {
n = strndupa(p, pm - p);
r = safe_atoi(n, &v);
if (r < 0)
return r;
if (v < 0)
return -ERANGE;
} else {
pc = endswith(p, "%");
if (!pc)
return -EINVAL;
dot = memchr(p, '.', pc - p);
if (dot) {
if (dot + 2 != pc)
return -EINVAL;
if (dot[1] < '0' || dot[1] > '9')
return -EINVAL;
q = dot[1] - '0';
n = strndupa(p, dot - p);
} else {
q = 0;
n = strndupa(p, pc - p);
}
r = safe_atoi(n, &v);
if (r < 0)
return r;
if (v < 0)
return -ERANGE;
if (v > (INT_MAX - q) / 10)
return -ERANGE;
v = v * 10 + q;
}
return v;
}
int parse_permille(const char *p) {
int v;
v = parse_permille_unbounded(p);
if (v > 1000)
return -ERANGE;
return v;
}
int parse_nice(const char *p, int *ret) {
int n, r;

View file

@ -24,9 +24,6 @@ int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size);
int parse_range(const char *t, unsigned *lower, unsigned *upper);
int parse_errno(const char *t);
#if HAVE_SECCOMP
int parse_syscall_and_errno(const char *in, char **name, int *error);
#endif
#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30)
#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29)
@ -132,12 +129,6 @@ int safe_atod(const char *s, double *ret_d);
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
int parse_permille_unbounded(const char *p);
int parse_permille(const char *p);
int parse_nice(const char *p, int *ret);
int parse_ip_port(const char *s, uint16_t *ret);

View file

@ -595,26 +595,25 @@ char* path_join_internal(const char *first, ...) {
#if 0 /* NM_IGNORED */
static int check_x_access(const char *path, int *ret_fd) {
if (ret_fd) {
_cleanup_close_ int fd = -1;
int r;
_cleanup_close_ int fd = -1;
int r;
/* We need to use O_PATH because there may be executables for which we have only exec
* permissions, but not read (usually suid executables). */
fd = open(path, O_PATH|O_CLOEXEC);
if (fd < 0)
return -errno;
/* We need to use O_PATH because there may be executables for which we have only exec
* permissions, but not read (usually suid executables). */
fd = open(path, O_PATH|O_CLOEXEC);
if (fd < 0)
return -errno;
r = access_fd(fd, X_OK);
if (r < 0)
return r;
r = fd_verify_regular(fd);
if (r < 0)
return r;
r = access_fd(fd, X_OK);
if (r < 0)
return r;
if (ret_fd)
*ret_fd = TAKE_FD(fd);
} else {
/* Let's optimize things a bit by not opening the file if we don't need the fd. */
if (access(path, X_OK) < 0)
return -errno;
}
return 0;
}
@ -672,32 +671,20 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
return -ENOMEM;
r = check_x_access(j, ret_fd ? &fd : NULL);
if (r >= 0) {
_cleanup_free_ char *with_dash;
with_dash = strjoin(j, "/");
if (!with_dash)
return -ENOMEM;
/* If this passes, it must be a directory, and so should be skipped. */
if (access(with_dash, X_OK) >= 0)
continue;
/* We can't just `continue` inverting this case, since we need to update last_error. */
if (errno == ENOTDIR) {
/* Found it! */
if (ret_filename)
*ret_filename = path_simplify(TAKE_PTR(j), false);
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (r < 0) {
/* PATH entries which we don't have access to are ignored, as per tradition. */
if (r != -EACCES)
last_error = r;
continue;
}
/* PATH entries which we don't have access to are ignored, as per tradition. */
if (errno != EACCES)
last_error = -errno;
/* Found it! */
if (ret_filename)
*ret_filename = path_simplify(TAKE_PTR(j), false);
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
return last_error;
@ -774,38 +761,6 @@ int fsck_exists(const char *fstype) {
return executable_is_good(checker);
}
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
char *p;
int r;
/*
* This function is intended to be used in command line
* parsers, to handle paths that are passed in. It makes the
* path absolute, and reduces it to NULL if omitted or
* root (the latter optionally).
*
* NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
* SUCCESS! Hence, do not pass in uninitialized pointers.
*/
if (isempty(path)) {
*arg = mfree(*arg);
return 0;
}
r = path_make_absolute_cwd(path, &p);
if (r < 0)
return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
path_simplify(p, false);
if (suppress_root && empty_or_root(p))
p = mfree(p);
free_and_replace(*arg, p);
return 0;
}
char* dirname_malloc(const char *path) {
char *d, *dir, *dir2;
@ -845,6 +800,8 @@ const char *last_path_component(const char *path) {
* Also, the empty string is mapped to itself.
*
* This is different than basename(), which returns "" when a trailing slash is present.
*
* This always succeeds (except if you pass NULL in which case it returns NULL, too).
*/
unsigned l, k;
@ -870,24 +827,35 @@ const char *last_path_component(const char *path) {
int path_extract_filename(const char *p, char **ret) {
_cleanup_free_ char *a = NULL;
const char *c, *e = NULL, *q;
const char *c;
size_t n;
/* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
* slashes. Returns:
*
* -EINVAL if the passed in path is not a valid path
* -EADDRNOTAVAIL if only a directory was specified, but no filename, i.e. the root dir itself is specified
* -ENOMEM no memory
*
* Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to indicate
* the referenced file must be a directory.
*
* This function guarantees to return a fully valid filename, i.e. one that passes
* filename_is_valid() this means "." and ".." are not accepted. */
if (!p)
if (!path_is_valid(p))
return -EINVAL;
/* Special case the root dir, because in that case we simply have no filename, but
* last_path_component() won't complain */
if (path_equal(p, "/"))
return -EADDRNOTAVAIL;
c = last_path_component(p);
n = strcspn(c, "/");
for (q = c; *q != 0; q++)
if (*q != '/')
e = q + 1;
if (!e) /* no valid character? */
return -EINVAL;
a = strndup(c, e - c);
a = strndup(c, n);
if (!a)
return -ENOMEM;
@ -895,7 +863,48 @@ int path_extract_filename(const char *p, char **ret) {
return -EINVAL;
*ret = TAKE_PTR(a);
return c[n] == '/' ? O_DIRECTORY : 0;
}
int path_extract_directory(const char *p, char **ret) {
_cleanup_free_ char *a = NULL;
const char *c;
/* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
*
* -EINVAL if the passed in path is not a valid path
* -EDESTADDRREQ if no directory was specified in the passed in path, i.e. only a filename was passed
* -EADDRNOTAVAIL if the passed in parameter had no filename but did have a directory, i.e. the root dir itself was specified
* -ENOMEM no memory (surprise!)
*
* This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
*/
if (!path_is_valid(p))
return -EINVAL;
/* Special case the root dir, because otherwise for an input of "///" last_path_component() returns
* the pointer to the last slash only, which might be seen as a valid path below. */
if (path_equal(p, "/"))
return -EADDRNOTAVAIL;
c = last_path_component(p);
/* Delete trailing slashes, but keep one */
while (c > p+1 && c[-1] == '/')
c--;
if (p == c) /* No path whatsoever? Then return a recognizable error */
return -EDESTADDRREQ;
a = strndup(p, c - p);
if (!a)
return -ENOMEM;
if (!path_is_valid(a))
return -EINVAL;
*ret = TAKE_PTR(a);
return 0;
}
#endif /* NM_IGNORED */
@ -913,7 +922,7 @@ bool filename_is_valid(const char *p) {
if (*e != 0)
return false;
if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */
if (e - p > NAME_MAX) /* NAME_MAX is counted *without* the trailing NUL byte */
return false;
return true;
@ -924,10 +933,25 @@ bool path_is_valid(const char *p) {
if (isempty(p))
return false;
if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */
return false;
for (const char *e = p;;) {
size_t n;
return true;
/* Skip over slashes */
e += strspn(e, "/");
if (e - p >= PATH_MAX) /* Already reached the maximum length for a path? (PATH_MAX is counted
* *with* the trailing NUL byte) */
return false;
if (*e == 0) /* End of string? Yay! */
return true;
/* Skip over one component */
n = strcspn(e, "/");
if (n > NAME_MAX) /* One component larger than NAME_MAX? (NAME_MAX is counted *without* the
* trailing NUL byte) */
return false;
e += n;
}
}
bool path_is_normalized(const char *p) {

View file

@ -146,11 +146,10 @@ int fsck_exists(const char *fstype);
_ret; \
})
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg);
char* dirname_malloc(const char *path);
const char *last_path_component(const char *path);
int path_extract_filename(const char *p, char **ret);
int path_extract_directory(const char *p, char **ret);
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid(const char *p) _pure_;

View file

@ -8,7 +8,7 @@
typedef struct Prioq Prioq;
#define PRIOQ_IDX_NULL ((unsigned) -1)
#define PRIOQ_IDX_NULL (UINT_MAX)
Prioq *prioq_new(compare_func_t compare);
Prioq *prioq_free(Prioq *q);

View file

@ -130,14 +130,10 @@ int get_process_comm(pid_t pid, char **ret) {
}
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *t = NULL, *ans = NULL;
const char *p;
int r;
size_t k;
/* This is supposed to be a safety guard against runaway command lines. */
size_t max_length = sc_arg_max();
int r;
assert(line);
assert(pid >= 0);
@ -153,36 +149,18 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
* comm_fallback is false). Returns 0 and sets *line otherwise. */
p = procfs_file_alloca(pid, "cmdline");
r = fopen_unlocked(p, "re", &f);
r = read_full_virtual_file(p, &t, &k);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
/* We assume that each four-byte character uses one or two columns. If we ever check for combining
* characters, this assumption will need to be adjusted. */
if ((size_t) 4 * max_columns + 1 < max_columns)
max_length = MIN(max_length, (size_t) 4 * max_columns + 1);
t = new(char, max_length);
if (!t)
return -ENOMEM;
k = fread(t, 1, max_length, f);
if (k > 0) {
/* Arguments are separated by NULs. Let's replace those with spaces. */
for (size_t i = 0; i < k - 1; i++)
if (t[i] == '\0')
t[i] = ' ';
t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */
} else {
/* We only treat getting nothing as an error. We *could* also get an error after reading some
* data, but we ignore that case, as such an error is rather unlikely and we prefer to get
* some data rather than none. */
if (ferror(f))
return -errno;
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
@ -193,7 +171,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
if (r < 0)
return r;
mfree(t);
free(t);
t = strjoin("[", t2, "]");
if (!t)
return -ENOMEM;
@ -762,7 +740,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
/* Drop into a sigtimewait-based timeout. Waiting for the
* pid to exit. */
until = now(CLOCK_MONOTONIC) + timeout;
until = usec_add(now(CLOCK_MONOTONIC), timeout);
for (;;) {
usec_t n;
siginfo_t status = {};
@ -1240,6 +1218,11 @@ int safe_fork_full(
original_pid = getpid_cached();
if (flags & FORK_FLUSH_STDIO) {
fflush(stdout);
fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */
}
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
/* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
* be sure that SIGTERMs are not lost we might send to the child. */
@ -1472,7 +1455,11 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret
/* Spawns a temporary TTY agent, making sure it goes away when we go away */
r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid);
r = safe_fork_full(name,
except,
n_except,
FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG,
ret_pid);
if (r < 0)
return r;
if (r > 0)
@ -1553,7 +1540,7 @@ int pidfd_get_pid(int fd, pid_t *ret) {
xsprintf(path, "/proc/self/fdinfo/%i", fd);
r = read_full_file(path, &fdinfo, NULL);
r = read_full_virtual_file(path, &fdinfo, NULL);
if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */
return -ESRCH;
if (r < 0)
@ -1630,6 +1617,16 @@ 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);
}
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",

View file

@ -164,6 +164,7 @@ typedef enum ForkFlags {
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 */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
@ -201,3 +202,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
int pidfd_get_pid(int fd, pid_t *ret);
int setpriority_closest(int priority);
bool invoked_as(char *argv[], const char *token);

View file

@ -504,4 +504,23 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
return 1;
}
uint64_t random_u64_range(uint64_t m) {
uint64_t x, remainder;
/* Generates a random number in the range 0…m-1, unbiased. (Java's algorithm) */
if (m == 0) /* Let's take m == 0 as special case to return an integer from the full range */
return random_u64();
if (m == 1)
return 0;
remainder = UINT64_MAX % m;
do {
x = random_u64();
} while (x >= UINT64_MAX - remainder);
return x % m;
}
#endif /* NM_IGNORED */

View file

@ -40,3 +40,5 @@ int rdrand(unsigned long *ret);
size_t random_pool_size(void);
int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
uint64_t random_u64_range(uint64_t max);

View file

@ -21,7 +21,7 @@ bool ratelimit_below(RateLimit *r) {
ts = now(CLOCK_MONOTONIC);
if (r->begin <= 0 ||
ts - r->begin > r->interval) {
usec_sub_unsigned(ts, r->begin) > r->interval) {
r->begin = ts;
/* Reset counter */

View file

@ -152,3 +152,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret);
bool set_equal(Set *a, Set *b);

View file

@ -18,9 +18,9 @@ int reset_all_signal_handlers(void) {
.sa_handler = SIG_DFL,
.sa_flags = SA_RESTART,
};
int sig, r = 0;
int r = 0;
for (sig = 1; sig < _NSIG; sig++) {
for (int sig = 1; sig < _NSIG; sig++) {
/* These two cannot be caught... */
if (IN_SET(sig, SIGKILL, SIGSTOP))
@ -48,11 +48,14 @@ int reset_signal_mask(void) {
return 0;
}
static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) {
int r = 0;
int sigaction_many_internal(const struct sigaction *sa, ...) {
int sig, r = 0;
va_list ap;
va_start(ap, sa);
/* negative signal ends the list. 0 signal is skipped. */
for (; sig >= 0; sig = va_arg(ap, int)) {
while ((sig = va_arg(ap, int)) >= 0) {
if (sig == 0)
continue;
@ -63,49 +66,6 @@ static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) {
}
}
return r;
}
int sigaction_many(const struct sigaction *sa, ...) {
va_list ap;
int r;
va_start(ap, sa);
r = sigaction_many_ap(sa, 0, ap);
va_end(ap);
return r;
}
int ignore_signals(int sig, ...) {
static const struct sigaction sa = {
.sa_handler = SIG_IGN,
.sa_flags = SA_RESTART,
};
va_list ap;
int r;
va_start(ap, sig);
r = sigaction_many_ap(&sa, sig, ap);
va_end(ap);
return r;
}
int default_signals(int sig, ...) {
static const struct sigaction sa = {
.sa_handler = SIG_DFL,
.sa_flags = SA_RESTART,
};
va_list ap;
int r;
va_start(ap, sig);
r = sigaction_many_ap(&sa, sig, ap);
va_end(ap);
return r;
@ -201,7 +161,7 @@ static const char *const __signal_table[] = {
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
const char *signal_to_string(int signo) {
static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int) + 1];
static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)];
const char *name;
name = __signal_to_string(signo);

View file

@ -8,9 +8,28 @@
int reset_all_signal_handlers(void);
int reset_signal_mask(void);
int ignore_signals(int sig, ...);
int default_signals(int sig, ...);
int sigaction_many(const struct sigaction *sa, ...);
int sigaction_many_internal(const struct sigaction *sa, ...);
#define ignore_signals(...) \
sigaction_many_internal( \
&(const struct sigaction) { \
.sa_handler = SIG_IGN, \
.sa_flags = SA_RESTART \
}, \
__VA_ARGS__, \
-1)
#define default_signals(...) \
sigaction_many_internal( \
&(const struct sigaction) { \
.sa_handler = SIG_DFL, \
.sa_flags = SA_RESTART \
}, \
__VA_ARGS__, \
-1)
#define sigaction_many(sa, ...) \
sigaction_many_internal(sa, __VA_ARGS__, -1)
int sigset_add_many(sigset_t *ss, ...);
int sigprocmask_many(int how, sigset_t *old, ...);

View file

@ -35,6 +35,7 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "sysctl-util.h"
#include "user-util.h"
#include "utf8.h"
@ -282,10 +283,48 @@ const char* socket_address_get_path(const SocketAddress *a) {
}
bool socket_ipv6_is_supported(void) {
if (access("/proc/net/if_inet6", F_OK) != 0)
static int cached = -1;
if (cached < 0) {
if (access("/proc/net/if_inet6", F_OK) < 0) {
if (errno != ENOENT) {
log_debug_errno(errno, "Unexpected error when checking whether /proc/net/if_inet6 exists: %m");
return false;
}
cached = false;
} else
cached = true;
}
return cached;
}
bool socket_ipv6_is_enabled(void) {
_cleanup_free_ char *v = NULL;
int r;
/* Much like socket_ipv6_is_supported(), but also checks that the sysctl that disables IPv6 on all
* interfaces isn't turned on */
if (!socket_ipv6_is_supported())
return false;
return true;
r = sysctl_read_ip_property(AF_INET6, "all", "disable_ipv6", &v);
if (r < 0) {
log_debug_errno(r, "Unexpected error reading 'net.ipv6.conf.all.disable_ipv6' sysctl: %m");
return true;
}
r = parse_boolean(v);
if (r < 0) {
log_debug_errno(r, "Failed to pare 'net.ipv6.conf.all.disable_ipv6' sysctl: %m");
return true;
}
return !r;
}
bool socket_address_matches_fd(const SocketAddress *a, int fd) {
@ -726,6 +765,10 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
if (isempty(p))
return false;
/* A valid ifindex? If so, it's valid iff IFNAME_VALID_NUMERIC is set */
if (parse_ifindex(p) >= 0)
return flags & IFNAME_VALID_NUMERIC;
if (flags & IFNAME_VALID_ALTERNATIVE) {
if (strlen(p) >= ALTIFNAMSIZ)
return false;
@ -737,6 +780,11 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
if (dot_or_dot_dot(p))
return false;
/* Let's refuse "all" and "default" as interface name, to avoid collisions with the special sysctl
* directories /proc/sys/net/{ipv4,ipv6}/conf/{all,default} */
if (STR_IN_SET(p, "all", "default"))
return false;
for (const char *t = p; *t; t++) {
if ((unsigned char) *t >= 127U)
return false;
@ -744,20 +792,19 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
if ((unsigned char) *t <= 32U)
return false;
if (IN_SET(*t, ':', '/'))
if (IN_SET(*t,
':', /* colons are used by the legacy "alias" interface logic */
'/', /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */
'%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */
return false;
numeric = numeric && (*t >= '0' && *t <= '9');
}
if (numeric) {
if (!(flags & IFNAME_VALID_NUMERIC))
return false;
/* Verify that the number is well-formatted and in range. */
if (parse_ifindex(p) < 0)
return false;
}
/* It's fully numeric but didn't parse as valid ifindex above? if so, it must be too large or zero or
* so, let's refuse that. */
if (numeric)
return false;
return true;
}

View file

@ -65,7 +65,7 @@ typedef enum SocketAddressBindIPv6Only {
SOCKET_ADDRESS_BOTH,
SOCKET_ADDRESS_IPV6_ONLY,
_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
_SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
_SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -EINVAL,
} SocketAddressBindIPv6Only;
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
@ -103,6 +103,7 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_
const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
bool socket_ipv6_is_enabled(void);
int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa);
@ -135,9 +136,9 @@ int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
typedef enum {
IFNAME_VALID_ALTERNATIVE = 1 << 0,
IFNAME_VALID_NUMERIC = 1 << 1,
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
IFNAME_VALID_ALTERNATIVE = 1 << 0,
IFNAME_VALID_NUMERIC = 1 << 1,
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
} IfnameValidFlags;
bool ifname_valid_full(const char *p, IfnameValidFlags flags);
static inline bool ifname_valid(const char *p) {

View file

@ -77,12 +77,17 @@ int dir_is_empty_at(int dir_fd, const char *path) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
if (path)
if (path) {
fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
else
fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (fd < 0)
return -errno;
if (fd < 0)
return -errno;
} else {
/* Note that DUPing is not enough, as the internal pointer
* would still be shared and moved by FOREACH_DIRENT. */
fd = fd_reopen(dir_fd, O_CLOEXEC);
if (fd < 0)
return fd;
}
d = take_fdopendir(&fd);
if (!d)

View file

@ -7,11 +7,11 @@
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
if (!key)
return -1;
return -EINVAL;
for (size_t i = 0; i < len; ++i)
if (streq_ptr(table[i], key))
return (ssize_t) i;
return -1;
return -EINVAL;
}

View file

@ -29,7 +29,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
scope type name##_from_string(const char *s) { \
if (!s) \
return -1; \
return -EINVAL; \
int b = parse_boolean(s); \
if (b == 0) \
return (type) 0; \
@ -60,14 +60,16 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
unsigned u = 0; \
type i; \
if (!s) \
return (type) -1; \
return -EINVAL; \
i = (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
if (i >= 0) \
return i; \
if (safe_atou(s, &u) >= 0 && u <= max) \
return (type) u; \
return (type) -1; \
} \
if (safe_atou(s, &u) < 0) \
return -EINVAL; \
if (u > max) \
return -EINVAL; \
return (type) u; \
}
#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
@ -79,12 +81,15 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
#define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,)
#define DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,static)
/* For string conversions where numbers are also acceptable */
#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
@ -98,9 +103,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define DUMP_STRING_TABLE(name,type,max) \
do { \
type _k; \
flockfile(stdout); \
for (_k = 0; _k < (max); _k++) { \
for (type _k = 0; _k < (max); _k++) { \
const char *_t; \
_t = name##_to_string(_k); \
if (!_t) \

View file

@ -22,64 +22,6 @@
#include "utf8.h"
#include "util.h"
int strcmp_ptr(const char *a, const char *b) {
/* Like strcmp(), but tries to make sense of NULL pointers */
if (a && b)
return strcmp(a, b);
return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */
}
int strcasecmp_ptr(const char *a, const char *b) {
/* Like strcasecmp(), but tries to make sense of NULL pointers */
if (a && b)
return strcasecmp(a, b);
return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */
}
char* endswith(const char *s, const char *postfix) {
size_t sl, pl;
assert(s);
assert(postfix);
sl = strlen(s);
pl = strlen(postfix);
if (pl == 0)
return (char*) s + sl;
if (sl < pl)
return NULL;
if (memcmp(s + sl - pl, postfix, pl) != 0)
return NULL;
return (char*) s + sl - pl;
}
char* endswith_no_case(const char *s, const char *postfix) {
size_t sl, pl;
assert(s);
assert(postfix);
sl = strlen(s);
pl = strlen(postfix);
if (pl == 0)
return (char*) s + sl;
if (sl < pl)
return NULL;
if (strcasecmp(s + sl - pl, postfix) != 0)
return NULL;
return (char*) s + sl - pl;
}
char* first_word(const char *s, const char *word) {
size_t sl, wl;
const char *p;
@ -131,7 +73,7 @@ char *strnappend(const char *s, const char *suffix, size_t b) {
assert(suffix);
a = strlen(s);
if (b > ((size_t) -1) - a)
if (b > SIZE_MAX - a)
return NULL;
r = new(char, a+b+1);
@ -370,7 +312,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
assert(s);
assert(percent <= 100);
assert(new_length != (size_t) -1);
assert(new_length != SIZE_MAX);
if (old_length <= new_length)
return strndup(s, old_length);
@ -441,7 +383,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
assert(s);
assert(percent <= 100);
if (new_length == (size_t) -1)
if (new_length == SIZE_MAX)
return strndup(s, old_length);
if (new_length == 0)
@ -870,9 +812,8 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) {
#endif /* NM_IGNORED */
char *strrep(const char *s, unsigned n) {
size_t l;
char *r, *p;
unsigned i;
size_t l;
assert(s);
@ -881,7 +822,7 @@ char *strrep(const char *s, unsigned n) {
if (!r)
return NULL;
for (i = 0; i < n; i++)
for (unsigned i = 0; i < n; i++)
p = stpcpy(p, s);
*p = 0;

View file

@ -7,6 +7,7 @@
#include "alloc-util.h"
#include "macro.h"
#include "string-util-fundamental.h"
/* What is interpreted as whitespace? */
#define WHITESPACE " \t\n\r"
@ -21,18 +22,6 @@
#define ALPHANUMERICAL LETTERS DIGITS
#define HEXDIGITS DIGITS "abcdefABCDEF"
#define streq(a,b) (strcmp((a),(b)) == 0)
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
int strcmp_ptr(const char *a, const char *b) _pure_;
int strcasecmp_ptr(const char *a, const char *b) _pure_;
static inline bool streq_ptr(const char *a, const char *b) {
return strcmp_ptr(a, b) == 0;
}
static inline char* strstr_ptr(const char *haystack, const char *needle) {
if (!haystack || !needle)
return NULL;
@ -51,10 +40,6 @@ static inline const char *strna(const char *s) {
return s ?: "n/a";
}
static inline const char* yes_no(bool b) {
return b ? "yes" : "no";
}
static inline const char* true_false(bool b) {
return b ? "true" : "false";
}
@ -71,10 +56,6 @@ static inline const char* enable_disable(bool b) {
return b ? "enable" : "disable";
}
static inline bool isempty(const char *p) {
return !p || !p[0];
}
static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p;
}
@ -93,29 +74,6 @@ static inline const char *empty_or_dash_to_null(const char *p) {
return empty_or_dash(p) ? NULL : p;
}
static inline char *startswith(const char *s, const char *prefix) {
size_t l;
l = strlen(prefix);
if (strncmp(s, prefix, l) == 0)
return (char*) s + l;
return NULL;
}
static inline char *startswith_no_case(const char *s, const char *prefix) {
size_t l;
l = strlen(prefix);
if (strncasecmp(s, prefix, l) == 0)
return (char*) s + l;
return NULL;
}
char *endswith(const char *s, const char *postfix) _pure_;
char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
char *strnappend(const char *s, const char *suffix, size_t length);

View file

@ -244,27 +244,28 @@ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
}
#endif /* NM_IGNORED */
char **strv_split_newlines(const char *s) {
char **l;
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
_cleanup_strv_free_ char **l = NULL;
size_t n;
int r;
assert(s);
/* Special version of strv_split() that splits on newlines and
* suppresses an empty string at the end */
/* Special version of strv_split_full() that splits on newlines and
* suppresses an empty string at the end. */
l = strv_split(s, NEWLINE);
if (!l)
return NULL;
r = strv_split_full(&l, s, NEWLINE, flags);
if (r < 0)
return r;
n = strv_length(l);
if (n <= 0)
return l;
if (isempty(l[n - 1]))
if (n > 0 && isempty(l[n - 1])) {
l[n - 1] = mfree(l[n - 1]);
n--;
}
return l;
*ret = TAKE_PTR(l);
return n;
}
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {

View file

@ -73,15 +73,21 @@ static inline bool strv_isempty(char * const *l) {
return !l || !*l;
}
char **strv_split_newlines(const char *s);
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags);
static inline char **strv_split(const char *s, const char *separators) {
char **ret;
int r;
r = strv_split_full(&ret, s, separators, 0);
if (r < 0)
if (strv_split_full(&ret, s, separators, 0) < 0)
return NULL;
return ret;
}
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags);
static inline char **strv_split_newlines(const char *s) {
char **ret;
if (strv_split_newlines_full(&ret, s, 0) < 0)
return NULL;
return ret;

View file

@ -497,7 +497,6 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
{ "us", 1 },
};
size_t i;
char *p = buf;
bool something = false;
@ -518,7 +517,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
/* The result of this function can be parsed with parse_sec */
for (i = 0; i < ELEMENTSOF(table); i++) {
for (size_t i = 0; i < ELEMENTSOF(table); i++) {
int k = 0;
size_t n;
bool done = false;
@ -967,9 +966,8 @@ static const char* extract_multiplier(const char *p, usec_t *multiplier) {
{ "us", 1ULL },
{ "µs", 1ULL },
};
size_t i;
for (i = 0; i < ELEMENTSOF(table); i++) {
for (size_t i = 0; i < ELEMENTSOF(table); i++) {
char *e;
e = startswith(p, table[i].suffix);

View file

@ -35,7 +35,7 @@ typedef enum TimestampStyle {
TIMESTAMP_UTC,
TIMESTAMP_US_UTC,
_TIMESTAMP_STYLE_MAX,
_TIMESTAMP_STYLE_INVALID = -1,
_TIMESTAMP_STYLE_INVALID = -EINVAL,
} TimestampStyle;
#define USEC_INFINITY ((usec_t) UINT64_MAX)
@ -155,16 +155,14 @@ usec_t jiffies_to_usec(uint32_t jiffies);
bool in_utc_timezone(void);
static inline usec_t usec_add(usec_t a, usec_t b) {
usec_t c;
/* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't
* overflow. */
c = a + b;
if (c < a || c < b) /* overflow check */
if (a > USEC_INFINITY - b) /* overflow check */
return USEC_INFINITY;
return c;
return a + b;
}
static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) {

View file

@ -98,16 +98,11 @@ int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
#endif /* NM_IGNORED */
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
const char *fn;
char *t;
_cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL;
int r;
assert(ret);
if (isempty(p))
return -EINVAL;
if (path_equal(p, "/"))
return -EINVAL;
/*
* Turns this:
* /foo/bar/waldo
@ -116,36 +111,42 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
* /foo/bar/.#<extra>waldoXXXXXX
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
r = path_extract_directory(p, &d);
if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
return r;
extra = strempty(extra);
r = path_extract_filename(p, &fn);
if (r < 0)
return r;
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
if (!t)
nf = strjoin(".#", strempty(extra), fn, "XXXXXX");
if (!nf)
return -ENOMEM;
strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
if (!filename_is_valid(nf)) /* New name is not valid? (Maybe because too long?) Refuse. */
return -EINVAL;
if (d) {
char *j;
j = path_join(d, nf);
if (!j)
return -ENOMEM;
*ret = path_simplify(j, false);
} else
*ret = TAKE_PTR(nf);
*ret = path_simplify(t, false);
return 0;
}
#if 0 /* NM_IGNORED */
int tempfn_random(const char *p, const char *extra, char **ret) {
const char *fn;
char *t, *x;
uint64_t u;
unsigned i;
_cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL;
int r;
assert(ret);
if (isempty(p))
return -EINVAL;
if (path_equal(p, "/"))
return -EINVAL;
/*
* Turns this:
* /foo/bar/waldo
@ -154,34 +155,40 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
* /foo/bar/.#<extra>waldobaa2a261115984a9
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
r = path_extract_directory(p, &d);
if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
return r;
extra = strempty(extra);
r = path_extract_filename(p, &fn);
if (r < 0)
return r;
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
if (!t)
if (asprintf(&nf, ".#%s%s%016" PRIx64,
strempty(extra),
fn,
random_u64()) < 0)
return -ENOMEM;
x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
if (!filename_is_valid(nf)) /* Not valid? (maybe because too long now?) — refuse early */
return -EINVAL;
u = random_u64();
for (i = 0; i < 16; i++) {
*(x++) = hexchar(u & 0xF);
u >>= 4;
}
if (d) {
char *j;
*x = 0;
j = path_join(d, nf);
if (!j)
return -ENOMEM;
*ret = path_simplify(j, false);
} else
*ret = TAKE_PTR(nf);
*ret = path_simplify(t, false);
return 0;
}
int tempfn_random_child(const char *p, const char *extra, char **ret) {
char *t, *x;
uint64_t u;
unsigned i;
int r;
assert(ret);
@ -210,7 +217,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
u = random_u64();
for (i = 0; i < 16; i++) {
for (unsigned i = 0; i < 16; i++) {
*(x++) = hexchar(u & 0xF);
u >>= 4;
}

View file

@ -83,7 +83,7 @@ static size_t utf8_encoded_expected_len(uint8_t c) {
/* decode one unicode char */
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
char32_t unichar;
size_t len, i;
size_t len;
assert(str);
@ -112,7 +112,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
return -EINVAL;
}
for (i = 1; i < len; i++) {
for (size_t i = 1; i < len; i++) {
if (((char32_t)str[i] & 0xc0) != 0x80)
return -EINVAL;
@ -158,14 +158,14 @@ char *utf8_is_valid_n(const char *str, size_t len_bytes) {
assert(str);
for (const char *p = str; len_bytes != (size_t) -1 ? (size_t) (p - str) < len_bytes : *p != '\0'; ) {
for (const char *p = str; len_bytes != SIZE_MAX ? (size_t) (p - str) < len_bytes : *p != '\0'; ) {
int len;
if (_unlikely_(*p == '\0') && len_bytes != (size_t) -1)
if (_unlikely_(*p == '\0') && len_bytes != SIZE_MAX)
return NULL; /* embedded NUL */
len = utf8_encoded_valid_unichar(p,
len_bytes != (size_t) -1 ? len_bytes - (p - str) : (size_t) -1);
len_bytes != SIZE_MAX ? len_bytes - (p - str) : SIZE_MAX);
if (_unlikely_(len < 0))
return NULL; /* invalid character */
@ -187,7 +187,7 @@ char *utf8_escape_invalid(const char *str) {
while (*str) {
int len;
len = utf8_encoded_valid_unichar(str, (size_t) -1);
len = utf8_encoded_valid_unichar(str, SIZE_MAX);
if (len > 0) {
s = mempcpy(s, str, len);
str += len;
@ -236,7 +236,7 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
if (!*str) /* done! */
goto finish;
len = utf8_encoded_valid_unichar(str, (size_t) -1);
len = utf8_encoded_valid_unichar(str, SIZE_MAX);
if (len > 0) {
if (utf8_is_printable(str, len)) {
int w;
@ -307,14 +307,12 @@ char *ascii_is_valid(const char *str) {
#if 0 /* NM_IGNORED */
char *ascii_is_valid_n(const char *str, size_t len) {
size_t i;
/* Very similar to ascii_is_valid(), but checks exactly len
* bytes and rejects any NULs in that range. */
assert(str);
for (i = 0; i < len; i++)
for (size_t i = 0; i < len; i++)
if ((unsigned char) str[i] >= 128 || str[i] == 0)
return NULL;
@ -443,7 +441,6 @@ size_t utf16_encode_unichar(char16_t *out, char32_t c) {
char16_t *utf8_to_utf16(const char *s, size_t length) {
char16_t *n, *p;
size_t i;
int r;
assert(s);
@ -454,7 +451,7 @@ char16_t *utf8_to_utf16(const char *s, size_t length) {
p = n;
for (i = 0; i < length;) {
for (size_t i = 0; i < length;) {
char32_t unichar;
size_t e;
@ -513,13 +510,13 @@ static int utf8_unichar_to_encoded_len(char32_t unichar) {
/* validate one encoded unicode char and return its length */
int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) {
char32_t unichar;
size_t len, i;
size_t len;
int r;
assert(str);
assert(length > 0);
/* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */
/* We read until NUL, at most length bytes. SIZE_MAX may be used to disable the length check. */
len = utf8_encoded_expected_len(str[0]);
if (len == 0)
@ -534,7 +531,7 @@ int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) {
return 1;
/* check if expected encoded chars are available */
for (i = 0; i < len; i++)
for (size_t i = 0; i < len; i++)
if ((str[i] & 0x80) != 0x80)
return -EINVAL;
@ -557,14 +554,14 @@ int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) {
size_t utf8_n_codepoints(const char *str) {
size_t n = 0;
/* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */
/* Returns the number of UTF-8 codepoints in this string, or SIZE_MAX if the string is not valid UTF-8. */
while (*str != 0) {
int k;
k = utf8_encoded_valid_unichar(str, (size_t) -1);
k = utf8_encoded_valid_unichar(str, SIZE_MAX);
if (k < 0)
return (size_t) -1;
return SIZE_MAX;
str += k;
n++;
@ -584,7 +581,7 @@ size_t utf8_console_width(const char *str) {
w = utf8_char_console_width(str);
if (w < 0)
return (size_t) -1;
return SIZE_MAX;
n += w;
str = utf8_next_char(str);

View file

@ -16,7 +16,7 @@ bool unichar_is_valid(char32_t c);
char *utf8_is_valid_n(const char *str, size_t len_bytes) _pure_;
static inline char *utf8_is_valid(const char *s) {
return utf8_is_valid_n(s, (size_t) -1);
return utf8_is_valid_n(s, SIZE_MAX);
}
char *ascii_is_valid(const char *s) _pure_;
char *ascii_is_valid_n(const char *str, size_t len);
@ -27,7 +27,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
char *utf8_escape_invalid(const char *s);
char *utf8_escape_non_printable_full(const char *str, size_t console_width);
static inline char *utf8_escape_non_printable(const char *str) {
return utf8_escape_non_printable_full(str, (size_t) -1);
return utf8_escape_non_printable_full(str, SIZE_MAX);
}
size_t utf8_encode_unichar(char *out_utf8, char32_t g);

View file

@ -55,13 +55,14 @@ int prot_from_flags(int flags) {
}
bool in_initrd(void) {
struct statfs s;
int r;
const char *e;
bool lenient = false;
if (saved_in_initrd >= 0)
return saved_in_initrd;
/* We make two checks here:
/* 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
@ -69,18 +70,46 @@ bool in_initrd(void) {
* 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.
*/
r = getenv_bool_secure("SYSTEMD_IN_INITRD");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
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");
if (r >= 0)
saved_in_initrd = r > 0;
else
saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
statfs("/", &s) >= 0 &&
is_temporary_fs(&s);
}
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;
}
@ -201,68 +230,6 @@ int version(void) {
return 0;
}
/* This is a direct translation of str_verscmp from boot.c */
static bool is_digit(int c) {
return c >= '0' && c <= '9';
}
static int c_order(int c) {
if (c == 0 || is_digit(c))
return 0;
if ((c >= 'a') && (c <= 'z'))
return c;
return c + 0x10000;
}
int str_verscmp(const char *s1, const char *s2) {
const char *os1, *os2;
assert(s1);
assert(s2);
os1 = s1;
os2 = s2;
while (*s1 || *s2) {
int first;
while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
int order;
order = c_order(*s1) - c_order(*s2);
if (order != 0)
return order;
s1++;
s2++;
}
while (*s1 == '0')
s1++;
while (*s2 == '0')
s2++;
first = 0;
while (is_digit(*s1) && is_digit(*s2)) {
if (first == 0)
first = *s1 - *s2;
s1++;
s2++;
}
if (is_digit(*s1))
return 1;
if (is_digit(*s2))
return -1;
if (first != 0)
return first;
}
return strcmp(os1, os2);
}
/* Turn off core dumps but only if we're running outside of a container. */
void disable_coredumps(void) {
int r;

View file

@ -63,6 +63,4 @@ int container_get_leader(const char *machine, pid_t *pid);
int version(void);
int str_verscmp(const char *s1, const char *s2);
void disable_coredumps(void);

View file

@ -0,0 +1,201 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#ifndef SD_BOOT
#include <assert.h>
#endif
#include "type.h"
#define _const_ __attribute__((__const__))
#define _pure_ __attribute__((__pure__))
#define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x)))
#ifndef __COVERITY__
# define VOID_0 ((void)0)
#else
# define VOID_0 ((void*)0)
#endif
#define ELEMENTSOF(x) \
(__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
sizeof(x)/sizeof((x)[0]), \
VOID_0))
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#ifdef SD_BOOT
#define assert(expr) do {} while (false)
#endif
#if defined(static_assert)
#define assert_cc(expr) \
static_assert(expr, #expr)
#else
#define assert_cc(expr) \
struct CONCATENATE(_assert_struct_, __COUNTER__) { \
char x[(expr) ? 0 : -1]; \
}
#endif
#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
#define UNIQ __COUNTER__
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define CONST_MAX(_A, _B) \
(__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) > (_B)) ? (_A) : (_B), \
VOID_0))
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
#define MAX3(x, y, z) \
({ \
const typeof(x) _c = MAX(x, y); \
MAX(_c, z); \
})
#define MAX4(x, y, z, a) \
({ \
const typeof(x) _d = MAX3(x, y, z); \
MAX(_d, a); \
})
#undef MIN
#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b))
#define __MIN(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
})
/* evaluates to (void) if _A or _B are not constant or of different types */
#define CONST_MIN(_A, _B) \
(__builtin_choose_expr( \
__builtin_constant_p(_A) && \
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) < (_B)) ? (_A) : (_B), \
VOID_0))
#define MIN3(x, y, z) \
({ \
const typeof(x) _c = MIN(x, y); \
MIN(_c, z); \
})
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \
})
#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b))
#define __CMP(aq, a, bq, b) \
({ \
const typeof(a) UNIQ_T(A, aq) = (a); \
const typeof(b) UNIQ_T(B, bq) = (b); \
UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \
})
#undef CLAMP
#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high))
#define __CLAMP(xq, x, lowq, low, highq, high) \
({ \
const typeof(x) UNIQ_T(X, xq) = (x); \
const typeof(low) UNIQ_T(LOW, lowq) = (low); \
const typeof(high) UNIQ_T(HIGH, highq) = (high); \
UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \
UNIQ_T(HIGH, highq) : \
UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \
UNIQ_T(LOW, lowq) : \
UNIQ_T(X, xq); \
})
/* [(x + y - 1) / y] suffers from an integer overflow, even though the
* computation should be possible in the given type. Therefore, we use
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
* quotient and the remainder, so both should be equally fast. */
#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
#define __DIV_ROUND_UP(xq, x, yq, y) \
({ \
const typeof(x) UNIQ_T(X, xq) = (x); \
const typeof(y) UNIQ_T(Y, yq) = (y); \
(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 GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
#define FOR_EACH_MAKE_CASE(...) \
GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \
CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
(CASE_F,__VA_ARGS__)
#define IN_SET(x, ...) \
({ \
sd_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__ }; \
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
switch(x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
_found = true; \
break; \
default: \
break; \
} \
_found; \
})
/* 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) _ptr_ = (ptr); \
(ptr) = NULL; \
_ptr_; \
})

View file

@ -0,0 +1,234 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef SD_BOOT
#include <ctype.h>
#include "macro.h"
#endif
#include "string-util-fundamental.h"
sd_char *startswith(const sd_char *s, const sd_char *prefix) {
sd_size_t l;
assert(s);
assert(prefix);
l = strlen(prefix);
if (!strneq(s, prefix, l))
return NULL;
return (sd_char*) s + l;
}
#ifndef SD_BOOT
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
sd_size_t l;
assert(s);
assert(prefix);
l = strlen(prefix);
if (!strncaseeq(s, prefix, l))
return NULL;
return (sd_char*) s + l;
}
#endif
sd_char* endswith(const sd_char *s, const sd_char *postfix) {
sd_size_t sl, pl;
assert(s);
assert(postfix);
sl = strlen(s);
pl = strlen(postfix);
if (pl == 0)
return (sd_char*) s + sl;
if (sl < pl)
return NULL;
if (strcmp(s + sl - pl, postfix) != 0)
return NULL;
return (sd_char*) s + sl - pl;
}
sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
sd_size_t sl, pl;
assert(s);
assert(postfix);
sl = strlen(s);
pl = strlen(postfix);
if (pl == 0)
return (sd_char*) s + sl;
if (sl < pl)
return NULL;
if (strcasecmp(s + sl - pl, postfix) != 0)
return NULL;
return (sd_char*) s + sl - pl;
}
#ifdef SD_BOOT
static sd_bool isdigit(sd_char a) {
return a >= '0' && a <= '9';
}
#endif
static sd_bool is_alpha(sd_char a) {
/* Locale independent version of isalpha(). */
return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
}
static sd_bool is_valid_version_char(sd_char a) {
return isdigit(a) || is_alpha(a) || IN_SET(a, '~', '-', '^', '.');
}
sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
/* This is based on RPM's rpmvercmp(). But this explicitly handles '-' and '.', as we usually
* want to directly compare strings which contain both version and release; e.g.
* '247.2-3.1.fc33.x86_64' or '5.11.0-0.rc5.20210128git76c057c84d28.137.fc34'.
* Unlike rpmvercmp(), this distiguishes e.g. 123a and 123.a, and 123a is newer.
*
* This splits the input strings into segments. Each segment is numeric or alpha, and may be
* prefixed with the following:
* '~' : used for pre-releases, a segment prefixed with this is the oldest,
* '-' : used for the separator between version and release,
* '^' : used for patched releases, a segment with this is newer than one with '-'.
* '.' : used for point releases.
* Note, no prefix segment is the newest. All non-supported characters are dropped, and
* handled as a separator of segments, e.g., 123_a is equivalent to 123a.
*
* By using this, version strings can be sorted like following:
* (older) 122.1
* ^ 123~rc1-1
* | 123
* | 123-a
* | 123-a.1
* | 123-1
* | 123-1.1
* | 123^post1
* | 123.a-1
* | 123.1-1
* v 123a-1
* (newer) 124-1
*/
if (isempty(a) || isempty(b))
return strcmp_ptr(a, b);
for (;;) {
const sd_char *aa, *bb;
sd_int r;
/* Drop leading invalid characters. */
while (*a != '\0' && !is_valid_version_char(*a))
a++;
while (*b != '\0' && !is_valid_version_char(*b))
b++;
/* Handle '~'. Used for pre-releases, e.g. 123~rc1, or 4.5~alpha1 */
if (*a == '~' || *b == '~') {
/* The string prefixed with '~' is older. */
r = CMP(*a != '~', *b != '~');
if (r != 0)
return r;
/* Now both strings are prefixed with '~'. Compare remaining strings. */
a++;
b++;
}
/* If at least one string reaches the end, then longer is newer.
* Note that except for '~' prefixed segments, a string has more segments is newer.
* So, this check must be after the '~' check. */
if (*a == '\0' || *b == '\0')
return strcmp(a, b);
/* Handle '-', which separates version and release, e.g 123.4-3.1.fc33.x86_64 */
if (*a == '-' || *b == '-') {
/* The string prefixed with '-' is older (e.g., 123-9 vs 123.1-1) */
r = CMP(*a != '-', *b != '-');
if (r != 0)
return r;
a++;
b++;
}
/* Handle '^'. Used for patched release. */
if (*a == '^' || *b == '^') {
r = CMP(*a != '^', *b != '^');
if (r != 0)
return r;
a++;
b++;
}
/* Handle '.'. Used for point releases. */
if (*a == '.' || *b == '.') {
r = CMP(*a != '.', *b != '.');
if (r != 0)
return r;
a++;
b++;
}
if (isdigit(*a) || isdigit(*b)) {
/* Skip leading '0', to make 00123 equivalent to 123. */
while (*a == '0')
a++;
while (*b == '0')
b++;
/* Find the leading numeric segments. One may be an empty string. So,
* numeric segments are always newer than alpha segments. */
for (aa = a; *aa != '\0' && isdigit(*aa); aa++)
;
for (bb = b; *bb != '\0' && isdigit(*bb); bb++)
;
/* To compare numeric segments without parsing their values, first compare the
* lengths of the segments. Eg. 12345 vs 123, longer is newer. */
r = CMP(aa - a, bb - b);
if (r != 0)
return r;
/* Then, compare them as strings. */
r = strncmp(a, b, aa - a);
if (r != 0)
return r;
} else {
/* Find the leading non-numeric segments. */
for (aa = a; *aa != '\0' && is_alpha(*aa); aa++)
;
for (bb = b; *bb != '\0' && is_alpha(*bb); bb++)
;
/* Note that the segments are usually not NUL-terminated. */
r = strncmp(a, b, MIN(aa - a, bb - b));
if (r != 0)
return r;
/* Longer is newer, e.g. abc vs abcde. */
r = CMP(aa - a, bb - b);
if (r != 0)
return r;
}
/* The current segments are equivalent. Let's compare the next one. */
a = aa;
b = bb;
}
}

View file

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#ifdef SD_BOOT
#include <efi.h>
#include <efilib.h>
#else
#include <string.h>
#endif
#include "macro-fundamental.h"
#ifdef SD_BOOT
#define strlen(a) StrLen((a))
#define strcmp(a, b) StrCmp((a), (b))
#define strncmp(a, b, n) StrnCmp((a), (b), (n))
#define strcasecmp(a, b) StriCmp((a), (b))
#define STR_C(str) (L ## str)
#else
#define STR_C(str) (str)
#endif
#define streq(a,b) (strcmp((a),(b)) == 0)
#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
#ifndef SD_BOOT
#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
#endif
static inline sd_int strcmp_ptr(const sd_char *a, const sd_char *b) {
if (a && b)
return strcmp(a, b);
return CMP(a, b);
}
static inline sd_int strcasecmp_ptr(const sd_char *a, const sd_char *b) {
if (a && b)
return strcasecmp(a, b);
return CMP(a, b);
}
static inline sd_bool streq_ptr(const sd_char *a, const sd_char *b) {
return strcmp_ptr(a, b) == 0;
}
static inline sd_bool strcaseeq_ptr(const sd_char *a, const sd_char *b) {
return strcasecmp_ptr(a, b) == 0;
}
sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
#ifndef 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_;
sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
static inline sd_bool isempty(const sd_char *a) {
return !a || a[0] == '\0';
}
static inline const sd_char *yes_no(sd_bool b) {
return b ? STR_C("yes") : STR_C("no");
}
sd_int strverscmp_improved(const sd_char *a, const sd_char *b);

View file

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#ifdef SD_BOOT
#include <efi.h>
typedef BOOLEAN sd_bool;
typedef CHAR16 sd_char;
typedef INTN sd_int;
typedef UINTN sd_size_t;
#define true TRUE
#define false FALSE
#else
#include <stdbool.h>
#include <stdint.h>
typedef bool sd_bool;
typedef char sd_char;
typedef int sd_int;
typedef size_t sd_size_t;
#endif

View file

@ -6,24 +6,10 @@
#include <stddef.h>
#include <stdint.h>
#include "dns-def.h"
#include "hashmap.h"
#include "in-addr-util.h"
/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */
#define DNS_LABEL_MAX 63
/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */
#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1)
/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */
#define DNS_HOSTNAME_MAX 253
/* Maximum length of a full hostname, on the wire, including the final NUL byte */
#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255
/* Maximum number of labels per valid hostname */
#define DNS_N_LABELS_MAX 127
typedef enum DNSLabelFlags {
DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */
DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */