/** * virNetlinkStartup: * * Perform any initialization that needs to take place before the * program starts up worker threads. This is currently used to assure * that an nl_handle is allocated prior to any attempts to bind a * netlink socket. For a discussion of why this is necessary, please * see the following email message: * * https://www.redhat.com/archives/libvir-list/2012-May/msg00202.html * * The short version is that, without this placeholder allocation of * an nl_handle that is never used, it is possible for nl_connect() in * one thread to collide with a direct bind() of a netlink socket in * another thread, leading to failure of the operation (which could * lead to failure of libvirtd to start). Since getaddrinfo() (used by * libvirtd in virSocketAddrParse, which is called quite frequently * during startup) directly calls bind() on a netlink socket, this is * actually a very common occurrence (15-20% failure rate on some * hardware). * * Returns 0 on success, -1 on failure. */ int virNetlinkStartup(void) { if (placeholder_nlhandle) return 0; placeholder_nlhandle = virNetlinkAlloc(); if (!placeholder_nlhandle) { virReportSystemError(errno, "%s", _("cannot allocate placeholder nlhandle for netlink")); return -1; } return 0; }
/** * virNetlinkStartup: * * Perform any initialization that needs to take place before the * program starts up worker threads. This is currently used to assure * that an nl_handle is allocated prior to any attempts to bind a * netlink socket. For a discussion of why this is necessary, please * see the following email message: * * https://www.redhat.com/archives/libvir-list/2012-May/msg00202.html * * The short version is that, without this placeholder allocation of * an nl_handle that is never used, it is possible for nl_connect() in * one thread to collide with a direct bind() of a netlink socket in * another thread, leading to failure of the operation (which could * lead to failure of libvirtd to start). Since getaddrinfo() (used by * libvirtd in virSocketAddrParse, which is called quite frequently * during startup) directly calls bind() on a netlink socket, this is * actually a very common occurrence (15-20% failure rate on some * hardware). * * Returns 0 on success, -1 on failure. */ int virNetlinkStartup(void) { if (placeholder_nlhandle) return 0; VIR_DEBUG("Running global netlink initialization"); placeholder_nlhandle = virNetlinkAlloc(); if (!placeholder_nlhandle) { virReportSystemError(errno, "%s", _("cannot allocate placeholder nlhandle for netlink")); return -1; } return 0; }
/** * virNetLinkCreateSocket: * * @protocol: which protocol to connect to (e.g. NETLINK_ROUTE, * * Create a netlink socket, set its buffer size, and turn on message * peeking (so the buffer size can be dynamically increased if * needed). * * Returns a handle to the new netlink socket, or 0 if there was a failure. * */ static virNetlinkHandle * virNetlinkCreateSocket(int protocol) { virNetlinkHandle *nlhandle = NULL; if (!(nlhandle = virNetlinkAlloc())) { virReportSystemError(errno, "%s", _("cannot allocate nlhandle for netlink")); goto error; } if (nl_connect(nlhandle, protocol) < 0) { virReportSystemError(errno, _("cannot connect to netlink socket " "with protocol %d"), protocol); goto error; } if (virNetlinkSetBufferSize(nlhandle, 131702, 0) < 0) { virReportSystemError(errno, "%s", _("cannot set netlink socket buffer " "size to 128k")); goto error; } nl_socket_enable_msg_peek(nlhandle); cleanup: return nlhandle; error: if (nlhandle) { nl_close(nlhandle); virNetlinkFree(nlhandle); nlhandle = NULL; } goto cleanup; }
/** * 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; }
/** * virNetlinkCommand: * @nlmsg: pointer to netlink message * @respbuf: pointer to pointer where response buffer will be allocated * @respbuflen: pointer to integer holding the size of the response buffer * on return of the function. * @nl_pid: the pid of the process to talk to, i.e., pid = 0 for kernel * * Send the given message to the netlink layer and receive response. * Returns 0 on success, -1 on error. In case of error, no response * buffer will be returned. */ int virNetlinkCommand(struct nl_msg *nl_msg, unsigned char **respbuf, unsigned int *respbuflen, uint32_t src_pid, uint32_t dst_pid) { int rc = 0; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = dst_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); virNetlinkHandle *nlhandle = virNetlinkAlloc(); 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 error; } nlmsg_set_dst(nl_msg, &nladdr); nlmsg->nlmsg_pid = src_pid ? src_pid : getpid(); nbytes = nl_send_auto_complete(nlhandle, nl_msg); if (nbytes < 0) { virReportSystemError(errno, "%s", _("cannot send to netlink socket")); rc = -1; goto error; } 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 error; } *respbuflen = nl_recv(nlhandle, &nladdr, respbuf, NULL); if (*respbuflen <= 0) { virReportSystemError(errno, "%s", _("nl_recv failed")); rc = -1; } error: if (rc == -1) { VIR_FREE(*respbuf); *respbuf = NULL; *respbuflen = 0; } virNetlinkFree(nlhandle); return rc; } static void virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver) { virMutexLock(&driver->lock); }
/** * virNetlinkEventServiceStart: * * start a monitor to receive netlink messages for libvirtd. * This registers a netlink socket with the event interface. * * @protocol: netlink protocol * @groups: broadcast groups to join in * Returns -1 if the monitor cannot be registered, 0 upon success */ int virNetlinkEventServiceStart(unsigned int protocol, unsigned int groups) { virNetlinkEventSrvPrivatePtr srv; int fd; int ret = -1; if (protocol >= MAX_LINKS) { virReportSystemError(EINVAL, _("invalid protocol argument: %d"), protocol); return -EINVAL; } if (server[protocol]) return 0; VIR_INFO("starting netlink event service with protocol %d", protocol); if (VIR_ALLOC(srv) < 0) 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, protocol) < 0) { virReportSystemError(errno, _("cannot connect to netlink socket with protocol %d"), protocol); 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 (groups && nl_socket_add_membership(srv->netlinknh, groups) < 0) { virReportSystemError(errno, "%s", _("cannot add netlink membership")); 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) { virReportError(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[protocol] = 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; }
/** * virNetlinkCommand: * @nlmsg: pointer to netlink message * @respbuf: pointer to pointer where response buffer will be allocated * @respbuflen: pointer to integer holding the size of the response buffer * on return of the function. * @src_pid: the pid of the process to send a message * @dst_pid: the pid of the process to talk to, i.e., pid = 0 for kernel * @protocol: netlink protocol * @groups: the group identifier * * Send the given message to the netlink layer and receive response. * Returns 0 on success, -1 on error. In case of error, no response * buffer will be returned. */ int virNetlinkCommand(struct nl_msg *nl_msg, struct nlmsghdr **resp, unsigned int *respbuflen, uint32_t src_pid, uint32_t dst_pid, unsigned int protocol, unsigned int groups) { int rc = 0; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = dst_pid, .nl_groups = 0, }; ssize_t nbytes; struct pollfd fds[1]; int fd; int n; struct nlmsghdr *nlmsg = nlmsg_hdr(nl_msg); virNetlinkHandle *nlhandle = NULL; if (protocol >= MAX_LINKS) { virReportSystemError(EINVAL, _("invalid protocol argument: %d"), protocol); return -EINVAL; } nlhandle = virNetlinkAlloc(); if (!nlhandle) { virReportSystemError(errno, "%s", _("cannot allocate nlhandle for netlink")); return -1; } if (nl_connect(nlhandle, protocol) < 0) { virReportSystemError(errno, _("cannot connect to netlink socket with protocol %d"), protocol); rc = -1; goto error; } fd = nl_socket_get_fd(nlhandle); if (fd < 0) { virReportSystemError(errno, "%s", _("cannot get netlink socket fd")); rc = -1; goto error; } if (groups && nl_socket_add_membership(nlhandle, groups) < 0) { virReportSystemError(errno, "%s", _("cannot add netlink membership")); rc = -1; goto error; } nlmsg_set_dst(nl_msg, &nladdr); nlmsg->nlmsg_pid = src_pid ? src_pid : getpid(); nbytes = nl_send_auto_complete(nlhandle, nl_msg); if (nbytes < 0) { virReportSystemError(errno, "%s", _("cannot send to netlink socket")); rc = -1; goto error; } memset(fds, 0, sizeof(fds)); fds[0].fd = fd; fds[0].events = POLLIN; n = poll(fds, ARRAY_CARDINALITY(fds), NETLINK_ACK_TIMEOUT_S); if (n <= 0) { if (n < 0) virReportSystemError(errno, "%s", _("error in poll call")); if (n == 0) virReportSystemError(ETIMEDOUT, "%s", _("no valid netlink response was received")); rc = -1; goto error; } *respbuflen = nl_recv(nlhandle, &nladdr, (unsigned char **)resp, NULL); if (*respbuflen <= 0) { virReportSystemError(errno, "%s", _("nl_recv failed")); rc = -1; } error: if (rc == -1) { VIR_FREE(*resp); *resp = NULL; *respbuflen = 0; } virNetlinkFree(nlhandle); return rc; } static void virNetlinkEventServerLock(virNetlinkEventSrvPrivatePtr driver) { virMutexLock(&driver->lock); }