gint host_epollControl(Host* host, gint epollDescriptor, gint operation, gint fileDescriptor, struct epoll_event* event) { MAGIC_ASSERT(host); /* EBADF epfd is not a valid file descriptor. */ Descriptor* descriptor = host_lookupDescriptor(host, epollDescriptor); if(descriptor == NULL) { return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", epollDescriptor); return EBADF; } /* EINVAL epfd is not an epoll file descriptor */ if(descriptor_getType(descriptor) != DT_EPOLL) { return EINVAL; } /* now we know its an epoll */ Epoll* epoll = (Epoll*) descriptor; /* if this is for a system file, forward to system call */ if(!host_isShadowDescriptor(host, fileDescriptor)) { gint osfd = host_getOSHandle(host, fileDescriptor); osfd = osfd >= 0 ? osfd : fileDescriptor; gint oldEventFD = event->data.fd; event->data.fd = osfd; gint result = epoll_controlOS(epoll, operation, osfd, event); event->data.fd = oldEventFD; return result; } /* EBADF fd is not a valid shadow file descriptor. */ descriptor = host_lookupDescriptor(host, fileDescriptor); if(descriptor == NULL) { return EBADF; } status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", fileDescriptor); return EBADF; } return epoll_control(epoll, operation, descriptor, event); }
gint host_epollGetEvents(Host* host, gint handle, struct epoll_event* eventArray, gint eventArrayLength, gint* nEvents) { MAGIC_ASSERT(host); /* EBADF epfd is not a valid file descriptor. */ Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } /* EINVAL epfd is not an epoll file descriptor */ if(descriptor_getType(descriptor) != DT_EPOLL) { return EINVAL; } Epoll* epoll = (Epoll*) descriptor; gint ret = epoll_getEvents(epoll, eventArray, eventArrayLength, nEvents); for(gint i = 0; i < *nEvents; i++) { if(!host_isShadowDescriptor(host, eventArray[i].data.fd)) { /* the fd is a file that the OS handled for us, translate to shadow fd */ eventArray[i].data.fd = host_getShadowHandle(host, eventArray[i].data.fd); utility_assert(eventArray[i].data.fd >= 0); } } return ret; }
static gboolean _epollwatch_needsNotify(EpollWatch* watch) { MAGIC_ASSERT(watch); /* check status */ enum DescriptorStatus status = descriptor_getStatus(watch->descriptor); /* check if we care about the status */ gboolean isActive = (status & DS_ACTIVE) ? TRUE : FALSE; gboolean isReadable = (status & DS_READABLE) ? TRUE : FALSE; gboolean waitingReadable = (watch->event.events & EPOLLIN) ? TRUE : FALSE; gboolean isWritable = (status & DS_WRITABLE) ? TRUE : FALSE; gboolean waitingWritable = (watch->event.events & EPOLLOUT) ? TRUE : FALSE; if(isActive && ((isReadable && waitingReadable) || (isWritable && waitingWritable))) { return TRUE; } else { return FALSE; } }
gint host_acceptNewPeer(Host* host, gint handle, in_addr_t* ip, in_port_t* port, gint* acceptedHandle) { MAGIC_ASSERT(host); Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET) { return EOPNOTSUPP; } return tcp_acceptServerPeer((TCP*)descriptor, ip, port, acceptedHandle); }
gint host_listenForPeer(Host* host, gint handle, gint backlog) { MAGIC_ASSERT(host); Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET) { warning("wrong type for descriptor handle '%i'", handle); return EOPNOTSUPP; } Socket* socket = (Socket*) descriptor; TCP* tcp = (TCP*) descriptor; if(!socket_isBound(socket)) { /* implicit bind */ in_addr_t bindAddress = htonl(INADDR_ANY); in_port_t bindPort = _host_getRandomFreePort(host, bindAddress, type); if(!bindPort) { return EADDRNOTAVAIL; } _host_associateInterface(host, socket, bindAddress, bindPort); } tcp_enterServerMode(tcp, backlog); return 0; }
gint host_getPeerName(Host* host, gint handle, const struct sockaddr* address, socklen_t* len) { MAGIC_ASSERT(host); Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET) { return ENOTCONN; } Socket* sock = (Socket*)descriptor; in_addr_t ip = 0; in_port_t port = 0; gboolean hasPeer = socket_getPeerName(sock, &ip, &port); if(hasPeer) { if(socket_isUnix(sock)) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; saddr->sun_family = AF_UNIX; gchar* unixPath = socket_getUnixPath(sock); if(unixPath) { g_snprintf(saddr->sun_path, 107, "%s\\0", unixPath); *len = offsetof(struct sockaddr_un, sun_path) + strlen(saddr->sun_path) + 1; } else { *len = sizeof(sa_family_t); } } else {
gint epoll_getEvents(Epoll* epoll, struct epoll_event* eventArray, gint eventArrayLength, gint* nEvents) { MAGIC_ASSERT(epoll); g_assert(nEvents); epoll->lastWaitTime = worker_getPrivate()->clock_now; /* return the available events in the eventArray, making sure not to * overflow. the number of actual events is returned in nEvents. * * @todo: could be more efficient if we kept track of which descriptors * are ready at any given time, at the cost of code complexity (we'd have * to manage descriptors in multiples structs, update when deleted, etc) */ GHashTableIter iter; gpointer key, value; gint i = 0; g_hash_table_iter_init(&iter, epoll->watches); while(g_hash_table_iter_next(&iter, &key, &value)) { EpollWatch* watch = value; MAGIC_ASSERT(watch); /* return the event types that are actually available if they are registered * without removing our knowledge of what was registered */ enum DescriptorStatus status = descriptor_getStatus(watch->descriptor); gboolean isReadable = (status & DS_READABLE) ? TRUE : FALSE; gboolean waitingReadable = (watch->event.events & EPOLLIN) ? TRUE : FALSE; gboolean isWritable = (status & DS_WRITABLE) ? TRUE : FALSE; gboolean waitingWritable = (watch->event.events & EPOLLOUT) ? TRUE : FALSE; if((isReadable && waitingReadable) || (isWritable && waitingWritable)) { /* report the event */ eventArray[i] = watch->event; eventArray[i].events = isReadable && isWritable ? EPOLLIN|EPOLLOUT : isReadable ? EPOLLIN : EPOLLOUT; /* no longer needs to be reported. if the user does not take action, * we'll mark it as needs reporting again in ensureNotifyTriggers */ g_hash_table_remove(epoll->reports, descriptor_getHandleReference(watch->descriptor)); i++; } /* if we've filled everything, stop iterating */ if(i >= eventArrayLength) { break; } } if(i < eventArrayLength) { /* now we have to get events from the OS descriptors */ struct epoll_event osEvents[20]; /* since we are in shadow context, this will be forwarded to the OS epoll */ gint nos = epoll_wait(epoll->osEpollDescriptor, osEvents, 20, 0); if(nos == -1) { warning("error in epoll_wait for OS events on epoll fd %i", epoll->osEpollDescriptor); } for(gint j = 0; j < nos; j++) { eventArray[i] = osEvents[j]; i++; /* if we've filled everything, stop iterating */ if(i >= eventArrayLength) { break; } } } *nEvents = i; return 0; }
gint host_connectToPeer(Host* host, gint handle, const struct sockaddr* address) { MAGIC_ASSERT(host); sa_family_t family = 0; in_addr_t peerIP = 0; in_port_t peerPort = 0; if(address->sa_family == AF_INET) { struct sockaddr_in* saddr = (struct sockaddr_in*) address; family = saddr->sin_family; peerIP = saddr->sin_addr.s_addr; peerPort = saddr->sin_port; } else if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; family = saddr->sun_family; gchar* sockpath = saddr->sun_path; peerIP = htonl(INADDR_LOOPBACK); gpointer val = g_hash_table_lookup(host->unixPathToPortMap, sockpath); if(val) { peerPort = (in_port_t)GPOINTER_TO_UINT(val); } } in_addr_t loIP = htonl(INADDR_LOOPBACK); /* make sure we will be able to route this later */ if(peerIP != loIP) { Address* myAddress = networkinterface_getAddress(host->defaultInterface); Address* peerAddress = dns_resolveIPToAddress(worker_getDNS(), peerIP); if(!peerAddress || !topology_isRoutable(worker_getTopology(), myAddress, peerAddress)) { /* can't route it - there is no node with this address */ gchar* peerAddressString = address_ipToNewString(peerIP); warning("attempting to connect to address '%s:%u' for which no host exists", peerAddressString, ntohs(peerPort)); g_free(peerAddressString); return ECONNREFUSED; } } Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET && type != DT_UDPSOCKET) { warning("wrong type for descriptor handle '%i'", handle); return ENOTSOCK; } Socket* socket = (Socket*) descriptor; if(!socket_isFamilySupported(socket, family)) { return EAFNOSUPPORT; } if(type == DT_TCPSOCKET) { gint error = tcp_getConnectError((TCP*)socket); if(error) { return error; } } if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; socket_setUnixPath(socket, saddr->sun_path, FALSE); } if(!socket_isBound(socket)) { /* do an implicit bind to a random port. * use default interface unless the remote peer is on loopback */ in_addr_t defaultIP = networkinterface_getIPAddress(host->defaultInterface); in_addr_t bindAddress = loIP == peerIP ? loIP : defaultIP; in_port_t bindPort = _host_getRandomFreePort(host, bindAddress, type); if(!bindPort) { return EADDRNOTAVAIL; } _host_associateInterface(host, socket, bindAddress, bindPort); } return socket_connectToPeer(socket, peerIP, peerPort, family); }
gint host_bindToInterface(Host* host, gint handle, const struct sockaddr* address) { MAGIC_ASSERT(host); in_addr_t bindAddress = 0; in_port_t bindPort = 0; if(address->sa_family == AF_INET) { struct sockaddr_in* saddr = (struct sockaddr_in*) address; bindAddress = saddr->sin_addr.s_addr; bindPort = saddr->sin_port; } else if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; /* cant bind twice to the same unix path */ if(g_hash_table_lookup(host->unixPathToPortMap, saddr->sun_path)) { return EADDRINUSE; } bindAddress = htonl(INADDR_LOOPBACK); bindPort = 0; /* choose a random free port below */ } Descriptor* descriptor = host_lookupDescriptor(host, handle); if(descriptor == NULL) { warning("descriptor handle '%i' not found", handle); return EBADF; } DescriptorStatus status = descriptor_getStatus(descriptor); if(status & DS_CLOSED) { warning("descriptor handle '%i' not a valid open descriptor", handle); return EBADF; } DescriptorType type = descriptor_getType(descriptor); if(type != DT_TCPSOCKET && type != DT_UDPSOCKET) { warning("wrong type for descriptor handle '%i'", handle); return ENOTSOCK; } /* make sure we have an interface at that address */ if(!_host_doesInterfaceExist(host, bindAddress)) { return EADDRNOTAVAIL; } Socket* socket = (Socket*) descriptor; /* make sure socket is not bound */ if(socket_isBound(socket)) { warning("socket already bound to requested address"); return EINVAL; } /* make sure we have a proper port */ if(bindPort == 0) { /* we know it will be available */ bindPort = _host_getRandomFreePort(host, bindAddress, type); if(!bindPort) { return EADDRNOTAVAIL; } } else { /* make sure their port is available at that address for this protocol. */ if(!_host_isInterfaceAvailable(host, bindAddress, type, bindPort)) { return EADDRINUSE; } } /* bind port and set associations */ _host_associateInterface(host, socket, bindAddress, bindPort); if (address->sa_family == AF_UNIX) { struct sockaddr_un* saddr = (struct sockaddr_un*) address; gchar* sockpath = g_strndup(saddr->sun_path, 108); /* UNIX_PATH_MAX=108 */ socket_setUnixPath(socket, sockpath, TRUE); g_hash_table_replace(host->unixPathToPortMap, sockpath, GUINT_TO_POINTER(bindPort)); } return 0; }