diff options
| author | Michael Biebl <biebl@debian.org> | 2018-06-17 14:54:10 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2018-06-17 14:54:10 +0200 |
| commit | 069cb5c3a525ebcc19cc2927964258acaca87b13 (patch) | |
| tree | 66ac6b21a44d630c0fc281c0df327239d491226a /src | |
| parent | 04bc9e1cd3544445d883ad29ea108c1645c8e7b7 (diff) | |
New upstream version 1.11.90 upstream/1.11.90
Diffstat (limited to 'src')
155 files changed, 2851 insertions, 1138 deletions
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 1d237d92..92ad0b31 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -903,11 +903,8 @@ act_stage3_ip6_config_start (NMDevice *device, { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device); - if (priv->bt_type == NM_BT_CAPABILITY_DUN) { - return nm_modem_stage3_ip6_config_start (priv->modem, - nm_device_get_act_request (device), - out_failure_reason); - } + if (priv->bt_type == NM_BT_CAPABILITY_DUN) + return nm_modem_stage3_ip6_config_start (priv->modem, device, out_failure_reason); return NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason); } diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 6c5e33e4..87d16dbe 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -753,13 +753,6 @@ supplicant_interface_init (NMDeviceEthernet *self) return TRUE; } -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (link_duplex_to_string, NMPlatformLinkDuplexType, - NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_LINK_DUPLEX_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_LINK_DUPLEX_FULL, "full"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_LINK_DUPLEX_HALF, "half"), -); - static NMPlatformLinkDuplexType link_duplex_to_platform (const char *duplex) { @@ -787,13 +780,11 @@ link_negotiation_set (NMDevice *device) s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); if (s_wired) { autoneg = nm_setting_wired_get_auto_negotiate (s_wired); - if (!autoneg) { - speed = nm_setting_wired_get_speed (s_wired); - duplex = link_duplex_to_platform (nm_setting_wired_get_duplex (s_wired)); - if (!speed && !duplex) { - _LOGD (LOGD_DEVICE, "set-link: ignore link negotiation"); - return; - } + speed = nm_setting_wired_get_speed (s_wired); + duplex = link_duplex_to_platform (nm_setting_wired_get_duplex (s_wired)); + if (!autoneg && !speed && !duplex) { + _LOGD (LOGD_DEVICE, "set-link: ignore link negotiation"); + return; } } @@ -804,20 +795,23 @@ link_negotiation_set (NMDevice *device) } /* If link negotiation setting are already in place do nothing and return with success */ - if ( (!!autoneg == !!link_autoneg) - && (!speed || (speed == link_speed)) - && (!duplex || (duplex == link_duplex))) { + if ( !!autoneg == !!link_autoneg + && speed == link_speed + && duplex == link_duplex) { _LOGD (LOGD_DEVICE, "set-link: link negotiation is already configured"); return; } - if (autoneg) - _LOGD (LOGD_DEVICE, "set-link: configure autonegotiation"); + if (autoneg && !speed && !duplex) + _LOGD (LOGD_DEVICE, "set-link: configure auto-negotiation"); else { - _LOGD (LOGD_DEVICE, "set-link: configure static negotiation (%u Mbit%s - %s duplex%s)", + _LOGD (LOGD_DEVICE, "set-link: configure %snegotiation (%u Mbit%s - %s duplex%s)", + autoneg ? "auto-" : "static ", speed ?: link_speed, speed ? "" : "*", - duplex ? link_duplex_to_string (duplex) : link_duplex_to_string (link_duplex), + duplex + ? nm_platform_link_duplex_type_to_string (duplex) + : nm_platform_link_duplex_type_to_string (link_duplex), duplex ? "" : "*"); } diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index 4eaf5469..166bfb57 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -704,6 +704,7 @@ create_and_realize (NMDevice *device, sci.s.port = htons (nm_setting_macsec_get_port (s_macsec)); lnk.sci = be64toh (sci.u); lnk.validation = nm_setting_macsec_get_validation (s_macsec); + lnk.include_sci = nm_setting_macsec_get_send_sci (s_macsec); parent_ifindex = nm_device_get_ifindex (parent); g_warn_if_fail (parent_ifindex > 0); diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 0b844411..c76debff 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -134,7 +134,7 @@ void nm_device_commit_mtu (NMDevice *self); } gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, - GError **error, const char **argv); + GError **error, const char **whitelist); #define nm_device_hash_check_invalid_keys(hash, setting_name, error, ...) \ _nm_device_hash_check_invalid_keys (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL })) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 9a40a0dd..9df41410 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -37,8 +37,11 @@ #include <linux/if_arp.h> #include <linux/rtnetlink.h> #include <linux/pkt_sched.h> +#include <uuid/uuid.h> #include "nm-utils/nm-dedup-multi.h" +#include "nm-utils/nm-random-utils.h" +#include "nm-utils/unaligned.h" #include "nm-common-macros.h" #include "nm-device-private.h" @@ -6830,11 +6833,12 @@ dhcp4_grace_period_expired (gpointer user_data) } static void -dhcp4_fail (NMDevice *self, gboolean timeout) +dhcp4_fail (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DHCP4, "DHCPv4 failed%s", timeout ? " (timeout)" : ""); + _LOGD (LOGD_DHCP4, "DHCPv4 failed (ip_state %s)", + _ip_state_to_string (priv->ip4_state)); /* Keep client running if there are static addresses configured * on the interface. @@ -6848,7 +6852,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout) * configuration. */ if ( !priv->dhcp4.was_active - && (timeout || priv->ip4_state == IP_CONF)) { + && priv->ip4_state == IP_CONF) { dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); nm_device_activate_schedule_ip4_config_timeout (self); return; @@ -6911,7 +6915,7 @@ dhcp4_state_changed (NMDhcpClient *client, case NM_DHCP_STATE_BOUND: if (!ip4_config) { _LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event."); - dhcp4_fail (self, FALSE); + dhcp4_fail (self); break; } @@ -6950,11 +6954,11 @@ dhcp4_state_changed (NMDhcpClient *client, if (dhcp4_lease_change (self, ip4_config)) nm_device_update_metered (self); else - dhcp4_fail (self, FALSE); + dhcp4_fail (self); } break; case NM_DHCP_STATE_TIMEOUT: - dhcp4_fail (self, TRUE); + dhcp4_fail (self); break; case NM_DHCP_STATE_EXPIRE: /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ @@ -6963,7 +6967,7 @@ dhcp4_state_changed (NMDhcpClient *client, /* fall through */ case NM_DHCP_STATE_DONE: case NM_DHCP_STATE_FAIL: - dhcp4_fail (self, FALSE); + dhcp4_fail (self); break; default: break; @@ -7014,13 +7018,32 @@ get_dhcp_timeout (NMDevice *self, int addr_family) } static GBytes * -dhcp4_get_client_id (NMDevice *self, NMConnection *connection) +dhcp4_get_client_id_mac (const guint8 *hwaddr /* ETH_ALEN bytes */) +{ + guint8 *client_id_buf; + guint8 hwaddr_type = ARPHRD_ETHER; + + client_id_buf = g_malloc (ETH_ALEN + 1); + client_id_buf[0] = hwaddr_type; + memcpy (&client_id_buf[1], hwaddr, ETH_ALEN); + return g_bytes_new_take (client_id_buf, ETH_ALEN + 1); +} + +static GBytes * +dhcp4_get_client_id (NMDevice *self, + NMConnection *connection, + GBytes *hwaddr) { NMSettingIPConfig *s_ip4; const char *client_id; gs_free char *client_id_default = NULL; guint8 *client_id_buf; - gboolean is_mac; + const char *fail_reason; + guint8 hwaddr_bin_buf[NM_UTILS_HWADDR_LEN_MAX]; + const guint8 *hwaddr_bin; + gsize hwaddr_len; + GBytes *result; + gs_free char *logstr1 = NULL; s_ip4 = nm_connection_get_setting_ip4_config (connection); client_id = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)); @@ -7028,42 +7051,54 @@ dhcp4_get_client_id (NMDevice *self, NMConnection *connection) if (!client_id) { client_id_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, "ipv4.dhcp-client-id", self); - if (client_id_default && client_id_default[0]) + if (client_id_default && client_id_default[0]) { + /* a non-empty client-id is always valid, see nm_dhcp_utils_client_id_string_to_bytes(). */ client_id = client_id_default; + } } - if (!client_id) + if (!client_id) { + _LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, + "ipv4.dhcp-client-id: no explicity client-id configured"); return NULL; + } - if ( (is_mac = nm_streq (client_id, "mac")) - || nm_streq (client_id, "perm-mac")) { - const char *hwaddr; - char addr_buf[NM_UTILS_HWADDR_LEN_MAX]; - gsize addr_len; - guint8 addr_type; - - hwaddr = is_mac - ? nm_device_get_hw_address (self) - : nm_device_get_permanent_hw_address (self); - if (!hwaddr) - return NULL; + if (nm_streq (client_id, "mac")) { + if (!hwaddr) { + fail_reason = "failed to get current MAC address"; + goto out_fail; + } + + hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); + if (hwaddr_len != ETH_ALEN) { + fail_reason = "MAC address is not ethernet"; + goto out_fail; + } + + result = dhcp4_get_client_id_mac (hwaddr_bin); + goto out_good; + } + + if (nm_streq (client_id, "perm-mac")) { + const char *hwaddr_str; + + hwaddr_str = nm_device_get_permanent_hw_address (self); + if (!hwaddr_str) { + fail_reason = "failed to get permanent MAC address"; + goto out_fail; + } - if (!_nm_utils_hwaddr_aton (hwaddr, addr_buf, sizeof (addr_buf), &addr_len)) + if (!_nm_utils_hwaddr_aton (hwaddr_str, hwaddr_bin_buf, sizeof (hwaddr_bin_buf), &hwaddr_len)) g_return_val_if_reached (NULL); - switch (addr_len) { - case ETH_ALEN: - addr_type = ARPHRD_ETHER; - break; - default: + if (hwaddr_len != ETH_ALEN) { /* unsupported type. */ - return NULL; + fail_reason = "MAC address is not ethernet"; + goto out_fail; } - client_id_buf = g_malloc (addr_len + 1); - client_id_buf[0] = addr_type; - memcpy (&client_id_buf[1], addr_buf, addr_len); - return g_bytes_new_take (client_id_buf, addr_len + 1); + result = dhcp4_get_client_id_mac (hwaddr_bin_buf); + goto out_good; } if (nm_streq (client_id, "stable")) { @@ -7099,10 +7134,30 @@ dhcp4_get_client_id (NMDevice *self, NMConnection *connection) client_id_buf = g_malloc (1 + 15); client_id_buf[0] = 0; memcpy (&client_id_buf[1], buf, 15); - return g_bytes_new_take (client_id_buf, 1 + 15); + result = g_bytes_new_take (client_id_buf, 1 + 15); + goto out_good; } - return nm_dhcp_utils_client_id_string_to_bytes (client_id); + result = nm_dhcp_utils_client_id_string_to_bytes (client_id); + goto out_good; + +out_fail: + nm_assert (fail_reason); + _LOGW (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, + "ipv4.dhcp-client-id: failure to generate client id (%s). Use random client id", + fail_reason); + client_id_buf = g_malloc (1 + 15); + client_id_buf[0] = 0; + nm_utils_random_bytes (&client_id_buf[1], 15); + result = g_bytes_new_take (client_id_buf, 1 + 15); + +out_good: + nm_assert (result); + _LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, + "ipv4.dhcp-client-id: use \"%s\" client ID: %s", + client_id, + (logstr1 = nm_dhcp_utils_duid_to_string (result))); + return result; } static NMActStageReturn @@ -7126,7 +7181,7 @@ dhcp4_start (NMDevice *self) hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)); - client_id = dhcp4_get_client_id (self, connection); + client_id = dhcp4_get_client_id (self, connection, hwaddr); g_warn_if_fail (priv->dhcp4.client == NULL); priv->dhcp4.client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), @@ -7678,12 +7733,260 @@ dhcp6_prefix_delegated (NMDhcpClient *client, g_signal_emit (self, signals[IP6_PREFIX_DELEGATED], 0, prefix); } +/* RFC 3315 defines the epoch for the DUID-LLT time field on Jan 1st 2000. */ +#define EPOCH_DATETIME_200001010000 946684800 + +static GBytes * +generate_duid_llt (const guint8 *hwaddr /* ETH_ALEN bytes */, + gint64 time) +{ + GByteArray *duid_arr; + const guint16 duid_type = htons (1); + const guint16 hw_type = htons (ARPHRD_ETHER); + const guint32 duid_time = htonl (NM_MAX (0, time - EPOCH_DATETIME_200001010000)); + + duid_arr = g_byte_array_sized_new (2 + 4 + 2 + ETH_ALEN); + + g_byte_array_append (duid_arr, (const guint8 *) &duid_type, 2); + g_byte_array_append (duid_arr, (const guint8 *) &hw_type, 2); + g_byte_array_append (duid_arr, (const guint8 *) &duid_time, 4); + g_byte_array_append (duid_arr, hwaddr, ETH_ALEN); + + return g_byte_array_free_to_bytes (duid_arr); +} + +static GBytes * +generate_duid_ll (const guint8 *hwaddr /* ETH_ALEN bytes */) +{ + GByteArray *duid_arr; + const guint16 duid_type = htons (3); + const guint16 hw_type = htons (ARPHRD_ETHER); + + duid_arr = g_byte_array_sized_new (2 + 2 + ETH_ALEN); + + g_byte_array_append (duid_arr, (const guint8 *) &duid_type, 2); + g_byte_array_append (duid_arr, (const guint8 *) &hw_type, 2); + g_byte_array_append (duid_arr, hwaddr, ETH_ALEN); + + return g_byte_array_free_to_bytes (duid_arr); +} + +static GBytes * +generate_duid_uuid (guint8 *data, gsize data_len) +{ + const guint16 duid_type = g_htons (4); + const int DUID_SIZE = 18; + guint8 *duid_buffer; + + nm_assert (data); + nm_assert (data_len >= 16); + + /* Generate a DHCP Unique Identifier for DHCPv6 using the + * DUID-UUID method (see RFC 6355 section 4). Format is: + * + * u16: type (DUID-UUID = 4) + * u8[16]: UUID bytes + */ + duid_buffer = g_malloc (DUID_SIZE); + + G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2); + memcpy (&duid_buffer[0], &duid_type, 2); + + /* UUID is 128 bits, we just take the first 128 bits + * (regardless of data size) as the DUID-UUID. + */ + memcpy (&duid_buffer[2], data, 16); + + return g_bytes_new_take (duid_buffer, DUID_SIZE); +} + +static GBytes * +generate_duid_from_machine_id (void) +{ + gs_free const char *machine_id_s = NULL; + uuid_t uuid; + GChecksum *sum; + guint8 sha256_digest[32]; + gsize len = sizeof (sha256_digest); + static GBytes *global_duid = NULL; + + if (global_duid) + return g_bytes_ref (global_duid); + + machine_id_s = nm_utils_machine_id_read (); + if (!nm_utils_machine_id_parse (machine_id_s, uuid)) + return NULL; + + /* Hash the machine ID so it's not leaked to the network */ + sum = g_checksum_new (G_CHECKSUM_SHA256); + g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid)); + g_checksum_get_digest (sum, sha256_digest, &len); + g_checksum_free (sum); + + global_duid = generate_duid_uuid (sha256_digest, len); + return g_bytes_ref (global_duid); +} + +static GBytes * +dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcpDuidEnforce *out_enforce) +{ + NMSettingIPConfig *s_ip6; + const char *duid; + gs_free char *duid_default = NULL; + const char *duid_error; + GBytes *duid_out; + guint8 sha256_digest[32]; + gsize len = sizeof (sha256_digest); + NMDhcpDuidEnforce duid_enforce = NM_DHCP_DUID_ENFORCE_ALWAYS; + gs_free char *logstr1 = NULL; + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + duid = nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6)); + + if (!duid) { + duid_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "ipv6.dhcp-duid", self); + duid = duid_default; + if (!duid) + duid = "lease"; + } + + if (nm_streq (duid, "lease")) { + duid_enforce = NM_DHCP_DUID_ENFORCE_NEVER; + duid_out = generate_duid_from_machine_id (); + if (!duid_out) { + duid_error = "failure to read machine-id"; + goto out_fail; + } + goto out_good; + } + + if (!_nm_utils_dhcp_duid_valid (duid, &duid_out)) { + duid_error = "invalid duid"; + goto out_fail; + } + + if (duid_out) + goto out_good; + + if (NM_IN_STRSET (duid, "ll", "llt")) { + if (!hwaddr) { + duid_error = "missing link-layer address"; + goto out_fail; + } + if (g_bytes_get_size (hwaddr) != ETH_ALEN) { + duid_error = "unsupported link-layer address"; + goto out_fail; + } + + if (nm_streq (duid, "ll")) { + duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL)); + } else { + gint64 time; + + time = nm_utils_secret_key_get_timestamp (); + if (!time) { + duid_error = "cannot retrieve the secret key timestamp"; + goto out_fail; + } + + duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), time); + } + + goto out_good; + } + + if (NM_IN_STRSET (duid, "stable-ll", "stable-llt", "stable-uuid")) { + NMUtilsStableType stable_type; + const char *stable_id = NULL; + guint32 salted_header; + GChecksum *sum; + const guint8 *secret_key; + gsize secret_key_len; + + stable_id = _get_stable_id (self, connection, &stable_type); + if (!stable_id) + g_return_val_if_reached (NULL); + + salted_header = htonl (670531087 + stable_type); + + nm_utils_secret_key_get (&secret_key, &secret_key_len); + + sum = g_checksum_new (G_CHECKSUM_SHA256); + + g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header)); + g_checksum_update (sum, (const guchar *) stable_id, -1); + g_checksum_update (sum, (const guchar *) secret_key, secret_key_len); + + g_checksum_get_digest (sum, sha256_digest, &len); + g_checksum_free (sum); + + if (nm_streq (duid, "stable-ll")) { + duid_out = generate_duid_ll (sha256_digest); + } else if (nm_streq (duid, "stable-llt")) { + gint64 time; + +#define EPOCH_DATETIME_THREE_YEARS (356 * 24 * 3600 * 3) + + /* We want a variable time between the secret_key timestamp and three years + * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll + * subtract it from the secret_key timestamp. + */ + time = nm_utils_secret_key_get_timestamp (); + if (!time) { + duid_error = "cannot retrieve the secret key timestamp"; + goto out_fail; + } + /* don't use too old timestamps. They cannot be expressed in DUID-LLT and + * would all be truncated to zero. */ + time = NM_MAX (time, EPOCH_DATETIME_200001010000 + EPOCH_DATETIME_THREE_YEARS); + time -= (unaligned_read_be32 (&sha256_digest[ETH_ALEN]) % EPOCH_DATETIME_THREE_YEARS); + + duid_out = generate_duid_llt (sha256_digest, time); + } else { + nm_assert (nm_streq (duid, "stable-uuid")); + duid_out = generate_duid_uuid (sha256_digest, len); + } + + goto out_good; + } + + g_return_val_if_reached (NULL); + +out_fail: + nm_assert (!duid_out && duid_error); + { + guint8 uuid[16]; + + _LOGW (LOGD_IP6 | LOGD_DHCP6, + "ipv6.dhcp-duid: failure to generate %s DUID: %s. Fallback to random DUID-UUID.", + duid, duid_error); + + nm_utils_random_bytes (uuid, sizeof (uuid)); + duid_out = generate_duid_uuid (uuid, sizeof (uuid)); + } + +out_good: + nm_assert (duid_out); + _LOGD (LOGD_IP6 | LOGD_DHCP6, + "ipv6.dhcp-duid: generate %s DUID '%s' (%s)", + duid, + (logstr1 = nm_dhcp_utils_duid_to_string (duid_out)), + (duid_enforce == NM_DHCP_DUID_ENFORCE_ALWAYS) ? "enforcing" : "fallback"); + + NM_SET_OUT (out_enforce, duid_enforce); + return duid_out; +} + static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingIPConfig *s_ip6; gs_unref_bytes GBytes *hwaddr = NULL; + gs_unref_bytes GBytes *duid = NULL; + NMDhcpDuidEnforce enforce_duid = NM_DHCP_DUID_ENFORCE_NEVER; + const NMPlatformIP6Address *ll_addr = NULL; g_assert (connection); @@ -7704,6 +8007,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)); + duid = dhcp6_get_duid (self, connection, hwaddr, &enforce_duid); priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), nm_device_get_multi_index (self), nm_device_get_ip_iface (self), @@ -7715,6 +8019,8 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) nm_device_get_route_metric (self, AF_INET6), nm_setting_ip_config_get_dhcp_send_hostname (s_ip6), nm_setting_ip_config_get_dhcp_hostname (s_ip6), + duid, + enforce_duid, get_dhcp_timeout (self, AF_INET6), priv->dhcp_anycast_address, (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE, @@ -8139,8 +8445,10 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) if (ifindex <= 0) return; - if (nm_device_sys_iface_state_is_external_or_assume (self)) { - /* for assumed connections we don't tamper with the MTU. */ + if ( !nm_device_get_applied_connection (self) + || nm_device_sys_iface_state_is_external_or_assume (self)) { + /* we don't tamper with the MTU of disconnected and + * external/assumed devices. */ return; } @@ -9886,43 +10194,45 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type) gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, - GError **error, const char **argv) + GError **error, const char **whitelist) { - guint found_keys = 0; + guint found_whitelisted_keys = 0; guint i; nm_assert (hash && g_hash_table_size (hash) > 0); - nm_assert (argv && argv[0]); + nm_assert (whitelist && whitelist[0]); #if NM_MORE_ASSERTS > 10 - /* Assert that the keys are unique. */ + /* Require whitelist to only contain unique keys. */ { gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); - for (i = 0; argv[i]; i++) { - if (!g_hash_table_add (check_dups, (char *) argv[i])) + for (i = 0; whitelist[i]; i++) { + if (!g_hash_table_add (check_dups, (char *) whitelist[i])) nm_assert (FALSE); } nm_assert (g_hash_table_size (check_dups) > 0); } #endif - for (i = 0; argv[i]; i++) { - if (g_hash_table_contains (hash, argv[i])) - found_keys++; + for (i = 0; whitelist[i]; i++) { + if (g_hash_table_contains (hash, whitelist[i])) + found_whitelisted_keys++; } - if (found_keys != g_hash_table_size (hash)) { + if (found_whitelisted_keys == g_hash_table_size (hash)) { + /* Good, there are only whitelisted keys in the hash. */ + return TRUE; + } + + if (error) { GHashTableIter iter; const char *k = NULL; const char *first_invalid_key = NULL; - if (!error) - return FALSE; - g_hash_table_iter_init (&iter, hash); while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) { - if (nm_utils_strv_find_first ((char **) argv, -1, k) < 0) { + if (nm_utils_strv_find_first ((char **) whitelist, -1, k) < 0) { first_invalid_key = k; break; } @@ -9942,10 +10252,9 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, first_invalid_key); } g_return_val_if_fail (first_invalid_key, FALSE); - return FALSE; } - return TRUE; + return FALSE; } void @@ -10088,9 +10397,11 @@ can_reapply_change (NMDevice *self, const char *setting_name, NM_SETTING_CONNECTION_METERED, NM_SETTING_CONNECTION_LLDP); } else if (NM_IN_STRSET (setting_name, - NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_PROXY_SETTING_NAME)) { + return TRUE; + } else if (NM_IN_STRSET (setting_name, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME)) { if (g_hash_table_contains (diffs, NM_SETTING_IP_CONFIG_ROUTE_TABLE)) { /* changing the route-table setting is complicated, because it affects * how we sync the routes. Don't support changing it without full @@ -10221,7 +10532,7 @@ check_and_reapply_connection (NMDevice *self, if (diffs) { NMConnection *connection_clean = connection; - gs_free NMConnection *connection_clean_free = NULL; + gs_unref_object NMConnection *connection_clean_free = NULL; { NMSettingConnection *s_con_a, *s_con_n; diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index eefe38c8..c59af7a6 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -116,7 +116,7 @@ link_changed (NMDevice *device, { NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device); - if (priv->waiting_for_interface) { + if (pllink && priv->waiting_for_interface) { priv->waiting_for_interface = FALSE; nm_device_bring_up (device, TRUE, NULL); nm_device_activate_schedule_stage3_ip_config_start (device); diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index d3c5ae9a..d28fcdbd 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -54,6 +54,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceIwd, PROP_ACTIVE_ACCESS_POINT, PROP_CAPABILITIES, PROP_SCANNING, + PROP_LAST_SCAN, ); enum { @@ -79,6 +80,7 @@ typedef struct { bool can_connect:1; bool scanning:1; bool scan_requested:1; + gint64 last_scan; } NMDeviceIwdPrivate; struct _NMDeviceIwd { @@ -863,6 +865,8 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) priv = NM_DEVICE_IWD_GET_PRIVATE (self); priv->scan_requested = FALSE; + priv->last_scan = nm_utils_get_monotonic_timestamp_ms (); + _notify (self, PROP_LAST_SCAN); /* On success, priv->scanning becomes true right before or right * after this callback, so the next automatic scan will be @@ -1530,6 +1534,12 @@ get_property (GObject *object, guint prop_id, case PROP_SCANNING: g_value_set_boolean (value, priv->scanning); break; + case PROP_LAST_SCAN: + g_value_set_int64 (value, + priv->last_scan > 0 + ? nm_utils_monotonic_timestamp_as_boottime (priv->last_scan, NM_UTILS_NS_PER_MSEC) + : (gint64) -1); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1909,6 +1919,11 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_LAST_SCAN] = + g_param_spec_int64 (NM_DEVICE_IWD_LAST_SCAN, "", "", + -1, G_MAXINT64, -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[SCANNING_PROHIBITED] = diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h index 699ba1bb..a4253f52 100644 --- a/src/devices/wifi/nm-device-iwd.h +++ b/src/devices/wifi/nm-device-iwd.h @@ -38,6 +38,7 @@ #define NM_DEVICE_IWD_ACTIVE_ACCESS_POINT NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT #define NM_DEVICE_IWD_CAPABILITIES NM_DEVICE_WIFI_CAPABILITIES #define NM_DEVICE_IWD_SCANNING NM_DEVICE_WIFI_SCANNING +#define NM_DEVICE_IWD_LAST_SCAN NM_DEVICE_WIFI_LAST_SCAN #define NM_DEVICE_IWD_SCANNING_PROHIBITED NM_DEVICE_WIFI_SCANNING_PROHIBITED diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 90f94557..1a86e13b 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -74,6 +74,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, PROP_ACTIVE_ACCESS_POINT, PROP_CAPABILITIES, PROP_SCANNING, + PROP_LAST_SCAN, ); enum { @@ -96,8 +97,8 @@ typedef struct { bool ssid_found:1; bool is_scanning:1; - gint32 last_scan; - gint32 scheduled_scan_time; + gint64 last_scan; /* milliseconds */ + gint32 scheduled_scan_time; /* seconds */ guint8 scan_interval; /* seconds */ guint pending_scan_id; guint ap_dump_id; @@ -1126,7 +1127,7 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self, { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); - gint32 last_scan; + gint64 last_scan; if ( !priv->enabled || !priv->sup_iface @@ -1147,8 +1148,8 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self, return; } - last_scan = nm_supplicant_interface_get_last_scan_time (priv->sup_iface); - if (last_scan && (nm_utils_get_monotonic_timestamp_s () - last_scan) < 10) { + last_scan = nm_supplicant_interface_get_last_scan (priv->sup_iface); + if (last_scan && (nm_utils_get_monotonic_timestamp_ms () - last_scan) < 10 * NM_UTILS_MSEC_PER_SECOND) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -1422,7 +1423,8 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, _LOGD (LOGD_WIFI, "wifi-scan: scan-done callback: %s", success ? "successful" : "failed"); - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + priv->last_scan = nm_utils_get_monotonic_timestamp_ms (); + _notify (self, PROP_LAST_SCAN); schedule_scan (self, success); _requested_scan_set (self, FALSE); @@ -1445,9 +1447,9 @@ ap_list_dump (gpointer user_data) NMWifiAP *ap; gint32 now_s = nm_utils_get_monotonic_timestamp_s (); - _LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%u next:%u]", + _LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%" G_GINT64_FORMAT " next:%u]", now_s, - priv->last_scan, + priv->last_scan / NM_UTILS_MSEC_PER_SECOND, priv->scheduled_scan_time); c_list_for_each_entry (ap, &priv->aps_lst_head, aps_lst) _ap_dump (self, LOGL_DEBUG, ap, "dump", now_s); @@ -2421,6 +2423,48 @@ error: /*****************************************************************************/ +static gboolean +wake_on_wlan_enable (NMDeviceWifi *self) +{ + NMSettingWirelessWakeOnWLan wowl; + NMSettingWireless *s_wireless; + gs_free char *value = NULL; + + s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); + if (s_wireless) { + wowl = nm_setting_wireless_get_wake_on_wlan (s_wireless); + if (wowl != NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT) + goto found; + } + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "wifi.wake-on-wlan", + NM_DEVICE (self)); + + if (value) { + wowl = _nm_utils_ascii_str_to_int64 (value, 10, + NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE, + G_MAXINT32, + NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT); + + if (NM_FLAGS_ANY (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_EXCLUSIVE_FLAGS)) { + if (!nm_utils_is_power_of_two (wowl)) { + _LOGD (LOGD_WIFI, "invalid default value %u for wake-on-wlan: " + "'default' and 'ignore' are exclusive flags", (guint) wowl); + wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT; + } + } else if (NM_FLAGS_ANY (wowl, ~NM_SETTING_WIRELESS_WAKE_ON_WLAN_ALL)) { + _LOGD (LOGD_WIFI, "invalid default value %u for wake-on-wlan", (guint) wowl); + wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT; + } + if (wowl != NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT) + goto found; + } + wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE; +found: + return nm_platform_wifi_set_wake_on_wlan (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), wowl); +} + static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) { @@ -2614,6 +2658,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_wireless = nm_connection_get_setting_wireless (connection); g_assert (s_wireless); + wake_on_wlan_enable (self); + /* If we need secrets, get them */ setting_name = nm_connection_need_secrets (connection, NULL); if (setting_name) { @@ -3081,7 +3127,8 @@ can_reapply_change (NMDevice *device, return nm_device_hash_check_invalid_keys (diffs, NM_SETTING_WIRELESS_SETTING_NAME, error, - NM_SETTING_WIRELESS_MTU); /* reapplied with IP config */ + NM_SETTING_WIRELESS_MTU, /* reapplied with IP config */ + NM_SETTING_WIRELESS_WAKE_ON_WLAN); } device_class = NM_DEVICE_CLASS (nm_device_wifi_parent_class); @@ -3093,6 +3140,20 @@ can_reapply_change (NMDevice *device, error); } +static void +reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_new) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + + NM_DEVICE_CLASS (nm_device_wifi_parent_class)->reapply_connection (device, + con_old, + con_new); + + _LOGD (LOGD_DEVICE, "reapplying wireless settings"); + + wake_on_wlan_enable (self); +} + /*****************************************************************************/ static void @@ -3123,6 +3184,12 @@ get_property (GObject *object, guint prop_id, case PROP_SCANNING: g_value_set_boolean (value, priv->is_scanning); break; + case PROP_LAST_SCAN: + g_value_set_int64 (value, + priv->last_scan > 0 + ? nm_utils_monotonic_timestamp_as_boottime (priv->last_scan, NM_UTILS_NS_PER_MSEC) + : (gint64) -1); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3256,6 +3323,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr; parent_class->unmanaged_on_quit = unmanaged_on_quit; parent_class->can_reapply_change = can_reapply_change; + parent_class->reapply_connection = reapply_connection; parent_class->state_changed = device_state_changed; @@ -3300,6 +3368,11 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_LAST_SCAN] = + g_param_spec_int64 (NM_DEVICE_WIFI_LAST_SCAN, "", "", + -1, G_MAXINT64, -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[SCANNING_PROHIBITED] = diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index c13b00de..1c555f5f 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -37,6 +37,7 @@ #define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point" #define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities" #define NM_DEVICE_WIFI_SCANNING "scanning" +#define NM_DEVICE_WIFI_LAST_SCAN "last-scan" #define NM_DEVICE_WIFI_SCANNING_PROHIBITED "scanning-prohibited" diff --git a/src/devices/wifi/nm-wifi-common.c b/src/devices/wifi/nm-wifi-common.c index e5e16f03..8e079d11 100644 --- a/src/devices/wifi/nm-wifi-common.c +++ b/src/devices/wifi/nm-wifi-common.c @@ -200,6 +200,7 @@ const NMDBusInterfaceInfoExtended nm_interface_info_device_wireless = { NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AccessPoints", "ao", NM_DEVICE_WIFI_ACCESS_POINTS), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveAccessPoint", "o", NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WirelessCapabilities", "u", NM_DEVICE_WIFI_CAPABILITIES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("LastScan", "x", NM_DEVICE_WIFI_LAST_SCAN), ), ), .legacy_property_changed = TRUE, diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 2a3e9ebe..352b1c3e 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -542,7 +542,7 @@ act_stage3_ip6_config_start (NMDevice *device, NMDeviceStateReason *out_failure_reason) { return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, - nm_device_get_act_request (device), + device, out_failure_reason); } diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index fbe99cc3..ff1579d1 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -704,6 +704,8 @@ nm_modem_stage3_ip4_config_start (NMModem *self, connection = nm_act_request_get_applied_connection (req); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + nm_modem_set_route_parameters_from_device (self, device); + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); /* Only Disabled and Auto methods make sense for WWAN */ @@ -745,8 +747,6 @@ nm_modem_ip4_pre_commit (NMModem *modem, { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (modem); - nm_modem_set_route_parameters_from_device (modem, device); - /* If the modem has an ethernet-type data interface (ie, not PPP and thus * not point-to-point) and IP config has a /32 prefix, then we assume that * ARP will be pointless and we turn it off. @@ -804,20 +804,25 @@ stage3_ip6_config_request (NMModem *self, NMDeviceStateReason *out_failure_reaso NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *self, - NMActRequest *req, + NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMModemPrivate *priv; + NMActRequest *req; NMActStageReturn ret; NMConnection *connection; const char *method; g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE); - g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); + + req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); connection = nm_act_request_get_applied_connection (req); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + nm_modem_set_route_parameters_from_device (self, device); + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); /* Only Ignore and Auto methods make sense for WWAN */ diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index 3e281c0c..6e78d2d5 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -221,7 +221,7 @@ NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem, NMDeviceStateReason *out_failure_reason); NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *modem, - NMActRequest *req, + NMDevice *device, NMDeviceStateReason *out_failure_reason); void nm_modem_ip4_pre_commit (NMModem *modem, NMDevice *device, NMIP4Config *config); diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index ba517606..390b7054 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -513,69 +513,15 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, } static GBytes * -generate_duid_from_machine_id (void) +get_duid (NMDhcpClient *self, gboolean global) { - const int DUID_SIZE = 18; - guint8 *duid_buffer; - GChecksum *sum; - guint8 buffer[32]; /* SHA256 digest size */ - gsize sumlen = sizeof (buffer); - const guint16 duid_type = g_htons (4); - uuid_t uuid; - gs_free char *machine_id_s = NULL; - gs_free char *str = NULL; - GBytes *duid; - - machine_id_s = nm_utils_machine_id_read (); - if (nm_utils_machine_id_parse (machine_id_s, uuid)) { - /* Hash the machine ID so it's not leaked to the network */ - sum = g_checksum_new (G_CHECKSUM_SHA256); - g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid)); - g_checksum_get_digest (sum, buffer, &sumlen); - g_checksum_free (sum); - } else { - nm_log_warn (LOGD_DHCP, "dhcp: failed to read " SYSCONFDIR "/machine-id " - "or " LOCALSTATEDIR "/lib/dbus/machine-id to generate " - "DHCPv6 DUID; creating non-persistent random DUID."); - - nm_utils_random_bytes (buffer, sizeof (buffer)); - } - - /* Generate a DHCP Unique Identifier for DHCPv6 using the - * DUID-UUID method (see RFC 6355 section 4). Format is: - * - * u16: type (DUID-UUID = 4) - * u8[16]: UUID bytes - */ - duid_buffer = g_malloc (DUID_SIZE); - - G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2); - memcpy (&duid_buffer[0], &duid_type, 2); - - /* Since SHA256 is 256 bits, but UUID is 128 bits, we just take the first - * 128 bits of the SHA256 as the DUID-UUID. - */ - memcpy (&duid_buffer[2], buffer, 16); - - duid = g_bytes_new_take (duid_buffer, DUID_SIZE); - nm_log_dbg (LOGD_DHCP, "dhcp: generated DUID %s", - (str = nm_dhcp_utils_duid_to_string (duid))); - return duid; -} - -static GBytes * -get_duid (NMDhcpClient *self) -{ - static GBytes *duid = NULL; - - if (G_UNLIKELY (!duid)) - duid = generate_duid_from_machine_id (); - - return g_bytes_ref (duid); + return NULL; } gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, + GBytes *client_id, + NMDhcpDuidEnforce enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, const char *hostname, @@ -592,11 +538,17 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, g_return_val_if_fail (priv->addr_family == AF_INET6, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); - /* If we don't have one yet, read the default DUID for this DHCPv6 client - * from the client-specific persistent configuration. - */ + nm_assert (!priv->duid); + nm_assert (client_id); + + if (enforce_duid == NM_DHCP_DUID_ENFORCE_NEVER) + priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self, TRUE); + else if (enforce_duid == NM_DHCP_DUID_ENFORCE_LEASE_FALLBACK) + priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self, FALSE); + + /* NM_DHCP_DUID_ENFORCE_ALWAYS and fallback */ if (!priv->duid) - priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); + priv->duid = g_bytes_ref (client_id); _LOGD ("DUID is '%s'", (str = nm_dhcp_utils_duid_to_string (priv->duid))); diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 111b063b..98c3ed26 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -23,6 +23,7 @@ #include "nm-setting-ip6-config.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" +#include "nm-dhcp-utils.h" #define NM_DHCP_TIMEOUT_DEFAULT ((guint32) 45) /* default DHCP timeout, in seconds */ #define NM_DHCP_TIMEOUT_INFINITY G_MAXINT32 @@ -95,13 +96,15 @@ typedef struct { /** * get_duid: * @self: the #NMDhcpClient + * @global: if set to #true, the duid should be searched also in the + * DHCP client's system-wide persistent configuration. * * Attempts to find an existing DHCPv6 DUID for this client in the DHCP * client's persistent configuration. Returned DUID should be the binary * representation of the DUID. If no DUID is found, %NULL should be * returned. */ - GBytes *(*get_duid) (NMDhcpClient *self); + GBytes *(*get_duid) (NMDhcpClient *self, gboolean global); /* Signals */ void (*state_changed) (NMDhcpClient *self, @@ -149,6 +152,8 @@ gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, const char *last_ip4_address); gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, + GBytes *client_id, + NMDhcpDuidEnforce enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, const char *hostname, diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index 16a76d23..3290dd65 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -594,7 +594,8 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, const char *escaped_duid, GError **error) { - char **lines = NULL, **iter, *l; + gs_strfreev char **lines = NULL; + char **iter, *l; GString *s; gboolean success; gsize len = 0; @@ -610,19 +611,9 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, return FALSE; } - /* If the file already contains an uncommented DUID, leave it */ g_assert (contents); lines = g_strsplit_set (contents, "\n\r", -1); g_free (contents); - for (iter = lines; iter && *iter; iter++) { - l = *iter; - while (g_ascii_isspace (*l)) - l++; - if (g_str_has_prefix (l, DUID_PREFIX)) { - g_strfreev (lines); - return TRUE; - } - } } s = g_string_sized_new (len + 50); @@ -630,9 +621,31 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, /* Preserve existing leasefile contents */ if (lines) { - for (iter = lines; iter && *iter; iter++) - g_string_append (s, *iter[0] ? *iter : "\n"); - g_strfreev (lines); + for (iter = lines; iter && *iter; iter++) { + l = *iter; + while (g_ascii_isspace (*l)) + l++; + /* If we find an uncommented DUID in the file, check if + * equal to the one we are going to write: if so, no need + * to update the lease file, otherwise skip the old DUID. + */ + if (g_str_has_prefix (l, DUID_PREFIX)) { + gs_strfreev char **split = NULL; + + split = g_strsplit (l, "\"", -1); + if (nm_streq0 (split[1], escaped_duid)) { + g_string_free (s, TRUE); + return TRUE; + } + continue; + } + + if (*iter[0]) + g_string_append (s, *iter); + /* avoid to add an extra '\n' at the end of file */ + if ((iter[1]) != NULL) + g_string_append_c (s, '\n'); + } } success = g_file_set_contents (leasefile, s->str, -1, error); diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index 93306ddd..43746dd3 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -582,7 +582,7 @@ state_changed (NMDhcpClient *client, } static GBytes * -get_duid (NMDhcpClient *client) +get_duid (NMDhcpClient *client, gboolean global) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); @@ -607,7 +607,7 @@ get_duid (NMDhcpClient *client) g_free (leasefile); } - if (!duid) { + if (!duid && global) { /* Otherwise read the default machine-wide DUID */ _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); @@ -619,8 +619,7 @@ get_duid (NMDhcpClient *client) } } - /* return our DUID, otherwise let the parent class make a default DUID */ - return duid ?: NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->get_duid (client); + return duid; } /*****************************************************************************/ diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index aa40e803..2d85c73a 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -164,6 +164,7 @@ client_start (NMDhcpManager *self, guint32 route_metric, const struct in6_addr *ipv6_ll_addr, GBytes *dhcp_client_id, + NMDhcpDuidEnforce enforce_duid, guint32 timeout, const char *dhcp_anycast_addr, const char *hostname, @@ -218,7 +219,7 @@ client_start (NMDhcpManager *self, if (addr_family == AF_INET) success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, last_ip4_address); else - success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, privacy, needed_prefixes); + success = nm_dhcp_client_start_ip6 (client, dhcp_client_id, enforce_duid, dhcp_anycast_addr, ipv6_ll_addr, hostname, privacy, needed_prefixes); if (!success) { remove_client_unref (self, client); @@ -280,7 +281,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, return client_start (self, AF_INET, multi_idx, iface, ifindex, hwaddr, uuid, route_table, route_metric, NULL, - dhcp_client_id, timeout, dhcp_anycast_addr, hostname, + dhcp_client_id, 0, timeout, dhcp_anycast_addr, hostname, use_fqdn, FALSE, 0, last_ip_address, 0); } @@ -297,6 +298,8 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, + GBytes *duid, + NMDhcpDuidEnforce enforce_duid, guint32 timeout, const char *dhcp_anycast_addr, gboolean info_only, @@ -314,8 +317,8 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, hostname = dhcp_hostname ?: priv->default_hostname; } return client_start (self, AF_INET6, multi_idx, iface, ifindex, hwaddr, uuid, - route_table, route_metric, ll_addr, - NULL, timeout, dhcp_anycast_addr, hostname, TRUE, info_only, + route_table, route_metric, ll_addr, duid, enforce_duid, + timeout, dhcp_anycast_addr, hostname, TRUE, info_only, privacy, NULL, needed_prefixes); } diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index f8a7e31d..ed8ee742 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -72,6 +72,8 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, guint32 route_metric, gboolean send_hostname, const char *dhcp_hostname, + GBytes *duid, + NMDhcpDuidEnforce enforce_duid, guint32 timeout, const char *dhcp_anycast_addr, gboolean info_only, diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h index 5c127bd1..afb87c1a 100644 --- a/src/dhcp/nm-dhcp-utils.h +++ b/src/dhcp/nm-dhcp-utils.h @@ -24,6 +24,12 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" +typedef enum { + NM_DHCP_DUID_ENFORCE_NEVER = 0, + NM_DHCP_DUID_ENFORCE_LEASE_FALLBACK, + NM_DHCP_DUID_ENFORCE_ALWAYS, +} NMDhcpDuidEnforce; + NMIP4Config *nm_dhcp_utils_ip4_config_from_options (struct _NMDedupMultiIndex *multi_idx, int ifindex, const char *iface, diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index a8284b23..2f369aac 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -785,17 +785,18 @@ static void test_write_existing_duid (void) { const char *duid = "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302"; - const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; + const char *original_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; + const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302\";\n"; GError *error = NULL; char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-existing-duid.leases"; - success = g_file_set_contents (path, expected_contents, -1, &error); + success = g_file_set_contents (path, original_contents, -1, &error); g_assert_no_error (error); g_assert (success); - /* Save other DUID; should be a no-op */ + /* Save other DUID; should be overwritten */ success = nm_dhcp_dhclient_save_duid (path, duid, &error); g_assert_no_error (error); g_assert (success); @@ -811,14 +812,14 @@ test_write_existing_duid (void) g_free (contents); } +#define DUID "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302" static void test_write_existing_commented_duid (void) { - #define DUID "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302" - #define ORIG_CONTENTS "#default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n" - const char *expected_contents = \ - "default-duid \"" DUID "\";\n" - ORIG_CONTENTS; +#define ORIG_CONTENTS "#default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n" + const char *expected_contents = + "default-duid \"" DUID "\";\n" + ORIG_CONTENTS; GError *error = NULL; char *contents = NULL; gboolean success; @@ -828,7 +829,7 @@ test_write_existing_commented_duid (void) g_assert_no_error (error); g_assert (success); - /* Save other DUID; should be a no-op */ + /* Save other DUID; should be saved on top */ success = nm_dhcp_dhclient_save_duid (path, DUID, &error); g_assert_no_error (error); g_assert (success); @@ -842,6 +843,33 @@ test_write_existing_commented_duid (void) g_assert_cmpstr (expected_contents, ==, contents); g_free (contents); +#undef ORIG_CONTENTS +} + +static void +test_write_existing_multiline_duid (void) +{ +#define ORIG_CONTENTS "### Commented old DUID ###\n" \ + "#default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n" + const char *expected_contents = \ + "default-duid \"" DUID "\";\n" + ORIG_CONTENTS; + GError *error = NULL; + gs_free char *contents = NULL; + gboolean success; + nmtst_auto_unlinkfile char *path = g_strdup ("test-dhclient-write-existing-multiline-duid.leases"); + + success = g_file_set_contents (path, ORIG_CONTENTS, -1, &error); + nmtst_assert_success (success, error); + + success = nm_dhcp_dhclient_save_duid (path, DUID, &error); + nmtst_assert_success (success, error); + + success = g_file_get_contents (path, &contents, NULL, &error); + nmtst_assert_success (success, error); + + g_assert_cmpstr (expected_contents, ==, contents); +#undef ORIG_CONTENTS } /*****************************************************************************/ @@ -1025,6 +1053,7 @@ main (int argc, char **argv) g_test_add_func ("/dhcp/dhclient/write_duid", test_write_duid); g_test_add_func ("/dhcp/dhclient/write_existing_duid", test_write_existing_duid); g_test_add_func ("/dhcp/dhclient/write_existing_commented_duid", test_write_existing_commented_duid); + g_test_add_func ("/dhcp/dhclient/write_existing_multiline_duid", test_write_existing_multiline_duid); return g_test_run (); } diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index f5500ee7..a29fd81f 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -768,8 +768,6 @@ update_resolv_conf (NMDnsManager *self, gs_free char *content = NULL; SpawnResult write_file_result = SR_SUCCESS; int errsv; - const char *rc_path = _PATH_RESCONF; - nm_auto_free char *rc_path_real = NULL; gboolean resconf_link_cached = FALSE; gs_free char *resconf_link = NULL; @@ -794,12 +792,27 @@ update_resolv_conf (NMDnsManager *self, if ( rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE || ( rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK && !_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link))) { + gs_free char *rc_path_syml = NULL; + nm_auto_free char *rc_path_real = NULL; + const char *rc_path = _PATH_RESCONF; GError *local = NULL; if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) { - rc_path_real = realpath (rc_path, NULL); + rc_path_real = realpath (_PATH_RESCONF, NULL); if (rc_path_real) rc_path = rc_path_real; + else { + /* realpath did not resolve a path-name. That either means, + * _PATH_RESCONF: + * - does not exist + * - is a plain file + * - is a dangling symlink + * + * Handle the case, where it is a dangling symlink... */ + rc_path_syml = nm_utils_read_link_absolute (_PATH_RESCONF, NULL); + if (rc_path_syml) + rc_path = rc_path_syml; + } } /* we first write to /etc/resolv.conf directly. If that fails, diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index b1a4cc25..54ccdb83 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2521,20 +2521,20 @@ nm_utils_machine_id_read (void) */ if ( !g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL) && !g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL)) - return FALSE; + return NULL; contents = g_strstrip (contents); for (i = 0; i < 32; i++) { if (!g_ascii_isxdigit (contents[i])) - return FALSE; + return NULL; if (contents[i] >= 'A' && contents[i] <= 'F') { /* canonicalize to lower-case */ contents[i] = 'a' + (contents[i] - 'A'); } } if (contents[i] != '\0') - return FALSE; + return NULL; return g_steal_pointer (&contents); } @@ -2896,6 +2896,22 @@ nm_utils_secret_key_get (const guint8 **out_secret_key, return secret_key->is_good; } +gint64 +nm_utils_secret_key_get_timestamp (void) +{ + struct stat stat_buf; + const guint8 *key; + gsize key_len; + + if (!nm_utils_secret_key_get (&key, &key_len)) + return 0; + + if (stat (NMSTATEDIR "/secret_key", &stat_buf) != 0) + return 0; + + return stat_buf.st_mtim.tv_sec; +} + /*****************************************************************************/ const char * diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 7f406d21..08530400 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -285,6 +285,7 @@ gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_u gboolean nm_utils_secret_key_get (const guint8 **out_secret_key, gsize *out_key_len); +gint64 nm_utils_secret_key_get_timestamp (void); const char *nm_utils_get_boot_id (void); diff --git a/src/nm-manager.c b/src/nm-manager.c index 9f0891a7..c6d82c9d 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1558,6 +1558,12 @@ again: } } +static gboolean +device_is_wake_on_lan (NMPlatform *platform, NMDevice *device) +{ + return nm_platform_link_get_wake_on_lan (platform, nm_device_get_ip_ifindex (device)); +} + static void remove_device (NMManager *self, NMDevice *device, @@ -1567,14 +1573,19 @@ remove_device (NMManager *self, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); gboolean unmanage = FALSE; - _LOG2D (LOGD_DEVICE, device, "removing device (allow_unmanage %d, managed %d)", - allow_unmanage, nm_device_get_managed (device, FALSE)); + _LOG2D (LOGD_DEVICE, device, "removing device (allow_unmanage %d, managed %d, wol %d)", + allow_unmanage, nm_device_get_managed (device, FALSE), + device_is_wake_on_lan (priv->platform, device)); if (allow_unmanage && nm_device_get_managed (device, FALSE)) { - if (quitting) - unmanage = nm_device_unmanage_on_quit (device); - else { + if (quitting) { + /* Leave configured if wo(w)lan and quitting */ + if (device_is_wake_on_lan (priv->platform, device)) + unmanage = FALSE; + else + unmanage = nm_device_unmanage_on_quit (device); + } else { /* the device is already gone. Unmanage it. */ unmanage = TRUE; } @@ -4747,12 +4758,16 @@ impl_manager_activate_connection (NMDBusObject *obj, if (!active) goto error; - nm_active_connection_authorize (g_steal_pointer (&active), + nm_active_connection_authorize (active, NULL, _async_op_complete_ac_auth_cb, _async_op_data_new_ac_auth_activate_user (self, active, invocation)); + + /* we passed the pointer on to _async_op_data_new_ac_auth_activate_user() */ + g_steal_pointer (&active); + return; error: @@ -5135,12 +5150,6 @@ done: } static gboolean -device_is_wake_on_lan (NMPlatform *platform, NMDevice *device) -{ - return nm_platform_link_get_wake_on_lan (platform, nm_device_get_ip_ifindex (device)); -} - -static gboolean sleep_devices_add (NMManager *self, NMDevice *device, gboolean suspending) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); diff --git a/src/nm-policy.c b/src/nm-policy.c index fe16cda5..125f2704 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1244,8 +1244,10 @@ auto_activate_device (NMPolicy *self, if (!best_connection) return; - _LOGI (LOGD_DEVICE, "auto-activating connection '%s'", - nm_settings_connection_get_id (best_connection)); + _LOGI (LOGD_DEVICE, "auto-activating connection '%s' (%s)", + nm_settings_connection_get_id (best_connection), + nm_settings_connection_get_uuid (best_connection)); + subject = nm_auth_subject_new_internal (); ac = nm_manager_activate_connection (priv->manager, best_connection, diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 3a671acf..9bd662a1 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -6092,6 +6092,14 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru wifi_utils_indicate_addressing_running (wifi_data, running); } +static gboolean +wifi_set_wake_on_wlan (NMPlatform *platform, int ifindex, + NMSettingWirelessWakeOnWLan wowl) +{ + WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); + return wifi_utils_set_wake_on_wlan (wifi_data, wowl); +} + /*****************************************************************************/ static gboolean @@ -7230,6 +7238,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->wifi_set_powersave = wifi_set_powersave; platform_class->wifi_find_frequency = wifi_find_frequency; platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; + platform_class->wifi_set_wake_on_wlan = wifi_set_wake_on_wlan; platform_class->mesh_get_channel = mesh_get_channel; platform_class->mesh_set_channel = mesh_set_channel; diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index c0dc09c4..b938fa4a 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -244,6 +244,9 @@ nla_put_string (struct nl_msg *msg, int attrtype, const char *str) #define NLA_PUT_STRING(msg, attrtype, value) \ NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) +#define NLA_PUT_FLAG(msg, attrtype) \ + NLA_PUT(msg, attrtype, 0, NULL) + struct nlattr *nla_find (const struct nlattr *head, int len, int attrtype); static inline int diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 114bf4b3..0d224443 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -384,6 +384,40 @@ nmp_utils_ethtool_get_link_settings (int ifindex, return TRUE; } + +#define ADVERTISED_INVALID 0 +#define BASET_ALL_MODES ( ADVERTISED_10baseT_Half \ + | ADVERTISED_10baseT_Full \ + | ADVERTISED_100baseT_Half \ + | ADVERTISED_100baseT_Full \ + | ADVERTISED_1000baseT_Half \ + | ADVERTISED_1000baseT_Full \ + | ADVERTISED_10000baseT_Full ) + +static inline guint32 +get_baset_mode (guint32 speed, NMPlatformLinkDuplexType duplex) +{ + if (duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN) + return ADVERTISED_INVALID; + + if (duplex == NM_PLATFORM_LINK_DUPLEX_HALF) { + switch (speed) { + case 10: return ADVERTISED_10baseT_Half; + case 100: return ADVERTISED_100baseT_Half; + case 1000: return ADVERTISED_1000baseT_Half; + default: return ADVERTISED_INVALID; + } + } else { + switch (speed) { + case 10: return ADVERTISED_10baseT_Full; + case 100: return ADVERTISED_100baseT_Full; + case 1000: return ADVERTISED_1000baseT_Full; + case 10000: return ADVERTISED_10000baseT_Full; + default: return ADVERTISED_INVALID; + } + } +} + gboolean nmp_utils_ethtool_set_link_settings (int ifindex, gboolean autoneg, @@ -395,6 +429,8 @@ nmp_utils_ethtool_set_link_settings (int ifindex, }; g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail ( (speed && duplex != NM_PLATFORM_LINK_DUPLEX_UNKNOWN) + || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), FALSE); /* retrieve first current settings */ if (!ethtool_get (ifindex, &edata)) @@ -404,7 +440,31 @@ nmp_utils_ethtool_set_link_settings (int ifindex, edata.cmd = ETHTOOL_SSET; if (autoneg) { edata.autoneg = AUTONEG_ENABLE; - edata.advertising = edata.supported; + if (!speed) + edata.advertising = edata.supported; + else { + guint32 mode; + + mode = get_baset_mode (speed, duplex); + + if (!mode) { + nm_log_trace (LOGD_PLATFORM, + "ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised", + ifindex, + speed, + nm_platform_link_duplex_type_to_string (duplex)); + return FALSE; + } + if (!(edata.supported & mode)) { + nm_log_trace (LOGD_PLATFORM, + "ethtool[%d]: device does not support %uBASE-T %s duplex mode", + ifindex, + speed, + nm_platform_link_duplex_type_to_string (duplex)); + return FALSE; + } + edata.advertising = (edata.supported & ~BASET_ALL_MODES) | mode; + } } else { edata.autoneg = AUTONEG_DISABLE; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 1a94ce28..f75019e8 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2888,6 +2888,16 @@ nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gbo klass->wifi_indicate_addressing_running (self, ifindex, running); } +gboolean +nm_platform_wifi_set_wake_on_wlan (NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + return klass->wifi_set_wake_on_wlan (self, ifindex, wowl); +} + guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex) { @@ -2995,6 +3005,13 @@ nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean * return nmp_utils_ethtool_get_link_settings (ifindex, out_autoneg, out_speed, out_duplex); } +NM_UTILS_LOOKUP_STR_DEFINE (nm_platform_link_duplex_type_to_string, NMPlatformLinkDuplexType, + NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_LINK_DUPLEX_UNKNOWN, "unknown"), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_LINK_DUPLEX_FULL, "full"), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_LINK_DUPLEX_HALF, "half"), +); + /*****************************************************************************/ const NMDedupMultiHeadEntry * diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 2d2bfaab..866df736 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -33,6 +33,7 @@ #include "nm-core-utils.h" #include "nm-setting-vlan.h" #include "nm-setting-wired.h" +#include "nm-setting-wireless.h" #include "nm-setting-ip-tunnel.h" #define NM_TYPE_PLATFORM (nm_platform_get_type ()) @@ -867,6 +868,7 @@ typedef struct { void (*wifi_set_powersave) (NMPlatform *, int ifindex, guint32 powersave); guint32 (*wifi_find_frequency) (NMPlatform *, int ifindex, const guint32 *freqs); void (*wifi_indicate_addressing_running) (NMPlatform *, int ifindex, gboolean running); + gboolean (*wifi_set_wake_on_wlan) (NMPlatform *, int ifindex, NMSettingWirelessWakeOnWLan wowl); guint32 (*mesh_get_channel) (NMPlatform *, int ifindex); gboolean (*mesh_set_channel) (NMPlatform *, int ifindex, guint32 channel); @@ -1245,6 +1247,7 @@ void nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM void nm_platform_wifi_set_powersave (NMPlatform *self, int ifindex, guint32 powersave); guint32 nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs); void nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gboolean running); +gboolean nm_platform_wifi_set_wake_on_wlan (NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl); guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex); gboolean nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel); @@ -1449,6 +1452,7 @@ int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPla gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password); gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +const char * nm_platform_link_duplex_type_to_string (NMPlatformLinkDuplexType duplex); void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, int ifindex, diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index 767e23a9..6c2ff3f5 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -271,6 +271,47 @@ nla_put_failure: return FALSE; } +static gboolean +wifi_nl80211_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl) +{ + WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + struct nlattr *triggers; + int err; + + if (wowl == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE) + return TRUE; + + msg = nl80211_alloc_msg(nl80211, NL80211_CMD_SET_WOWLAN, 0); + if (!msg) + return FALSE; + + triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); + + if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY)) + NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_ANY); + if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT)) + NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_DISCONNECT); + if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC)) + NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); + if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE)) + NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); + if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST)) + NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); + if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE)) + NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); + if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE)) + NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); + + nla_nest_end(msg, triggers); + + err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); + return err ? FALSE : TRUE; + +nla_put_failure: + return FALSE; +} + /* @divisor: pass what value @xbm should be divided by to get dBm */ static guint32 nl80211_xbm_to_percent (gint32 xbm, guint32 divisor) @@ -846,6 +887,7 @@ wifi_nl80211_init (int ifindex) .get_mode = wifi_nl80211_get_mode, .set_mode = wifi_nl80211_set_mode, .set_powersave = wifi_nl80211_set_powersave, + .set_wake_on_wlan = wifi_nl80211_set_wake_on_wlan, .get_freq = wifi_nl80211_get_freq, .find_freq = wifi_nl80211_find_freq, .get_bssid = wifi_nl80211_get_bssid, diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h index 59386514..627108cb 100644 --- a/src/platform/wifi/wifi-utils-private.h +++ b/src/platform/wifi/wifi-utils-private.h @@ -34,6 +34,9 @@ typedef struct { /* Set power saving mode on an interface */ gboolean (*set_powersave) (WifiData *data, guint32 powersave); + /* Set WakeOnWLAN mode on an interface */ + gboolean (*set_wake_on_wlan) (WifiData *data, NMSettingWirelessWakeOnWLan wowl); + /* Return current frequency in MHz (really associated BSS frequency) */ guint32 (*get_freq) (WifiData *data); diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c index 8818dc9d..b3dd92ba 100644 --- a/src/platform/wifi/wifi-utils.c +++ b/src/platform/wifi/wifi-utils.c @@ -112,6 +112,15 @@ wifi_utils_set_powersave (WifiData *data, guint32 powersave) return data->klass->set_powersave ? data->klass->set_powersave (data, powersave) : TRUE; } +gboolean +wifi_utils_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl) +{ + g_return_val_if_fail (data != NULL, FALSE); + + return data->klass->set_wake_on_wlan ? + data->klass->set_wake_on_wlan (data, wowl) : FALSE; +} + guint32 wifi_utils_get_freq (WifiData *data) { diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index 8acc35fc..c69561de 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -25,6 +25,7 @@ #include <net/ethernet.h> #include "nm-dbus-interface.h" +#include "nm-setting-wireless.h" typedef struct WifiData WifiData; @@ -66,6 +67,8 @@ gboolean wifi_utils_get_wowlan (WifiData *data); gboolean wifi_utils_set_powersave (WifiData *data, guint32 powersave); +gboolean wifi_utils_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl); + /* OLPC Mesh-only functions */ guint32 wifi_utils_get_mesh_channel (WifiData *data); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 6526abc7..50cc1427 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -3189,8 +3189,9 @@ static const NMDBusInterfaceInfoExtended interface_info_settings_connection = { &signal_info_removed, ), .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Unsaved", "b", NM_SETTINGS_CONNECTION_UNSAVED), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Flags", "u", NM_SETTINGS_CONNECTION_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Unsaved", "b", NM_SETTINGS_CONNECTION_UNSAVED), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Flags", "u", NM_SETTINGS_CONNECTION_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Filename", "s", NM_SETTINGS_CONNECTION_FILENAME), ), ), .legacy_property_changed = TRUE, diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index 70518687..cd64f976 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -42,11 +42,11 @@ /* Properties */ #define NM_SETTINGS_CONNECTION_UNSAVED "unsaved" +#define NM_SETTINGS_CONNECTION_FLAGS "flags" +#define NM_SETTINGS_CONNECTION_FILENAME "filename" /* Internal properties */ #define NM_SETTINGS_CONNECTION_READY "ready" -#define NM_SETTINGS_CONNECTION_FLAGS "flags" -#define NM_SETTINGS_CONNECTION_FILENAME "filename" /** * NMSettingsConnectionIntFlags: diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 94018734..5bb629dc 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -653,7 +653,7 @@ plugin_loaded (GSList *list, const char *path) } static gboolean -load_plugin (NMSettings *self, GSList *list, const char *pname, GError **error) +load_plugin (NMSettings *self, GSList **list, const char *pname, GError **error) { gs_free char *full_name = NULL; gs_free char *path = NULL; @@ -666,7 +666,7 @@ load_plugin (NMSettings *self, GSList *list, const char *pname, GError **error) full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); path = g_module_build_path (NMPLUGINDIR, full_name); - if (plugin_loaded (list, path)) + if (plugin_loaded (*list, path)) return TRUE; if (stat (path, &st) != 0) { @@ -719,7 +719,7 @@ load_plugin (NMSettings *self, GSList *list, const char *pname, GError **error) g_object_set_qdata_full (obj, plugin_module_path_quark (), path, g_free); path = NULL; if (add_plugin (self, NM_SETTINGS_PLUGIN (obj))) - list = g_slist_append (list, g_steal_pointer (&obj)); + *list = g_slist_append (*list, g_steal_pointer (&obj)); return TRUE; } @@ -787,7 +787,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) continue; } - success = load_plugin (self, list, pname, error); + success = load_plugin (self, &list, pname, error); if (!success) break; @@ -797,7 +797,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) pname = "ibft"; add_ibft = FALSE; - success = load_plugin (self, list, "ibft", error); + success = load_plugin (self, &list, "ibft", error); if (!success) break; } @@ -886,6 +886,8 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data) g_object_unref (connection); check_startup_complete (self); + + g_object_unref (self); /* Balanced by a ref in claim_connection() */ } #define NM_DBUS_SERVICE_OPENCONNECT "org.freedesktop.NetworkManager.openconnect" @@ -998,6 +1000,11 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection) _clear_connections_cached_list (priv); g_object_ref (connection); + /* FIXME(shutdown): The NMSettings instance can't be disposed + * while there is any exported connection. Ideally we should + * unexport all connections on NMSettings' disposal, but for now + * leak @self on termination when there are connections alive. */ + g_object_ref (self); priv->connections_len++; c_list_link_tail (&priv->connections_lst_head, &connection->_connections_lst); diff --git a/src/settings/plugins/ifcfg-rh/nm-ifdown b/src/settings/plugins/ifcfg-rh/nm-ifdown new file mode 100644 index 00000000..e7bd73ae --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nm-ifdown @@ -0,0 +1,3 @@ +#!/bin/sh +nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" && +exec nmcli connection down filename "/etc/sysconfig/network-scripts/ifcfg-$1" diff --git a/src/settings/plugins/ifcfg-rh/nm-ifup b/src/settings/plugins/ifcfg-rh/nm-ifup new file mode 100644 index 00000000..96637a92 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nm-ifup @@ -0,0 +1,3 @@ +#!/bin/sh +nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" && +exec nmcli connection up filename "/etc/sysconfig/network-scripts/ifcfg-$1" diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 722d4e36..c1960846 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1685,12 +1685,13 @@ make_ip6_setting (shvarFile *ifcfg, { NMSettingIPConfig *s_ip6 = NULL; const char *v; - char *value = NULL; - char *str_value; + gs_free char *value = NULL; char *route6_path = NULL; gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; - char *ipv6addr, *ipv6addr_secondaries; + const char *ipv6addr, *ipv6addr_secondaries; + gs_free char *ipv6addr_to_free = NULL; + gs_free char *ipv6addr_secondaries_to_free = NULL; gs_free const char **list = NULL; const char *const *iter; guint32 i; @@ -1716,13 +1717,16 @@ make_ip6_setting (shvarFile *ifcfg, * When both are set, the device specified in IPV6_DEFAULTGW takes preference. */ if (network_ifcfg) { - char *ipv6_defaultgw, *ipv6_defaultdev; - char *default_dev = NULL; + const char *ipv6_defaultgw, *ipv6_defaultdev; + gs_free char *ipv6_defaultgw_to_free = NULL; + gs_free char *ipv6_defaultdev_to_free = NULL; + const char *default_dev = NULL; /* Get the connection ifcfg device name and the global default route device */ - value = svGetValueStr_cp (ifcfg, "DEVICE"); - ipv6_defaultgw = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTGW"); - ipv6_defaultdev = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTDEV"); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DEVICE", &value); + ipv6_defaultgw = svGetValueStr (network_ifcfg, "IPV6_DEFAULTGW", &ipv6_defaultgw_to_free); + ipv6_defaultdev = svGetValueStr (network_ifcfg, "IPV6_DEFAULTDEV", &ipv6_defaultdev_to_free); if (ipv6_defaultgw) { default_dev = strchr (ipv6_defaultgw, '%'); @@ -1735,66 +1739,64 @@ make_ip6_setting (shvarFile *ifcfg, /* If there was a global default route device specified, then only connections * for that device can be the default connection. */ - if (default_dev && value) - never_default = !!strcmp (value, default_dev); - - g_free (ipv6_defaultgw); - g_free (ipv6_defaultdev); - g_free (value); + if (default_dev && v) + never_default = !!strcmp (v, default_dev); } /* Find out method property */ /* Is IPV6 enabled? Set method to "ignored", when not enabled */ - str_value = svGetValueStr_cp (ifcfg, "IPV6INIT"); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IPV6INIT", &value); ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE); - if (!str_value) { + if (!v) { if (network_ifcfg) ipv6init = svGetValueBoolean (network_ifcfg, "IPV6INIT", FALSE); } - g_free (str_value); if (!ipv6init) method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; /* IPv6 is disabled */ else { ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE); - str_value = svGetValueStr_cp (ifcfg, "IPV6_AUTOCONF"); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IPV6_AUTOCONF", &value); dhcp6 = svGetValueBoolean (ifcfg, "DHCPV6C", FALSE); - if (!g_strcmp0 (str_value, "shared")) + if (!g_strcmp0 (v, "shared")) method = NM_SETTING_IP6_CONFIG_METHOD_SHARED; - else if (svParseBoolean (str_value, !ipv6forwarding)) + else if (svParseBoolean (v, !ipv6forwarding)) method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; else if (dhcp6) method = NM_SETTING_IP6_CONFIG_METHOD_DHCP; else { /* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */ - g_free (str_value); - str_value = svGetValueStr_cp (ifcfg, "IPV6ADDR"); - if (!str_value) - str_value = svGetValueStr_cp (ifcfg, "IPV6ADDR_SECONDARIES"); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IPV6ADDR", &value); + if (!v) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IPV6ADDR_SECONDARIES", &value); + } - if (!str_value) + if (!v) method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; } - g_free (str_value); } /* TODO - handle other methods */ /* Read IPv6 Privacy Extensions configuration */ - str_value = svGetValueStr_cp (ifcfg, "IPV6_PRIVACY"); - if (str_value) { - ip6_privacy = svParseBoolean (str_value, FALSE); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IPV6_PRIVACY", &value); + if (v) { + ip6_privacy = svParseBoolean (v, FALSE); if (!ip6_privacy) - ip6_privacy = (g_strcmp0 (str_value, "rfc4941") == 0) || - (g_strcmp0 (str_value, "rfc3041") == 0); + ip6_privacy = (g_strcmp0 (v, "rfc4941") == 0) || + (g_strcmp0 (v, "rfc3041") == 0); } ip6_privacy_prefer_public_ip = svGetValueBoolean (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", FALSE); - ip6_privacy_val = str_value ? + ip6_privacy_val = v ? (ip6_privacy ? (ip6_privacy_prefer_public_ip ? NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR : NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR) : NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED) : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; - g_free (str_value); /* the route table (policy routing) is ignored if we don't handle routes. */ route_table = svGetValueInt64 (ifcfg, "IPV6_ROUTE_TABLE", 10, @@ -1821,19 +1823,25 @@ make_ip6_setting (shvarFile *ifcfg, if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) return NM_SETTING (s_ip6); - value = svGetValueStr_cp (ifcfg, "DHCPV6_HOSTNAME"); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCPV6_DUID", &value); + if (v) + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_DHCP_DUID, v, NULL); + + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCPV6_HOSTNAME", &value); /* Use DHCP_HOSTNAME as fallback if it is in FQDN format and ipv6.method is * auto or dhcp: this is required to support old ifcfg files */ - if (!value && ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + if (!v && ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP))) { - value = svGetValueStr_cp (ifcfg, "DHCP_HOSTNAME"); - if (value && !strchr (value, '.')) - g_clear_pointer (&value, g_free); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DHCP_HOSTNAME", &value); + if (v && !strchr (v, '.')) + v = NULL; } - if (value) - g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, value, NULL); - g_free (value); + if (v) + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL); g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL); @@ -1843,18 +1851,16 @@ make_ip6_setting (shvarFile *ifcfg, * added to the automatic ones. Note that this is not currently supported by * the legacy 'network' service (ifup-eth). */ - ipv6addr = svGetValueStr_cp (ifcfg, "IPV6ADDR"); - ipv6addr_secondaries = svGetValueStr_cp (ifcfg, "IPV6ADDR_SECONDARIES"); + ipv6addr = svGetValueStr (ifcfg, "IPV6ADDR", &ipv6addr_to_free); + ipv6addr_secondaries = svGetValueStr (ifcfg, "IPV6ADDR_SECONDARIES", &ipv6addr_secondaries_to_free); + nm_clear_g_free (&value); value = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL, ipv6addr ?: "", ipv6addr_secondaries ?: "", NULL); - g_free (ipv6addr); - g_free (ipv6addr_secondaries); list = nm_utils_strsplit_set (value, " "); - g_free (value); for (iter = list, i = 0; iter && *iter; iter++, i++) { NMIPAddress *addr = NULL; @@ -1868,25 +1874,26 @@ make_ip6_setting (shvarFile *ifcfg, /* Gateway */ if (nm_setting_ip_config_get_num_addresses (s_ip6)) { - value = svGetValueStr_cp (ifcfg, "IPV6_DEFAULTGW"); - if (!value) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IPV6_DEFAULTGW", &value); + if (!v) { /* If no gateway in the ifcfg, try global /etc/sysconfig/network instead */ - if (network_ifcfg) - value = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTGW"); + if (network_ifcfg) { + nm_clear_g_free (&value); + v = svGetValueStr (network_ifcfg, "IPV6_DEFAULTGW", &value); + } } - if (value) { + if (v) { char *ptr; - if ((ptr = strchr (value, '%')) != NULL) + if ((ptr = strchr (v, '%')) != NULL) *ptr = '\0'; /* remove %interface prefix if present */ - if (!nm_utils_ipaddr_valid (AF_INET6, value)) { + if (!nm_utils_ipaddr_valid (AF_INET6, v)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid IP6 address '%s'", value); - g_free (value); + "Invalid IP6 address '%s'", v); goto error; } - g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, value, NULL); - g_free (value); + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, v, NULL); } } @@ -1900,11 +1907,10 @@ make_ip6_setting (shvarFile *ifcfg, g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, i_val, NULL); /* IPv6 tokenized interface identifier */ - str_value = svGetValueStr_cp (ifcfg, "IPV6_TOKEN"); - if (str_value) { - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, str_value, NULL); - g_free (str_value); - } + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "IPV6_TOKEN", &value); + if (v) + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, v, NULL); /* DNS servers * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting()) @@ -1913,24 +1919,22 @@ make_ip6_setting (shvarFile *ifcfg, char tag[256]; numbered_tag (tag, "DNS", i); - value = svGetValueStr_cp (ifcfg, tag); - if (!value) { + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, tag, &value); + if (!v) { /* all done */ break; } - if (nm_utils_ipaddr_valid (AF_INET6, value)) { - if (!nm_setting_ip_config_add_dns (s_ip6, value)) + if (nm_utils_ipaddr_valid (AF_INET6, v)) { + if (!nm_setting_ip_config_add_dns (s_ip6, v)) PARSE_WARNING ("duplicate DNS server %s", tag); - } else if (nm_utils_ipaddr_valid (AF_INET, value)) { + } else if (nm_utils_ipaddr_valid (AF_INET, v)) { /* Ignore IPv4 addresses */ } else { - PARSE_WARNING ("invalid DNS server address %s", value); - g_free (value); + PARSE_WARNING ("invalid DNS server address %s", v); goto error; } - - g_free (value); } if (!routes_read) { @@ -1961,7 +1965,6 @@ make_ip6_setting (shvarFile *ifcfg, /* DNS options */ nm_clear_g_free (&value); parse_dns_options (s_ip6, svGetValue (ifcfg, "IPV6_RES_OPTIONS", &value)); - g_free (value); /* DNS priority */ priority = svGetValueInt64 (ifcfg, "IPV6_DNS_PRIORITY", 10, G_MININT32, G_MAXINT32, 0); @@ -4171,8 +4174,8 @@ parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, const char *va NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags, NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, ignore_wol_password ? NULL : wol_password, NM_SETTING_WIRED_AUTO_NEGOTIATE, autoneg, - NM_SETTING_WIRED_SPEED, autoneg ? 0 : speed, - NM_SETTING_WIRED_DUPLEX, autoneg ? NULL : duplex, + NM_SETTING_WIRED_SPEED, speed, + NM_SETTING_WIRED_DUPLEX, duplex, NULL); } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index bdad7389..fdb2e10a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1135,6 +1135,9 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) /* Stuff ETHTOOL_OPT with required options */ str = NULL; auto_negotiate = nm_setting_wired_get_auto_negotiate (s_wired); + speed = nm_setting_wired_get_speed (s_wired); + duplex = nm_setting_wired_get_duplex (s_wired); + /* autoneg off + speed 0 + duplex NULL, means we want NM * to skip link configuration which is default. So write * down link config only if we have auto-negotiate true or @@ -1143,18 +1146,14 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) if (auto_negotiate) { str = g_string_sized_new (64); g_string_printf (str, "autoneg on"); - } else { - speed = nm_setting_wired_get_speed (s_wired); - duplex = nm_setting_wired_get_duplex (s_wired); - if (speed || duplex) { - str = g_string_sized_new (64); - g_string_printf (str, "autoneg off"); - if (speed) - g_string_append_printf (str, " speed %u", speed); - if (duplex) - g_string_append_printf (str, " duplex %s", duplex); - } + } else if (speed || duplex) { + str = g_string_sized_new (64); + g_string_printf (str, "autoneg off"); } + if (speed) + g_string_append_printf (str, " speed %u", speed); + if (duplex) + g_string_append_printf (str, " duplex %s", duplex); wol = nm_setting_wired_get_wake_on_lan (s_wired); wol_password = nm_setting_wired_get_wake_on_lan_password (s_wired); @@ -2568,6 +2567,7 @@ write_ip6_setting (NMConnection *connection, svUnsetValue (ifcfg, "IPV6INIT"); svUnsetValue (ifcfg, "IPV6_AUTOCONF"); svUnsetValue (ifcfg, "DHCPV6C"); + svUnsetValue (ifcfg, "DHCPv6_DUID"); svUnsetValue (ifcfg, "DHCPV6_HOSTNAME"); svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME"); svUnsetValue (ifcfg, "IPV6_DEFROUTE"); @@ -2608,6 +2608,9 @@ write_ip6_setting (NMConnection *connection, svUnsetValue (ifcfg, "DHCPV6C"); } + svSetValueStr (ifcfg, "DHCPV6_DUID", + nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6))); + write_ip6_setting_dhcp_hostname (s_ip6, ifcfg); /* Write out IP addresses */ diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 3511b151..71e6a35a 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -129,7 +129,7 @@ typedef struct { GHashTable * bss_proxies; char * current_bss; - gint32 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */ + gint64 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_ms() */ } NMSupplicantInterfacePrivate; @@ -209,7 +209,7 @@ bss_proxy_properties_changed_cb (GDBusProxy *proxy, NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); if (priv->scanning) - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + priv->last_scan = nm_utils_get_monotonic_timestamp_ms (); g_signal_emit (self, signals[BSS_UPDATED], 0, g_dbus_proxy_get_object_path (proxy), @@ -344,7 +344,7 @@ set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state) if ( priv->state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + priv->last_scan = nm_utils_get_monotonic_timestamp_ms (); /* Disconnect reason is no longer relevant when not in the DISCONNECTED state */ if (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED) @@ -406,7 +406,7 @@ set_scanning (NMSupplicantInterface *self, gboolean new_scanning) /* Cache time of last scan completion */ if (priv->scanning == FALSE) - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + priv->last_scan = nm_utils_get_monotonic_timestamp_ms (); _notify (self, PROP_SCANNING); } @@ -438,8 +438,8 @@ nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self) return priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY ? priv->current_bss : NULL; } -gint32 -nm_supplicant_interface_get_last_scan_time (NMSupplicantInterface *self) +gint64 +nm_supplicant_interface_get_last_scan (NMSupplicantInterface *self) { return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->last_scan; } @@ -987,7 +987,7 @@ wpas_iface_scan_done (GDBusProxy *proxy, NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); /* Cache last scan completed time */ - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + priv->last_scan = nm_utils_get_monotonic_timestamp_ms (); priv->scan_done_success |= success; scan_done_emit_signal (self); } @@ -1002,7 +1002,7 @@ wpas_iface_bss_added (GDBusProxy *proxy, NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); if (priv->scanning) - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + priv->last_scan = nm_utils_get_monotonic_timestamp_ms (); bss_add_new (self, path); } diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index f32ad8dd..31272b3c 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -110,7 +110,7 @@ gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self); const char *nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self); -gint32 nm_supplicant_interface_get_last_scan_time (NMSupplicantInterface *self); +gint64 nm_supplicant_interface_get_last_scan (NMSupplicantInterface *self); const char *nm_supplicant_interface_get_ifname (NMSupplicantInterface *self); diff --git a/src/systemd/sd-adapt/copy.h b/src/systemd/sd-adapt/copy.h new file mode 100644 index 00000000..637892c2 --- /dev/null +++ b/src/systemd/sd-adapt/copy.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c index 79b823de..ef405098 100644 --- a/src/systemd/src/basic/alloc-util.c +++ b/src/systemd/src/basic/alloc-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h index bae6a284..ebe42889 100644 --- a/src/systemd/src/basic/alloc-util.h +++ b/src/systemd/src/basic/alloc-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <alloca.h> #include <stddef.h> #include <stdlib.h> diff --git a/src/systemd/src/basic/async.h b/src/systemd/src/basic/async.h index 36f33377..31606131 100644 --- a/src/systemd/src/basic/async.h +++ b/src/systemd/src/basic/async.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering -***/ - int asynchronous_job(void* (*func)(void *p), void *arg); int asynchronous_sync(pid_t *ret_pid); diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c index f51de788..5c82a545 100644 --- a/src/systemd/src/basic/escape.c +++ b/src/systemd/src/basic/escape.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -17,8 +12,10 @@ #include "macro.h" #include "utf8.h" -size_t cescape_char(char c, char *buf) { - char * buf_old = buf; +int cescape_char(char c, char *buf) { + char *buf_old = buf; + + /* Needs space for 4 characters in the buffer */ switch (c) { diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h index 80b0946e..c612a7c0 100644 --- a/src/systemd/src/basic/escape.h +++ b/src/systemd/src/basic/escape.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <inttypes.h> #include <stddef.h> #include <stdint.h> @@ -47,7 +41,7 @@ typedef enum EscapeStyle { char *cescape(const char *s); char *cescape_length(const char *s, size_t n); -size_t cescape_char(char c, char *buf); +int cescape_char(char c, char *buf); int cunescape(const char *s, UnescapeFlags flags, char **ret); int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret); diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c index 75de9a19..6f946c3e 100644 --- a/src/systemd/src/basic/ether-addr-util.c +++ b/src/systemd/src/basic/ether-addr-util.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen + Copyright © 2014 Tom Gundersen ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h index f7e0de54..f02cefea 100644 --- a/src/systemd/src/basic/ether-addr-util.h +++ b/src/systemd/src/basic/ether-addr-util.h @@ -2,9 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen + Copyright © 2014 Tom Gundersen ***/ #include <net/ethernet.h> diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c index 2adb8966..404fe615 100644 --- a/src/systemd/src/basic/extract-word.c +++ b/src/systemd/src/basic/extract-word.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/extract-word.h b/src/systemd/src/basic/extract-word.h index d2113948..8c63b7c3 100644 --- a/src/systemd/src/basic/extract-word.h +++ b/src/systemd/src/basic/extract-word.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include "macro.h" typedef enum ExtractFlags { diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index 6a753195..71babe2f 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -14,10 +9,13 @@ #include <sys/stat.h> #include <unistd.h> +#include "alloc-util.h" +#include "copy.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "io-util.h" #include "macro.h" #include "memfd-util.h" #include "missing.h" @@ -566,6 +564,201 @@ try_dev_shm_without_o_tmpfile: return -EOPNOTSUPP; } + +/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */ +#define DATA_FD_MEMORY_LIMIT (64U*1024U) + +/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */ +#define DATA_FD_TMP_LIMIT (1024U*1024U) + +int fd_duplicate_data_fd(int fd) { + + _cleanup_close_ int copy_fd = -1, tmp_fd = -1; + _cleanup_free_ void *remains = NULL; + size_t remains_size = 0; + const char *td; + struct stat st; + int r; + + /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but + * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be + * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported + * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in + * /var/tmp. */ + + if (fstat(fd, &st) < 0) + return -errno; + + /* For now, let's only accept regular files, sockets, pipes and char devices */ + if (S_ISDIR(st.st_mode)) + return -EISDIR; + if (S_ISLNK(st.st_mode)) + return -ELOOP; + if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode)) + return -EBADFD; + + /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note + * that we use the reported regular file size only as a hint, given that there are plenty special files in + * /proc and /sys which report a zero file size but can be read from. */ + + if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) { + + /* Try a memfd first */ + copy_fd = memfd_new("data-fd"); + if (copy_fd >= 0) { + off_t f; + + r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0); + if (r < 0) + return r; + + f = lseek(copy_fd, 0, SEEK_SET); + if (f != 0) + return -errno; + + if (r == 0) { + /* Did it fit into the limit? If so, we are done. */ + r = memfd_set_sealed(copy_fd); + if (r < 0) + return r; + + return TAKE_FD(copy_fd); + } + + /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */ + + } else { + _cleanup_(close_pairp) int pipefds[2] = { -1, -1 }; + int isz; + + /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather + * then block indefinitely when we hit the pipe size limit */ + + if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) + return -errno; + + isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); + if (isz < 0) + return -errno; + + /* Try to enlarge the pipe size if necessary */ + if ((size_t) isz < DATA_FD_MEMORY_LIMIT) { + + (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT); + + isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); + if (isz < 0) + return -errno; + } + + if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) { + + r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size); + if (r < 0 && r != -EAGAIN) + return r; /* If we get EAGAIN it could be because of the source or because of + * the destination fd, we can't know, as sendfile() and friends won't + * tell us. Hence, treat this as reason to fall back, just to be + * sure. */ + if (r == 0) { + /* Everything fit in, yay! */ + (void) fd_nonblock(pipefds[0], false); + + return TAKE_FD(pipefds[0]); + } + + /* Things didn't fit in. But we read data into the pipe, let's remember that, so that + * when writing the new file we incorporate this first. */ + copy_fd = TAKE_FD(pipefds[0]); + } + } + } + + /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */ + if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) && + (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) { + off_t f; + + tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC); + if (tmp_fd < 0) + return tmp_fd; + + if (copy_fd >= 0) { + /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the + * temporary file first. */ + + r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0); + if (r < 0) + return r; + + assert(r == 0); + } + + if (remains_size > 0) { + /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the + * failed copy operation, let's flush them out next. */ + + r = loop_write(tmp_fd, remains, remains_size, false); + if (r < 0) + return r; + } + + r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK); + if (r < 0) + return r; + if (r == 0) + goto finish; /* Yay, it fit in */ + + /* It didn't fit in. Let's not forget to use what we already used */ + f = lseek(tmp_fd, 0, SEEK_SET); + if (f != 0) + return -errno; + + safe_close(copy_fd); + copy_fd = TAKE_FD(tmp_fd); + + remains = mfree(remains); + remains_size = 0; + } + + /* As last fallback use /var/tmp */ + r = var_tmp_dir(&td); + if (r < 0) + return r; + + tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC); + if (tmp_fd < 0) + return tmp_fd; + + if (copy_fd >= 0) { + /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this + * into the temporary file first. */ + r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK); + if (r < 0) + return r; + + assert(r == 0); + } + + if (remains_size > 0) { + /* Then, copy in any read but not yet written bytes. */ + r = loop_write(tmp_fd, remains, remains_size, false); + if (r < 0) + return r; + } + + /* Copy in the rest */ + r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK); + if (r < 0) + return r; + + assert(r == 0); + +finish: + /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the + * file again */ + + return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC); +} #endif /* NM_IGNORED */ int fd_move_above_stdio(int fd) { @@ -739,4 +932,28 @@ int fd_reopen(int fd, int flags) { return new_fd; } + +int read_nr_open(void) { + _cleanup_free_ char *nr_open = NULL; + int r; + + /* Returns the kernel's current fd limit, either by reading it of /proc/sys if that works, or using the + * hard-coded default compiled-in value of current kernels (1M) if not. This call will never fail. */ + + r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); + if (r < 0) + log_debug_errno(r, "Failed to read /proc/sys/fs/nr_open, ignoring: %m"); + else { + int v; + + r = safe_atoi(nr_open, &v); + if (r < 0) + log_debug_errno(r, "Failed to parse /proc/sys/fs/nr_open value '%s', ignoring: %m", nr_open); + else + return v; + } + + /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ + return 1024 * 1024; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h index 89c3f34c..8adc959d 100644 --- a/src/systemd/src/basic/fd-util.h +++ b/src/systemd/src/basic/fd-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <dirent.h> #include <stdbool.h> #include <stdio.h> @@ -81,6 +75,8 @@ enum { int acquire_data_fd(const void *data, size_t size, unsigned flags); +int fd_duplicate_data_fd(int fd); + /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ #define ERRNO_IS_DISCONNECT(r) \ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) @@ -106,3 +102,5 @@ static inline int make_null_stdio(void) { }) int fd_reopen(int fd, int flags); + +int read_nr_open(void); diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index 4f71384e..2d3aef2c 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -209,6 +204,25 @@ fail: return 0; } +int write_string_filef( + const char *fn, + WriteStringFileFlags flags, + const char *format, ...) { + + _cleanup_free_ char *p = NULL; + va_list ap; + int r; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return write_string_file(fn, p, flags); +} + int read_one_line_file(const char *fn, char **line) { _cleanup_fclose_ FILE *f = NULL; int r; @@ -267,29 +281,35 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { #endif /* NM_IGNORED */ int read_full_stream(FILE *f, char **contents, size_t *size) { - size_t n, l; _cleanup_free_ char *buf = NULL; struct stat st; + size_t n, l; + int fd; assert(f); assert(contents); - if (fstat(fileno(f), &st) < 0) - return -errno; - n = LINE_MAX; - if (S_ISREG(st.st_mode)) { + fd = fileno(f); + if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's + * optimize our buffering) */ - /* Safety check */ - if (st.st_size > READ_FULL_BYTES_MAX) - return -E2BIG; + if (fstat(fileno(f), &st) < 0) + return -errno; + + if (S_ISREG(st.st_mode)) { + + /* Safety check */ + if (st.st_size > READ_FULL_BYTES_MAX) + return -E2BIG; - /* Start with the right file size, but be prepared for files from /proc which generally report a file - * size of 0. Note that we increase the size to read here by one, so that the first read attempt - * already makes us notice the EOF. */ - if (st.st_size > 0) - n = st.st_size + 1; + /* Start with the right file size, but be prepared for files from /proc which generally report a file + * size of 0. Note that we increase the size to read here by one, so that the first read attempt + * already makes us notice the EOF. */ + if (st.st_size > 0) + n = st.st_size + 1; + } } l = 0; @@ -680,21 +700,41 @@ static int parse_env_file_push( return 0; } -int parse_env_file( +int parse_env_filev( + FILE *f, const char *fname, - const char *newline, ...) { + const char *newline, + va_list ap) { - va_list ap; int r, n_pushed = 0; + va_list aq; if (!newline) newline = NEWLINE; + va_copy(aq, ap); + r = parse_env_file_internal(f, fname, newline, parse_env_file_push, &aq, &n_pushed); + va_end(aq); + if (r < 0) + return r; + + return n_pushed; +} + +int parse_env_file( + FILE *f, + const char *fname, + const char *newline, + ...) { + + va_list ap; + int r; + va_start(ap, newline); - r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed); + r = parse_env_filev(f, fname, newline, ap); va_end(ap); - return r < 0 ? r : n_pushed; + return r; } #if 0 /* NM_IGNORED */ @@ -1218,7 +1258,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); - *ret = path_kill_slashes(t); + *ret = path_simplify(t, false); return 0; } @@ -1260,7 +1300,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) { *x = 0; - *ret = path_kill_slashes(t); + *ret = path_simplify(t, false); return 0; } @@ -1300,7 +1340,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { *x = 0; - *ret = path_kill_slashes(t); + *ret = path_simplify(t, false); return 0; } @@ -1528,21 +1568,29 @@ int read_nul_string(FILE *f, char **ret) { } int mkdtemp_malloc(const char *template, char **ret) { - char *p; + _cleanup_free_ char *p = NULL; + int r; - assert(template); assert(ret); - p = strdup(template); + if (template) + p = strdup(template); + else { + const char *tmp; + + r = tmp_dir(&tmp); + if (r < 0) + return r; + + p = strjoin(tmp, "/XXXXXX"); + } if (!p) return -ENOMEM; - if (!mkdtemp(p)) { - free(p); + if (!mkdtemp(p)) return -errno; - } - *ret = p; + *ret = TAKE_PTR(p); return 0; } diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index bed7c8fb..77e6206e 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <dirent.h> #include <stdbool.h> #include <stddef.h> @@ -17,12 +11,12 @@ #include "time-util.h" typedef enum { - WRITE_STRING_FILE_CREATE = 1<<0, - WRITE_STRING_FILE_ATOMIC = 1<<1, - WRITE_STRING_FILE_AVOID_NEWLINE = 1<<2, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1<<3, - WRITE_STRING_FILE_SYNC = 1<<4, - WRITE_STRING_FILE_DISABLE_BUFFER = 1<<5, + WRITE_STRING_FILE_CREATE = 1 << 0, + WRITE_STRING_FILE_ATOMIC = 1 << 1, + WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3, + WRITE_STRING_FILE_SYNC = 1 << 4, + WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5, /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() @@ -39,13 +33,16 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin return write_string_file_ts(fn, line, flags, NULL); } +int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); + int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); int read_full_stream(FILE *f, char **contents, size_t *size); int verify_file(const char *fn, const char *blob, bool accept_extra_nl); -int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; +int parse_env_filev(FILE *f, const char *fname, const char *separator, va_list ap); +int parse_env_file(FILE *f, const char *fname, const char *separator, ...) _sentinel_; int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c index 049b238e..e3ae9916 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -234,6 +229,22 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return 0; } + +int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (mode != MODE_INVALID) + if (fchmod(fd, mode) < 0) + return -errno; + + if (uid != UID_INVALID || gid != GID_INVALID) + if (fchown(fd, uid, gid) < 0) + return -errno; + + return 0; +} #endif /* NM_IGNORED */ int fchmod_umask(int fd, mode_t m) { @@ -249,7 +260,7 @@ int fchmod_umask(int fd, mode_t m) { #if 0 /* NM_IGNORED */ int fchmod_opath(int fd, mode_t m) { - char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; /* This function operates also on fd that might have been opened with * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like @@ -571,6 +582,7 @@ int unlink_or_warn(const char *filename) { return 0; } +#endif /* NM_IGNORED */ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; @@ -586,6 +598,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } +#if 0 /* NM_IGNORED */ static bool safe_transition(const struct stat *a, const struct stat *b) { /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files @@ -609,10 +622,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, assert(path); /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ - if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN)) + if (FLAGS_SET(flags, CHASE_NONEXISTENT | CHASE_OPEN)) return -EINVAL; - if ((flags & (CHASE_STEP|CHASE_OPEN)) == (CHASE_STEP|CHASE_OPEN)) + if (FLAGS_SET(flags, CHASE_STEP | CHASE_OPEN)) return -EINVAL; if (isempty(path)) @@ -1073,6 +1086,15 @@ int access_fd(int fd, int mode) { return r; } +void unlink_tempfilep(char (*p)[]) { + /* If the file is created with mkstemp(), it will (almost always) + * change the suffix. Treat this as a sign that the file was + * successfully created. We ignore both the rare case where the + * original suffix is used and unlink failures. */ + if (!endswith(*p, ".XXXXXX")) + (void) unlink_noerrno(*p); +} + int unlinkat_deallocate(int fd, const char *name, int flags) { _cleanup_close_ int truncate_fd = -1; struct stat st; @@ -1153,9 +1175,9 @@ int fsync_directory_of_file(int fd) { r = fd_get_path(fd, &path); if (r < 0) { - log_debug("Failed to query /proc/self/fd/%d%s: %m", - fd, - r == -EOPNOTSUPP ? ", ignoring" : ""); + log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", + fd, + r == -EOPNOTSUPP ? ", ignoring" : ""); if (r == -EOPNOTSUPP) /* If /proc is not available, we're most likely running in some diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h index 6157fe81..28566773 100644 --- a/src/systemd/src/basic/fs-util.h +++ b/src/systemd/src/basic/fs-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <dirent.h> #include <fcntl.h> #include <limits.h> @@ -31,6 +25,7 @@ int readlink_value(const char *p, char **ret); int readlink_and_make_absolute(const char *p, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid); int fchmod_umask(int fd, mode_t mode); int fchmod_opath(int fd, mode_t m); @@ -70,13 +65,13 @@ union inotify_event_buffer { int inotify_add_watch_fd(int fd, int what, uint32_t mask); enum { - CHASE_PREFIX_ROOT = 1U << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */ - CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ - CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */ - CHASE_TRAIL_SLASH = 1U << 5, /* If set, any trailing slash will be preserved */ - CHASE_STEP = 1U << 6, /* If set, just execute a single step of the normalization */ + CHASE_PREFIX_ROOT = 1 << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ + CHASE_NONEXISTENT = 1 << 1, /* If set, it's OK if the path doesn't actually exist. */ + CHASE_NO_AUTOFS = 1 << 2, /* If set, return -EREMOTE if autofs mount point found */ + CHASE_SAFE = 1 << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ + CHASE_OPEN = 1 << 4, /* If set, return an O_PATH object to the final component */ + CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */ + CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */ }; /* How many iterations to execute before returning -ELOOP */ @@ -104,6 +99,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); int access_fd(int fd, int mode); +void unlink_tempfilep(char (*p)[]); int unlinkat_deallocate(int fd, const char *name, int flags); int fsync_directory_of_file(int fd); diff --git a/src/systemd/src/basic/hash-funcs.c b/src/systemd/src/basic/hash-funcs.c index cb763f56..7ed35cb1 100644 --- a/src/systemd/src/basic/hash-funcs.c +++ b/src/systemd/src/basic/hash-funcs.c @@ -1,9 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt + Copyright © 2014 Michal Schmidt ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/hash-funcs.h b/src/systemd/src/basic/hash-funcs.h index 0c963af8..3715c56e 100644 --- a/src/systemd/src/basic/hash-funcs.h +++ b/src/systemd/src/basic/hash-funcs.h @@ -2,10 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt + Copyright © 2014 Michal Schmidt ***/ #include "macro.h" diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c index b8a8e37b..c91f8bd2 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -1,9 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt + Copyright © 2014 Michal Schmidt ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h index 701eecbd..d1d1b9c8 100644 --- a/src/systemd/src/basic/hashmap.h +++ b/src/systemd/src/basic/hashmap.h @@ -2,10 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt + Copyright © 2014 Michal Schmidt ***/ #include <limits.h> diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c index 1fe410f5..09f2a9e7 100644 --- a/src/systemd/src/basic/hexdecoct.c +++ b/src/systemd/src/basic/hexdecoct.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/hexdecoct.h b/src/systemd/src/basic/hexdecoct.h index c2f11013..9477d16e 100644 --- a/src/systemd/src/basic/hexdecoct.h +++ b/src/systemd/src/basic/hexdecoct.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <stdbool.h> #include <stddef.h> #include <stdio.h> diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c index 0feeaa5d..85708394 100644 --- a/src/systemd/src/basic/hostname-util.c +++ b/src/systemd/src/basic/hostname-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h index 8e29e9e1..74948172 100644 --- a/src/systemd/src/basic/hostname-util.h +++ b/src/systemd/src/basic/hostname-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010-2015 Lennart Poettering -***/ - #include <stdbool.h> #include <stdio.h> diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c index 65748615..d945567a 100644 --- a/src/systemd/src/basic/in-addr-util.c +++ b/src/systemd/src/basic/in-addr-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h index acd567c9..956c00a8 100644 --- a/src/systemd/src/basic/in-addr-util.h +++ b/src/systemd/src/basic/in-addr-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering -***/ - #include <netinet/in.h> #include <stddef.h> #include <sys/socket.h> diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c index 1bc5c345..6f46c944 100644 --- a/src/systemd/src/basic/io-util.c +++ b/src/systemd/src/basic/io-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h index e4717b6f..ed189b58 100644 --- a/src/systemd/src/basic/io-util.h +++ b/src/systemd/src/basic/io-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <stdbool.h> #include <stddef.h> #include <stdint.h> diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h index 3ec7e73f..643e0bea 100644 --- a/src/systemd/src/basic/list.h +++ b/src/systemd/src/basic/list.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - /* The head of the linked list. Use this in the structure that shall * contain the head of the linked list */ #define LIST_HEAD(t,name) \ diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h index e7d7c8b1..28edd572 100644 --- a/src/systemd/src/basic/log.h +++ b/src/systemd/src/basic/log.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <stdarg.h> #include <stdbool.h> #include <stdlib.h> @@ -251,7 +245,7 @@ int log_emergency_level(void); /* Structured logging */ #define log_struct_errno(level, error, ...) \ log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, __FILE__, __LINE__, __func__, __VA_ARGS__) + error, __FILE__, __LINE__, __func__, __VA_ARGS__, NULL) #define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) #define log_struct_iovec_errno(level, error, iovec, n_iovec) \ @@ -290,6 +284,8 @@ void log_set_open_when_needed(bool b); * stderr, the console or kmsg */ void log_set_prohibit_ipc(bool b); +int log_dup_console(void); + int log_syntax_internal( const char *unit, int level, diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h index c89fb84e..cdb54f82 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <inttypes.h> #include <stdbool.h> #include <sys/param.h> @@ -366,6 +360,8 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { #define SET_FLAG(v, flag, b) \ (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) +#define FLAGS_SET(v, flags) \ + (((v) & (flags)) == (flags)) #define CASE_F(X) case X: #define CASE_F_1(CASE, X) CASE_F(X) diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c index 07096766..a0154263 100644 --- a/src/systemd/src/basic/mempool.c +++ b/src/systemd/src/basic/mempool.c @@ -1,9 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2010-2014 Lennart Poettering - Copyright 2014 Michal Schmidt + Copyright © 2014 Michal Schmidt ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h index 68249cd8..291415de 100644 --- a/src/systemd/src/basic/mempool.h +++ b/src/systemd/src/basic/mempool.h @@ -2,10 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2011-2014 Lennart Poettering - Copyright 2014 Michal Schmidt + Copyright © 2014 Michal Schmidt ***/ #include <stddef.h> @@ -29,7 +26,6 @@ static struct mempool pool_name = { \ .at_least = alloc_at_least, \ } - #if VALGRIND void mempool_drop(struct mempool *mp); #endif diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c index ae43a041..7d6bcb5a 100644 --- a/src/systemd/src/basic/parse-util.c +++ b/src/systemd/src/basic/parse-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -637,6 +632,58 @@ int parse_percent(const char *p) { return v; } +int parse_permille_unbounded(const char *p) { + const char *pc, *pm, *dot, *n; + int r, q, v; + + pm = endswith(p, "‰"); + if (pm) { + n = strndupa(p, pm - p); + r = safe_atoi(n, &v); + if (r < 0) + return r; + } else { + pc = endswith(p, "%"); + if (!pc) + return -EINVAL; + + dot = memchr(p, '.', pc - p); + if (dot) { + if (dot + 2 != pc) + return -EINVAL; + if (dot[1] < '0' || dot[1] > '9') + return -EINVAL; + q = dot[1] - '0'; + n = strndupa(p, dot - p); + } else { + q = 0; + n = strndupa(p, pc - p); + } + r = safe_atoi(n, &v); + if (r < 0) + return r; + if (v > (INT_MAX - q) / 10) + return -ERANGE; + + v = v * 10 + q; + } + + if (v < 0) + return -ERANGE; + + return v; +} + +int parse_permille(const char *p) { + int v; + + v = parse_permille_unbounded(p); + if (v > 1000) + return -ERANGE; + + return v; +} + int parse_nice(const char *p, int *ret) { int n, r; diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h index 2b75b938..f3267f4c 100644 --- a/src/systemd/src/basic/parse-util.h +++ b/src/systemd/src/basic/parse-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <inttypes.h> #include <limits.h> #include <stddef.h> @@ -115,6 +109,9 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); int parse_percent_unbounded(const char *p); int parse_percent(const char *p); +int parse_permille_unbounded(const char *p); +int parse_permille(const char *p); + int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index 567fc61c..69bdb894 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010-2012 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -34,12 +29,13 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "utf8.h" -#if 0 /* NM_IGNORED */ bool path_is_absolute(const char *p) { return p[0] == '/'; } +#if 0 /* NM_IGNORED */ bool is_path(const char *p) { return !!strchr(p, '/'); } @@ -130,8 +126,8 @@ int path_make_absolute_cwd(const char *p, char **ret) { } int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *r, *p; - unsigned n_parents; + char *f, *t, *r, *p; + unsigned n_parents = 0; assert(from_dir); assert(to_path); @@ -139,85 +135,81 @@ int path_make_relative(const char *from_dir, const char *to_path, char **_r) { /* Strips the common part, and adds ".." elements as necessary. */ - if (!path_is_absolute(from_dir)) + if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) return -EINVAL; - if (!path_is_absolute(to_path)) - return -EINVAL; + f = strdupa(from_dir); + t = strdupa(to_path); + + path_simplify(f, true); + path_simplify(t, true); /* Skip the common part. */ for (;;) { size_t a, b; - from_dir += strspn(from_dir, "/"); - to_path += strspn(to_path, "/"); + f += *f == '/'; + t += *t == '/'; - if (!*from_dir) { - if (!*to_path) + if (!*f) { + if (!*t) /* from_dir equals to_path. */ r = strdup("."); else /* from_dir is a parent directory of to_path. */ - r = strdup(to_path); + r = strdup(t); if (!r) return -ENOMEM; - path_kill_slashes(r); - *_r = r; return 0; } - if (!*to_path) + if (!*t) break; - a = strcspn(from_dir, "/"); - b = strcspn(to_path, "/"); + a = strcspn(f, "/"); + b = strcspn(t, "/"); - if (a != b) + if (a != b || memcmp(f, t, a) != 0) break; - if (memcmp(from_dir, to_path, a) != 0) - break; - - from_dir += a; - to_path += b; + f += a; + t += b; } /* If we're here, then "from_dir" has one or more elements that need to * be replaced with "..". */ /* Count the number of necessary ".." elements. */ - for (n_parents = 0;;) { + for (; *f;) { size_t w; - from_dir += strspn(from_dir, "/"); - - if (!*from_dir) - break; - - w = strcspn(from_dir, "/"); + w = strcspn(f, "/"); /* If this includes ".." we can't do a simple series of "..", refuse */ - if (w == 2 && from_dir[0] == '.' && from_dir[1] == '.') + if (w == 2 && f[0] == '.' && f[1] == '.') return -EINVAL; - /* Count number of elements, except if they are "." */ - if (w != 1 || from_dir[0] != '.') - n_parents++; + /* Count number of elements */ + n_parents++; - from_dir += w; + f += w; + f += *f == '/'; } - r = new(char, n_parents * 3 + strlen(to_path) + 1); + r = new(char, n_parents * 3 + strlen(t) + 1); if (!r) return -ENOMEM; for (p = r; n_parents > 0; n_parents--) p = mempcpy(p, "../", 3); - strcpy(p, to_path); - path_kill_slashes(r); + if (*t) + strcpy(p, t); + else + /* Remove trailing slash */ + *(--p) = 0; *_r = r; return 0; @@ -238,7 +230,7 @@ int path_strv_make_absolute_cwd(char **l) { if (r < 0) return r; - path_kill_slashes(t); + path_simplify(t, false); free_and_replace(*s, t); } @@ -339,17 +331,30 @@ char **path_strv_resolve_uniq(char **l, const char *root) { } #endif /* NM_IGNORED */ -char *path_kill_slashes(char *path) { +char *path_simplify(char *path, bool kill_dots) { char *f, *t; - bool slash = false; + bool slash = false, ignore_slash = false, absolute; + + assert(path); - /* Removes redundant inner and trailing slashes. Modifies the - * passed string in-place. + /* Removes redundant inner and trailing slashes. Also removes unnecessary dots + * if kill_dots is true. Modifies the passed string in-place. * - * ///foo///bar/ becomes /foo/bar + * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) + * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) + * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false) + * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) */ - for (f = path, t = path; *f; f++) { + absolute = path_is_absolute(path); + + f = path; + if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) { + ignore_slash = true; + f++; + } + + for (t = path; *f; f++) { if (*f == '/') { slash = true; @@ -357,17 +362,21 @@ char *path_kill_slashes(char *path) { } if (slash) { + if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) + continue; + slash = false; - *(t++) = '/'; + if (ignore_slash) + ignore_slash = false; + else + *(t++) = '/'; } *(t++) = *f; } - /* Special rule, if we are talking of the root directory, a - trailing slash is good */ - - if (t == path && slash) + /* Special rule, if we are talking of the root directory, a trailing slash is good */ + if (absolute && t == path) *(t++) = '/'; *t = 0; @@ -535,7 +544,7 @@ int find_binary(const char *name, char **ret) { /* Found it! */ if (ret) { - *ret = path_kill_slashes(j); + *ret = path_simplify(j, false); j = NULL; } @@ -689,12 +698,11 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar if (r < 0) return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); - path_kill_slashes(p); + path_simplify(p, false); if (suppress_root && empty_or_root(p)) p = mfree(p); - free(*arg); - *arg = p; + free_and_replace(*arg, p); return 0; } @@ -778,7 +786,6 @@ bool filename_is_valid(const char *p) { return true; } -#if 0 /* NM_IGNORED */ bool path_is_normalized(const char *p) { if (isempty(p)) @@ -802,6 +809,7 @@ bool path_is_normalized(const char *p) { return true; } +#if 0 /* NM_IGNORED */ char *file_in_same_dir(const char *path, const char *filename) { char *e, *ret; size_t k; @@ -880,17 +888,36 @@ bool hidden_or_backup_file(const char *filename) { bool is_device_path(const char *path) { - /* Returns true on paths that refer to a device, either in - * sysfs or in /dev */ + /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ + + return PATH_STARTSWITH_SET(path, "/dev/", "/sys/"); +} + +bool valid_device_node_path(const char *path) { + + /* Some superficial checks whether the specified path is a valid device node path, all without looking at the + * actual device node. */ + + if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")) + return false; + + if (endswith(path, "/")) /* can't be a device node if it ends in a slash */ + return false; - return path_startswith(path, "/dev/") || - path_startswith(path, "/sys/"); + return path_is_normalized(path); } -bool is_deviceallow_pattern(const char *path) { - return path_startswith(path, "/dev/") || - startswith(path, "block-") || - startswith(path, "char-"); +bool valid_device_allow_pattern(const char *path) { + assert(path); + + /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= + * accept it */ + + if (startswith(path, "block-") || + startswith(path, "char-")) + return true; + + return valid_device_node_path(path); } int systemd_installation_has_version(const char *root, unsigned minimal_version) { @@ -926,7 +953,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) if (r < 0) return r; - assert_se((c = endswith(path, "*.so"))); + assert_se(c = endswith(path, "*.so")); *c = '\0'; /* truncate the glob part */ STRV_FOREACH(name, names) { @@ -975,6 +1002,7 @@ bool dot_or_dot_dot(const char *path) { return path[2] == 0; } +#if 0 /* NM_IGNORED */ bool empty_or_root(const char *root) { /* For operations relative to some root directory, returns true if the specified root directory is redundant, @@ -985,3 +1013,51 @@ bool empty_or_root(const char *root) { return root[strspn(root, "/")] == 0; } + +int path_simplify_and_warn( + char *path, + unsigned flag, + const char *unit, + const char *filename, + unsigned line, + const char *lvalue) { + + bool absolute, fatal = flag & PATH_CHECK_FATAL; + + assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); + + if (!utf8_is_valid(path)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); + return -EINVAL; + } + + if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { + absolute = path_is_absolute(path); + + if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "%s= path is not absolute%s: %s", + lvalue, fatal ? "" : ", ignoring", path); + return -EINVAL; + } + + if (absolute && (flag & PATH_CHECK_RELATIVE)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "%s= path is absolute%s: %s", + lvalue, fatal ? "" : ", ignoring", path); + return -EINVAL; + } + } + + path_simplify(path, true); + + if (!path_is_normalized(path)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "%s= path is not normalized%s: %s", + lvalue, fatal ? "" : ", ignoring", path); + return -EINVAL; + } + + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 79db819e..519060cf 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010-2012 Lennart Poettering -***/ - #include <alloca.h> #include <stdbool.h> #include <stddef.h> @@ -52,12 +46,12 @@ char* path_make_absolute(const char *p, const char *prefix); int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from_dir, const char *to_path, char **_r); -char* path_kill_slashes(char *path); char* path_startswith(const char *path, const char *prefix) _pure_; int path_compare(const char *a, const char *b) _pure_; bool path_equal(const char *a, const char *b) _pure_; bool path_equal_or_files_same(const char *a, const char *b, int flags); char* path_join(const char *root, const char *path, const char *rest); +char* path_simplify(char *path, bool kill_dots); static inline bool path_equal_ptr(const char *a, const char *b) { return !!a == !!b && (!a || path_equal(a, b)); @@ -103,11 +97,11 @@ int mkfs_exists(const char *fstype); * 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), '/')) + for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); 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), '/')) + for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) char *prefix_root(const char *root, const char *path); @@ -149,7 +143,9 @@ char *file_in_same_dir(const char *path, const char *filename); bool hidden_or_backup_file(const char *filename) _pure_; bool is_device_path(const char *path); -bool is_deviceallow_pattern(const char *path); + +bool valid_device_node_path(const char *path); +bool valid_device_allow_pattern(const char *path); int systemd_installation_has_version(const char *root, unsigned minimal_version); @@ -169,3 +165,11 @@ bool empty_or_root(const char *root); static inline const char *empty_to_root(const char *path) { return isempty(path) ? "/" : path; } + +enum { + PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */ + PATH_CHECK_ABSOLUTE = 1 << 1, + PATH_CHECK_RELATIVE = 1 << 2, +}; + +int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue); diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c index 805a6174..79900df3 100644 --- a/src/systemd/src/basic/prioq.c +++ b/src/systemd/src/basic/prioq.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering -***/ /* * Priority Queue diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h index e348c646..e0361752 100644 --- a/src/systemd/src/basic/prioq.h +++ b/src/systemd/src/basic/prioq.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering -***/ - #include <stdbool.h> #include "hashmap.h" diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c index 4af99957..10aa0e56 100644 --- a/src/systemd/src/basic/process-util.c +++ b/src/systemd/src/basic/process-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -19,6 +14,7 @@ #include <stdlib.h> #include <string.h> #include <sys/mman.h> +#include <sys/mount.h> #include <sys/personality.h> #include <sys/prctl.h> #include <sys/types.h> @@ -80,20 +76,31 @@ int get_process_state(pid_t pid) { return (unsigned char) state; } -int get_process_comm(pid_t pid, char **name) { +int get_process_comm(pid_t pid, char **ret) { + _cleanup_free_ char *escaped = NULL, *comm = NULL; const char *p; int r; - assert(name); + assert(ret); assert(pid >= 0); + escaped = new(char, TASK_COMM_LEN); + if (!escaped) + return -ENOMEM; + p = procfs_file_alloca(pid, "comm"); - r = read_one_line_file(p, name); + r = read_one_line_file(p, &comm); if (r == -ENOENT) return -ESRCH; + if (r < 0) + return r; - return r; + /* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */ + cellescape(escaped, TASK_COMM_LEN, comm); + + *ret = TAKE_PTR(escaped); + return 0; } int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { @@ -251,15 +258,10 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * memcpy(ans, "[...]", max_length-1); ans[max_length-1] = 0; } else { - char *e; - t[max_length - 6] = 0; /* Chop off final spaces */ - e = strchr(t, 0); - while (e > t && isspace(e[-1])) - e--; - *e = 0; + delete_trailing_chars(t, WHITESPACE); ans = strjoin("[", t, "...]"); } @@ -300,7 +302,7 @@ int rename_process(const char name[]) { * can use PR_SET_NAME, which sets the thread name for the calling thread. */ if (prctl(PR_SET_NAME, name) < 0) log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l > 15) /* Linux process names can be 15 chars at max */ + if (l >= TASK_COMM_LEN) /* Linux process names can be 15 chars at max */ truncated = true; /* Second step, change glibc's ID of the process name. */ @@ -744,14 +746,17 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) { /* * Return values: - * < 0 : wait_for_terminate_with_timeout() failed to get the state of the - * process, the process timed out, the process was terminated by a - * signal, or failed for an unknown reason. + * + * < 0 : wait_for_terminate_with_timeout() failed to get the state of the process, the process timed out, the process + * was terminated by a signal, or failed for an unknown reason. + * * >=0 : The process terminated normally with no failures. * - * Success is indicated by a return value of zero, a timeout is indicated - * by ETIMEDOUT, and all other child failure states are indicated by error - * is indicated by a non-zero value. + * Success is indicated by a return value of zero, a timeout is indicated by ETIMEDOUT, and all other child failure + * states are indicated by error is indicated by a non-zero value. + * + * This call assumes SIGCHLD has been blocked already, in particular before the child to wait for has been forked off + * to remain entirely race-free. */ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { sigset_t mask; @@ -1353,6 +1358,16 @@ int safe_fork_full( } } + if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) { + + /* Optionally, make sure we never propagate mounts to the host. */ + + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { + log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m"); + _exit(EXIT_FAILURE); + } + } + if (flags & FORK_CLOSE_ALL_FDS) { /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ log_close(); diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h index e3bf36bf..e11164bd 100644 --- a/src/systemd/src/basic/process-util.h +++ b/src/systemd/src/basic/process-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <alloca.h> #include <errno.h> #include <sched.h> @@ -51,8 +45,8 @@ int get_process_ppid(pid_t pid, pid_t *ppid); int wait_for_terminate(pid_t pid, siginfo_t *status); typedef enum WaitFlags { - WAIT_LOG_ABNORMAL = 1U << 0, - WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1, + WAIT_LOG_ABNORMAL = 1 << 0, + WAIT_LOG_NON_ZERO_EXIT_STATUS = 1 << 1, /* A shortcut for requesting the most complete logging */ WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, @@ -157,14 +151,15 @@ void reset_cached_pid(void); int must_be_root(void); typedef enum ForkFlags { - FORK_RESET_SIGNALS = 1U << 0, - FORK_CLOSE_ALL_FDS = 1U << 1, - FORK_DEATHSIG = 1U << 2, - FORK_NULL_STDIO = 1U << 3, - FORK_REOPEN_LOG = 1U << 4, - FORK_LOG = 1U << 5, - FORK_WAIT = 1U << 6, - FORK_NEW_MOUNTNS = 1U << 7, + FORK_RESET_SIGNALS = 1 << 0, + FORK_CLOSE_ALL_FDS = 1 << 1, + FORK_DEATHSIG = 1 << 2, + FORK_NULL_STDIO = 1 << 3, + FORK_REOPEN_LOG = 1 << 4, + FORK_LOG = 1 << 5, + FORK_WAIT = 1 << 6, + FORK_NEW_MOUNTNS = 1 << 7, + FORK_MOUNTNS_SLAVE = 1 << 8, } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c index d80751cf..01a8488f 100644 --- a/src/systemd/src/basic/random-util.c +++ b/src/systemd/src/basic/random-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -48,9 +43,9 @@ int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { * for us. */ /* Use the getrandom() syscall unless we know we don't have it. */ - if (have_syscall != 0) { + if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { #if !HAVE_GETRANDOM - /* XXX: NM: systemd calls the syscall directly in this case. Don't add that workaround. + /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. * If you don't compile against a libc that provides getrandom(), you don't get it. */ r = -1; errno = ENOSYS; diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h index 72391705..9a103f0e 100644 --- a/src/systemd/src/basic/random-util.h +++ b/src/systemd/src/basic/random-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <stdbool.h> #include <stddef.h> #include <stdint.h> diff --git a/src/systemd/src/basic/refcnt.h b/src/systemd/src/basic/refcnt.h index 0d5b3f4e..d2be6086 100644 --- a/src/systemd/src/basic/refcnt.h +++ b/src/systemd/src/basic/refcnt.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering -***/ - /* A type-safe atomic refcounter. * * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h index dc0f1e17..66471381 100644 --- a/src/systemd/src/basic/set.h +++ b/src/systemd/src/basic/set.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include "extract-word.h" #include "hashmap.h" #include "macro.h" diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h index 0c43467b..92f2804c 100644 --- a/src/systemd/src/basic/signal-util.h +++ b/src/systemd/src/basic/signal-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010-2015 Lennart Poettering -***/ - #include <signal.h> #include "macro.h" diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index aa06070e..a71db5a7 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -793,7 +788,7 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); -SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) { +SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *n) { int r; r = parse_boolean(n); diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index c2d21b45..e2db795a 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <netinet/ether.h> #include <netinet/in.h> #include <stdbool.h> @@ -118,7 +112,7 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; -SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s); +SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *s); int netlink_family_to_string_alloc(int b, char **s); int netlink_family_from_string(const char *s) _pure_; diff --git a/src/systemd/src/basic/stat-util.c b/src/systemd/src/basic/stat-util.c index 28e75618..9892d8bc 100644 --- a/src/systemd/src/basic/stat-util.c +++ b/src/systemd/src/basic/stat-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010-2012 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -135,32 +130,6 @@ int path_is_read_only_fs(const char *path) { return false; } -int path_is_os_tree(const char *path) { - int r; - - assert(path); - - /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir - * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from - * the case where just the os-release file is missing. */ - if (laccess(path, F_OK) < 0) - return -errno; - - /* We use /usr/lib/os-release as flag file if something is an OS */ - r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL); - if (r == -ENOENT) { - - /* Also check for the old location in /etc, just in case. */ - r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL); - if (r == -ENOENT) - return 0; /* We got nothing */ - } - if (r < 0) - return r; - - return 1; -} - int files_same(const char *filea, const char *fileb, int flags) { struct stat a, b; diff --git a/src/systemd/src/basic/stat-util.h b/src/systemd/src/basic/stat-util.h index d1e8d330..f8014ed3 100644 --- a/src/systemd/src/basic/stat-util.h +++ b/src/systemd/src/basic/stat-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010-2012 Lennart Poettering -***/ - #include <stdbool.h> #include <stddef.h> #include <sys/stat.h> @@ -35,7 +29,6 @@ int null_or_empty_path(const char *fn); int null_or_empty_fd(int fd); int path_is_read_only_fs(const char *path); -int path_is_os_tree(const char *path); int files_same(const char *filea, const char *fileb, int flags); diff --git a/src/systemd/src/basic/stdio-util.h b/src/systemd/src/basic/stdio-util.h index 5330789a..73c03274 100644 --- a/src/systemd/src/basic/stdio-util.h +++ b/src/systemd/src/basic/stdio-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <printf.h> #include <stdarg.h> #include <stdio.h> diff --git a/src/systemd/src/basic/string-table.c b/src/systemd/src/basic/string-table.c index 34723b4c..94412ed2 100644 --- a/src/systemd/src/basic/string-table.c +++ b/src/systemd/src/basic/string-table.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h index a39206fb..9bd78793 100644 --- a/src/systemd/src/basic/string-table.h +++ b/src/systemd/src/basic/string-table.h @@ -2,12 +2,6 @@ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <errno.h> #include <stddef.h> #include <stdio.h> @@ -77,7 +71,6 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k return (type) -1; \ } \ - #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) @@ -102,3 +95,18 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) + +#define DUMP_STRING_TABLE(name,type,max) \ + do { \ + type _k; \ + flockfile(stdout); \ + for (_k = 0; _k < (max); _k++) { \ + const char *_t; \ + _t = name##_to_string(_k); \ + if (!_t) \ + continue; \ + fputs_unlocked(_t, stdout); \ + fputc_unlocked('\n', stdout); \ + } \ + funlockfile(stdout); \ + } while(false) diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c index a60c7dcc..1747f35b 100644 --- a/src/systemd/src/basic/string-util.c +++ b/src/systemd/src/basic/string-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -16,6 +11,7 @@ #include <string.h> #include "alloc-util.h" +#include "escape.h" #include "gunicode.h" #include "locale-util.h" #include "macro.h" @@ -271,23 +267,12 @@ char *strjoin_real(const char *x, ...) { } char *strstrip(char *s) { - char *e; - if (!s) return NULL; - /* Drops trailing whitespace. Modifies the string in - * place. Returns pointer to first non-space character */ - - s += strspn(s, WHITESPACE); - - for (e = strchr(s, 0); e > s; e --) - if (!strchr(WHITESPACE, e[-1])) - break; - - *e = 0; + /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */ - return s; + return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE); } char *delete_chars(char *s, const char *bad) { @@ -458,9 +443,23 @@ bool string_has_cc(const char *p, const char *ok) { } #if 0 /* NM_IGNORED */ +static int write_ellipsis(char *buf, bool unicode) { + if (unicode || is_locale_utf8()) { + buf[0] = 0xe2; /* tri-dot ellipsis: … */ + buf[1] = 0x80; + buf[2] = 0xa6; + } else { + buf[0] = '.'; + buf[1] = '.'; + buf[2] = '.'; + } + + return 3; +} + static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x, need_space; - char *r; + size_t x, need_space, suffix_len; + char *t; assert(s); assert(percent <= 100); @@ -496,8 +495,8 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le * either for the UTF-8 encoded character or for three ASCII characters. */ need_space = is_locale_utf8() ? 1 : 3; - r = new(char, new_length+3); - if (!r) + t = new(char, new_length+3); + if (!t) return NULL; assert(new_length >= need_space); @@ -505,23 +504,13 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le x = ((new_length - need_space) * percent + 50) / 100; assert(x <= new_length - need_space); - memcpy(r, s, x); - - if (is_locale_utf8()) { - r[x+0] = 0xe2; /* tri-dot ellipsis: … */ - r[x+1] = 0x80; - r[x+2] = 0xa6; - } else { - r[x+0] = '.'; - r[x+1] = '.'; - r[x+2] = '.'; - } - - memcpy(r + x + 3, - s + old_length - (new_length - x - need_space), - new_length - x - need_space + 1); + memcpy(t, s, x); + write_ellipsis(t + x, false); + suffix_len = new_length - x - need_space; + memcpy(t + x + 3, s + old_length - suffix_len, suffix_len); + *(t + x + 3 + suffix_len) = '\0'; - return r; + return t; } char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { @@ -552,42 +541,56 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne return strdup(""); /* If no multibyte characters use ascii_ellipsize_mem for speed */ - if (ascii_is_valid(s)) + if (ascii_is_valid_n(s, old_length)) return ascii_ellipsize_mem(s, old_length, new_length, percent); x = ((new_length - 1) * percent) / 100; assert(x <= new_length - 1); k = 0; - for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) { + for (i = s; i < s + old_length; i = utf8_next_char(i)) { char32_t c; + int w; r = utf8_encoded_to_unichar(i, &c); if (r < 0) return NULL; - k += unichar_iswide(c) ? 2 : 1; - } - if (k > x) /* last character was wide and went over quota */ - x++; + w = unichar_iswide(c) ? 2 : 1; + if (k + w <= x) + k += w; + else + break; + } - for (j = s + old_length; k < new_length && j > i; ) { + for (j = s + old_length; j > i; ) { char32_t c; + int w; + const char *jj; - j = utf8_prev_char(j); - r = utf8_encoded_to_unichar(j, &c); + jj = utf8_prev_char(j); + r = utf8_encoded_to_unichar(jj, &c); if (r < 0) return NULL; - k += unichar_iswide(c) ? 2 : 1; + + w = unichar_iswide(c) ? 2 : 1; + if (k + w <= new_length) { + k += w; + j = jj; + } else + break; } assert(i <= j); /* we don't actually need to ellipsize */ if (i == j) - return memdup(s, old_length + 1); + return memdup_suffix0(s, old_length); - /* make space for ellipsis */ - j = utf8_next_char(j); + /* make space for ellipsis, if possible */ + if (j < s + old_length) + j = utf8_next_char(j); + else if (i > s) + i = utf8_prev_char(i); len = i - s; len2 = s + old_length - j; @@ -601,21 +604,81 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne */ memcpy(e, s, len); - e[len + 0] = 0xe2; /* tri-dot ellipsis: … */ - e[len + 1] = 0x80; - e[len + 2] = 0xa6; - - memcpy(e + len + 3, j, len2 + 1); + write_ellipsis(e + len, true); + memcpy(e + len + 3, j, len2); + *(e + len + 3 + len2) = '\0'; return e; } -char *ellipsize(const char *s, size_t length, unsigned percent) { +char *cellescape(char *buf, size_t len, const char *s) { + /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII + * characters are copied as they are, everything else is escaped. The result + * is different then if escaping and ellipsization was performed in two + * separate steps, because each sequence is either stored in full or skipped. + * + * This function should be used for logging about strings which expected to + * be plain ASCII in a safe way. + * + * An ellipsis will be used if s is too long. It was always placed at the + * very end. + */ - if (length == (size_t) -1) - return strdup(s); + size_t i = 0, last_char_width[4] = {}, k = 0, j; + + assert(len > 0); /* at least a terminating NUL */ + + for (;;) { + char four[4]; + int w; + + if (*s == 0) /* terminating NUL detected? then we are done! */ + goto done; + + w = cescape_char(*s, four); + if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's + * ellipsize at the previous location */ + break; + + /* OK, there was space, let's add this escaped character to the buffer */ + memcpy(buf + i, four, w); + i += w; + + /* And remember its width in the ring buffer */ + last_char_width[k] = w; + k = (k + 1) % 4; + + s++; + } + + /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4 + * characters ideally, but the buffer is shorter than that in the first place take what we can get */ + for (j = 0; j < ELEMENTSOF(last_char_width); j++) { + + if (i + 4 <= len) /* nice, we reached our space goal */ + break; + + k = k == 0 ? 3 : k - 1; + if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */ + break; + + assert(i >= last_char_width[k]); + i -= last_char_width[k]; + } + + if (i + 4 <= len) /* yay, enough space */ + i += write_ellipsis(buf + i, false); + else if (i + 3 <= len) { /* only space for ".." */ + buf[i++] = '.'; + buf[i++] = '.'; + } else if (i + 2 <= len) /* only space for a single "." */ + buf[i++] = '.'; + else + assert(i + 1 <= len); - return ellipsize_mem(s, strlen(s), length, percent); + done: + buf[i] = '\0'; + return buf; } #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h index 5a10eeab..c0cc4e78 100644 --- a/src/systemd/src/basic/string-util.h +++ b/src/systemd/src/basic/string-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <alloca.h> #include <stdbool.h> #include <stddef.h> @@ -156,7 +150,14 @@ static inline bool _pure_ in_charset(const char *s, const char* charset) { bool string_has_cc(const char *p, const char *ok) _pure_; char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); -char *ellipsize(const char *s, size_t length, unsigned percent); +static inline char *ellipsize(const char *s, size_t length, unsigned percent) { + return ellipsize_mem(s, strlen(s), length, percent); +} + +char *cellescape(char *buf, size_t len, const char *s); + +/* This limit is arbitrary, enough to give some idea what the string contains */ +#define CELLESCAPE_DEFAULT_LENGTH 64 bool nulstr_contains(const char *nulstr, const char *needle); @@ -209,3 +210,21 @@ static inline size_t strlen_ptr(const char *s) { return strlen(s); } + +/* Like startswith(), but operates on arbitrary memory blocks */ +static inline void *memory_startswith(const void *p, size_t sz, const char *token) { + size_t n; + + assert(token); + + n = strlen(token); + if (sz < n) + return NULL; + + assert(p); + + if (memcmp(p, token, n) != 0) + return NULL; + + return (uint8_t*) p + n; +} diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c index 9d5000aa..9da8cece 100644 --- a/src/systemd/src/basic/strv.c +++ b/src/systemd/src/basic/strv.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h index 958c5f3a..51d03db9 100644 --- a/src/systemd/src/basic/strv.h +++ b/src/systemd/src/basic/strv.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <fnmatch.h> #include <stdarg.h> #include <stdbool.h> diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index fa9f66f2..aafa0375 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -285,8 +280,11 @@ static char *format_timestamp_internal( return NULL; /* Timestamp is unset */ /* Let's not format times with years > 9999 */ - if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) - return NULL; + if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { + assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1); + strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); + return buf; + } sec = (time_t) (t / USEC_PER_SEC); /* Round down */ @@ -485,7 +483,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { /* Let's see if we should shows this in dot notation */ if (t < USEC_PER_MINUTE && b > 0) { usec_t cc; - int j; + signed char j; j = 0; for (cc = table[i].usec; cc > 1; cc /= 10) @@ -1314,6 +1312,9 @@ bool timezone_is_valid(const char *name, int log_level) { if (slash) return false; + if (p - name >= PATH_MAX) + return false; + t = strjoina("/usr/share/zoneinfo/", name); fd = open(t, O_RDONLY|O_CLOEXEC); @@ -1467,4 +1468,28 @@ bool in_utc_timezone(void) { return timezone == 0 && daylight == 0; } + +int time_change_fd(void) { + + /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */ + static const struct itimerspec its = { + .it_value.tv_sec = TIME_T_MAX, + }; + + _cleanup_close_ int fd; + + assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); + + /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to + * CLOCK_MONOTONIC. */ + + fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) + return -errno; + + if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) + return -errno; + + return TAKE_FD(fd); +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h index e720688c..344f2dc5 100644 --- a/src/systemd/src/basic/time-util.h +++ b/src/systemd/src/basic/time-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <inttypes.h> #include <stdbool.h> #include <stddef.h> @@ -183,5 +177,7 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { /* With a 32bit time_t we can't go beyond 2038... */ #define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000) #else -#error "Yuck, time_t is neither 4 not 8 bytes wide?" +#error "Yuck, time_t is neither 4 nor 8 bytes wide?" #endif + +int time_change_fd(void); diff --git a/src/systemd/src/basic/umask-util.h b/src/systemd/src/basic/umask-util.h index ce705247..e964292e 100644 --- a/src/systemd/src/basic/umask-util.h +++ b/src/systemd/src/basic/umask-util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <stdbool.h> #include <sys/stat.h> #include <sys/types.h> diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c index 7a382204..80de43c5 100644 --- a/src/systemd/src/basic/utf8.c +++ b/src/systemd/src/basic/utf8.c @@ -1,9 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2008-2011 Kay Sievers - Copyright 2012 Lennart Poettering + Copyright © 2008-2011 Kay Sievers ***/ /* Parts of this file are based on the GLIB utf8 validation functions. The @@ -249,6 +246,9 @@ char *utf8_escape_non_printable(const char *str) { char *ascii_is_valid(const char *str) { const char *p; + /* Check whether the string consists of valid ASCII bytes, + * i.e values between 0 and 127, inclusive. */ + assert(str); for (p = str; *p; p++) @@ -258,6 +258,21 @@ char *ascii_is_valid(const char *str) { return (char*) str; } +char *ascii_is_valid_n(const char *str, size_t len) { + size_t i; + + /* Very similar to ascii_is_valid(), but checks exactly len + * bytes and rejects any NULs in that range. */ + + assert(str); + + for (i = 0; i < len; i++) + if ((unsigned char) str[i] >= 128 || str[i] == 0) + return NULL; + + return (char*) str; +} + /** * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 * @out_utf8: output buffer of at least 4 bytes or NULL diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h index 0ebe36d9..f37f87ff 100644 --- a/src/systemd/src/basic/utf8.h +++ b/src/systemd/src/basic/utf8.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering -***/ - #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -24,6 +18,7 @@ bool unichar_is_valid(char32_t c); const char *utf8_is_valid(const char *s) _pure_; char *ascii_is_valid(const char *s) _pure_; +char *ascii_is_valid_n(const char *str, size_t len); bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; #define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index 12e2f451..ba7474f0 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ #include "nm-sd-adapt.h" @@ -269,7 +264,7 @@ int container_get_leader(const char *machine, pid_t *pid) { return -EINVAL; p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); + r = parse_env_file(NULL, p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); if (r == -ENOENT) return -EHOSTDOWN; if (r < 0) @@ -410,6 +405,7 @@ uint64_t physical_memory(void) { uint64_t mem, lim; size_t ps; long sc; + int r; /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of * memory. @@ -423,13 +419,40 @@ uint64_t physical_memory(void) { ps = page_size(); mem = (uint64_t) sc * (uint64_t) ps; - if (cg_get_root_path(&root) < 0) + r = cg_get_root_path(&root); + if (r < 0) { + log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m"); return mem; + } - if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value)) + r = cg_all_unified(); + if (r < 0) { + log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m"); return mem; + } + if (r > 0) { + r = cg_get_attribute("memory", root, "memory.max", &value); + if (r < 0) { + log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m"); + return mem; + } + + if (streq(value, "max")) + return mem; + } else { + r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value); + if (r < 0) { + log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m"); + return mem; + } + } - if (safe_atou64(value, &lim) < 0) + r = safe_atou64(value, &lim); + if (r < 0) { + log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value); + return mem; + } + if (lim == UINT64_MAX) return mem; /* Make sure the limit is a multiple of our own page size */ @@ -470,6 +493,7 @@ uint64_t system_tasks_max(void) { uint64_t a = TASKS_MAX, b = TASKS_MAX; _cleanup_free_ char *root = NULL; + int r; /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this * limit: @@ -480,13 +504,24 @@ uint64_t system_tasks_max(void) { * * And then pick the smallest of the three */ - (void) procfs_tasks_get_limit(&a); + r = procfs_tasks_get_limit(&a); + if (r < 0) + log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m"); - if (cg_get_root_path(&root) >= 0) { + r = cg_get_root_path(&root); + if (r < 0) + log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); + else { _cleanup_free_ char *value = NULL; - if (cg_get_attribute("pids", root, "pids.max", &value) >= 0) - (void) safe_atou64(value, &b); + r = cg_get_attribute("pids", root, "pids.max", &value); + if (r < 0) + log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); + else if (!streq(value, "max")) { + r = safe_atou64(value, &b); + if (r < 0) + log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); + } } return MIN3(TASKS_MAX, diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index ad1931f7..9699d228 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering -***/ - #include <alloca.h> #include <errno.h> #include <fcntl.h> diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c index 26db7ea8..1b711c95 100644 --- a/src/systemd/src/libsystemd-network/arp-util.c +++ b/src/systemd/src/libsystemd-network/arp-util.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 2015 Tom Gundersen + Copyright © 2014 Axis Communications AB. All rights reserved. + Copyright © 2015 Tom Gundersen ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/arp-util.h b/src/systemd/src/libsystemd-network/arp-util.h index 86864e9f..10c68486 100644 --- a/src/systemd/src/libsystemd-network/arp-util.h +++ b/src/systemd/src/libsystemd-network/arp-util.h @@ -2,9 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright © 2014 Axis Communications AB. All rights reserved. ***/ #include <netinet/if_ether.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index 6f8adfa6..75357367 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2015 Tom Gundersen <teg@jklmen> + Copyright © 2015 Tom Gundersen <teg@jklmen> ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index c0a10b6c..42d4956d 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -2,9 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2015 Tom Gundersen <teg@jklmen> + Copyright © 2015 Tom Gundersen <teg@jklmen> ***/ #include "sd-id128.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h index f8276222..257a3c2e 100644 --- a/src/systemd/src/libsystemd-network/dhcp-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-internal.h @@ -2,10 +2,8 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - Copyright (C) 2014 Tom Gundersen + Copyright © 2013 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen ***/ #include <linux/if_packet.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 66c213e2..fabac183 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -2,10 +2,8 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - Copyright (C) 2014 Tom Gundersen + Copyright © 2013 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen ***/ #include <stdint.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 37c1ba19..90fe29d0 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. + Copyright © 2013 Intel Corporation. All rights reserved. ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c index 86e67358..4a4f7de0 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. + Copyright © 2013 Intel Corporation. All rights reserved. ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c index 136bab9d..214aed8e 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - Copyright (C) 2014 Tom Gundersen + Copyright © 2013 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h index 2230e094..f0366324 100644 --- a/src/systemd/src/libsystemd-network/dhcp-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h @@ -2,9 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. + Copyright © 2013 Intel Corporation. All rights reserved. ***/ #include <netinet/ip.h> diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 33c43320..f1cbd6a4 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -2,9 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2014-2015 Intel Corporation. All rights reserved. + Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ #include <net/ethernet.h> diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h index 2bf54997..dc85c437 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h @@ -2,10 +2,8 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014-2015 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen + Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ #include <stdint.h> diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index 2c6dc713..98aa6261 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright © 2014 Intel Corporation. All rights reserved. ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index 87ae96a2..a8a56463 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014-2015 Intel Corporation. All rights reserved. + Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ #include "nm-sd-adapt.h" @@ -143,7 +141,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { } int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { - uint8_t buffer[1 + DNS_WIRE_FOMAT_HOSTNAME_MAX]; + uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX]; int r; assert_return(buf && *buf && buflen && fqdn, -EINVAL); diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h index 18417062..ffae4453 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h @@ -2,9 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright © 2014 Intel Corporation. All rights reserved. ***/ #include <netinet/ip6.h> diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h index 06cf7e8d..e5650988 100644 --- a/src/systemd/src/libsystemd-network/lldp-internal.h +++ b/src/systemd/src/libsystemd-network/lldp-internal.h @@ -2,10 +2,8 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani + Copyright © 2014 Tom Gundersen + Copyright © 2014 Susant Sahani ***/ #include "sd-event.h" diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index 373e34c8..c28c9956 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -1,9 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - This file is part of systemd. - - Copyright 2016 Lennart Poettering -***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h index fae49c59..494bc517 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.h +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - This file is part of systemd. - - Copyright 2016 Lennart Poettering -***/ - #include <inttypes.h> #include <stdbool.h> #include <sys/types.h> diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c index 307cf6c3..bc6c20d1 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.c +++ b/src/systemd/src/libsystemd-network/lldp-network.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani + Copyright © 2014 Tom Gundersen + Copyright © 2014 Susant Sahani ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/lldp-network.h b/src/systemd/src/libsystemd-network/lldp-network.h index 7a1e8b10..1d773acc 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.h +++ b/src/systemd/src/libsystemd-network/lldp-network.h @@ -2,10 +2,8 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani + Copyright © 2014 Tom Gundersen + Copyright © 2014 Susant Sahani ***/ #include "sd-event.h" diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index e05f52d4..308762ee 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen <teg@jklm.no> + Copyright © 2013 Tom Gundersen <teg@jklm.no> ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index d0076f45..ef69e701 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -2,9 +2,7 @@ #pragma once /*** - This file is part of systemd. - - Copyright (C) 2013 Tom Gundersen <teg@jklm.no> + Copyright © 2013 Tom Gundersen <teg@jklm.no> ***/ #include <stdbool.h> @@ -12,6 +10,7 @@ #include "sd-dhcp-lease.h" #include "condition.h" +#include "conf-parser.h" #include "set.h" #include "udev.h" @@ -35,33 +34,15 @@ bool net_match_config(Set *match_mac, const char *dev_type, const char *dev_name); -int config_parse_net_condition(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_hwaddr(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_hwaddrs(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); - -int config_parse_iaid(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_bridge_port_priority(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); +#if 0 /* NM_IGNORED */ +CONFIG_PARSER_PROTOTYPE(config_parse_net_condition); +CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); +CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); +CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); +CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); +CONFIG_PARSER_PROTOTYPE(config_parse_iaid); +CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); +#endif /* NM_IGNORED */ int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result); const char *net_get_name(struct udev_device *device); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index ef2620f4..c2f81e1c 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. + Copyright © 2013 Intel Corporation. All rights reserved. ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index d92c5126..33a0796a 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - Copyright (C) 2014 Tom Gundersen + Copyright © 2013 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen ***/ #include "nm-sd-adapt.h" @@ -1036,7 +1034,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (r < 0) return r; - r = parse_env_file(lease_file, NEWLINE, + r = parse_env_file(NULL, lease_file, NEWLINE, "ADDRESS", &address, "ROUTER", &router, "NETMASK", &netmask, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index 1cec6022..ca03f580 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014-2015 Intel Corporation. All rights reserved. + Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index 3952cd99..1c8346d4 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014-2015 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen + Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index ed98c0b6..3766c1f3 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 2015 Tom Gundersen + Copyright © 2014 Axis Communications AB. All rights reserved. + Copyright © 2015 Tom Gundersen ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index 001ebbd8..ca1e0bdb 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 2015 Tom Gundersen + Copyright © 2014 Axis Communications AB. All rights reserved. + Copyright © 2015 Tom Gundersen ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index d88a0c6f..ea1dda4d 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani + Copyright © 2014 Tom Gundersen + Copyright © 2014 Susant Sahani ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 8a9b8055..f4d5dbe4 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -1,8 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering ***/ #include "nm-sd-adapt.h" @@ -17,6 +14,7 @@ #include "alloc-util.h" #include "fd-util.h" +#include "fs-util.h" #include "hashmap.h" #include "list.h" #include "macro.h" @@ -45,6 +43,7 @@ typedef enum EventSourceType { SOURCE_POST, SOURCE_EXIT, SOURCE_WATCHDOG, + SOURCE_INOTIFY, _SOURCE_EVENT_SOURCE_TYPE_MAX, _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 } EventSourceType; @@ -62,6 +61,7 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] [SOURCE_POST] = "post", [SOURCE_EXIT] = "exit", [SOURCE_WATCHDOG] = "watchdog", + [SOURCE_INOTIFY] = "inotify", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); @@ -73,12 +73,15 @@ typedef enum WakeupType { WAKEUP_EVENT_SOURCE, WAKEUP_CLOCK_DATA, WAKEUP_SIGNAL_DATA, + WAKEUP_INOTIFY_DATA, _WAKEUP_TYPE_MAX, _WAKEUP_TYPE_INVALID = -1, } WakeupType; #define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) +struct inode_data; + struct sd_event_source { WakeupType wakeup; @@ -102,6 +105,8 @@ struct sd_event_source { uint64_t pending_iteration; uint64_t prepare_iteration; + sd_event_destroy_t destroy_callback; + LIST_FIELDS(sd_event_source, sources); union { @@ -140,6 +145,12 @@ struct sd_event_source { sd_event_handler_t callback; unsigned prioq_index; } exit; + struct { + sd_event_inotify_handler_t callback; + uint32_t mask; + struct inode_data *inode_data; + LIST_FIELDS(sd_event_source, by_inode_data); + } inotify; }; }; @@ -174,6 +185,64 @@ struct signal_data { sd_event_source *current; }; +/* A structure listing all event sources currently watching a specific inode */ +struct inode_data { + /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */ + ino_t ino; + dev_t dev; + + /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can + * rearrange the priority still until then, as we need the original inode to change the priority as we need to + * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the + * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of + * the sd-event object, so that it is efficient to close everything, before entering the next event loop + * iteration. */ + int fd; + + /* The inotify "watch descriptor" */ + int wd; + + /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has + * most recently been set on the watch descriptor. */ + uint32_t combined_mask; + + /* All event sources subscribed to this inode */ + LIST_HEAD(sd_event_source, event_sources); + + /* The inotify object we watch this inode with */ + struct inotify_data *inotify_data; + + /* A linked list of all inode data objects with fds to close (see above) */ + LIST_FIELDS(struct inode_data, to_close); +}; + +/* A structure encapsulating an inotify fd */ +struct inotify_data { + WakeupType wakeup; + + /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at + * a time */ + + int fd; + int64_t priority; + + Hashmap *inodes; /* The inode_data structures keyed by dev+ino */ + Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */ + + /* The buffer we read inotify events into */ + union inotify_event_buffer buffer; + size_t buffer_filled; /* fill level of the buffer */ + + /* How many event sources are currently marked pending for this inotify. We won't read new events off the + * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing + * the events locally if they can't be coalesced). */ + unsigned n_pending; + + /* A linked list of all inotify objects with data already read, that still need processing. We keep this list + * to make it efficient to figure out what inotify objects to process data on next. */ + LIST_FIELDS(struct inotify_data, buffered); +}; + struct sd_event { unsigned n_ref; @@ -204,6 +273,14 @@ struct sd_event { Prioq *exit; + Hashmap *inotify_data; /* indexed by priority */ + + /* A list of inode structures that still have an fd open, that we need to close before the next loop iteration */ + LIST_HEAD(struct inode_data, inode_data_to_close); + + /* A list of inotify objects that already have events buffered which aren't processed yet */ + LIST_HEAD(struct inotify_data, inotify_data_buffered); + pid_t original_pid; uint64_t iteration; @@ -233,6 +310,7 @@ struct sd_event { static thread_local sd_event *default_event = NULL; static void source_disconnect(sd_event_source *s); +static void event_gc_inode_data(sd_event *e, struct inode_data *d); static sd_event *event_resolve(sd_event *e) { return e == SD_EVENT_DEFAULT ? default_event : e; @@ -414,6 +492,8 @@ static void event_free(sd_event *e) { free(e->signal_sources); hashmap_free(e->signal_data); + hashmap_free(e->inotify_data); + hashmap_free(e->child_sources); set_free(e->post_sources); free(e); @@ -425,16 +505,32 @@ _public_ int sd_event_new(sd_event** ret) { assert_return(ret, -EINVAL); - e = new0(sd_event, 1); + e = new(sd_event, 1); if (!e) return -ENOMEM; - e->n_ref = 1; - e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; - e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY; - e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA; - e->original_pid = getpid_cached(); - e->perturb = USEC_INFINITY; + *e = (sd_event) { + .n_ref = 1, + .epoll_fd = -1, + .watchdog_fd = -1, + .realtime.wakeup = WAKEUP_CLOCK_DATA, + .realtime.fd = -1, + .realtime.next = USEC_INFINITY, + .boottime.wakeup = WAKEUP_CLOCK_DATA, + .boottime.fd = -1, + .boottime.next = USEC_INFINITY, + .monotonic.wakeup = WAKEUP_CLOCK_DATA, + .monotonic.fd = -1, + .monotonic.next = USEC_INFINITY, + .realtime_alarm.wakeup = WAKEUP_CLOCK_DATA, + .realtime_alarm.fd = -1, + .realtime_alarm.next = USEC_INFINITY, + .boottime_alarm.wakeup = WAKEUP_CLOCK_DATA, + .boottime_alarm.fd = -1, + .boottime_alarm.next = USEC_INFINITY, + .perturb = USEC_INFINITY, + .original_pid = getpid_cached(), + }; r = prioq_ensure_allocated(&e->pending, pending_prioq_compare); if (r < 0) @@ -520,18 +616,17 @@ static int source_io_register( int enabled, uint32_t events) { - struct epoll_event ev = {}; + struct epoll_event ev; int r; assert(s); assert(s->type == SOURCE_IO); assert(enabled != SD_EVENT_OFF); - ev.events = events; - ev.data.ptr = s; - - if (enabled == SD_EVENT_ONESHOT) - ev.events |= EPOLLONESHOT; + ev = (struct epoll_event) { + .events = events | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0), + .data.ptr = s, + }; if (s->io.registered) r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev); @@ -623,7 +718,7 @@ static int event_make_signal_data( int sig, struct signal_data **ret) { - struct epoll_event ev = {}; + struct epoll_event ev; struct signal_data *d; bool added = false; sigset_t ss_copy; @@ -638,7 +733,7 @@ static int event_make_signal_data( if (e->signal_sources && e->signal_sources[sig]) priority = e->signal_sources[sig]->priority; else - priority = 0; + priority = SD_EVENT_PRIORITY_NORMAL; d = hashmap_get(e->signal_data, &priority); if (d) { @@ -652,13 +747,15 @@ static int event_make_signal_data( if (r < 0) return r; - d = new0(struct signal_data, 1); + d = new(struct signal_data, 1); if (!d) return -ENOMEM; - d->wakeup = WAKEUP_SIGNAL_DATA; - d->fd = -1; - d->priority = priority; + *d = (struct signal_data) { + .wakeup = WAKEUP_SIGNAL_DATA, + .fd = -1, + .priority = priority, + }; r = hashmap_put(e->signal_data, &d->priority, d); if (r < 0) { @@ -688,8 +785,10 @@ static int event_make_signal_data( d->fd = fd_move_above_stdio(r); - ev.events = EPOLLIN; - ev.data.ptr = d; + ev = (struct epoll_event) { + .events = EPOLLIN, + .data.ptr = d, + }; r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev); if (r < 0) { @@ -856,6 +955,41 @@ static void source_disconnect(sd_event_source *s) { prioq_remove(s->event->exit, s, &s->exit.prioq_index); break; + case SOURCE_INOTIFY: { + struct inode_data *inode_data; + + inode_data = s->inotify.inode_data; + if (inode_data) { + struct inotify_data *inotify_data; + assert_se(inotify_data = inode_data->inotify_data); + + /* Detach this event source from the inode object */ + LIST_REMOVE(inotify.by_inode_data, inode_data->event_sources, s); + s->inotify.inode_data = NULL; + + if (s->pending) { + assert(inotify_data->n_pending > 0); + inotify_data->n_pending--; + } + + /* Note that we don't reduce the inotify mask for the watch descriptor here if the inode is + * continued to being watched. That's because inotify doesn't really have an API for that: we + * can only change watch masks with access to the original inode either by fd or by path. But + * paths aren't stable, and keeping an O_PATH fd open all the time would mean wasting an fd + * continously and keeping the mount busy which we can't really do. We could reconstruct the + * original inode from /proc/self/fdinfo/$INOTIFY_FD (as all watch descriptors are listed + * there), but given the need for open_by_handle_at() which is privileged and not universally + * available this would be quite an incomplete solution. Hence we go the other way, leave the + * mask set, even if it is not minimized now, and ignore all events we aren't interested in + * anymore after reception. Yes, this sucks, but … Linux … */ + + /* Maybe release the inode data (and its inotify) */ + event_gc_inode_data(s->event, inode_data); + } + + break; + } + default: assert_not_reached("Wut? I shouldn't exist."); } @@ -883,7 +1017,10 @@ static void source_free(sd_event_source *s) { source_disconnect(s); if (s->type == SOURCE_IO && s->io.owned) - safe_close(s->io.fd); + s->io.fd = safe_close(s->io.fd); + + if (s->destroy_callback) + s->destroy_callback(s->userdata); free(s->description); free(s); @@ -930,6 +1067,19 @@ static int source_set_pending(sd_event_source *s, bool b) { d->current = NULL; } + if (s->type == SOURCE_INOTIFY) { + + assert(s->inotify.inode_data); + assert(s->inotify.inode_data->inotify_data); + + if (b) + s->inotify.inode_data->inotify_data->n_pending ++; + else { + assert(s->inotify.inode_data->inotify_data->n_pending > 0); + s->inotify.inode_data->inotify_data->n_pending --; + } + } + return 0; } @@ -938,15 +1088,18 @@ static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType t assert(e); - s = new0(sd_event_source, 1); + s = new(sd_event_source, 1); if (!s) return NULL; - s->n_ref = 1; - s->event = e; - s->floating = floating; - s->type = type; - s->pending_index = s->prepare_index = PRIOQ_IDX_NULL; + *s = (struct sd_event_source) { + .n_ref = 1, + .event = e, + .floating = floating, + .type = type, + .pending_index = PRIOQ_IDX_NULL, + .prepare_index = PRIOQ_IDX_NULL, + }; if (!floating) sd_event_ref(e); @@ -1023,7 +1176,7 @@ static int event_setup_timer_fd( struct clock_data *d, clockid_t clock) { - struct epoll_event ev = {}; + struct epoll_event ev; int r, fd; assert(e); @@ -1038,8 +1191,10 @@ static int event_setup_timer_fd( fd = fd_move_above_stdio(fd); - ev.events = EPOLLIN; - ev.data.ptr = d; + ev = (struct epoll_event) { + .events = EPOLLIN, + .data.ptr = d, + }; r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev); if (r < 0) { @@ -1378,6 +1533,405 @@ _public_ int sd_event_add_exit( return 0; } +static void event_free_inotify_data(sd_event *e, struct inotify_data *d) { + assert(e); + + if (!d) + return; + + assert(hashmap_isempty(d->inodes)); + assert(hashmap_isempty(d->wd)); + + if (d->buffer_filled > 0) + LIST_REMOVE(buffered, e->inotify_data_buffered, d); + + hashmap_free(d->inodes); + hashmap_free(d->wd); + + assert_se(hashmap_remove(e->inotify_data, &d->priority) == d); + + if (d->fd >= 0) { + if (epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, d->fd, NULL) < 0) + log_debug_errno(errno, "Failed to remove inotify fd from epoll, ignoring: %m"); + + safe_close(d->fd); + } + free(d); +} + +static int event_make_inotify_data( + sd_event *e, + int64_t priority, + struct inotify_data **ret) { + + _cleanup_close_ int fd = -1; + struct inotify_data *d; + struct epoll_event ev; + int r; + + assert(e); + + d = hashmap_get(e->inotify_data, &priority); + if (d) { + if (ret) + *ret = d; + return 0; + } + + fd = inotify_init1(IN_NONBLOCK|O_CLOEXEC); + if (fd < 0) + return -errno; + + fd = fd_move_above_stdio(fd); + + r = hashmap_ensure_allocated(&e->inotify_data, &uint64_hash_ops); + if (r < 0) + return r; + + d = new(struct inotify_data, 1); + if (!d) + return -ENOMEM; + + *d = (struct inotify_data) { + .wakeup = WAKEUP_INOTIFY_DATA, + .fd = TAKE_FD(fd), + .priority = priority, + }; + + r = hashmap_put(e->inotify_data, &d->priority, d); + if (r < 0) { + d->fd = safe_close(d->fd); + free(d); + return r; + } + + ev = (struct epoll_event) { + .events = EPOLLIN, + .data.ptr = d, + }; + + if (epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev) < 0) { + r = -errno; + d->fd = safe_close(d->fd); /* let's close this ourselves, as event_free_inotify_data() would otherwise + * remove the fd from the epoll first, which we don't want as we couldn't + * add it in the first place. */ + event_free_inotify_data(e, d); + return r; + } + + if (ret) + *ret = d; + + return 1; +} + +static int inode_data_compare(const void *a, const void *b) { + const struct inode_data *x = a, *y = b; + + assert(x); + assert(y); + + if (x->dev < y->dev) + return -1; + if (x->dev > y->dev) + return 1; + + if (x->ino < y->ino) + return -1; + if (x->ino > y->ino) + return 1; + + return 0; +} + +static void inode_data_hash_func(const void *p, struct siphash *state) { + const struct inode_data *d = p; + + assert(p); + + siphash24_compress(&d->dev, sizeof(d->dev), state); + siphash24_compress(&d->ino, sizeof(d->ino), state); +} + +const struct hash_ops inode_data_hash_ops = { + .hash = inode_data_hash_func, + .compare = inode_data_compare +}; + +static void event_free_inode_data( + sd_event *e, + struct inode_data *d) { + + assert(e); + + if (!d) + return; + + assert(!d->event_sources); + + if (d->fd >= 0) { + LIST_REMOVE(to_close, e->inode_data_to_close, d); + safe_close(d->fd); + } + + if (d->inotify_data) { + + if (d->wd >= 0) { + if (d->inotify_data->fd >= 0) { + /* So here's a problem. At the time this runs the watch descriptor might already be + * invalidated, because an IN_IGNORED event might be queued right the moment we enter + * the syscall. Hence, whenever we get EINVAL, ignore it entirely, since it's a very + * likely case to happen. */ + + if (inotify_rm_watch(d->inotify_data->fd, d->wd) < 0 && errno != EINVAL) + log_debug_errno(errno, "Failed to remove watch descriptor %i from inotify, ignoring: %m", d->wd); + } + + assert_se(hashmap_remove(d->inotify_data->wd, INT_TO_PTR(d->wd)) == d); + } + + assert_se(hashmap_remove(d->inotify_data->inodes, d) == d); + } + + free(d); +} + +static void event_gc_inode_data( + sd_event *e, + struct inode_data *d) { + + struct inotify_data *inotify_data; + + assert(e); + + if (!d) + return; + + if (d->event_sources) + return; + + inotify_data = d->inotify_data; + event_free_inode_data(e, d); + + if (inotify_data && hashmap_isempty(inotify_data->inodes)) + event_free_inotify_data(e, inotify_data); +} + +static int event_make_inode_data( + sd_event *e, + struct inotify_data *inotify_data, + dev_t dev, + ino_t ino, + struct inode_data **ret) { + + struct inode_data *d, key; + int r; + + assert(e); + assert(inotify_data); + + key = (struct inode_data) { + .ino = ino, + .dev = dev, + }; + + d = hashmap_get(inotify_data->inodes, &key); + if (d) { + if (ret) + *ret = d; + + return 0; + } + + r = hashmap_ensure_allocated(&inotify_data->inodes, &inode_data_hash_ops); + if (r < 0) + return r; + + d = new(struct inode_data, 1); + if (!d) + return -ENOMEM; + + *d = (struct inode_data) { + .dev = dev, + .ino = ino, + .wd = -1, + .fd = -1, + .inotify_data = inotify_data, + }; + + r = hashmap_put(inotify_data->inodes, d, d); + if (r < 0) { + free(d); + return r; + } + + if (ret) + *ret = d; + + return 1; +} + +static uint32_t inode_data_determine_mask(struct inode_data *d) { + bool excl_unlink = true; + uint32_t combined = 0; + sd_event_source *s; + + assert(d); + + /* Combines the watch masks of all event sources watching this inode. We generally just OR them together, but + * the IN_EXCL_UNLINK flag is ANDed instead. + * + * Note that we add all sources to the mask here, regardless whether enabled, disabled or oneshot. That's + * because we cannot change the mask anymore after the event source was created once, since the kernel has no + * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and supress + * events we don't care for client-side. */ + + LIST_FOREACH(inotify.by_inode_data, s, d->event_sources) { + + if ((s->inotify.mask & IN_EXCL_UNLINK) == 0) + excl_unlink = false; + + combined |= s->inotify.mask; + } + + return (combined & ~(IN_ONESHOT|IN_DONT_FOLLOW|IN_ONLYDIR|IN_EXCL_UNLINK)) | (excl_unlink ? IN_EXCL_UNLINK : 0); +} + +static int inode_data_realize_watch(sd_event *e, struct inode_data *d) { + uint32_t combined_mask; + int wd, r; + + assert(d); + assert(d->fd >= 0); + + combined_mask = inode_data_determine_mask(d); + + if (d->wd >= 0 && combined_mask == d->combined_mask) + return 0; + + r = hashmap_ensure_allocated(&d->inotify_data->wd, NULL); + if (r < 0) + return r; + + wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask); + if (wd < 0) + return -errno; + + if (d->wd < 0) { + r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d); + if (r < 0) { + (void) inotify_rm_watch(d->inotify_data->fd, wd); + return r; + } + + d->wd = wd; + + } else if (d->wd != wd) { + + log_debug("Weird, the watch descriptor we already knew for this inode changed?"); + (void) inotify_rm_watch(d->fd, wd); + return -EINVAL; + } + + d->combined_mask = combined_mask; + return 1; +} + +_public_ int sd_event_add_inotify( + sd_event *e, + sd_event_source **ret, + const char *path, + uint32_t mask, + sd_event_inotify_handler_t callback, + void *userdata) { + + bool rm_inotify = false, rm_inode = false; + struct inotify_data *inotify_data = NULL; + struct inode_data *inode_data = NULL; + _cleanup_close_ int fd = -1; + sd_event_source *s; + struct stat st; + int r; + + assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); + assert_return(path, -EINVAL); + assert_return(callback, -EINVAL); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(!event_pid_changed(e), -ECHILD); + + /* Refuse IN_MASK_ADD since we coalesce watches on the same inode, and hence really don't want to merge + * masks. Or in other words, this whole code exists only to manage IN_MASK_ADD type operations for you, hence + * the user can't use them for us. */ + if (mask & IN_MASK_ADD) + return -EINVAL; + + fd = open(path, O_PATH|O_CLOEXEC| + (mask & IN_ONLYDIR ? O_DIRECTORY : 0)| + (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0)); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + s = source_new(e, !ret, SOURCE_INOTIFY); + if (!s) + return -ENOMEM; + + s->enabled = mask & IN_ONESHOT ? SD_EVENT_ONESHOT : SD_EVENT_ON; + s->inotify.mask = mask; + s->inotify.callback = callback; + s->userdata = userdata; + + /* Allocate an inotify object for this priority, and an inode object within it */ + r = event_make_inotify_data(e, SD_EVENT_PRIORITY_NORMAL, &inotify_data); + if (r < 0) + goto fail; + rm_inotify = r > 0; + + r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data); + if (r < 0) + goto fail; + rm_inode = r > 0; + + /* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of + * the event source, until then, for which we need the original inode. */ + if (inode_data->fd < 0) { + inode_data->fd = TAKE_FD(fd); + LIST_PREPEND(to_close, e->inode_data_to_close, inode_data); + } + + /* Link our event source to the inode data object */ + LIST_PREPEND(inotify.by_inode_data, inode_data->event_sources, s); + s->inotify.inode_data = inode_data; + + rm_inode = rm_inotify = false; + + /* Actually realize the watch now */ + r = inode_data_realize_watch(e, inode_data); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(s, path); + + if (ret) + *ret = s; + + return 0; + +fail: + source_free(s); + + if (rm_inode) + event_free_inode_data(e, inode_data); + + if (rm_inotify) + event_free_inotify_data(e, inotify_data); + + return r; +} + _public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { if (!s) @@ -1532,6 +2086,10 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) if (s->io.events == events && !(events & EPOLLET)) return 0; + r = source_set_pending(s, false); + if (r < 0) + return r; + if (s->enabled != SD_EVENT_OFF) { r = source_io_register(s, s->enabled, events); if (r < 0) @@ -1539,7 +2097,6 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) } s->io.events = events; - source_set_pending(s, false); return 0; } @@ -1572,6 +2129,9 @@ _public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) } _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) { + bool rm_inotify = false, rm_inode = false; + struct inotify_data *new_inotify_data = NULL; + struct inode_data *new_inode_data = NULL; int r; assert_return(s, -EINVAL); @@ -1581,7 +2141,59 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) if (s->priority == priority) return 0; - if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { + if (s->type == SOURCE_INOTIFY) { + struct inode_data *old_inode_data; + + assert(s->inotify.inode_data); + old_inode_data = s->inotify.inode_data; + + /* We need the original fd to change the priority. If we don't have it we can't change the priority, + * anymore. Note that we close any fds when entering the next event loop iteration, i.e. for inotify + * events we allow priority changes only until the first following iteration. */ + if (old_inode_data->fd < 0) + return -EOPNOTSUPP; + + r = event_make_inotify_data(s->event, priority, &new_inotify_data); + if (r < 0) + return r; + rm_inotify = r > 0; + + r = event_make_inode_data(s->event, new_inotify_data, old_inode_data->dev, old_inode_data->ino, &new_inode_data); + if (r < 0) + goto fail; + rm_inode = r > 0; + + if (new_inode_data->fd < 0) { + /* Duplicate the fd for the new inode object if we don't have any yet */ + new_inode_data->fd = fcntl(old_inode_data->fd, F_DUPFD_CLOEXEC, 3); + if (new_inode_data->fd < 0) { + r = -errno; + goto fail; + } + + LIST_PREPEND(to_close, s->event->inode_data_to_close, new_inode_data); + } + + /* Move the event source to the new inode data structure */ + LIST_REMOVE(inotify.by_inode_data, old_inode_data->event_sources, s); + LIST_PREPEND(inotify.by_inode_data, new_inode_data->event_sources, s); + s->inotify.inode_data = new_inode_data; + + /* Now create the new watch */ + r = inode_data_realize_watch(s->event, new_inode_data); + if (r < 0) { + /* Move it back */ + LIST_REMOVE(inotify.by_inode_data, new_inode_data->event_sources, s); + LIST_PREPEND(inotify.by_inode_data, old_inode_data->event_sources, s); + s->inotify.inode_data = old_inode_data; + goto fail; + } + + s->priority = priority; + + event_gc_inode_data(s->event, old_inode_data); + + } else if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { struct signal_data *old, *d; /* Move us from the signalfd belonging to the old @@ -1611,6 +2223,15 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); return 0; + +fail: + if (rm_inode) + event_free_inode_data(s->event, new_inode_data); + + if (rm_inotify) + event_free_inotify_data(s->event, new_inotify_data); + + return r; } _public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { @@ -1639,6 +2260,13 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { if (m == SD_EVENT_OFF) { + /* Unset the pending flag when this event source is disabled */ + if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { + r = source_set_pending(s, false); + if (r < 0) + return r; + } + switch (s->type) { case SOURCE_IO: @@ -1685,6 +2313,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { case SOURCE_DEFER: case SOURCE_POST: + case SOURCE_INOTIFY: s->enabled = m; break; @@ -1693,6 +2322,14 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { } } else { + + /* Unset the pending flag when this event source is enabled */ + if (s->enabled == SD_EVENT_OFF && !IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { + r = source_set_pending(s, false); + if (r < 0) + return r; + } + switch (s->type) { case SOURCE_IO: @@ -1757,6 +2394,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { case SOURCE_DEFER: case SOURCE_POST: + case SOURCE_INOTIFY: s->enabled = m; break; @@ -1786,15 +2424,18 @@ _public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { _public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { struct clock_data *d; + int r; assert_return(s, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(s->event), -ECHILD); - s->time.next = usec; + r = source_set_pending(s, false); + if (r < 0) + return r; - source_set_pending(s, false); + s->time.next = usec; d = event_get_clock_data(s->event, s->type); assert(d); @@ -1818,6 +2459,7 @@ _public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *use _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { struct clock_data *d; + int r; assert_return(s, -EINVAL); assert_return(usec != (uint64_t) -1, -EINVAL); @@ -1825,13 +2467,15 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(s->event), -ECHILD); + r = source_set_pending(s, false); + if (r < 0) + return r; + if (usec == 0) usec = DEFAULT_ACCURACY_USEC; s->time.accuracy = usec; - source_set_pending(s, false); - d = event_get_clock_data(s->event, s->type); assert(d); @@ -1861,6 +2505,16 @@ _public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { return 0; } +_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) { + assert_return(s, -EINVAL); + assert_return(mask, -EINVAL); + assert_return(s->type == SOURCE_INOTIFY, -EDOM); + assert_return(!event_pid_changed(s->event), -ECHILD); + + *mask = s->inotify.mask; + return 0; +} + _public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) { int r; @@ -2194,6 +2848,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { int r; assert(e); + assert(d); assert_return(events == EPOLLIN, -EIO); /* If there's a signal queued on this priority and SIGCHLD is @@ -2250,6 +2905,160 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { } } +static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents) { + ssize_t n; + + assert(e); + assert(d); + + assert_return(revents == EPOLLIN, -EIO); + + /* If there's already an event source pending for this priority, don't read another */ + if (d->n_pending > 0) + return 0; + + /* Is the read buffer non-empty? If so, let's not read more */ + if (d->buffer_filled > 0) + return 0; + + n = read(d->fd, &d->buffer, sizeof(d->buffer)); + if (n < 0) { + if (IN_SET(errno, EAGAIN, EINTR)) + return 0; + + return -errno; + } + + assert(n > 0); + d->buffer_filled = (size_t) n; + LIST_PREPEND(buffered, e->inotify_data_buffered, d); + + return 1; +} + +static void event_inotify_data_drop(sd_event *e, struct inotify_data *d, size_t sz) { + assert(e); + assert(d); + assert(sz <= d->buffer_filled); + + if (sz == 0) + return; + + /* Move the rest to the buffer to the front, in order to get things properly aligned again */ + memmove(d->buffer.raw, d->buffer.raw + sz, d->buffer_filled - sz); + d->buffer_filled -= sz; + + if (d->buffer_filled == 0) + LIST_REMOVE(buffered, e->inotify_data_buffered, d); +} + +static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { + int r; + + assert(e); + assert(d); + + /* If there's already an event source pending for this priority, don't read another */ + if (d->n_pending > 0) + return 0; + + while (d->buffer_filled > 0) { + size_t sz; + + /* Let's validate that the event structures are complete */ + if (d->buffer_filled < offsetof(struct inotify_event, name)) + return -EIO; + + sz = offsetof(struct inotify_event, name) + d->buffer.ev.len; + if (d->buffer_filled < sz) + return -EIO; + + if (d->buffer.ev.mask & IN_Q_OVERFLOW) { + struct inode_data *inode_data; + Iterator i; + + /* The queue overran, let's pass this event to all event sources connected to this inotify + * object */ + + HASHMAP_FOREACH(inode_data, d->inodes, i) { + sd_event_source *s; + + LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { + + if (s->enabled == SD_EVENT_OFF) + continue; + + r = source_set_pending(s, true); + if (r < 0) + return r; + } + } + } else { + struct inode_data *inode_data; + sd_event_source *s; + + /* Find the inode object for this watch descriptor. If IN_IGNORED is set we also remove it from + * our watch descriptor table. */ + if (d->buffer.ev.mask & IN_IGNORED) { + + inode_data = hashmap_remove(d->wd, INT_TO_PTR(d->buffer.ev.wd)); + if (!inode_data) { + event_inotify_data_drop(e, d, sz); + continue; + } + + /* The watch descriptor was removed by the kernel, let's drop it here too */ + inode_data->wd = -1; + } else { + inode_data = hashmap_get(d->wd, INT_TO_PTR(d->buffer.ev.wd)); + if (!inode_data) { + event_inotify_data_drop(e, d, sz); + continue; + } + } + + /* Trigger all event sources that are interested in these events. Also trigger all event + * sources if IN_IGNORED or IN_UNMOUNT is set. */ + LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { + + if (s->enabled == SD_EVENT_OFF) + continue; + + if ((d->buffer.ev.mask & (IN_IGNORED|IN_UNMOUNT)) == 0 && + (s->inotify.mask & d->buffer.ev.mask & IN_ALL_EVENTS) == 0) + continue; + + r = source_set_pending(s, true); + if (r < 0) + return r; + } + } + + /* Something pending now? If so, let's finish, otherwise let's read more. */ + if (d->n_pending > 0) + return 1; + } + + return 0; +} + +static int process_inotify(sd_event *e) { + struct inotify_data *d; + int r, done = 0; + + assert(e); + + LIST_FOREACH(buffered, d, e->inotify_data_buffered) { + r = event_inotify_data_process(e, d); + if (r < 0) + return r; + if (r > 0) + done ++; + } + + return done; +} + static int source_dispatch(sd_event_source *s) { EventSourceType saved_type; int r = 0; @@ -2319,7 +3128,7 @@ static int source_dispatch(sd_event_source *s) { /* Now, reap the PID for good. */ if (zombie) - waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED); + (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED); break; } @@ -2336,6 +3145,28 @@ static int source_dispatch(sd_event_source *s) { r = s->exit.callback(s, s->userdata); break; + case SOURCE_INOTIFY: { + struct sd_event *e = s->event; + struct inotify_data *d; + size_t sz; + + assert(s->inotify.inode_data); + assert_se(d = s->inotify.inode_data->inotify_data); + + assert(d->buffer_filled >= offsetof(struct inotify_event, name)); + sz = offsetof(struct inotify_event, name) + d->buffer.ev.len; + assert(d->buffer_filled >= sz); + + r = s->inotify.callback(s, &d->buffer.ev, s->userdata); + + /* When no event is pending anymore on this inotify object, then let's drop the event from the + * buffer. */ + if (d->n_pending == 0) + event_inotify_data_drop(e, d, sz); + + break; + } + case SOURCE_WATCHDOG: case _SOURCE_EVENT_SOURCE_TYPE_MAX: case _SOURCE_EVENT_SOURCE_TYPE_INVALID: @@ -2470,6 +3301,25 @@ static int process_watchdog(sd_event *e) { return arm_watchdog(e); } +static void event_close_inode_data_fds(sd_event *e) { + struct inode_data *d; + + assert(e); + + /* Close the fds pointing to the inodes to watch now. We need to close them as they might otherwise pin + * filesystems. But we can't close them right-away as we need them as long as the user still wants to make + * adjustments to the even source, such as changing the priority (which requires us to remove and readd a watch + * for the inode). Hence, let's close them when entering the first iteration after they were added, as a + * compromise. */ + + while ((d = e->inode_data_to_close)) { + assert(d->fd >= 0); + d->fd = safe_close(d->fd); + + LIST_REMOVE(to_close, e->inode_data_to_close, d); + } +} + _public_ int sd_event_prepare(sd_event *e) { int r; @@ -2510,6 +3360,8 @@ _public_ int sd_event_prepare(sd_event *e) { if (r < 0) return r; + event_close_inode_data_fds(e); + if (event_next_pending(e) || e->need_process_child) goto pending; @@ -2545,6 +3397,10 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { ev_queue_max = MAX(e->n_sources, 1u); ev_queue = newa(struct epoll_event, ev_queue_max); + /* If we still have inotify data buffered, then query the other fds, but don't wait on it */ + if (e->inotify_data_buffered) + timeout = 0; + m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max, timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC)); if (m < 0) { @@ -2582,6 +3438,10 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events); break; + case WAKEUP_INOTIFY_DATA: + r = event_inotify_data_read(e, ev_queue[i].data.ptr, ev_queue[i].events); + break; + default: assert_not_reached("Invalid wake-up pointer"); } @@ -2620,6 +3480,10 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { goto finish; } + r = process_inotify(e); + if (r < 0) + goto finish; + if (event_next_pending(e)) { e->state = SD_EVENT_PENDING; @@ -2858,7 +3722,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) { return e->watchdog; if (b) { - struct epoll_event ev = {}; + struct epoll_event ev; r = sd_watchdog_enabled(false, &e->watchdog_period); if (r <= 0) @@ -2876,8 +3740,10 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) { if (r < 0) goto fail; - ev.events = EPOLLIN; - ev.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG); + ev = (struct epoll_event) { + .events = EPOLLIN, + .data.ptr = INT_TO_PTR(SOURCE_WATCHDOG), + }; r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev); if (r < 0) { @@ -2916,4 +3782,20 @@ _public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { *ret = e->iteration; return 0; } + +_public_ int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback) { + assert_return(s, -EINVAL); + + s->destroy_callback = callback; + return 0; +} + +_public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret) { + assert_return(s, -EINVAL); + + if (ret) + *ret = s->destroy_callback; + + return !!s->destroy_callback; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c index 26eef0fd..03a38699 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -1,8 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2016 Lennart Poettering ***/ #include "nm-sd-adapt.h" diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h index 9aeb27a2..f0b4eca5 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.h +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h @@ -2,9 +2,6 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2016 Lennart Poettering ***/ #include <stdbool.h> diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index de0ae600..8fe8ff43 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -1,8 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering ***/ #include "nm-sd-adapt.h" @@ -102,7 +99,7 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { return r; if (sd_id128_is_null(saved_machine_id)) - return -EINVAL; + return -ENOMEDIUM; } *ret = saved_machine_id; diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index 3f41be62..ab034043 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -1,8 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering ***/ #include "nm-sd-adapt.h" @@ -360,10 +357,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX) return -EINVAL; - if (encoded_size < sizeof(IDNA_ACE_PREFIX)-1) - return 0; - - if (memcmp(encoded, IDNA_ACE_PREFIX, sizeof(IDNA_ACE_PREFIX) -1) != 0) + if (!memory_startswith(encoded, encoded_size, IDNA_ACE_PREFIX)) return 0; input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index 47bd5357..8781eec6 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -2,9 +2,6 @@ #pragma once /*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering ***/ #include <errno.h> @@ -25,7 +22,7 @@ #define DNS_HOSTNAME_MAX 253 /* Maximum length of a full hostname, on the wire, including the final NUL byte */ -#define DNS_WIRE_FOMAT_HOSTNAME_MAX 255 +#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255 /* Maximum number of labels per valid hostname */ #define DNS_N_LABELS_MAX 127 diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h index b4400e7b..7b54d179 100644 --- a/src/systemd/src/systemd/_sd-common.h +++ b/src/systemd/src/systemd/_sd-common.h @@ -3,9 +3,6 @@ #define foosdcommonhfoo /*** - This file is part of systemd. - - Copyright 2013 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 diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index fd0a5693..931b0e89 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -3,9 +3,7 @@ #define foosddhcpclienthfoo /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. + Copyright © 2013 Intel Corporation. All rights reserved. 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 diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 3cc7fcab..16c05661 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -3,10 +3,8 @@ #define foosddhcpleasehfoo /*** - This file is part of systemd. - - Copyright (C) 2013 Intel Corporation. All rights reserved. - Copyright (C) 2014 Tom Gundersen + Copyright © 2013 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen 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 diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h index cadb32a0..4f3b2d9e 100644 --- a/src/systemd/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/src/systemd/sd-dhcp6-client.h @@ -3,9 +3,7 @@ #define foosddhcp6clienthfoo /*** - This file is part of systemd. - - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright © 2014 Intel Corporation. All rights reserved. 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 diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h index 22a5f8ce..a673de5e 100644 --- a/src/systemd/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/src/systemd/sd-dhcp6-lease.h @@ -3,10 +3,8 @@ #define foosddhcp6leasehfoo /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014-2015 Intel Corporation. All rights reserved. + Copyright © 2014 Tom Gundersen + Copyright © 2014-2015 Intel Corporation. All rights reserved. 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 diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index ec4b7bcf..7fcae4ac 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -3,9 +3,6 @@ #define foosdeventhfoo /*** - This file is part of systemd. - - Copyright 2013 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 @@ -24,6 +21,7 @@ #include <inttypes.h> #include <signal.h> #include <sys/epoll.h> +#include <sys/inotify.h> #include <sys/signalfd.h> #include <sys/types.h> #include <time.h> @@ -78,6 +76,8 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, #else typedef void* sd_event_child_handler_t; #endif +typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata); +typedef void (*sd_event_destroy_t)(void *userdata); int sd_event_default(sd_event **e); @@ -89,6 +89,7 @@ int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, s int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata); int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata); int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata); +int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata); int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); @@ -139,6 +140,9 @@ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec); int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock); int sd_event_source_get_signal(sd_event_source *s); int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid); +int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); +int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback); +int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h index 67fc5956..b24fd06f 100644 --- a/src/systemd/src/systemd/sd-id128.h +++ b/src/systemd/src/systemd/sd-id128.h @@ -3,9 +3,6 @@ #define foosdid128hfoo /*** - 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 diff --git a/src/systemd/src/systemd/sd-ipv4acd.h b/src/systemd/src/systemd/sd-ipv4acd.h index 677ae3b2..259db263 100644 --- a/src/systemd/src/systemd/sd-ipv4acd.h +++ b/src/systemd/src/systemd/sd-ipv4acd.h @@ -3,10 +3,8 @@ #define foosdipv4acdfoo /*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. - Copyright (C) 2015 Tom Gundersen + Copyright © 2014 Axis Communications AB. All rights reserved. + Copyright © 2015 Tom Gundersen 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 diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h index c330b0ae..71bd4cfe 100644 --- a/src/systemd/src/systemd/sd-ipv4ll.h +++ b/src/systemd/src/systemd/sd-ipv4ll.h @@ -3,9 +3,7 @@ #define foosdipv4llfoo /*** - This file is part of systemd. - - Copyright (C) 2014 Axis Communications AB. All rights reserved. + Copyright © 2014 Axis Communications AB. All rights reserved. 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 diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h index 0a76fa63..11a2119a 100644 --- a/src/systemd/src/systemd/sd-lldp.h +++ b/src/systemd/src/systemd/sd-lldp.h @@ -3,10 +3,8 @@ #define foosdlldphfoo /*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani + Copyright © 2014 Tom Gundersen + Copyright © 2014 Susant Sahani 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 diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h index 15211450..6b6249ca 100644 --- a/src/systemd/src/systemd/sd-ndisc.h +++ b/src/systemd/src/systemd/sd-ndisc.h @@ -3,9 +3,7 @@ #define foosdndiscfoo /*** - This file is part of systemd. - - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright © 2014 Intel Corporation. All rights reserved. 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 |