chiark / gitweb /
sd-rtnl: never set serial to 0
[elogind.git] / src / libsystemd / sd-rtnl / sd-rtnl.c
index a45ca5e9f57370528836a1fc8aa6be862e8cc124..f483af991bc2f8e46861c77641703047d06249bb 100644 (file)
@@ -61,6 +61,36 @@ static int sd_rtnl_new(sd_rtnl **ret) {
                             sizeof(struct nlmsghdr), sizeof(uint8_t)))
                 return -ENOMEM;
 
+        /* Change notification responses have sequence 0, so we must
+         * start our request sequence numbers at 1, or we may confuse our
+         * responses with notifications from the kernel */
+        rtnl->serial = 1;
+
+        *ret = rtnl;
+        rtnl = NULL;
+
+        return 0;
+}
+
+int sd_rtnl_new_from_netlink(sd_rtnl **ret, int fd) {
+        _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
+        socklen_t addrlen;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        r = sd_rtnl_new(&rtnl);
+        if (r < 0)
+                return r;
+
+        addrlen = sizeof(rtnl->sockaddr);
+
+        r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
+        if (r < 0)
+                return -errno;
+
+        rtnl->fd = fd;
+
         *ret = rtnl;
         rtnl = NULL;
 
@@ -121,12 +151,13 @@ static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap)
         addrlen = sizeof(rtnl->sockaddr);
 
         r = bind(fd, &rtnl->sockaddr.sa, addrlen);
-        if (r < 0)
+        /* ignore EINVAL to allow opening an already bound socket */
+        if (r < 0 && errno != EINVAL)
                 return -errno;
 
         r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
         if (r < 0)
-                return r;
+                return -errno;
 
         rtnl->fd = fd;
 
@@ -187,7 +218,7 @@ sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) {
 
         assert_return(!rtnl_pid_changed(rtnl), NULL);
 
-        if (REFCNT_DEC(rtnl->n_ref) <= 0) {
+        if (REFCNT_DEC(rtnl->n_ref) == 0) {
                 struct match_callback *f;
                 unsigned i;
 
@@ -231,7 +262,7 @@ static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) {
         assert(m);
         assert(m->hdr);
 
-        m->hdr->nlmsg_seq = rtnl->serial++;
+        m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++;
 
         rtnl_message_seal(m);
 
@@ -379,9 +410,12 @@ static int process_timeout(sd_rtnl *rtnl) {
         hashmap_remove(rtnl->reply_callbacks, &c->serial);
 
         r = c->callback(rtnl, m, c->userdata);
+        if (r < 0)
+                log_debug_errno(r, "sd-rtnl: timedout callback failed: %m");
+
         free(c);
 
-        return r < 0 ? r : 1;
+        return 1;
 }
 
 static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
@@ -404,9 +438,12 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
                 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
 
         r = c->callback(rtnl, m, c->userdata);
+        if (r < 0)
+                log_debug_errno(r, "sd-rtnl: callback failed: %m");
+
         free(c);
 
-        return r;
+        return 1;
 }
 
 static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
@@ -424,12 +461,16 @@ static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) {
         LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
                 if (type == c->type) {
                         r = c->callback(rtnl, m, c->userdata);
-                        if (r != 0)
-                                return r;
+                        if (r != 0) {
+                                if (r < 0)
+                                        log_debug_errno(r, "sd-rtnl: match callback failed: %m");
+
+                                break;
+                        }
                 }
         }
 
-        return 0;
+        return 1;
 }
 
 static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) {
@@ -957,7 +998,7 @@ int sd_rtnl_add_match(sd_rtnl *rtnl,
         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
         assert_return(rtnl_message_type_is_link(type) ||
                       rtnl_message_type_is_addr(type) ||
-                      rtnl_message_type_is_route(type), -ENOTSUP);
+                      rtnl_message_type_is_route(type), -EOPNOTSUPP);
 
         c = new0(struct match_callback, 1);
         if (!c)