[libvirt] [PATCH] Changes squashed into netlink event service patches

Laine Stump laine at laine.org
Wed Feb 29 09:00:36 UTC 2012


Squashed into "util: Add netlink event handling to virnetlink.c" (Note
that I am also re-sending the complete patchset with these changes
already squashed in).

* The remove callback is now an argument of
  virNetlinnkEventAddClient instead of virNetlinkeRemoveClient,
  and is stored in the virNetlinkEventHandle. This way the event
  service shutdown can properly remove any remaining clients to
  avoid memory leaks.

* Several small whitespace changes (moving the opening { of
  functions down to a separate line, for example).

* As said above, the service stop function now removes any
  remaining clients on the list.

* The libvirt convention to check for a failure return from a
  function is (ret < 0) rather than (ret == -1). That was changed
  in two places.

* If macaddr isn't given when adding a client, it is initialized
  to all 0's (on a clean init, it would be 0 anyway, but if the
  entry is being re-used, it would have stale data from the
  previous use).

* In virNetlinkEventRemoveClient, the to if() clauses inside the
  for loop had identical bodies (except for one word in the debug
  message). These have been combined into a single if() clause.

* Again in virNetlinkEventRemoveClient, the opaque object was
  being freed by the event service code, which is supposed to
  treat opaque as a complete unknown. This is no longer done - if
  opaque is dynamically allocated data that needs to be freed,
  this should be done in the client-supplied removal callback (as
  noted earlier, the client has been appropriately changed).

Changes squashed into "Add de-association handling to macvlan code"

* In the virNetCallbackData struct, the pointers to data that are
  in the domain's config object are a bit dangerous - it's
  possible that the domain's config could be updated while the
  domain remains active, freeing the original config object and
  allocating a new one. This would leave the
  virNetlinkCallbackData pointing at garbage data. To fix this,
  the struct has had all consts removed, the object is
  initialized by allocating new memory for each of these items
  and copying (rather than just pointing at the original), and
  the remove callback frees all of the items. The result is that
  the callback data object is completely decoupled from the config
  object.

* Since the callback data object needs to be freed both in the
  remove callback as well as in the error path at the time the
  client is added, a separate virNetlinkCallbackDataFree()
  function has been added. The remove callback now simply calls
  that function.

* Previously the callback data object itself was freed by the
  netlink event service code as the client was being removed. This
  makes it impossible to use the supposedly "opaque" data as
  anything other than a pure pointer to dynamically allocated
  data (a client might want to use it simply as a cookie, or
  pointer to a piece of static data). Now this object is freed by
  the client's remove callback function instead.

* The error path of virNetDevMacVLanCreateWithVPortProfile had
  been missing a call to free the callback data object if the
  strdup(cr_ifname) failed. Now with even more error cases (since
  all the other fields are also dynamically allocated), it became
  even more apparent that disassociate_exit: needed to do
  this. Since all of these cases need to call
  virReportOOMError(), I factored that out into a new goto
  label "memory_error".

* Due to a change in the netlink event service code, the remove callback
  function is now given when calling virNetlinkEventAddClient
  rather than when calling virNetlinkEventRemoveClient.
---
 src/util/virnetdevmacvlan.c |   79 +++++++++++------
 src/util/virnetlink.c       |  197 ++++++++++++++++++++++++-------------------
 src/util/virnetlink.h       |    7 +-
 3 files changed, 165 insertions(+), 118 deletions(-)

