dhcp: merge branch 'th/systemd-dhcp-integration' (bgo #742719)

Update internal dhcp library with new code from upstream systemd.

  HEAD=117cb022b13cedf4035e2a778b8d61528075a8b4
  MERGE=$(git rev-list --merges -n1 $HEAD)

  # how did we modify the systemd code?
  git diffs $MERGE^1 $HEAD -- :/src/dhcp-manager/systemd-dhcp/

  # what changed in systemd since last merge:
  git diffs $MERGE^2 $HEAD -- :/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/

https://bugzilla.gnome.org/show_bug.cgi?id=742719
This commit is contained in:
Thomas Haller 2015-03-03 12:28:49 +01:00
commit 4e07f61173
37 changed files with 3513 additions and 842 deletions

View file

@ -65,6 +65,9 @@ SYSTEMD_DHCP_CFLAGS = \
-I$(top_srcdir)/src/dhcp-manager/systemd-dhcp
libsystemd_dhcp_la_SOURCES = \
dhcp-manager/systemd-dhcp/src/libsystemd/sd-id128/sd-id128.c \
dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.c \
dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-identifier.h \
dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-network.c \
dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-packet.c \
dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-internal.h \
@ -96,8 +99,11 @@ libsystemd_dhcp_la_SOURCES = \
dhcp-manager/systemd-dhcp/src/shared/util.h \
dhcp-manager/systemd-dhcp/src/shared/in-addr-util.h \
dhcp-manager/systemd-dhcp/src/shared/list.h \
dhcp-manager/systemd-dhcp/src/shared/log.h \
dhcp-manager/systemd-dhcp/src/shared/fileio.h \
dhcp-manager/systemd-dhcp/src/shared/fileio.c \
dhcp-manager/systemd-dhcp/src/shared/path-util.c \
dhcp-manager/systemd-dhcp/src/shared/path-util.h \
dhcp-manager/systemd-dhcp/src/shared/strv.h \
dhcp-manager/systemd-dhcp/src/shared/strv.c \
dhcp-manager/systemd-dhcp/src/shared/unaligned.h \

View file

@ -746,12 +746,6 @@ ip6_start (NMDhcpClient *client,
goto error;
}
r = sd_dhcp6_client_set_ifname (priv->client6, iface);
if (r < 0) {
nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP ifname (%d)", iface, r);
goto error;
}
r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client);
if (r < 0) {
nm_log_warn (LOGD_DHCP6, "(%s): failed to set DHCP callback (%d)", iface, r);

View file

@ -36,14 +36,20 @@
#include <unistd.h>
#include <sys/syscall.h>
#include <net/if_arp.h>
#include <sys/resource.h>
#include "nm-logging.h"
static inline guint32
/*****************************************************************************/
static inline NMLogLevel
_slog_level_to_nm (int slevel)
{
switch (slevel) {
case LOG_DEBUG: return LOGL_DEBUG;
case LOG_WARNING: return LOGL_WARN;
case LOG_CRIT:
case LOG_ERR: return LOGL_ERR;
case LOG_INFO:
case LOG_NOTICE:
@ -51,39 +57,37 @@ _slog_level_to_nm (int slevel)
}
}
#define log_meta(level, file, line, func, format, ...) \
G_STMT_START { \
guint32 _l = _slog_level_to_nm ((level)); \
if (nm_logging_enabled (_l, LOGD_DHCP)) { \
const char *_location = strrchr (file "", '/'); \
#define log_internal(level, error, file, line, func, format, ...) \
({ \
int _nm_e = (error); \
NMLogLevel _nm_l = _slog_level_to_nm ((level)); \
if (nm_logging_enabled (_nm_l, LOGD_DHCP)) { \
const char *_nm_location = strrchr ((""file), '/'); \
\
_nm_log (_location ? _location + 1 : file, line, func, _l, LOGD_DHCP, 0, format, ## __VA_ARGS__); \
_nm_log (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, ("%s"format), "sd-dhcp: ", ## __VA_ARGS__); \
} \
} G_STMT_END
(_nm_e > 0 ? -_nm_e : _nm_e); \
})
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
#define log_full(level, ...) log_meta((level), __FILE__, __LINE__, __func__, __VA_ARGS__);
#define log_full_errno(level, error, ...) \
({ \
log_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__); \
})
#define log_dhcp_client(client, fmt, ...) \
log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
#define log_assert_failed(e, file, line, func) \
#define log_assert_failed(text, file, line, func) \
G_STMT_START { \
nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assertion failed: " # e); \
g_assert (FALSE); \
} G_STMT_END
#define log_assert_failed_unreachable(t, file, line, func) \
G_STMT_START { \
nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assert unreachable: " # t); \
log_internal (LOG_CRIT, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", text, file, line, func); \
g_assert_not_reached (); \
} G_STMT_END
#define log_assert_failed_return(e, file, line, func) \
nm_log_err (LOGD_DHCP, #file ":" #line "(" #func "): assert return: " # e); \
#define log_assert_failed_return(text, file, line, func) \
G_STMT_START { \
log_internal (LOG_DEBUG, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", text, file, line, func); \
g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, text); \
} G_STMT_END
#define log_oom nm_log_err(LOGD_CORE, "%s:%s/%s: OOM", __FILE__, __LINE__, __func__)
/*****************************************************************************/
/* Can't include both net/if.h and linux/if.h; so have to define this here */
#ifndef IFNAMSIZ

View file

@ -0,0 +1,111 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright (C) 2015 Tom Gundersen <teg@jklmen>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "sd-id128.h"
#if 0 /* NM_IGNORED a*/
#include "libudev.h"
#include "udev-util.h"
#include "virt.h"
#include "sparse-endian.h"
#else /* NM_IGNORED */
#include <net/if.h>
#endif /* NM_IGNORED */
#include "siphash24.h"
#include "dhcp6-protocol.h"
#include "dhcp-identifier.h"
#include "network-internal.h"
#define SYSTEMD_PEN 43793
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
sd_id128_t machine_id;
int r;
assert(duid);
assert(len);
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
duid->type = htobe16(DHCP6_DUID_EN);
duid->en.pen = htobe32(SYSTEMD_PEN);
*len = sizeof(duid->type) + sizeof(duid->en);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
directly */
siphash24(duid->en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
return 0;
}
int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id) {
#if 0 /* NM_IGNORED */
/* name is a pointer to memory in the udev_device struct, so must
have the same scope */
_cleanup_udev_device_unref_ struct udev_device *device = NULL;
#else /* NM_IGNORED */
char name_buf[IF_NAMESIZE];
#endif /* NM_IGNORED */
const char *name = NULL;
uint64_t id;
#if 0 /* NM_IGNORED */
if (detect_container(NULL) <= 0) {
/* not in a container, udev will be around */
_cleanup_udev_unref_ struct udev *udev;
char ifindex_str[2 + DECIMAL_STR_MAX(int)];
udev = udev_new();
if (!udev)
return -ENOMEM;
sprintf(ifindex_str, "n%d", ifindex);
device = udev_device_new_from_device_id(udev, ifindex_str);
if (device) {
if (udev_device_get_is_initialized(device) <= 0)
/* not yet ready */
return -EBUSY;
name = net_get_name(device);
}
}
#else /* NM_IGNORED */
name = if_indextoname(ifindex, name_buf);
#endif /* NM_IGNORED */
if (name)
siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes);
/* fold into 32 bits */
*_id = (id & 0xffffffff) ^ (id >> 32);
return 0;
}

View file

@ -0,0 +1,65 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright (C) 2015 Tom Gundersen <teg@jklmen>
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include "macro.h"
#include "sparse-endian.h"
#include "sd-id128.h"
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
struct duid {
uint16_t type;
union {
struct {
/* DHCP6_DUID_LLT */
uint16_t htype;
uint32_t time;
uint8_t haddr[0];
} _packed_ llt;
struct {
/* DHCP6_DUID_EN */
uint32_t pen;
uint8_t id[8];
} _packed_ en;
struct {
/* DHCP6_DUID_LL */
int16_t htype;
uint8_t haddr[0];
} _packed_ ll;
struct {
/* DHCP6_DUID_UUID */
sd_id128_t uuid;
} _packed_ uuid;
struct {
uint8_t data[MAX_DUID_LEN];
} _packed_ raw;
};
} _packed_;
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id);

View file

@ -73,4 +73,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
#define DHCP_CLIENT_DONT_DESTROY(client) \
_cleanup_dhcp_client_unref_ _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
#define log_dhcp_client(client, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)

View file

@ -20,7 +20,6 @@
#include "nm-sd-adapt.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/if_packet.h>
@ -28,7 +27,6 @@
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/filter.h>
#include "socket-util.h"
@ -65,7 +63,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- mac address length */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */

View file

@ -20,22 +20,14 @@
#include "nm-sd-adapt.h"
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <sys/param.h>
#include "util.h"
#include "list.h"
#include "dhcp-protocol.h"
#include "dhcp-lease-internal.h"
#include "dhcp-internal.h"
#include "sd-dhcp-lease.h"
#include "sd-dhcp-client.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312

View file

@ -58,7 +58,7 @@ struct DHCP6IA {
typedef struct DHCP6IA DHCP6IA;
#define log_dhcp6_client(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
int dhcp_network_icmp6_bind_router_solicitation(int index);
int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);

View file

@ -53,6 +53,8 @@ enum {
DHCP6_PORT_CLIENT = 546,
};
#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC
#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC
#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC
#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC
#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC
@ -73,6 +75,7 @@ enum {
enum DHCP6State {
DHCP6_STATE_STOPPED = 0,
DHCP6_STATE_INFORMATION_REQUEST = 1,
DHCP6_STATE_SOLICITATION = 2,
DHCP6_STATE_REQUEST = 3,
DHCP6_STATE_BOUND = 4,

View file

@ -24,12 +24,10 @@
#include <netinet/ether.h>
#include <linux/if.h>
#include <arpa/inet.h>
#include <fnmatch.h>
#if 0 /* NM_IGNORED */
#include "strv.h"
#include "siphash24.h"
#include "libudev-private.h"
#endif /* NM_IGNORED */
#include "dhcp-lease-internal.h"
#if 0 /* NM_IGNORED */
@ -92,10 +90,10 @@ int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8
}
bool net_match_config(const struct ether_addr *match_mac,
const char *match_path,
const char *match_driver,
const char *match_type,
const char *match_name,
char * const *match_paths,
char * const *match_drivers,
char * const *match_types,
char * const *match_names,
Condition *match_host,
Condition *match_virt,
Condition *match_kernel,
@ -108,37 +106,37 @@ bool net_match_config(const struct ether_addr *match_mac,
const char *dev_name) {
if (match_host && !condition_test(match_host))
return 0;
return false;
if (match_virt && !condition_test(match_virt))
return 0;
return false;
if (match_kernel && !condition_test(match_kernel))
return 0;
return false;
if (match_arch && !condition_test(match_arch))
return 0;
return false;
if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
return 0;
return false;
if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0)))
return 0;
if (!strv_isempty(match_paths) &&
(!dev_path || !strv_fnmatch(match_paths, dev_path, 0)))
return false;
if (match_driver) {
if (dev_parent_driver && !streq(match_driver, dev_parent_driver))
return 0;
else if (!streq_ptr(match_driver, dev_driver))
return 0;
}
if (!strv_isempty(match_drivers) &&
(!dev_driver || !strv_fnmatch(match_drivers, dev_driver, 0)))
return false;
if (match_type && !streq_ptr(match_type, dev_type))
return 0;
if (!strv_isempty(match_types) &&
(!dev_type || !strv_fnmatch_or_empty(match_types, dev_type, 0)))
return false;
if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0)))
return 0;
if (!strv_isempty(match_names) &&
(!dev_name || !strv_fnmatch_or_empty(match_names, dev_name, 0)))
return false;
return 1;
return true;
}
int config_parse_net_condition(const char *unit,
@ -221,6 +219,49 @@ int config_parse_ifname(const char *unit,
return 0;
}
int config_parse_ifnames(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char ***sv = data;
const char *word, *state;
size_t l;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
FOREACH_WORD(word, l, rvalue, state) {
char *n;
n = strndup(word, l);
if (!n)
return log_oom();
if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
r = strv_consume(sv, n);
if (r < 0)
return log_oom();
}
return 0;
}
int config_parse_ifalias(const char *unit,
const char *filename,
unsigned line,
@ -233,7 +274,7 @@ int config_parse_ifalias(const char *unit,
void *userdata) {
char **s = data;
char *n;
_cleanup_free_ char *n = NULL;
assert(filename);
assert(lvalue);
@ -247,17 +288,15 @@ int config_parse_ifalias(const char *unit,
if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
free(n);
return 0;
}
free(*s);
if (*n)
if (*n) {
*s = n;
else {
free(n);
n = NULL;
} else
*s = NULL;
}
return 0;
}

View file

@ -23,8 +23,6 @@
#include "nm-sd-adapt.h"
#include <netinet/ether.h>
#include <netinet/in.h>
#include <stdbool.h>
#if 0 /* NM_IGNORED */
@ -32,10 +30,10 @@
#include "condition.h"
bool net_match_config(const struct ether_addr *match_mac,
const char *match_path,
const char *match_driver,
const char *match_type,
const char *match_name,
char * const *match_path,
char * const *match_driver,
char * const *match_type,
char * const *match_name,
Condition *match_host,
Condition *match_virt,
Condition *match_kernel,
@ -59,6 +57,10 @@ int config_parse_ifname(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_ifnames(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_ifalias(const char *unit, const char *filename, unsigned line,
const char *section, unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data, void *userdata);

View file

@ -26,21 +26,19 @@
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <linux/if_infiniband.h>
#include <netinet/ether.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include "util.h"
#include "list.h"
#include "refcnt.h"
#include "async.h"
#include "dhcp-protocol.h"
#include "dhcp-internal.h"
#include "dhcp-lease-internal.h"
#include "dhcp-identifier.h"
#include "sd-dhcp-client.h"
#define MAX_CLIENT_ID_LEN 64 /* Arbitrary limit */
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
struct sd_dhcp_client {
@ -62,29 +60,31 @@ struct sd_dhcp_client {
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
uint16_t arp_type;
union {
struct {
uint8_t type; /* 0: Generic (non-LL) (RFC 2132) */
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ gen;
struct {
uint8_t type; /* 1: Ethernet Link-Layer (RFC 2132) */
uint8_t haddr[ETH_ALEN];
} _packed_ eth;
struct {
uint8_t type; /* 2 - 254: ARP/Link-Layer (RFC 2132) */
uint8_t haddr[0];
} _packed_ ll;
struct {
uint8_t type; /* 255: Node-specific (RFC 4361) */
uint8_t iaid[4];
uint8_t duid[MAX_CLIENT_ID_LEN - 4];
} _packed_ ns;
struct {
uint8_t type;
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ raw;
} client_id;
struct {
uint8_t type;
union {
struct {
/* 0: Generic (non-LL) (RFC 2132) */
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ gen;
struct {
/* 1: Ethernet Link-Layer (RFC 2132) */
uint8_t haddr[ETH_ALEN];
} _packed_ eth;
struct {
/* 2 - 254: ARP/Link-Layer (RFC 2132) */
uint8_t haddr[0];
} _packed_ ll;
struct {
/* 255: Node-specific (RFC 4361) */
uint32_t iaid;
struct duid duid;
} _packed_ ns;
struct {
uint8_t data[MAX_CLIENT_ID_LEN];
} _packed_ raw;
};
} _packed_ client_id;
size_t client_id_len;
char *hostname;
char *vendor_class_identifier;
@ -241,10 +241,9 @@ int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
*data = NULL;
*data_len = 0;
if (client->client_id_len) {
*type = client->client_id.raw.type;
*type = client->client_id.type;
*data = client->client_id.raw.data;
*data_len = client->client_id_len -
sizeof (client->client_id.raw.type);
*data_len = client->client_id_len - sizeof(client->client_id.type);
}
return 0;
@ -272,8 +271,8 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
break;
}
if (client->client_id_len == data_len + sizeof (client->client_id.raw.type) &&
client->client_id.raw.type == type &&
if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
client->client_id.type == type &&
memcmp(&client->client_id.raw.data, data, data_len) == 0)
return 0;
@ -284,9 +283,9 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
client_stop(client, DHCP_EVENT_STOP);
}
client->client_id.raw.type = type;
client->client_id.type = type;
memcpy(&client->client_id.raw.data, data, data_len);
client->client_id_len = data_len + sizeof (client->client_id.raw.type);
client->client_id_len = data_len + sizeof (client->client_id.type);
if (need_restart && client->state != DHCP_STATE_STOPPED)
sd_dhcp_client_start(client);
@ -402,7 +401,7 @@ static void client_stop(sd_dhcp_client *client, int error) {
static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
uint8_t type, size_t *_optlen, size_t *_optoffset) {
_cleanup_free_ DHCPPacket *packet = NULL;
_cleanup_free_ DHCPPacket *packet;
size_t optlen, optoffset, size;
be16_t max_size;
usec_t time_now;
@ -416,8 +415,6 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
assert(_optoffset);
assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
/* See RFC2131 section 4.4.1 */
optlen = DHCP_MIN_OPTIONS_SIZE;
size = sizeof(DHCPPacket) + optlen;
@ -465,12 +462,21 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
if (client->arp_type == ARPHRD_ETHER)
memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
/* If no client identifier exists, construct one from an ethernet
address if present */
if (client->client_id_len == 0 && client->arp_type == ARPHRD_ETHER) {
client->client_id.eth.type = ARPHRD_ETHER;
memcpy(&client->client_id.eth.haddr, &client->mac_addr, ETH_ALEN);
client->client_id_len = sizeof (client->client_id.eth);
/* If no client identifier exists, construct an RFC 4361-compliant one */
if (client->client_id_len == 0) {
size_t duid_len;
client->client_id.type = 255;
r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
if (r < 0)
return r;
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
if (r < 0)
return r;
client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
}
/* Some DHCP servers will refuse to issue an DHCP lease if the Client
@ -479,7 +485,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
DHCP_OPTION_CLIENT_IDENTIFIER,
client->client_id_len,
&client->client_id.raw);
&client->client_id);
if (r < 0)
return r;
}
@ -504,7 +510,7 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
than the defined default size unless the Maximum Messge Size option
is explicitely set
is explicitly set
RFC3442 "Requirements to Avoid Sizing Constraints":
Because a full routing table can be quite large, the standard 576
@ -1035,7 +1041,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
if (client->client_id_len) {
r = dhcp_lease_set_client_id(lease,
(uint8_t *) &client->client_id.raw,
(uint8_t *) &client->client_id,
client->client_id_len);
if (r < 0)
return r;
@ -1102,7 +1108,7 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
if (client->client_id_len) {
r = dhcp_lease_set_client_id(lease,
(uint8_t *) &client->client_id.raw,
(uint8_t *) &client->client_id,
client->client_id_len);
if (r < 0)
return r;
@ -1386,8 +1392,10 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
client->last_addr = client->lease->address;
r = client_set_lease_timeouts(client);
if (r < 0)
if (r < 0) {
log_dhcp_client(client, "could not set lease timeouts");
goto error;
}
r = dhcp_network_bind_udp_socket(client->lease->address,
DHCP_PORT_CLIENT);
@ -1615,7 +1623,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
r = client_start(client);
if (r >= 0)
log_dhcp_client(client, "STARTED on ifindex %u", client->index);
log_dhcp_client(client, "STARTED on ifindex %i", client->index);
return r;
}
@ -1674,7 +1682,7 @@ sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
}
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
if (client && REFCNT_DEC(client->n_ref) <= 0) {
if (client && REFCNT_DEC(client->n_ref) == 0) {
log_dhcp_client(client, "FREE");
client_initialize(client);

View file

@ -24,24 +24,15 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include "util.h"
#include "list.h"
#if 0 /* NM_IGNORED */
#include "mkdir.h"
#endif /* NM_IGNORED */
#include "fileio.h"
#include "unaligned.h"
#include "in-addr-util.h"
#include "dhcp-protocol.h"
#include "dhcp-internal.h"
#include "dhcp-lease-internal.h"
#include "sd-dhcp-lease.h"
#include "sd-dhcp-client.h"
#include "network-internal.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
@ -55,7 +46,7 @@ int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
assert_return(lease, -EINVAL);
assert_return(lease, -EINVAL);
assert_return(lifetime, -EINVAL);
*lifetime = lease->lifetime;
@ -197,7 +188,7 @@ sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
}
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
if (lease && REFCNT_DEC(lease->n_ref) == 0) {
free(lease->hostname);
free(lease->domainname);
free(lease->dns);
@ -501,11 +492,20 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
case DHCP_OPTION_DOMAIN_NAME:
{
_cleanup_free_ char *domainname = NULL;
char *e;
r = lease_parse_string(option, len, &domainname);
if (r < 0)
return r;
/* Chop off trailing dot of domain name that some DHCP
* servers send us back. Internally we want to store
* host names without trailing dots and
* host_name_is_valid() doesn't accept them. */
e = endswith(domainname, ".");
if (e)
*e = 0;
if (!hostname_is_valid(domainname) || is_localhost(domainname))
break;
@ -518,11 +518,16 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
case DHCP_OPTION_HOST_NAME:
{
_cleanup_free_ char *hostname = NULL;
char *e;
r = lease_parse_string(option, len, &hostname);
if (r < 0)
return r;
e = endswith(hostname, ".");
if (e)
*e = 0;
if (!hostname_is_valid(hostname) || is_localhost(hostname))
break;
@ -667,7 +672,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
if (r >= 0) {
_cleanup_free_ char *client_id_hex = NULL;
_cleanup_free_ char *client_id_hex;
client_id_hex = hexmem (client_id, client_id_len);
if (!client_id_hex) {
@ -689,7 +694,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
finish:
if (r < 0)
log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
log_error_errno(r, "Failed to save lease data %s: %m", lease_file);
return r;
}
@ -729,8 +734,7 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (r == -ENOENT)
return 0;
log_error("Failed to read %s: %s", lease_file, strerror(-r));
return r;
return log_error_errno(r, "Failed to read %s: %m", lease_file);
}
r = inet_pton(AF_INET, address, &addr);

View file

@ -29,9 +29,7 @@
#if 0 /* NM_IGNORED */
#include "udev.h"
#include "udev-util.h"
#include "virt.h"
#endif /* NM_IGNORED */
#include "siphash24.h"
#include "util.h"
#include "refcnt.h"
@ -40,14 +38,7 @@
#include "dhcp6-protocol.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#define SYSTEMD_PEN 43793
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
#include "dhcp-identifier.h"
#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
@ -61,12 +52,12 @@ struct sd_dhcp6_client {
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
uint16_t arp_type;
char ifname[IFNAMSIZ];
DHCP6IA ia_na;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
int fd;
bool information_request;
be16_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_len;
@ -77,32 +68,7 @@ struct sd_dhcp6_client {
sd_event_source *timeout_resend_expire;
sd_dhcp6_client_cb_t cb;
void *userdata;
union {
struct {
uint16_t type; /* DHCP6_DUID_LLT */
uint16_t htype;
uint32_t time;
uint8_t haddr[0];
} _packed_ llt;
struct {
uint16_t type; /* DHCP6_DUID_EN */
uint32_t pen;
uint8_t id[8];
} _packed_ en;
struct {
uint16_t type; /* DHCP6_DUID_LL */
uint16_t htype;
uint8_t haddr[0];
} _packed_ ll;
struct {
uint16_t type; /* DHCP6_DUID_UUID */
sd_id128_t uuid;
} _packed_ uuid;
struct {
uint16_t type;
uint8_t data[MAX_DUID_LEN];
} _packed_ raw;
} duid;
struct duid duid;
size_t duid_len;
};
@ -205,19 +171,19 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du
switch (type) {
case DHCP6_DUID_LLT:
if (duid_len <= sizeof(client->duid.llt) - 2)
if (duid_len <= sizeof(client->duid.llt))
return -EINVAL;
break;
case DHCP6_DUID_EN:
if (duid_len != sizeof(client->duid.en) - 2)
if (duid_len != sizeof(client->duid.en))
return -EINVAL;
break;
case DHCP6_DUID_LL:
if (duid_len <= sizeof(client->duid.ll) - 2)
if (duid_len <= sizeof(client->duid.ll))
return -EINVAL;
break;
case DHCP6_DUID_UUID:
if (duid_len != sizeof(client->duid.uuid) - 2)
if (duid_len != sizeof(client->duid.uuid))
return -EINVAL;
break;
default:
@ -225,9 +191,28 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du
break;
}
client->duid.raw.type = htobe16(type);
client->duid.type = htobe16(type);
memcpy(&client->duid.raw.data, duid, duid_len);
client->duid_len = duid_len + 2; /* +2 for sizeof(type) */
client->duid_len = duid_len + sizeof(client->duid.type);
return 0;
}
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
bool enabled) {
assert_return(client, -EINVAL);
client->information_request = enabled;
return 0;
}
int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
bool *enabled) {
assert_return(client, -EINVAL);
assert_return(enabled, -EINVAL);
*enabled = client->information_request;
return 0;
}
@ -338,6 +323,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
message->transaction_id = client->transaction_id;
switch(client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
message->type = DHCP6_INFORMATION_REQUEST;
break;
case DHCP6_STATE_SOLICITATION:
message->type = DHCP6_SOLICIT;
@ -499,6 +489,12 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
init_retransmit_time = DHCP6_INF_TIMEOUT;
max_retransmit_time = DHCP6_INF_MAX_RT;
break;
case DHCP6_STATE_SOLICITATION:
if (client->retransmit_count && client->lease) {
@ -631,24 +627,16 @@ error:
}
static int client_ensure_iaid(sd_dhcp6_client *client) {
const char *name;
uint64_t id;
int r;
assert(client);
if (client->ia_na.id)
return 0;
name = client->ifname;
if (name)
siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
else
/* fall back to mac address if no predictable name available */
siphash24((uint8_t*)&id, &client->mac_addr,
client->mac_addr_len, HASH_KEY.bytes);
/* fold into 32 bits */
client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
if (r < 0)
return r;
return 0;
}
@ -726,6 +714,12 @@ static int client_parse_message(sd_dhcp6_client *client,
break;
case DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Information request ignoring IA NA option");
break;
}
r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
&lease->ia);
if (r < 0 && r != -ENOMSG)
@ -752,16 +746,21 @@ static int client_parse_message(sd_dhcp6_client *client,
}
}
if ((r < 0 && r != -ENOMSG) || !clientid) {
if (r == -ENOMSG)
r = 0;
if (r < 0 || !clientid) {
log_dhcp6_client(client, "%s has incomplete options",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r < 0)
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
r = dhcp6_lease_get_serverid(lease, &id, &id_len);
if (r < 0)
log_dhcp6_client(client, "%s has no server id",
dhcp6_message_type_to_string(message->type));
}
return r;
}
@ -793,12 +792,15 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
return 0;
}
if (client->lease)
if (client->lease) {
dhcp6_lease_clear_timers(&client->lease->ia);
client->lease = sd_dhcp6_lease_unref(client->lease);
}
client->lease = sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
client->lease = lease;
lease = NULL;
}
return DHCP6_STATE_BOUND;
}
@ -825,7 +827,8 @@ static int client_receive_advertise(sd_dhcp6_client *client,
return r;
r = dhcp6_lease_get_preference(client->lease, &pref_lease);
if (!client->lease || r < 0 || pref_advertise > pref_lease) {
if (r < 0 || pref_advertise > pref_lease) {
sd_dhcp6_lease_unref(client->lease);
client->lease = lease;
lease = NULL;
@ -842,7 +845,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
void *userdata) {
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
_cleanup_free_ DHCP6Message *message = NULL;
_cleanup_free_ DHCP6Message *message;
int r, buflen, len;
assert(s);
@ -892,6 +895,17 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
return 0;
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
r = client_receive_reply(client, message, len);
if (r < 0)
return 0;
client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
client_start(client, DHCP6_STATE_STOPPED);
break;
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
@ -967,37 +981,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
switch (state) {
case DHCP6_STATE_STOPPED:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
client->state = DHCP6_STATE_STOPPED;
return 0;
}
/* fall through */
case DHCP6_STATE_SOLICITATION:
r = client_ensure_iaid(client);
if (r < 0)
return r;
r = dhcp6_network_bind_udp_socket(client->index, NULL);
if (r < 0)
return r;
client->fd = r;
r = sd_event_add_io(client->event, &client->receive_message,
client->fd, EPOLLIN, client_receive_message,
client);
if (r < 0)
return r;
r = sd_event_source_set_priority(client->receive_message,
client->event_priority);
if (r < 0)
return r;
r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message");
if (r < 0)
return r;
client->state = DHCP6_STATE_SOLICITATION;
break;
case DHCP6_STATE_INFORMATION_REQUEST:
case DHCP6_STATE_REQUEST:
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
@ -1102,6 +1098,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client)
int sd_dhcp6_client_start(sd_dhcp6_client *client)
{
int r = 0;
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
@ -1111,7 +1108,44 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client)
if (r < 0)
return r;
return client_start(client, DHCP6_STATE_SOLICITATION);
r = client_ensure_iaid(client);
if (r < 0)
return r;
r = dhcp6_network_bind_udp_socket(client->index, NULL);
if (r < 0)
return r;
client->fd = r;
r = sd_event_add_io(client->event, &client->receive_message,
client->fd, EPOLLIN, client_receive_message,
client);
if (r < 0)
goto error;
r = sd_event_source_set_priority(client->receive_message,
client->event_priority);
if (r < 0)
goto error;
r = sd_event_source_set_description(client->receive_message,
"dhcp6-receive-message");
if (r < 0)
goto error;
if (client->information_request)
state = DHCP6_STATE_INFORMATION_REQUEST;
log_dhcp6_client(client, "Started in %s mode",
client->information_request? "Information request":
"Managed");
return client_start(client, state);
error:
client_reset(client);
return r;
}
int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
@ -1158,7 +1192,7 @@ sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
}
sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
if (client && REFCNT_DEC(client->n_ref) <= 0) {
if (client && REFCNT_DEC(client->n_ref) == 0) {
client_reset(client);
sd_dhcp6_client_detach_event(client);
@ -1176,7 +1210,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
{
_cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
#if 0 /* NM_IGNORED */
sd_id128_t machine_id;
int r;
#endif /* NM_IGNORED */
size_t t;
@ -1197,17 +1230,9 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
#if 0 /* NM_IGNORED */
/* initialize DUID */
client->duid.en.type = htobe16(DHCP6_DUID_EN);
client->duid.en.pen = htobe32(SYSTEMD_PEN);
client->duid_len = sizeof(client->duid.en);
r = sd_id128_get_machine(&machine_id);
r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
if (r < 0)
return r;
/* a bit of snake-oil perhaps, but no need to expose the machine-id
directly */
siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
#endif /* NM_IGNORED */
client->req_opts_len = ELEMENTSOF(default_req_opts);
@ -1225,16 +1250,3 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret)
return 0;
}
/*******************************************/
/* NetworkManager additions */
int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname)
{
assert_return(client, -EINVAL);
assert_return(ifname, -EINVAL);
assert_return(strlen (ifname) < sizeof (client->ifname), -EINVAL);
strcpy(client->ifname, ifname);
return 0;
}

View file

@ -112,9 +112,11 @@ int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
}
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
assert_return(lease, -EINVAL);
assert_return(preference, -EINVAL);
if (!lease)
return -EINVAL;
*preference = lease->preference;
return 0;
@ -146,10 +148,9 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
return 0;
}
int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(lifetime_preferred, -EINVAL);
@ -169,22 +170,9 @@ int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease,
return 0;
}
int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(lifetime_preferred, -EINVAL);
assert_return(lifetime_valid, -EINVAL);
if (!lease->ia.addresses)
return -ENOMSG;
lease->addr_iter = lease->ia.addresses;
return sd_dhcp6_lease_get_next_address(lease, addr, lifetime_preferred,
lifetime_valid);
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
if (lease)
lease->addr_iter = lease->ia.addresses;
}
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
@ -195,7 +183,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) {
}
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
if (lease && REFCNT_DEC(lease->n_ref) == 0) {
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);

View file

@ -0,0 +1,235 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include "util.h"
#include "macro.h"
#include "sd-id128.h"
#if 0 /* NM_IGNORED */
_public_ char *sd_id128_to_string(sd_id128_t id, char s[33]) {
unsigned n;
assert_return(s, NULL);
for (n = 0; n < 16; n++) {
s[n*2] = hexchar(id.bytes[n] >> 4);
s[n*2+1] = hexchar(id.bytes[n] & 0xF);
}
s[32] = 0;
return s;
}
_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
unsigned n, i;
sd_id128_t t;
bool is_guid = false;
assert_return(s, -EINVAL);
assert_return(ret, -EINVAL);
for (n = 0, i = 0; n < 16;) {
int a, b;
if (s[i] == '-') {
/* Is this a GUID? Then be nice, and skip over
* the dashes */
if (i == 8)
is_guid = true;
else if (i == 13 || i == 18 || i == 23) {
if (!is_guid)
return -EINVAL;
} else
return -EINVAL;
i++;
continue;
}
a = unhexchar(s[i++]);
if (a < 0)
return -EINVAL;
b = unhexchar(s[i++]);
if (b < 0)
return -EINVAL;
t.bytes[n++] = (a << 4) | b;
}
if (i != (is_guid ? 36 : 32))
return -EINVAL;
if (s[i] != 0)
return -EINVAL;
*ret = t;
return 0;
}
static sd_id128_t make_v4_uuid(sd_id128_t id) {
/* Stolen from generate_random_uuid() of drivers/char/random.c
* in the kernel sources */
/* Set UUID version to 4 --- truly random generation */
id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
/* Set the UUID variant to DCE */
id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
return id;
}
#endif
_public_ int sd_id128_get_machine(sd_id128_t *ret) {
static thread_local sd_id128_t saved_machine_id;
static thread_local bool saved_machine_id_valid = false;
_cleanup_close_ int fd = -1;
char buf[33];
ssize_t k;
unsigned j;
sd_id128_t t;
assert_return(ret, -EINVAL);
if (saved_machine_id_valid) {
*ret = saved_machine_id;
return 0;
}
fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return -errno;
k = loop_read(fd, buf, 33, false);
if (k < 0)
return (int) k;
if (k != 33)
return -EIO;
if (buf[32] !='\n')
return -EIO;
for (j = 0; j < 16; j++) {
int a, b;
a = unhexchar(buf[j*2]);
b = unhexchar(buf[j*2+1]);
if (a < 0 || b < 0)
return -EIO;
t.bytes[j] = a << 4 | b;
}
saved_machine_id = t;
saved_machine_id_valid = true;
*ret = t;
return 0;
}
#if 0 /* NM_IGNORED */
_public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local sd_id128_t saved_boot_id;
static thread_local bool saved_boot_id_valid = false;
_cleanup_close_ int fd = -1;
char buf[36];
ssize_t k;
unsigned j;
sd_id128_t t;
char *p;
assert_return(ret, -EINVAL);
if (saved_boot_id_valid) {
*ret = saved_boot_id;
return 0;
}
fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return -errno;
k = loop_read(fd, buf, 36, false);
if (k < 0)
return (int) k;
if (k != 36)
return -EIO;
for (j = 0, p = buf; j < 16; j++) {
int a, b;
if (p >= buf + k - 1)
return -EIO;
if (*p == '-') {
p++;
if (p >= buf + k - 1)
return -EIO;
}
a = unhexchar(p[0]);
b = unhexchar(p[1]);
if (a < 0 || b < 0)
return -EIO;
t.bytes[j] = a << 4 | b;
p += 2;
}
saved_boot_id = t;
saved_boot_id_valid = true;
*ret = t;
return 0;
}
_public_ int sd_id128_randomize(sd_id128_t *ret) {
sd_id128_t t;
int r;
assert_return(ret, -EINVAL);
r = dev_urandom(&t, sizeof(t));
if (r < 0)
return r;
/* Turn this into a valid v4 UUID, to be nice. Note that we
* only guarantee this for newly generated UUIDs, not for
* pre-existing ones. */
*ret = make_v4_uuid(t);
return 0;
}
#endif /* NM_IGNORED */

