/*! ** @brief Close an Xsocket. ** ** Causes the XIA transport to tear down the underlying XIA socket state and ** also closes the UDP control socket used to talk to the transport. ** ** @param sockfd The control socket ** ** @returns 0 on success ** @returns -1 on error with errno set to a value compatible with the standard ** close API call. */ int Xclose(int sockfd) { xia::XSocketCallType type; int rc; if (getSocketType(sockfd) == XSOCK_INVALID) { LOG("The socket is not a valid Xsocket"); errno = EBADF; return -1; } xia::XSocketMsg xsm; xsm.set_type(xia::XCLOSE); // set back to blocking just in case Xfcntl(sockfd, F_SETFL, 0); if ((rc = click_send(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); } else if ((rc = click_reply2(sockfd, &type)) < 0) { LOGF("Error getting status from Click: %s", strerror(errno)); } close(sockfd); freeSocketState(sockfd); return rc; }
/*! ** @brief Block waiting for notification that the network interface has changed state. ** ** WARNING: THIS IS A STOPGAP API THAT WILL BE REPLACED ONCE WE DETERMIN WHAT ** THE LONGERM NOTIFICATION MECHANISM LOOKS LIKE. BE PREPARED FOR THIS CALL ** TO BE DEPRECATED OR MODIFIED ** ** Blocks and waits for for click to return a status when the XHCP client daemon ** changes the AD or other network parameters. Multiple applications or threads ** may call this API, and all will be nofitied when a change occurs. ** ** Do not call Xfork while blocking on this function. Only one of the processes will ** properly recieve the notification. ** ** @returns 0 on success ** @returns -1 on error with errno set */ int Xnotify(void) { int rc = -1; int sock = 0; xia::XSocketMsg xsm; //xia::X_Notify_Msg *xnm; xsm.set_type(xia::XNOTIFY); xsm.set_sequence(0); sock = MakeApiSocket(SOCK_DGRAM); if ((rc = click_send(sock, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); goto done; } else if ((rc = click_reply(sock, 0, &xsm)) < 0) { LOGF("Error getting status from Click: %s", strerror(errno)); } done: if (sock > 0) { freeSocketState(sock); (_f_close)(sock); } return rc; }
/*! ** @brief waits for one of a set of Xsockets to become ready to perform I/O. ** ** Xsocket specific version of poll. See the poll man page for more detailed information. ** This function is compatible with Xsockets as well as regular sockets and fds. Xsockets ** are polled via click, and regular sockets and fds are handled through the normal poll ** API. ** ** #include <sys/poll.h> ** ** @param ufds array of pollfds indicating sockets and states to check for ** @param nfds number of entries in ufds ** \n socket ids specified as 0 or negative will be ignored ** \n valid flags for events are POLLIN | POLLOUT | POLLERR ** \n revents contains the returned flags and can be POLLIN | POLLOUT | POLLERR | POLLINVAL | POLLHUP ** @param timeout number of milliseconds to wait for an event to happen ** ** @returns 0 if timeout occured ** @returns a positive integer indicating the number of sockets with return events ** @retuns -1 with errno set if an error occured */ int Xpoll(struct pollfd *ufds, unsigned nfds, int timeout) { int rc; int sock = 0; int nxfds = 0; int xrc = 0; if (nfds == 0) { // it's just a timer return (_f_poll)(ufds, nfds, timeout); } else if (ufds == NULL) { errno = EFAULT; return -1; } struct pollfd *rfds = (struct pollfd*)calloc(nfds + 1, sizeof(struct pollfd)); Sock2Port *s2p = (Sock2Port*)calloc(nfds, sizeof(Sock2Port)); memcpy(rfds, ufds, nfds * sizeof(struct pollfd)); xia::XSocketMsg xsm; xia::X_Poll_Msg *pollMsg = xsm.mutable_x_poll(); for (unsigned i = 0; i < nfds; i++) { ufds[i].revents = 0; if (ufds[i].fd > 0 && (ufds[i].events != 0)) { if (getSocketType(ufds[i].fd) != XSOCK_INVALID) { // add the Xsocket to the xpoll struct // TODO: should this work for Content sockets? xia::X_Poll_Msg::PollFD *pfd = pollMsg->add_pfds(); // find the port number associated with this Xsocket struct sockaddr_in sin; socklen_t slen = sizeof(sin); (_f_getsockname)(ufds[i].fd, (struct sockaddr*)&sin, &slen); // LOGF("XSocket! sock %d, port %d, flags %x\n", ufds[i].fd, ntohs(sin.sin_port), ufds[i].events); pfd->set_port(sin.sin_port); s2p[i].fd = ufds[i].fd; s2p[i].port = sin.sin_port; // FIXME: hack for curl - think about better ways to deal with this if (ufds[i].events & POLLRDNORM || ufds[i].events & POLLRDBAND) { ufds[i].events |= POLLIN; } if (ufds[i].events & POLLWRNORM || ufds[i].events & POLLWRBAND) { ufds[i].events |= POLLOUT; } pfd->set_flags(ufds[i].events); nxfds++; // disable the socket in the real poll list rfds[i].fd = -rfds[i].fd; } else { s2p[i].fd = s2p[i].port = 0; } } } if (nxfds == 0) { // there are no Xsocket to poll for, just do a straight poll with the original data rc = (_f_poll)(ufds, nfds, timeout); goto done; } xsm.set_type(xia::XPOLL); xsm.set_sequence(0); pollMsg->set_nfds(nxfds); pollMsg->set_type(xia::X_Poll_Msg::DOPOLL); // Real sockets in the Poll message are set to 0. They are left in the list to make processing easier // the rfds (Real fd) list has the fds flipped negative for the xsockets so they will be ignored // for the same reason sock = MakeApiSocket(SOCK_DGRAM); click_send(sock, &xsm); // now we need to do a real poll // it will trigger once click generates an xpoll event or pone of the external fds has an event // add the poll control socket rfds[nfds].fd = sock; rfds[nfds].events = POLLIN; rfds[nfds].revents = 0; rc = (_f_poll)(rfds, nfds + 1, timeout); if (rc > 0) { // go through and update the fds in the output for (unsigned i = 0; i < nfds; i++) ufds[i].revents = rfds[i].revents; // now do click events if any if (rfds[nfds].revents != 0) { if (click_reply(sock, 0, &xsm) < 0) { LOG("Error getting data from Click\n"); rc = -1; goto done; } xia::X_Poll_Msg *pout = xsm.mutable_x_poll(); xrc = pout->nfds(); // loop thru returned xsockets for (int i = 0; i < xrc; i++) { const xia::X_Poll_Msg::PollFD& pfd_out = pout->pfds(i); unsigned port = pfd_out.port(); unsigned flags = pfd_out.flags(); //LOGF("poll returned x%0x for %d\n", flags, port); // find the associated socket int fd = 0; for (unsigned j = 0; j < nfds; j++) { if (port == s2p[j].port) { fd = s2p[j].fd; break; } } // find the socket in the original poll & update the revents field for (unsigned j = 0; j < nfds; j++) { if (ufds[j].fd == fd) { // if a non-blocking connect is in progress, set connected state appropriately if (flags && POLLOUT) { setNBConnState(fd); } // FIXME: hack for curl - think about better ways to deal with this if (flags && POLLIN && (ufds[i].events & POLLRDNORM || ufds[i].events & POLLRDBAND)) { flags |= (POLLRDNORM | POLLRDBAND); } if (flags && POLLOUT && (ufds[i].events & POLLWRNORM || ufds[i].events & POLLWRBAND)) { flags |= (POLLWRNORM | POLLWRBAND); } ufds[j].revents = flags; break; } } } } else { // we need to tell click to cancel the Xpoll event xsm.Clear(); xsm.set_type(xia::XPOLL); xsm.set_sequence(0); pollMsg = xsm.mutable_x_poll(); pollMsg->set_type(xia::X_Poll_Msg::CANCEL); pollMsg->set_nfds(0); click_send(sock, &xsm); } // rc is the number of fds returned by poll + plus number of sockets found by click // minus the event for the control socket if (xrc > 0) rc += xrc - 1; } done: int eno = errno; if (sock > 0) { freeSocketState(sock); (_f_close)(sock); } free(rfds); free(s2p); errno = eno; return rc; }
/*! ** @brief waits for one of a set of Xsockets to become ready to perform I/O. ** ** Xsocket specific version of select. See the select man page for more detailed information. ** This function is compatible with Xsockets as well as regular sockets and fds. Xsockets ** are handled with the Xpoll APIs via click, and regular sockets and fds are handled ** through the normal select API. ** ** @param ndfs The highest socket number contained in the fd_sets plus 1 ** @param readfds fd_set containing sockets to check for readability ** @param writefds fd_set containing sockets to check for writability ** @param errorfds fd_set containing sockets to check for errors ** @param timeout amount of time to wait for a socket to change state ** @returns greater than 0, number of sockets ready ** @returns 0 if the timeout expired ** @returns less than 0 if an error occurs ** @warning this function is only valid for stream and datagram sockets. */ int Xselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) { fd_set rfds; fd_set wfds; fd_set efds; fd_set immediate_fds; unsigned nx = 0; int xrc = 0; int sock = 0; int largest = 0; int count = 0; int rc = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); FD_ZERO(&immediate_fds); // if the fd sets are sparse, this will waste space especially if nfds is large Sock2Port *s2p = (Sock2Port*)calloc(nfds, sizeof(Sock2Port)); // create protobuf message xia::XSocketMsg xsm; xsm.set_type(xia::XPOLL); xsm.set_sequence(0); xia::X_Poll_Msg *pollMsg = xsm.mutable_x_poll(); pollMsg->set_type(xia::X_Poll_Msg::DOPOLL); for (int i = 0; i < nfds; i++) { int flags = 0; int r = 0; int w = 0; int e = 0; if (readfds && FD_ISSET(i, readfds)) { flags |= POLLIN; r = i; } if (writefds && FD_ISSET(i, writefds)) { flags |= POLLOUT; w = i; } if (errorfds && FD_ISSET(i, errorfds)) { flags |= POLLERR; e = i; } // is it an xsocket if (flags && getSocketType(i) != XSOCK_INVALID) { // we found an Xsocket, do the Xpoll magic nx++; xia::X_Poll_Msg::PollFD *pfd = pollMsg->add_pfds(); // find the port number associated with this Xsocket struct sockaddr_in sin; socklen_t slen = sizeof(sin); (_f_getsockname)(i, (struct sockaddr*)&sin, &slen); //printf("sock %d, port %d, flags %x\n", ufds[i].fd, ntohs(sin.sin_port), ufds[i].events); pfd->set_port(sin.sin_port); s2p[i].fd = i; s2p[i].port = sin.sin_port; pfd->set_flags(flags); } else { if (i > largest) largest = i; // it's a regular fd, put it into the select fdsets if (r != 0) FD_SET(i, &rfds); if (w != 0) FD_SET(i, &wfds); if (e != 0) FD_SET(i, &efds); s2p[i].fd = s2p[i].port = 0; } } if (nx == 0) { // there were no xsockets in the FD_SETS, just do a normal select rc = (_f_select)(nfds, readfds, writefds, errorfds, timeout); goto done; } sock = MakeApiSocket(SOCK_DGRAM); pollMsg->set_type(xia::X_Poll_Msg::DOPOLL); pollMsg->set_nfds(nx); click_send(sock, &xsm); // add the control socket to the select read fdset if (sock > largest) largest = sock; FD_SET(sock, &rfds); rc = (_f_select)(largest + 1, &rfds, (writefds != NULL ? &wfds : NULL), (errorfds != NULL ? &efds : NULL), timeout); // reset the bit arrays for the return to caller if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); if (errorfds) FD_ZERO(errorfds); // fill the fdsets in with the triggered sockets/fds count = 0; if (rc > 0) { // get the regular fds for (int i = 0; i < largest; i++) { if (i != sock) { if (readfds && FD_ISSET(i, &rfds)) { FD_SET(i, readfds); count++; } if (writefds && FD_ISSET(i, &wfds)) { FD_SET(i, writefds); count++; } if (errorfds && FD_ISSET(i, &efds)) { FD_SET(i, errorfds); count++; } } } if (FD_ISSET(sock, &rfds)) { // we have Xsockets data xsm.Clear(); if (click_reply(sock, 0, &xsm) < 0) { LOG("Error getting data from Click\n"); rc = -1; goto done; } xia::X_Poll_Msg *pout = xsm.mutable_x_poll(); xrc = pout->nfds(); for (int i = 0; i < xrc; i++) { const xia::X_Poll_Msg::PollFD& pfd_out = pout->pfds(i); int flags = pfd_out.flags(); unsigned port = pfd_out.port(); int fd = 0; for (int j = 0; j < nfds; j++) { if (port == s2p[j].port) { fd = s2p[j].fd; break; } } // printf("socket %d out flags:%08x\n", pfds[i].fd, pfds[i].revents); if (readfds && (flags & POLLIN)) { FD_SET(fd, readfds); count++; } if (writefds && (flags & POLLOUT)) { FD_SET(fd, writefds); count++; // if a non-blocking connect is in progress, set connected state appropriately setNBConnState(fd); } if (errorfds && (flags & POLLERR)) { FD_SET(fd, errorfds); count++; } } } else { // we need to tell click to cancel the Xpoll event XselectCancel(sock); } } else { XselectCancel(sock); } done: int eno = errno; if (sock > 0) { freeSocketState(sock); (_f_close)(sock); } free(s2p); errno = eno; return (rc <= 0 ? rc : count); }