diff --git a/src/util/virnetdevmacvlan.c b/src/util/virnetdevmacvlan.c
index 1c4d082..d36b326 100644
--- a/src/util/virnetdevmacvlan.c
+++ b/src/util/virnetdevmacvlan.c
@@ -450,9 +450,9 @@ static const uint32_t modeMap[VIR_NETDEV_MACVLAN_MODE_LAST] = {
 struct virNetlinkCallbackData {
     char *cr_ifname;
     virNetDevVPortProfilePtr virtPortProfile;
-    const unsigned char *macaddress;
-    const char *linkdev;
-    const unsigned char *vmuuid;
+    unsigned char *macaddress;
+    char *linkdev;
+    unsigned char *vmuuid;
     enum virNetDevVPortProfileOp vmOp;
     unsigned int linkState;
 };
@@ -726,6 +726,29 @@ virNetDevMacVLanVPortProfileCallback(unsigned char *msg,
 }
 
 /**
+ * virNetlinkCallbackDataFree
+ *
+ * @calld: pointer to a virNetlinkCallbackData object to free
+ *
+ * This function frees all the data associated with a virNetlinkCallbackData object
+ * as well as the object itself. If called with NULL, it does nothing.
+ *
+ * Returns nothing.
+ */
+static void
+virNetlinkCallbackDataFree(virNetlinkCallbackDataPtr calld)
+{
+    if (calld) {
+        VIR_FREE(calld->cr_ifname);
+        VIR_FREE(calld->virtPortProfile);
+        VIR_FREE(calld->macaddress);
+        VIR_FREE(calld->linkdev);
+        VIR_FREE(calld->vmuuid);
+    }
+    VIR_FREE(calld);
+}
+
+/**
  * virNetDevMacVLanVPortProfileDestroyCallback:
  *
  * @watch: watch whose handle to remove
@@ -734,18 +757,14 @@ virNetDevMacVLanVPortProfileCallback(unsigned char *msg,
  *
  * This function is called when a netlink message handler is terminated.
  * The function frees locally allocated data referenced in the opaque
- * data.
+ * data, and the opaque object itself.
  */
 static void
 virNetDevMacVLanVPortProfileDestroyCallback(int watch ATTRIBUTE_UNUSED,
                                             const unsigned char *macaddr ATTRIBUTE_UNUSED,
                                             void *opaque)
 {
-    virNetlinkCallbackDataPtr calld = opaque;
-
-    if (calld) {
-        VIR_FREE(calld->cr_ifname);
-    }
+    virNetlinkCallbackDataFree((virNetlinkCallbackDataPtr)opaque);
 }
 
 
@@ -789,6 +808,7 @@ int virNetDevMacVLanCreateWithVPortProfile(const char *tgifname,
     int retries, do_retry = 0;
     uint32_t macvtapMode;
     const char *cr_ifname;
+    virNetlinkCallbackDataPtr calld = NULL;
     int ret;
 
     macvtapMode = modeMap[mode];
@@ -894,30 +914,35 @@ create_name:
     }
 
     if (virNetlinkEventServiceIsRunning()) {
-        virNetlinkCallbackDataPtr calld;
+        if (VIR_ALLOC(calld) < 0)
+            goto memory_error;
+        if ((calld->cr_ifname = strdup(cr_ifname)) == NULL)
+            goto memory_error;
+        if (VIR_ALLOC(calld->virtPortProfile) < 0)
+            goto memory_error;
+        memcpy(calld->virtPortProfile, virtPortProfile, sizeof(*virtPortProfile));
+        if (VIR_ALLOC_N(calld->macaddress, VIR_MAC_BUFLEN) < 0)
+            goto memory_error;
+        memcpy(calld->macaddress, macaddress, VIR_MAC_BUFLEN);
+        if ((calld->linkdev = strdup(linkdev)) == NULL)
+            goto  memory_error;
+        if (VIR_ALLOC_N(calld->vmuuid, VIR_UUID_BUFLEN) < 0)
+            goto memory_error;
+        memcpy(calld->vmuuid, vmuuid, VIR_UUID_BUFLEN);
 
-        if (VIR_ALLOC(calld) < 0) {
-            virReportOOMError();
-            goto disassociate_exit;
-        }
-
-        calld->cr_ifname=strdup(cr_ifname);
-        if (calld->cr_ifname == NULL) {
-            virReportOOMError();
-            goto disassociate_exit;
-        }
-
-        calld->virtPortProfile = virtPortProfile;
-        calld->macaddress = macaddress;
-        calld->linkdev = linkdev;
-        calld->vmuuid = vmuuid;
         calld->vmOp = vmOp;
 
-        virNetlinkEventAddClient(virNetDevMacVLanVPortProfileCallback, calld, macaddress);
+        virNetlinkEventAddClient(virNetDevMacVLanVPortProfileCallback,
+                                 virNetDevMacVLanVPortProfileDestroyCallback,
+                                 calld, macaddress);
     }
 
     return rc;
 
+ memory_error:
+    virReportOOMError();
+    virNetlinkCallbackDataFree(calld);
+
 disassociate_exit:
     ignore_value(virNetDevVPortProfileDisassociate(cr_ifname,
                                                    virtPortProfile,
@@ -965,7 +990,7 @@ int virNetDevMacVLanDeleteWithVPortProfile(const char *ifname,
             ret = -1;
     }
 
-    virNetlinkEventRemoveClient(0, macaddr, virNetDevMacVLanVPortProfileDestroyCallback);
+    virNetlinkEventRemoveClient(0, macaddr);
 
     return ret;
 }
diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c
index de6a135..fd6f751 100644
--- a/src/util/virnetlink.c
+++ b/src/util/virnetlink.c
@@ -53,7 +53,8 @@
 /* State for a single netlink event handle */
 struct virNetlinkEventHandle {
     int watch;
-    virNetlinkEventHandleCallback cb;
+    virNetlinkEventHandleCallback handleCB;
+    virNetlinkEventRemoveCallback removeCB;
     void *opaque;
     unsigned char macaddr[VIR_MAC_BUFLEN];
     int deleted;
@@ -182,20 +183,49 @@ error:
 }
 
 static void
-virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver) {
+virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver)
+{
     virMutexLock(&driver->lock);
 }
 
 static void
-virNetlinkEventServerUnlock(virNetlinkEventSrvPrivatePtr driver) {
+virNetlinkEventServerUnlock(virNetlinkEventSrvPrivatePtr driver)
+{
     virMutexUnlock(&driver->lock);
 }
 
+/**
+ * virNetlinkEventRemoveClientPrimitive:
+ *
+ * @i: index of the client to remove from the table
+ *
+ * This static function does the low level removal of a client from
+ * the table once its index is known, including calling the remove
+ * callback (which usually will free resources required by the
+ * handler). The event server lock *must* be locked before calling
+ * this function.
+ *
+ * assumes success, returns nothing.
+ */
+static void
+virNetlinkEventRemoveClientPrimitive(size_t i)
+{
+    virNetlinkEventRemoveCallback removeCB = server->handles[i].removeCB;
+
+    if (removeCB) {
+        (removeCB)(server->handles[i].watch,
+                   server->handles[i].macaddr,
+                   server->handles[i].opaque);
+    }
+    server->handles[i].deleted = VIR_NETLINK_HANDLE_DELETED;
+}
+
 static void
 virNetlinkEventCallback(int watch,
                         int fd ATTRIBUTE_UNUSED,
                         int events ATTRIBUTE_UNUSED,
-                        void *opaque) {
+                        void *opaque)
+{
     virNetlinkEventSrvPrivatePtr srv = opaque;
     unsigned char *msg;
     struct sockaddr_nl peer;
@@ -219,23 +249,18 @@ virNetlinkEventCallback(int watch,
             (int)srv->handlesCount, watch);
 
     for (i = 0; i < srv->handlesCount; i++) {
-        if (srv->handles[i].deleted != VIR_NETLINK_HANDLE_VALID) {
+        if (srv->handles[i].deleted != VIR_NETLINK_HANDLE_VALID)
             continue;
-        }
 
-        VIR_DEBUG("dispatching client %d.",i);
+        VIR_DEBUG("dispatching client %d.", i);
 
-        virNetlinkEventHandleCallback cb = srv->handles[i].cb;
-        void *cpopaque = srv->handles[i].opaque;
-        (cb)( msg, length, &peer, &handled, cpopaque);
+        (srv->handles[i].handleCB)(msg, length, &peer, &handled,
+                                   srv->handles[i].opaque);
     }
 
-    if (!handled) {
+    if (!handled)
         VIR_DEBUG("event not handled.");
-    }
-
     VIR_FREE(msg);
-
     virNetlinkEventServerUnlock(srv);
 }
 
@@ -248,29 +273,32 @@ virNetlinkEventCallback(int watch,
  * returns -1 if the monitor cannot be unregistered, 0 upon success
  */
 int
-virNetlinkEventServiceStop(void) {
+virNetlinkEventServiceStop(void)
+{
     virNetlinkEventSrvPrivatePtr srv = server;
+    int i;
 
     VIR_INFO("stopping netlink event service");
 
-    if (!server) {
+    if (!server)
         return 0;
-    }
 
     virNetlinkEventServerLock(srv);
-
     nl_close(srv->netlinknh);
     nl_handle_destroy(srv->netlinknh);
-
     virEventRemoveHandle(srv->eventwatch);
-    server=0;
 
+    /* free any remaining clients on the list */
+    for (i = 0; i < srv->handlesCount; i++) {
+        if (srv->handles[i].deleted == VIR_NETLINK_HANDLE_VALID)
+            virNetlinkEventRemoveClientPrimitive(i);
+    }
+
+    server = 0;
     virNetlinkEventServerUnlock(srv);
 
     virMutexDestroy(&srv->lock);
-
     VIR_FREE(srv);
-
     return 0;
 }
 
@@ -282,7 +310,8 @@ virNetlinkEventServiceStop(void) {
  * returns 'true' if the service is running, 'false' if stopped.
  */
 bool
-virNetlinkEventServiceIsRunning(void) {
+virNetlinkEventServiceIsRunning(void)
+{
     return (server != NULL);
 }
 
@@ -295,14 +324,14 @@ virNetlinkEventServiceIsRunning(void) {
  * returns -1 if the monitor cannot be registered, 0 upon success
  */
 int
-virNetlinkEventServiceStart(void) {
+virNetlinkEventServiceStart(void)
+{
     virNetlinkEventSrvPrivatePtr srv;
     int fd;
     int ret = -1;
 
-    if (server) {
+    if (server)
         return 0;
-    }
 
     VIR_INFO("starting netlink event service");
 
@@ -311,7 +340,7 @@ virNetlinkEventServiceStart(void) {
         goto error;
     }
 
-    if ( virMutexInit(&srv->lock) < 0)
+    if (virMutexInit(&srv->lock) < 0)
         goto error;
 
     virNetlinkEventServerLock(srv);
@@ -340,35 +369,34 @@ virNetlinkEventServiceStart(void) {
     }
 
     if (nl_socket_set_nonblocking(srv->netlinknh)) {
-        netlinkError(errno,
-                     "%s", _("cannot set netlink socket nonblocking"));
+        netlinkError(errno, "%s",
+                     _("cannot set netlink socket nonblocking"));
         goto error_server;
     }
 
     if ((srv->eventwatch = virEventAddHandle(fd,
-                            VIR_EVENT_HANDLE_READABLE,
-                            virNetlinkEventCallback,
-                            srv, NULL)) < 0) {
+                                             VIR_EVENT_HANDLE_READABLE,
+                                             virNetlinkEventCallback,
+                                             srv, NULL)) < 0) {
         netlinkError(VIR_ERR_INTERNAL_ERROR, "%s",
                      _("Failed to add netlink event handle watch"));
-
         goto error_server;
     }
 
     srv->netlinkfd = fd;
-    VIR_DEBUG("netlink event listener on fd: %i running",fd);
+    VIR_DEBUG("netlink event listener on fd: %i running", fd);
 
     ret = 0;
-    server=srv;
+    server = srv;
 
 error_server:
-    if (ret == -1){
+    if (ret < 0) {
         nl_close(srv->netlinknh);
         nl_handle_destroy(srv->netlinknh);
     }
 error_locked:
     virNetlinkEventServerUnlock(srv);
-    if (ret == -1) {
+    if (ret < 0) {
         virMutexDestroy(&srv->lock);
         VIR_FREE(srv);
     }
@@ -379,7 +407,8 @@ error:
 /**
  * virNetlinkEventAddClient:
  *
- * @cb: callback to invoke when an event occurs
+ * @handleCB: callback to invoke when an event occurs
+ * @removeCB: callback to invoke when removing a client
  * @opaque: user data to pass to callback
  * @macaddr: macaddr to store with the data. Used to identify callers. May be null.
  *
@@ -390,18 +419,19 @@ error:
  * returns -1 if the file handle cannot be registered, number of monitor upon success
  */
 int
-virNetlinkEventAddClient(virNetlinkEventHandleCallback cb,
-                         void *opaque,
-                         const unsigned char *macaddr) {
-    int i,r, result;
+virNetlinkEventAddClient(virNetlinkEventHandleCallback handleCB,
+                         virNetlinkEventRemoveCallback removeCB,
+                         void *opaque, const unsigned char *macaddr)
+{
+    int i, r, ret = -1;
     virNetlinkEventSrvPrivatePtr srv = server;
 
-    if ( cb == NULL )
+    if (handleCB == NULL)
         return -1;
 
     virNetlinkEventServerLock(srv);
 
-    VIR_DEBUG("adding client: %d.",nextWatch);
+    VIR_DEBUG("adding client: %d.", nextWatch);
 
     r = 0;
     /* first try to re-use deleted free slots */
@@ -414,31 +444,31 @@ virNetlinkEventAddClient(virNetlinkEventHandleCallback cb,
     /* Resize the eventLoop array if needed */
     if (srv->handlesCount == srv->handlesAlloc) {
         VIR_DEBUG("Used %zu handle slots, adding at least %d more",
-                srv->handlesAlloc, NETLINK_EVENT_ALLOC_EXTENT);
+                  srv->handlesAlloc, NETLINK_EVENT_ALLOC_EXTENT);
         if (VIR_RESIZE_N(srv->handles, srv->handlesAlloc,
                         srv->handlesCount, NETLINK_EVENT_ALLOC_EXTENT) < 0) {
-            result = -1;
             goto error;
         }
     }
     r = srv->handlesCount++;
 
 addentry:
-    srv->handles[r].watch = nextWatch;
-    srv->handles[r].cb = cb;
-    srv->handles[r].opaque = opaque;
-    srv->handles[r].deleted = VIR_NETLINK_HANDLE_VALID;
+    srv->handles[r].watch    = nextWatch;
+    srv->handles[r].handleCB = handleCB;
+    srv->handles[r].removeCB = removeCB;
+    srv->handles[r].opaque   = opaque;
+    srv->handles[r].deleted  = VIR_NETLINK_HANDLE_VALID;
     if (macaddr)
         memcpy(srv->handles[r].macaddr, macaddr, VIR_MAC_BUFLEN);
+    else
+        memset(srv->handles[r].macaddr, 0, VIR_MAC_BUFLEN);
 
     VIR_DEBUG("added client to loop slot: %d. with macaddr ptr=%p", r, macaddr);
 
-    result = nextWatch++;
-
+    ret = nextWatch++;
 error:
     virNetlinkEventServerUnlock(srv);
-
-    return result;
+    return ret;
 }
 
 /**
@@ -446,7 +476,6 @@ error:
  *
  * @watch: watch whose handle to remove
  * @macaddr: macaddr whose handle to remove
- * &cb: callback for the destruction of local data
  *
  * Unregister a callback from a netlink monitor.
  * The handler function referenced will no longer receive netlink messages.
@@ -455,14 +484,13 @@ error:
  * returns -1 if the file handle was not registered, 0 upon success
  */
 int
-virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr,
-                            virNetlinkEventRemoveCallback cb) {
+virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr)
+{
     int i;
     int ret = -1;
     virNetlinkEventSrvPrivatePtr srv = server;
 
-    VIR_DEBUG("removing client watch=%d, mac=%p.",
-                        watch, macaddr);
+    VIR_DEBUG("removing client watch=%d, mac=%p.", watch, macaddr);
 
     if (watch <= 0 && !macaddr) {
         VIR_WARN("Ignoring invalid netlink client id: %d", watch);
@@ -473,34 +501,22 @@ virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr,
 
     for (i = 0; i < srv->handlesCount; i++) {
         if (srv->handles[i].deleted != VIR_NETLINK_HANDLE_VALID)
-        continue;
+            continue;
 
-        if (watch && srv->handles[i].watch == watch) {
-            void *cpopaque = srv->handles[i].opaque;
-            (cb)( watch, macaddr, cpopaque);
+        if ((watch && srv->handles[i].watch == watch) ||
+            (!watch &&
+             memcmp(macaddr, srv->handles[i].macaddr, VIR_MAC_BUFLEN) == 0)) {
 
-            VIR_FREE(srv->handles[i].opaque);
-            srv->handles[i].deleted = VIR_NETLINK_HANDLE_DELETED;
-            VIR_DEBUG("removed client: %d by index.",
-                    srv->handles[i].watch);
-            ret = 0;
-            goto error;
-        }
-        if (!watch && memcmp(macaddr, srv->handles[i].macaddr, VIR_MAC_BUFLEN) == 0) {
-            void *cpopaque = srv->handles[i].opaque;
-            (cb)( watch, macaddr, cpopaque);
-
-            VIR_FREE(srv->handles[i].opaque);
-            srv->handles[i].deleted = VIR_NETLINK_HANDLE_DELETED;
-            VIR_DEBUG("removed client: %d by mac.",
-                    srv->handles[i].watch);
+            virNetlinkEventRemoveClientPrimitive(i);
+            VIR_DEBUG("removed client: %d by %s.", srv->handles[i].watch,
+                      srv->handles[i].watch ? "index" : "mac");
             ret = 0;
-            goto error;
+            goto cleanup;
         }
     }
-    VIR_DEBUG("client not found to remove.");
+    VIR_DEBUG("no client found to remove.");
 
-error:
+cleanup:
     virNetlinkEventServerUnlock(srv);
     return ret;
 }
@@ -524,7 +540,8 @@ int virNetlinkCommand(struct nl_msg *nl_msg ATTRIBUTE_UNUSED,
 /**
  * stopNetlinkEventServer: stop the monitor to receive netlink messages for libvirtd
  */
-int virNetlinkEventServiceStop(void) {
+int virNetlinkEventServiceStop(void)
+{
     netlinkError(VIR_ERR_INTERNAL_ERROR,
                 "%s",
 # if defined(__linux__) && !defined(HAVE_LIBNL)
@@ -536,7 +553,8 @@ int virNetlinkEventServiceStop(void) {
 /**
  * startNetlinkEventServer: start a monitor to receive netlink messages for libvirtd
  */
-int virNetlinkEventServiceStart(void) {
+int virNetlinkEventServiceStart(void)
+{
 # if defined(__linux__) && !defined(HAVE_LIBNL)
     netlinkError(VIR_ERR_INTERNAL_ERROR,
                 "%s",
@@ -548,7 +566,8 @@ int virNetlinkEventServiceStart(void) {
 /**
  * virNetlinkEventServiceIsRunning: returns if the netlink event service is running.
  */
-int virNetlinkEventServiceIsRunning(void) {
+int virNetlinkEventServiceIsRunning(void)
+{
 # if defined(__linux__) && !defined(HAVE_LIBNL)
     netlinkError(VIR_ERR_INTERNAL_ERROR,
                 "%s",
@@ -560,8 +579,10 @@ int virNetlinkEventServiceIsRunning(void) {
 /**
  * virNetlinkEventAddClient: register a callback for handling of netlink messages
  */
-int virNetlinkEventAddClient(virNetlinkEventHandleCallback cb, void *opaque,
-                             const unsigned char *macaddr) {
+int virNetlinkEventAddClient(virNetlinkEventHandleCallback cb,
+                             virNetlinkEventRemoveCallback cb,
+                             void *opaque, const unsigned char *macaddr)
+{
     netlinkError(VIR_ERR_INTERNAL_ERROR,
                 "%s",
 # if defined(__linux__) && !defined(HAVE_LIBNL)
@@ -575,8 +596,8 @@ int virNetlinkEventAddClient(virNetlinkEventHandleCallback cb, void *opaque,
 /**
  * virNetlinkEventRemoveClient: unregister a callback from a netlink monitor
  */
-int virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr,
-                                virNetlinkEventRemoveCallback cb) {
+int virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr)
+{
     netlinkError(VIR_ERR_INTERNAL_ERROR,
                 "%s",
 # if defined(__linux__) && !defined(HAVE_LIBNL)
diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h
index b787a8f..75533a3 100644
--- a/src/util/virnetlink.h
+++ b/src/util/virnetlink.h
@@ -60,12 +60,13 @@ bool virNetlinkEventServiceIsRunning(void);
 /**
  * virNetlinkEventAddClient: register a callback for handling of netlink messages
  */
-int virNetlinkEventAddClient(virNetlinkEventHandleCallback cb, void *opaque, const unsigned char *macaddr);
+int virNetlinkEventAddClient(virNetlinkEventHandleCallback handleCB,
+                             virNetlinkEventRemoveCallback removeCB,
+                             void *opaque, const unsigned char *macaddr);
 
 /**
  * virNetlinkEventRemoveClient: unregister a callback from a netlink monitor
  */
-int virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr,
-                                virNetlinkEventRemoveCallback cb);
+int virNetlinkEventRemoveClient(int watch, const unsigned char *macaddr);
 
 #endif /* __VIR_NETLINK_H__ */
-- 
1.7.7.6




More information about the libvir-list mailing list