View file

@ -510,15 +510,17 @@ static int parse_env_file_push(
va_list aq, *ap = userdata;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p = utf8_escape_invalid(key);
_cleanup_free_ char *p;
p = utf8_escape_invalid(key);
log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *p = utf8_escape_invalid(value);
_cleanup_free_ char *p;
p = utf8_escape_invalid(value);
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
return -EINVAL;
}

View file

@ -245,12 +245,25 @@ int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *re
return -EINVAL;
}
unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr);
return 32 - u32ctz(be32toh(addr->s_addr));
}
struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
assert(addr);
assert(prefixlen <= 32);
/* Shifting beyond 32 is not defined, handle this specially. */
if (prefixlen == 0)
addr->s_addr = 0;
else
addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
return addr;
}
int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) {
uint8_t msb_octet = *(uint8_t*) addr;
@ -286,9 +299,42 @@ int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask
if (r < 0)
return r;
assert(prefixlen > 0 && prefixlen < 32);
mask->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff);
in_addr_prefixlen_to_netmask(mask, prefixlen);
return 0;
}
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
assert(addr);
if (family == AF_INET) {
struct in_addr mask;
if (!in_addr_prefixlen_to_netmask(&mask, prefixlen))
return -EINVAL;
addr->in.s_addr &= mask.s_addr;
return 0;
}
if (family == AF_INET6) {
unsigned i;
for (i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
}
addr->in6.s6_addr[i] &= mask;
}
return 0;
}
return -EAFNOSUPPORT;
}

