[systemd-commits] 4 commits - src/libsystemd-network src/network src/systemd

Patrik Flykt pflykt at kemper.freedesktop.org
Fri Apr 11 00:57:14 PDT 2014


 src/libsystemd-network/dhcp-protocol.h    |    1 
 src/libsystemd-network/sd-dhcp-client.c   |  123 +++++++++++++++++++++---------
 src/libsystemd-network/sd-ipv4ll.c        |   99 +++++++++++++++++-------
 src/libsystemd-network/test-dhcp-client.c |    4 
 src/network/networkd-link.c               |    4 
 src/systemd/sd-dhcp-client.h              |    5 -
 src/systemd/sd-ipv4ll.h                   |    5 -
 7 files changed, 172 insertions(+), 69 deletions(-)

New commits:
commit 4d978a46693e4f23bc73da6a0bafacfcff2aba05
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Apr 9 13:12:10 2014 +0300

    sd-ipv4ll: Add an explicit stop state for IPv4LL
    
    Add an explicit stop state for IPv4LL so that the user can stop the
    IPv4LL client from the callback. When returning from the callback,
    check also the stop state in order to halt any further protocol
    processing.

diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 37cb802..72289b2 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -61,6 +61,7 @@ typedef enum IPv4LLState {
         IPV4LL_STATE_WAITING_ANNOUNCE,
         IPV4LL_STATE_ANNOUNCING,
         IPV4LL_STATE_RUNNING,
+        IPV4LL_STATE_STOPPED,
         _IPV4LL_STATE_MAX,
         _IPV4LL_STATE_INVALID = -1
 } IPv4LLState;
@@ -264,7 +265,7 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void
                         log_ipv4ll(ll, "ANNOUNCE");
                         ll->claimed_address = ll->address;
                         ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
-                        if (!ll)
+                        if (!ll || ll->state == IPV4LL_STATE_STOPPED)
                                 goto out;
 
                         ll->conflict = 0;
@@ -311,7 +312,7 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void
                 if (conflicted) {
                         log_ipv4ll(ll, "CONFLICT");
                         ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
-                        if (!ll)
+                        if (!ll || ll->state == IPV4LL_STATE_STOPPED)
                                 goto out;
 
                         ll->claimed_address = 0;
@@ -382,7 +383,8 @@ static int ipv4ll_receive_message(sd_event_source *s, int fd,
 int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
         assert_return(ll, -EINVAL);
         assert_return(interface_index >= -1, -EINVAL);
-        assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
+        assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
+                             IPV4LL_STATE_STOPPED), -EBUSY);
 
         ll->index = interface_index;
 
@@ -398,7 +400,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
         if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
                 return 0;
 
-        if (ll->state != IPV4LL_STATE_INIT) {
+        if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) {
                 log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
                            "client, restarting");
                 ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
@@ -500,7 +502,7 @@ error:
 bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
         assert_return(ll, -EINVAL);
 
-        return ll->state != IPV4LL_STATE_INIT;
+        return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
 }
 
 #define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
@@ -511,7 +513,10 @@ int sd_ipv4ll_start (sd_ipv4ll *ll) {
         assert_return(ll, -EINVAL);
         assert_return(ll->event, -EINVAL);
         assert_return(ll->index > 0, -EINVAL);
-        assert_return(ll->state == IPV4LL_STATE_INIT, -EBUSY);
+        assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
+                             IPV4LL_STATE_STOPPED), -EBUSY);
+
+        ll->state = IPV4LL_STATE_INIT;
 
         r = arp_network_bind_raw_socket(ll->index, &ll->link);
 
@@ -572,6 +577,8 @@ out:
 
 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
         ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
+        if (ll)
+                ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1);
 
         return 0;
 }

commit 56cd007ab83749b85670c9c7f560e083980c2ff4
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Apr 9 13:12:09 2014 +0300

    sd-ipv4ll: Add reference counting for IPv4LL
    
    Similar to DHCP, the IPv4LL library user can decide to free the LL
    client any time the callback is called. Guard against freeing the
    LL client in the callback by introducing proper reference counting.
    
    Also update code using the IPv4LL library to properly handle a
    returned NULL from the notify and stop functions if the IPv4LL
    client was freed.

diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index 81fe85b..37cb802 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -26,6 +26,7 @@
 #include "util.h"
 #include "siphash24.h"
 #include "list.h"
+#include "refcnt.h"
 
 #include "ipv4ll-internal.h"
 #include "sd-ipv4ll.h"
@@ -65,6 +66,8 @@ typedef enum IPv4LLState {
 } IPv4LLState;
 
 struct sd_ipv4ll {
+        RefCount n_ref;
+
         IPv4LLState state;
         int index;
         int fd;
@@ -103,16 +106,19 @@ static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) {
         }
 }
 
-static int ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
+static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
         assert(ll);
 
-        if (ll->cb)
+        if (ll->cb) {
+                ll = sd_ipv4ll_ref(ll);
                 ll->cb(ll, event, ll->userdata);
+                ll = sd_ipv4ll_unref(ll);
+        }
 
-        return 0;
+        return ll;
 }
 
-static int ipv4ll_stop(sd_ipv4ll *ll, int event) {
+static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) {
         assert(ll);
 
         ll->receive_message = sd_event_source_unref(ll->receive_message);
@@ -120,15 +126,16 @@ static int ipv4ll_stop(sd_ipv4ll *ll, int event) {
 
         ll->timer = sd_event_source_unref(ll->timer);
 
-        ipv4ll_client_notify(ll, event);
-
-        ll->claimed_address = 0;
+        log_ipv4ll(ll, "STOPPED");
 
-        ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
+        ll = ipv4ll_client_notify(ll, event);
 
-        log_ipv4ll(ll, "STOPPED");
+        if (ll) {
+                ll->claimed_address = 0;
+                ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
+        }
 
-        return 0;
+        return ll;
 }
 
 static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
@@ -256,7 +263,10 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void
                 if (ll->iteration == 0) {
                         log_ipv4ll(ll, "ANNOUNCE");
                         ll->claimed_address = ll->address;
-                        r = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
+                        ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
+                        if (!ll)
+                                goto out;
+
                         ll->conflict = 0;
                 }
 
@@ -300,7 +310,10 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void
 
                 if (conflicted) {
                         log_ipv4ll(ll, "CONFLICT");
-                        r = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
+                        ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
+                        if (!ll)
+                                goto out;
+
                         ll->claimed_address = 0;
 
                         /* Pick a new address */
@@ -341,7 +354,7 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void
         }
 
 out:
-        if (r < 0)
+        if (r < 0 && ll)
                 ipv4ll_stop(ll, r);
 }
 
@@ -388,10 +401,13 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
         if (ll->state != IPV4LL_STATE_INIT) {
                 log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
                            "client, restarting");
-                sd_ipv4ll_stop(ll);
+                ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
                 need_restart = true;
         }
 
+        if (!ll)
+                return 0;
+
         memcpy(&ll->mac_addr, addr, ETH_ALEN);
 
         if (need_restart)
@@ -555,23 +571,40 @@ out:
 }
 
 int sd_ipv4ll_stop(sd_ipv4ll *ll) {
-        return ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
+        ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
+
+        return 0;
 }
 
-void sd_ipv4ll_free (sd_ipv4ll *ll) {
-        if (!ll)
-                return;
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
+        if (ll)
+                assert_se(REFCNT_INC(ll->n_ref) >= 2);
 
-        sd_ipv4ll_stop(ll);
-        sd_ipv4ll_detach_event(ll);
+        return ll;
+}
 
