Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

383 Commits   1 Branch   1 Tag
Index: radix-1.9/base/radix-setup/scripts/setup
===================================================================
--- radix-1.9/base/radix-setup/scripts/setup	(revision 19)
+++ radix-1.9/base/radix-setup/scripts/setup	(revision 20)
@@ -2332,12 +2332,60 @@
   fi
 }
 #
-# End of install packages:
+# End of install packages.
 #
 ####################################################################
 
 ####################################################################
 #
+# Set EFI boot entry:
+#
+set_efiboot_entry() {
+  local uefi=${1}
+  local loader="${2}"
+
+  local devlen=${#device}
+  local partlen=
+
+  let 'partlen = devlen + 1'
+  if [ "${#p}" -gt "0" ] ; then
+    let 'plen++'
+  fi
+
+  local uefi_device=
+  local uefi_partition=
+
+  modprobe efivarfs 1> /dev/null 2> /dev/null
+
+  if [ ! -d '/sys/firmware/efi/efivars' ] ; then return ; fi
+  if [ -z "${uefi}" -o -z "${loader}" ] ; then return ; fi
+
+  uefi_device=$(echo "${uefi}" | cut -b 1-${devlen})
+  uefi_partition=$(echo "${uefi}" | cut -b ${partlen}- | sed 's/[^0-9]*//g')
+
+  #
+  # Remove the previous boot entry if set:
+  #
+  efibootmgr -v | rev | cut -f2- | rev | grep Boot0 | grep "${DISTRO_CAPTION}" | while read line ; do
+    if ! $(echo ${line} | cut -f2- -d' ' | grep -q "${DISTRO_CAPTION}") ; then
+      continue
+    fi
+    efibootmgr -q -B -b $(echo ${line} | cut -b5-8)
+    sleep 1
+  done
+
+  #
+  # Set boot entry:
+  #
+  efibootmgr -q -c -d ${uefi_device} -p ${uefi_partition} -l "${loader}" -L "${DISTRO_CAPTION}-${DISTRO_FULL_VERSION}"
+}
+#
+# End of set EFI boot entry.
+#
+####################################################################
+
+####################################################################
+#
 # Install GRUB:
 #
 UEFI=
@@ -2447,6 +2495,8 @@
                         --config="${grub_load_cfg}" \
                         --output=${UEFI_MPOINT}/efi/boot/bootx64.efi \
                         ${grub_modules}  2>/dev/null 1>/dev/null
+
+  set_efiboot_entry "${UEFI}" "\\efi\\boot\\bootx64.efi"
 }
 
 install_intel_pc32_grub()
Index: radix-1.9/net/NetworkManager/1.31.3/Makefile
===================================================================
--- radix-1.9/net/NetworkManager/1.31.3/Makefile	(revision 19)
+++ radix-1.9/net/NetworkManager/1.31.3/Makefile	(revision 20)
@@ -176,7 +176,9 @@
 TARGET_BIN_RPATH = /lib$(LIBSUFFIX):/usr/lib$(LIBSUFFIX)
 TARGET_LIB_RPATH = /lib$(LIBSUFFIX):/usr/lib$(LIBSUFFIX):/usr/lib/../lib$(LIBSUFFIX)
 
+TARGET_PLUGINS_RPATH = /usr/lib$(LIBSUFFIX)/NetworkManager/$(version)
 
+
 ####### Dependencies
 
 $(src_done): $(SRC_ARCHIVE) $(PATCHES_DEP)
@@ -333,6 +335,12 @@
 	     fi ; \
 	   done ; \
 	 )
+	# ======= Set RPATH/RUNPATH for target plugins =======
+	@( cd $(LIBNM_PKG)/usr/lib$(LIBSUFFIX)/NetworkManager/$(version) ; \
+	   for file in `find . | xargs file | grep "shared object" | grep ELF | cut -f 1 -d : | xargs echo` ; do \
+	     $(PATCHELF) --set-rpath $(TARGET_PLUGINS_RPATH):$(TARGET_LIB_RPATH) $$file 1> /dev/null 2> /dev/null ; \
+	   done ; \
+	 )
 endif
 	@touch $@
 
Index: radix-1.9/net/NetworkManager/1.31.3/PATCHES
===================================================================
--- radix-1.9/net/NetworkManager/1.31.3/PATCHES	(revision 19)
+++ radix-1.9/net/NetworkManager/1.31.3/PATCHES	(revision 20)
@@ -0,0 +1,2 @@
+
+../../../sources/GNOME/core/NetworkManager/patches/NetworkManager-1.31.3-dhcpcd-graceful-exit.patch -p0
Index: radix-1.9/sources/GNOME/core/NetworkManager/Makefile
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/Makefile	(revision 19)
+++ radix-1.9/sources/GNOME/core/NetworkManager/Makefile	(revision 20)
@@ -14,10 +14,14 @@
 tarballs    = $(addsuffix .$(suffix), $(addprefix $(pkgname)-, $(versions)))
 sha1s       = $(addsuffix .sha1sum, $(tarballs))
 
+patches     = $(CURDIR)/patches/NetworkManager-1.31.3-dhcpcd-graceful-exit.patch
 
-BUILD_TARGETS = $(tarballs) $(sha1s)
+.NOTPARALLEL: $(patches)
 
 
+BUILD_TARGETS = $(tarballs) $(sha1s) $(patches)
+
+
 include ../../../../build-system/core.mk
 
 
@@ -43,5 +47,10 @@
 	   fi ; \
 	 done
 
+$(patches): $(sha1s)
+	@echo -e "\n======= Create Patches =======\n" ; \
+	 ( cd create-1.31.3-dhcpcd-graceful-exit-patch ; ./create.patch.sh ) ; \
+	 echo -e "\n"
+
 download_clean:
