kernel-netlink: Retry netlink query while kernel returns EBUSY
authorMartin Willi <martin@revosec.ch>
Mon, 14 Jul 2014 14:50:07 +0000 (16:50 +0200)
committerMartin Willi <martin@revosec.ch>
Fri, 21 Nov 2014 09:55:45 +0000 (10:55 +0100)
If the kernel can't execute a Netlink query because a different query is already
active, it returns EBUSY. As this can happen now as we support parallel queries,
retry on this error condition.

src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c

index 6e1dd8c..9c2e34f 100644 (file)
@@ -211,9 +211,11 @@ CALLBACK(watch, bool,
        return TRUE;
 }
 
-METHOD(netlink_socket_t, netlink_send, status_t,
-       private_netlink_socket_t *this, struct nlmsghdr *in, struct nlmsghdr **out,
-       size_t *out_len)
+/**
+ * Send a netlink request, try once
+ */
+static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in,
+                                                 struct nlmsghdr **out, size_t *out_len)
 {
        struct nlmsghdr *hdr;
        chunk_t result = {};
@@ -277,6 +279,38 @@ METHOD(netlink_socket_t, netlink_send, status_t,
        return SUCCESS;
 }
 
+METHOD(netlink_socket_t, netlink_send, status_t,
+       private_netlink_socket_t *this, struct nlmsghdr *in, struct nlmsghdr **out,
+       size_t *out_len)
+{
+       while (TRUE)
+       {
+               struct nlmsghdr *hdr;
+               status_t status;
+               size_t len;
+
+               status = send_once(this, in, &hdr, &len);
+               if (status != SUCCESS)
+               {
+                       return status;
+               }
+               if (hdr->nlmsg_type == NLMSG_ERROR)
+               {
+                       struct nlmsgerr* err;
+
+                       err = NLMSG_DATA(hdr);
+                       if (err->error == -EBUSY)
+                       {
+                               free(hdr);
+                               continue;
+                       }
+               }
+               *out = hdr;
+               *out_len = len;
+               return SUCCESS;
+       }
+}
+
 METHOD(netlink_socket_t, netlink_send_ack, status_t,
        private_netlink_socket_t *this, struct nlmsghdr *in)
 {