-        free(ll->random_data);
-        free(ll->random_data_state);
-        free(ll);
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
+        if (ll && REFCNT_DEC(ll->n_ref) <= 0) {
+                ll->receive_message =
+                        sd_event_source_unref(ll->receive_message);
+                ll->fd = safe_close(ll->fd);
+
+                ll->timer = sd_event_source_unref(ll->timer);
+
+                sd_ipv4ll_detach_event(ll);
+
+                free(ll->random_data);
+                free(ll->random_data_state);
+                free(ll);
+
+                return NULL;
+        }
+
+        return ll;
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_free);
-#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_freep)
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
+#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
 
 int sd_ipv4ll_new(sd_ipv4ll **ret) {
         _cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
@@ -582,6 +615,7 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
         if (!ll)
                 return -ENOMEM;
 
+        ll->n_ref = REFCNT_INIT;
         ll->state = IPV4LL_STATE_INIT;
         ll->index = -1;
         ll->fd = -1;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 2630b46..684e1e5 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -83,7 +83,7 @@ void link_free(Link *link) {
         sd_dhcp_client_unref(link->dhcp_client);
         sd_dhcp_lease_unref(link->dhcp_lease);
 
-        sd_ipv4ll_free(link->ipv4ll);
+        sd_ipv4ll_unref(link->ipv4ll);
 
         hashmap_remove(link->manager->links, &link->ifindex);
 
diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h
index 28405a1..d017158 100644
--- a/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/sd-ipv4ll.h
@@ -47,7 +47,8 @@ int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint8_t seed[8]);
 bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
 int sd_ipv4ll_start(sd_ipv4ll *ll);
 int sd_ipv4ll_stop(sd_ipv4ll *ll);
-void sd_ipv4ll_free(sd_ipv4ll *ll);
-int sd_ipv4ll_new(sd_ipv4ll **ret);
+sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
+sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
+int sd_ipv4ll_new (sd_ipv4ll **ret);
 
 #endif

commit 781ca7a13feb01739a9aa8b629ff1ff7c51e74aa
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Apr 9 13:12:08 2014 +0300

    sd-dhcp-client: Add an explicit stop state for the DHCP client
    
    Add an explicit stop state for the DHCP client so that the library
    user can issue a stop at any time the callback has been called.
    When returning from the callback, check also the stop state and
    stop any further DHCP processing.

diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index 400e953..539606c 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -76,6 +76,7 @@ enum DHCPState {
         DHCP_STATE_BOUND                        = 5,
         DHCP_STATE_RENEWING                     = 6,
         DHCP_STATE_REBINDING                    = 7,
+        DHCP_STATE_STOPPED                      = 8,
 };
 
 typedef enum DHCPState DHCPState;
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 512bc93..afad776 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -96,7 +96,8 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
         size_t i;
 
         assert_return(client, -EINVAL);
-        assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
+        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+                              DHCP_STATE_STOPPED), -EBUSY);
 
         switch(option) {
         case DHCP_OPTION_PAD:
@@ -126,7 +127,8 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
                                        const struct in_addr *last_addr) {
         assert_return(client, -EINVAL);
-        assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+                              DHCP_STATE_STOPPED), -EBUSY);
 
         if (last_addr)
                 client->last_addr = last_addr->s_addr;
@@ -138,7 +140,8 @@ int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
 
 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
         assert_return(client, -EINVAL);
-        assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
+        assert_return (IN_SET(client->state, DHCP_STATE_INIT,
+                              DHCP_STATE_STOPPED), -EBUSY);
         assert_return(interface_index >= -1, -EINVAL);
 
         client->index = interface_index;
@@ -156,7 +159,7 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
         if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
                 return 0;
 
-        if (client->state != DHCP_STATE_INIT) {
+        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
                 log_dhcp_client(client, "Changing MAC address on running DHCP "
                                 "client, restarting");
                 need_restart = true;
@@ -169,7 +172,7 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
         memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
         client->client_id.type = 0x01;
 
-        if (need_restart)
+        if (need_restart && client->state != DHCP_STATE_STOPPED)
                 sd_dhcp_client_start(client);
 
         return 0;
@@ -404,6 +407,9 @@ static int client_send_request(sd_dhcp_client *client) {
         case DHCP_STATE_REBINDING:
 
                 break;
+
+        case DHCP_STATE_STOPPED:
+                return -EINVAL;
         }
 
         r = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
@@ -482,6 +488,10 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
                 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
 
                 break;
+
+        case DHCP_STATE_STOPPED:
+                r = -EINVAL;
+                goto error;
         }
 
         next_timeout += (random_u32() & 0x1fffff);
@@ -540,6 +550,10 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
         case DHCP_STATE_BOUND:
 
                 break;
+
+        case DHCP_STATE_STOPPED:
+                r = -EINVAL;
+                goto error;
         }
 
         return 0;
@@ -630,8 +644,8 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec,
 
         client = client_notify(client, DHCP_EVENT_EXPIRED);
 
-        /* lease was lost, start over if not freed */
-        if (client) {
+        /* lease was lost, start over if not freed or stopped in callback */
+        if (client && client->state != DHCP_STATE_STOPPED) {
                 client_initialize(client);
                 client_start(client);
         }
@@ -1046,7 +1060,8 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
 
                         if (notify_event) {
                                 client = client_notify(client, notify_event);
-                                if (!client)
+                                if (!client ||
+                                    client->state == DHCP_STATE_STOPPED)
                                         return 0;
                         }
 
@@ -1064,6 +1079,10 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
         case DHCP_STATE_BOUND:
 
                 break;
+
+        case DHCP_STATE_STOPPED:
+                r = -EINVAL;
+                goto error;
         }
 
 error:
@@ -1177,7 +1196,8 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
 int sd_dhcp_client_stop(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
 
-        client_stop(client, DHCP_EVENT_STOP);
+        if (client_stop(client, DHCP_EVENT_STOP))
+                client->state = DHCP_STATE_STOPPED;
 
         return 0;
 }

commit e5b04c8de83aa17e324bae95f18a822931fb8078
Author: Patrik Flykt <patrik.flykt at linux.intel.com>
Date:   Wed Apr 9 13:12:07 2014 +0300

    sd-dhcp-client: Add reference counting for DHCP
    
    The DHCP library user can decide to free the DHCP client any time
    the callback is called. After the callback has been called, other
    computations may still be needed - the best example being a full
    restart of the DHCP procedure in case of lease expiry.
    
    Fix this by introducing proper reference counting. Properly handle
    a returned NULL from the notify and stop functions if the DHCP
    client was freed.

diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 392e294..512bc93 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -28,6 +28,7 @@
 
 #include "util.h"
 #include "list.h"
+#include "refcnt.h"
 
 #include "dhcp-protocol.h"
 #include "dhcp-internal.h"
@@ -35,6 +36,8 @@
 #include "sd-dhcp-client.h"
 
 struct sd_dhcp_client {
+        RefCount n_ref;
+
         DHCPState state;
         sd_event *event;
         int event_priority;
@@ -77,6 +80,7 @@ static int client_receive_message_raw(sd_event_source *s, int fd,
                                       uint32_t revents, void *userdata);
 static int client_receive_message_udp(sd_event_source *s, int fd,
                                       uint32_t revents, void *userdata);
+static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error);
 
 int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
                                 void *userdata) {
@@ -155,10 +159,13 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
         if (client->state != DHCP_STATE_INIT) {
                 log_dhcp_client(client, "Changing MAC address on running DHCP "
                                 "client, restarting");
-                sd_dhcp_client_stop(client);
                 need_restart = true;
+                client = client_stop(client, DHCP_EVENT_STOP);
         }
 
+        if (!client)
+                return 0;
+
         memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
         client->client_id.type = 0x01;
 
@@ -182,11 +189,14 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
         return 0;
 }
 
-static int client_notify(sd_dhcp_client *client, int event) {
-        if (client->cb)
+static sd_dhcp_client *client_notify(sd_dhcp_client *client, int event) {
+        if (client->cb) {
+                client = sd_dhcp_client_ref(client);
                 client->cb(client, event, client->userdata);
+                client = sd_dhcp_client_unref(client);
+        }
 
-        return 0;
+        return client;
 }
 
 static int client_initialize(sd_dhcp_client *client) {
@@ -214,16 +224,17 @@ static int client_initialize(sd_dhcp_client *client) {
         return 0;
 }
 
-static int client_stop(sd_dhcp_client *client, int error) {
-        assert_return(client, -EINVAL);
+static sd_dhcp_client *client_stop(sd_dhcp_client *client, int error) {
+        assert_return(client, NULL);
 
-        client_notify(client, error);
+        log_dhcp_client(client, "STOPPED %d", error);
 
-        client_initialize(client);
+        client = client_notify(client, error);
 
-        log_dhcp_client(client, "STOPPED");
+        if (client)
+                client_initialize(client);
 
-        return 0;
+        return client;
 }
 
 static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
@@ -617,11 +628,13 @@ static int client_timeout_expire(sd_event_source *s, uint64_t usec,
 
         log_dhcp_client(client, "EXPIRED");
 
-        client_notify(client, DHCP_EVENT_EXPIRED);
+        client = client_notify(client, DHCP_EVENT_EXPIRED);
 
-        /* start over as the lease was lost */
-        client_initialize(client);
-        client_start(client);
+        /* lease was lost, start over if not freed */
+        if (client) {
+                client_initialize(client);
+                client_start(client);
+        }
 
         return 0;
 }
@@ -1031,8 +1044,11 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
                         if (r < 0)
                                 goto error;
 
-                        if (notify_event)
-                                client_notify(client, notify_event);
+                        if (notify_event) {
+                                client = client_notify(client, notify_event);
+                                if (!client)
+                                        return 0;
+                        }
 
                         client->receive_message =
                                 sd_event_source_unref(client->receive_message);
@@ -1052,9 +1068,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
 
 error:
         if (r < 0 || r == DHCP_EVENT_NO_LEASE)
-                return client_stop(client, r);
+                client_stop(client, r);
 
-        return 0;
+        return r;
 }
 
 static int client_receive_message_udp(sd_event_source *s, int fd,
@@ -1159,7 +1175,11 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
 }
 
 int sd_dhcp_client_stop(sd_dhcp_client *client) {
-        return client_stop(client, DHCP_EVENT_STOP);
+        assert_return(client, -EINVAL);
+
+        client_stop(client, DHCP_EVENT_STOP);
+
+        return 0;
 }
 
 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
@@ -1197,19 +1217,35 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
         return client->event;
 }
 
-void sd_dhcp_client_free(sd_dhcp_client *client) {
-        if (!client)
-                return;
+sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
+        if (client)
+                assert_se(REFCNT_INC(client->n_ref) >= 2);
+
+        return client;
+}
+
+sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
+        if (client && REFCNT_DEC(client->n_ref) <= 0) {
+                log_dhcp_client(client, "UNREF");
 
-        sd_dhcp_client_stop(client);
-        sd_dhcp_client_detach_event(client);
+                client_initialize(client);
+
+                client->receive_message =
+                        sd_event_source_unref(client->receive_message);
+
+                sd_dhcp_client_detach_event(client);
+
+                free(client->req_opts);
+                free(client);
+
+                return NULL;
+        }
 
-        free(client->req_opts);
-        free(client);
+        return client;
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
-#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref);
+#define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_unrefp)
 
 int sd_dhcp_client_new(sd_dhcp_client **ret) {
         _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
@@ -1220,6 +1256,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
         if (!client)
                 return -ENOMEM;
 
+        client->n_ref = REFCNT_INIT;
         client->state = DHCP_STATE_INIT;
         client->index = -1;
         client->fd = -1;
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index 71b06b1..9c316d7 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -251,7 +251,7 @@ static void test_discover_message(sd_event *e)
         sd_event_run(e, (uint64_t) -1);
 
         sd_dhcp_client_stop(client);
-        sd_dhcp_client_free(client);
+        sd_dhcp_client_unref(client);
 
         test_fd[1] = safe_close(test_fd[1]);
 
@@ -476,7 +476,7 @@ static void test_addr_acq(sd_event *e) {
 
         sd_dhcp_client_set_callback(client, NULL, NULL);
         sd_dhcp_client_stop(client);
-        sd_dhcp_client_free(client);
+        sd_dhcp_client_unref(client);
 
         test_fd[1] = safe_close(test_fd[1]);
 
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index f076525..2630b46 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -80,7 +80,7 @@ void link_free(Link *link) {
 
         assert(link->manager);
 
-        sd_dhcp_client_free(link->dhcp_client);
+        sd_dhcp_client_unref(link->dhcp_client);
         sd_dhcp_lease_unref(link->dhcp_lease);
 
         sd_ipv4ll_free(link->ipv4ll);
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index a60bb58..5818ec4 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -54,7 +54,10 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret);
 
 int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
-void sd_dhcp_client_free(sd_dhcp_client *client);
+
+sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);
+sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
+
 int sd_dhcp_client_new(sd_dhcp_client **ret);
 
 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority);



More information about the systemd-commits mailing list