-	@rm -f $(tarballs) $(sha1s)
+	@rm -f $(tarballs) $(sha1s) $(patches)
Index: radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-client.c
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-client.c	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-client.c	(revision 20)
@@ -0,0 +1,1368 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
+ */
+
+#include "src/core/nm-default-daemon.h"
+
+#include "nm-dhcp-client.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/rtnetlink.h>
+
+#include "libnm-glib-aux/nm-dedup-multi.h"
+#include "libnm-glib-aux/nm-random-utils.h"
+
+#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
+#include "nm-dhcp-utils.h"
+#include "nm-dhcp-options.h"
+#include "libnm-platform/nm-platform.h"
+
+#include "nm-dhcp-client-logging.h"
+
+/*****************************************************************************/
+
+enum { SIGNAL_STATE_CHANGED, SIGNAL_PREFIX_DELEGATED, LAST_SIGNAL };
+
+static guint signals[LAST_SIGNAL] = {0};
+
+NM_GOBJECT_PROPERTIES_DEFINE(NMDhcpClient,
+                             PROP_ADDR_FAMILY,
+                             PROP_FLAGS,
+                             PROP_HWADDR,
+                             PROP_BROADCAST_HWADDR,
+                             PROP_IFACE,
+                             PROP_IFINDEX,
+                             PROP_MULTI_IDX,
+                             PROP_ROUTE_METRIC,
+                             PROP_ROUTE_TABLE,
+                             PROP_TIMEOUT,
+                             PROP_UUID,
+                             PROP_IAID,
+                             PROP_IAID_EXPLICIT,
+                             PROP_HOSTNAME,
+                             PROP_HOSTNAME_FLAGS,
+                             PROP_MUD_URL,
+                             PROP_VENDOR_CLASS_IDENTIFIER,
+                             PROP_REJECT_SERVERS, );
+
+typedef struct _NMDhcpClientPrivate {
+    NMDedupMultiIndex * multi_idx;
+    char *              iface;
+    GBytes *            hwaddr;
+    GBytes *            bcast_hwaddr;
+    char *              uuid;
+    GBytes *            client_id;
+    char *              hostname;
+    const char **       reject_servers;
+    char *              mud_url;
+    GBytes *            vendor_class_identifier;
+    pid_t               pid;
+    guint               timeout_id;
+    guint               watch_id;
+    int                 addr_family;
+    int                 ifindex;
+    guint32             route_table;
+    guint32             route_metric;
+    guint32             timeout;
+    guint32             iaid;
+    NMDhcpState         state;
+    NMDhcpHostnameFlags hostname_flags;
+    bool                info_only : 1;
+    bool                use_fqdn : 1;
+    bool                iaid_explicit : 1;
+} NMDhcpClientPrivate;
+
+G_DEFINE_ABSTRACT_TYPE(NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT)
+
+#define NM_DHCP_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMDhcpClient, NM_IS_DHCP_CLIENT)
+
+/*****************************************************************************/
+
+pid_t
+nm_dhcp_client_get_pid(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), -1);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid;
+}
+
+NMDedupMultiIndex *
+nm_dhcp_client_get_multi_idx(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->multi_idx;
+}
+
+const char *
+nm_dhcp_client_get_iface(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->iface;
+}
+
+int
+nm_dhcp_client_get_ifindex(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), -1);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->ifindex;
+}
+
+int
+nm_dhcp_client_get_addr_family(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), AF_UNSPEC);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->addr_family;
+}
+
+const char *
+nm_dhcp_client_get_uuid(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->uuid;
+}
+
+GBytes *
+nm_dhcp_client_get_hw_addr(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->hwaddr;
+}
+
+GBytes *
+nm_dhcp_client_get_broadcast_hw_addr(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->bcast_hwaddr;
+}
+
+guint32
+nm_dhcp_client_get_route_table(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), RT_TABLE_MAIN);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->route_table;
+}
+
+void
+nm_dhcp_client_set_route_table(NMDhcpClient *self, guint32 route_table)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    if (route_table != priv->route_table) {
+        priv->route_table = route_table;
+        _notify(self, PROP_ROUTE_TABLE);
+    }
+}
+
+guint32
+nm_dhcp_client_get_route_metric(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), G_MAXUINT32);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->route_metric;
+}
+
+void
+nm_dhcp_client_set_route_metric(NMDhcpClient *self, guint32 route_metric)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    if (route_metric != priv->route_metric) {
+        priv->route_metric = route_metric;
+        _notify(self, PROP_ROUTE_METRIC);
+    }
+}
+
+guint32
+nm_dhcp_client_get_timeout(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), 0);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->timeout;
+}
+
+guint32
+nm_dhcp_client_get_iaid(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), 0);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->iaid;
+}
+
+gboolean
+nm_dhcp_client_get_iaid_explicit(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->iaid_explicit;
+}
+
+GBytes *
+nm_dhcp_client_get_client_id(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->client_id;
+}
+
+static void
+_set_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    nm_assert(!client_id || g_bytes_get_size(client_id) >= 2);
+
+    if (priv->client_id == client_id
+        || (priv->client_id && client_id && g_bytes_equal(priv->client_id, client_id))) {
+        if (take && client_id)
+            g_bytes_unref(client_id);
+        return;
+    }
+
+    if (priv->client_id)
+        g_bytes_unref(priv->client_id);
+    priv->client_id = client_id;
+    if (!take && client_id)
+        g_bytes_ref(client_id);
+
+    {
+        gs_free char *s = NULL;
+
+        _LOGT("%s: set %s",
+              nm_dhcp_client_get_addr_family(self) == AF_INET6 ? "duid" : "client-id",
+              priv->client_id ? (s = nm_dhcp_utils_duid_to_string(priv->client_id)) : "default");
+    }
+}
+
+void
+nm_dhcp_client_set_client_id(NMDhcpClient *self, GBytes *client_id)
+{
+    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
+    g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2);
+
+    _set_client_id(self, client_id, FALSE);
+}
+
+void
+nm_dhcp_client_set_client_id_bin(NMDhcpClient *self,
+                                 guint8        type,
+                                 const guint8 *client_id,
+                                 gsize         len)
+{
+    guint8 *buf;
+    GBytes *b;
+
+    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
+    g_return_if_fail(client_id);
+    g_return_if_fail(len > 0);
+
+    buf    = g_malloc(len + 1);
+    buf[0] = type;
+    memcpy(buf + 1, client_id, len);
+    b = g_bytes_new_take(buf, len + 1);
+    _set_client_id(self, b, TRUE);
+}
+
+const char *
+nm_dhcp_client_get_hostname(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->hostname;
+}
+
+NMDhcpHostnameFlags
+nm_dhcp_client_get_hostname_flags(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NM_DHCP_HOSTNAME_FLAG_NONE);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->hostname_flags;
+}
+
+gboolean
+nm_dhcp_client_get_info_only(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->info_only;
+}
+
+gboolean
+nm_dhcp_client_get_use_fqdn(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->use_fqdn;
+}
+
+const char *
+nm_dhcp_client_get_mud_url(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->mud_url;
+}
+
+GBytes *
+nm_dhcp_client_get_vendor_class_identifier(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->vendor_class_identifier;
+}
+
+const char *const *
+nm_dhcp_client_get_reject_servers(NMDhcpClient *self)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), NULL);
+
+    return (const char *const *) NM_DHCP_CLIENT_GET_PRIVATE(self)->reject_servers;
+}
+
+/*****************************************************************************/
+
+static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
+    [NM_DHCP_STATE_UNKNOWN]    = "unknown",
+    [NM_DHCP_STATE_BOUND]      = "bound",
+    [NM_DHCP_STATE_EXTENDED]   = "extended",
+    [NM_DHCP_STATE_TIMEOUT]    = "timeout",
+    [NM_DHCP_STATE_EXPIRE]     = "expire",
+    [NM_DHCP_STATE_DONE]       = "done",
+    [NM_DHCP_STATE_FAIL]       = "fail",
+    [NM_DHCP_STATE_TERMINATED] = "terminated",
+};
+
+static const char *
+state_to_string(NMDhcpState state)
+{
+    if ((gsize) state < G_N_ELEMENTS(state_table))
+        return state_table[state];
+    return NULL;
+}
+
+static NMDhcpState
+reason_to_state(NMDhcpClient *self, const char *iface, const char *reason)
+{
+    if (g_ascii_strcasecmp(reason, "bound") == 0 || g_ascii_strcasecmp(reason, "bound6") == 0)
+        return NM_DHCP_STATE_BOUND;
+    else if (g_ascii_strcasecmp(reason, "renew") == 0 || g_ascii_strcasecmp(reason, "renew6") == 0
+             || g_ascii_strcasecmp(reason, "reboot") == 0
+             || g_ascii_strcasecmp(reason, "rebind") == 0
+             || g_ascii_strcasecmp(reason, "rebind6") == 0)
+        return NM_DHCP_STATE_EXTENDED;
+    else if (g_ascii_strcasecmp(reason, "timeout") == 0)
+        return NM_DHCP_STATE_TIMEOUT;
+    else if (g_ascii_strcasecmp(reason, "nak") == 0 || g_ascii_strcasecmp(reason, "expire") == 0
+             || g_ascii_strcasecmp(reason, "expire6") == 0)
+        return NM_DHCP_STATE_EXPIRE;
+    else if (g_ascii_strcasecmp(reason, "end") == 0 || g_ascii_strcasecmp(reason, "stop") == 0
+             || g_ascii_strcasecmp(reason, "stopped") == 0)
+        return NM_DHCP_STATE_DONE;
+    else if (g_ascii_strcasecmp(reason, "fail") == 0 || g_ascii_strcasecmp(reason, "abend") == 0)
+        return NM_DHCP_STATE_FAIL;
+    else if (g_ascii_strcasecmp(reason, "preinit") == 0)
+        return NM_DHCP_STATE_NOOP;
+
+    _LOGD("unmapped DHCP state '%s'", reason);
+    return NM_DHCP_STATE_UNKNOWN;
+}
+
+/*****************************************************************************/
+
+static void
+timeout_cleanup(NMDhcpClient *self)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    nm_clear_g_source(&priv->timeout_id);
+}
+
+static void
+watch_cleanup(NMDhcpClient *self)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    nm_clear_g_source(&priv->watch_id);
+}
+
+void
+nm_dhcp_client_stop_pid(pid_t pid, const char *iface, int sig)
+{
+    char *name = iface ? g_strdup_printf("dhcp-client-%s", iface) : NULL;
+
+    g_return_if_fail(pid > 1);
+
+    nm_utils_kill_child_sync(pid,
+                             sig,
+                             LOGD_DHCP,
+                             name ?: "dhcp-client",
+                             NULL,
+                             1000 / 2,
+                             1000 / 20);
+    g_free(name);
+}
+
+static void
+stop(NMDhcpClient *self, gboolean release)
+{
+    NMDhcpClientPrivate *priv;
+
+    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
+
+    priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    if (priv->pid > 0) {
+        /* Clean up the watch handler since we're explicitly killing the daemon */
+        watch_cleanup(self);
+        nm_dhcp_client_stop_pid(priv->pid, priv->iface, SIGTERM);
+    }
+    priv->pid = -1;
+}
+
+void
+nm_dhcp_client_set_state(NMDhcpClient *self,
+                         NMDhcpState   new_state,
+                         NMIPConfig *  ip_config,
+                         GHashTable *  options)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
+        g_return_if_fail(NM_IS_IP_CONFIG_ADDR_FAMILY(ip_config, priv->addr_family));
+        g_return_if_fail(options);
+    } else {
+        g_return_if_fail(!ip_config);
+        g_return_if_fail(!options);
+    }
+
+    if (new_state >= NM_DHCP_STATE_BOUND)
+        timeout_cleanup(self);
+    if (new_state >= NM_DHCP_STATE_TIMEOUT)
+        watch_cleanup(self);
+
+    /* The client may send same-state transitions for RENEW/REBIND events and
+     * the lease may have changed, so handle same-state transitions for the
+     * EXTENDED and BOUND states.  Ignore same-state transitions for other
+     * events since the lease won't have changed and the state was already handled.
+     */
+    if ((priv->state == new_state)
+        && !NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED))
+        return;
+
+    if (_LOGD_ENABLED()) {
+        gs_free const char **keys = NULL;
+        guint                i, nkeys;
+
+        keys = nm_utils_strdict_get_keys(options, TRUE, &nkeys);
+        for (i = 0; i < nkeys; i++) {
+            _LOGD("option %-20s => '%s'", keys[i], (char *) g_hash_table_lookup(options, keys[i]));
+        }
+    }
+
+    if (_LOGT_ENABLED() && priv->addr_family == AF_INET6) {
+        gs_free char *event_id = NULL;
+
+        event_id = nm_dhcp_utils_get_dhcp6_event_id(options);
+        if (event_id)
+            _LOGT("event-id: \"%s\"", event_id);
+    }
+
+    if (_LOGI_ENABLED()) {
+        const char *req_str =
+            NM_IS_IPv4(priv->addr_family)
+                ? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS)
+                : nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS);
+        const char *addr = nm_g_hash_table_lookup(options, req_str);
+
+        _LOGI("state changed %s -> %s%s%s%s",
+              state_to_string(priv->state),
+              state_to_string(new_state),
+              NM_PRINT_FMT_QUOTED(addr, ", address=", addr, "", ""));
+    }
+
+    priv->state = new_state;
+    g_signal_emit(G_OBJECT(self), signals[SIGNAL_STATE_CHANGED], 0, new_state, ip_config, options);
+}
+
+static gboolean
+transaction_timeout(gpointer user_data)
+{
+    NMDhcpClient *       self = NM_DHCP_CLIENT(user_data);
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    priv->timeout_id = 0;
+    _LOGW("request timed out");
+    nm_dhcp_client_set_state(self, NM_DHCP_STATE_TIMEOUT, NULL, NULL);
+    return G_SOURCE_REMOVE;
+}
+
+static void
+daemon_watch_cb(GPid pid, int status, gpointer user_data)
+{
+    NMDhcpClient *       self = NM_DHCP_CLIENT(user_data);
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    g_return_if_fail(priv->watch_id);
+    priv->watch_id = 0;
+
+    if (WIFEXITED(status))
+        _LOGI("client pid %d exited with status %d", pid, WEXITSTATUS(status));
+    else if (WIFSIGNALED(status))
+        _LOGI("client pid %d killed by signal %d", pid, WTERMSIG(status));
+    else if (WIFSTOPPED(status))
+        _LOGI("client pid %d stopped by signal %d", pid, WSTOPSIG(status));
+    else if (WIFCONTINUED(status))
+        _LOGI("client pid %d resumed (by SIGCONT)", pid);
+    else
+        _LOGW("client died abnormally");
+
+    priv->pid = -1;
+
+    nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL, NULL);
+}
+
+void
+nm_dhcp_client_start_timeout(NMDhcpClient *self)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    g_return_if_fail(priv->timeout_id == 0);
+
+    /* Set up a timeout on the transaction to kill it after the timeout */
+
+    if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY)
+        return;
+
+    priv->timeout_id = g_timeout_add_seconds(priv->timeout, transaction_timeout, self);
+}
+
+void
+nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    g_return_if_fail(priv->pid == -1);
+    priv->pid = pid;
+
+    nm_dhcp_client_start_timeout(self);
+
+    g_return_if_fail(priv->watch_id == 0);
+    priv->watch_id = g_child_watch_add(pid, daemon_watch_cb, self);
+}
+
+void
+nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    g_return_if_fail(priv->pid == pid);
+    priv->pid = -1;
+
+    watch_cleanup(self);
+    timeout_cleanup(self);
+}
+
+gboolean
+nm_dhcp_client_start_ip4(NMDhcpClient *self,
+                         GBytes *      client_id,
+                         const char *  dhcp_anycast_addr,
+                         const char *  last_ip4_address,
+                         GError **     error)
+{
+    NMDhcpClientPrivate *priv;
+
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+
+    priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+    g_return_val_if_fail(priv->pid == -1, FALSE);
+    g_return_val_if_fail(priv->addr_family == AF_INET, FALSE);
+    g_return_val_if_fail(priv->uuid != NULL, FALSE);
+
+    if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY)
+        _LOGI("activation: beginning transaction (no timeout)");
+    else
+        _LOGI("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout);
+
+    nm_dhcp_client_set_client_id(self, client_id);
+
+    return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self,
+                                                     dhcp_anycast_addr,
+                                                     last_ip4_address,
+                                                     error);
+}
+
+gboolean
+nm_dhcp_client_accept(NMDhcpClient *self, GError **error)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+
+    if (NM_DHCP_CLIENT_GET_CLASS(self)->accept) {
+        return NM_DHCP_CLIENT_GET_CLASS(self)->accept(self, error);
+    }
+
+    return TRUE;
+}
+
+gboolean
+nm_dhcp_client_decline(NMDhcpClient *self, const char *error_message, GError **error)
+{
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+
+    if (NM_DHCP_CLIENT_GET_CLASS(self)->decline) {
+        return NM_DHCP_CLIENT_GET_CLASS(self)->decline(self, error_message, error);
+    }
+
+    return TRUE;
+}
+
+static GBytes *
+get_duid(NMDhcpClient *self)
+{
+    return NULL;
+}
+
+gboolean
+nm_dhcp_client_start_ip6(NMDhcpClient *            self,
+                         GBytes *                  client_id,
+                         gboolean                  enforce_duid,
+                         const char *              dhcp_anycast_addr,
+                         const struct in6_addr *   ll_addr,
+                         NMSettingIP6ConfigPrivacy privacy,
+                         guint                     needed_prefixes,
+                         GError **                 error)
+{
+    NMDhcpClientPrivate *priv;
+    gs_unref_bytes GBytes *own_client_id = NULL;
+
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+    g_return_val_if_fail(client_id, FALSE);
+
+    priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    g_return_val_if_fail(priv->pid == -1, FALSE);
+    g_return_val_if_fail(priv->addr_family == AF_INET6, FALSE);
+    g_return_val_if_fail(priv->uuid != NULL, FALSE);
+    g_return_val_if_fail(!priv->client_id, FALSE);
+
+    if (!enforce_duid)
+        own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self);
+
+    _set_client_id(self, own_client_id ?: client_id, FALSE);
+
+    if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY)
+        _LOGI("activation: beginning transaction (no timeout)");
+    else
+        _LOGI("activation: beginning transaction (timeout in %u seconds)", (guint) priv->timeout);
+
+    return NM_DHCP_CLIENT_GET_CLASS(self)
+        ->ip6_start(self, dhcp_anycast_addr, ll_addr, privacy, needed_prefixes, error);
+}
+
+void
+nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name)
+{
+    guint64       start_time;
+    pid_t         pid, ppid;
+    const char *  exe;
+    char          proc_path[NM_STRLEN("/proc/%lu/cmdline") + 100];
+    gs_free char *pid_contents = NULL, *proc_contents = NULL;
+
+    /* Check for an existing instance and stop it */
+    if (!g_file_get_contents(pid_file, &pid_contents, NULL, NULL))
+        return;
+
+    pid = _nm_utils_ascii_str_to_int64(pid_contents, 10, 1, G_MAXINT64, 0);
+    if (pid <= 0)
+        goto out;
+
+    start_time = nm_utils_get_start_time_for_pid(pid, NULL, &ppid);
+    if (start_time == 0)
+        goto out;
+
+    nm_sprintf_buf(proc_path, "/proc/%lu/cmdline", (unsigned long) pid);
+    if (!g_file_get_contents(proc_path, &proc_contents, NULL, NULL))
+        goto out;
+
+    exe = strrchr(proc_contents, '/');
+    if (exe)
+        exe++;
+    else
+        exe = proc_contents;
+    if (!nm_streq0(exe, binary_name))
+        goto out;
+
+    if (ppid == getpid()) {
+        /* the process is our own child. */
+        nm_utils_kill_child_sync(pid, SIGTERM, LOGD_DHCP, "dhcp-client", NULL, 1000 / 2, 1000 / 20);
+    } else {
+        nm_utils_kill_process_sync(pid,
+                                   start_time,
+                                   SIGTERM,
+                                   LOGD_DHCP,
+                                   "dhcp-client",
+                                   1000 / 2,
+                                   1000 / 20,
+                                   2000);
+    }
+
+out:
+    if (remove(pid_file) == -1) {
+        int errsv = errno;
+
+        nm_log_dbg(LOGD_DHCP,
+                   "dhcp: could not remove pid file \"%s\": %s (%d)",
+                   pid_file,
+                   nm_strerror_native(errsv),
+                   errsv);
+    }
+}
+
+void
+nm_dhcp_client_stop(NMDhcpClient *self, gboolean release)
+{
+    NMDhcpClientPrivate *priv;
+    pid_t                old_pid = 0;
+
+    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
+
+    priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    /* Kill the DHCP client */
+    old_pid = priv->pid;
+    NM_DHCP_CLIENT_GET_CLASS(self)->stop(self, release);
+    if (old_pid > 0)
+        _LOGI("canceled DHCP transaction, DHCP client pid %d", old_pid);
+    else
+        _LOGI("canceled DHCP transaction");
+    nm_assert(priv->pid == -1);
+
+    nm_dhcp_client_set_state(self, NM_DHCP_STATE_TERMINATED, NULL, NULL);
+}
+
+/*****************************************************************************/
+
+static char *
+bytearray_variant_to_string(NMDhcpClient *self, GVariant *value, const char *key)
+{
+    const guint8 *array;
+    gsize         length;
+    GString *     str;
+    int           i;
+    unsigned char c;
+    char *        converted = NULL;
+
+    g_return_val_if_fail(value != NULL, NULL);
+
+    array = g_variant_get_fixed_array(value, &length, 1);
+
+    /* Since the DHCP options come through environment variables, they should
+     * already be UTF-8 safe, but just make sure.
+     */
+    str = g_string_sized_new(length);
+    for (i = 0; i < length; i++) {
+        c = array[i];
+
+        /* Convert NULLs to spaces and non-ASCII characters to ? */
+        if (c == '\0')
+            c = ' ';
+        else if (c > 127)
+            c = '?';
+        str = g_string_append_c(str, c);
+    }
+    str = g_string_append_c(str, '\0');
+
+    converted = str->str;
+    if (!g_utf8_validate(converted, -1, NULL))
+        _LOGW("option '%s' couldn't be converted to UTF-8", key);
+    g_string_free(str, FALSE);
+    return converted;
+}
+
+static int
+label_is_unknown_xyz(const char *label)
+{
+    if (!NM_STR_HAS_PREFIX(label, "unknown_"))
+        return -EINVAL;
+
+    label += NM_STRLEN("unknown_");
+    if (label[0] != '2' || !g_ascii_isdigit(label[1]) || !g_ascii_isdigit(label[2])
+        || label[3] != '\0')
+        return -EINVAL;
+
+    return _nm_utils_ascii_str_to_int64(label, 10, 224, 254, -EINVAL);
+}
+
+#define OLD_TAG "old_"
+#define NEW_TAG "new_"
+
+static void
+maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant *value)
+{
+    char *str_value = NULL;
+
+    g_return_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING));
+
+    if (g_str_has_prefix(key, OLD_TAG))
+        return;
+
+    /* Filter out stuff that's not actually new DHCP options */
+    if (NM_IN_STRSET(key, "interface", "pid", "reason", "dhcp_message_type"))
+        return;
+
+    if (NM_STR_HAS_PREFIX(key, NEW_TAG))
+        key += NM_STRLEN(NEW_TAG);
+    if (NM_STR_HAS_PREFIX(key, "private_") || !key[0])
+        return;
+
+    str_value = bytearray_variant_to_string(self, value, key);
+    if (str_value) {
+        int priv_opt_num;
+
+        g_hash_table_insert(hash, g_strdup(key), str_value);
+
+        /* dhclient has no special labels for private dhcp options: it uses "unknown_xyz"
+         * labels for that. We need to identify those to alias them to our "private_xyz"
+         * format unused in the internal dchp plugins.
+         */
+        if ((priv_opt_num = label_is_unknown_xyz(key)) > 0) {
+            gs_free guint8 *check_val = NULL;
+            char *          hex_str   = NULL;
+            gsize           len;
+
+            /* dhclient passes values from dhcp private options in its own "string" format:
+             * if the raw values are printable as ascii strings, it will pass the string
+             * representation; if the values are not printable as an ascii string, it will
+             * pass a string displaying the hex values (hex string). Try to enforce passing
+             * always an hex string, converting string representation if needed.
+             */
+            check_val = nm_utils_hexstr2bin_alloc(str_value, FALSE, TRUE, ":", 0, &len);
+            hex_str   = nm_utils_bin2hexstr_full(check_val ?: (guint8 *) str_value,
+                                               check_val ? len : strlen(str_value),
+                                               ':',
+                                               FALSE,
+                                               NULL);
+            g_hash_table_insert(hash, g_strdup_printf("private_%d", priv_opt_num), hex_str);
+        }
+    }
+}
+
+void
+nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient *self, const NMPlatformIP6Address *prefix)
+{
+    g_signal_emit(G_OBJECT(self), signals[SIGNAL_PREFIX_DELEGATED], 0, prefix);
+}
+
+gboolean
+nm_dhcp_client_handle_event(gpointer      unused,
+                            const char *  iface,
+                            int           pid,
+                            GVariant *    options,
+                            const char *  reason,
+                            NMDhcpClient *self)
+{
+    NMDhcpClientPrivate *priv;
+    guint32              old_state;
+    guint32              new_state;
+    gs_unref_hashtable GHashTable *str_options = NULL;
+    gs_unref_object NMIPConfig *ip_config      = NULL;
+    NMPlatformIP6Address        prefix         = {
+        0,
+    };
+
+    g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
+    g_return_val_if_fail(iface != NULL, FALSE);
+    g_return_val_if_fail(pid > 0, FALSE);
+    g_return_val_if_fail(g_variant_is_of_type(options, G_VARIANT_TYPE_VARDICT), FALSE);
+    g_return_val_if_fail(reason != NULL, FALSE);
+
+    priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    if (g_strcmp0(priv->iface, iface) != 0)
+        return FALSE;
+    if (priv->pid != pid)
+        return FALSE;
+
+    old_state = priv->state;
+    new_state = reason_to_state(self, priv->iface, reason);
+    _LOGD("DHCP state '%s' -> '%s' (reason: '%s')",
+          state_to_string(old_state),
+          state_to_string(new_state),
+          reason);
+
+    if (new_state == NM_DHCP_STATE_NOOP)
+        return TRUE;
+
+    if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
+        GVariantIter iter;
+        const char * name;
+        GVariant *   value;
+
+        /* Copy options */
+        str_options = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
+        g_variant_iter_init(&iter, options);
+        while (g_variant_iter_next(&iter, "{&sv}", &name, &value)) {
+            maybe_add_option(self, str_options, name, value);
+            g_variant_unref(value);
+        }
+
+        /* Create the IP config */
+        if (g_hash_table_size(str_options) > 0) {
+            if (priv->addr_family == AF_INET) {
+                ip_config = NM_IP_CONFIG_CAST(
+                    nm_dhcp_utils_ip4_config_from_options(nm_dhcp_client_get_multi_idx(self),
+                                                          priv->ifindex,
+                                                          priv->iface,
+                                                          str_options,
+                                                          priv->route_table,
+                                                          priv->route_metric));
+            } else {
+                prefix    = nm_dhcp_utils_ip6_prefix_from_options(str_options);
+                ip_config = NM_IP_CONFIG_CAST(
+                    nm_dhcp_utils_ip6_config_from_options(nm_dhcp_client_get_multi_idx(self),
+                                                          priv->ifindex,
+                                                          priv->iface,
+                                                          str_options,
+                                                          priv->info_only));
+            }
+        } else
+            g_warn_if_reached();
+    }
+
+    if (!IN6_IS_ADDR_UNSPECIFIED(&prefix.address)) {
+        /* If we got an IPv6 prefix to delegate, we don't change the state
+         * of the DHCP client instance. Instead, we just signal the prefix
+         * to the device. */
+        nm_dhcp_client_emit_ipv6_prefix_delegated(self, &prefix);
+    } else {
+        /* Fail if no valid IP config was received */
+        if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED) && !ip_config) {
+            _LOGW("client bound but IP config not received");
+            new_state = NM_DHCP_STATE_FAIL;
+            nm_clear_pointer(&str_options, g_hash_table_unref);
+        }
+
+        nm_dhcp_client_set_state(self, new_state, ip_config, str_options);
+    }
+
+    return TRUE;
+}
+
+gboolean
+nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr)
+{
+    NMDhcpClientPrivate *priv  = NM_DHCP_CLIENT_GET_PRIVATE(self);
+    in_addr_t            addr4 = *(in_addr_t *) addr;
+    guint                i;
+
+    /* IPv6 not implemented yet */
+    nm_assert(priv->addr_family == AF_INET);
+
+    if (!priv->reject_servers || !priv->reject_servers[0])
+        return FALSE;
+
+    for (i = 0; priv->reject_servers[i]; i++) {
+        in_addr_t r_addr;
+        in_addr_t mask;
+        int       r_prefix;
+
+        if (!nm_utils_parse_inaddr_prefix_bin(AF_INET,
+                                              priv->reject_servers[i],
+                                              NULL,
+                                              &r_addr,
+                                              &r_prefix))
+            nm_assert_not_reached();
+        mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix);
+        if ((addr4 & mask) == (r_addr & mask))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object);
+
+    switch (prop_id) {
+    case PROP_IFACE:
+        g_value_set_string(value, priv->iface);
+        break;
+    case PROP_IFINDEX:
+        g_value_set_int(value, priv->ifindex);
+        break;
+    case PROP_HWADDR:
+        g_value_set_boxed(value, priv->hwaddr);
+        break;
+    case PROP_BROADCAST_HWADDR:
+        g_value_set_boxed(value, priv->bcast_hwaddr);
+        break;
+    case PROP_ADDR_FAMILY:
+        g_value_set_int(value, priv->addr_family);
+        break;
+    case PROP_UUID:
+        g_value_set_string(value, priv->uuid);
+        break;
+    case PROP_IAID:
+        g_value_set_uint(value, priv->iaid);
+        break;
+    case PROP_IAID_EXPLICIT:
+        g_value_set_boolean(value, priv->iaid_explicit);
+        break;
+    case PROP_HOSTNAME:
+        g_value_set_string(value, priv->hostname);
+        break;
+    case PROP_ROUTE_METRIC:
+        g_value_set_uint(value, priv->route_metric);
+        break;
+    case PROP_ROUTE_TABLE:
+        g_value_set_uint(value, priv->route_table);
+        break;
+    case PROP_TIMEOUT:
+        g_value_set_uint(value, priv->timeout);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(object);
+    guint                flags;
+
+    switch (prop_id) {
+    case PROP_FLAGS:
+        /* construct-only */
+        flags = g_value_get_uint(value);
+        nm_assert(
+            (flags & ~((guint)(NM_DHCP_CLIENT_FLAGS_INFO_ONLY | NM_DHCP_CLIENT_FLAGS_USE_FQDN)))
+            == 0);
+        priv->info_only = NM_FLAGS_HAS(flags, NM_DHCP_CLIENT_FLAGS_INFO_ONLY);
+        priv->use_fqdn  = NM_FLAGS_HAS(flags, NM_DHCP_CLIENT_FLAGS_USE_FQDN);
+        break;
+    case PROP_MULTI_IDX:
+        /* construct-only */
+        priv->multi_idx = g_value_get_pointer(value);
+        if (!priv->multi_idx)
+            g_return_if_reached();
+        nm_dedup_multi_index_ref(priv->multi_idx);
+        break;
+    case PROP_IFACE:
+        /* construct-only */
+        priv->iface = g_value_dup_string(value);
+        g_return_if_fail(priv->iface);
+        nm_assert(nm_utils_ifname_valid_kernel(priv->iface, NULL));
+        break;
+    case PROP_IFINDEX:
+        /* construct-only */
+        priv->ifindex = g_value_get_int(value);
+        g_return_if_fail(priv->ifindex > 0);
+        break;
+    case PROP_HWADDR:
+        /* construct-only */
+        priv->hwaddr = g_value_dup_boxed(value);
+        break;
+    case PROP_BROADCAST_HWADDR:
+        /* construct-only */
+        priv->bcast_hwaddr = g_value_dup_boxed(value);
+        break;
+    case PROP_ADDR_FAMILY:
+        /* construct-only */
+        priv->addr_family = g_value_get_int(value);
+        if (!NM_IN_SET(priv->addr_family, AF_INET, AF_INET6))
+            g_return_if_reached();
+        break;
+    case PROP_UUID:
+        /* construct-only */
+        priv->uuid = g_value_dup_string(value);
+        break;
+    case PROP_IAID:
+        /* construct-only */
+        priv->iaid = g_value_get_uint(value);
+        break;
+    case PROP_IAID_EXPLICIT:
+        /* construct-only */
+        priv->iaid_explicit = g_value_get_boolean(value);
+        break;
+    case PROP_HOSTNAME:
+        /* construct-only */
+        priv->hostname = g_value_dup_string(value);
+        break;
+    case PROP_HOSTNAME_FLAGS:
+        /* construct-only */
+        priv->hostname_flags = g_value_get_uint(value);
+        break;
+    case PROP_MUD_URL:
+        /* construct-only */
+        priv->mud_url = g_value_dup_string(value);
+        break;
+    case PROP_ROUTE_TABLE:
+        priv->route_table = g_value_get_uint(value);
+        break;
+    case PROP_ROUTE_METRIC:
+        priv->route_metric = g_value_get_uint(value);
+        break;
+    case PROP_TIMEOUT:
+        /* construct-only */
+        priv->timeout = g_value_get_uint(value);
+        break;
+    case PROP_VENDOR_CLASS_IDENTIFIER:
+        /* construct-only */
+        priv->vendor_class_identifier = g_value_dup_boxed(value);
+        break;
+    case PROP_REJECT_SERVERS:
+        /* construct-only */
+        priv->reject_servers = nm_utils_strv_dup_packed(g_value_get_boxed(value), -1);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+/*****************************************************************************/
+
+static void
+nm_dhcp_client_init(NMDhcpClient *self)
+{
+    NMDhcpClientPrivate *priv;
+
+    priv        = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_DHCP_CLIENT, NMDhcpClientPrivate);
+    self->_priv = priv;
+
+    c_list_init(&self->dhcp_client_lst);
+
+    priv->pid = -1;
+}
+
+static void
+dispose(GObject *object)
+{
+    NMDhcpClient *       self = NM_DHCP_CLIENT(object);
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    /* Stopping the client is left up to the controlling device
+     * explicitly since we may want to quit NetworkManager but not terminate
+     * the DHCP client.
+     */
+
+    nm_assert(c_list_is_empty(&self->dhcp_client_lst));
+
+    watch_cleanup(self);
+    timeout_cleanup(self);
+
+    nm_clear_g_free(&priv->iface);
+    nm_clear_g_free(&priv->hostname);
+    nm_clear_g_free(&priv->uuid);
+    nm_clear_g_free(&priv->mud_url);
+    nm_clear_g_free(&priv->reject_servers);
+    nm_clear_pointer(&priv->client_id, g_bytes_unref);
+    nm_clear_pointer(&priv->hwaddr, g_bytes_unref);
+    nm_clear_pointer(&priv->bcast_hwaddr, g_bytes_unref);
+    nm_clear_pointer(&priv->vendor_class_identifier, g_bytes_unref);
+
+    G_OBJECT_CLASS(nm_dhcp_client_parent_class)->dispose(object);
+
+    priv->multi_idx = nm_dedup_multi_index_unref(priv->multi_idx);
+}
+
+static void
+nm_dhcp_client_class_init(NMDhcpClientClass *client_class)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(client_class);
+
+    g_type_class_add_private(client_class, sizeof(NMDhcpClientPrivate));
+
+    object_class->dispose      = dispose;
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+
+    client_class->stop     = stop;
+    client_class->get_duid = get_duid;
+
+    obj_properties[PROP_MULTI_IDX] =
+        g_param_spec_pointer(NM_DHCP_CLIENT_MULTI_IDX,
+                             "",
+                             "",
+                             G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_IFACE] =
+        g_param_spec_string(NM_DHCP_CLIENT_INTERFACE,
+                            "",
+                            "",
+                            NULL,
+                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_IFINDEX] =
+        g_param_spec_int(NM_DHCP_CLIENT_IFINDEX,
+                         "",
+                         "",
+                         -1,
+                         G_MAXINT,
+                         -1,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_HWADDR] =
+        g_param_spec_boxed(NM_DHCP_CLIENT_HWADDR,
+                           "",
+                           "",
+                           G_TYPE_BYTES,
+                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_BROADCAST_HWADDR] =
+        g_param_spec_boxed(NM_DHCP_CLIENT_BROADCAST_HWADDR,
+                           "",
+                           "",
+                           G_TYPE_BYTES,
+                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_ADDR_FAMILY] =
+        g_param_spec_int(NM_DHCP_CLIENT_ADDR_FAMILY,
+                         "",
+                         "",
+                         0,
+                         G_MAXINT,
+                         AF_UNSPEC,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_UUID] =
+        g_param_spec_string(NM_DHCP_CLIENT_UUID,
+                            "",
+                            "",
+                            NULL,
+                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_IAID] =
+        g_param_spec_uint(NM_DHCP_CLIENT_IAID,
+                          "",
+                          "",
+                          0,
+                          G_MAXUINT32,
+                          0,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_IAID_EXPLICIT] =
+        g_param_spec_boolean(NM_DHCP_CLIENT_IAID_EXPLICIT,
+                             "",
+                             "",
+                             FALSE,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_HOSTNAME] =
+        g_param_spec_string(NM_DHCP_CLIENT_HOSTNAME,
+                            "",
+                            "",
+                            NULL,
+                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_HOSTNAME_FLAGS] =
+        g_param_spec_uint(NM_DHCP_CLIENT_HOSTNAME_FLAGS,
+                          "",
+                          "",
+                          0,
+                          G_MAXUINT32,
+                          NM_DHCP_HOSTNAME_FLAG_NONE,
+                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_MUD_URL] =
+        g_param_spec_string(NM_DHCP_CLIENT_MUD_URL,
+                            "",
+                            "",
+                            NULL,
+                            G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_ROUTE_TABLE] =
+        g_param_spec_uint(NM_DHCP_CLIENT_ROUTE_TABLE,
+                          "",
+                          "",
+                          0,
+                          G_MAXUINT32,
+                          RT_TABLE_MAIN,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_ROUTE_METRIC] =
+        g_param_spec_uint(NM_DHCP_CLIENT_ROUTE_METRIC,
+                          "",
+                          "",
+                          0,
+                          G_MAXUINT32,
+                          0,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+    G_STATIC_ASSERT_EXPR(G_MAXINT32 == NM_DHCP_TIMEOUT_INFINITY);
+    obj_properties[PROP_TIMEOUT] =
+        g_param_spec_uint(NM_DHCP_CLIENT_TIMEOUT,
+                          "",
+                          "",
+                          1,
+                          G_MAXINT32,
+                          NM_DHCP_TIMEOUT_DEFAULT,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_FLAGS] =
+        g_param_spec_uint(NM_DHCP_CLIENT_FLAGS,
+                          "",
+                          "",
+                          0,
+                          G_MAXUINT32,
+                          0,
+                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_VENDOR_CLASS_IDENTIFIER] =
+        g_param_spec_boxed(NM_DHCP_CLIENT_VENDOR_CLASS_IDENTIFIER,
+                           "",
+                           "",
+                           G_TYPE_BYTES,
+                           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    obj_properties[PROP_REJECT_SERVERS] =
+        g_param_spec_boxed(NM_DHCP_CLIENT_REJECT_SERVERS,
+                           "",
+                           "",
+                           G_TYPE_STRV,
+                           G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+    signals[SIGNAL_STATE_CHANGED] = g_signal_new(NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
+                                                 G_OBJECT_CLASS_TYPE(object_class),
+                                                 G_SIGNAL_RUN_FIRST,
+                                                 0,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL,
+                                                 G_TYPE_NONE,
+                                                 3,
+                                                 G_TYPE_UINT,
+                                                 G_TYPE_OBJECT,
+                                                 G_TYPE_HASH_TABLE);
+
+    signals[SIGNAL_PREFIX_DELEGATED] = g_signal_new(NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED,
+                                                    G_OBJECT_CLASS_TYPE(object_class),
+                                                    G_SIGNAL_RUN_FIRST,
+                                                    0,
+                                                    NULL,
+                                                    NULL,
+                                                    NULL,
+                                                    G_TYPE_NONE,
+                                                    1,
+                                                    G_TYPE_POINTER);
+}
Index: radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-client.h
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-client.h	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-client.h	(revision 20)
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DHCP_CLIENT_H__
+#define __NETWORKMANAGER_DHCP_CLIENT_H__
+
+#include "nm-setting-ip4-config.h"
+#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 ((guint32) G_MAXINT32)
+
+#define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type())
+#define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient))
+#define NM_DHCP_CLIENT_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
+#define NM_IS_DHCP_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_CLIENT))
+#define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP_CLIENT))
+#define NM_DHCP_CLIENT_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
+
+#define NM_DHCP_CLIENT_ADDR_FAMILY             "addr-family"
+#define NM_DHCP_CLIENT_FLAGS                   "flags"
+#define NM_DHCP_CLIENT_HWADDR                  "hwaddr"
+#define NM_DHCP_CLIENT_BROADCAST_HWADDR        "broadcast-hwaddr"
+#define NM_DHCP_CLIENT_IFINDEX                 "ifindex"
+#define NM_DHCP_CLIENT_INTERFACE               "iface"
+#define NM_DHCP_CLIENT_MULTI_IDX               "multi-idx"
+#define NM_DHCP_CLIENT_HOSTNAME                "hostname"
+#define NM_DHCP_CLIENT_MUD_URL                 "mud-url"
+#define NM_DHCP_CLIENT_ROUTE_METRIC            "route-metric"
+#define NM_DHCP_CLIENT_ROUTE_TABLE             "route-table"
+#define NM_DHCP_CLIENT_TIMEOUT                 "timeout"
+#define NM_DHCP_CLIENT_UUID                    "uuid"
+#define NM_DHCP_CLIENT_IAID                    "iaid"
+#define NM_DHCP_CLIENT_IAID_EXPLICIT           "iaid-explicit"
+#define NM_DHCP_CLIENT_HOSTNAME_FLAGS          "hostname-flags"
+#define NM_DHCP_CLIENT_VENDOR_CLASS_IDENTIFIER "vendor-class-identifier"
+#define NM_DHCP_CLIENT_REJECT_SERVERS          "reject-servers"
+
+#define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED    "state-changed"
+#define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated"
+
+typedef enum {
+    NM_DHCP_STATE_UNKNOWN = 0,
+    NM_DHCP_STATE_BOUND,      /* new lease */
+    NM_DHCP_STATE_EXTENDED,   /* lease extended */
+    NM_DHCP_STATE_TIMEOUT,    /* timed out contacting server */
+    NM_DHCP_STATE_DONE,       /* client reported it's stopping */
+    NM_DHCP_STATE_EXPIRE,     /* lease expired or NAKed */
+    NM_DHCP_STATE_FAIL,       /* failed for some reason */
+    NM_DHCP_STATE_TERMINATED, /* client is no longer running */
+    NM_DHCP_STATE_NOOP,       /* state is a non operation for NetworkManager */
+    __NM_DHCP_STATE_MAX,
+    NM_DHCP_STATE_MAX = __NM_DHCP_STATE_MAX - 1,
+} NMDhcpState;
+
+struct _NMDhcpClientPrivate;
+
+typedef struct {
+    GObject                      parent;
+    struct _NMDhcpClientPrivate *_priv;
+    CList                        dhcp_client_lst;
+} NMDhcpClient;
+
+typedef enum {
+    NM_DHCP_CLIENT_FLAGS_INFO_ONLY = (1LL << 0),
+    NM_DHCP_CLIENT_FLAGS_USE_FQDN  = (1LL << 1),
+} NMDhcpClientFlags;
+
+typedef struct {
+    GObjectClass parent;
+
+    gboolean (*ip4_start)(NMDhcpClient *self,
+                          const char *  anycast_addr,
+                          const char *  last_ip4_address,
+                          GError **     error);
+
+    gboolean (*accept)(NMDhcpClient *self, GError **error);
+
+    gboolean (*decline)(NMDhcpClient *self, const char *error_message, GError **error);
+
+    gboolean (*ip6_start)(NMDhcpClient *            self,
+                          const char *              anycast_addr,
+                          const struct in6_addr *   ll_addr,
+                          NMSettingIP6ConfigPrivacy privacy,
+                          guint                     needed_prefixes,
+                          GError **                 error);
+
+    void (*stop)(NMDhcpClient *self, gboolean release);
+
+    /**
+     * get_duid:
+     * @self: the #NMDhcpClient
+     *
+     * 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);
+} NMDhcpClientClass;
+
+GType nm_dhcp_client_get_type(void);
+
+struct _NMDedupMultiIndex *nm_dhcp_client_get_multi_idx(NMDhcpClient *self);
+
+pid_t nm_dhcp_client_get_pid(NMDhcpClient *self);
+
+int nm_dhcp_client_get_addr_family(NMDhcpClient *self);
+
+const char *nm_dhcp_client_get_iface(NMDhcpClient *self);
+
+int nm_dhcp_client_get_ifindex(NMDhcpClient *self);
+
+const char *nm_dhcp_client_get_uuid(NMDhcpClient *self);
+
+GBytes *nm_dhcp_client_get_duid(NMDhcpClient *self);
+
+GBytes *nm_dhcp_client_get_hw_addr(NMDhcpClient *self);
+
+GBytes *nm_dhcp_client_get_broadcast_hw_addr(NMDhcpClient *self);
+
+guint32 nm_dhcp_client_get_route_table(NMDhcpClient *self);
+
+void nm_dhcp_client_set_route_table(NMDhcpClient *self, guint32 route_table);
+
+guint32 nm_dhcp_client_get_route_metric(NMDhcpClient *self);
+
+void nm_dhcp_client_set_route_metric(NMDhcpClient *self, guint32 route_metric);
+
+guint32 nm_dhcp_client_get_timeout(NMDhcpClient *self);
+
+guint32 nm_dhcp_client_get_iaid(NMDhcpClient *self);
+
+gboolean nm_dhcp_client_get_iaid_explicit(NMDhcpClient *self);
+
+GBytes *nm_dhcp_client_get_client_id(NMDhcpClient *self);
+
+const char *       nm_dhcp_client_get_hostname(NMDhcpClient *self);
+const char *       nm_dhcp_client_get_mud_url(NMDhcpClient *self);
+const char *const *nm_dhcp_client_get_reject_servers(NMDhcpClient *self);
+
+NMDhcpHostnameFlags nm_dhcp_client_get_hostname_flags(NMDhcpClient *self);
+
+gboolean nm_dhcp_client_get_info_only(NMDhcpClient *self);
+
+gboolean nm_dhcp_client_get_use_fqdn(NMDhcpClient *self);
+
+GBytes *nm_dhcp_client_get_vendor_class_identifier(NMDhcpClient *self);
+
+gboolean nm_dhcp_client_start_ip4(NMDhcpClient *self,
+                                  GBytes *      client_id,
+                                  const char *  dhcp_anycast_addr,
+                                  const char *  last_ip4_address,
+                                  GError **     error);
+
+gboolean nm_dhcp_client_start_ip6(NMDhcpClient *            self,
+                                  GBytes *                  client_id,
+                                  gboolean                  enforce_duid,
+                                  const char *              dhcp_anycast_addr,
+                                  const struct in6_addr *   ll_addr,
+                                  NMSettingIP6ConfigPrivacy privacy,
+                                  guint                     needed_prefixes,
+                                  GError **                 error);
+
+gboolean nm_dhcp_client_accept(NMDhcpClient *self, GError **error);
+
+gboolean nm_dhcp_client_decline(NMDhcpClient *self, const char *error_message, GError **error);
+
+void nm_dhcp_client_stop(NMDhcpClient *self, gboolean release);
+
+/* Backend helpers for subclasses */
+void nm_dhcp_client_stop_existing(const char *pid_file, const char *binary_name);
+
+void nm_dhcp_client_stop_pid(pid_t pid, const char *iface, int sig);
+
+void nm_dhcp_client_start_timeout(NMDhcpClient *self);
+
+void nm_dhcp_client_watch_child(NMDhcpClient *self, pid_t pid);
+
+void nm_dhcp_client_stop_watch_child(NMDhcpClient *self, pid_t pid);
+
+void nm_dhcp_client_set_state(NMDhcpClient *self,
+                              NMDhcpState   new_state,
+                              NMIPConfig *  ip_config,
+                              GHashTable *  options); /* str:str hash */
+
+gboolean nm_dhcp_client_handle_event(gpointer      unused,
+                                     const char *  iface,
+                                     int           pid,
+                                     GVariant *    options,
+                                     const char *  reason,
+                                     NMDhcpClient *self);
+
+void nm_dhcp_client_set_client_id(NMDhcpClient *self, GBytes *client_id);
+void nm_dhcp_client_set_client_id_bin(NMDhcpClient *self,
+                                      guint8        type,
+                                      const guint8 *client_id,
+                                      gsize         len);
+
+void nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient *              self,
+                                               const NMPlatformIP6Address *prefix);
+
+gboolean nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr);
+
+/*****************************************************************************
+ * Client data
+ *****************************************************************************/
+
+typedef struct {
+    GType (*get_type)(void);
+    GType (*get_type_per_addr_family)(int addr_family);
+    const char *name;
+    const char *(*get_path)(void);
+    bool experimental : 1;
+} NMDhcpClientFactory;
+
+GType nm_dhcp_nettools_get_type(void);
+
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon;
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient;
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd;
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_internal;
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_systemd;
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_nettools;
+
+#endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */
Index: radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-dhclient.c
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-dhclient.c	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-dhclient.c	(revision 20)
@@ -0,0 +1,733 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2005 - 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#define __CONFIG_H__
+
+#define _XOPEN_SOURCE
+#include <time.h>
+#undef _XOPEN_SOURCE
+
+#include "src/core/nm-default-daemon.h"
+
+#if WITH_DHCLIENT
+
+    #include <stdlib.h>
+    #include <unistd.h>
+    #include <stdio.h>
+    #include <netinet/in.h>
+    #include <arpa/inet.h>
+    #include <ctype.h>
+
+    #include "libnm-glib-aux/nm-dedup-multi.h"
+
+    #include "nm-utils.h"
+    #include "nm-dhcp-dhclient-utils.h"
+    #include "nm-dhcp-manager.h"
+    #include "NetworkManagerUtils.h"
+    #include "nm-dhcp-listener.h"
+    #include "nm-dhcp-client-logging.h"
+
+/*****************************************************************************/
+
+static const char *
+_addr_family_to_path_part(int addr_family)
+{
+    nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
+    return (addr_family == AF_INET6) ? "6" : "";
+}
+
+/*****************************************************************************/
+
+    #define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type())
+    #define NM_DHCP_DHCLIENT(obj) \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient))
+    #define NM_DHCP_DHCLIENT_CLASS(klass) \
+        (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass))
+    #define NM_IS_DHCP_DHCLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCLIENT))
+    #define NM_IS_DHCP_DHCLIENT_CLASS(klass) \
+        (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP_DHCLIENT))
+    #define NM_DHCP_DHCLIENT_GET_CLASS(obj) \
+        (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass))
+
+typedef struct _NMDhcpDhclient      NMDhcpDhclient;
+typedef struct _NMDhcpDhclientClass NMDhcpDhclientClass;
+
+static GType nm_dhcp_dhclient_get_type(void);
+
+/*****************************************************************************/
+
+typedef struct {
+    char *          conf_file;
+    const char *    def_leasefile;
+    char *          lease_file;
+    char *          pid_file;
+    NMDhcpListener *dhcp_listener;
+} NMDhcpDhclientPrivate;
+
+struct _NMDhcpDhclient {
+    NMDhcpClient          parent;
+    NMDhcpDhclientPrivate _priv;
+};
+
+struct _NMDhcpDhclientClass {
+    NMDhcpClientClass parent;
+};
+
+G_DEFINE_TYPE(NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
+
+    #define NM_DHCP_DHCLIENT_GET_PRIVATE(self) \
+        _NM_GET_PRIVATE(self, NMDhcpDhclient, NM_IS_DHCP_DHCLIENT)
+
+/*****************************************************************************/
+
+static const char *
+nm_dhcp_dhclient_get_path(void)
+{
+    return nm_utils_find_helper("dhclient", DHCLIENT_PATH, NULL);
+}
+
+/**
+ * get_dhclient_leasefile():
+ * @addr_family: AF_INET or AF_INET6
+ * @iface: the interface name of the device on which DHCP will be done
+ * @uuid: the connection UUID to which the returned lease should belong
+ * @out_preferred_path: on return, the "most preferred" leasefile path
+ *
+ * Returns the path of an existing leasefile (if any) for this interface and
+ * connection UUID.  Also returns the "most preferred" leasefile path, which
+ * may be different than any found leasefile.
+ *
+ * Returns: an existing leasefile, or %NULL if no matching leasefile could be found
+ */
+static char *
+get_dhclient_leasefile(int         addr_family,
+                       const char *iface,
+                       const char *uuid,
+                       char **     out_preferred_path)
+{
+    gs_free char *path = NULL;
+
+    if (nm_dhcp_utils_get_leasefile_path(addr_family, "dhclient", iface, uuid, &path)) {
+        NM_SET_OUT(out_preferred_path, g_strdup(path));
+        return g_steal_pointer(&path);
+    }
+
+    NM_SET_OUT(out_preferred_path, g_steal_pointer(&path));
+
+    /* If the leasefile we're looking for doesn't exist yet in the new location
+     * (eg, /var/lib/NetworkManager) then look in old locations to maintain
+     * backwards compatibility with external tools (like dracut) that put
+     * leasefiles there.
+     */
+
+    /* Old Debian, SUSE, and Mandriva location */
+    g_free(path);
+    path = g_strdup_printf(LOCALSTATEDIR "/lib/dhcp/dhclient%s-%s-%s.lease",
+                           _addr_family_to_path_part(addr_family),
+                           uuid,
+                           iface);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return g_steal_pointer(&path);
+
+    /* Old Red Hat and Fedora location */
+    g_free(path);
+    path = g_strdup_printf(LOCALSTATEDIR "/lib/dhclient/dhclient%s-%s-%s.lease",
+                           _addr_family_to_path_part(addr_family),
+                           uuid,
+                           iface);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return g_steal_pointer(&path);
+
+    /* Fail */
+    return NULL;
+}
+
+static gboolean
+merge_dhclient_config(NMDhcpDhclient *    self,
+                      int                 addr_family,
+                      const char *        iface,
+                      const char *        conf_file,
+                      GBytes *            client_id,
+                      const char *        anycast_addr,
+                      const char *        hostname,
+                      guint32             timeout,
+                      gboolean            use_fqdn,
+                      NMDhcpHostnameFlags hostname_flags,
+                      const char *        mud_url,
+                      const char *const * reject_servers,
+                      const char *        orig_path,
+                      GBytes **           out_new_client_id,
+                      GError **           error)
+{
+    gs_free char *orig = NULL;
+    gs_free char *new  = NULL;
+
+    g_return_val_if_fail(iface, FALSE);
+    g_return_val_if_fail(conf_file, FALSE);
+
+    if (orig_path && g_file_test(orig_path, G_FILE_TEST_EXISTS)) {
+        GError *read_error = NULL;
+
+        if (!g_file_get_contents(orig_path, &orig, NULL, &read_error)) {
+            _LOGW("error reading dhclient configuration %s: %s", orig_path, read_error->message);
+            g_error_free(read_error);
+        }
+    }
+
+    new = nm_dhcp_dhclient_create_config(iface,
+                                         addr_family,
+                                         client_id,
+                                         anycast_addr,
+                                         hostname,
+                                         timeout,
+                                         use_fqdn,
+                                         hostname_flags,
+                                         mud_url,
+                                         reject_servers,
+                                         orig_path,
+                                         orig,
+                                         out_new_client_id);
+    nm_assert(new);
+
+    return g_file_set_contents(conf_file, new, -1, error);
+}
+
+static char *
+find_existing_config(NMDhcpDhclient *self, int addr_family, const char *iface, const char *uuid)
+{
+    char *path;
+
+    /* NetworkManager-overridden configuration can be used to ship DHCP config
+     * with NetworkManager itself. It can be uuid-specific, device-specific
+     * or generic.
+     */
+    if (uuid) {
+        path = g_strdup_printf(NMCONFDIR "/dhclient%s-%s.conf",
+                               _addr_family_to_path_part(addr_family),
+                               uuid);
+        _LOGD("looking for existing config %s", path);
+        if (g_file_test(path, G_FILE_TEST_EXISTS))
+            return path;
+        g_free(path);
+    }
+
+    path = g_strdup_printf(NMCONFDIR "/dhclient%s-%s.conf",
+                           _addr_family_to_path_part(addr_family),
+                           iface);
+    _LOGD("looking for existing config %s", path);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return path;
+    g_free(path);
+
+    path = g_strdup_printf(NMCONFDIR "/dhclient%s.conf", _addr_family_to_path_part(addr_family));
+    _LOGD("looking for existing config %s", path);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return path;
+    g_free(path);
+
+    /* Distribution's dhclient configuration is used so that we can use
+     * configuration shipped with dhclient (if any).
+     *
+     * This replaces conditional compilation based on distribution name. Fedora
+     * and Debian store the configs in /etc/dhcp while upstream defaults to /etc
+     * which is then used by many other distributions. Some distributions
+     * (including Fedora) don't even provide a default configuration file.
+     */
+    path = g_strdup_printf(SYSCONFDIR "/dhcp/dhclient%s-%s.conf",
+                           _addr_family_to_path_part(addr_family),
+                           iface);
+    _LOGD("looking for existing config %s", path);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return path;
+    g_free(path);
+
+    path = g_strdup_printf(SYSCONFDIR "/dhclient%s-%s.conf",
+                           _addr_family_to_path_part(addr_family),
+                           iface);
+    _LOGD("looking for existing config %s", path);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return path;
+    g_free(path);
+
+    path =
+        g_strdup_printf(SYSCONFDIR "/dhcp/dhclient%s.conf", _addr_family_to_path_part(addr_family));
+    _LOGD("looking for existing config %s", path);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return path;
+    g_free(path);
+
+    path = g_strdup_printf(SYSCONFDIR "/dhclient%s.conf", _addr_family_to_path_part(addr_family));
+    _LOGD("looking for existing config %s", path);
+    if (g_file_test(path, G_FILE_TEST_EXISTS))
+        return path;
+    g_free(path);
+
+    return NULL;
+}
+
+/* NM provides interface-specific options; thus the same dhclient config
+ * file cannot be used since DHCP transactions can happen in parallel.
+ * Since some distros don't have default per-interface dhclient config files,
+ * read their single config file and merge that into a custom per-interface
+ * config file along with the NM options.
+ */
+static char *
+create_dhclient_config(NMDhcpDhclient *    self,
+                       int                 addr_family,
+                       const char *        iface,
+                       const char *        uuid,
+                       GBytes *            client_id,
+                       const char *        dhcp_anycast_addr,
+                       const char *        hostname,
+                       guint32             timeout,
+                       gboolean            use_fqdn,
+                       NMDhcpHostnameFlags hostname_flags,
+                       const char *        mud_url,
+                       const char *const * reject_servers,
+                       GBytes **           out_new_client_id)
+{
+    gs_free char *orig = NULL;
+    char *new          = NULL;
+    GError *error      = NULL;
+
+    g_return_val_if_fail(iface != NULL, NULL);
+
+    new = g_strdup_printf(NMSTATEDIR "/dhclient%s-%s.conf",
+                          _addr_family_to_path_part(addr_family),
+                          iface);
+
+    _LOGD("creating composite dhclient config %s", new);
+
+    orig = find_existing_config(self, addr_family, iface, uuid);
+    if (orig)
+        _LOGD("merging existing dhclient config %s", orig);
+    else
+        _LOGD("no existing dhclient configuration to merge");
+
+    if (!merge_dhclient_config(self,
+                               addr_family,
+                               iface,
+                               new,
+                               client_id,
+                               dhcp_anycast_addr,
+                               hostname,
+                               timeout,
+                               use_fqdn,
+                               hostname_flags,
+                               mud_url,
+                               reject_servers,
+                               orig,
+                               out_new_client_id,
+                               &error)) {
+        _LOGW("error creating dhclient configuration: %s", error->message);
+        g_clear_error(&error);
+    }
+
+    return new;
+}
+
+static gboolean
+dhclient_start(NMDhcpClient *client,
+               const char *  mode_opt,
+               gboolean      release,
+               pid_t *       out_pid,
+               int           prefixes,
+               GError **     error)
+{
+    NMDhcpDhclient *       self       = NM_DHCP_DHCLIENT(client);
+    NMDhcpDhclientPrivate *priv       = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
+    gs_unref_ptrarray GPtrArray *argv = NULL;
+    pid_t                        pid;
+    gs_free_error GError *local = NULL;
+    const char *          iface;
+    const char *          uuid;
+    const char *          system_bus_address;
+    const char *          dhclient_path;
+    char *                binary_name;
+    gs_free char *        cmd_str                  = NULL;
+    gs_free char *        pid_file                 = NULL;
+    gs_free char *        system_bus_address_env   = NULL;
+    gs_free char *        preferred_leasefile_path = NULL;
+    const int             addr_family              = nm_dhcp_client_get_addr_family(client);
+
+    g_return_val_if_fail(!priv->pid_file, FALSE);
+
+    NM_SET_OUT(out_pid, 0);
+
+    dhclient_path = nm_dhcp_dhclient_get_path();
+    if (!dhclient_path) {
+        nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "dhclient binary not found");
+        return FALSE;
+    }
+
+    iface = nm_dhcp_client_get_iface(client);
+    uuid  = nm_dhcp_client_get_uuid(client);
+
+    pid_file = g_strdup_printf(NMRUNDIR "/dhclient%s-%s.pid",
+                               _addr_family_to_path_part(addr_family),
+                               iface);
+
+    /* Kill any existing dhclient from the pidfile */
+    binary_name = g_path_get_basename(dhclient_path);
+    nm_dhcp_client_stop_existing(pid_file, binary_name);
+    g_free(binary_name);
+
+    if (release) {
+        /* release doesn't use the pidfile after killing an old client */
+        nm_clear_g_free(&pid_file);
+    }
+
+    g_free(priv->lease_file);
+    priv->lease_file = get_dhclient_leasefile(addr_family, iface, uuid, &preferred_leasefile_path);
+    nm_assert(preferred_leasefile_path);
+    if (!priv->lease_file) {
+        /* No existing leasefile, dhclient will create one at the preferred path */
+        priv->lease_file = g_steal_pointer(&preferred_leasefile_path);
+    } else if (!nm_streq0(priv->lease_file, preferred_leasefile_path)) {
+        gs_unref_object GFile *src = g_file_new_for_path(priv->lease_file);
+        gs_unref_object GFile *dst = g_file_new_for_path(preferred_leasefile_path);
+
+        /* Try to copy the existing leasefile to the preferred location */
+        if (!g_file_copy(src, dst, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &local)) {
+            gs_free char *s_path = NULL;
+            gs_free char *d_path = NULL;
+
+            /* Failure; just use the existing leasefile */
+            _LOGW("failed to copy leasefile %s to %s: %s",
+                  (s_path = g_file_get_path(src)),
+                  (d_path = g_file_get_path(dst)),
+                  local->message);
+            g_clear_error(&local);
+        } else {
+            /* Success; use the preferred leasefile path */
+            g_free(priv->lease_file);
+            priv->lease_file = g_file_get_path(dst);
+        }
+    }
+
+    /* Save the DUID to the leasefile dhclient will actually use */
+    if (addr_family == AF_INET6) {
+        if (!nm_dhcp_dhclient_save_duid(priv->lease_file,
+                                        nm_dhcp_client_get_client_id(client),
+                                        &local)) {
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               "failed to save DUID to '%s': %s",
+                               priv->lease_file,
+                               local->message);
+            return FALSE;
+        }
+    }
+
+    argv = g_ptr_array_new();
+    g_ptr_array_add(argv, (gpointer) dhclient_path);
+
+    g_ptr_array_add(argv, (gpointer) "-d");
+
+    /* Be quiet. dhclient logs to syslog anyway. And we duplicate the syslog
+     * to stderr in case of NM running with --debug.
+     */
+    g_ptr_array_add(argv, (gpointer) "-q");
+
+    if (release)
+        g_ptr_array_add(argv, (gpointer) "-r");
+
+    if (addr_family == AF_INET6) {
+        g_ptr_array_add(argv, (gpointer) "-6");
+
+        if (prefixes > 0 && nm_streq0(mode_opt, "-S")) {
+            /* -S is incompatible with -P, only use the latter */
+            mode_opt = NULL;
+        }
+
+        if (mode_opt)
+            g_ptr_array_add(argv, (gpointer) mode_opt);
+        while (prefixes--)
+            g_ptr_array_add(argv, (gpointer) "-P");
+    }
+    g_ptr_array_add(argv, (gpointer) "-sf"); /* Set script file */
+    g_ptr_array_add(argv, (gpointer) nm_dhcp_helper_path);
+
+    if (pid_file) {
+        g_ptr_array_add(argv, (gpointer) "-pf"); /* Set pid file */
+        g_ptr_array_add(argv, (gpointer) pid_file);
+    }
+
+    g_ptr_array_add(argv, (gpointer) "-lf"); /* Set lease file */
+    g_ptr_array_add(argv, (gpointer) priv->lease_file);
+
+    if (priv->conf_file) {
+        g_ptr_array_add(argv, (gpointer) "-cf"); /* Set interface config file */
+        g_ptr_array_add(argv, (gpointer) priv->conf_file);
+    }
+
+    /* Usually the system bus address is well-known; but if it's supposed
+     * to be something else, we need to push it to dhclient, since dhclient
+     * sanitizes the environment it gives the action scripts.
+     */
+    system_bus_address = getenv("DBUS_SYSTEM_BUS_ADDRESS");
+    if (system_bus_address) {
+        system_bus_address_env = g_strdup_printf("DBUS_SYSTEM_BUS_ADDRESS=%s", system_bus_address);
+        g_ptr_array_add(argv, (gpointer) "-e");
+        g_ptr_array_add(argv, (gpointer) system_bus_address_env);
+    }
+
+    g_ptr_array_add(argv, (gpointer) iface);
+    g_ptr_array_add(argv, NULL);
+
+    _LOGD("running: %s", (cmd_str = g_strjoinv(" ", (char **) argv->pdata)));
+
+    if (!g_spawn_async(NULL,
+                       (char **) argv->pdata,
+                       NULL,
+                       G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL
+                           | G_SPAWN_STDERR_TO_DEV_NULL,
+                       nm_utils_setpgid,
+                       NULL,
+                       &pid,
+                       &local)) {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           "dhclient failed to start: %s",
+                           local->message);
+        return FALSE;
+    }
+
+    _LOGI("dhclient started with pid %lld", (long long int) pid);
+
+    if (!release)
+        nm_dhcp_client_watch_child(client, pid);
+
+    priv->pid_file = g_steal_pointer(&pid_file);
+
+    NM_SET_OUT(out_pid, pid);
+    return TRUE;
+}
+
+static gboolean
+ip4_start(NMDhcpClient *client,
+          const char *  dhcp_anycast_addr,
+          const char *  last_ip4_address,
+          GError **     error)
+{
+    NMDhcpDhclient *       self = NM_DHCP_DHCLIENT(client);
+    NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
+    GBytes *               client_id;
+    gs_unref_bytes GBytes *new_client_id = NULL;
+
+    client_id = nm_dhcp_client_get_client_id(client);
+
+    priv->conf_file = create_dhclient_config(self,
+                                             AF_INET,
+                                             nm_dhcp_client_get_iface(client),
+                                             nm_dhcp_client_get_uuid(client),
+                                             client_id,
+                                             dhcp_anycast_addr,
+                                             nm_dhcp_client_get_hostname(client),
+                                             nm_dhcp_client_get_timeout(client),
+                                             nm_dhcp_client_get_use_fqdn(client),
+                                             nm_dhcp_client_get_hostname_flags(client),
+                                             nm_dhcp_client_get_mud_url(client),
+                                             nm_dhcp_client_get_reject_servers(client),
+                                             &new_client_id);
+    if (!priv->conf_file) {
+        nm_utils_error_set_literal(error,
+                                   NM_UTILS_ERROR_UNKNOWN,
+                                   "error creating dhclient configuration file");
+        return FALSE;
+    }
+
+    if (new_client_id) {
+        nm_assert(!client_id);
+        nm_dhcp_client_set_client_id(client, new_client_id);
+    }
+    return dhclient_start(client, NULL, FALSE, NULL, 0, error);
+}
+
+static gboolean
+ip6_start(NMDhcpClient *            client,
+          const char *              dhcp_anycast_addr,
+          const struct in6_addr *   ll_addr,
+          NMSettingIP6ConfigPrivacy privacy,
+          guint                     needed_prefixes,
+          GError **                 error)
+{
+    NMDhcpDhclient *       self = NM_DHCP_DHCLIENT(client);
+    NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
+
+    if (nm_dhcp_client_get_iaid_explicit(client))
+        _LOGW("dhclient does not support specifying an IAID for DHCPv6, it will be ignored");
+
+    priv->conf_file = create_dhclient_config(self,
+                                             AF_INET6,
+                                             nm_dhcp_client_get_iface(client),
+                                             nm_dhcp_client_get_uuid(client),
+                                             NULL,
+                                             dhcp_anycast_addr,
+                                             nm_dhcp_client_get_hostname(client),
+                                             nm_dhcp_client_get_timeout(client),
+                                             TRUE,
+                                             nm_dhcp_client_get_hostname_flags(client),
+                                             nm_dhcp_client_get_mud_url(client),
+                                             NULL,
+                                             NULL);
+    if (!priv->conf_file) {
+        nm_utils_error_set_literal(error,
+                                   NM_UTILS_ERROR_UNKNOWN,
+                                   "error creating dhclient configuration file");
+        return FALSE;
+    }
+
+    return dhclient_start(client,
+                          nm_dhcp_client_get_info_only(NM_DHCP_CLIENT(self)) ? "-S" : "-N",
+                          FALSE,
+                          NULL,
+                          needed_prefixes,
+                          error);
+}
+
+static void
+stop(NMDhcpClient *client, gboolean release)
+{
+    NMDhcpDhclient *       self = NM_DHCP_DHCLIENT(client);
+    NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
+    int                    errsv;
+
+    NM_DHCP_CLIENT_CLASS(nm_dhcp_dhclient_parent_class)->stop(client, release);
+
+    if (priv->conf_file)
+        if (remove(priv->conf_file) == -1) {
+            errsv = errno;
+            _LOGD("could not remove dhcp config file \"%s\": %d (%s)",
+                  priv->conf_file,
+                  errsv,
+                  nm_strerror_native(errsv));
+        }
+    if (priv->pid_file) {
+        if (remove(priv->pid_file) == -1) {
+            errsv = errno;
+            _LOGD("could not remove dhcp pid file \"%s\": %s (%d)",
+                  priv->pid_file,
+                  nm_strerror_native(errsv),
+                  errsv);
+        }
+        nm_clear_g_free(&priv->pid_file);
+    }
+
+    if (release) {
+        pid_t rpid = -1;
+
+        if (dhclient_start(client, NULL, TRUE, &rpid, 0, NULL)) {
+            /* Wait a few seconds for the release to happen */
+            nm_dhcp_client_stop_pid(rpid, nm_dhcp_client_get_iface(client), SIGTERM);
+        }
+    }
+}
+
+static GBytes *
+get_duid(NMDhcpClient *client)
+{
+    NMDhcpDhclient *       self      = NM_DHCP_DHCLIENT(client);
+    NMDhcpDhclientPrivate *priv      = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
+    GBytes *               duid      = NULL;
+    gs_free char *         leasefile = NULL;
+    GError *               error     = NULL;
+
+    /* Look in interface-specific leasefile first for backwards compat */
+    leasefile = get_dhclient_leasefile(AF_INET6,
+                                       nm_dhcp_client_get_iface(client),
+                                       nm_dhcp_client_get_uuid(client),
+                                       NULL);
+    if (leasefile) {
+        _LOGD("looking for DUID in '%s'", leasefile);
+        duid = nm_dhcp_dhclient_read_duid(leasefile, &error);
+        if (error) {
+            _LOGW("failed to read leasefile '%s': %s", leasefile, error->message);
+            g_clear_error(&error);
+        }
+        if (duid)
+            return duid;
+    }
+
+    /* 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);
+    if (error) {
+        _LOGW("failed to read leasefile '%s': %s", priv->def_leasefile, error->message);
+        g_clear_error(&error);
+    }
+
+    return duid;
+}
+
+/*****************************************************************************/
+
+static void
+nm_dhcp_dhclient_init(NMDhcpDhclient *self)
+{
+    static const char *const FILES[] = {
+        SYSCONFDIR "/dhclient6.leases", /* default */
+        LOCALSTATEDIR "/lib/dhcp/dhclient6.leases",
+        LOCALSTATEDIR "/lib/dhclient/dhclient6.leases",
+    };
+    NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(self);
+    int                    i;
+
+    priv->def_leasefile = FILES[0];
+    for (i = 0; i < G_N_ELEMENTS(FILES); i++) {
+        if (g_file_test(FILES[i], G_FILE_TEST_EXISTS)) {
+            priv->def_leasefile = FILES[i];
+            break;
+        }
+    }
+
+    priv->dhcp_listener = g_object_ref(nm_dhcp_listener_get());
+    g_signal_connect(priv->dhcp_listener,
+                     NM_DHCP_LISTENER_EVENT,
+                     G_CALLBACK(nm_dhcp_client_handle_event),
+                     self);
+}
+
+static void
+dispose(GObject *object)
+{
+    NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE(object);
+
+    if (priv->dhcp_listener) {
+        g_signal_handlers_disconnect_by_func(priv->dhcp_listener,
+                                             G_CALLBACK(nm_dhcp_client_handle_event),
+                                             NM_DHCP_DHCLIENT(object));
+        g_clear_object(&priv->dhcp_listener);
+    }
+
+    nm_clear_g_free(&priv->pid_file);
+    nm_clear_g_free(&priv->conf_file);
+    nm_clear_g_free(&priv->lease_file);
+
+    G_OBJECT_CLASS(nm_dhcp_dhclient_parent_class)->dispose(object);
+}
+
+static void
+nm_dhcp_dhclient_class_init(NMDhcpDhclientClass *dhclient_class)
+{
+    NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS(dhclient_class);
+    GObjectClass *     object_class = G_OBJECT_CLASS(dhclient_class);
+
+    object_class->dispose = dispose;
+
+    client_class->ip4_start = ip4_start;
+    client_class->ip6_start = ip6_start;
+    client_class->stop      = stop;
+    client_class->get_duid  = get_duid;
+}
+
+const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = {
+    .name     = "dhclient",
+    .get_type = nm_dhcp_dhclient_get_type,
+    .get_path = nm_dhcp_dhclient_get_path,
+};
+
+#endif /* WITH_DHCLIENT */
Index: radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-dhcpcd.c
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-dhcpcd.c	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/NetworkManager-1.31.3-new/src/core/dhcp/nm-dhcp-dhcpcd.c	(revision 20)
@@ -0,0 +1,230 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2008,2020 Roy Marples <roy@marples.name>
+ * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
+ */
+
+#include "src/core/nm-default-daemon.h"
+
+#if WITH_DHCPCD
+
+    #include <stdlib.h>
+    #include <unistd.h>
+    #include <stdio.h>
+    #include <netinet/in.h>
+    #include <arpa/inet.h>
+
+    #include "nm-dhcp-manager.h"
+    #include "nm-utils.h"
+    #include "NetworkManagerUtils.h"
+    #include "nm-dhcp-listener.h"
+    #include "nm-dhcp-client-logging.h"
+
+/*****************************************************************************/
+
+    #define NM_TYPE_DHCP_DHCPCD (nm_dhcp_dhcpcd_get_type())
+    #define NM_DHCP_DHCPCD(obj) \
+        (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcd))
+    #define NM_DHCP_DHCPCD_CLASS(klass) \
+        (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcdClass))
+    #define NM_IS_DHCP_DHCPCD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCPCD))
+    #define NM_IS_DHCP_DHCPCD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_DHCP_DHCPCD))
+    #define NM_DHCP_DHCPCD_GET_CLASS(obj) \
+        (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcdClass))
+
+typedef struct _NMDhcpDhcpcd      NMDhcpDhcpcd;
+typedef struct _NMDhcpDhcpcdClass NMDhcpDhcpcdClass;
+
+static GType nm_dhcp_dhcpcd_get_type(void);
+
+/*****************************************************************************/
+
+typedef struct {
+    NMDhcpListener *dhcp_listener;
+} NMDhcpDhcpcdPrivate;
+
+struct _NMDhcpDhcpcd {
+    NMDhcpClient        parent;
+    NMDhcpDhcpcdPrivate _priv;
+};
+
+struct _NMDhcpDhcpcdClass {
+    NMDhcpClientClass parent;
+};
+
+G_DEFINE_TYPE(NMDhcpDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT)
+
+    #define NM_DHCP_DHCPCD_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDhcpDhcpcd, NM_IS_DHCP_DHCPCD)
+
+/*****************************************************************************/
+
+static const char *
+nm_dhcp_dhcpcd_get_path(void)
+{
+    return nm_utils_find_helper("dhcpcd", DHCPCD_PATH, NULL);
+}
+
+static gboolean
+ip4_start(NMDhcpClient *client,
+          const char *  dhcp_anycast_addr,
+          const char *  last_ip4_address,
+          GError **     error)
+{
+    NMDhcpDhcpcd *    self            = NM_DHCP_DHCPCD(client);
+    gs_unref_ptrarray GPtrArray *argv = NULL;
+    pid_t                        pid;
+    GError *                     local;
+    gs_free char *               cmd_str = NULL;
+    const char *                 iface;
+    const char *                 dhcpcd_path;
+    const char *                 hostname;
+
+    pid = nm_dhcp_client_get_pid(client);
+    g_return_val_if_fail(pid == -1, FALSE);
+
+    iface = nm_dhcp_client_get_iface(client);
+
+    dhcpcd_path = nm_dhcp_dhcpcd_get_path();
+    if (!dhcpcd_path) {
+        nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "dhcpcd binary not found");
+        return FALSE;
+    }
+
+    argv = g_ptr_array_new();
+    g_ptr_array_add(argv, (gpointer) dhcpcd_path);
+
+    /* Don't configure anything, we will do that instead.
+     * This requires dhcpcd-9.3.3 or newer.
+     * Older versions only had an option not to install a default route,
+     * dhcpcd still added addresses and other routes so we no longer support that
+     * as it doesn't fit how NetworkManager wants to work.
+     */
+    g_ptr_array_add(argv, (gpointer) "--noconfigure");
+
+    g_ptr_array_add(argv, (gpointer) "-B"); /* Don't background on lease (disable fork()) */
+
+    g_ptr_array_add(argv, (gpointer) "-K"); /* Disable built-in carrier detection */
+
+    g_ptr_array_add(argv, (gpointer) "-L"); /* Disable built-in IPv4LL */
+
+    /* --noarp. Don't request or claim the address by ARP; this also disables IPv4LL. */
+    g_ptr_array_add(argv, (gpointer) "-A");
+
+    g_ptr_array_add(argv, (gpointer) "-c"); /* Set script file */
+    g_ptr_array_add(argv, (gpointer) nm_dhcp_helper_path);
+
+    /* IPv4-only for now.  NetworkManager knows better than dhcpcd when to
+     * run IPv6, and dhcpcd's automatic Router Solicitations cause problems
+     * with devices that don't expect them.
+     */
+    g_ptr_array_add(argv, (gpointer) "-4");
+
+    hostname = nm_dhcp_client_get_hostname(client);
+
+    if (hostname) {
+        if (nm_dhcp_client_get_use_fqdn(client)) {
+            g_ptr_array_add(argv, (gpointer) "-h");
+            g_ptr_array_add(argv, (gpointer) hostname);
+            g_ptr_array_add(argv, (gpointer) "-F");
+            g_ptr_array_add(argv, (gpointer) "both");
+        } else {
+            g_ptr_array_add(argv, (gpointer) "-h");
+            g_ptr_array_add(argv, (gpointer) hostname);
+        }
+    }
+
+    g_ptr_array_add(argv, (gpointer) iface);
+    g_ptr_array_add(argv, NULL);
+
+    _LOGD("running: %s", (cmd_str = g_strjoinv(" ", (char **) argv->pdata)));
+
+    if (!g_spawn_async(NULL,
+                       (char **) argv->pdata,
+                       NULL,
+                       G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL
+                           | G_SPAWN_DO_NOT_REAP_CHILD,
+                       nm_utils_setpgid,
+                       NULL,
+                       &pid,
+                       &local)) {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           "dhcpcd failed to start: %s",
+                           local->message);
+        g_error_free(local);
+        return FALSE;
+    }
+
+    nm_assert(pid > 0);
+    _LOGI("dhcpcd started with pid %d", pid);
+    nm_dhcp_client_watch_child(client, pid);
+    return TRUE;
+}
+
+static void
+stop(NMDhcpClient *client, gboolean release)
+{
+    NMDhcpDhcpcd *self = NM_DHCP_DHCPCD(client);
+    pid_t         pid;
+    int           sig;
+
+    pid = nm_dhcp_client_get_pid(client);
+    if (pid > 1) {
+        sig = release ? SIGALRM : SIGTERM;
+        _LOGD("sending %s to dhcpcd pid %d", sig == SIGALRM ? "SIGALRM" : "SIGTERM", pid);
+
+        /* We need to remove the watch before stopping the process */
+        nm_dhcp_client_stop_watch_child(client, pid);
+
+        nm_dhcp_client_stop_pid(pid, nm_dhcp_client_get_iface(client), sig);
+    }
+}
+
+/*****************************************************************************/
+
+static void
+nm_dhcp_dhcpcd_init(NMDhcpDhcpcd *self)
+{
+    NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE(self);
+
+    priv->dhcp_listener = g_object_ref(nm_dhcp_listener_get());
+    g_signal_connect(priv->dhcp_listener,
+                     NM_DHCP_LISTENER_EVENT,
+                     G_CALLBACK(nm_dhcp_client_handle_event),
+                     self);
+}
+
+static void
+dispose(GObject *object)
+{
+    NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE(object);
+
+    if (priv->dhcp_listener) {
+        g_signal_handlers_disconnect_by_func(priv->dhcp_listener,
+                                             G_CALLBACK(nm_dhcp_client_handle_event),
+                                             NM_DHCP_DHCPCD(object));
+        g_clear_object(&priv->dhcp_listener);
+    }
+
+    G_OBJECT_CLASS(nm_dhcp_dhcpcd_parent_class)->dispose(object);
+}
+
+static void
+nm_dhcp_dhcpcd_class_init(NMDhcpDhcpcdClass *dhcpcd_class)
+{
+    NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS(dhcpcd_class);
+    GObjectClass *     object_class = G_OBJECT_CLASS(dhcpcd_class);
+
+    object_class->dispose = dispose;
+
+    client_class->ip4_start = ip4_start;
+    client_class->stop      = stop;
+}
+
+const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd = {
+    .name     = "dhcpcd",
+    .get_type = nm_dhcp_dhcpcd_get_type,
+    .get_path = nm_dhcp_dhcpcd_get_path,
+};
+
+#endif /* WITH_DHCPCD */
Index: radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/create.patch.sh
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/create.patch.sh	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/create.patch.sh	(revision 20)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=1.31.3
+
+tar --files-from=file.list -xJvf ../NetworkManager-$VERSION.tar.xz
+mv NetworkManager-$VERSION NetworkManager-$VERSION-orig
+
+cp -rf ./NetworkManager-$VERSION-new ./NetworkManager-$VERSION
+
+diff --unified -Nr  NetworkManager-$VERSION-orig  NetworkManager-$VERSION > NetworkManager-$VERSION-dhcpcd-graceful-exit.patch
+
+mv NetworkManager-$VERSION-dhcpcd-graceful-exit.patch ../patches
+
+rm -rf ./NetworkManager-$VERSION
+rm -rf ./NetworkManager-$VERSION-orig

Property changes on: radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/file.list
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/file.list	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/create-1.31.3-dhcpcd-graceful-exit-patch/file.list	(revision 20)
@@ -0,0 +1,4 @@
+NetworkManager-1.31.3/src/core/dhcp/nm-dhcp-client.c
+NetworkManager-1.31.3/src/core/dhcp/nm-dhcp-client.h
+NetworkManager-1.31.3/src/core/dhcp/nm-dhcp-dhclient.c
+NetworkManager-1.31.3/src/core/dhcp/nm-dhcp-dhcpcd.c
Index: radix-1.9/sources/GNOME/core/NetworkManager/patches/README
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/patches/README	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/patches/README	(revision 20)
@@ -0,0 +1,6 @@
+
+/* begin *
+
+   TODO: Leave some comment here.
+
+ * end */
Index: radix-1.9/sources/GNOME/core/NetworkManager/patches
===================================================================
--- radix-1.9/sources/GNOME/core/NetworkManager/patches	(nonexistent)
+++ radix-1.9/sources/GNOME/core/NetworkManager/patches	(revision 20)

Property changes on: radix-1.9/sources/GNOME/core/NetworkManager/patches
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~