/* ** get an integer sized parameter from click */ int ssoGetInt(int sockfd, int optname, int *optval, socklen_t *optlen) { if (ssoCheckSize(optlen, sizeof(int)) < 0) return -1; xia::XSocketMsg xsm; xsm.set_type(xia::XGETSOCKOPT); unsigned seq = seqNo(sockfd); xsm.set_sequence(seq); xia::X_Getsockopt_Msg *msg = xsm.mutable_x_getsockopt(); msg->set_opt_type(optname); if (click_send(sockfd, &xsm) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); return -1; } xsm.Clear(); if (click_reply(sockfd, seq, &xsm) < 0) { LOGF("Error getting status from Click: %s", strerror(errno)); return -1; } msg = xsm.mutable_x_getsockopt(); *optval = msg->int_opt(); *optlen = sizeof(int); // FIXME: get return code from protobuf return 0; }
/*! ** @tell if this node is an XIA-IPv4 dual-stack router ** ** ** @param sockfd an Xsocket (may be of any type XSOCK_STREAM, etc...) ** ** @returns 1 if this is an XIA-IPv4 dual-stack router ** @returns 0 if this is an XIA router ** @returns -1 on failure with errno set ** */ int XisDualStackRouter(int sockfd) { int rc; char UDPbuf[MAXBUFLEN]; if (getSocketType(sockfd) == XSOCK_INVALID) { LOG("The socket is not a valid Xsocket"); errno = EBADF; return -1; } xia::XSocketMsg xsm; xsm.set_type(xia::XISDUALSTACKROUTER); if ((rc = click_send(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); return -1; } if ((rc = click_reply(sockfd, UDPbuf, sizeof(UDPbuf))) < 0) { LOGF("Error retrieving status from Click: %s", strerror(errno)); return -1; } xia::XSocketMsg xsm1; xsm1.ParseFromString(UDPbuf); if (xsm1.type() == xia::XISDUALSTACKROUTER) { xia::X_IsDualStackRouter_Msg *_msg = xsm1.mutable_x_isdualstackrouter(); rc = _msg->flag(); } else { rc = -1; } return rc; }
/*! ** @brief Receive data from an Xsocket ** ** Xrecv() retrieves data from a connected Xsocket of type XSOCK_STREAM. ** sockfd must have previously been connected via Xaccept() or ** Xconnect(). ** ** Xrecv() does not currently have a non-blocking mode, and will block ** until a data is available on sockfd. However, the standard socket API ** calls select and poll may be used with the Xsocket. Either function ** will deliver a readable event when a new connection is attempted and ** you may then call Xrecv() to get the data. ** ** NOTE: in cases where more data is received than specified by the caller, ** the excess data will be stored at the API level. Subsequent Xrecv calls ** return the stored data until it is drained, and will then resume requesting ** data from the transport. ** ** @param sockfd The socket to receive with ** @param rbuf where to put the received data ** @param len maximum amount of data to receive. the amount of data ** returned may be less than len bytes. ** @param flags (This is not currently used but is kept to be compatible ** with the standard sendto socket call). ** ** @returns the number of bytes received, which may be less than the number ** requested by the caller ** @returns -1 on failure with errno set to an error compatible with the standard ** recv call. */ int Xrecv(int sockfd, void *rbuf, size_t len, int flags) { int numbytes; char UDPbuf[MAXBUFLEN]; if (flags) { errno = EOPNOTSUPP; return -1; } if (len == 0) return 0; if (!rbuf) { LOG("buffer pointer is null!\n"); errno = EFAULT; return -1; } if (validateSocket(sockfd, XSOCK_STREAM, EOPNOTSUPP) < 0) { LOGF("Socket %d must be a stream socket", sockfd); return -1; } if (!isConnected(sockfd)) { LOGF("Socket %d is not connected", sockfd); errno = ENOTCONN; return -1; } // see if we have bytes leftover from a previous Xrecv call if ((numbytes = getSocketData(sockfd, (char *)rbuf, len)) > 0) return numbytes; if ((numbytes = click_reply(sockfd, UDPbuf, sizeof(UDPbuf))) < 0) { LOGF("Error retrieving recv data from Click: %s", strerror(errno)); return -1; } std::string str(UDPbuf, numbytes); xia::XSocketMsg xsm; xsm.ParseFromString(str); xia::X_Recv_Msg *msg = xsm.mutable_x_recv(); unsigned paylen = msg->payload().size(); const char *payload = msg->payload().c_str(); if (paylen <= len) memcpy(rbuf, payload, paylen); else { // we got back more data than the caller requested // stash the extra away for subsequent Xrecv calls memcpy(rbuf, payload, len); paylen -= len; setSocketData(sockfd, payload + len, paylen); paylen = len; } return paylen; }
/*! ** @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; }
int XreadNameServerDAG(int sockfd, char *nsDAG) { int rc; char UDPbuf[MAXBUFLEN]; if (getSocketType(sockfd) == XSOCK_INVALID) { LOG("The socket is not a valid Xsocket"); errno = EBADF; return -1; } xia::XSocketMsg xsm; xsm.set_type(xia::XREADNAMESERVERDAG); if ((rc = click_control(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); return -1; } if ((rc = click_reply(sockfd, UDPbuf, sizeof(UDPbuf))) < 0) { LOGF("Error retrieving status from Click: %s", strerror(errno)); return -1; } xia::XSocketMsg xsm1; xsm1.ParseFromString(UDPbuf); if (xsm1.type() == xia::XREADNAMESERVERDAG) { xia::X_ReadNameServerDag_Msg *_msg = xsm1.mutable_x_readnameserverdag(); strcpy(nsDAG, (_msg->dag()).c_str() ); } else { rc = -1; } return rc; }
/*! ** @tell if this node is an XIA-IPv4 dual-stack router ** ** ** @param sockfd an Xsocket (may be of any type XSOCK_STREAM, etc...) ** ** @returns 1 if this is an XIA-IPv4 dual-stack router ** @returns 0 if this is an XIA router ** @returns -1 on failure with errno set ** */ int XisDualStackRouter(int sockfd) { 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::XISDUALSTACKROUTER); unsigned seq = seqNo(sockfd); xsm.set_sequence(seq); if ((rc = click_send(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); return -1; } xia::XSocketMsg xsm1; if ((rc = click_reply(sockfd, seq, &xsm1)) < 0) { LOGF("Error retrieving status from Click: %s", strerror(errno)); return -1; } if (xsm1.type() == xia::XISDUALSTACKROUTER) { xia::X_IsDualStackRouter_Msg *_msg = xsm1.mutable_x_isdualstackrouter(); rc = _msg->flag(); } else { rc = -1; } return rc; }
/*! ** @brief retrieve the AD and HID associated with this socket. ** ** The HID and AD are assigned by the XIA stack. This call retrieves them ** so that they can be used for creating DAGs or for other purposes in user ** applications. ** ** @param sockfd an Xsocket (may be of any type XSOCK_STREAM, etc...) ** @param localhostAD buffer to receive the AD for this host ** @param lenAD size of the localhostAD buffer ** @param localhostHID buffer to receive the HID for this host ** @param lenHID size of the localhostHID buffer ** ** @returns 0 on success ** @returns -1 on failure with errno set ** */ int XreadLocalHostAddr(int sockfd, char *localhostAD, unsigned lenAD, char *localhostHID, unsigned lenHID, char *local4ID, unsigned len4ID) { int rc; if (getSocketType(sockfd) == XSOCK_INVALID) { LOGF("The socket %d is not a valid Xsocket", sockfd); errno = EBADF; return -1; } if (localhostAD == NULL || localhostHID == NULL || local4ID == NULL) { LOG("NULL pointer!"); errno = EINVAL; return -1; } xia::XSocketMsg xsm; xsm.set_type(xia::XREADLOCALHOSTADDR); unsigned seq = seqNo(sockfd); xsm.set_sequence(seq); if ((rc = click_send(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); return -1; } xia::XSocketMsg xsm1; if ((rc = click_reply(sockfd, seq, &xsm1)) < 0) { LOGF("Error retrieving status from Click: %s", strerror(errno)); return -1; } if (xsm1.type() == xia::XREADLOCALHOSTADDR) { xia::X_ReadLocalHostAddr_Msg *_msg = xsm1.mutable_x_readlocalhostaddr(); strncpy(localhostAD, (_msg->ad()).c_str(), lenAD); strncpy(localhostHID, (_msg->hid()).c_str(), lenHID); strncpy(local4ID, (_msg->ip4id()).c_str(), len4ID); // put in null terminators in case buffers were too short localhostAD[lenAD - 1] = 0; localhostHID[lenHID - 1] = 0; local4ID[len4ID - 1] = 0; rc = 0; } else { LOG("XreadlocalHostAddr: ERROR: Invalid response for XREADLOCALHOSTADDR request"); rc = -1; } return rc; }
/*! ** @brief retrieve the AD and HID associated with this socket. ** ** The HID and AD are assigned by the XIA stack. This call retrieves them ** so that they can be used for creating DAGs or for other purposes in user ** applications. ** ** @param sockfd an Xsocket (may be of any type XSOCK_STREAM, etc...) ** @param localhostAD buffer to receive the AD for this host ** @param lenAD size of the localhostAD buffer ** @param localhostHID buffer to receive the HID for this host ** @param lenHID size of the localhostHID buffer ** ** @returns 0 on success ** @returns -1 on failure with errno set ** */ int XreadLocalHostAddr(int sockfd, char *localhostAD, unsigned lenAD, char *localhostHID, unsigned lenHID, char *local4ID, unsigned len4ID) { int rc; char UDPbuf[MAXBUFLEN]; if (getSocketType(sockfd) == XSOCK_INVALID) { LOG("The socket is not a valid Xsocket"); errno = EBADF; return -1; } if (localhostAD == NULL || localhostHID == NULL || local4ID == NULL) { LOG("NULL pointer!"); errno = EINVAL; return -1; } xia::XSocketMsg xsm; xsm.set_type(xia::XREADLOCALHOSTADDR); if ((rc = click_send(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); return -1; } if ((rc = click_reply(sockfd, UDPbuf, sizeof(UDPbuf))) < 0) { LOGF("Error retrieving status from Click: %s", strerror(errno)); return -1; } xia::XSocketMsg xsm1; xsm1.ParseFromString(UDPbuf); if (xsm1.type() == xia::XREADLOCALHOSTADDR) { xia::X_ReadLocalHostAddr_Msg *_msg = xsm1.mutable_x_readlocalhostaddr(); strncpy(localhostAD, (_msg->ad()).c_str(), lenAD); strncpy(localhostHID, (_msg->hid()).c_str(), lenHID); strncpy(local4ID, (_msg->ip4id()).c_str(), len4ID); // put in null terminators in case buffers were too short localhostAD[lenAD - 1] = 0; localhostHID[lenHID - 1] = 0; local4ID[len4ID - 1] = 0; rc = 0; } else { rc = -1; } return rc; }
/*! ** @brief Get the full DAG of the local socket. ** ** @param sockfd An Xsocket of type SOCK_STREAM ** @param dag A sockaddr to hold the returned DAG. ** @param len On input contans the size of the sockaddr, ** on output contains sizeof(sockaddr_x). ** ** @returns 0 on success ** @returns -1 on failure with errno set ** @returns errno = EFAULT if dag is NULL ** @returns errno = EOPNOTSUPP if sockfd is not of type XSSOCK_STREAM ** @returns errno = ENOTCONN if sockfd is not in a connected state ** */ int Xgetsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int rc; int flags; char buf[MAXBUFLEN]; if (!addr || !addrlen) { LOG("pointer is null!\n"); errno = EFAULT; return -1; } if (*addrlen < sizeof(sockaddr_x)) { errno = EINVAL; return -1; } if (validateSocket(sockfd, XSOCK_STREAM, EOPNOTSUPP) < 0) { LOG("Xgetsockname is only valid with stream sockets."); return -1; } if (connState(sockfd) != CONNECTED) { LOGF("Socket %d is not connected", sockfd); errno = ENOTCONN; return -1; } xia::XSocketMsg xsm; xsm.set_type(xia::XGETSOCKNAME); flags = Xfcntl(sockfd, F_GETFL); fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK); // send the protobuf containing the user data to click if ((rc = click_send(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); fcntl(sockfd, F_SETFL, flags); return -1; } // get the dag // FIXME: loop here till done or error if ((rc = click_reply(sockfd, xia::XGETSOCKNAME, buf, sizeof(buf))) < 0) { LOGF("Error retrieving status from Click: %s", strerror(errno)); fcntl(sockfd, F_SETFL, flags); return -1; } fcntl(sockfd, F_SETFL, flags); xsm.Clear(); xsm.ParseFromString(buf); if (xsm.type() != xia::XGETSOCKNAME) { LOGF("error: expected %d, got %d\n", xia::XGETPEERNAME, xsm.type()); return -1; } xia::X_GetSockname_Msg *msg = xsm.mutable_x_getsockname(); Graph g(msg->dag().c_str()); g.fill_sockaddr((sockaddr_x*)addr); *addrlen = sizeof(sockaddr_x); return 0; }
/*! ** @brief Xsocket implemention of the standard getsockopt function. ** ** Xgetsockopt is used to retrieve the settings of the underlying Xsocket ** in the Click layer. It does not access the settings of the actual ** socket passed in which is used by the API to communicate with Click. ** ** Supported Options: ** \n XOPT_HLIM Retrieves the 'hop limit' element of the XIA header as an integer value ** \n XOPT_NEXT_PROTO Gets the next proto field in the XIA header ** ** @param sockfd The control socket ** @param optname The socket option to set (currently must be IP_TTL) ** @param optval A pointer to the value to retrieve ** @param optlen A pointer to the length of optval. On input this ** should be set to the length of the data passed in. If optlen is not ** large enough, Xgetsockopt will set it to the size needed and return ** with errno set to EINVAL. If larger than needed, Xgetsockopt will set ** it to the actual length of the returned data. ** ** @returns 0 on success ** @returns -1 on error with errno set ** @returns errno values: ** @returns EBADF: The socket descriptor is invalid ** @returns EINVAL: Either optval is null, or optlen is invalid, or optval is out of range ** @returns ENOPROTOOPT: the specified optname is not recognized */ int Xgetsockopt(int sockfd, int optname, void *optval, socklen_t *optlen) { char buf[MAXBUFLEN]; /* TODO: we may need to check the type of the socket at some point, but for now ** treat them all the same as far as options go. ** ** Should we add a validate socket function that takes the expected type? */ if (getSocketType(sockfd) == XSOCK_INVALID) { errno = EBADF; return -1; } if (!optval || !optlen) { errno = EINVAL; return -1; } xia::XSocketMsg xsm; xsm.set_type(xia::XGETSOCKOPT); xia::X_Getsockopt_Msg *msg = xsm.mutable_x_getsockopt(); msg->set_opt_type(optname); switch (optname) { case SO_ERROR: { if (*optlen < sizeof(int)) { *optlen = sizeof(int); errno = EINVAL; return -1; } *(int *)optval = getError(sockfd); break; } case XOPT_HLIM: { if (*optlen < sizeof(int)) { *optlen = sizeof(int); errno = EINVAL; return -1; } int flags = fcntl(sockfd, F_GETFL); fcntl(sockfd, F_SETFL, flags &= ~O_NONBLOCK); if (click_send(sockfd, &xsm) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); fcntl(sockfd, F_SETFL, flags); return -1; } xia::XSocketMsg reply; if (click_reply(sockfd, xia::XGETSOCKOPT, buf, sizeof(buf)) < 0) { LOGF("Error getting status from Click: %s", strerror(errno)); fcntl(sockfd, F_SETFL, flags); return -1; } fcntl(sockfd, F_SETFL, flags); xsm.Clear(); xsm.ParseFromString(buf); xia::X_Getsockopt_Msg *msg = xsm.mutable_x_getsockopt(); int hlim = msg->int_opt(); *optlen = sizeof(int); *(int *)optval = hlim; break; } case XOPT_NEXT_PROTO: { if (*optlen < sizeof(int)) { *optlen = sizeof(int); errno = EINVAL; return -1; } int flags = fcntl(sockfd, F_GETFL); fcntl(sockfd, F_SETFL, flags &= ~O_NONBLOCK); if (click_send(sockfd, &xsm) < 0) { fcntl(sockfd, F_SETFL, flags); LOGF("Error talking to Click: %s", strerror(errno)); return -1; } xia::XSocketMsg reply; if (click_reply(sockfd, xia::XGETSOCKOPT, buf, sizeof(buf)) < 0) { fcntl(sockfd, F_SETFL, flags); LOGF("Error getting status from Click: %s", strerror(errno)); return -1; } fcntl(sockfd, F_SETFL, flags); xsm.Clear(); xsm.ParseFromString(buf); xia::X_Getsockopt_Msg *msg = xsm.mutable_x_getsockopt(); int nxt = msg->int_opt(); *optlen = sizeof(int); *(int *)optval = nxt; break; } default: errno = ENOPROTOOPT; return -1; } return 0; }
/*! ** @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); }
/* ** @brief Checks the status for each of the requested CIDs. ** ** XgetChunkStatuses updates the cDAGv list with the status for each ** of the requested CIDs. An overall status value is returned, and ** the cDAGv list can be examined to check the status of each individual CID. ** ** @note This function Should be called after calling XrequestChunk() or ** XrequestChunks(). Otherwise the content chunk will never be loaded into ** the content cache and will result in a REQUEST_FAILED error. ** ** @param sockfd - the control socket (must be of type XSOCK_CHUNK) ** @param cDAGv - list of CIDs to check. On return, also contains the status for ** each of the specified CIDs. ** @param numCIDs - number of CIDs in cDAGv ** ** @returns a bitfield indicating the status of the chunks. ** @returns if return equals READY_TO_READ, all chunks are avilable to read ** @returns otherwise the return value contains a bitfield of status codes ** @returns if REQUEST_FAILED is set, one or more of the requested chunks could not be found ** @returns if WAITING_FOR_CHUNK is set, one or more of the requested chunks is still in transit ** @returns if INVALID_HASH is set, the content of one or more chunks does not match the hash in the CID ** @returns REQUEST_FAILED if one of the specified chunks has not been requested, ** @returns -1 if a socket error occurs. In that case errno is set with the appropriate code. */ int XgetChunkStatuses(int sockfd, ChunkStatus *statusList, int numCIDs) { int rc; char buffer[MAXBUFLEN]; const char *buf="CID list request status query";//Maybe send more useful information here. if (validateSocket(sockfd, XSOCK_CHUNK, EAFNOSUPPORT) < 0) { LOGF("Socket %d must be a chunk socket\n", sockfd); return -1; } if (numCIDs == 0) return 0; if (!statusList) { LOG("statusList is null!"); errno = EFAULT; return -1; } // protobuf message xia::XSocketMsg xsm; xsm.set_type(xia::XGETCHUNKSTATUS); xia::X_Getchunkstatus_Msg *x_getchunkstatus_msg = xsm.mutable_x_getchunkstatus(); for (int i = 0; i < numCIDs; i++) { if (statusList[i].cid) { x_getchunkstatus_msg->add_dag(statusList[i].cid); } else { LOGF("cDAGv[%d] is NULL", i); } } if (x_getchunkstatus_msg->dag_size() == 0) { LOG("no dags were specified!"); errno = EFAULT; return -1; } x_getchunkstatus_msg->set_payload((const char*)buf, strlen(buf) + 1); std::string p_buf; xsm.SerializeToString(&p_buf); if ((rc = click_send(sockfd, &xsm)) < 0) { LOGF("Error talking to Click: %s", strerror(errno)); return -1; } if ((rc = click_reply(sockfd, buffer, sizeof(buffer))) < 0) { LOGF("Error retrieving status from Click: %s", strerror(errno)); return -1; } xia::XSocketMsg xia_socket_msg1; xia_socket_msg1.ParseFromString(buffer); if (xia_socket_msg1.type() == xia::XGETCHUNKSTATUS) { xia::X_Getchunkstatus_Msg *x_getchunkstatus_msg1 = xia_socket_msg1.mutable_x_getchunkstatus(); char status_tmp[100]; int status_for_all = READY_TO_READ; for (int i = 0; i < numCIDs; i++) { strcpy(status_tmp, x_getchunkstatus_msg1->status(i).c_str()); if (strcmp(status_tmp, "WAITING") == 0) { statusList[i].status = WAITING_FOR_CHUNK; status_for_all &= ~READY_TO_READ; status_for_all |= WAITING_FOR_CHUNK; } else if (strcmp(status_tmp, "INVALID_HASH") == 0) { statusList[i].status = INVALID_HASH; status_for_all &= ~READY_TO_READ; status_for_all |= INVALID_HASH; } else if (strcmp(status_tmp, "READY") == 0) { statusList[i].status = READY_TO_READ; } else if (strcmp(status_tmp, "FAILED") == 0) { statusList[i].status = REQUEST_FAILED; status_for_all &= ~READY_TO_READ; status_for_all |= REQUEST_FAILED; } else { statusList[i].status = REQUEST_FAILED; status_for_all &= ~READY_TO_READ; status_for_all |= REQUEST_FAILED; } } rc = status_for_all; } else rc = -1; return rc; }