/** * virNetlinkEventServiceLocalPid: * * Returns the nl_pid value that was used to bind() the netlink socket * used by the netlink event service, or -1 on error (netlink * guarantees that this value will always be > 0). */ int virNetlinkEventServiceLocalPid(void) { if (!(server && server->netlinknh)) { netlinkError(VIR_ERR_INTERNAL_ERROR, "%s", _("netlink event service not running")); return -1; } return (int)nl_socket_get_local_port(server->netlinknh); }
/** * virNetlinkEventAddClient: * * @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. * * register a callback for handling of netlink messages. The * registered function receives the entire netlink message and * may choose to act upon it. * * Returns -1 if the file handle cannot be registered, number of * monitor upon success. */ int virNetlinkEventAddClient(virNetlinkEventHandleCallback handleCB, virNetlinkEventRemoveCallback removeCB, void *opaque, const unsigned char *macaddr) { int i, r, ret = -1; virNetlinkEventSrvPrivatePtr srv = server; if (handleCB == NULL) { netlinkError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid NULL callback provided")); return -1; } virNetlinkEventServerLock(srv); VIR_DEBUG("adding client: %d.", nextWatch); r = 0; /* first try to re-use deleted free slots */ for (i = 0; i < srv->handlesCount; i++) { if (srv->handles[i].deleted == VIR_NETLINK_HANDLE_DELETED) { r = i; goto addentry; } } /* 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); if (VIR_RESIZE_N(srv->handles, srv->handlesAlloc, srv->handlesCount, NETLINK_EVENT_ALLOC_EXTENT) < 0) { virReportOOMError(); goto error; } } r = srv->handlesCount++; addentry: 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); ret = nextWatch++; error: virNetlinkEventServerUnlock(srv); return ret; }
/** * virNetlinkEventServiceStart: * * start a monitor to receive netlink messages for libvirtd. * This registers a netlink socket with the event interface. * * Returns -1 if the monitor cannot be registered, 0 upon success */ int virNetlinkEventServiceStart(void) { virNetlinkEventSrvPrivatePtr srv; int fd; int ret = -1; if (server) return 0; VIR_INFO("starting netlink event service"); if (VIR_ALLOC(srv) < 0) { virReportOOMError(); return -1; } if (virMutexInit(&srv->lock) < 0) { VIR_FREE(srv); return -1; } virNetlinkEventServerLock(srv); /* Allocate a new socket and get fd */ srv->netlinknh = virNetlinkAlloc(); if (!srv->netlinknh) { virReportSystemError(errno, "%s", _("cannot allocate nlhandle for virNetlinkEvent server")); goto error_locked; } if (nl_connect(srv->netlinknh, NETLINK_ROUTE) < 0) { virReportSystemError(errno, "%s", _("cannot connect to netlink socket")); goto error_server; } fd = nl_socket_get_fd(srv->netlinknh); if (fd < 0) { virReportSystemError(errno, "%s", _("cannot get netlink socket fd")); goto error_server; } if (nl_socket_set_nonblocking(srv->netlinknh)) { virReportSystemError(errno, "%s", _("cannot set netlink socket nonblocking")); goto error_server; } if ((srv->eventwatch = virEventAddHandle(fd, 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); ret = 0; server = srv; error_server: if (ret < 0) { nl_close(srv->netlinknh); virNetlinkFree(srv->netlinknh); } error_locked: virNetlinkEventServerUnlock(srv); if (ret < 0) { virMutexDestroy(&srv->lock); VIR_FREE(srv); } return ret; }
int nlComm(struct nl_msg *nl_msg, unsigned char **respbuf, unsigned int *respbuflen, int nl_pid) { int rc = 0; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = nl_pid, .nl_groups = 0, }; ssize_t nbytes; struct timeval tv = { .tv_sec = NETLINK_ACK_TIMEOUT_S, }; fd_set readfds; int fd; int n; struct nlmsghdr *nlmsg = nlmsg_hdr(nl_msg); struct nl_handle *nlhandle = nl_handle_alloc(); if (!nlhandle) { virReportSystemError(errno, "%s", _("cannot allocate nlhandle for netlink")); return -1; } if (nl_connect(nlhandle, NETLINK_ROUTE) < 0) { virReportSystemError(errno, "%s", _("cannot connect to netlink socket")); rc = -1; goto err_exit; } nlmsg_set_dst(nl_msg, &nladdr); nlmsg->nlmsg_pid = getpid(); nbytes = nl_send_auto_complete(nlhandle, nl_msg); if (nbytes < 0) { virReportSystemError(errno, "%s", _("cannot send to netlink socket")); rc = -1; goto err_exit; } fd = nl_socket_get_fd(nlhandle); FD_ZERO(&readfds); FD_SET(fd, &readfds); n = select(fd + 1, &readfds, NULL, NULL, &tv); if (n <= 0) { if (n < 0) virReportSystemError(errno, "%s", _("error in select call")); if (n == 0) virReportSystemError(ETIMEDOUT, "%s", _("no valid netlink response was received")); rc = -1; goto err_exit; } *respbuflen = nl_recv(nlhandle, &nladdr, respbuf, NULL); if (*respbuflen <= 0) { virReportSystemError(errno, "%s", _("nl_recv failed")); rc = -1; } err_exit: if (rc == -1) { VIR_FREE(*respbuf); *respbuf = NULL; *respbuflen = 0; } nl_handle_destroy(nlhandle); return rc; } #else int nlComm(struct nl_msg *nl_msg ATTRIBUTE_UNUSED, unsigned char **respbuf ATTRIBUTE_UNUSED, unsigned int *respbuflen ATTRIBUTE_UNUSED, int nl_pid ATTRIBUTE_UNUSED) { netlinkError(VIR_ERR_INTERNAL_ERROR, "%s", # if defined(__linux__) && !defined(HAVE_LIBNL) _("nlComm is not supported since libnl was not available")); # else _("nlComm is not supported on non-linux platforms")); # endif return -1; }