View file

@ -41,11 +41,15 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret);
unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr);
unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr);
struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
static inline size_t FAMILY_ADDRESS_SIZE(int family) {
assert(family == AF_INET || family == AF_INET6);
return family == AF_INET6 ? 16 : 4;
}
#define IN_ADDR_NULL ((union in_addr_union) {})

View file

@ -57,6 +57,14 @@
*_head = _item; \
} while(false)
/* Append an item to the list */
#define LIST_APPEND(name,head,item) \
do { \
typeof(*(head)) *_tail; \
LIST_FIND_TAIL(name,head,_tail); \
LIST_INSERT_AFTER(name,head,_tail,item); \
} while(false)
/* Remove an item from the list */
#define LIST_REMOVE(name,head,item) \
do { \
@ -132,6 +140,18 @@
#define LIST_FOREACH_AFTER(name,i,p) \
for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
/* Iterate through all the members of the list p is included in, but skip over p */
#define LIST_FOREACH_OTHERS(name,i,p) \
for (({ \
(i) = (p); \
while ((i) && (i)->name##_prev) \
(i) = (i)->name##_prev; \
if ((i) == (p)) \
(i) = (p)->name##_next; \
}); \
(i); \
(i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
/* Loop starting from p->next until p->prev.
p can be adjusted meanwhile. */
#define LIST_LOOP_BUT_ONE(name,i,head,p) \

View file

@ -0,0 +1,214 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <stdbool.h>
#include <stdarg.h>
#include <syslog.h>
#include <sys/signalfd.h>
#include <errno.h>
#include "macro.h"
#include "sd-id128.h"
typedef enum LogTarget{
LOG_TARGET_CONSOLE,
LOG_TARGET_CONSOLE_PREFIXED,
LOG_TARGET_KMSG,
LOG_TARGET_JOURNAL,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1
} LogTarget;
void log_set_target(LogTarget target);
void log_set_max_level(int level);
void log_set_facility(int facility);
int log_set_target_from_string(const char *e);
int log_set_max_level_from_string(const char *e);
void log_show_color(bool b);
bool log_get_show_color(void) _pure_;
void log_show_location(bool b);
bool log_get_show_location(void) _pure_;
int log_show_color_from_string(const char *e);
int log_show_location_from_string(const char *e);
LogTarget log_get_target(void) _pure_;
int log_get_max_level(void) _pure_;
int log_open(void);
void log_close(void);
void log_forget_fds(void);
void log_close_syslog(void);
void log_close_journal(void);
void log_close_kmsg(void);
void log_close_console(void);
void log_parse_environment(void);
#if 0 /* NM_IGNORED */
int log_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(6,7);
int log_internalv(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format,
va_list ap) _printf_(6,0);
int log_object_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *format, ...) _printf_(8,9);
int log_object_internalv(
int level,
int error,
const char*file,
int line,
const char *func,
const char *object_field,
const char *object,
const char *format,
va_list ap) _printf_(8,0);
int log_struct_internal(
int level,
int error,
const char *file,
int line,
const char *func,
const char *format, ...) _printf_(6,0) _sentinel_;
int log_oom_internal(
const char *file,
int line,
const char *func);
/* This modifies the buffer passed! */
int log_dump_internal(
int level,
int error,
const char *file,
int line,
const char *func,
char *buffer);
/* Logging for various assertions */
noreturn void log_assert_failed(
const char *text,
const char *file,
int line,
const char *func);
noreturn void log_assert_failed_unreachable(
const char *text,
const char *file,
int line,
const char *func);
void log_assert_failed_return(
const char *text,
const char *file,
int line,
const char *func);
/* Logging with level */
#define log_full_errno(level, error, ...) \
({ \
int _level = (level), _e = (error); \
(log_get_max_level() >= LOG_PRI(_level)) \
? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
#endif /* NM_IGNORED */
#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__)
/* Normal logging */
#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__)
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
/* Logging triggered by an errno-like error */
#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__)
#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
#else
# define log_trace(...) do {} while(0)
#endif
/* Structured logging */
#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__)
/* This modifies the buffer passed! */
#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer)
#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__)
bool log_on_console(void) _pure_;
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
/* Helpers to prepare various fields for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
#define LOG_ERRNO(error) "ERRNO=%i", abs(error)
void log_received_signal(int level, const struct signalfd_siginfo *si);
void log_set_upgrade_syslog_to_journal(bool b);

View file

@ -69,6 +69,10 @@
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
#define REENABLE_WARNING \
_Pragma("GCC diagnostic pop")
@ -97,15 +101,15 @@
#error "Wut? Pointers are neither 4 nor 8 bytes long?"
#endif
#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p))
#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p))
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p))
#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p)))
#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
static inline size_t ALIGN_TO(size_t l, size_t ali) {
return ((l + ali - 1) & ~(ali - 1));
}
#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p, ali))
#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
static inline unsigned long ALIGN_POWER2(unsigned long u) {
@ -199,6 +203,17 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
UNIQ_T(X,xq); \
})
/* [(x + y - 1) / y] suffers from an integer overflow, even though the
* computation should be possible in the given type. Therefore, we use
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
* quotient and the remainder, so both should be equally fast. */
#define DIV_ROUND_UP(_x, _y) \
__extension__ ({ \
const typeof(_x) __x = (_x); \
const typeof(_y) __y = (_y); \
(__x / __y + !!(__x % __y)); \
})
#define assert_se(expr) \
do { \
if (_unlikely_(!(expr))) \
@ -229,7 +244,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
#else
#define assert_cc(expr) \
DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \
struct CONCATENATE(_assert_struct_, __LINE__) { \
struct CONCATENATE(_assert_struct_, __COUNTER__) { \
char x[(expr) ? 0 : -1]; \
}; \
REENABLE_WARNING
@ -266,6 +281,14 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
/* The following macros add 1 when converting things, since UID 0 is a
* valid UID, while the pointer NULL is special */
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
#define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1))
#define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
#define memzero(x,l) (memset((x), 0, (l)))
#define zero(x) (memzero(&(x), sizeof(x)))
@ -362,7 +385,8 @@ do { \
/* Returns the number of chars needed to format variables of the
* specified type as a decimal string. Adds in extra space for a
* negative '-' prefix. */
* negative '-' prefix (hence works correctly on signed
* types). Includes space for the trailing NUL. */
#define DECIMAL_STR_MAX(type) \
(2+(sizeof(type) <= 1 ? 3 : \
sizeof(type) <= 2 ? 5 : \
@ -386,7 +410,21 @@ do { \
_found; \
})
#if 0 /* NM_IGNORED */
/* Return a nulstr for a standard cascade of configuration directories,
* suitable to pass to conf_files_list_nulstr or config_parse_many. */
#define CONF_DIRS_NULSTR(n) \
"/etc/" n ".d\0" \
"/run/" n ".d\0" \
"/usr/local/lib/" n ".d\0" \
"/usr/lib/" n ".d\0" \
CONF_DIR_SPLIT_USR(n)
#ifdef HAVE_SPLIT_USR
#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0"
#else
#define CONF_DIR_SPLIT_USR(n)
#endif
/* Define C11 thread_local attribute even on older gcc compiler
* version */
#ifndef thread_local
@ -394,13 +432,14 @@ do { \
* Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
* see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
*/
#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
#define thread_local _Thread_local
#else
#define thread_local __thread
#endif
#endif
#if 0 /* NM_IGNORED */
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef noreturn
@ -411,5 +450,16 @@ do { \
#endif
#endif
#include "log.h"
#define UID_INVALID ((uid_t) -1)
#define GID_INVALID ((gid_t) -1)
#define MODE_INVALID ((mode_t) -1)
#endif /* NM_IGNORED */
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
func(*p); \
} \
struct __useless_struct_to_allow_trailing_semicolon__
#include "log.h"

View file

@ -0,0 +1,694 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#if 0 /* NM_IGNORED */
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/statvfs.h>
#include "macro.h"
#include "util.h"
#include "log.h"
#include "strv.h"
#endif /* NM_IGNORED */
#include "path-util.h"
#if 0 /* NM_IGNORED */
#include "missing.h"
bool path_is_absolute(const char *p) {
return p[0] == '/';
}
bool is_path(const char *p) {
return !!strchr(p, '/');
}
int path_get_parent(const char *path, char **_r) {
const char *e, *a = NULL, *b = NULL, *p;
char *r;
bool slash = false;
assert(path);
assert(_r);
if (!*path)
return -EINVAL;
for (e = path; *e; e++) {
if (!slash && *e == '/') {
a = b;
b = e;
slash = true;
} else if (slash && *e != '/')
slash = false;
}
if (*(e-1) == '/')
p = a;
else
p = b;
if (!p)
return -EINVAL;
if (p == path)
r = strdup("/");
else
r = strndup(path, p-path);
if (!r)
return -ENOMEM;
*_r = r;
return 0;
}
char **path_split_and_make_absolute(const char *p) {
char **l;
assert(p);
l = strv_split(p, ":");
if (!l)
return NULL;
if (!path_strv_make_absolute_cwd(l)) {
strv_free(l);
return NULL;
}
return l;
}
char *path_make_absolute(const char *p, const char *prefix) {
assert(p);
/* Makes every item in the list an absolute path by prepending
* the prefix, if specified and necessary */
if (path_is_absolute(p) || !prefix)
return strdup(p);
return strjoin(prefix, "/", p, NULL);
}
char *path_make_absolute_cwd(const char *p) {
_cleanup_free_ char *cwd = NULL;
assert(p);
/* Similar to path_make_absolute(), but prefixes with the
* current working directory. */
if (path_is_absolute(p))
return strdup(p);
cwd = get_current_dir_name();
if (!cwd)
return NULL;
return strjoin(cwd, "/", p, NULL);
}
int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
char *r, *p;
unsigned n_parents;
assert(from_dir);
assert(to_path);
assert(_r);
/* Strips the common part, and adds ".." elements as necessary. */
if (!path_is_absolute(from_dir))
return -EINVAL;
if (!path_is_absolute(to_path))
return -EINVAL;
/* Skip the common part. */
for (;;) {
size_t a;
size_t b;
from_dir += strspn(from_dir, "/");
to_path += strspn(to_path, "/");
if (!*from_dir) {
if (!*to_path)
/* from_dir equals to_path. */
r = strdup(".");
else
/* from_dir is a parent directory of to_path. */
r = strdup(to_path);
if (!r)
return -ENOMEM;
path_kill_slashes(r);
*_r = r;
return 0;
}
if (!*to_path)
break;
a = strcspn(from_dir, "/");
b = strcspn(to_path, "/");
if (a != b)
break;
if (memcmp(from_dir, to_path, a) != 0)
break;
from_dir += a;
to_path += 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 (n_parents = 0;;) {
from_dir += strspn(from_dir, "/");
if (!*from_dir)
break;
from_dir += strcspn(from_dir, "/");
n_parents++;
}
r = malloc(n_parents * 3 + strlen(to_path) + 1);
if (!r)
return -ENOMEM;
for (p = r; n_parents > 0; n_parents--, p += 3)
memcpy(p, "../", 3);
strcpy(p, to_path);
path_kill_slashes(r);
*_r = r;
return 0;
}
char **path_strv_make_absolute_cwd(char **l) {
char **s;
/* Goes through every item in the string list and makes it
* absolute. This works in place and won't rollback any
* changes on failure. */
STRV_FOREACH(s, l) {
char *t;
t = path_make_absolute_cwd(*s);
if (!t)
return NULL;
free(*s);
*s = t;
}
return l;
}
char **path_strv_resolve(char **l, const char *prefix) {
char **s;
unsigned k = 0;
bool enomem = false;
if (strv_isempty(l))
return l;
/* Goes through every item in the string list and canonicalize
* the path. This works in place and won't rollback any
* changes on failure. */
STRV_FOREACH(s, l) {
char *t, *u;
_cleanup_free_ char *orig = NULL;
if (!path_is_absolute(*s)) {
free(*s);
continue;
}
if (prefix) {
orig = *s;
t = strappend(prefix, orig);
if (!t) {
enomem = true;
continue;
}
} else
t = *s;
errno = 0;
u = canonicalize_file_name(t);
if (!u) {
if (errno == ENOENT) {
if (prefix) {
u = orig;
orig = NULL;
free(t);
} else
u = t;
} else {
free(t);
if (errno == ENOMEM || errno == 0)
enomem = true;
continue;
}
} else if (prefix) {
char *x;
free(t);
x = path_startswith(u, prefix);
if (x) {
/* restore the slash if it was lost */
if (!startswith(x, "/"))
*(--x) = '/';
t = strdup(x);
free(u);
if (!t) {
enomem = true;
continue;
}
u = t;
} else {
/* canonicalized path goes outside of
* prefix, keep the original path instead */
free(u);
u = orig;
orig = NULL;
}
} else
free(t);
l[k++] = u;
}
l[k] = NULL;
if (enomem)
return NULL;
return l;
}
char **path_strv_resolve_uniq(char **l, const char *prefix) {
if (strv_isempty(l))
return l;
if (!path_strv_resolve(l, prefix))
return NULL;
return strv_uniq(l);
}
#endif /* NM_IGNORED */
char *path_kill_slashes(char *path) {
char *f, *t;
bool slash = false;
/* Removes redundant inner and trailing slashes. Modifies the
* passed string in-place.
*
* ///foo///bar/ becomes /foo/bar
*/
for (f = path, t = path; *f; f++) {
if (*f == '/') {
slash = true;
continue;
}
if (slash) {
slash = false;
*(t++) = '/';
}
*(t++) = *f;
}
/* Special rule, if we are talking of the root directory, a
trailing slash is good */
if (t == path && slash)
*(t++) = '/';
*t = 0;
return path;
}
#if 0 /* NM_IGNORED */
char* path_startswith(const char *path, const char *prefix) {
assert(path);
assert(prefix);
if ((path[0] == '/') != (prefix[0] == '/'))
return NULL;
for (;;) {
size_t a, b;
path += strspn(path, "/");
prefix += strspn(prefix, "/");
if (*prefix == 0)
return (char*) path;
if (*path == 0)
return NULL;
a = strcspn(path, "/");
b = strcspn(prefix, "/");
if (a != b)
return NULL;
if (memcmp(path, prefix, a) != 0)
return NULL;
path += a;
prefix += b;
}
}
bool path_equal(const char *a, const char *b) {
assert(a);
assert(b);
if ((a[0] == '/') != (b[0] == '/'))
return false;
for (;;) {
size_t j, k;
a += strspn(a, "/");
b += strspn(b, "/");
if (*a == 0 && *b == 0)
return true;
if (*a == 0 || *b == 0)
return false;
j = strcspn(a, "/");
k = strcspn(b, "/");
if (j != k)
return false;
if (memcmp(a, b, j) != 0)
return false;
a += j;
b += k;
}
}
bool path_equal_or_files_same(const char *a, const char *b) {
return path_equal(a, b) || files_same(a, b) > 0;
}
char* path_join(const char *root, const char *path, const char *rest) {
assert(path);
if (!isempty(root))
return strjoin(root, endswith(root, "/") ? "" : "/",
path[0] == '/' ? path+1 : path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
rest && rest[0] == '/' ? rest+1 : rest,
NULL);
else
return strjoin(path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
rest && rest[0] == '/' ? rest+1 : rest,
NULL);
}
int path_is_mount_point(const char *t, bool allow_symlink) {
union file_handle_union h = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
_cleanup_free_ char *parent = NULL;
struct stat a, b;
int r;
bool nosupp = false;
/* We are not actually interested in the file handles, but
* name_to_handle_at() also passes us the mount ID, hence use
* it but throw the handle away */
if (path_equal(t, "/"))
return 1;
r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
if (r < 0) {
if (errno == ENOSYS)
/* This kernel does not support name_to_handle_at()
* fall back to the traditional stat() logic. */
goto fallback;
else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
* name_to_handle_at(), hence fallback to the
* traditional stat() logic */
nosupp = true;
else if (errno == ENOENT)
return 0;
else
return -errno;
}
r = path_get_parent(t, &parent);
if (r < 0)
return r;
h.handle.handle_bytes = MAX_HANDLE_SZ;
r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
if (r < 0)
if (errno == EOPNOTSUPP)
if (nosupp)
/* Neither parent nor child do name_to_handle_at()?
We have no choice but to fall back. */
goto fallback;
else
/* The parent can't do name_to_handle_at() but
* the directory we are interested in can?
* Or the other way around?
* If so, it must be a mount point. */
return 1;
else
return -errno;
else
return mount_id != mount_id_parent;
fallback:
if (allow_symlink)
r = stat(t, &a);
else
r = lstat(t, &a);
if (r < 0) {
if (errno == ENOENT)
return 0;
return -errno;
}
free(parent);
parent = NULL;
r = path_get_parent(t, &parent);
if (r < 0)
return r;
r = stat(parent, &b);
if (r < 0)
return -errno;
return a.st_dev != b.st_dev;
}
int path_is_read_only_fs(const char *path) {
struct statvfs st;
assert(path);
if (statvfs(path, &st) < 0)
return -errno;
if (st.f_flag & ST_RDONLY)
return true;
/* On NFS, statvfs() might not reflect whether we can actually
* write to the remote share. Let's try again with
* access(W_OK) which is more reliable, at least sometimes. */
if (access(path, W_OK) < 0 && errno == EROFS)
return true;
return false;
}
int path_is_os_tree(const char *path) {
char *p;
int r;
/* We use /usr/lib/os-release as flag file if something is an OS */
p = strjoina(path, "/usr/lib/os-release");
r = access(p, F_OK);
if (r >= 0)
return 1;
/* Also check for the old location in /etc, just in case. */
p = strjoina(path, "/etc/os-release");
r = access(p, F_OK);
return r >= 0;
}
int find_binary(const char *name, bool local, char **filename) {
assert(name);
if (is_path(name)) {
if (local && access(name, X_OK) < 0)
return -errno;
if (filename) {
char *p;
p = path_make_absolute_cwd(name);
if (!p)
return -ENOMEM;
*filename = p;
}
return 0;
} else {
const char *path;
const char *word, *state;
size_t l;
/**
* Plain getenv, not secure_getenv, because we want
* to actually allow the user to pick the binary.
*/
path = getenv("PATH");
if (!path)
path = DEFAULT_PATH;
FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
_cleanup_free_ char *p = NULL;
if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
return -ENOMEM;
if (access(p, X_OK) < 0)
continue;
if (filename) {
*filename = path_kill_slashes(p);
p = NULL;
}
return 0;
}
return -ENOENT;
}
}
bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
bool changed = false;
const char* const* i;
assert(timestamp);
if (paths == NULL)
return false;
STRV_FOREACH(i, paths) {
struct stat stats;
usec_t u;
if (stat(*i, &stats) < 0)
continue;
u = timespec_load(&stats.st_mtim);
/* first check */
if (*timestamp >= u)
continue;
log_debug("timestamp of '%s' changed", *i);
/* update timestamp */
if (update) {
*timestamp = u;
changed = true;
} else
return true;
}
return changed;
}
int fsck_exists(const char *fstype) {
_cleanup_free_ char *p = NULL, *d = NULL;
const char *checker;
int r;
checker = strjoina("fsck.", fstype);
r = find_binary(checker, true, &p);
if (r < 0)
return r;
/* An fsck that is linked to /bin/true is a non-existent
* fsck */
r = readlink_malloc(p, &d);
if (r >= 0 &&
(path_equal(d, "/bin/true") ||
path_equal(d, "/usr/bin/true") ||
path_equal(d, "/dev/null")))
return -ENOENT;
return 0;
}
#endif /* NM_IGNORED */

View file

@ -0,0 +1,75 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "nm-sd-adapt.h"
#include <stdbool.h>
#include "macro.h"
#include "time-util.h"
#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin"
#ifdef HAVE_SPLIT_USR
# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
#else
# define DEFAULT_PATH DEFAULT_PATH_NORMAL
#endif
bool is_path(const char *p) _pure_;
char** path_split_and_make_absolute(const char *p);
int path_get_parent(const char *path, char **parent);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
char* path_make_absolute_cwd(const char *p);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_kill_slashes(char *path);
char* path_startswith(const char *path, const char *prefix) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b);
char* path_join(const char *root, const char *path, const char *rest);
char** path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *prefix);
char** path_strv_resolve_uniq(char **l, const char *prefix);
int path_is_mount_point(const char *path, bool allow_symlink);
int path_is_read_only_fs(const char *path);
int path_is_os_tree(const char *path);
int find_binary(const char *name, bool local, char **filename);
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
int fsck_exists(const char *fstype);
/* Iterates through the path prefixes of the specified path, going up
* the tree, to root. Also returns "" (and not "/"!) for the root
* directory. Excludes the specified directory itself */
#define PATH_FOREACH_PREFIX(prefix, path) \
for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))

View file

@ -27,7 +27,6 @@
#include <netinet/in.h>
#include <netinet/ether.h>
#include <sys/un.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/if_packet.h>

View file

@ -21,7 +21,6 @@
#include "nm-sd-adapt.h"
#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
@ -71,7 +70,7 @@ char *strv_find_startswith(char **l, const char *name) {
return NULL;
}
void strv_free(char **l) {
void strv_clear(char **l) {
char **k;
if (!l)
@ -80,6 +79,11 @@ void strv_free(char **l) {
for (k = l; *k; k++)
free(*k);
*l = NULL;
}
void strv_free(char **l) {
strv_clear(l);
free(l);
}
@ -392,7 +396,7 @@ int strv_push(char ***l, char *value) {
n = strv_length(*l);
/* increase and check for overflow */
/* Increase and check for overflow */
m = n + 2;
if (m < n)
return -ENOMEM;
@ -408,6 +412,34 @@ int strv_push(char ***l, char *value) {
return 0;
}
int strv_push_pair(char ***l, char *a, char *b) {
char **c;
unsigned n, m;
if (!a && !b)
return 0;
n = strv_length(*l);
/* increase and check for overflow */
m = n + !!a + !!b + 1;
if (m < n)
return -ENOMEM;
c = realloc_multiply(*l, sizeof(char*), m);
if (!c)
return -ENOMEM;
if (a)
c[n++] = a;
if (b)
c[n++] = b;
c[n] = NULL;
*l = c;
return 0;
}
int strv_push_prepend(char ***l, char *value) {
char **c;
unsigned n, m, i;
@ -448,6 +480,18 @@ int strv_consume(char ***l, char *value) {
return r;
}
int strv_consume_pair(char ***l, char *a, char *b) {
int r;
r = strv_push_pair(l, a, b);
if (r < 0) {
free(a);
free(b);
}
return r;
}
int strv_consume_prepend(char ***l, char *value) {
int r;
@ -483,6 +527,16 @@ char **strv_uniq(char **l) {
return l;
}
bool strv_is_uniq(char **l) {
char **i;
STRV_FOREACH(i, l)
if (strv_find(i+1, *i))
return false;
return true;
}
char **strv_remove(char **l, const char *s) {
char **f, **t;
@ -591,6 +645,17 @@ char **strv_sort(char **l) {
return l;
}
bool strv_equal(char **a, char **b) {
if (!a || !b)
return a == b;
for ( ; *a || *b; ++a, ++b)
if (!streq_ptr(*a, *b))
return false;
return true;
}
void strv_print(char **l) {
char **s;
@ -612,3 +677,31 @@ int strv_extendf(char ***l, const char *format, ...) {
return strv_consume(l, x);
}
char **strv_reverse(char **l) {
unsigned n, i;
n = strv_length(l);
if (n <= 1)
return l;
for (i = 0; i < n / 2; i++) {
char *t;
t = l[i];
l[i] = l[n-1-i];
l[n-1-i] = t;
}
return l;
}
bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
char* const* p;
STRV_FOREACH(p, patterns)
if (fnmatch(*p, s, 0) == 0)
return true;
return false;
}

View file

@ -25,6 +25,7 @@
#include <stdarg.h>
#include <stdbool.h>
#include <fnmatch.h>
#include "util.h"
@ -36,6 +37,8 @@ void strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
void strv_clear(char **l);
char **strv_copy(char * const *l);
unsigned strv_length(char * const *l) _pure_;
@ -44,12 +47,17 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
int strv_extend(char ***l, const char *value);
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
int strv_push(char ***l, char *value);
int strv_push_pair(char ***l, char *a, char *b);
int strv_push_prepend(char ***l, char *value);
int strv_consume(char ***l, char *value);
int strv_consume_pair(char ***l, char *a, char *b);
int strv_consume_prepend(char ***l, char *value);
char **strv_remove(char **l, const char *s);
char **strv_uniq(char **l);
bool strv_is_uniq(char **l);
bool strv_equal(char **a, char **b);
#define strv_contains(l, s) (!!strv_find((l), (s)))
@ -137,3 +145,13 @@ void strv_print(char **l);
_l ++; \
_l[0]; \
}))
char **strv_reverse(char **l);
bool strv_fnmatch(char* const* patterns, const char *s, int flags);
static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) {
assert(s);
return strv_isempty(patterns) ||
strv_fnmatch(patterns, s, flags);
}

View file

@ -507,8 +507,9 @@ int parse_timestamp(const char *t, usec_t *usec) {
return parse_sec(t + 1, usec);
else if (endswith(t, " ago")) {
_cleanup_free_ char *z = strndup(t, strlen(t) - 4);
_cleanup_free_ char *z;
z = strndup(t, strlen(t) - 4);
if (!z)
return -ENOMEM;
@ -518,8 +519,9 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto finish;
} else if (endswith(t, " left")) {
_cleanup_free_ char *z = strndup(t, strlen(t) - 4);
_cleanup_free_ char *z;
z = strndup(t, strlen(t) - 4);
if (!z)
return -ENOMEM;
@ -791,7 +793,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
s = startswith(p, "infinity");
if (s) {
s += strspn(s, WHITESPACE);
if (!*s != 0)
if (*s != 0)
return -EINVAL;
*nsec = NSEC_INFINITY;
@ -970,7 +972,7 @@ bool timezone_is_valid(const char *name) {
if (slash)
return false;
t = strappenda("/usr/share/zoneinfo/", name);
t = strjoina("/usr/share/zoneinfo/", name);
if (stat(t, &st) < 0)
return false;
@ -998,4 +1000,3 @@ clockid_t clock_boottime_or_monotonic(void) {
return clock;
}

View file

@ -69,7 +69,7 @@ typedef struct dual_timestamp {
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0, 0 })
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL })
usec_t now(clockid_t clock);
@ -109,3 +109,5 @@ int get_timezones(char ***l);
bool timezone_is_valid(const char *name);
clockid_t clock_boottime_or_monotonic(void);
#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0)

View file

@ -144,19 +144,19 @@ int utf8_encoded_to_unichar(const char *str) {
}
bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
const uint8_t *p;
const char *p;
assert(str);
for (p = (const uint8_t*) str; length;) {
for (p = str; length;) {
int encoded_len, val;
encoded_len = utf8_encoded_valid_unichar((const char *) p);
encoded_len = utf8_encoded_valid_unichar(p);
if (encoded_len < 0 ||
(size_t) encoded_len > length)
return false;
val = utf8_encoded_to_unichar((const char*) p);
val = utf8_encoded_to_unichar(p);
if (val < 0 ||
is_unicode_control(val) ||
(!newline && val == '\n'))
@ -204,7 +204,46 @@ char *utf8_escape_invalid(const char *str) {
s = mempcpy(s, str, len);
str += len;
} else {
s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
str += 1;
}
}
*s = '\0';
return p;
}
char *utf8_escape_non_printable(const char *str) {
char *p, *s;
assert(str);
p = s = malloc(strlen(str) * 4 + 1);
if (!p)
return NULL;
while (*str) {
int len;
len = utf8_encoded_valid_unichar(str);
if (len > 0) {
if (utf8_is_printable(str, len)) {
s = mempcpy(s, str, len);
str += len;
} else {
while (len > 0) {
*(s++) = '\\';
*(s++) = 'x';
*(s++) = hexchar((int) *str >> 4);
*(s++) = hexchar((int) *str);
str += 1;
len --;
}
}
} else {
s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
str += 1;
}
}
@ -226,39 +265,91 @@ char *ascii_is_valid(const char *str) {
return (char*) str;
}
char *utf16_to_utf8(const void *s, size_t length) {
char *r;
const uint8_t *f;
uint8_t *t;
/**
* utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
* @out_utf8: output buffer of at least 4 bytes or NULL
* @g: UCS-4 character to encode
*
* This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
* The length of the character is returned. It is not zero-terminated! If the
* output buffer is NULL, only the length is returned.
*
* Returns: The length in bytes that the UTF-8 representation does or would
* occupy.
*/
size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
if (g < (1 << 7)) {
if (out_utf8)
out_utf8[0] = g & 0x7f;
return 1;
} else if (g < (1 << 11)) {
if (out_utf8) {
out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
out_utf8[1] = 0x80 | (g & 0x3f);
}
return 2;
} else if (g < (1 << 16)) {
if (out_utf8) {
out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
out_utf8[2] = 0x80 | (g & 0x3f);
}
return 3;
} else if (g < (1 << 21)) {
if (out_utf8) {
out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
out_utf8[3] = 0x80 | (g & 0x3f);
}
return 4;
} else {
return 0;
}
}
r = new(char, (length*3+1)/2 + 1);
char *utf16_to_utf8(const void *s, size_t length) {
const uint8_t *f;
char *r, *t;
r = new(char, (length * 4 + 1) / 2 + 1);
if (!r)
return NULL;
t = (uint8_t*) r;
f = s;
t = r;
for (f = s; f < (const uint8_t*) s + length; f += 2) {
uint16_t c;
while (f < (const uint8_t*) s + length) {
uint16_t w1, w2;
c = (f[1] << 8) | f[0];
/* see RFC 2781 section 2.2 */
if (c == 0) {
*t = 0;
return r;
} else if (c < 0x80) {
*(t++) = (uint8_t) c;
} else if (c < 0x800) {
*(t++) = (uint8_t) (0xc0 | (c >> 6));
*(t++) = (uint8_t) (0x80 | (c & 0x3f));
} else {
*(t++) = (uint8_t) (0xe0 | (c >> 12));
*(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
*(t++) = (uint8_t) (0x80 | (c & 0x3f));
w1 = f[1] << 8 | f[0];
f += 2;
if (!utf16_is_surrogate(w1)) {
t += utf8_encode_unichar(t, w1);
continue;
}
if (utf16_is_trailing_surrogate(w1))
continue;
else if (f >= (const uint8_t*) s + length)
break;
w2 = f[1] << 8 | f[0];
f += 2;
if (!utf16_is_trailing_surrogate(w2)) {
f -= 2;
continue;
}
t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
}
*t = 0;
return r;
}

View file

@ -31,14 +31,27 @@
const char *utf8_is_valid(const char *s) _pure_;
char *ascii_is_valid(const char *s) _pure_;
char *utf8_escape_invalid(const char *s);
bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
_pure_ static inline bool utf8_is_printable(const char* str, size_t length) {
return utf8_is_printable_newline(str, length, true);
}
#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(const char *str);
size_t utf8_encode_unichar(char *out_utf8, uint32_t g);
char *utf16_to_utf8(const void *s, size_t length);
int utf8_encoded_valid_unichar(const char *str);
int utf8_encoded_to_unichar(const char *str);
static inline bool utf16_is_surrogate(uint16_t c) {
return (0xd800 <= c && c <= 0xdfff);
}
static inline bool utf16_is_trailing_surrogate(uint16_t c) {
return (0xdc00 <= c && c <= 0xdfff);
}
static inline uint32_t utf16_surrogate_pair_to_unichar(uint16_t lead, uint16_t trail) {
return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000;
}

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,6 @@
#include <fcntl.h>
#include <inttypes.h>
#include <time.h>
#include <sys/time.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
@ -36,23 +35,24 @@
#include <sched.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/resource.h>
#include <stddef.h>
#include <unistd.h>
#include <locale.h>
#include <mntent.h>
#include <sys/socket.h>
#include <sys/inotify.h>
#if 0 /* NM_IGNORED */
#if SIZEOF_PID_T == 4
# define PID_FMT "%" PRIu32
# define PID_PRI PRIi32
#elif SIZEOF_PID_T == 2
# define PID_FMT "%" PRIu16
# define PID_PRI PRIi16
#else
# error Unknown pid_t size
#endif
#define PID_FMT "%" PID_PRI
#if SIZEOF_UID_T == 4
# define UID_FMT "%" PRIu32
@ -71,7 +71,7 @@
#endif
#if SIZEOF_TIME_T == 8
# define PRI_TIME PRIu64
# define PRI_TIME PRIi64
#elif SIZEOF_TIME_T == 4
# define PRI_TIME PRIu32
#else
@ -118,7 +118,7 @@
#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
size_t page_size(void);
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
#define streq(a,b) (strcmp((a),(b)) == 0)
@ -148,6 +148,10 @@ static inline const char* true_false(bool b) {
return b ? "true" : "false";
}
static inline const char* one_zero(bool b) {
return b ? "1" : "0";
}
static inline const char* strempty(const char *s) {
return s ? s : "";
}
@ -269,7 +273,6 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
int get_starttime_of_pid(pid_t pid, unsigned long long *st);
char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length);
@ -301,6 +304,9 @@ int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
int get_process_capeff(pid_t pid, char **capeff);
int get_process_cwd(pid_t pid, char **cwd);
int get_process_root(pid_t pid, char **root);
int get_process_environ(pid_t pid, char **environ);
char hexchar(int x) _const_;
int unhexchar(char c) _const_;
@ -321,7 +327,7 @@ char *ascii_strlower(char *path);
bool dirent_is_file(const struct dirent *de) _pure_;
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_;
bool ignore_file(const char *filename) _pure_;
bool hidden_file(const char *filename) _pure_;
bool chars_intersect(const char *a, const char *b) _pure_;
@ -346,26 +352,29 @@ static inline uint32_t random_u32(void) {
}
/* For basic lookup tables with strictly enumerated entries */
#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
scope const char *name##_to_string(type i) { \
if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
return NULL; \
return name##_table[i]; \
} \
scope type name##_from_string(const char *s) { \
type i; \
if (!s) \
return (type) -1; \
for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
if (name##_table[i] && \
streq(name##_table[i], s)) \
return i; \
return (type) -1; \
} \
}
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key);
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
scope inline type name##_from_string(const char *s) { \
return (type)string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
}
#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \
struct __useless_struct_to_allow_trailing_semicolon__
#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static)
#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
/* For string conversions where numbers are also acceptable */
#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
@ -379,7 +388,7 @@ static inline uint32_t random_u32(void) {
if (!s) \
return log_oom(); \
} else { \
r = asprintf(&s, "%u", i); \
r = asprintf(&s, "%i", i); \
if (r < 0) \
return log_oom(); \
} \
@ -429,7 +438,7 @@ int sigaction_many(const struct sigaction *sa, ...);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
bool is_device_path(const char *path);
@ -457,6 +466,8 @@ int get_ctty(pid_t, dev_t *_devnr, char **r);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
int is_fd_on_temporary_fs(int fd);
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
@ -469,6 +480,8 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus);
int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf))
int fd_columns(int fd);
unsigned columns(void);
int fd_lines(int fd);
@ -516,7 +529,7 @@ char *unquote(const char *s, const char *quotes);
char *normalize_env_assignment(const char *s);
int wait_for_terminate(pid_t pid, siginfo_t *status);
int wait_for_terminate_and_warn(const char *name, pid_t pid);
int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
noreturn void freeze(void);
@ -535,7 +548,7 @@ bool tty_is_console(const char *tty) _pure_;
int vtnr_from_tty(const char *tty);
const char *default_term_for_tty(const char *tty);
void execute_directory(const char *directory, DIR *_d, usec_t timeout, char *argv[]);
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
int kill_and_sigcont(pid_t pid, int sig);
@ -648,7 +661,10 @@ int setrlimit_closest(int resource, const struct rlimit *rlim);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
bool is_valid_documentation_url(const char *url) _pure_;
bool http_url_is_valid(const char *url) _pure_;
bool documentation_url_is_valid(const char *url) _pure_;
bool http_etag_is_valid(const char *etag);
bool in_initrd(void);
@ -661,13 +677,6 @@ static inline void freep(void *p) {
free(*(void**) p);
}
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
func(*p); \
} \
struct __useless_struct_to_allow_trailing_semicolon__
static inline void closep(int *fd) {
safe_close(*fd);
}
@ -716,7 +725,7 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_
return memdup(p, a * b);
}
bool filename_is_safe(const char *p) _pure_;
bool filename_is_valid(const char *p) _pure_;
bool path_is_safe(const char *p) _pure_;
bool string_is_safe(const char *p) _pure_;
bool string_has_cc(const char *p, const char *ok) _pure_;
@ -732,6 +741,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *, void *),
void *arg);
#define _(String) gettext (String)
void init_gettext(void);
bool is_locale_utf8(void);
typedef enum DrawSpecialChar {
@ -773,10 +784,19 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
on_error; \
} \
break; \
} else if (ignore_file((de)->d_name)) \
} else if (hidden_file((de)->d_name)) \
continue; \
else
#define FOREACH_DIRENT_ALL(de, d, on_error) \
for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
if (!de) { \
if (errno > 0) { \
on_error; \
} \
break; \
} else
static inline void *mempset(void *s, int c, size_t n) {
memset(s, c, n);
return (uint8_t*)s + n;
@ -841,7 +861,7 @@ static inline unsigned u32ctz(uint32_t n) {
#endif
}
static inline int log2i(int x) {
static inline unsigned log2i(int x) {
assert(x > 0);
return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1;
@ -885,6 +905,7 @@ int unlink_noerrno(const char *path);
(void *) memset(_new_, 0, _len_); \
})
/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
#define alloca_align(size, align) \
({ \
void *_ptr_; \
@ -901,19 +922,19 @@ int unlink_noerrno(const char *path);
(void*)memset(_new_, 0, _size_); \
})
#define strappenda(a, ...) \
({ \
int _len = strlen(a); \
unsigned _i; \
char *_d_, *_p_; \
const char *_appendees_[] = { __VA_ARGS__ }; \
for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \
_len += strlen(_appendees_[_i]); \
_d_ = alloca(_len + 1); \
_p_ = stpcpy(_d_, a); \
for (_i = 0; _i < ELEMENTSOF(_appendees_); _i++) \
_p_ = stpcpy(_p_, _appendees_[_i]); \
_d_; \
#define strjoina(a, ...) \
({ \
const char *_appendees_[] = { a, __VA_ARGS__ }; \
char *_d_, *_p_; \
int _len_ = 0; \
unsigned _i_; \
for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
_len_ += strlen(_appendees_[_i_]); \
_p_ = _d_ = alloca(_len_ + 1); \
for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
_p_ = stpcpy(_p_, _appendees_[_i_]); \
*_p_ = 0; \
_d_; \
})
#define procfs_file_alloca(pid, field) \
@ -929,32 +950,6 @@ int unlink_noerrno(const char *path);
_r_; \
})
struct _locale_struct_ {
locale_t saved_locale;
locale_t new_locale;
bool quit;
};
static inline void _reset_locale_(struct _locale_struct_ *s) {
PROTECT_ERRNO;
if (s->saved_locale != (locale_t) 0)
uselocale(s->saved_locale);
if (s->new_locale != (locale_t) 0)
freelocale(s->new_locale);
}
#define RUN_WITH_LOCALE(mask, loc) \
for (_cleanup_(_reset_locale_) struct _locale_struct_ _saved_locale_ = { (locale_t) 0, (locale_t) 0, false }; \
({ \
if (!_saved_locale_.quit) { \
PROTECT_ERRNO; \
_saved_locale_.new_locale = newlocale((mask), (loc), (locale_t) 0); \
if (_saved_locale_.new_locale != (locale_t) 0) \
_saved_locale_.saved_locale = uselocale(_saved_locale_.new_locale); \
} \
!_saved_locale_.quit; }) ; \
_saved_locale_.quit = true)
bool id128_is_valid(const char *s) _pure_;
int split_pair(const char *s, const char *sep, char **l, char **r);
@ -975,6 +970,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size,
int proc_cmdline(char **ret);
int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
int get_proc_cmdline_key(const char *parameter, char **value);
int container_get_leader(const char *machine, pid_t *pid);
@ -1001,8 +997,6 @@ const char *personality_to_string(unsigned long);
uint64_t physical_memory(void);
char* mount_test_option(const char *haystack, const char *needle);
void hexdump(FILE *f, const void *p, size_t s);
#if 0 /* NM_IGNORED */
@ -1010,6 +1004,7 @@ union file_handle_union {
struct file_handle handle;
char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ];
};
#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ }
#endif /* NM_IGNORED */
int update_reboot_param_file(const char *param);
@ -1020,8 +1015,9 @@ int bind_remount_recursive(const char *prefix, bool ro);
int fflush_and_check(FILE *f);
char *tempfn_xxxxxx(const char *p);
char *tempfn_random(const char *p);
int tempfn_xxxxxx(const char *p, char **ret);
int tempfn_random(const char *p, char **ret);
int tempfn_random_child(const char *p, char **ret);
bool is_localhost(const char *hostname);
@ -1036,3 +1032,61 @@ int unquote_many_words(const char **p, ...) _sentinel_;
int free_and_strdup(char **p, const char *s);
int sethostname_idempotent(const char *s);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
for ((e) = &buffer.ev; \
(uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
(e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
int ptsname_malloc(int fd, char **ret);
int openpt_in_namespace(pid_t pid, int flags);
ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags);
int fd_setcrtime(int fd, usec_t usec);
int fd_getcrtime(int fd, usec_t *usec);
int path_getcrtime(const char *p, usec_t *usec);
int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags);
int same_fd(int a, int b);
int chattr_fd(int fd, bool b, unsigned mask);
int chattr_path(const char *p, bool b, unsigned mask);
int read_attr_fd(int fd, unsigned *ret);
int read_attr_path(const char *p, unsigned *ret);
typedef struct LockFile {
char *path;
int fd;
int operation;
} LockFile;
int make_lock_file(const char *p, int operation, LockFile *ret);
int make_lock_file_for(const char *p, int operation, LockFile *ret);
void release_lock_file(LockFile *f);
#define _cleanup_release_lock_file_ _cleanup_(release_lock_file)
#define LOCK_FILE_INIT { .fd = -1, .path = NULL }
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
void sigkill_wait(pid_t *pid);
#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
int syslog_parse_priority(const char **p, int *priority, bool with_facility);
void cmsg_close_all(struct msghdr *mh);

View file

@ -35,6 +35,7 @@ enum {
DHCP6_EVENT_RESEND_EXPIRE = 10,
DHCP6_EVENT_RETRANS_MAX = 11,
DHCP6_EVENT_IP_ACQUIRE = 12,
DHCP6_EVENT_INFORMATION_REQUEST = 13,
};
typedef struct sd_dhcp6_client sd_dhcp6_client;
@ -49,7 +50,10 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
size_t addr_len, uint16_t arp_type);
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
size_t duid_len);
int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname);
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
bool enabled);
int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
bool *enabled);
int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
uint16_t option);

View file

@ -29,14 +29,11 @@
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
int sd_dhcp6_lease_get_first_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
int sd_dhcp6_lease_get_next_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);

View file

@ -62,7 +62,7 @@ int sd_id128_get_boot(sd_id128_t *ret);
/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16
* times. It is hence not a good idea to call this macro with an
* expensive function as paramater or an expression with side
* expensive function as parameter or an expression with side
* effects */
#define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
@ -108,6 +108,10 @@ _sd_pure_ static inline int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
return memcmp(&a, &b, 16) == 0;
}
_sd_pure_ static inline int sd_id128_is_null(sd_id128_t a) {
return a.qwords[0] == 0 && a.qwords[1] == 0;
}
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
_SD_END_DECLARATIONS;