mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-01 04:50:11 +01:00
systemd: merge branch systemd into main
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/876
This commit is contained in:
commit
aa76c260a7
76 changed files with 1853 additions and 1022 deletions
|
|
@ -1246,9 +1246,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin)
|
|||
/* dirname_libs are a set of read-only directories with lower priority than /etc or /run.
|
||||
* There is nothing complicated about having multiple of such directories, so dirname_libs
|
||||
* is a list (which currently only has at most one directory). */
|
||||
priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB), FALSE);
|
||||
priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB));
|
||||
priv->dirname_libs[1] = NULL;
|
||||
priv->dirname_run = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN), FALSE);
|
||||
priv->dirname_run = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN));
|
||||
priv->dirname_etc = nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG,
|
||||
NM_CONFIG_KEYFILE_GROUP_KEYFILE,
|
||||
NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH,
|
||||
|
|
@ -1261,10 +1261,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin)
|
|||
} else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') {
|
||||
/* either invalid path or unspecified. Use the default. */
|
||||
g_free(priv->dirname_etc);
|
||||
priv->dirname_etc =
|
||||
nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE);
|
||||
priv->dirname_etc = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT));
|
||||
} else
|
||||
nm_sd_utils_path_simplify(priv->dirname_etc, FALSE);
|
||||
nm_sd_utils_path_simplify(priv->dirname_etc);
|
||||
|
||||
/* no duplicates */
|
||||
if (NM_IN_STRSET(priv->dirname_libs[0], priv->dirname_etc, priv->dirname_run))
|
||||
|
|
|
|||
|
|
@ -2251,7 +2251,7 @@ test_utils_file_is_in_path(void)
|
|||
g_assert(!nm_utils_file_is_in_path("//b///a/", "/b//"));
|
||||
g_assert(!nm_utils_file_is_in_path("//b///a/", "/b/a/"));
|
||||
g_assert(!nm_utils_file_is_in_path("//b///a", "/b/a/"));
|
||||
g_assert(nm_utils_file_is_in_path("//b///a/.", "/b/a/"));
|
||||
g_assert(!nm_utils_file_is_in_path("//b///a/.", "/b/a/"));
|
||||
g_assert(nm_utils_file_is_in_path("//b///a/..", "/b/a/"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -122,46 +122,34 @@ test_sd_event(void)
|
|||
static void
|
||||
test_path_equal(void)
|
||||
{
|
||||
#define _path_equal_check1(path, kill_dots, expected) \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
const gboolean _kill_dots = (kill_dots); \
|
||||
const char * _path0 = (path); \
|
||||
const char * _expected = (expected); \
|
||||
gs_free char * _path = g_strdup(_path0); \
|
||||
const char * _path_result; \
|
||||
\
|
||||
if (!_kill_dots && !nm_sd_utils_path_equal(_path0, _expected)) \
|
||||
g_error("Paths \"%s\" and \"%s\" don't compare equal", _path0, _expected); \
|
||||
\
|
||||
_path_result = nm_sd_utils_path_simplify(_path, _kill_dots); \
|
||||
g_assert(_path_result == _path); \
|
||||
g_assert_cmpstr(_path, ==, _expected); \
|
||||
} \
|
||||
#define _path_equal_check(path, expected) \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
const char * _path0 = (path); \
|
||||
const char * _expected = (expected); \
|
||||
gs_free char *_path = g_strdup(_path0); \
|
||||
const char * _path_result; \
|
||||
\
|
||||
_path_result = nm_sd_utils_path_simplify(_path); \
|
||||
g_assert(_path_result == _path); \
|
||||
g_assert_cmpstr(_path, ==, _expected); \
|
||||
} \
|
||||
G_STMT_END
|
||||
|
||||
#define _path_equal_check(path, expected_no_kill_dots, expected_kill_dots) \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
_path_equal_check1(path, FALSE, expected_no_kill_dots); \
|
||||
_path_equal_check1(path, TRUE, expected_kill_dots ?: expected_no_kill_dots); \
|
||||
} \
|
||||
G_STMT_END
|
||||
|
||||
_path_equal_check("", "", NULL);
|
||||
_path_equal_check(".", ".", NULL);
|
||||
_path_equal_check("..", "..", NULL);
|
||||
_path_equal_check("/..", "/..", NULL);
|
||||
_path_equal_check("//..", "/..", NULL);
|
||||
_path_equal_check("/.", "/.", "/");
|
||||
_path_equal_check("./", ".", ".");
|
||||
_path_equal_check("./.", "./.", ".");
|
||||
_path_equal_check(".///.", "./.", ".");
|
||||
_path_equal_check(".///./", "./.", ".");
|
||||
_path_equal_check(".////", ".", ".");
|
||||
_path_equal_check("//..//foo/", "/../foo", NULL);
|
||||
_path_equal_check("///foo//./bar/.", "/foo/./bar/.", "/foo/bar");
|
||||
_path_equal_check(".//./foo//./bar/.", "././foo/./bar/.", "foo/bar");
|
||||
_path_equal_check("", "");
|
||||
_path_equal_check(".", ".");
|
||||
_path_equal_check("..", "..");
|
||||
_path_equal_check("/..", "/..");
|
||||
_path_equal_check("//..", "/..");
|
||||
_path_equal_check("/.", "/");
|
||||
_path_equal_check("./", ".");
|
||||
_path_equal_check("./.", ".");
|
||||
_path_equal_check(".///.", ".");
|
||||
_path_equal_check(".///./", ".");
|
||||
_path_equal_check(".////", ".");
|
||||
_path_equal_check("//..//foo/", "/../foo");
|
||||
_path_equal_check("///foo//./bar/.", "/foo/bar");
|
||||
_path_equal_check(".//./foo//./bar/.", "foo/bar");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
|
|||
|
||||
int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload,
|
||||
uint8_t code, size_t optlen, const void *optval);
|
||||
int dhcp_option_find_option(uint8_t *options, size_t length, uint8_t wanted_code, size_t *ret_offset);
|
||||
int dhcp_option_remove_option(uint8_t *options, size_t buflen, uint8_t option_code);
|
||||
|
||||
typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
|
||||
const void *option, void *userdata);
|
||||
|
|
@ -66,15 +68,13 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui
|
|||
#define DHCP_CLIENT_DONT_DESTROY(client) \
|
||||
_cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
|
||||
|
||||
#define log_dhcp_client_errno(client, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) \
|
||||
log_interface_full_errno( \
|
||||
sd_dhcp_client_get_ifname(client), \
|
||||
LOG_DEBUG, _e, "DHCPv4 client: " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
#define log_dhcp_client_errno(client, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"DHCPv4 client: ", \
|
||||
sd_dhcp_client_get_ifname(client), \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_dhcp_client(client, fmt, ...) \
|
||||
log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__)
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"DHCPv4 client: ", \
|
||||
sd_dhcp_client_get_ifname(client), \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ struct sd_dhcp_lease {
|
|||
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
|
||||
|
||||
struct sd_dhcp_route *static_route;
|
||||
size_t static_route_size, static_route_allocated;
|
||||
size_t static_route_size;
|
||||
|
||||
uint16_t mtu; /* 0 if unset */
|
||||
|
||||
|
|
|
|||
|
|
@ -188,15 +188,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
|
|||
return r;
|
||||
}
|
||||
|
||||
if (address == INADDR_ANY) {
|
||||
r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (port == DHCP_PORT_SERVER) {
|
||||
r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (address == INADDR_ANY) {
|
||||
/* IP_PKTINFO filter should not be applied when packets are
|
||||
allowed to enter/leave through the interface other than
|
||||
DHCP server sits on(BindToInterface option). */
|
||||
r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true);
|
||||
if (r < 0)
|
||||
|
|
|
|||
|
|
@ -16,11 +16,33 @@
|
|||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
/* Append type-length value structure to the options buffer */
|
||||
static int dhcp_option_append_tlv(uint8_t options[], size_t size, size_t *offset, uint8_t code, size_t optlen, const void *optval) {
|
||||
assert(options);
|
||||
assert(size > 0);
|
||||
assert(offset);
|
||||
assert(optlen <= UINT8_MAX);
|
||||
assert(*offset < size);
|
||||
|
||||
if (*offset + 2 + optlen > size)
|
||||
return -ENOBUFS;
|
||||
|
||||
options[*offset] = code;
|
||||
options[*offset + 1] = optlen;
|
||||
|
||||
memcpy_safe(&options[*offset + 2], optval, optlen);
|
||||
*offset += 2 + optlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int option_append(uint8_t options[], size_t size, size_t *offset,
|
||||
uint8_t code, size_t optlen, const void *optval) {
|
||||
assert(options);
|
||||
assert(size > 0);
|
||||
assert(offset);
|
||||
|
||||
int r;
|
||||
|
||||
if (code != SD_DHCP_OPTION_END)
|
||||
/* always make sure there is space for an END option */
|
||||
size--;
|
||||
|
|
@ -94,34 +116,96 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
|
|||
|
||||
options[*offset] = code;
|
||||
options[*offset + 1] = l;
|
||||
|
||||
*offset += 2;
|
||||
|
||||
ORDERED_SET_FOREACH(p, s) {
|
||||
options[*offset] = p->option;
|
||||
options[*offset + 1] = p->length;
|
||||
memcpy(&options[*offset + 2], p->data, p->length);
|
||||
*offset += 2 + p->length;
|
||||
r = dhcp_option_append_tlv(options, size, offset, p->option, p->length, p->data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION: {
|
||||
#if 0 /* NM_IGNORED */
|
||||
sd_dhcp_server *server = (sd_dhcp_server *) optval;
|
||||
size_t current_offset = *offset + 2;
|
||||
|
||||
if (server->agent_circuit_id) {
|
||||
r = dhcp_option_append_tlv(options, size, ¤t_offset, SD_DHCP_RELAY_AGENT_CIRCUIT_ID,
|
||||
strlen(server->agent_circuit_id), server->agent_circuit_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (server->agent_remote_id) {
|
||||
r = dhcp_option_append_tlv(options, size, ¤t_offset, SD_DHCP_RELAY_AGENT_REMOTE_ID,
|
||||
strlen(server->agent_remote_id), server->agent_remote_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
options[*offset] = code;
|
||||
options[*offset + 1] = current_offset - *offset - 2;
|
||||
assert(current_offset - *offset - 2 <= UINT8_MAX);
|
||||
*offset = current_offset;
|
||||
break;
|
||||
#endif /* NM_IGNORED */
|
||||
g_return_val_if_reached(-EINVAL);
|
||||
}
|
||||
default:
|
||||
if (*offset + 2 + optlen > size)
|
||||
return -ENOBUFS;
|
||||
|
||||
options[*offset] = code;
|
||||
options[*offset + 1] = optlen;
|
||||
|
||||
memcpy_safe(&options[*offset + 2], optval, optlen);
|
||||
*offset += 2 + optlen;
|
||||
|
||||
break;
|
||||
return dhcp_option_append_tlv(options, size, offset, code, optlen, optval);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int option_length(uint8_t *options, size_t length, size_t offset) {
|
||||
assert(options);
|
||||
assert(offset < length);
|
||||
|
||||
if (IN_SET(options[offset], SD_DHCP_OPTION_PAD, SD_DHCP_OPTION_END))
|
||||
return 1;
|
||||
if (length < offset + 2)
|
||||
return -ENOBUFS;
|
||||
|
||||
/* validating that buffer is long enough */
|
||||
if (length < offset + 2 + options[offset + 1])
|
||||
return -ENOBUFS;
|
||||
|
||||
return options[offset + 1] + 2;
|
||||
}
|
||||
|
||||
int dhcp_option_find_option(uint8_t *options, size_t length, uint8_t code, size_t *ret_offset) {
|
||||
int r;
|
||||
|
||||
assert(options);
|
||||
assert(ret_offset);
|
||||
|
||||
for (size_t offset = 0; offset < length; offset += r) {
|
||||
r = option_length(options, length, offset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (code == options[offset]) {
|
||||
*ret_offset = offset;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int dhcp_option_remove_option(uint8_t *options, size_t length, uint8_t option_code) {
|
||||
int r;
|
||||
size_t offset;
|
||||
|
||||
assert(options);
|
||||
|
||||
r = dhcp_option_find_option(options, length, option_code, &offset);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
memmove(options + offset, options + offset + r, length - offset - r);
|
||||
return length - r;
|
||||
}
|
||||
|
||||
int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
|
||||
uint8_t overload,
|
||||
uint8_t code, size_t optlen, const void *optval) {
|
||||
|
|
@ -167,7 +251,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset,
|
|||
} else if (r == -ENOBUFS && use_sname) {
|
||||
/* did not fit, but we have more buffers to try
|
||||
close the file array and move the offset to its end */
|
||||
r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL);
|
||||
r = option_append(message->file, sizeof(message->file), &file_offset, SD_DHCP_OPTION_END, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
|||
|
|
@ -104,8 +104,7 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
|||
int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
|
||||
int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count,
|
||||
size_t *allocated);
|
||||
struct in6_addr **addrs, size_t count);
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen,
|
||||
char ***str_arr);
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str);
|
||||
|
|
@ -119,15 +118,13 @@ int dhcp6_message_type_from_string(const char *s) _pure_;
|
|||
const char *dhcp6_message_status_to_string(int s) _const_;
|
||||
int dhcp6_message_status_from_string(const char *s) _pure_;
|
||||
|
||||
#define log_dhcp6_client_errno(client, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) \
|
||||
log_interface_full_errno( \
|
||||
sd_dhcp6_client_get_ifname(client), \
|
||||
LOG_DEBUG, _e, "DHCPv6 client: " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
#define log_dhcp6_client(client, fmt, ...) \
|
||||
log_dhcp6_client_errno(client, 0, fmt, ##__VA_ARGS__)
|
||||
#define log_dhcp6_client_errno(client, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"DHCPv6 client: ", \
|
||||
sd_dhcp6_client_get_ifname(client), \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_dhcp6_client(client, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"DHCPv6 client: ", \
|
||||
sd_dhcp6_client_get_ifname(client), \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -27,12 +27,10 @@ struct sd_dhcp6_lease {
|
|||
|
||||
struct in6_addr *dns;
|
||||
size_t dns_count;
|
||||
size_t dns_allocated;
|
||||
char **domains;
|
||||
size_t domains_count;
|
||||
struct in6_addr *ntp;
|
||||
size_t ntp_count;
|
||||
size_t ntp_allocated;
|
||||
char **ntp_fqdn;
|
||||
size_t ntp_fqdn_count;
|
||||
char *fqdn;
|
||||
|
|
|
|||
|
|
@ -604,7 +604,7 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I
|
|||
|
||||
case SD_DHCP6_OPTION_IA_PD_PREFIX:
|
||||
|
||||
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD))
|
||||
if (ia->type != SD_DHCP6_OPTION_IA_PD)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"IA PD Prefix option not in IA PD option");
|
||||
|
||||
|
|
@ -680,14 +680,12 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I
|
|||
}
|
||||
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count,
|
||||
size_t *allocated) {
|
||||
struct in6_addr **addrs, size_t count) {
|
||||
|
||||
if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!GREEDY_REALLOC(*addrs, *allocated,
|
||||
count * sizeof(struct in6_addr) + optlen))
|
||||
if (!GREEDY_REALLOC(*addrs, count * sizeof(struct in6_addr) + optlen))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(*addrs + count, optval, optlen);
|
||||
|
|
@ -699,10 +697,10 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
|||
|
||||
static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) {
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
const uint8_t *optval = *data;
|
||||
uint16_t optlen = *len;
|
||||
bool first = true;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
if (optlen <= 1)
|
||||
|
|
@ -732,7 +730,7 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain)
|
|||
optval += c;
|
||||
optlen -= c;
|
||||
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
if (first)
|
||||
|
|
@ -748,7 +746,7 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain)
|
|||
}
|
||||
|
||||
if (n) {
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + 1))
|
||||
if (!GREEDY_REALLOC(ret, n + 1))
|
||||
return -ENOMEM;
|
||||
ret[n] = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,15 +36,13 @@ struct sd_lldp {
|
|||
const char* lldp_event_to_string(sd_lldp_event_t e) _const_;
|
||||
sd_lldp_event_t lldp_event_from_string(const char *s) _pure_;
|
||||
|
||||
#define log_lldp_errno(lldp, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) \
|
||||
log_interface_full_errno( \
|
||||
sd_lldp_get_ifname(lldp), \
|
||||
LOG_DEBUG, _e, "LLDP: " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
#define log_lldp(lldp, fmt, ...) \
|
||||
log_lldp_errno(lldp, 0, fmt, ##__VA_ARGS__)
|
||||
#define log_lldp_errno(lldp, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"LLDP: ", \
|
||||
sd_lldp_get_ifname(lldp), \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_lldp(lldp, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"LLDP: ", \
|
||||
sd_lldp_get_ifname(lldp), \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -160,13 +160,12 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
|
|||
fputs("\n", f);
|
||||
}
|
||||
|
||||
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
|
||||
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string) {
|
||||
_cleanup_free_ struct sd_dhcp_route *routes = NULL;
|
||||
size_t size = 0, allocated = 0;
|
||||
size_t size = 0;
|
||||
|
||||
assert(ret);
|
||||
assert(ret_size);
|
||||
assert(ret_allocated);
|
||||
assert(string);
|
||||
|
||||
/* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
|
||||
|
|
@ -182,7 +181,7 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
|
|||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (!GREEDY_REALLOC(routes, allocated, size + 1))
|
||||
if (!GREEDY_REALLOC(routes, size + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
tok = word;
|
||||
|
|
@ -222,7 +221,6 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
|
|||
}
|
||||
|
||||
*ret_size = size;
|
||||
*ret_allocated = allocated;
|
||||
*ret = TAKE_PTR(routes);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ struct sd_dhcp_route;
|
|||
struct sd_dhcp_lease;
|
||||
|
||||
void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
|
||||
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
|
||||
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string);
|
||||
|
||||
/* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */
|
||||
int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
|
||||
|
|
|
|||
|
|
@ -2057,6 +2057,13 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
|
|||
return client_initialize_time_events(client);
|
||||
}
|
||||
|
||||
int sd_dhcp_client_is_running(sd_dhcp_client *client) {
|
||||
if (!client)
|
||||
return 0;
|
||||
|
||||
return !IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED);
|
||||
}
|
||||
|
||||
int sd_dhcp_client_start(sd_dhcp_client *client) {
|
||||
int r;
|
||||
|
||||
|
|
|
|||
|
|
@ -442,14 +442,13 @@ static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_a
|
|||
|
||||
static int lease_parse_routes(
|
||||
const uint8_t *option, size_t len,
|
||||
struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
|
||||
struct sd_dhcp_route **routes, size_t *routes_size) {
|
||||
|
||||
struct in_addr addr;
|
||||
|
||||
assert(option || len <= 0);
|
||||
assert(routes);
|
||||
assert(routes_size);
|
||||
assert(routes_allocated);
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
|
@ -457,7 +456,7 @@ static int lease_parse_routes(
|
|||
if (len % 8 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
|
||||
if (!GREEDY_REALLOC(*routes, *routes_size + (len / 8)))
|
||||
return -ENOMEM;
|
||||
|
||||
while (len >= 8) {
|
||||
|
|
@ -488,12 +487,11 @@ static int lease_parse_routes(
|
|||
/* parses RFC3442 Classless Static Route Option */
|
||||
static int lease_parse_classless_routes(
|
||||
const uint8_t *option, size_t len,
|
||||
struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) {
|
||||
struct sd_dhcp_route **routes, size_t *routes_size) {
|
||||
|
||||
assert(option || len <= 0);
|
||||
assert(routes);
|
||||
assert(routes_size);
|
||||
assert(routes_allocated);
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
|
@ -504,7 +502,7 @@ static int lease_parse_classless_routes(
|
|||
uint8_t dst_octets;
|
||||
struct sd_dhcp_route *route;
|
||||
|
||||
if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
|
||||
if (!GREEDY_REALLOC(*routes, *routes_size + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
route = *routes + *routes_size;
|
||||
|
|
@ -618,7 +616,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
|||
break;
|
||||
|
||||
case SD_DHCP_OPTION_STATIC_ROUTE:
|
||||
r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
|
||||
r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
|
||||
break;
|
||||
|
|
@ -680,8 +678,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
|||
r = lease_parse_classless_routes(
|
||||
option, len,
|
||||
&lease->static_route,
|
||||
&lease->static_route_size,
|
||||
&lease->static_route_allocated);
|
||||
&lease->static_route_size);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
|
||||
break;
|
||||
|
|
@ -749,7 +746,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
|
|||
|
||||
while (pos < len) {
|
||||
_cleanup_free_ char *name = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
size_t n = 0;
|
||||
size_t jump_barrier = pos, next_chunk = 0;
|
||||
bool first = true;
|
||||
|
||||
|
|
@ -769,7 +766,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
|
|||
if (pos >= len)
|
||||
return -EBADMSG;
|
||||
|
||||
if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
if (!GREEDY_REALLOC(name, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
if (first)
|
||||
|
|
@ -808,7 +805,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
|
|||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(name, allocated, n + 1))
|
||||
if (!GREEDY_REALLOC(name, n + 1))
|
||||
return -ENOMEM;
|
||||
name[n] = 0;
|
||||
|
||||
|
|
@ -1234,7 +1231,6 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
|||
r = deserialize_dhcp_routes(
|
||||
&lease->static_route,
|
||||
&lease->static_route_size,
|
||||
&lease->static_route_allocated,
|
||||
routes);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ struct sd_dhcp6_client {
|
|||
bool information_request;
|
||||
bool iaid_set;
|
||||
be16_t *req_opts;
|
||||
size_t req_opts_allocated;
|
||||
size_t req_opts_len;
|
||||
char *fqdn;
|
||||
char *mudurl;
|
||||
|
|
@ -167,7 +166,7 @@ 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);
|
||||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
client->ifindex = ifindex;
|
||||
return 0;
|
||||
|
|
@ -197,8 +196,7 @@ int sd_dhcp6_client_set_local_address(
|
|||
assert_return(client, -EINVAL);
|
||||
assert_return(local_address, -EINVAL);
|
||||
assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
|
||||
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
client->local_address = *local_address;
|
||||
|
||||
|
|
@ -213,8 +211,7 @@ int sd_dhcp6_client_set_mac(
|
|||
assert_return(client, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
|
||||
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
if (arp_type == ARPHRD_ETHER)
|
||||
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
||||
|
|
@ -244,8 +241,7 @@ int sd_dhcp6_client_set_prefix_delegation_hint(
|
|||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(pd_address, -EINVAL);
|
||||
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
client->hint_pd_prefix.iapdprefix.address = *pd_address;
|
||||
client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
|
||||
|
|
@ -290,7 +286,7 @@ static int dhcp6_client_set_duid_internal(
|
|||
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(duid_len == 0 || duid != NULL, -EINVAL);
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
if (duid) {
|
||||
r = dhcp_validate_duid_len(duid_type, duid_len, true);
|
||||
|
|
@ -403,7 +399,7 @@ int sd_dhcp6_client_duid_as_string(
|
|||
|
||||
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
client->ia_na.ia_na.id = htobe32(iaid);
|
||||
client->ia_pd.ia_pd.id = htobe32(iaid);
|
||||
|
|
@ -440,7 +436,7 @@ int sd_dhcp6_client_set_fqdn(
|
|||
|
||||
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
|
||||
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
|
||||
|
||||
client->information_request = enabled;
|
||||
|
||||
|
|
@ -469,8 +465,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
|
|||
if (client->req_opts[t] == htobe16(option))
|
||||
return -EEXIST;
|
||||
|
||||
if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
|
||||
client->req_opts_len + 1))
|
||||
if (!GREEDY_REALLOC(client->req_opts, client->req_opts_len + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
client->req_opts[client->req_opts_len++] = htobe16(option);
|
||||
|
|
@ -1715,7 +1710,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
|
|||
assert_return(client->ifindex > 0, -EINVAL);
|
||||
assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
|
||||
|
||||
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
|
||||
if (client->state != DHCP6_STATE_STOPPED)
|
||||
return -EBUSY;
|
||||
|
||||
if (!client->information_request && !client->request)
|
||||
|
|
|
|||
|
|
@ -205,8 +205,7 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
|||
return 0;
|
||||
|
||||
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
|
||||
lease->dns_count,
|
||||
&lease->dns_allocated);
|
||||
lease->dns_count);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -271,7 +270,6 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
|||
|
||||
lease->ntp = mfree(lease->ntp);
|
||||
lease->ntp_count = 0;
|
||||
lease->ntp_allocated = 0;
|
||||
|
||||
while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
|
||||
&subval)) >= 0) {
|
||||
|
|
@ -286,8 +284,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
|||
|
||||
s = dhcp6_option_parse_ip6addrs(subval, sublen,
|
||||
&lease->ntp,
|
||||
lease->ntp_count,
|
||||
&lease->ntp_allocated);
|
||||
lease->ntp_count);
|
||||
if (s < 0)
|
||||
return s;
|
||||
|
||||
|
|
@ -329,8 +326,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen)
|
|||
/* Using deprecated SNTP information */
|
||||
|
||||
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
|
||||
lease->ntp_count,
|
||||
&lease->ntp_allocated);
|
||||
lease->ntp_count);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
|||
|
|
@ -77,18 +77,16 @@ struct sd_ipv4acd {
|
|||
void* userdata;
|
||||
};
|
||||
|
||||
#define log_ipv4acd_errno(acd, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) \
|
||||
log_interface_full_errno( \
|
||||
sd_ipv4acd_get_ifname(acd), \
|
||||
LOG_DEBUG, _e, "IPv4ACD: " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
#define log_ipv4acd_errno(acd, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"IPv4ACD: ", \
|
||||
sd_ipv4acd_get_ifname(acd), \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_ipv4acd(acd, fmt, ...) \
|
||||
log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"IPv4ACD: ", \
|
||||
sd_ipv4acd_get_ifname(acd), \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
|
||||
[IPV4ACD_STATE_INIT] = "init",
|
||||
|
|
|
|||
|
|
@ -51,18 +51,16 @@ struct sd_ipv4ll {
|
|||
void* userdata;
|
||||
};
|
||||
|
||||
#define log_ipv4ll_errno(ll, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) \
|
||||
log_interface_full_errno( \
|
||||
sd_ipv4ll_get_ifname(ll), \
|
||||
LOG_DEBUG, _e, "IPv4LL: " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
#define log_ipv4ll_errno(ll, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"IPv4LL: ", \
|
||||
sd_ipv4ll_get_ifname(ll), \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_ipv4ll(ll, fmt, ...) \
|
||||
log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"IPv4LL: ", \
|
||||
sd_ipv4ll_get_ifname(ll), \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ struct sd_event {
|
|||
unsigned n_sources;
|
||||
|
||||
struct epoll_event *event_queue;
|
||||
size_t event_queue_allocated;
|
||||
|
||||
LIST_HEAD(sd_event_source, sources);
|
||||
|
||||
|
|
@ -3466,11 +3465,11 @@ static int source_dispatch(sd_event_source *s) {
|
|||
* invalidate the event. */
|
||||
saved_type = s->type;
|
||||
|
||||
/* Similar, store a reference to the event loop object, so that we can still access it after the
|
||||
/* Similarly, store a reference to the event loop object, so that we can still access it after the
|
||||
* callback might have invalidated/disconnected the event source. */
|
||||
saved_event = sd_event_ref(s->event);
|
||||
|
||||
/* Check if we hit the ratelimit for this event source, if so, let's disable it. */
|
||||
/* Check if we hit the ratelimit for this event source, and if so, let's disable it. */
|
||||
assert(!s->ratelimited);
|
||||
if (!ratelimit_below(&s->rate_limit)) {
|
||||
r = event_source_enter_ratelimited(s);
|
||||
|
|
@ -3489,8 +3488,7 @@ static int source_dispatch(sd_event_source *s) {
|
|||
if (s->type != SOURCE_POST) {
|
||||
sd_event_source *z;
|
||||
|
||||
/* If we execute a non-post source, let's mark all
|
||||
* post sources as pending */
|
||||
/* If we execute a non-post source, let's mark all post sources as pending. */
|
||||
|
||||
SET_FOREACH(z, s->event->post_sources) {
|
||||
if (event_source_is_offline(z))
|
||||
|
|
@ -3863,38 +3861,45 @@ static int epoll_wait_usec(
|
|||
}
|
||||
|
||||
static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) {
|
||||
size_t n_event_queue, m, n_event_max;
|
||||
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))
|
||||
if (!GREEDY_REALLOC(e->event_queue, n_event_queue))
|
||||
return -ENOMEM;
|
||||
|
||||
n_event_max = MALLOC_ELEMENTSOF(e->event_queue);
|
||||
|
||||
/* 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;
|
||||
|
||||
for (;;) {
|
||||
r = epoll_wait_usec(e->epoll_fd, e->event_queue, e->event_queue_allocated, timeout);
|
||||
r = epoll_wait_usec(
|
||||
e->epoll_fd,
|
||||
e->event_queue,
|
||||
n_event_max,
|
||||
timeout);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m = (size_t) r;
|
||||
|
||||
if (m < e->event_queue_allocated)
|
||||
if (m < n_event_max)
|
||||
break;
|
||||
|
||||
if (e->event_queue_allocated >= n_event_queue * 10)
|
||||
if (n_event_max >= n_event_queue * 10)
|
||||
break;
|
||||
|
||||
if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, e->event_queue_allocated + n_event_queue))
|
||||
if (!GREEDY_REALLOC(e->event_queue, n_event_max + n_event_queue))
|
||||
return -ENOMEM;
|
||||
|
||||
n_event_max = MALLOC_ELEMENTSOF(e->event_queue);
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -215,4 +215,26 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
|
|||
}
|
||||
|
||||
DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
|
||||
|
||||
int id128_get_product(sd_id128_t *ret) {
|
||||
sd_id128_t uuid;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
|
||||
* particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
|
||||
|
||||
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
|
||||
if (r == -ENOENT)
|
||||
r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
|
||||
return -EADDRNOTAVAIL; /* Recognizable error */
|
||||
|
||||
*ret = uuid;
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -36,3 +36,5 @@ int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
|
|||
extern const struct hash_ops id128_hash_ops;
|
||||
|
||||
sd_id128_t id128_make_v4_uuid(sd_id128_t id);
|
||||
|
||||
int id128_get_product(sd_id128_t *ret);
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ enum {
|
|||
SD_DHCP_OPTION_POP3_SERVER = 70,
|
||||
SD_DHCP_OPTION_USER_CLASS = 77,
|
||||
SD_DHCP_OPTION_FQDN = 81,
|
||||
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82,
|
||||
SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
|
||||
SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
|
||||
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
|
||||
|
|
@ -105,6 +106,12 @@ enum {
|
|||
SD_DHCP_OPTION_END = 255,
|
||||
};
|
||||
|
||||
/* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */
|
||||
enum {
|
||||
SD_DHCP_RELAY_AGENT_CIRCUIT_ID = 1,
|
||||
SD_DHCP_RELAY_AGENT_REMOTE_ID = 2,
|
||||
};
|
||||
|
||||
typedef struct sd_dhcp_client sd_dhcp_client;
|
||||
|
||||
typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata);
|
||||
|
|
@ -199,6 +206,7 @@ int sd_dhcp_client_set_fallback_lease_lifetime(
|
|||
int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
|
||||
int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
|
||||
|
||||
int sd_dhcp_client_is_running(sd_dhcp_client *client);
|
||||
int sd_dhcp_client_stop(sd_dhcp_client *client);
|
||||
int sd_dhcp_client_start(sd_dhcp_client *client);
|
||||
int sd_dhcp_client_send_release(sd_dhcp_client *client);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
***/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
|
@ -119,6 +120,32 @@ _sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
|
|||
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
|
||||
#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
|
||||
|
||||
_sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
|
||||
for (;;) {
|
||||
sd_id128_t b = va_arg(ap, sd_id128_t);
|
||||
|
||||
if (sd_id128_is_null(b))
|
||||
return 0;
|
||||
|
||||
if (sd_id128_equal(a, b))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
_sd_pure_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
va_start(ap, a);
|
||||
r = sd_id128_in_setv(a, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#define sd_id128_in_set(a, ...) \
|
||||
sd_id128_in_set_sentinel(a, ##__VA_ARGS__, SD_ID128_NULL)
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ nm_sd_utils_path_equal(const char *a, const char *b)
|
|||
}
|
||||
|
||||
char *
|
||||
nm_sd_utils_path_simplify(char *path, gboolean kill_dots)
|
||||
nm_sd_utils_path_simplify(char *path)
|
||||
{
|
||||
return path_simplify(path, kill_dots);
|
||||
return path_simplify(path);
|
||||
}
|
||||
|
||||
const char *
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
gboolean nm_sd_utils_path_equal(const char *a, const char *b);
|
||||
|
||||
char *nm_sd_utils_path_simplify(char *path, gboolean kill_dots);
|
||||
char *nm_sd_utils_path_simplify(char *path);
|
||||
|
||||
const char *nm_sd_utils_path_startswith(const char *path, const char *prefix);
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@
|
|||
|
||||
#define LOG_TRACE 0
|
||||
|
||||
#define BUILD_MODE_DEVELOPER (NM_MORE_ASSERTS > 0)
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* systemd cannot be compiled with "-Wdeclaration-after-statement". In particular
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ void* memdup(const void *p, size_t l) {
|
|||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
memcpy(ret, p, l);
|
||||
return ret;
|
||||
return memcpy_safe(ret, p, l);
|
||||
}
|
||||
|
||||
void* memdup_suffix0(const void *p, size_t l) {
|
||||
|
|
@ -37,28 +36,36 @@ void* memdup_suffix0(const void *p, size_t l) {
|
|||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
*((uint8_t*) mempcpy(ret, p, l)) = 0;
|
||||
return ret;
|
||||
((uint8_t*) ret)[l] = 0;
|
||||
return memcpy_safe(ret, p, l);
|
||||
}
|
||||
|
||||
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
|
||||
void* greedy_realloc(
|
||||
void **p,
|
||||
size_t need,
|
||||
size_t size) {
|
||||
|
||||
size_t a, newalloc;
|
||||
void *q;
|
||||
|
||||
assert(p);
|
||||
assert(allocated);
|
||||
|
||||
if (*allocated >= need)
|
||||
/* We use malloc_usable_size() for determining the current allocated size. On all systems we care
|
||||
* about this should be safe to rely on. Should there ever arise the need to avoid relying on this we
|
||||
* can instead locally fall back to realloc() on every call, rounded up to the next exponent of 2 or
|
||||
* so. */
|
||||
|
||||
if (*p && (size == 0 || (MALLOC_SIZEOF_SAFE(*p) / size >= need)))
|
||||
return *p;
|
||||
|
||||
if (_unlikely_(need > SIZE_MAX/2)) /* Overflow check */
|
||||
return NULL;
|
||||
|
||||
newalloc = need * 2;
|
||||
|
||||
if (size_multiply_overflow(newalloc, size))
|
||||
return NULL;
|
||||
|
||||
a = newalloc * size;
|
||||
|
||||
if (a < 64) /* Allocate at least 64 bytes */
|
||||
a = 64;
|
||||
|
||||
|
|
@ -66,49 +73,34 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
|
|||
if (!q)
|
||||
return NULL;
|
||||
|
||||
if (size > 0) {
|
||||
size_t bn;
|
||||
|
||||
/* Adjust for the 64 byte minimum */
|
||||
newalloc = a / size;
|
||||
|
||||
bn = malloc_usable_size(q) / size;
|
||||
if (bn > newalloc) {
|
||||
void *qq;
|
||||
|
||||
/* The actual size allocated is larger than what we asked for. Let's call realloc() again to
|
||||
* take possession of the extra space. This should be cheap, since libc doesn't have to move
|
||||
* the memory for this. */
|
||||
|
||||
qq = reallocarray(q, bn, size);
|
||||
if (_likely_(qq)) {
|
||||
*p = qq;
|
||||
*allocated = bn;
|
||||
return qq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*p = q;
|
||||
*allocated = newalloc;
|
||||
return q;
|
||||
return *p = q;
|
||||
}
|
||||
|
||||
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
|
||||
size_t prev;
|
||||
void* greedy_realloc0(
|
||||
void **p,
|
||||
size_t need,
|
||||
size_t size) {
|
||||
|
||||
size_t before, after;
|
||||
uint8_t *q;
|
||||
|
||||
assert(p);
|
||||
assert(allocated);
|
||||
|
||||
prev = *allocated;
|
||||
before = MALLOC_SIZEOF_SAFE(*p); /* malloc_usable_size() will return 0 on NULL input, as per docs */
|
||||
|
||||
q = greedy_realloc(p, allocated, need, size);
|
||||
q = greedy_realloc(p, need, size);
|
||||
if (!q)
|
||||
return NULL;
|
||||
|
||||
if (*allocated > prev)
|
||||
memzero(q + prev * size, (*allocated - prev) * size);
|
||||
after = MALLOC_SIZEOF_SAFE(q);
|
||||
|
||||
if (size == 0) /* avoid division by zero */
|
||||
before = 0;
|
||||
else
|
||||
before = (before / size) * size; /* Round down */
|
||||
|
||||
if (after > before)
|
||||
memzero(q + before, after - before);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
|
|||
size_t _l_ = l; \
|
||||
assert(_l_ <= ALLOCA_MAX); \
|
||||
_q_ = alloca(_l_ ?: 1); \
|
||||
memcpy(_q_, p, _l_); \
|
||||
memcpy_safe(_q_, p, _l_); \
|
||||
})
|
||||
|
||||
#define memdupa_suffix0(p, l) \
|
||||
|
|
@ -76,7 +76,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
|
|||
assert(_l_ <= ALLOCA_MAX); \
|
||||
_q_ = alloca(_l_ + 1); \
|
||||
((uint8_t*) _q_)[_l_] = 0; \
|
||||
memcpy(_q_, p, _l_); \
|
||||
memcpy_safe(_q_, p, _l_); \
|
||||
})
|
||||
|
||||
static inline void freep(void *p) {
|
||||
|
|
@ -121,14 +121,14 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n
|
|||
return memdup_suffix0(p, size * need);
|
||||
}
|
||||
|
||||
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
|
||||
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
|
||||
void* greedy_realloc(void **p, size_t need, size_t size);
|
||||
void* greedy_realloc0(void **p, size_t need, size_t size);
|
||||
|
||||
#define GREEDY_REALLOC(array, allocated, need) \
|
||||
greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0]))
|
||||
#define GREEDY_REALLOC(array, need) \
|
||||
greedy_realloc((void**) &(array), (need), sizeof((array)[0]))
|
||||
|
||||
#define GREEDY_REALLOC0(array, allocated, need) \
|
||||
greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0]))
|
||||
#define GREEDY_REALLOC0(array, need) \
|
||||
greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
|
||||
|
||||
#define alloca0(n) \
|
||||
({ \
|
||||
|
|
@ -163,3 +163,23 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
|
|||
#else
|
||||
# define msan_unpoison(r, s)
|
||||
#endif
|
||||
|
||||
/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), in a way that
|
||||
* is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the
|
||||
* object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of
|
||||
* malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by
|
||||
* both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the
|
||||
* size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory,
|
||||
* too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
|
||||
* __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner
|
||||
* case. */
|
||||
#define MALLOC_SIZEOF_SAFE(x) \
|
||||
MIN(malloc_usable_size(x), __builtin_object_size(x, 0))
|
||||
|
||||
/* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items
|
||||
* that fit into the specified memory block */
|
||||
#define MALLOC_ELEMENTSOF(x) \
|
||||
(__builtin_choose_expr( \
|
||||
__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
|
||||
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
|
||||
VOID_0))
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ typedef enum CGroupController {
|
|||
/* BPF-based pseudo-controllers, v2 only */
|
||||
CGROUP_CONTROLLER_BPF_FIREWALL,
|
||||
CGROUP_CONTROLLER_BPF_DEVICES,
|
||||
CGROUP_CONTROLLER_BPF_FOREIGN,
|
||||
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
|
||||
|
||||
_CGROUP_CONTROLLER_MAX,
|
||||
_CGROUP_CONTROLLER_INVALID = -EINVAL,
|
||||
|
|
@ -49,6 +51,8 @@ typedef enum CGroupMask {
|
|||
CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS),
|
||||
CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL),
|
||||
CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
|
||||
CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
|
||||
CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND),
|
||||
|
||||
/* All real cgroup v1 controllers */
|
||||
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
|
||||
|
|
@ -57,7 +61,7 @@ typedef enum CGroupMask {
|
|||
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
|
||||
|
||||
/* All cgroup v2 BPF pseudo-controllers */
|
||||
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES,
|
||||
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
|
||||
|
||||
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
|
||||
} CGroupMask;
|
||||
|
|
|
|||
|
|
@ -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_MAX, last_key_whitespace = SIZE_MAX;
|
||||
size_t n_key = 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;
|
||||
|
|
@ -60,7 +60,7 @@ static int parse_env_file_internal(
|
|||
state = KEY;
|
||||
last_key_whitespace = SIZE_MAX;
|
||||
|
||||
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
|
||||
if (!GREEDY_REALLOC(key, n_key+2))
|
||||
return -ENOMEM;
|
||||
|
||||
key[n_key++] = c;
|
||||
|
|
@ -81,7 +81,7 @@ static int parse_env_file_internal(
|
|||
else if (last_key_whitespace == SIZE_MAX)
|
||||
last_key_whitespace = n_key;
|
||||
|
||||
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
|
||||
if (!GREEDY_REALLOC(key, n_key+2))
|
||||
return -ENOMEM;
|
||||
|
||||
key[n_key++] = c;
|
||||
|
|
@ -108,7 +108,7 @@ static int parse_env_file_internal(
|
|||
|
||||
n_key = 0;
|
||||
value = NULL;
|
||||
value_alloc = n_value = 0;
|
||||
n_value = 0;
|
||||
|
||||
} else if (c == '\'')
|
||||
state = SINGLE_QUOTE_VALUE;
|
||||
|
|
@ -119,7 +119,7 @@ static int parse_env_file_internal(
|
|||
else if (!strchr(WHITESPACE, c)) {
|
||||
state = VALUE;
|
||||
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
if (!GREEDY_REALLOC(value, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
|
|
@ -151,7 +151,7 @@ static int parse_env_file_internal(
|
|||
|
||||
n_key = 0;
|
||||
value = NULL;
|
||||
value_alloc = n_value = 0;
|
||||
n_value = 0;
|
||||
|
||||
} else if (c == '\\') {
|
||||
state = VALUE_ESCAPE;
|
||||
|
|
@ -162,7 +162,7 @@ static int parse_env_file_internal(
|
|||
else if (last_value_whitespace == SIZE_MAX)
|
||||
last_value_whitespace = n_value;
|
||||
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
if (!GREEDY_REALLOC(value, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
|
|
@ -175,7 +175,7 @@ static int parse_env_file_internal(
|
|||
|
||||
if (!strchr(NEWLINE, c)) {
|
||||
/* Escaped newlines we eat up entirely */
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
if (!GREEDY_REALLOC(value, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
|
|
@ -186,7 +186,7 @@ static int parse_env_file_internal(
|
|||
if (c == '\'')
|
||||
state = PRE_VALUE;
|
||||
else {
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
if (!GREEDY_REALLOC(value, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
|
|
@ -200,7 +200,7 @@ static int parse_env_file_internal(
|
|||
else if (c == '\\')
|
||||
state = DOUBLE_QUOTE_VALUE_ESCAPE;
|
||||
else {
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
if (!GREEDY_REALLOC(value, n_value+2))
|
||||
return -ENOMEM;
|
||||
|
||||
value[n_value++] = c;
|
||||
|
|
@ -213,13 +213,13 @@ static int parse_env_file_internal(
|
|||
|
||||
if (strchr(SHELL_NEED_ESCAPE, c)) {
|
||||
/* If this is a char that needs escaping, just unescape it. */
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
|
||||
if (!GREEDY_REALLOC(value, n_value+2))
|
||||
return -ENOMEM;
|
||||
value[n_value++] = c;
|
||||
} else if (c != '\n') {
|
||||
/* If other char than what needs escaping, keep the "\" in place, like the
|
||||
* real shell does. */
|
||||
if (!GREEDY_REALLOC(value, value_alloc, n_value+3))
|
||||
if (!GREEDY_REALLOC(value, n_value+3))
|
||||
return -ENOMEM;
|
||||
value[n_value++] = '\\';
|
||||
value[n_value++] = c;
|
||||
|
|
|
|||
|
|
@ -575,12 +575,9 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
|
|||
|
||||
t = strv_env_get_n(env, word+2, e-word-2, flags);
|
||||
|
||||
k = strjoin(r, t);
|
||||
if (!k)
|
||||
if (!strextend(&r, t))
|
||||
return NULL;
|
||||
|
||||
free_and_replace(r, k);
|
||||
|
||||
word = e+1;
|
||||
state = WORD;
|
||||
} else if (*e == ':') {
|
||||
|
|
@ -630,12 +627,9 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
|
|||
else if (!t && state == DEFAULT_VALUE)
|
||||
t = v = replace_env_n(test_value, e-test_value, env, flags);
|
||||
|
||||
k = strjoin(r, t);
|
||||
if (!k)
|
||||
if (!strextend(&r, t))
|
||||
return NULL;
|
||||
|
||||
free_and_replace(r, k);
|
||||
|
||||
word = e+1;
|
||||
state = WORD;
|
||||
}
|
||||
|
|
@ -649,12 +643,9 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
|
|||
|
||||
t = strv_env_get_n(env, word+1, e-word-1, flags);
|
||||
|
||||
k = strjoin(r, t);
|
||||
if (!k)
|
||||
if (!strextend(&r, t))
|
||||
return NULL;
|
||||
|
||||
free_and_replace(r, k);
|
||||
|
||||
word = e--;
|
||||
i--;
|
||||
state = WORD;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ static inline int negative_errno(void) {
|
|||
|
||||
static inline const char *strerror_safe(int error) {
|
||||
/* 'safe' here does NOT mean thread safety. */
|
||||
return strerror(abs(error));
|
||||
return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */
|
||||
}
|
||||
|
||||
static inline int errno_or_else(int fallback) {
|
||||
|
|
|
|||
|
|
@ -363,15 +363,16 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
|
|||
return t - r;
|
||||
}
|
||||
|
||||
char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
|
||||
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) {
|
||||
char *ans, *t, *prev, *prev2;
|
||||
const char *f;
|
||||
|
||||
/* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
|
||||
* reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
|
||||
* This corresponds to non-ASCII printable characters in pre-unicode encodings.
|
||||
* reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
|
||||
* unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
|
||||
*
|
||||
* If console_width is reached, output is truncated and "..." is appended. */
|
||||
* If console_width is reached, or XESCAPE_FORCE_ELLIPSIS is set, output is truncated and "..." is
|
||||
* appended. */
|
||||
|
||||
if (console_width == 0)
|
||||
return strdup("");
|
||||
|
|
@ -383,17 +384,23 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei
|
|||
memset(ans, '_', MIN(strlen(s), console_width) * 4);
|
||||
ans[MIN(strlen(s), console_width) * 4] = 0;
|
||||
|
||||
bool force_ellipsis = FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS);
|
||||
|
||||
for (f = s, t = prev = prev2 = ans; ; f++) {
|
||||
char *tmp_t = t;
|
||||
|
||||
if (!*f) {
|
||||
if (force_ellipsis)
|
||||
break;
|
||||
|
||||
*t = 0;
|
||||
return ans;
|
||||
}
|
||||
|
||||
if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) ||
|
||||
if ((unsigned char) *f < ' ' ||
|
||||
(!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
|
||||
*f == '\\' || strchr(bad, *f)) {
|
||||
if ((size_t) (t - ans) + 4 > console_width)
|
||||
if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
|
||||
break;
|
||||
|
||||
*(t++) = '\\';
|
||||
|
|
@ -401,7 +408,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei
|
|||
*(t++) = hexchar(*f >> 4);
|
||||
*(t++) = hexchar(*f);
|
||||
} else {
|
||||
if ((size_t) (t - ans) + 1 > console_width)
|
||||
if ((size_t) (t - ans) + 1 + 3 * force_ellipsis > console_width)
|
||||
break;
|
||||
|
||||
*(t++) = *f;
|
||||
|
|
@ -430,11 +437,13 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei
|
|||
return ans;
|
||||
}
|
||||
|
||||
char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
|
||||
if (eight_bit)
|
||||
return xescape_full(str, "", console_width, true);
|
||||
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) {
|
||||
if (FLAGS_SET(flags, XESCAPE_8_BIT))
|
||||
return xescape_full(str, "", console_width, flags);
|
||||
else
|
||||
return utf8_escape_non_printable_full(str, console_width);
|
||||
return utf8_escape_non_printable_full(str,
|
||||
console_width,
|
||||
FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS));
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
|
|
@ -466,88 +475,74 @@ char* octescape(const char *s, size_t len) {
|
|||
|
||||
}
|
||||
|
||||
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
|
||||
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
|
||||
assert(bad);
|
||||
|
||||
for (; *s; s++) {
|
||||
if (escape_tab_nl && IN_SET(*s, '\n', '\t')) {
|
||||
*(t++) = '\\';
|
||||
*(t++) = *s == '\n' ? 'n' : 't';
|
||||
continue;
|
||||
for (; *s; s++)
|
||||
if (char_is_cc(*s))
|
||||
t += cescape_char(*s, t);
|
||||
else {
|
||||
if (*s == '\\' || strchr(bad, *s))
|
||||
*(t++) = '\\';
|
||||
*(t++) = *s;
|
||||
}
|
||||
|
||||
if (*s == '\\' || strchr(bad, *s))
|
||||
*(t++) = '\\';
|
||||
|
||||
*(t++) = *s;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
char* shell_escape(const char *s, const char *bad) {
|
||||
char *r, *t;
|
||||
char *buf, *t;
|
||||
|
||||
r = new(char, strlen(s)*2+1);
|
||||
if (!r)
|
||||
buf = new(char, strlen(s)*4+1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
t = strcpy_backslash_escaped(r, s, bad, false);
|
||||
t = strcpy_backslash_escaped(buf, s, bad);
|
||||
*t = 0;
|
||||
|
||||
return r;
|
||||
return buf;
|
||||
}
|
||||
|
||||
char* shell_maybe_quote(const char *s, EscapeStyle style) {
|
||||
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
|
||||
const char *p;
|
||||
char *r, *t;
|
||||
char *buf, *t;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Encloses a string in quotes if necessary to make it OK as a shell
|
||||
* string. Note that we treat benign UTF-8 characters as needing
|
||||
* escaping too, but that should be OK. */
|
||||
/* Encloses a string in quotes if necessary to make it OK as a shell string. */
|
||||
|
||||
if (FLAGS_SET(flags, SHELL_ESCAPE_EMPTY) && isempty(s))
|
||||
return strdup("\"\""); /* We don't use $'' here in the POSIX mode. "" is fine too. */
|
||||
|
||||
for (p = s; *p; p++)
|
||||
if (*p <= ' ' ||
|
||||
*p >= 127 ||
|
||||
strchr(SHELL_NEED_QUOTES, *p))
|
||||
if (char_is_cc(*p) ||
|
||||
strchr(WHITESPACE SHELL_NEED_QUOTES, *p))
|
||||
break;
|
||||
|
||||
if (!*p)
|
||||
return strdup(s);
|
||||
|
||||
r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1);
|
||||
if (!r)
|
||||
buf = new(char, FLAGS_SET(flags, SHELL_ESCAPE_POSIX) + 1 + strlen(s)*4 + 1 + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
t = r;
|
||||
switch (style) {
|
||||
case ESCAPE_BACKSLASH:
|
||||
case ESCAPE_BACKSLASH_ONELINE:
|
||||
*(t++) = '"';
|
||||
break;
|
||||
case ESCAPE_POSIX:
|
||||
t = buf;
|
||||
if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) {
|
||||
*(t++) = '$';
|
||||
*(t++) = '\'';
|
||||
break;
|
||||
default:
|
||||
assert_not_reached("Bad EscapeStyle");
|
||||
}
|
||||
} else
|
||||
*(t++) = '"';
|
||||
|
||||
t = mempcpy(t, s, p - s);
|
||||
|
||||
if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
|
||||
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE,
|
||||
style == ESCAPE_BACKSLASH_ONELINE);
|
||||
else
|
||||
t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
|
||||
t = strcpy_backslash_escaped(t, p,
|
||||
FLAGS_SET(flags, SHELL_ESCAPE_POSIX) ? SHELL_NEED_ESCAPE_POSIX : SHELL_NEED_ESCAPE);
|
||||
|
||||
if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
|
||||
*(t++) = '"';
|
||||
else
|
||||
if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX))
|
||||
*(t++) = '\'';
|
||||
else
|
||||
*(t++) = '"';
|
||||
*t = 0;
|
||||
|
||||
return r;
|
||||
return str_realloc(buf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,15 +33,13 @@ typedef enum UnescapeFlags {
|
|||
UNESCAPE_ACCEPT_NUL = 1 << 1,
|
||||
} UnescapeFlags;
|
||||
|
||||
typedef enum EscapeStyle {
|
||||
ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single
|
||||
argument, possibly multiline. Tabs and newlines are not escaped. */
|
||||
ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line
|
||||
string instead. Shell escape sequences are produced for tabs and
|
||||
newlines. */
|
||||
ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape
|
||||
* syntax (a string enclosed in $'') instead of plain quotes. */
|
||||
} EscapeStyle;
|
||||
typedef enum ShellEscapeFlags {
|
||||
/* The default is to add shell quotes ("") so the shell will consider this a single argument.
|
||||
* Tabs and newlines are escaped. */
|
||||
|
||||
SHELL_ESCAPE_POSIX = 1 << 1, /* Use POSIX shell escape syntax (a string enclosed in $'') instead of plain quotes. */
|
||||
SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */
|
||||
} ShellEscapeFlags;
|
||||
|
||||
char* cescape(const char *s);
|
||||
char* cescape_length(const char *s, size_t n);
|
||||
|
|
@ -56,12 +54,17 @@ static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) {
|
|||
}
|
||||
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
|
||||
|
||||
char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
|
||||
typedef enum XEscapeFlags {
|
||||
XESCAPE_8_BIT = 1 << 0,
|
||||
XESCAPE_FORCE_ELLIPSIS = 1 << 1,
|
||||
} XEscapeFlags;
|
||||
|
||||
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags);
|
||||
static inline char* xescape(const char *s, const char *bad) {
|
||||
return xescape_full(s, bad, SIZE_MAX, false);
|
||||
return xescape_full(s, bad, SIZE_MAX, 0);
|
||||
}
|
||||
char* octescape(const char *s, size_t len);
|
||||
char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
|
||||
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
|
||||
|
||||
char* shell_escape(const char *s, const char *bad);
|
||||
char* shell_maybe_quote(const char *s, EscapeStyle style);
|
||||
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,22 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR
|
|||
return buffer;
|
||||
}
|
||||
|
||||
int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret) {
|
||||
char *buf;
|
||||
|
||||
assert(addr);
|
||||
assert(ret);
|
||||
|
||||
buf = new(char, ETHER_ADDR_TO_STRING_MAX);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ether_addr_to_string(addr, buf);
|
||||
|
||||
*ret = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
|
||||
return memcmp(a, b, ETH_ALEN);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_
|
|||
|
||||
#define ETHER_ADDR_TO_STRING_MAX (3*6)
|
||||
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
|
||||
int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret);
|
||||
|
||||
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b);
|
||||
static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
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;
|
||||
size_t sz = 0;
|
||||
char quote = 0; /* 0 or ' or " */
|
||||
bool backslash = false; /* whether we've just seen a backslash */
|
||||
char c;
|
||||
|
|
@ -44,7 +44,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
* the pointer *p at the first invalid character. */
|
||||
|
||||
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+1))
|
||||
if (!GREEDY_REALLOC(s, sz+1))
|
||||
return -ENOMEM;
|
||||
|
||||
for (;; (*p)++, c = **p) {
|
||||
|
|
@ -59,7 +59,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
/* We found a non-blank character, so we will always
|
||||
* want to return a string (even if it is empty),
|
||||
* allocate it here. */
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+1))
|
||||
if (!GREEDY_REALLOC(s, sz+1))
|
||||
return -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
|
||||
for (;; (*p)++, c = **p) {
|
||||
if (backslash) {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+7))
|
||||
if (!GREEDY_REALLOC(s, sz+7))
|
||||
return -ENOMEM;
|
||||
|
||||
if (c == 0) {
|
||||
|
|
@ -130,7 +130,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
backslash = true;
|
||||
break;
|
||||
} else {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||
if (!GREEDY_REALLOC(s, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
s[sz++] = c;
|
||||
|
|
@ -162,7 +162,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
|
|||
goto finish;
|
||||
|
||||
} else {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||
if (!GREEDY_REALLOC(s, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
s[sz++] = c;
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ bool fdname_is_valid(const char *s) {
|
|||
return false;
|
||||
}
|
||||
|
||||
return p - s < 256;
|
||||
return p - s <= FDNAME_MAX;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include "macro.h"
|
||||
|
||||
/* maximum length of fdname */
|
||||
#define FDNAME_MAX 255
|
||||
|
||||
/* Make sure we can distinguish fd 0 and NULL */
|
||||
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
|
||||
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ int fdopen_unlocked(int fd, const char *options, FILE **ret) {
|
|||
}
|
||||
|
||||
int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) {
|
||||
int r;
|
||||
int r;
|
||||
|
||||
assert(fd);
|
||||
|
||||
|
|
@ -289,7 +289,7 @@ fail:
|
|||
/* OK, the operation failed, but let's see if the right
|
||||
* contents in place already. If so, eat up the error. */
|
||||
|
||||
q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
|
||||
q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE));
|
||||
if (q <= 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -368,32 +368,38 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
|
|||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
|
||||
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct stat st;
|
||||
size_t n, size;
|
||||
int n_retries;
|
||||
bool truncated = false;
|
||||
|
||||
assert(ret_contents);
|
||||
|
||||
/* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work
|
||||
* with two sorts of virtual files. One sort uses "seq_file", and the results of
|
||||
* the first read are buffered for the second read. The other sort uses "raw"
|
||||
* reads which always go direct to the device. In the latter case, the content of
|
||||
* the virtual file must be retrieved with a single read otherwise a second read
|
||||
* might get the new value instead of finding EOF immediately. That's the reason
|
||||
* why the usage of fread(3) is prohibited in this case as it always performs a
|
||||
* second call to read(2) looking for EOF. See issue 13585. */
|
||||
/* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work with two sorts of
|
||||
* virtual files. One sort uses "seq_file", and the results of the first read are buffered for the
|
||||
* second read. The other sort uses "raw" reads which always go direct to the device. In the latter
|
||||
* case, the content of the virtual file must be retrieved with a single read otherwise a second read
|
||||
* might get the new value instead of finding EOF immediately. That's the reason why the usage of
|
||||
* fread(3) is prohibited in this case as it always performs a second call to read(2) looking for
|
||||
* EOF. See issue #13585.
|
||||
*
|
||||
* max_size specifies a limit on the bytes read. If max_size is SIZE_MAX, the full file is read. If
|
||||
* the full file is too large to read, an error is returned. For other values of max_size, *partial
|
||||
* contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
|
||||
* partial success, 1 if untruncated contents were read. */
|
||||
|
||||
fd = open(filename, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
assert(max_size <= READ_FULL_BYTES_MAX || max_size == SIZE_MAX);
|
||||
|
||||
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
|
||||
n_retries = 3;
|
||||
|
||||
for (;;) {
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
|
|
@ -402,22 +408,35 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
|
|||
|
||||
/* 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 -EFBIG;
|
||||
if (st.st_size > 0 && n_retries > 1) {
|
||||
/* Let's use the file size if we have more than 1 attempt left. On the last attempt
|
||||
* we'll ignore the file size */
|
||||
|
||||
if (st.st_size > SSIZE_MAX) { /* Avoid overflow with 32-bit size_t and 64-bit off_t. */
|
||||
|
||||
if (max_size == SIZE_MAX)
|
||||
return -EFBIG;
|
||||
|
||||
size = max_size;
|
||||
} else {
|
||||
size = MIN((size_t) st.st_size, max_size);
|
||||
|
||||
if (size > READ_FULL_BYTES_MAX)
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
size = st.st_size;
|
||||
n_retries--;
|
||||
} else {
|
||||
size = READ_FULL_BYTES_MAX;
|
||||
size = MIN(READ_FULL_BYTES_MAX, max_size);
|
||||
n_retries = 0;
|
||||
}
|
||||
|
||||
buf = malloc(size + 1);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Use a bigger allocation if we got it anyway, but not more than the limit. */
|
||||
size = MIN(malloc_usable_size(buf) - 1, READ_FULL_BYTES_MAX);
|
||||
size = MIN3(MALLOC_SIZEOF_SAFE(buf) - 1, max_size, READ_FULL_BYTES_MAX);
|
||||
|
||||
for (;;) {
|
||||
ssize_t k;
|
||||
|
|
@ -439,13 +458,20 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
|
|||
if (n <= size)
|
||||
break;
|
||||
|
||||
/* Hmm... either we read too few bytes from /proc or less likely the content
|
||||
* of the file might have been changed (and is now bigger) while we were
|
||||
* processing, let's try again either with a bigger guessed size or the new
|
||||
* file size. */
|
||||
/* If a maximum size is specified and we already read as much, no need to try again */
|
||||
if (max_size != SIZE_MAX && n >= max_size) {
|
||||
n = max_size;
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We have no further attempts left? Then the file is apparently larger than our limits. Give up. */
|
||||
if (n_retries <= 0)
|
||||
return st.st_size > 0 ? -EIO : -EFBIG;
|
||||
return -EFBIG;
|
||||
|
||||
/* Hmm... either we read too few bytes from /proc or less likely the content of the file
|
||||
* might have been changed (and is now bigger) while we were processing, let's try again
|
||||
* either with the new file size. */
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||
return -errno;
|
||||
|
|
@ -453,28 +479,32 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
|
|||
buf = mfree(buf);
|
||||
}
|
||||
|
||||
if (n < size) {
|
||||
char *p;
|
||||
if (ret_contents) {
|
||||
|
||||
/* Return rest of the buffer to libc */
|
||||
p = realloc(buf, n + 1);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
buf = p;
|
||||
/* 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 (!ret_size && memchr(buf, 0, n))
|
||||
return -EBADMSG;
|
||||
|
||||
if (n < size) {
|
||||
char *p;
|
||||
|
||||
/* Return rest of the buffer to libc */
|
||||
p = realloc(buf, n + 1);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
buf = p;
|
||||
}
|
||||
|
||||
buf[n] = 0;
|
||||
*ret_contents = TAKE_PTR(buf);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
return !truncated;
|
||||
}
|
||||
|
||||
int read_full_stream_full(
|
||||
|
|
@ -556,7 +586,7 @@ int read_full_stream_full(
|
|||
buf = t;
|
||||
/* 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;
|
||||
n = size == SIZE_MAX ? MALLOC_SIZEOF_SAFE(buf) - 1 : n_next;
|
||||
|
||||
errno = 0;
|
||||
k = fread(buf + l, 1, n - l, f);
|
||||
|
|
@ -921,12 +951,19 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
|
|||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
|
||||
static int search_and_fopen_internal(
|
||||
const char *path,
|
||||
const char *mode,
|
||||
const char *root,
|
||||
char **search,
|
||||
FILE **ret,
|
||||
char **ret_path) {
|
||||
|
||||
char **i;
|
||||
|
||||
assert(path);
|
||||
assert(mode);
|
||||
assert(_f);
|
||||
assert(ret);
|
||||
|
||||
if (!path_strv_resolve_uniq(search, root))
|
||||
return -ENOMEM;
|
||||
|
|
@ -941,7 +978,10 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
|
|||
|
||||
f = fopen(p, mode);
|
||||
if (f) {
|
||||
*_f = f;
|
||||
if (ret_path)
|
||||
*ret_path = path_simplify(TAKE_PTR(p));
|
||||
|
||||
*ret = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -952,52 +992,84 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
|
||||
int search_and_fopen(
|
||||
const char *filename,
|
||||
const char *mode,
|
||||
const char *root,
|
||||
const char **search,
|
||||
FILE **ret,
|
||||
char **ret_path) {
|
||||
|
||||
_cleanup_strv_free_ char **copy = NULL;
|
||||
|
||||
assert(path);
|
||||
assert(filename);
|
||||
assert(mode);
|
||||
assert(_f);
|
||||
assert(ret);
|
||||
|
||||
if (path_is_absolute(path)) {
|
||||
FILE *f;
|
||||
if (path_is_absolute(filename)) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
f = fopen(path, mode);
|
||||
if (f) {
|
||||
*_f = f;
|
||||
return 0;
|
||||
f = fopen(filename, mode);
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
if (ret_path) {
|
||||
char *p;
|
||||
|
||||
p = strdup(filename);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_path = path_simplify(p);
|
||||
}
|
||||
|
||||
return -errno;
|
||||
*ret = TAKE_PTR(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
copy = strv_copy((char**) search);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
return search_and_fopen_internal(path, mode, root, copy, _f);
|
||||
return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
|
||||
}
|
||||
|
||||
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
|
||||
int search_and_fopen_nulstr(
|
||||
const char *filename,
|
||||
const char *mode,
|
||||
const char *root,
|
||||
const char *search,
|
||||
FILE **ret,
|
||||
char **ret_path) {
|
||||
|
||||
_cleanup_strv_free_ char **s = NULL;
|
||||
|
||||
if (path_is_absolute(path)) {
|
||||
FILE *f;
|
||||
if (path_is_absolute(filename)) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
f = fopen(path, mode);
|
||||
if (f) {
|
||||
*_f = f;
|
||||
return 0;
|
||||
f = fopen(filename, mode);
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
if (ret_path) {
|
||||
char *p;
|
||||
|
||||
p = strdup(filename);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_path = path_simplify(p);
|
||||
}
|
||||
|
||||
return -errno;
|
||||
*ret = TAKE_PTR(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = strv_split_nulstr(search);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
return search_and_fopen_internal(path, mode, root, s, _f);
|
||||
return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
|
||||
}
|
||||
|
||||
int chase_symlinks_and_fopen_unlocked(
|
||||
|
|
@ -1011,7 +1083,6 @@ int chase_symlinks_and_fopen_unlocked(
|
|||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_free_ char *final_path = NULL;
|
||||
int mode_flags, r;
|
||||
FILE *f;
|
||||
|
||||
assert(path);
|
||||
assert(open_flags);
|
||||
|
|
@ -1025,12 +1096,10 @@ int chase_symlinks_and_fopen_unlocked(
|
|||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = fdopen_unlocked(fd, open_flags, &f);
|
||||
r = take_fdopen_unlocked(&fd, open_flags, ret_file);
|
||||
if (r < 0)
|
||||
return r;
|
||||
TAKE_FD(fd);
|
||||
|
||||
*ret_file = f;
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(final_path);
|
||||
return 0;
|
||||
|
|
@ -1166,8 +1235,8 @@ static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) {
|
|||
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;
|
||||
_cleanup_free_ char *buffer = NULL;
|
||||
size_t n = 0, count = 0;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
|
|
@ -1197,7 +1266,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
|
|||
* If a line shall be skipped ret may be initialized as NULL. */
|
||||
|
||||
if (ret) {
|
||||
if (!GREEDY_REALLOC(buffer, allocated, 1))
|
||||
if (!GREEDY_REALLOC(buffer, 1))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -1269,7 +1338,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
|
|||
}
|
||||
|
||||
if (ret) {
|
||||
if (!GREEDY_REALLOC(buffer, allocated, n + 2))
|
||||
if (!GREEDY_REALLOC(buffer, n + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
buffer[n] = c;
|
||||
|
|
@ -1347,7 +1416,7 @@ int rename_and_apply_smack_floor_label(const char *from, const char *to) {
|
|||
if (rename(from, to) < 0)
|
||||
return -errno;
|
||||
|
||||
#ifdef SMACK_RUN_LABEL
|
||||
#if HAVE_SMACK_RUN_LABEL
|
||||
r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
|||
|
|
@ -19,16 +19,17 @@
|
|||
#define LONG_LINE_MAX (1U*1024U*1024U)
|
||||
|
||||
typedef enum {
|
||||
WRITE_STRING_FILE_CREATE = 1 << 0,
|
||||
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
|
||||
WRITE_STRING_FILE_ATOMIC = 1 << 2,
|
||||
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
|
||||
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
|
||||
WRITE_STRING_FILE_SYNC = 1 << 5,
|
||||
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6,
|
||||
WRITE_STRING_FILE_NOFOLLOW = 1 << 7,
|
||||
WRITE_STRING_FILE_MKDIR_0755 = 1 << 8,
|
||||
WRITE_STRING_FILE_MODE_0600 = 1 << 9,
|
||||
WRITE_STRING_FILE_CREATE = 1 << 0,
|
||||
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
|
||||
WRITE_STRING_FILE_ATOMIC = 1 << 2,
|
||||
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
|
||||
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
|
||||
WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
|
||||
WRITE_STRING_FILE_SYNC = 1 << 6,
|
||||
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7,
|
||||
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
|
||||
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
|
||||
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
|
||||
|
||||
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
|
||||
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
|
||||
|
|
@ -68,7 +69,12 @@ int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_
|
|||
static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) {
|
||||
return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
|
||||
}
|
||||
int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size);
|
||||
|
||||
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
|
||||
static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
|
||||
return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
|
||||
}
|
||||
|
||||
int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size);
|
||||
static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) {
|
||||
return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size);
|
||||
|
|
@ -83,8 +89,8 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
|
|||
DIR *xopendirat(int dirfd, const char *name, int flags);
|
||||
int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret);
|
||||
|
||||
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
|
||||
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
|
||||
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
|
||||
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
|
||||
|
||||
int chase_symlinks_and_fopen_unlocked(
|
||||
const char *path,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "random-util.h"
|
||||
#include "ratelimit.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
|
|
@ -231,7 +232,7 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
|
|||
return fchmod_and_chown(fd, mode, uid, gid);
|
||||
}
|
||||
|
||||
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
|
||||
int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) {
|
||||
bool do_chown, do_chmod;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
|
@ -242,7 +243,11 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
|
|||
* unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does
|
||||
* on chown().
|
||||
*
|
||||
* This call is happy with O_PATH fds. */
|
||||
* This call is happy with O_PATH fds.
|
||||
*
|
||||
* If path is given, allow a fallback path which does not use /proc/self/fd/. On any normal system
|
||||
* /proc will be mounted, but in certain improperly assembled environments it might not be. This is
|
||||
* less secure (potential TOCTOU), so should only be used after consideration. */
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
|
@ -267,8 +272,14 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
|
|||
|
||||
if (((minimal ^ st.st_mode) & 07777) != 0) {
|
||||
r = fchmod_opath(fd, minimal & 07777);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < 0) {
|
||||
if (!path || r != -ENOSYS)
|
||||
return r;
|
||||
|
||||
/* Fallback path which doesn't use /proc/self/fd/. */
|
||||
if (chmod(path, minimal & 07777) < 0)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,8 +289,14 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
|
|||
|
||||
if (do_chmod) {
|
||||
r = fchmod_opath(fd, mode & 07777);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < 0) {
|
||||
if (!path || r != -ENOSYS)
|
||||
return r;
|
||||
|
||||
/* Fallback path which doesn't use /proc/self/fd/. */
|
||||
if (chmod(path, mode & 07777) < 0)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return do_chown || do_chmod;
|
||||
|
|
@ -553,10 +570,10 @@ int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
|
|||
}
|
||||
|
||||
int get_files_in_directory(const char *path, char ***list) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
size_t bufsize = 0, n = 0;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
size_t n = 0;
|
||||
|
||||
assert(path);
|
||||
|
||||
|
|
@ -576,7 +593,7 @@ int get_files_in_directory(const char *path, char ***list) {
|
|||
|
||||
if (list) {
|
||||
/* one extra slot is needed for the terminating NULL */
|
||||
if (!GREEDY_REALLOC(l, bufsize, n + 2))
|
||||
if (!GREEDY_REALLOC(l, n + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
l[n] = strdup(de->d_name);
|
||||
|
|
@ -769,9 +786,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
|
||||
bool exists = true, append_trail_slash = false;
|
||||
struct stat previous_stat;
|
||||
bool exists = true;
|
||||
char *todo;
|
||||
const char *todo;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
|
@ -860,7 +877,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
* anyway. Moreover at the end of this function after processing everything we'll always turn
|
||||
* the empty string back to "/". */
|
||||
delete_trailing_chars(root, "/");
|
||||
path_simplify(root, true);
|
||||
path_simplify(root);
|
||||
|
||||
if (flags & CHASE_PREFIX_ROOT) {
|
||||
/* We don't support relative paths in combination with a root directory */
|
||||
|
|
@ -879,84 +896,51 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_SAFE) {
|
||||
if (flags & CHASE_SAFE)
|
||||
if (fstat(fd, &previous_stat) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (flags & CHASE_TRAIL_SLASH)
|
||||
append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/.");
|
||||
|
||||
if (root) {
|
||||
_cleanup_free_ char *absolute = NULL;
|
||||
const char *e;
|
||||
|
||||
/* If we are operating on a root directory, let's take the root directory as it is. */
|
||||
|
||||
e = path_startswith(buffer, root);
|
||||
if (!e)
|
||||
todo = path_startswith(buffer, root);
|
||||
if (!todo)
|
||||
return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG,
|
||||
SYNTHETIC_ERRNO(ECHRNG),
|
||||
"Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
|
||||
path, root);
|
||||
|
||||
done = strdup(root);
|
||||
if (!done)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Make sure "todo" starts with a slash */
|
||||
absolute = strjoin("/", e);
|
||||
if (!absolute)
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(buffer, absolute);
|
||||
} else {
|
||||
todo = buffer;
|
||||
done = strdup("/");
|
||||
}
|
||||
|
||||
todo = buffer;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *first = NULL;
|
||||
_cleanup_close_ int child = -1;
|
||||
struct stat st;
|
||||
size_t n, m;
|
||||
const char *e;
|
||||
|
||||
/* Determine length of first component in the path */
|
||||
n = strspn(todo, "/"); /* The slashes */
|
||||
|
||||
if (n > 1) {
|
||||
/* If we are looking at more than a single slash then skip all but one, so that when
|
||||
* we are done with everything we have a normalized path with only single slashes
|
||||
* separating the path components. */
|
||||
todo += n - 1;
|
||||
n = 1;
|
||||
r = path_find_first_component(&todo, true, &e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) { /* We reached the end. */
|
||||
if (append_trail_slash)
|
||||
if (!strextend(&done, "/"))
|
||||
return -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
m = n + strcspn(todo + n, "/"); /* The entire length of the component */
|
||||
|
||||
/* Extract the first component. */
|
||||
first = strndup(todo, m);
|
||||
first = strndup(e, r);
|
||||
if (!first)
|
||||
return -ENOMEM;
|
||||
|
||||
todo += m;
|
||||
|
||||
/* Empty? Then we reached the end. */
|
||||
if (isempty(first))
|
||||
break;
|
||||
|
||||
/* Just a single slash? Then we reached the end. */
|
||||
if (path_equal(first, "/")) {
|
||||
/* Preserve the trailing slash */
|
||||
|
||||
if (flags & CHASE_TRAIL_SLASH)
|
||||
if (!strextend(&done, "/"))
|
||||
return -ENOMEM;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Just a dot? Then let's eat this up. */
|
||||
if (path_equal(first, "/."))
|
||||
continue;
|
||||
|
||||
/* Two dots? Then chop off the last bit of what we already found out. */
|
||||
if (path_equal(first, "/..")) {
|
||||
if (path_equal(first, "..")) {
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
_cleanup_close_ int fd_parent = -1;
|
||||
|
||||
|
|
@ -1001,22 +985,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
}
|
||||
|
||||
/* Otherwise let's see what this is. */
|
||||
child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
|
||||
child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH);
|
||||
if (child < 0) {
|
||||
|
||||
if (errno == ENOENT &&
|
||||
(flags & CHASE_NONEXISTENT) &&
|
||||
(isempty(todo) || path_is_normalized(todo))) {
|
||||
(isempty(todo) || path_is_safe(todo))) {
|
||||
/* If CHASE_NONEXISTENT is set, and the path does not exist, then
|
||||
* that's OK, return what we got so far. But don't allow this if the
|
||||
* remaining path contains "../" or something else weird. */
|
||||
|
||||
/* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
|
||||
* what we got so far. But don't allow this if the remaining path contains "../ or "./"
|
||||
* or something else weird. */
|
||||
|
||||
/* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
|
||||
if (streq_ptr(done, "/"))
|
||||
*done = '\0';
|
||||
|
||||
if (!strextend(&done, first, todo))
|
||||
if (!path_extend(&done, first, todo))
|
||||
return -ENOMEM;
|
||||
|
||||
exists = false;
|
||||
|
|
@ -1039,15 +1017,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
return log_autofs_mount_point(child, path, flags);
|
||||
|
||||
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
|
||||
char *joined;
|
||||
_cleanup_free_ char *destination = NULL;
|
||||
|
||||
/* This is a symlink, in this case read the destination. But let's make sure we don't follow
|
||||
* symlinks without bounds. */
|
||||
/* This is a symlink, in this case read the destination. But let's make sure we
|
||||
* don't follow symlinks without bounds. */
|
||||
if (--max_follow <= 0)
|
||||
return -ELOOP;
|
||||
|
||||
r = readlinkat_malloc(fd, first + n, &destination);
|
||||
r = readlinkat_malloc(fd, first, &destination);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (isempty(destination))
|
||||
|
|
@ -1073,27 +1050,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
previous_stat = st;
|
||||
}
|
||||
|
||||
free(done);
|
||||
|
||||
/* Note that we do not revalidate the root, we take it as is. */
|
||||
if (isempty(root))
|
||||
done = NULL;
|
||||
else {
|
||||
done = strdup(root);
|
||||
if (!done)
|
||||
return -ENOMEM;
|
||||
}
|
||||
r = free_and_strdup(&done, empty_to_root(root));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Prefix what's left to do with what we just read, and start the loop again, but
|
||||
* remain in the current directory. */
|
||||
joined = path_join(destination, todo);
|
||||
} else
|
||||
joined = path_join("/", destination, todo);
|
||||
if (!joined)
|
||||
/* Prefix what's left to do with what we just read, and start the loop again, but
|
||||
* remain in the current directory. */
|
||||
if (!path_extend(&destination, todo))
|
||||
return -ENOMEM;
|
||||
|
||||
free(buffer);
|
||||
todo = buffer = joined;
|
||||
free_and_replace(buffer, destination);
|
||||
todo = buffer;
|
||||
|
||||
if (flags & CHASE_STEP)
|
||||
goto chased_one;
|
||||
|
|
@ -1102,29 +1071,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
}
|
||||
|
||||
/* If this is not a symlink, then let's just add the name we read to what we already verified. */
|
||||
if (!done)
|
||||
done = TAKE_PTR(first);
|
||||
else {
|
||||
/* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
|
||||
if (streq(done, "/"))
|
||||
*done = '\0';
|
||||
|
||||
if (!strextend(&done, first))
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!path_extend(&done, first))
|
||||
return -ENOMEM;
|
||||
|
||||
/* And iterate again, but go one directory further down. */
|
||||
safe_close(fd);
|
||||
fd = TAKE_FD(child);
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
/* Special case, turn the empty string into "/", to indicate the root directory. */
|
||||
done = strdup("/");
|
||||
if (!done)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(done);
|
||||
|
||||
|
|
@ -1143,13 +1097,23 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
|
||||
chased_one:
|
||||
if (ret_path) {
|
||||
char *c;
|
||||
const char *e;
|
||||
|
||||
c = strjoin(strempty(done), todo);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
/* todo may contain slashes at the beginning. */
|
||||
r = path_find_first_component(&todo, true, &e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
*ret_path = TAKE_PTR(done);
|
||||
else {
|
||||
char *c;
|
||||
|
||||
*ret_path = c;
|
||||
c = path_join(done, e);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_path = c;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1729,3 +1693,23 @@ do_rename:
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
|
||||
RateLimit rl;
|
||||
int r;
|
||||
|
||||
r = posix_fallocate(fd, offset, size); /* returns positive errnos on error */
|
||||
if (r != EINTR)
|
||||
return -r; /* Let's return negative errnos, like common in our codebase */
|
||||
|
||||
/* On EINTR try a couple of times more, but protect against busy looping
|
||||
* (not more than 16 times per 10s) */
|
||||
rl = (RateLimit) { 10 * USEC_PER_SEC, 16 };
|
||||
while (ratelimit_below(&rl)) {
|
||||
r = posix_fallocate(fd, offset, size);
|
||||
if (r != EINTR)
|
||||
return -r;
|
||||
}
|
||||
|
||||
return -EINTR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,10 @@ int readlink_value(const char *p, char **ret);
|
|||
int readlink_and_make_absolute(const char *p, char **r);
|
||||
|
||||
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid);
|
||||
int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||
static inline int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
|
||||
return fchmod_and_chown_with_fallback(fd, NULL, mode, uid, gid); /* no fallback */
|
||||
}
|
||||
|
||||
int fchmod_umask(int fd, mode_t mode);
|
||||
int fchmod_opath(int fd, mode_t m);
|
||||
|
|
@ -146,3 +149,5 @@ int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const
|
|||
static inline int conservative_rename(const char *oldpath, const char *newpath) {
|
||||
return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
|
||||
}
|
||||
|
||||
int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
|
|||
char, string_hash_func, string_compare_func, free);
|
||||
DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
|
||||
char, string_hash_func, string_compare_func, free,
|
||||
char, free);
|
||||
void, free);
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
void path_hash_func(const char *q, struct siphash *state) {
|
||||
size_t n;
|
||||
bool add_slash = false;
|
||||
|
||||
assert(q);
|
||||
assert(state);
|
||||
|
|
@ -30,36 +30,40 @@ void path_hash_func(const char *q, struct siphash *state) {
|
|||
* similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those
|
||||
* which begin in a slash or not) will hash differently though. */
|
||||
|
||||
n = strspn(q, "/");
|
||||
if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */
|
||||
siphash24_compress(q, 1, state);
|
||||
q += n;
|
||||
}
|
||||
/* if path is absolute, add one "/" to the hash. */
|
||||
if (path_is_absolute(q))
|
||||
siphash24_compress("/", 1, state);
|
||||
|
||||
for (;;) {
|
||||
/* Determine length of next component */
|
||||
n = strcspn(q, "/");
|
||||
if (n == 0) /* Reached the end? */
|
||||
break;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
/* Add this component to the hash and skip over it */
|
||||
siphash24_compress(q, n, state);
|
||||
q += n;
|
||||
r = path_find_first_component(&q, true, &e);
|
||||
if (r == 0)
|
||||
return;
|
||||
|
||||
/* How many slashes follow this component? */
|
||||
n = strspn(q, "/");
|
||||
if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */
|
||||
break;
|
||||
if (add_slash)
|
||||
siphash24_compress_byte('/', state);
|
||||
|
||||
/* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */
|
||||
siphash24_compress(q, 1, state);
|
||||
q += n;
|
||||
if (r < 0) {
|
||||
/* if a component is invalid, then add remaining part as a string. */
|
||||
string_hash_func(q, state);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add this component to the hash. */
|
||||
siphash24_compress(e, r, state);
|
||||
|
||||
add_slash = true;
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
|
||||
char, path_hash_func, path_compare, free);
|
||||
DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
|
||||
char, path_hash_func, path_compare, free,
|
||||
void, free);
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
void trivial_hash_func(const void *p, struct siphash *state) {
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ extern const struct hash_ops string_hash_ops_free_free;
|
|||
void path_hash_func(const char *p, struct siphash *state);
|
||||
extern const struct hash_ops path_hash_ops;
|
||||
extern const struct hash_ops path_hash_ops_free;
|
||||
extern const struct hash_ops path_hash_ops_free_free;
|
||||
|
||||
/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
|
||||
* or suchlike. */
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ struct Set {
|
|||
|
||||
typedef struct CacheMem {
|
||||
const void **ptr;
|
||||
size_t n_populated, n_allocated;
|
||||
size_t n_populated;
|
||||
bool active:1;
|
||||
} CacheMem;
|
||||
|
||||
|
|
@ -1901,10 +1901,10 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags
|
|||
}
|
||||
|
||||
/* expand the cachemem if needed, return true if newly (re)activated. */
|
||||
static int cachemem_maintain(CacheMem *mem, unsigned size) {
|
||||
static int cachemem_maintain(CacheMem *mem, size_t size) {
|
||||
assert(mem);
|
||||
|
||||
if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) {
|
||||
if (!GREEDY_REALLOC(mem->ptr, size)) {
|
||||
if (size > 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -1919,7 +1919,7 @@ static int cachemem_maintain(CacheMem *mem, unsigned size) {
|
|||
|
||||
int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) {
|
||||
bool sync_keys = false, sync_values = false;
|
||||
unsigned size;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
assert(cache);
|
||||
|
|
@ -1992,8 +1992,8 @@ IteratedCache* iterated_cache_free(IteratedCache *cache) {
|
|||
}
|
||||
|
||||
int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret) {
|
||||
size_t separator_len, allocated = 0, len = 0;
|
||||
_cleanup_free_ char *str = NULL;
|
||||
size_t separator_len, len = 0;
|
||||
const char *value;
|
||||
bool first;
|
||||
|
||||
|
|
@ -2017,7 +2017,7 @@ int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **
|
|||
if (l == 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(str, allocated, len + l + (first ? 0 : separator_len) + (wrap_with_separator ? separator_len : 0) + 1))
|
||||
if (!GREEDY_REALLOC(str, len + l + (first ? 0 : separator_len) + (wrap_with_separator ? separator_len : 0) + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
if (separator_len > 0 && !first) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ typedef struct {
|
|||
|
||||
#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
|
||||
#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
|
||||
#define ITERATOR_IS_FIRST(i) ((i).idx == _IDX_ITERATOR_FIRST)
|
||||
|
||||
/* Macros for type checking */
|
||||
#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
|
||||
|
|
@ -371,28 +372,26 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
|
|||
return _hashmap_first_key(HASHMAP_BASE(h), false);
|
||||
}
|
||||
|
||||
#define hashmap_clear_with_destructor(_s, _f) \
|
||||
#define hashmap_clear_with_destructor(h, f) \
|
||||
({ \
|
||||
Hashmap *_h = (h); \
|
||||
void *_item; \
|
||||
while ((_item = hashmap_steal_first(_s))) \
|
||||
_f(_item); \
|
||||
while ((_item = hashmap_steal_first(_h))) \
|
||||
f(_item); \
|
||||
_h; \
|
||||
})
|
||||
#define hashmap_free_with_destructor(_s, _f) \
|
||||
({ \
|
||||
hashmap_clear_with_destructor(_s, _f); \
|
||||
hashmap_free(_s); \
|
||||
})
|
||||
#define ordered_hashmap_clear_with_destructor(_s, _f) \
|
||||
#define hashmap_free_with_destructor(h, f) \
|
||||
hashmap_free(hashmap_clear_with_destructor(h, f))
|
||||
#define ordered_hashmap_clear_with_destructor(h, f) \
|
||||
({ \
|
||||
OrderedHashmap *_h = (h); \
|
||||
void *_item; \
|
||||
while ((_item = ordered_hashmap_steal_first(_s))) \
|
||||
_f(_item); \
|
||||
})
|
||||
#define ordered_hashmap_free_with_destructor(_s, _f) \
|
||||
({ \
|
||||
ordered_hashmap_clear_with_destructor(_s, _f); \
|
||||
ordered_hashmap_free(_s); \
|
||||
while ((_item = ordered_hashmap_steal_first(_h))) \
|
||||
f(_item); \
|
||||
_h; \
|
||||
})
|
||||
#define ordered_hashmap_free_with_destructor(h, f) \
|
||||
ordered_hashmap_free(ordered_hashmap_clear_with_destructor(h, f))
|
||||
|
||||
/* no hashmap_next */
|
||||
void* ordered_hashmap_next(OrderedHashmap *h, const void *key);
|
||||
|
|
|
|||
|
|
@ -117,8 +117,6 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_
|
|||
uint8_t *z;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(ret_len);
|
||||
assert(p || l == 0);
|
||||
|
||||
if (l == SIZE_MAX)
|
||||
|
|
@ -152,8 +150,10 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_
|
|||
|
||||
*z = 0;
|
||||
|
||||
*ret_len = (size_t) (z - buf);
|
||||
*ret = TAKE_PTR(buf);
|
||||
if (ret_len)
|
||||
*ret_len = (size_t) (z - buf);
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -711,8 +711,6 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
|
|||
int r;
|
||||
|
||||
assert(p || l == 0);
|
||||
assert(ret);
|
||||
assert(ret_size);
|
||||
|
||||
if (l == SIZE_MAX)
|
||||
l = strlen(p);
|
||||
|
|
@ -808,8 +806,10 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r
|
|||
|
||||
*z = 0;
|
||||
|
||||
*ret_size = (size_t) (z - buf);
|
||||
*ret = TAKE_PTR(buf);
|
||||
if (ret_size)
|
||||
*ret_size = (size_t) (z - buf);
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,3 +28,8 @@ static inline bool is_gateway_hostname(const char *hostname) {
|
|||
/* This tries to identify the valid syntaxes for the our synthetic "gateway" host. */
|
||||
return STRCASE_IN_SET(hostname, "_gateway", "_gateway.");
|
||||
}
|
||||
|
||||
static inline bool is_outbound_hostname(const char *hostname) {
|
||||
/* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */
|
||||
return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ bool in4_addr_is_link_local(const struct in_addr *a) {
|
|||
bool in6_addr_is_link_local(const struct in6_addr *a) {
|
||||
assert(a);
|
||||
|
||||
return IN6_IS_ADDR_LINKLOCAL(a);
|
||||
return IN6_IS_ADDR_LINKLOCAL(a); /* lgtm [cpp/potentially-dangerous-function] */
|
||||
}
|
||||
|
||||
int in_addr_is_link_local(int family, const union in_addr_union *u) {
|
||||
|
|
@ -118,7 +118,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
|
|||
return in4_addr_is_localhost(&u->in);
|
||||
|
||||
if (family == AF_INET6)
|
||||
return IN6_IS_ADDR_LOOPBACK(&u->in6);
|
||||
return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */
|
||||
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
|
@ -812,6 +812,9 @@ int in_addr_prefix_from_string_auto_internal(
|
|||
}
|
||||
|
||||
static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
|
||||
assert(a);
|
||||
assert(state);
|
||||
|
||||
siphash24_compress(&a->family, sizeof(a->family), state);
|
||||
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
|
||||
}
|
||||
|
|
@ -819,6 +822,9 @@ static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash
|
|||
static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
|
||||
int r;
|
||||
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
r = CMP(x->family, y->family);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
|
@ -828,13 +834,46 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
|
|||
|
||||
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
|
||||
|
||||
static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
|
||||
assert(a);
|
||||
assert(state);
|
||||
|
||||
siphash24_compress(&a->family, sizeof(a->family), state);
|
||||
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
|
||||
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
|
||||
}
|
||||
|
||||
static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
|
||||
int r;
|
||||
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
r = CMP(x->family, y->family);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = CMP(x->prefixlen, y->prefixlen);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free);
|
||||
|
||||
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
|
||||
assert(addr);
|
||||
assert(state);
|
||||
|
||||
siphash24_compress(addr, sizeof(*addr), state);
|
||||
}
|
||||
|
||||
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
return memcmp(a, b, sizeof(*a));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ struct in_addr_data {
|
|||
union in_addr_union address;
|
||||
};
|
||||
|
||||
struct in_addr_prefix {
|
||||
int family;
|
||||
uint8_t prefixlen;
|
||||
union in_addr_union address;
|
||||
};
|
||||
|
||||
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);
|
||||
|
|
@ -67,7 +73,13 @@ int in_addr_prefix_range(
|
|||
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);
|
||||
static inline int in6_addr_to_string(const struct in6_addr *u, char **ret) {
|
||||
return in_addr_to_string(AF_INET6, (const union in_addr_union*) u, ret);
|
||||
}
|
||||
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
|
||||
static inline int in6_addr_prefix_to_string(const struct in6_addr *u, unsigned prefixlen, char **ret) {
|
||||
return in_addr_prefix_to_string(AF_INET6, (const union in_addr_union*) u, prefixlen, 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);
|
||||
static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
|
||||
return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret);
|
||||
|
|
@ -111,4 +123,13 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
|
|||
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
|
||||
|
||||
extern const struct hash_ops in_addr_data_hash_ops;
|
||||
extern const struct hash_ops in_addr_prefix_hash_ops;
|
||||
extern const struct hash_ops in_addr_prefix_hash_ops_free;
|
||||
extern const struct hash_ops in6_addr_hash_ops;
|
||||
|
||||
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
|
||||
#define IPV4_ADDRESS_FMT_VAL(address) \
|
||||
be32toh((address).s_addr) >> 24, \
|
||||
(be32toh((address).s_addr) >> 16) & 0xFFu, \
|
||||
(be32toh((address).s_addr) >> 8) & 0xFFu, \
|
||||
be32toh((address).s_addr) & 0xFFu
|
||||
|
|
|
|||
|
|
@ -295,7 +295,6 @@ void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
|
|||
|
||||
iovw->iovec = mfree(iovw->iovec);
|
||||
iovw->count = 0;
|
||||
iovw->size_bytes = 0;
|
||||
}
|
||||
|
||||
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
|
||||
|
|
@ -314,7 +313,7 @@ int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
|
|||
if (iovw->count >= IOV_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
|
||||
if (!GREEDY_REALLOC(iovw->iovec, iovw->count + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
|
||||
|
|
|
|||
|
|
@ -25,22 +25,25 @@ int fd_wait_for_event(int fd, int event, usec_t timeout);
|
|||
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
|
||||
|
||||
static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) {
|
||||
size_t j, r = 0;
|
||||
size_t r = 0;
|
||||
|
||||
for (j = 0; j < n; j++)
|
||||
for (size_t j = 0; j < n; j++)
|
||||
r += i[j].iov_len;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
|
||||
size_t j;
|
||||
static inline bool IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
|
||||
/* Returns true if there is nothing else to send (bytes written cover all of the iovec),
|
||||
* false if there's still work to do. */
|
||||
|
||||
for (j = 0; j < n; j++) {
|
||||
for (size_t j = 0; j < n; j++) {
|
||||
size_t sub;
|
||||
|
||||
if (_unlikely_(k <= 0))
|
||||
break;
|
||||
if (i[j].iov_len == 0)
|
||||
continue;
|
||||
if (k == 0)
|
||||
return false;
|
||||
|
||||
sub = MIN(i[j].iov_len, k);
|
||||
i[j].iov_len -= sub;
|
||||
|
|
@ -48,7 +51,9 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
|
|||
k -= sub;
|
||||
}
|
||||
|
||||
return k;
|
||||
assert(k == 0); /* Anything else would mean that we wrote more bytes than available,
|
||||
* or the kernel reported writing more bytes than sent. */
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool FILE_SIZE_VALID(uint64_t l) {
|
||||
|
|
@ -80,7 +85,6 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch
|
|||
struct iovec_wrapper {
|
||||
struct iovec *iovec;
|
||||
size_t count;
|
||||
size_t size_bytes;
|
||||
};
|
||||
|
||||
struct iovec_wrapper *iovw_new(void);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
|
@ -325,20 +326,39 @@ void log_assert_failed_return(
|
|||
#endif /* NM_IGNORED */
|
||||
|
||||
/* Logging with level */
|
||||
#define log_full_errno(level, error, ...) \
|
||||
#define log_full_errno_zerook(level, error, ...) \
|
||||
({ \
|
||||
int _level = (level), _e = (error); \
|
||||
(log_get_max_level() >= LOG_PRI(_level)) \
|
||||
_e = (log_get_max_level() >= LOG_PRI(_level)) \
|
||||
? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
|
||||
: -ERRNO_VALUE(_e); \
|
||||
_e < 0 ? _e : -ESTRPIPE; \
|
||||
})
|
||||
|
||||
#define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__)
|
||||
#if BUILD_MODE_DEVELOPER && !defined(TEST_CODE)
|
||||
# define ASSERT_NON_ZERO(x) assert((x) != 0)
|
||||
#else
|
||||
# define ASSERT_NON_ZERO(x)
|
||||
#endif
|
||||
|
||||
#define log_full_errno(level, error, ...) \
|
||||
({ \
|
||||
int _error = (error); \
|
||||
ASSERT_NON_ZERO(_error); \
|
||||
log_full_errno_zerook(level, _error, __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define log_full(level, fmt, ...) \
|
||||
({ \
|
||||
if (BUILD_MODE_DEVELOPER) \
|
||||
assert(!strstr(fmt, "%m")); \
|
||||
(void) log_full_errno_zerook(level, 0, fmt, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
int log_emergency_level(void);
|
||||
|
||||
/* Normal logging */
|
||||
#define log_debug(...) log_full_errno(LOG_DEBUG, 0, __VA_ARGS__)
|
||||
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
|
||||
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
|
||||
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
|
||||
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
|
||||
|
|
@ -353,6 +373,29 @@ 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__)
|
||||
|
||||
/* This logs at the specified level the first time it is called, and then
|
||||
* logs at debug. If the specified level is debug, this logs only the first
|
||||
* time it is called. */
|
||||
#define log_once(level, ...) \
|
||||
({ \
|
||||
if (ONCE) \
|
||||
log_full(level, __VA_ARGS__); \
|
||||
else if (LOG_PRI(level) != LOG_DEBUG) \
|
||||
log_debug(__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define log_once_errno(level, error, ...) \
|
||||
({ \
|
||||
int _err = (error); \
|
||||
if (ONCE) \
|
||||
_err = log_full_errno(level, _err, __VA_ARGS__); \
|
||||
else if (LOG_PRI(level) != LOG_DEBUG) \
|
||||
_err = log_debug_errno(_err, __VA_ARGS__); \
|
||||
else \
|
||||
_err = -ERRNO_VALUE(_err); \
|
||||
_err; \
|
||||
})
|
||||
|
||||
#if LOG_TRACE
|
||||
# define log_trace(...) log_debug(__VA_ARGS__)
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
|
||||
#endif
|
||||
#define _sentinel_ __attribute__((__sentinel__))
|
||||
#define _section_(x) __attribute__((__section__(x)))
|
||||
#define _used_ __attribute__((__used__))
|
||||
#define _destructor_ __attribute__((__destructor__))
|
||||
#define _deprecated_ __attribute__((__deprecated__))
|
||||
#define _packed_ __attribute__((__packed__))
|
||||
|
|
@ -30,7 +28,6 @@
|
|||
#define _public_ __attribute__((__visibility__("default")))
|
||||
#define _hidden_ __attribute__((__visibility__("hidden")))
|
||||
#define _weakref_(x) __attribute__((__weakref__(#x)))
|
||||
#define _align_(x) __attribute__((__aligned__(x)))
|
||||
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
|
||||
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
|
||||
#if __GNUC__ >= 7
|
||||
|
|
@ -147,9 +144,6 @@
|
|||
/* automake test harness */
|
||||
#define EXIT_TEST_SKIP 77
|
||||
|
||||
#define XSTRINGIFY(x) #x
|
||||
#define STRINGIFY(x) XSTRINGIFY(x)
|
||||
|
||||
/* builtins */
|
||||
#if __SIZEOF_INT__ == 4
|
||||
#define BUILTIN_FFS_U32(x) __builtin_ffs(x);
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ size_t page_size(void) _pure_;
|
|||
#define PAGE_OFFSET(l) ((l) & (page_size() - 1))
|
||||
|
||||
/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
|
||||
static inline void memcpy_safe(void *dst, const void *src, size_t n) {
|
||||
static inline void *memcpy_safe(void *dst, const void *src, size_t n) {
|
||||
if (n == 0)
|
||||
return;
|
||||
return dst;
|
||||
assert(src);
|
||||
memcpy(dst, src, n);
|
||||
return memcpy(dst, src, n);
|
||||
}
|
||||
|
||||
/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */
|
||||
|
|
@ -88,7 +88,7 @@ static inline void* erase_and_free(void *p) {
|
|||
if (!p)
|
||||
return NULL;
|
||||
|
||||
l = malloc_usable_size(p);
|
||||
l = MALLOC_SIZEOF_SAFE(p);
|
||||
explicit_bzero_safe(p, l);
|
||||
return mfree(p);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -430,4 +430,99 @@ static inline int missing_epoll_pwait2(
|
|||
|
||||
# define epoll_pwait2 missing_epoll_pwait2
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_MOUNT_SETATTR
|
||||
|
||||
#if !HAVE_STRUCT_MOUNT_ATTR
|
||||
struct mount_attr {
|
||||
uint64_t attr_set;
|
||||
uint64_t attr_clr;
|
||||
uint64_t propagation;
|
||||
uint64_t userns_fd;
|
||||
};
|
||||
#else
|
||||
struct mount_attr;
|
||||
#endif
|
||||
|
||||
#ifndef MOUNT_ATTR_IDMAP
|
||||
#define MOUNT_ATTR_IDMAP 0x00100000
|
||||
#endif
|
||||
|
||||
#ifndef AT_RECURSIVE
|
||||
#define AT_RECURSIVE 0x8000
|
||||
#endif
|
||||
|
||||
static inline int missing_mount_setattr(
|
||||
int dfd,
|
||||
const char *path,
|
||||
unsigned flags,
|
||||
struct mount_attr *attr,
|
||||
size_t size) {
|
||||
|
||||
# if defined __NR_mount_setattr && __NR_mount_setattr >= 0
|
||||
return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define mount_setattr missing_mount_setattr
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_OPEN_TREE
|
||||
|
||||
#ifndef OPEN_TREE_CLONE
|
||||
#define OPEN_TREE_CLONE 1
|
||||
#endif
|
||||
|
||||
#ifndef OPEN_TREE_CLOEXEC
|
||||
#define OPEN_TREE_CLOEXEC O_CLOEXEC
|
||||
#endif
|
||||
|
||||
static inline int missing_open_tree(
|
||||
int dfd,
|
||||
const char *filename,
|
||||
unsigned flags) {
|
||||
|
||||
# if defined __NR_open_tree && __NR_open_tree >= 0
|
||||
return syscall(__NR_open_tree, dfd, filename, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define open_tree missing_open_tree
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_MOVE_MOUNT
|
||||
|
||||
#ifndef MOVE_MOUNT_F_EMPTY_PATH
|
||||
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
|
||||
#endif
|
||||
|
||||
static inline int missing_move_mount(
|
||||
int from_dfd,
|
||||
const char *from_pathname,
|
||||
int to_dfd,
|
||||
const char *to_pathname,
|
||||
unsigned flags) {
|
||||
|
||||
# if defined __NR_move_mount && __NR_move_mount >= 0
|
||||
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
|
||||
# else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
# endif
|
||||
}
|
||||
|
||||
# define move_mount missing_move_mount
|
||||
#endif
|
||||
#endif /* NM_IGNORED */
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ static inline int ordered_set_put(OrderedSet *s, void *p) {
|
|||
return ordered_hashmap_put((OrderedHashmap*) s, p, p);
|
||||
}
|
||||
|
||||
static inline void *ordered_set_get(OrderedSet *s, const void *p) {
|
||||
return ordered_hashmap_get((OrderedHashmap*) s, p);
|
||||
}
|
||||
|
||||
static inline unsigned ordered_set_size(OrderedSet *s) {
|
||||
return ordered_hashmap_size((OrderedHashmap*) s);
|
||||
}
|
||||
|
|
@ -75,6 +79,17 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s);
|
|||
#define ORDERED_SET_FOREACH(e, s) \
|
||||
_ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ))
|
||||
|
||||
#define ordered_set_clear_with_destructor(s, f) \
|
||||
({ \
|
||||
OrderedSet *_s = (s); \
|
||||
void *_item; \
|
||||
while ((_item = ordered_set_steal_first(_s))) \
|
||||
f(_item); \
|
||||
_s; \
|
||||
})
|
||||
#define ordered_set_free_with_destructor(s, f) \
|
||||
ordered_set_free(ordered_set_clear_with_destructor(s, f))
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
|
||||
|
||||
|
|
|
|||
|
|
@ -110,93 +110,96 @@ int path_make_absolute_cwd(const char *p, char **ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
|
||||
char *f, *t, *r, *p;
|
||||
unsigned n_parents = 0;
|
||||
int path_make_relative(const char *from, const char *to, char **ret) {
|
||||
_cleanup_free_ char *result = NULL;
|
||||
unsigned n_parents;
|
||||
const char *f, *t;
|
||||
int r, k;
|
||||
char *p;
|
||||
|
||||
assert(from_dir);
|
||||
assert(to_path);
|
||||
assert(_r);
|
||||
assert(from);
|
||||
assert(to);
|
||||
assert(ret);
|
||||
|
||||
/* Strips the common part, and adds ".." elements as necessary. */
|
||||
|
||||
if (!path_is_absolute(from_dir) || !path_is_absolute(to_path))
|
||||
if (!path_is_absolute(from) || !path_is_absolute(to))
|
||||
return -EINVAL;
|
||||
|
||||
f = strdupa(from_dir);
|
||||
t = strdupa(to_path);
|
||||
|
||||
path_simplify(f, true);
|
||||
path_simplify(t, true);
|
||||
|
||||
/* Skip the common part. */
|
||||
for (;;) {
|
||||
size_t a, b;
|
||||
r = path_find_first_component(&from, true, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
f += *f == '/';
|
||||
t += *t == '/';
|
||||
k = path_find_first_component(&to, true, &t);
|
||||
if (k < 0)
|
||||
return k;
|
||||
|
||||
if (!*f) {
|
||||
if (!*t)
|
||||
/* from_dir equals to_path. */
|
||||
r = strdup(".");
|
||||
else
|
||||
/* from_dir is a parent directory of to_path. */
|
||||
r = strdup(t);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
if (r == 0) {
|
||||
/* end of 'from' */
|
||||
if (k == 0) {
|
||||
/* from and to are equivalent. */
|
||||
result = strdup(".");
|
||||
if (!result)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
/* 'to' is inside of 'from'. */
|
||||
result = strdup(t);
|
||||
if (!result)
|
||||
return -ENOMEM;
|
||||
|
||||
*_r = r;
|
||||
path_simplify(result);
|
||||
|
||||
if (!path_is_valid(result))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!*t)
|
||||
if (r != k || !strneq(f, t, r))
|
||||
break;
|
||||
|
||||
a = strcspn(f, "/");
|
||||
b = strcspn(t, "/");
|
||||
|
||||
if (a != b || memcmp(f, t, a) != 0)
|
||||
break;
|
||||
|
||||
f += a;
|
||||
t += b;
|
||||
}
|
||||
|
||||
/* If we're here, then "from_dir" has one or more elements that need to
|
||||
* be replaced with "..". */
|
||||
|
||||
/* Count the number of necessary ".." elements. */
|
||||
for (; *f;) {
|
||||
size_t w;
|
||||
|
||||
w = strcspn(f, "/");
|
||||
|
||||
/* If this includes ".." we can't do a simple series of "..", refuse */
|
||||
if (w == 2 && f[0] == '.' && f[1] == '.')
|
||||
return -EINVAL;
|
||||
|
||||
/* Count number of elements */
|
||||
n_parents++;
|
||||
|
||||
f += w;
|
||||
f += *f == '/';
|
||||
for (n_parents = 1;; n_parents++) {
|
||||
/* If this includes ".." we can't do a simple series of "..". */
|
||||
r = path_find_first_component(&from, false, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
r = new(char, n_parents * 3 + strlen(t) + 1);
|
||||
if (!r)
|
||||
if (isempty(t) && n_parents * 3 > PATH_MAX)
|
||||
/* PATH_MAX is counted *with* the trailing NUL byte */
|
||||
return -EINVAL;
|
||||
|
||||
result = new(char, n_parents * 3 + !isempty(t) + strlen_ptr(t));
|
||||
if (!result)
|
||||
return -ENOMEM;
|
||||
|
||||
for (p = r; n_parents > 0; n_parents--)
|
||||
for (p = result; n_parents > 0; n_parents--)
|
||||
p = mempcpy(p, "../", 3);
|
||||
|
||||
if (*t)
|
||||
strcpy(p, t);
|
||||
else
|
||||
/* Remove trailing slash */
|
||||
*(--p) = 0;
|
||||
if (isempty(t)) {
|
||||
/* Remove trailing slash and terminate string. */
|
||||
*(--p) = '\0';
|
||||
*ret = TAKE_PTR(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*_r = r;
|
||||
strcpy(p, t);
|
||||
|
||||
path_simplify(result);
|
||||
|
||||
if (!path_is_valid(result))
|
||||
return -EINVAL;
|
||||
|
||||
*ret = TAKE_PTR(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +230,7 @@ int path_strv_make_absolute_cwd(char **l) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path_simplify(t, false);
|
||||
path_simplify(t);
|
||||
free_and_replace(*s, t);
|
||||
}
|
||||
|
||||
|
|
@ -328,63 +331,53 @@ char **path_strv_resolve_uniq(char **l, const char *root) {
|
|||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
char *path_simplify(char *path, bool kill_dots) {
|
||||
char *f, *t;
|
||||
bool slash = false, ignore_slash = false, absolute;
|
||||
char *path_simplify(char *path) {
|
||||
bool add_slash = false;
|
||||
char *f = path;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* Removes redundant inner and trailing slashes. Also removes unnecessary dots
|
||||
* if kill_dots is true. Modifies the passed string in-place.
|
||||
/* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
|
||||
* Modifies the passed string in-place.
|
||||
*
|
||||
* ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false)
|
||||
* ///foo//./bar/. becomes /foo/bar (if kill_dots is true)
|
||||
* .//./foo//./bar/. becomes ././foo/./bar/. (if kill_dots is false)
|
||||
* .//./foo//./bar/. becomes foo/bar (if kill_dots is true)
|
||||
* ///foo//./bar/. becomes /foo/bar
|
||||
* .//./foo//./bar/. becomes foo/bar
|
||||
*/
|
||||
|
||||
if (isempty(path))
|
||||
return path;
|
||||
|
||||
absolute = path_is_absolute(path);
|
||||
|
||||
f = path;
|
||||
if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) {
|
||||
ignore_slash = true;
|
||||
if (path_is_absolute(path))
|
||||
f++;
|
||||
}
|
||||
|
||||
for (t = path; *f; f++) {
|
||||
for (const char *p = f;;) {
|
||||
const char *e;
|
||||
|
||||
if (*f == '/') {
|
||||
slash = true;
|
||||
continue;
|
||||
r = path_find_first_component(&p, true, &e);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (add_slash)
|
||||
*f++ = '/';
|
||||
|
||||
if (r < 0) {
|
||||
/* if path is invalid, then refuse to simplify remaining part. */
|
||||
memmove(f, p, strlen(p) + 1);
|
||||
return path;
|
||||
}
|
||||
|
||||
if (slash) {
|
||||
if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/'))
|
||||
continue;
|
||||
memmove(f, e, r);
|
||||
f += r;
|
||||
|
||||
slash = false;
|
||||
if (ignore_slash)
|
||||
ignore_slash = false;
|
||||
else
|
||||
*(t++) = '/';
|
||||
}
|
||||
|
||||
*(t++) = *f;
|
||||
add_slash = true;
|
||||
}
|
||||
|
||||
/* Special rule, if we stripped everything, we either need a "/" (for the root directory)
|
||||
* or "." for the current directory */
|
||||
if (t == path) {
|
||||
if (absolute)
|
||||
*(t++) = '/';
|
||||
else
|
||||
*(t++) = '.';
|
||||
}
|
||||
/* Special rule, if we stripped everything, we need a "." for the current directory. */
|
||||
if (f == path)
|
||||
*f++ = '.';
|
||||
|
||||
*t = 0;
|
||||
*f = '\0';
|
||||
return path;
|
||||
}
|
||||
|
||||
|
|
@ -420,7 +413,7 @@ int path_simplify_and_warn(
|
|||
lvalue, fatal ? "" : ", ignoring", path);
|
||||
}
|
||||
|
||||
path_simplify(path, true);
|
||||
path_simplify(path);
|
||||
|
||||
if (!path_is_valid(path))
|
||||
return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
|
|
@ -436,7 +429,7 @@ int path_simplify_and_warn(
|
|||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
char* path_startswith(const char *path, const char *prefix) {
|
||||
char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
|
||||
assert(path);
|
||||
assert(prefix);
|
||||
|
||||
|
|
@ -454,33 +447,30 @@ char* path_startswith(const char *path, const char *prefix) {
|
|||
return NULL;
|
||||
|
||||
for (;;) {
|
||||
size_t a, b;
|
||||
const char *p, *q;
|
||||
int r, k;
|
||||
|
||||
path += strspn(path, "/");
|
||||
prefix += strspn(prefix, "/");
|
||||
|
||||
if (*prefix == 0)
|
||||
return (char*) path;
|
||||
|
||||
if (*path == 0)
|
||||
r = path_find_first_component(&path, accept_dot_dot, &p);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
a = strcspn(path, "/");
|
||||
b = strcspn(prefix, "/");
|
||||
|
||||
if (a != b)
|
||||
k = path_find_first_component(&prefix, accept_dot_dot, &q);
|
||||
if (k < 0)
|
||||
return NULL;
|
||||
|
||||
if (memcmp(path, prefix, a) != 0)
|
||||
if (k == 0)
|
||||
return (char*) (p ?: path);
|
||||
|
||||
if (r != k)
|
||||
return NULL;
|
||||
|
||||
path += a;
|
||||
prefix += b;
|
||||
if (!strneq(p, q, r))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int path_compare(const char *a, const char *b) {
|
||||
int d;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
|
@ -488,40 +478,45 @@ int path_compare(const char *a, const char *b) {
|
|||
/* A relative path and an absolute path must not compare as equal.
|
||||
* Which one is sorted before the other does not really matter.
|
||||
* Here a relative path is ordered before an absolute path. */
|
||||
d = (a[0] == '/') - (b[0] == '/');
|
||||
if (d != 0)
|
||||
return d;
|
||||
r = CMP(path_is_absolute(a), path_is_absolute(b));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
for (;;) {
|
||||
size_t j, k;
|
||||
const char *aa, *bb;
|
||||
int j, k;
|
||||
|
||||
a += strspn(a, "/");
|
||||
b += strspn(b, "/");
|
||||
j = path_find_first_component(&a, true, &aa);
|
||||
k = path_find_first_component(&b, true, &bb);
|
||||
|
||||
if (*a == 0 && *b == 0)
|
||||
return 0;
|
||||
if (j < 0 || k < 0) {
|
||||
/* When one of paths is invalid, order invalid path after valid one. */
|
||||
r = CMP(j < 0, k < 0);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* fallback to use strcmp() if both paths are invalid. */
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
/* Order prefixes first: "/foo" before "/foo/bar" */
|
||||
if (*a == 0)
|
||||
if (j == 0) {
|
||||
if (k == 0)
|
||||
return 0;
|
||||
return -1;
|
||||
if (*b == 0)
|
||||
}
|
||||
if (k == 0)
|
||||
return 1;
|
||||
|
||||
j = strcspn(a, "/");
|
||||
k = strcspn(b, "/");
|
||||
|
||||
/* Alphabetical sort: "/foo/aaa" before "/foo/b" */
|
||||
d = memcmp(a, b, MIN(j, k));
|
||||
if (d != 0)
|
||||
return (d > 0) - (d < 0); /* sign of d */
|
||||
r = memcmp(aa, bb, MIN(j, k));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
/* Sort "/foo/a" before "/foo/aaa" */
|
||||
d = (j > k) - (j < k); /* sign of (j - k) */
|
||||
if (d != 0)
|
||||
return d;
|
||||
|
||||
a += j;
|
||||
b += k;
|
||||
r = CMP(j, k);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -535,18 +530,43 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) {
|
|||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
char* path_join_internal(const char *first, ...) {
|
||||
char *joined, *q;
|
||||
bool path_equal_filename(const char *a, const char *b) {
|
||||
_cleanup_free_ char *a_basename = NULL, *b_basename = NULL;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
r = path_extract_filename(a, &a_basename);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse basename of %s: %m", a);
|
||||
return false;
|
||||
}
|
||||
r = path_extract_filename(b, &b_basename);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse basename of %s: %m", b);
|
||||
return false;
|
||||
}
|
||||
|
||||
return path_equal(a_basename, b_basename);
|
||||
}
|
||||
|
||||
char* path_extend_internal(char **x, ...) {
|
||||
size_t sz, old_sz;
|
||||
char *q, *nx;
|
||||
const char *p;
|
||||
va_list ap;
|
||||
bool slash;
|
||||
size_t sz;
|
||||
|
||||
/* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
|
||||
* already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
|
||||
* removed. The string returned is hence always equal to or longer than the sum of the lengths of each
|
||||
* individual string.
|
||||
*
|
||||
* The first argument may be an already allocated string that is extended via realloc() if
|
||||
* non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the
|
||||
* first parameter to distinguish the two operations.
|
||||
*
|
||||
* Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
|
||||
* are optional.
|
||||
*
|
||||
|
|
@ -556,28 +576,40 @@ char* path_join_internal(const char *first, ...) {
|
|||
* path_join("foo/", "bar") → "foo/bar"
|
||||
* path_join("", "foo", "", "bar", "") → "foo/bar" */
|
||||
|
||||
sz = strlen_ptr(first);
|
||||
va_start(ap, first);
|
||||
while ((p = va_arg(ap, char*)) != POINTER_MAX)
|
||||
if (!isempty(p))
|
||||
sz += 1 + strlen(p);
|
||||
sz = old_sz = x ? strlen_ptr(*x) : 0;
|
||||
va_start(ap, x);
|
||||
while ((p = va_arg(ap, char*)) != POINTER_MAX) {
|
||||
size_t add;
|
||||
|
||||
if (isempty(p))
|
||||
continue;
|
||||
|
||||
add = 1 + strlen(p);
|
||||
if (sz > SIZE_MAX - add) { /* overflow check */
|
||||
va_end(ap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sz += add;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
joined = new(char, sz + 1);
|
||||
if (!joined)
|
||||
nx = realloc(x ? *x : NULL, GREEDY_ALLOC_ROUND_UP(sz+1));
|
||||
if (!nx)
|
||||
return NULL;
|
||||
if (x)
|
||||
*x = nx;
|
||||
|
||||
if (!isempty(first)) {
|
||||
q = stpcpy(joined, first);
|
||||
slash = endswith(first, "/");
|
||||
} else {
|
||||
/* Skip empty items */
|
||||
joined[0] = 0;
|
||||
q = joined;
|
||||
if (old_sz > 0)
|
||||
slash = nx[old_sz-1] == '/';
|
||||
else {
|
||||
nx[old_sz] = 0;
|
||||
slash = true; /* no need to generate a slash anymore */
|
||||
}
|
||||
|
||||
va_start(ap, first);
|
||||
q = nx + old_sz;
|
||||
|
||||
va_start(ap, x);
|
||||
while ((p = va_arg(ap, char*)) != POINTER_MAX) {
|
||||
if (isempty(p))
|
||||
continue;
|
||||
|
|
@ -590,7 +622,7 @@ char* path_join_internal(const char *first, ...) {
|
|||
}
|
||||
va_end(ap);
|
||||
|
||||
return joined;
|
||||
return nx;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
|
|
@ -654,7 +686,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
|
|||
|
||||
/* Resolve a single-component name to a full path */
|
||||
for (;;) {
|
||||
_cleanup_free_ char *j = NULL, *element = NULL;
|
||||
_cleanup_free_ char *element = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
|
|
@ -666,11 +698,10 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
|
|||
if (!path_is_absolute(element))
|
||||
continue;
|
||||
|
||||
j = path_join(element, name);
|
||||
if (!j)
|
||||
if (!path_extend(&element, name))
|
||||
return -ENOMEM;
|
||||
|
||||
r = check_x_access(j, ret_fd ? &fd : NULL);
|
||||
r = check_x_access(element, ret_fd ? &fd : NULL);
|
||||
if (r < 0) {
|
||||
/* PATH entries which we don't have access to are ignored, as per tradition. */
|
||||
if (r != -EACCES)
|
||||
|
|
@ -680,7 +711,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
|
|||
|
||||
/* Found it! */
|
||||
if (ret_filename)
|
||||
*ret_filename = path_simplify(TAKE_PTR(j), false);
|
||||
*ret_filename = path_simplify(TAKE_PTR(element));
|
||||
if (ret_fd)
|
||||
*ret_fd = TAKE_FD(fd);
|
||||
|
||||
|
|
@ -783,6 +814,193 @@ char* dirname_malloc(const char *path) {
|
|||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
static const char *skip_slash_or_dot(const char *p) {
|
||||
for (; !isempty(p); p++) {
|
||||
if (*p == '/')
|
||||
continue;
|
||||
if (startswith(p, "./")) {
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret) {
|
||||
const char *q, *first, *end_first, *next;
|
||||
size_t len;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* When a path is input, then returns the pointer to the first component and its length, and
|
||||
* move the input pointer to the next component or nul. This skips both over any '/'
|
||||
* immediately *before* and *after* the first component before returning.
|
||||
*
|
||||
* Examples
|
||||
* Input: p: "//.//aaa///bbbbb/cc"
|
||||
* Output: p: "bbbbb///cc"
|
||||
* ret: "aaa///bbbbb/cc"
|
||||
* return value: 3 (== strlen("aaa"))
|
||||
*
|
||||
* Input: p: "aaa//"
|
||||
* Output: p: (pointer to NUL)
|
||||
* ret: "aaa//"
|
||||
* return value: 3 (== strlen("aaa"))
|
||||
*
|
||||
* Input: p: "/", ".", ""
|
||||
* Output: p: (pointer to NUL)
|
||||
* ret: NULL
|
||||
* return value: 0
|
||||
*
|
||||
* Input: p: NULL
|
||||
* Output: p: NULL
|
||||
* ret: NULL
|
||||
* return value: 0
|
||||
*
|
||||
* Input: p: "(too long component)"
|
||||
* Output: return value: -EINVAL
|
||||
*
|
||||
* (when accept_dot_dot is false)
|
||||
* Input: p: "//..//aaa///bbbbb/cc"
|
||||
* Output: return value: -EINVAL
|
||||
*/
|
||||
|
||||
q = *p;
|
||||
|
||||
first = skip_slash_or_dot(q);
|
||||
if (isempty(first)) {
|
||||
*p = first;
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
if (streq(first, ".")) {
|
||||
*p = first + 1;
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
end_first = strchrnul(first, '/');
|
||||
len = end_first - first;
|
||||
|
||||
if (len > NAME_MAX)
|
||||
return -EINVAL;
|
||||
if (!accept_dot_dot && len == 2 && first[0] == '.' && first[1] == '.')
|
||||
return -EINVAL;
|
||||
|
||||
next = skip_slash_or_dot(end_first);
|
||||
|
||||
*p = next + streq(next, ".");
|
||||
if (ret)
|
||||
*ret = first;
|
||||
return len;
|
||||
}
|
||||
|
||||
static const char *skip_slash_or_dot_backward(const char *path, const char *q) {
|
||||
assert(path);
|
||||
|
||||
for (; q >= path; q--) {
|
||||
if (*q == '/')
|
||||
continue;
|
||||
if (q > path && strneq(q - 1, "/.", 2))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret) {
|
||||
const char *q, *last_end, *last_begin;
|
||||
size_t len;
|
||||
|
||||
/* Similar to path_find_first_component(), but search components from the end.
|
||||
*
|
||||
* Examples
|
||||
* Input: path: "//.//aaa///bbbbb/cc//././"
|
||||
* next: NULL
|
||||
* Output: next: "/cc//././"
|
||||
* ret: "cc//././"
|
||||
* return value: 2 (== strlen("cc"))
|
||||
*
|
||||
* Input: path: "//.//aaa///bbbbb/cc//././"
|
||||
* next: "/cc//././"
|
||||
* Output: next: "///bbbbb/cc//././"
|
||||
* ret: "bbbbb/cc//././"
|
||||
* return value: 5 (== strlen("bbbbb"))
|
||||
*
|
||||
* Input: path: "/", ".", "", or NULL
|
||||
* Output: next: equivalent to path
|
||||
* ret: NULL
|
||||
* return value: 0
|
||||
*
|
||||
* Input: path: "(too long component)"
|
||||
* Output: return value: -EINVAL
|
||||
*
|
||||
* (when accept_dot_dot is false)
|
||||
* Input: path: "//..//aaa///bbbbb/cc/..//"
|
||||
* Output: return value: -EINVAL
|
||||
*/
|
||||
|
||||
if (isempty(path)) {
|
||||
if (next)
|
||||
*next = path;
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (next && *next) {
|
||||
if (*next < path || *next > path + strlen(path))
|
||||
return -EINVAL;
|
||||
if (*next == path) {
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
if (!IN_SET(**next, '\0', '/'))
|
||||
return -EINVAL;
|
||||
q = *next - 1;
|
||||
} else
|
||||
q = path + strlen(path) - 1;
|
||||
|
||||
q = skip_slash_or_dot_backward(path, q);
|
||||
if ((q < path) || /* the root directory */
|
||||
(q == path && *q == '.')) { /* path is "." or "./" */
|
||||
if (next)
|
||||
*next = path;
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
last_end = q + 1;
|
||||
|
||||
while (q >= path && *q != '/')
|
||||
q--;
|
||||
|
||||
last_begin = q + 1;
|
||||
len = last_end - last_begin;
|
||||
|
||||
if (len > NAME_MAX)
|
||||
return -EINVAL;
|
||||
if (!accept_dot_dot && len == 2 && strneq(last_begin, "..", 2))
|
||||
return -EINVAL;
|
||||
|
||||
if (next) {
|
||||
q = skip_slash_or_dot_backward(path, q);
|
||||
if (q < path)
|
||||
*next = path;
|
||||
else
|
||||
*next = q + 1;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = last_begin;
|
||||
return len;
|
||||
}
|
||||
|
||||
const char *last_path_component(const char *path) {
|
||||
|
||||
/* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
|
||||
|
|
@ -826,82 +1044,81 @@ const char *last_path_component(const char *path) {
|
|||
return path + k;
|
||||
}
|
||||
|
||||
int path_extract_filename(const char *p, char **ret) {
|
||||
int path_extract_filename(const char *path, char **ret) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
const char *c;
|
||||
size_t n;
|
||||
const char *c, *next = NULL;
|
||||
int r;
|
||||
|
||||
/* 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. 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
|
||||
* -EINVAL → if the path is not valid
|
||||
* -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir
|
||||
* itself or "." 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.
|
||||
* 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 (!path_is_valid(p))
|
||||
if (!path_is_valid(path))
|
||||
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, "/"))
|
||||
r = path_find_last_component(path, false, &next, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* root directory */
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
c = last_path_component(p);
|
||||
n = strcspn(c, "/");
|
||||
|
||||
a = strndup(c, n);
|
||||
a = strndup(c, r);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!filename_is_valid(a))
|
||||
return -EINVAL;
|
||||
|
||||
*ret = TAKE_PTR(a);
|
||||
return c[n] == '/' ? O_DIRECTORY : 0;
|
||||
return strlen(c) > (size_t)r ? O_DIRECTORY : 0;
|
||||
}
|
||||
|
||||
int path_extract_directory(const char *p, char **ret) {
|
||||
int path_extract_directory(const char *path, char **ret) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
const char *c;
|
||||
const char *c, *next = NULL;
|
||||
int r;
|
||||
|
||||
/* 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
|
||||
* -EINVAL → if the path is not valid
|
||||
* -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
|
||||
* -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e.
|
||||
* the root dir itself or "." 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;
|
||||
r = path_find_last_component(path, false, &next, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* empty or root */
|
||||
return isempty(path) ? -EINVAL : -EADDRNOTAVAIL;
|
||||
if (next == path) {
|
||||
if (*path != '/') /* filename only */
|
||||
return -EDESTADDRREQ;
|
||||
|
||||
/* 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;
|
||||
a = strdup("/");
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
*ret = TAKE_PTR(a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
a = strndup(path, next - path);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
path_simplify(a);
|
||||
|
||||
if (!path_is_valid(a))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -928,44 +1145,30 @@ bool filename_is_valid(const char *p) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool path_is_valid(const char *p) {
|
||||
|
||||
bool path_is_valid_full(const char *p, bool accept_dot_dot) {
|
||||
if (isempty(p))
|
||||
return false;
|
||||
|
||||
for (const char *e = p;;) {
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
r = path_find_first_component(&e, accept_dot_dot, NULL);
|
||||
if (r < 0)
|
||||
return false;
|
||||
|
||||
/* 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) {
|
||||
|
||||
if (!path_is_valid(p))
|
||||
if (!path_is_safe(p))
|
||||
return false;
|
||||
|
||||
if (dot_or_dot_dot(p))
|
||||
return false;
|
||||
|
||||
if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
|
||||
return false;
|
||||
|
||||
if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
|
||||
if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
|
||||
return false;
|
||||
|
||||
if (strstr(p, "//"))
|
||||
|
|
@ -1167,15 +1370,15 @@ bool dot_or_dot_dot(const char *path) {
|
|||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
bool empty_or_root(const char *root) {
|
||||
bool empty_or_root(const char *path) {
|
||||
|
||||
/* For operations relative to some root directory, returns true if the specified root directory is redundant,
|
||||
* i.e. either / or NULL or the empty string or any equivalent. */
|
||||
|
||||
if (!root)
|
||||
if (isempty(path))
|
||||
return true;
|
||||
|
||||
return root[strspn(root, "/")] == 0;
|
||||
return path_equal(path, "/");
|
||||
}
|
||||
|
||||
bool path_strv_contains(char **l, const char *path) {
|
||||
|
|
|
|||
|
|
@ -58,15 +58,22 @@ int path_split_and_make_absolute(const char *p, char ***ret);
|
|||
char* path_make_absolute(const char *p, const char *prefix);
|
||||
int safe_getcwd(char **ret);
|
||||
int path_make_absolute_cwd(const char *p, char **ret);
|
||||
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
|
||||
char* path_startswith(const char *path, const char *prefix) _pure_;
|
||||
int path_make_relative(const char *from, const char *to, char **ret);
|
||||
char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
|
||||
static inline char* path_startswith(const char *path, const char *prefix) {
|
||||
return path_startswith_full(path, prefix, true);
|
||||
}
|
||||
int path_compare(const char *a, const char *b) _pure_;
|
||||
bool path_equal(const char *a, const char *b) _pure_;
|
||||
bool path_equal_or_files_same(const char *a, const char *b, int flags);
|
||||
char* path_join_internal(const char *first, ...);
|
||||
#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, POINTER_MAX)
|
||||
/* Compares only the last portion of the input paths, ie: the filenames */
|
||||
bool path_equal_filename(const char *a, const char *b);
|
||||
|
||||
char* path_simplify(char *path, bool kill_dots);
|
||||
char* path_extend_internal(char **x, ...);
|
||||
#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
|
||||
#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
|
||||
|
||||
char* path_simplify(char *path);
|
||||
|
||||
enum {
|
||||
PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */
|
||||
|
|
@ -104,7 +111,7 @@ int fsck_exists(const char *fstype);
|
|||
* directory. Excludes the specified directory itself */
|
||||
#define PATH_FOREACH_PREFIX(prefix, path) \
|
||||
for (char *_slash = ({ \
|
||||
path_simplify(strcpy(prefix, path), false); \
|
||||
path_simplify(strcpy(prefix, path)); \
|
||||
streq(prefix, "/") ? NULL : strrchr(prefix, '/'); \
|
||||
}); \
|
||||
_slash && ((*_slash = 0), true); \
|
||||
|
|
@ -113,7 +120,7 @@ int fsck_exists(const char *fstype);
|
|||
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
|
||||
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
|
||||
for (char *_slash = ({ \
|
||||
path_simplify(strcpy(prefix, path), false); \
|
||||
path_simplify(strcpy(prefix, path)); \
|
||||
if (streq(prefix, "/")) \
|
||||
prefix[0] = 0; \
|
||||
strrchr(prefix, 0); \
|
||||
|
|
@ -147,12 +154,20 @@ int fsck_exists(const char *fstype);
|
|||
})
|
||||
|
||||
char* dirname_malloc(const char *path);
|
||||
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
|
||||
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
|
||||
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);
|
||||
int path_extract_filename(const char *path, char **ret);
|
||||
int path_extract_directory(const char *path, char **ret);
|
||||
|
||||
bool filename_is_valid(const char *p) _pure_;
|
||||
bool path_is_valid(const char *p) _pure_;
|
||||
bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
|
||||
static inline bool path_is_valid(const char *p) {
|
||||
return path_is_valid_full(p, true);
|
||||
}
|
||||
static inline bool path_is_safe(const char *p) {
|
||||
return path_is_valid_full(p, false);
|
||||
}
|
||||
bool path_is_normalized(const char *p) _pure_;
|
||||
|
||||
char *file_in_same_dir(const char *path, const char *filename);
|
||||
|
|
@ -178,7 +193,7 @@ static inline const char *skip_dev_prefix(const char *p) {
|
|||
return e ?: p;
|
||||
}
|
||||
|
||||
bool empty_or_root(const char *root);
|
||||
bool empty_or_root(const char *path);
|
||||
static inline const char *empty_to_root(const char *path) {
|
||||
return isempty(path) ? "/" : path;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,64 +129,136 @@ int get_process_comm(pid_t pid, char **ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
|
||||
_cleanup_free_ char *t = NULL, *ans = NULL;
|
||||
static int get_process_cmdline_nulstr(
|
||||
pid_t pid,
|
||||
size_t max_size,
|
||||
ProcessCmdlineFlags flags,
|
||||
char **ret,
|
||||
size_t *ret_size) {
|
||||
|
||||
const char *p;
|
||||
char *t;
|
||||
size_t k;
|
||||
int r;
|
||||
|
||||
assert(line);
|
||||
assert(pid >= 0);
|
||||
|
||||
/* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (<28>). If
|
||||
* max_columns is != -1 will return a string of the specified console width at most, abbreviated with
|
||||
* an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command
|
||||
* line set (the case for kernel threads), or has a command line that resolves to the empty string
|
||||
* will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of
|
||||
* input data.
|
||||
/* Retrieves a process' command line as a "sized nulstr", i.e. possibly without the last NUL, but
|
||||
* with a specified size.
|
||||
*
|
||||
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
|
||||
* comm_fallback is false). Returns 0 and sets *line otherwise. */
|
||||
* If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command line set
|
||||
* (the case for kernel threads), or has a command line that resolves to the empty string, will
|
||||
* return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of input
|
||||
* data.
|
||||
*
|
||||
* Returns an error, 0 if output was read but is truncated, 1 otherwise.
|
||||
*/
|
||||
|
||||
p = procfs_file_alloca(pid, "cmdline");
|
||||
r = read_full_virtual_file(p, &t, &k);
|
||||
r = read_virtual_file(p, max_size, &t, &k); /* Let's assume that each input byte results in >= 1
|
||||
* columns of output. We ignore zero-width codepoints. */
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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] = ' ';
|
||||
} else {
|
||||
if (k == 0) {
|
||||
t = mfree(t);
|
||||
|
||||
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
|
||||
return -ENOENT;
|
||||
|
||||
/* Kernel threads have no argv[] */
|
||||
_cleanup_free_ char *t2 = NULL;
|
||||
_cleanup_free_ char *comm = NULL;
|
||||
|
||||
r = get_process_comm(pid, &t2);
|
||||
r = get_process_comm(pid, &comm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
free(t);
|
||||
t = strjoin("[", t2, "]");
|
||||
t = strjoin("[", comm, "]");
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
k = strlen(t);
|
||||
r = k <= max_size;
|
||||
if (r == 0) /* truncation */
|
||||
t[max_size] = '\0';
|
||||
}
|
||||
|
||||
delete_trailing_chars(t, WHITESPACE);
|
||||
*ret = t;
|
||||
*ret_size = k;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
|
||||
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
size_t k;
|
||||
char *ans;
|
||||
|
||||
ans = escape_non_printable_full(t, max_columns, eight_bit);
|
||||
if (!ans)
|
||||
return -ENOMEM;
|
||||
assert(line);
|
||||
assert(pid >= 0);
|
||||
|
||||
(void) str_realloc(&ans);
|
||||
*line = TAKE_PTR(ans);
|
||||
/* Retrieve and format a commandline. See above for discussion of retrieval options.
|
||||
*
|
||||
* There are two main formatting modes:
|
||||
*
|
||||
* - when PROCESS_CMDLINE_QUOTE is specified, output is quoted in C/Python style. If no shell special
|
||||
* characters are present, this output can be copy-pasted into the terminal to execute. UTF-8
|
||||
* output is assumed.
|
||||
*
|
||||
* - otherwise, a compact non-roundtrippable form is returned. Non-UTF8 bytes are replaced by <EFBFBD>. The
|
||||
* returned string is of the specified console width at most, abbreviated with an ellipsis.
|
||||
*
|
||||
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
|
||||
* PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */
|
||||
|
||||
int full = get_process_cmdline_nulstr(pid, max_columns, flags, &t, &k);
|
||||
if (full < 0)
|
||||
return full;
|
||||
|
||||
if (flags & (PROCESS_CMDLINE_QUOTE | PROCESS_CMDLINE_QUOTE_POSIX)) {
|
||||
ShellEscapeFlags shflags = SHELL_ESCAPE_EMPTY |
|
||||
FLAGS_SET(flags, PROCESS_CMDLINE_QUOTE_POSIX) * SHELL_ESCAPE_POSIX;
|
||||
|
||||
assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
|
||||
|
||||
_cleanup_strv_free_ char **args = NULL;
|
||||
|
||||
args = strv_parse_nulstr(t, k);
|
||||
if (!args)
|
||||
return -ENOMEM;
|
||||
|
||||
for (size_t i = 0; args[i]; i++) {
|
||||
char *e;
|
||||
|
||||
e = shell_maybe_quote(args[i], shflags);
|
||||
if (!e)
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(args[i], e);
|
||||
}
|
||||
|
||||
ans = strv_join(args, " ");
|
||||
if (!ans)
|
||||
return -ENOMEM;
|
||||
|
||||
} else {
|
||||
/* 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] = ' ';
|
||||
|
||||
delete_trailing_chars(t, WHITESPACE);
|
||||
|
||||
bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8();
|
||||
|
||||
ans = escape_non_printable_full(t, max_columns,
|
||||
eight_bit * XESCAPE_8_BIT | !full * XESCAPE_FORCE_ELLIPSIS);
|
||||
if (!ans)
|
||||
return -ENOMEM;
|
||||
|
||||
ans = str_realloc(ans);
|
||||
}
|
||||
|
||||
*line = ans;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -535,7 +607,7 @@ int get_process_root(pid_t pid, char **root) {
|
|||
int get_process_environ(pid_t pid, char **env) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *outcome = NULL;
|
||||
size_t allocated = 0, sz = 0;
|
||||
size_t sz = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
|
|
@ -556,7 +628,7 @@ int get_process_environ(pid_t pid, char **env) {
|
|||
if (sz >= ENVIRONMENT_BLOCK_MAX)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
|
||||
if (!GREEDY_REALLOC(outcome, sz + 5))
|
||||
return -ENOMEM;
|
||||
|
||||
r = safe_fgetc(f, &c);
|
||||
|
|
@ -1244,8 +1316,10 @@ int safe_fork_full(
|
|||
saved_ssp = &saved_ss;
|
||||
}
|
||||
|
||||
if (flags & FORK_NEW_MOUNTNS)
|
||||
pid = raw_clone(SIGCHLD|CLONE_NEWNS);
|
||||
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
|
||||
pid = raw_clone(SIGCHLD|
|
||||
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
|
||||
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
|
||||
else
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
typedef enum ProcessCmdlineFlags {
|
||||
PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
|
||||
PROCESS_CMDLINE_USE_LOCALE = 1 << 1,
|
||||
PROCESS_CMDLINE_QUOTE = 1 << 2,
|
||||
PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3,
|
||||
} ProcessCmdlineFlags;
|
||||
|
||||
int get_process_comm(pid_t pid, char **name);
|
||||
|
|
@ -165,6 +167,7 @@ typedef enum ForkFlags {
|
|||
FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
|
||||
FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */
|
||||
FORK_FLUSH_STDIO = 1 << 12, /* fflush() stdout (and stderr) before forking */
|
||||
FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
|
||||
} ForkFlags;
|
||||
|
||||
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
|
||||
|
|
|
|||
|
|
@ -95,17 +95,16 @@ static inline void *set_steal_first(Set *s) {
|
|||
return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
|
||||
}
|
||||
|
||||
#define set_clear_with_destructor(_s, _f) \
|
||||
#define set_clear_with_destructor(s, f) \
|
||||
({ \
|
||||
Set *_s = (s); \
|
||||
void *_item; \
|
||||
while ((_item = set_steal_first(_s))) \
|
||||
_f(_item); \
|
||||
})
|
||||
#define set_free_with_destructor(_s, _f) \
|
||||
({ \
|
||||
set_clear_with_destructor(_s, _f); \
|
||||
set_free(_s); \
|
||||
f(_item); \
|
||||
_s; \
|
||||
})
|
||||
#define set_free_with_destructor(s, f) \
|
||||
set_free(set_clear_with_destructor(s, f))
|
||||
|
||||
/* no set_steal_first_key */
|
||||
/* no set_first_key */
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ char *delete_chars(char *s, const char *bad) {
|
|||
}
|
||||
|
||||
char *delete_trailing_chars(char *s, const char *bad) {
|
||||
char *p, *c = s;
|
||||
char *c = s;
|
||||
|
||||
/* Drops all specified bad characters, at the end of the string */
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ char *delete_trailing_chars(char *s, const char *bad) {
|
|||
if (!bad)
|
||||
bad = WHITESPACE;
|
||||
|
||||
for (p = s; *p; p++)
|
||||
for (char *p = s; *p; p++)
|
||||
if (!strchr(bad, *p))
|
||||
c = p + 1;
|
||||
|
||||
|
|
@ -197,34 +197,28 @@ char ascii_toupper(char x) {
|
|||
}
|
||||
|
||||
char *ascii_strlower(char *t) {
|
||||
char *p;
|
||||
|
||||
assert(t);
|
||||
|
||||
for (p = t; *p; p++)
|
||||
for (char *p = t; *p; p++)
|
||||
*p = ascii_tolower(*p);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
char *ascii_strupper(char *t) {
|
||||
char *p;
|
||||
|
||||
assert(t);
|
||||
|
||||
for (p = t; *p; p++)
|
||||
for (char *p = t; *p; p++)
|
||||
*p = ascii_toupper(*p);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
char *ascii_strlower_n(char *t, size_t n) {
|
||||
size_t i;
|
||||
|
||||
if (n <= 0)
|
||||
return t;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (size_t i = 0; i < n; i++)
|
||||
t[i] = ascii_tolower(t[i]);
|
||||
|
||||
return t;
|
||||
|
|
@ -256,10 +250,8 @@ int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
|
|||
}
|
||||
|
||||
bool chars_intersect(const char *a, const char *b) {
|
||||
const char *p;
|
||||
|
||||
/* Returns true if any of the chars in a are in b. */
|
||||
for (p = a; *p; p++)
|
||||
for (const char *p = a; *p; p++)
|
||||
if (strchr(b, *p))
|
||||
return true;
|
||||
|
||||
|
|
@ -267,8 +259,6 @@ bool chars_intersect(const char *a, const char *b) {
|
|||
}
|
||||
|
||||
bool string_has_cc(const char *p, const char *ok) {
|
||||
const char *t;
|
||||
|
||||
assert(p);
|
||||
|
||||
/*
|
||||
|
|
@ -277,14 +267,11 @@ bool string_has_cc(const char *p, const char *ok) {
|
|||
* considered OK.
|
||||
*/
|
||||
|
||||
for (t = p; *t; t++) {
|
||||
for (const char *t = p; *t; t++) {
|
||||
if (ok && strchr(ok, *t))
|
||||
continue;
|
||||
|
||||
if (*t > 0 && *t < ' ')
|
||||
return true;
|
||||
|
||||
if (*t == 127)
|
||||
if (char_is_cc(*t))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +460,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
|
|||
* very end.
|
||||
*/
|
||||
|
||||
size_t i = 0, last_char_width[4] = {}, k = 0, j;
|
||||
size_t i = 0, last_char_width[4] = {}, k = 0;
|
||||
|
||||
assert(len > 0); /* at least a terminating NUL */
|
||||
|
||||
|
|
@ -502,7 +489,7 @@ char *cellescape(char *buf, size_t len, const char *s) {
|
|||
|
||||
/* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
|
||||
* characters ideally, but the buffer is shorter than that in the first place take what we can get */
|
||||
for (j = 0; j < ELEMENTSOF(last_char_width); j++) {
|
||||
for (size_t j = 0; j < ELEMENTSOF(last_char_width); j++) {
|
||||
|
||||
if (i + 4 <= len) /* nice, we reached our space goal */
|
||||
break;
|
||||
|
|
@ -541,7 +528,7 @@ char* strshorten(char *s, size_t l) {
|
|||
}
|
||||
|
||||
char *strreplace(const char *text, const char *old_string, const char *new_string) {
|
||||
size_t l, old_len, new_len, allocated = 0;
|
||||
size_t l, old_len, new_len;
|
||||
char *t, *ret = NULL;
|
||||
const char *f;
|
||||
|
||||
|
|
@ -555,7 +542,7 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
|
|||
new_len = strlen(new_string);
|
||||
|
||||
l = strlen(text);
|
||||
if (!GREEDY_REALLOC(ret, allocated, l+1))
|
||||
if (!GREEDY_REALLOC(ret, l+1))
|
||||
return NULL;
|
||||
|
||||
f = text;
|
||||
|
|
@ -571,7 +558,7 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
|
|||
d = t - ret;
|
||||
nl = l - old_len + new_len;
|
||||
|
||||
if (!GREEDY_REALLOC(ret, allocated, nl + 1))
|
||||
if (!GREEDY_REALLOC(ret, nl + 1))
|
||||
return mfree(ret);
|
||||
|
||||
l = nl;
|
||||
|
|
@ -811,6 +798,92 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) {
|
|||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) {
|
||||
size_t m, a, l_separator;
|
||||
va_list ap;
|
||||
int l;
|
||||
|
||||
/* Appends a formatted string to the specified string. Don't use this in inner loops, since then
|
||||
* we'll spend a tonload of time in determining the length of the string passed in, over and over
|
||||
* again. */
|
||||
|
||||
assert(x);
|
||||
assert(format);
|
||||
|
||||
l_separator = isempty(*x) ? 0 : strlen_ptr(separator);
|
||||
|
||||
/* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
|
||||
if (*x) {
|
||||
m = strlen(*x);
|
||||
a = MALLOC_SIZEOF_SAFE(*x);
|
||||
assert(a >= m + 1);
|
||||
} else
|
||||
m = a = 0;
|
||||
|
||||
if (a - m < 17 + l_separator) { /* if there's less than 16 chars space, then enlarge the buffer first */
|
||||
char *n;
|
||||
|
||||
if (_unlikely_(l_separator > SIZE_MAX - 64)) /* overflow check #1 */
|
||||
return -ENOMEM;
|
||||
if (_unlikely_(m > SIZE_MAX - 64 - l_separator)) /* overflow check #2 */
|
||||
return -ENOMEM;
|
||||
|
||||
n = realloc(*x, m + 64 + l_separator);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
*x = n;
|
||||
a = MALLOC_SIZEOF_SAFE(*x);
|
||||
}
|
||||
|
||||
/* Now, let's try to format the string into it */
|
||||
memcpy_safe(*x + m, separator, l_separator);
|
||||
va_start(ap, format);
|
||||
l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
assert(l >= 0);
|
||||
|
||||
if ((size_t) l < a - m - l_separator) {
|
||||
char *n;
|
||||
|
||||
/* Nice! This worked. We are done. But first, let's return the extra space we don't
|
||||
* need. This should be a cheap operation, since we only lower the allocation size here,
|
||||
* never increase. */
|
||||
n = realloc(*x, m + (size_t) l + l_separator + 1);
|
||||
if (n)
|
||||
*x = n;
|
||||
} else {
|
||||
char *n;
|
||||
|
||||
/* Wasn't enough. Then let's allocate exactly what we need. */
|
||||
|
||||
if (_unlikely_((size_t) l > SIZE_MAX - (l_separator + 1))) /* overflow check #1 */
|
||||
goto oom;
|
||||
if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */
|
||||
goto oom;
|
||||
|
||||
a = m + (size_t) l + l_separator + 1;
|
||||
n = realloc(*x, a);
|
||||
if (!n)
|
||||
goto oom;
|
||||
*x = n;
|
||||
|
||||
va_start(ap, format);
|
||||
l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
assert((size_t) l < a - m - l_separator);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
oom:
|
||||
/* truncate the bytes added after the first vsnprintf() attempt again */
|
||||
(*x)[m] = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
char *strrep(const char *s, unsigned n) {
|
||||
char *r, *p;
|
||||
size_t l;
|
||||
|
|
@ -911,14 +984,12 @@ int free_and_strndup(char **p, const char *s, size_t l) {
|
|||
}
|
||||
|
||||
bool string_is_safe(const char *p) {
|
||||
const char *t;
|
||||
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
/* Checks if the specified string contains no quotes or control characters */
|
||||
|
||||
for (t = p; *t; t++) {
|
||||
for (const char *t = p; *t; t++) {
|
||||
if (*t > 0 && *t < ' ') /* no control characters */
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,14 @@ static inline bool _pure_ in_charset(const char *s, const char* charset) {
|
|||
return s[strspn(s, charset)] == '\0';
|
||||
}
|
||||
|
||||
static inline bool char_is_cc(char p) {
|
||||
/* char is unsigned on some architectures, e.g. aarch64. So, compiler may warn the condition
|
||||
* p >= 0 is always true. See #19543. Hence, let's cast to unsigned before the comparison. Note
|
||||
* that the cast in the right hand side is redundant, as according to the C standard, compilers
|
||||
* automatically cast a signed value to unsigned when comparing with an unsigned variable. Just
|
||||
* for safety and readability. */
|
||||
return (uint8_t) p < (uint8_t) ' ' || p == 127;
|
||||
}
|
||||
bool string_has_cc(const char *p, const char *ok) _pure_;
|
||||
|
||||
char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent);
|
||||
|
|
@ -148,10 +156,12 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
|
|||
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
|
||||
|
||||
char *strextend_with_separator_internal(char **x, const char *separator, ...) _sentinel_;
|
||||
|
||||
#define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL)
|
||||
#define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL)
|
||||
|
||||
int strextendf_with_separator(char **x, const char *separator, const char *format, ...) _printf_(3,4);
|
||||
#define strextendf(x, ...) strextendf_with_separator(x, NULL, __VA_ARGS__)
|
||||
|
||||
char *strrep(const char *s, unsigned n);
|
||||
|
||||
int split_pair(const char *s, const char *sep, char **l, char **r);
|
||||
|
|
@ -214,17 +224,13 @@ static inline void *memory_startswith_no_case(const void *p, size_t sz, const ch
|
|||
return (uint8_t*) p + n;
|
||||
}
|
||||
|
||||
static inline char* str_realloc(char **p) {
|
||||
/* Reallocate *p to actual size */
|
||||
static inline char* str_realloc(char *p) {
|
||||
/* Reallocate *p to actual size. Ignore failure, and return the original string on error. */
|
||||
|
||||
if (!*p)
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
char *t = realloc(*p, strlen(*p) + 1);
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
return (*p = t);
|
||||
return realloc(p, strlen(p) + 1) ?: p;
|
||||
}
|
||||
|
||||
char* string_erase(char *x);
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) {
|
|||
|
||||
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
|
@ -285,7 +285,7 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
|
|||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (!GREEDY_REALLOC(l, allocated, n + 2))
|
||||
if (!GREEDY_REALLOC(l, n + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
l[n++] = TAKE_PTR(word);
|
||||
|
|
@ -307,7 +307,7 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
|
|||
#if 0 /* NM_IGNORED */
|
||||
int strv_split_colon_pairs(char ***t, const char *s) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
|
@ -337,7 +337,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
|
|||
if (!second_or_empty)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!GREEDY_REALLOC(l, allocated, n + 3))
|
||||
if (!GREEDY_REALLOC(l, n + 3))
|
||||
return -ENOMEM;
|
||||
|
||||
l[n++] = TAKE_PTR(first);
|
||||
|
|
@ -716,9 +716,9 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
|
|||
* is provided separately.
|
||||
*/
|
||||
|
||||
size_t n_allocated = 0, n = 0;
|
||||
_cleanup_free_ char *m = NULL;
|
||||
char * const *i;
|
||||
size_t n = 0;
|
||||
|
||||
assert(ret);
|
||||
assert(ret_size);
|
||||
|
|
@ -728,7 +728,7 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
|
|||
|
||||
z = strlen(*i);
|
||||
|
||||
if (!GREEDY_REALLOC(m, n_allocated, n + z + 2))
|
||||
if (!GREEDY_REALLOC(m, n + z + 2))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(m + n, *i, z + 1);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/timex.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
|
@ -1250,25 +1249,10 @@ int parse_nsec(const char *t, nsec_t *nsec) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool ntp_synced(void) {
|
||||
struct timex txc = {};
|
||||
|
||||
if (adjtimex(&txc) < 0)
|
||||
return false;
|
||||
|
||||
/* Consider the system clock synchronized if the reported maximum error is smaller than the maximum
|
||||
* value (16 seconds). Ignore the STA_UNSYNC flag as it may have been set to prevent the kernel from
|
||||
* touching the RTC. */
|
||||
if (txc.maxerror >= 16000000)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_timezones(char ***ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_strv_free_ char **zones = NULL;
|
||||
size_t n_zones = 0, n_allocated = 0;
|
||||
size_t n_zones = 0;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
|
@ -1277,14 +1261,13 @@ int get_timezones(char ***ret) {
|
|||
if (!zones)
|
||||
return -ENOMEM;
|
||||
|
||||
n_allocated = 2;
|
||||
n_zones = 1;
|
||||
|
||||
f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
|
||||
if (f) {
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
char *p, *w;
|
||||
_cleanup_free_ char *line = NULL, *w = NULL;
|
||||
char *p;
|
||||
size_t k;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
|
|
@ -1315,12 +1298,10 @@ int get_timezones(char ***ret) {
|
|||
if (!w)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
|
||||
free(w);
|
||||
if (!GREEDY_REALLOC(zones, n_zones + 2))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
zones[n_zones++] = w;
|
||||
zones[n_zones++] = TAKE_PTR(w);
|
||||
zones[n_zones] = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1554,7 +1535,7 @@ int time_change_fd(void) {
|
|||
.it_value.tv_sec = TIME_T_MAX,
|
||||
};
|
||||
|
||||
_cleanup_close_ int fd;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
|
||||
|
||||
|
|
|
|||
|
|
@ -133,8 +133,6 @@ int parse_sec_def_infinity(const char *t, usec_t *usec);
|
|||
int parse_time(const char *t, usec_t *usec, usec_t default_unit);
|
||||
int parse_nsec(const char *t, nsec_t *nsec);
|
||||
|
||||
bool ntp_synced(void);
|
||||
|
||||
int get_timezones(char ***l);
|
||||
bool timezone_is_valid(const char *name, int log_level);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
|
|||
|
||||
/* This is much like mkostemp() but is subject to umask(). */
|
||||
int mkostemp_safe(char *pattern) {
|
||||
int fd = -1; /* avoid false maybe-uninitialized warning */
|
||||
int fd = -1; /* avoid false maybe-uninitialized warning */
|
||||
|
||||
assert(pattern);
|
||||
|
||||
|
|
@ -127,13 +127,10 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
|
|||
return -EINVAL;
|
||||
|
||||
if (d) {
|
||||
char *j;
|
||||
|
||||
j = path_join(d, nf);
|
||||
if (!j)
|
||||
if (!path_extend(&d, nf))
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = path_simplify(j, false);
|
||||
*ret = path_simplify(TAKE_PTR(d));
|
||||
} else
|
||||
*ret = TAKE_PTR(nf);
|
||||
|
||||
|
|
@ -173,13 +170,10 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
|
|||
return -EINVAL;
|
||||
|
||||
if (d) {
|
||||
char *j;
|
||||
|
||||
j = path_join(d, nf);
|
||||
if (!j)
|
||||
if (!path_extend(&d, nf))
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = path_simplify(j, false);
|
||||
*ret = path_simplify(TAKE_PTR(d));
|
||||
} else
|
||||
*ret = TAKE_PTR(nf);
|
||||
|
||||
|
|
@ -224,7 +218,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
|
|||
|
||||
*x = 0;
|
||||
|
||||
*ret = path_simplify(t, false);
|
||||
*ret = path_simplify(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,3 +109,14 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
|||
#endif
|
||||
|
||||
bool is_nologin_shell(const char *shell);
|
||||
|
||||
int is_this_me(const char *username);
|
||||
|
||||
/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */
|
||||
#define PASSWORD_LOCKED_AND_INVALID "!*"
|
||||
|
||||
/* A password indicating "look in shadow file, please!" for "struct passwd"'s .pw_passwd */
|
||||
#define PASSWORD_SEE_SHADOW "x"
|
||||
|
||||
/* A password indicating "hey, no password required for login" */
|
||||
#define PASSWORD_NONE ""
|
||||
|
|
|
|||
|
|
@ -198,8 +198,7 @@ char *utf8_escape_invalid(const char *str) {
|
|||
}
|
||||
|
||||
*s = '\0';
|
||||
(void) str_realloc(&p);
|
||||
return p;
|
||||
return str_realloc(p);
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
|
|
@ -216,7 +215,7 @@ static int utf8_char_console_width(const char *str) {
|
|||
return unichar_iswide(c) ? 2 : 1;
|
||||
}
|
||||
|
||||
char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
|
||||
char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis) {
|
||||
char *p, *s, *prev_s;
|
||||
size_t n = 0; /* estimated print width */
|
||||
|
||||
|
|
@ -233,8 +232,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
|
|||
int len;
|
||||
char *saved_s = s;
|
||||
|
||||
if (!*str) /* done! */
|
||||
goto finish;
|
||||
if (!*str) { /* done! */
|
||||
if (force_ellipsis)
|
||||
goto truncation;
|
||||
else
|
||||
goto finish;
|
||||
}
|
||||
|
||||
len = utf8_encoded_valid_unichar(str, SIZE_MAX);
|
||||
if (len > 0) {
|
||||
|
|
@ -278,15 +281,14 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
|
|||
|
||||
truncation:
|
||||
/* Try to go back one if we don't have enough space for the ellipsis */
|
||||
if (n + 1 >= console_width)
|
||||
if (n + 1 > console_width)
|
||||
s = prev_s;
|
||||
|
||||
s = mempcpy(s, "…", strlen("…"));
|
||||
|
||||
finish:
|
||||
*s = '\0';
|
||||
(void) str_realloc(&p);
|
||||
return p;
|
||||
return str_realloc(p);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
|
|||
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
|
||||
|
||||
char *utf8_escape_invalid(const char *s);
|
||||
char *utf8_escape_non_printable_full(const char *str, size_t console_width);
|
||||
char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis);
|
||||
static inline char *utf8_escape_non_printable(const char *str) {
|
||||
return utf8_escape_non_printable_full(str, SIZE_MAX);
|
||||
return utf8_escape_non_printable_full(str, SIZE_MAX, false);
|
||||
}
|
||||
|
||||
size_t utf8_encode_unichar(char *out_utf8, char32_t g);
|
||||
|
|
|
|||
|
|
@ -7,11 +7,17 @@
|
|||
|
||||
#include "type.h"
|
||||
|
||||
#define _align_(x) __attribute__((__aligned__(x)))
|
||||
#define _const_ __attribute__((__const__))
|
||||
#define _pure_ __attribute__((__pure__))
|
||||
#define _section_(x) __attribute__((__section__(x)))
|
||||
#define _used_ __attribute__((__used__))
|
||||
#define _unused_ __attribute__((__unused__))
|
||||
#define _cleanup_(x) __attribute__((__cleanup__(x)))
|
||||
|
||||
#define XSTRINGIFY(x) #x
|
||||
#define STRINGIFY(x) XSTRINGIFY(x)
|
||||
|
||||
#ifndef __COVERITY__
|
||||
# define VOID_0 ((void)0)
|
||||
#else
|
||||
|
|
@ -44,6 +50,17 @@
|
|||
#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
|
||||
#define UNIQ __COUNTER__
|
||||
|
||||
/* Note that this works differently from pthread_once(): this macro does
|
||||
* not synchronize code execution, i.e. code that is run conditionalized
|
||||
* on this macro will run concurrently to all other code conditionalized
|
||||
* the same way, there's no ordering or completion enforced. */
|
||||
#define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
|
||||
#define __ONCE(o) \
|
||||
({ \
|
||||
static bool (o) = false; \
|
||||
__sync_bool_compare_and_swap(&(o), false, true); \
|
||||
})
|
||||
|
||||
#undef MAX
|
||||
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
|
||||
#define __MAX(aq, a, bq, b) \
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
|
|||
|
||||
int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
size_t n = 0;
|
||||
const char *p;
|
||||
bool first = true;
|
||||
int r;
|
||||
|
|
@ -445,7 +445,7 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r
|
|||
}
|
||||
|
||||
if (_ret) {
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
|
||||
|
|
@ -477,12 +477,12 @@ finish:
|
|||
if (_ret) {
|
||||
if (n == 0) {
|
||||
/* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
|
||||
if (!GREEDY_REALLOC(ret, allocated, 2))
|
||||
if (!GREEDY_REALLOC(ret, 2))
|
||||
return -ENOMEM;
|
||||
|
||||
ret[n++] = '.';
|
||||
} else {
|
||||
if (!GREEDY_REALLOC(ret, allocated, n + 1))
|
||||
if (!GREEDY_REALLOC(ret, n + 1))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
|
@ -994,7 +994,7 @@ bool dns_service_name_is_valid(const char *name) {
|
|||
l = strlen(name);
|
||||
if (l <= 0)
|
||||
return false;
|
||||
if (l > 63)
|
||||
if (l > DNS_LABEL_MAX)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -1200,13 +1200,11 @@ int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
|
|||
|
||||
int dns_name_count_labels(const char *name) {
|
||||
unsigned n = 0;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
p = name;
|
||||
for (;;) {
|
||||
for (const char *p = name;;) {
|
||||
r = dns_name_parent(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
@ -1219,7 +1217,7 @@ int dns_name_count_labels(const char *name) {
|
|||
n++;
|
||||
}
|
||||
|
||||
return (int) n;
|
||||
return n;
|
||||
}
|
||||
|
||||
int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
|
||||
|
|
@ -1346,7 +1344,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
|
|||
return -EINVAL;
|
||||
#elif HAVE_LIBIDN
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t n = 0, allocated = 0;
|
||||
size_t n = 0;
|
||||
bool first = true;
|
||||
int r, q;
|
||||
|
||||
|
|
@ -1368,7 +1366,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
|
|||
if (q > 0)
|
||||
r = q;
|
||||
|
||||
if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
if (!GREEDY_REALLOC(buf, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
|
||||
|
|
@ -1386,7 +1384,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
|
|||
if (n > DNS_HOSTNAME_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!GREEDY_REALLOC(buf, allocated, n + 1))
|
||||
if (!GREEDY_REALLOC(buf, n + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
buf[n] = 0;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,38 @@
|
|||
|
||||
#include "log.h"
|
||||
|
||||
#define log_interface_full_errno(ifname, level, error, ...) \
|
||||
#define log_interface_full_errno_zerook(ifname, level, error, ...) \
|
||||
({ \
|
||||
const char *_ifname = (ifname); \
|
||||
_ifname ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _ifname, NULL, NULL, ##__VA_ARGS__) : \
|
||||
log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define log_interface_full_errno(ifname, level, error, ...) \
|
||||
({ \
|
||||
int _error = (error); \
|
||||
ASSERT_NON_ZERO(_error); \
|
||||
log_interface_full_errno_zerook(ifname, level, _error, __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define log_interface_prefix_full_errno_zerook(prefix, ifname_expr, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) \
|
||||
log_interface_full_errno_zerook( \
|
||||
ifname_expr, \
|
||||
LOG_DEBUG, _e, prefix fmt, \
|
||||
##__VA_ARGS__); \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
|
||||
#define log_interface_prefix_full_errno(prefix, ifname_expr, error, fmt, ...) \
|
||||
({ \
|
||||
int _error = (error); \
|
||||
ASSERT_NON_ZERO(_error); \
|
||||
log_interface_prefix_full_errno_zerook(prefix, ifname_expr, _error, fmt, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* The following macros append INTERFACE= to the message.
|
||||
* The macros require a struct named 'Link' which contains 'char *ifname':
|
||||
|
|
@ -21,15 +46,22 @@
|
|||
* See, network/networkd-link.h for example.
|
||||
*/
|
||||
|
||||
#define log_link_full_errno(link, level, error, ...) \
|
||||
#define log_link_full_errno_zerook(link, level, error, ...) \
|
||||
({ \
|
||||
const Link *_l = (link); \
|
||||
log_interface_full_errno(_l ? _l->ifname : NULL, level, error, ##__VA_ARGS__); \
|
||||
log_interface_full_errno_zerook(_l ? _l->ifname : NULL, level, error, __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define log_link_full(link, level, ...) (void) log_link_full_errno(link, level, 0, __VA_ARGS__)
|
||||
#define log_link_full_errno(link, level, error, ...) \
|
||||
({ \
|
||||
int _error = (error); \
|
||||
ASSERT_NON_ZERO(_error); \
|
||||
log_link_full_errno_zerook(link, level, _error, __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define log_link_debug(link, ...) log_link_full_errno(link, LOG_DEBUG, 0, __VA_ARGS__)
|
||||
#define log_link_full(link, level, ...) (void) log_link_full_errno_zerook(link, level, 0, __VA_ARGS__)
|
||||
|
||||
#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, __VA_ARGS__)
|
||||
#define log_link_info(link, ...) log_link_full(link, LOG_INFO, __VA_ARGS__)
|
||||
#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, __VA_ARGS__)
|
||||
#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, __VA_ARGS__)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue