chiark / gitweb /
sd-rtnl: fix broken test cases and add support for tunnel
[elogind.git] / src / libsystemd / sd-rtnl / sd-rtnl.c
index 04fcb3d002703f7ffacac96797d7a2befcc41bab..2ab9d90aa790d4f76360d6adc45111d2b6a6fe1b 100644 (file)
@@ -31,7 +31,7 @@
 #include "rtnl-util.h"
 
 static int sd_rtnl_new(sd_rtnl **ret) {
-        sd_rtnl *rtnl;
+        _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
 
         assert_return(ret, -EINVAL);
 
@@ -52,12 +52,12 @@ static int sd_rtnl_new(sd_rtnl **ret) {
         /* We guarantee that wqueue always has space for at least
          * one entry */
         rtnl->wqueue = new(sd_rtnl_message*, 1);
-        if (!rtnl->wqueue) {
-                free(rtnl);
+        if (!rtnl->wqueue)
                 return -ENOMEM;
-        }
 
         *ret = rtnl;
+        rtnl = NULL;
+
         return 0;
 }
 
@@ -70,8 +70,8 @@ static bool rtnl_pid_changed(sd_rtnl *rtnl) {
         return rtnl->original_pid != getpid();
 }
 
-int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
-        _cleanup_sd_rtnl_unref_ sd_rtnl *rtnl = NULL;
+int sd_rtnl_open(sd_rtnl **ret, uint32_t groups) {
+        _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
         socklen_t addrlen;
         int r;
 
@@ -104,6 +104,9 @@ int sd_rtnl_open(uint32_t groups, sd_rtnl **ret) {
 }
 
 sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
+        assert_return(rtnl, NULL);
+        assert_return(!rtnl_pid_changed(rtnl), NULL);
+
         if (rtnl)
                 assert_se(REFCNT_INC(rtnl->n_ref) >= 2);
 
@@ -111,8 +114,12 @@ sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) {
 }
 
 sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
+        if (!rtnl)
+                return NULL;
 
-        if (rtnl && REFCNT_DEC(rtnl->n_ref) <= 0) {
+        assert_return(!rtnl_pid_changed(rtnl), NULL);
+
+        if (REFCNT_DEC(rtnl->n_ref) <= 0) {
                 struct match_callback *f;
                 unsigned i;
 
@@ -127,20 +134,36 @@ sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
                 hashmap_free_free(rtnl->reply_callbacks);
                 prioq_free(rtnl->reply_callbacks_prioq);
 
+                sd_event_source_unref(rtnl->io_event_source);
+                sd_event_source_unref(rtnl->time_event_source);
+                sd_event_source_unref(rtnl->exit_event_source);
+                sd_event_unref(rtnl->event);
+
                 while ((f = rtnl->match_callbacks)) {
                         LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f);
                         free(f);
                 }
 
-                if (rtnl->fd >= 0)
-                        close_nointr_nofail(rtnl->fd);
-
+                safe_close(rtnl->fd);
                 free(rtnl);
         }
 
         return NULL;
 }
 
+static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) {
+        assert(rtnl);
+        assert(!rtnl_pid_changed(rtnl));
+        assert(m);
+        assert(m->hdr);
+
+        m->hdr->nlmsg_seq = rtnl->serial++;
+
+        rtnl_message_seal(m);
+
+        return;
+}
+
 int sd_rtnl_send(sd_rtnl *nl,
                  sd_rtnl_message *message,
                  uint32_t *serial) {
@@ -149,10 +172,9 @@ int sd_rtnl_send(sd_rtnl *nl,
         assert_return(nl, -EINVAL);
         assert_return(!rtnl_pid_changed(nl), -ECHILD);
         assert_return(message, -EINVAL);
+        assert_return(!message->sealed, -EPERM);
 
-        r = rtnl_message_seal(nl, message);
-        if (r < 0)
-                return r;
+        rtnl_seal_message(nl, message);
 
         if (nl->wqueue_size <= 0) {
                 /* send directly */
@@ -205,10 +227,8 @@ static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) {
 
         /* Try to read a new message */
         r = socket_read_message(rtnl, &z);
-        if (r < 0)
+        if (r <= 0)
                 return r;
-        if (r == 0)
-                return 0;
 
         *message = z;
 
@@ -241,7 +261,7 @@ static int dispatch_wqueue(sd_rtnl *rtnl) {
 }
 
 static int process_timeout(sd_rtnl *rtnl) {
-        _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+        _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
         struct reply_callback *c;
         usec_t n;
         int r;
@@ -277,6 +297,9 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
         assert(rtnl);
         assert(m);
 
+        if (sd_rtnl_message_is_broadcast(m))
+                return 0;
+
         serial = rtnl_message_get_serial(m);
         c = hashmap_remove(rtnl->reply_callbacks, &serial);
         if (!c)
@@ -315,7 +338,7 @@ static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
 }
 
 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
-        _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *m = NULL;
+        _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
         int r;
 
         assert(rtnl);
@@ -557,7 +580,7 @@ int sd_rtnl_call(sd_rtnl *nl,
 
         for (;;) {
                 usec_t left;
-                _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
+                _cleanup_rtnl_message_unref_ sd_rtnl_message *incoming = NULL;
 
                 if (!room) {
                         sd_rtnl_message **q;
@@ -774,7 +797,7 @@ int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
                         return r;
         }
 
-        r = sd_event_add_io(rtnl->event, rtnl->fd, 0, io_callback, rtnl, &rtnl->io_event_source);
+        r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
         if (r < 0)
                 goto fail;
 
@@ -786,7 +809,7 @@ int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
         if (r < 0)
                 goto fail;
 
-        r = sd_event_add_monotonic(rtnl->event, 0, 0, time_callback, rtnl, &rtnl->time_event_source);
+        r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
         if (r < 0)
                 goto fail;
 
@@ -794,7 +817,7 @@ int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) {
         if (r < 0)
                 goto fail;
 
-        r = sd_event_add_exit(rtnl->event, exit_callback, rtnl, &rtnl->exit_event_source);
+        r = sd_event_add_exit(rtnl->event, &rtnl->exit_event_source, exit_callback, rtnl);
         if (r < 0)
                 goto fail;