Index: libraries/nacl-mounts/base/KernelProxy.cc |
=================================================================== |
--- libraries/nacl-mounts/base/KernelProxy.cc (revision 594) |
+++ libraries/nacl-mounts/base/KernelProxy.cc (working copy) |
@@ -3,13 +3,22 @@ |
* Use of this source code is governed by a BSD-style license that can be |
* found in the LICENSE file. |
*/ |
+#include "base/KernelProxy.h" |
#include <assert.h> |
+#ifdef __GLIBC__ |
+#include <netinet/in.h> |
+#else |
+#include "../base/newlib_hacks.h" |
+#endif |
+#include <sys/time.h> |
#include <list> |
#include <utility> |
-#include "../console/ConsoleMount.h" |
-#include "../dev/DevMount.h" |
-#include "KernelProxy.h" |
+#include "console/ConsoleMount.h" |
+#include "dev/DevMount.h" |
#include "MountManager.h" |
+#include "net/BaseSocketSubSystem.h" |
+#include "net/SocketSubSystem.h" |
+#include "util/DebugPrint.h" |
static pthread_once_t kp_once_ = PTHREAD_ONCE_INIT; |
KernelProxy *KernelProxy::kp_instance_; |
@@ -18,6 +27,9 @@ |
#define MAXPATHLEN 256 |
#endif |
+static const int64_t kMicrosecondsPerSecond = 1000 * 1000; |
+static const int64_t kNanosecondsPerMicrosecond = 1000; |
+ |
KernelProxy::KernelProxy() { |
if (pthread_mutex_init(&kp_lock_, NULL)) assert(0); |
cwd_ = Path("/"); |
@@ -42,6 +54,34 @@ |
assert(fd == 2); |
} |
+void KernelProxy::SetSocketSubSystem(BaseSocketSubSystem* bss) { |
+ socket_subsystem_ = bss; |
+} |
+ |
+void KernelProxy::RemoveSocket(int fd) { |
+ this->close(fd); |
+} |
+ |
+int KernelProxy::AddSocket(Socket* stream) { |
+ SimpleAutoLock lock(&kp_lock_); |
+ |
+ // Setup file handle. |
+ int handle_slot = open_files_.Alloc(); |
+ int fd = fds_.Alloc(); |
+ FileDescriptor* file = fds_.At(fd); |
+ file->handle = handle_slot; |
+ FileHandle* handle = open_files_.At(handle_slot); |
+ |
+ // init should be safe because we have the kernel proxy lock |
+ if (pthread_mutex_init(&handle->lock, NULL)) assert(0); |
+ |
+ handle->mount = (Mount*) NULL; |
+ handle->stream = (Socket*) NULL; |
+ handle->use_count = 1; |
+ |
+ return fd; |
+} |
+ |
KernelProxy *KernelProxy::KPInstance() { |
pthread_once(&kp_once_, Instantiate); |
return kp_instance_; |
@@ -177,12 +217,13 @@ |
int fd = fds_.Alloc(); |
FileDescriptor* file = fds_.At(fd); |
file->handle = handle_slot; |
- FileHandle *handle = open_files_.At(handle_slot); |
+ FileHandle* handle = open_files_.At(handle_slot); |
// init should be safe because we have the kernel proxy lock |
if (pthread_mutex_init(&handle->lock, NULL)) assert(0); |
handle->mount = mount; |
+ handle->stream = NULL; |
handle->node = st.st_ino; |
handle->flags = flags; |
handle->use_count = 1; |
@@ -196,6 +237,50 @@ |
return fd; |
} |
+static __thread struct hostent* ghn_res = NULL; |
+static __thread char** ghn_addr_list = NULL; |
+static __thread char* ghn_item = NULL; |
+ |
+struct hostent* KernelProxy::gethostbyname(const char* name) { |
+ struct hostent *res = ghn_res == NULL |
+ ? (ghn_res = (struct hostent*) malloc(sizeof(struct hostent))) |
+ : ghn_res; |
+ if (!res) return NULL; |
+ res->h_addr_list = ghn_addr_list == NULL |
+ ? (ghn_addr_list = (char**) malloc(sizeof(char*) * 2)) |
+ : ghn_addr_list; |
+ if (!res->h_addr_list) return NULL; |
+ res->h_addr_list[0] = ghn_item == NULL |
+ ? (ghn_item = (char*) malloc(sizeof(long int))) |
+ : ghn_item; |
+ if (!res->h_addr_list[0]) return NULL; |
+ *((long int*)res->h_addr_list[0]) = socket_subsystem_->gethostbyname(name); |
+ res->h_addr_list[1] = NULL; |
+ res->h_length = sizeof(long int); |
+ return res; |
+} |
+ |
+int KernelProxy::getaddrinfo(const char* hostname, const char* servname, |
+ const struct addrinfo* hints, struct addrinfo** res) { |
+ dbgprintf("getaddrinfo: %s %s\n", |
+ hostname ? hostname : "", servname ? servname : ""); |
+ return socket_subsystem_->getaddrinfo( |
+ hostname, servname, hints, res); |
+} |
+ |
+void KernelProxy::freeaddrinfo(struct addrinfo* ai) { |
+ dbgprintf("freeaddrinfo\n"); |
+ return socket_subsystem_->freeaddrinfo(ai); |
+} |
+ |
+int KernelProxy::getnameinfo(const struct sockaddr *sa, socklen_t salen, |
+ char *host, socklen_t hostlen, |
+ char *serv, socklen_t servlen, unsigned int flags) { |
+ dbgprintf("getnameinfo\n"); |
+ return socket_subsystem_->getnameinfo( |
+ sa, salen, host, hostlen, serv, servlen, flags); |
+} |
+ |
int KernelProxy::open(const std::string& path, int flags, mode_t mode) { |
if (path.empty()) { |
errno = EINVAL; |
@@ -219,7 +304,7 @@ |
} |
int KernelProxy::close(int fd) { |
- SimpleAutoLock lock(&kp_lock_); |
+ SimpleAutoLock* lock = new SimpleAutoLock(&kp_lock_); |
FileDescriptor* file = fds_.At(fd); |
if (file == NULL) { |
@@ -228,48 +313,66 @@ |
} |
int h = file->handle; |
fds_.Free(fd); |
- FileHandle *handle = open_files_.At(h); |
+ FileHandle* handle = open_files_.At(h); |
if (handle == NULL) { |
errno = EBADF; |
return -1; |
} |
handle->use_count--; |
ino_t node = handle->node; |
- Mount *mount = handle->mount; |
- if (handle->use_count <= 0) { |
- open_files_.Free(h); |
- mount->Unref(node); |
+ if (handle->mount) { |
+ Mount* mount = handle->mount; |
+ if (handle->use_count <= 0) { |
+ open_files_.Free(h); |
+ mount->Unref(node); |
+ } |
+ mount->Unref(); |
+ } else { |
+ Socket* stream = handle->stream; |
+ delete lock; |
+ socket_subsystem_->close(stream); |
+ SimpleAutoLock lock(&kp_lock_); |
+ if (handle->use_count <= 0) { |
+ open_files_.Free(h); |
+ } |
+ return 0; |
} |
- mount->Unref(); |
+ delete lock; |
return 0; |
} |
ssize_t KernelProxy::read(int fd, void *buf, size_t count) { |
- FileHandle *handle; |
+ FileHandle* handle; |
// check if fd is valid and handle exists |
if (!(handle = GetFileHandle(fd))) { |
errno = EBADF; |
return -1; |
} |
- SimpleAutoLock(&handle->lock); |
+ if (handle->mount) { |
+ SimpleAutoLock(&handle->lock); |
+ // Check that this file handle can be read from. |
+ if ((handle->flags & O_ACCMODE) == O_WRONLY || |
+ is_dir(handle->mount, handle->node)) { |
+ errno = EBADF; |
+ return -1; |
+ } |
- // Check that this file handle can be read from. |
- if ((handle->flags & O_ACCMODE) == O_WRONLY || |
- is_dir(handle->mount, handle->node)) { |
- errno = EBADF; |
- return -1; |
+ ssize_t n = handle->mount->Read(handle->node, handle->offset, buf, count); |
+ if (n > 0) { |
+ handle->offset += n; |
+ } |
+ return n; |
+ } else if (handle->stream) { |
+ // TODO(vissi): more elaborate implementation |
+ return recv(fd, buf, count, 0); |
} |
- |
- ssize_t n = handle->mount->Read(handle->node, handle->offset, buf, count); |
- if (n > 0) { |
- handle->offset += n; |
- } |
- return n; |
+ errno = EBADF; |
+ return -1; |
} |
ssize_t KernelProxy::write(int fd, const void *buf, size_t count) { |
- FileHandle *handle; |
+ FileHandle* handle; |
// check if fd is valid and handle exists |
if (!(handle = GetFileHandle(fd))) { |
@@ -277,25 +380,31 @@ |
return -1; |
} |
- SimpleAutoLock(&handle->lock); |
+ if (handle->mount) { |
+ SimpleAutoLock(&handle->lock); |
+ // Check that this file handle can be written to. |
+ if ((handle->flags & O_ACCMODE) == O_RDONLY || |
+ is_dir(handle->mount, handle->node)) { |
+ errno = EBADF; |
+ return -1; |
+ } |
- // Check that this file handle can be written to. |
- if ((handle->flags & O_ACCMODE) == O_RDONLY || |
- is_dir(handle->mount, handle->node)) { |
- errno = EBADF; |
- return -1; |
- } |
+ ssize_t n = handle->mount->Write(handle->node, handle->offset, buf, count); |
- ssize_t n = handle->mount->Write(handle->node, handle->offset, buf, count); |
- |
- if (n > 0) { |
- handle->offset += n; |
+ if (n > 0) { |
+ handle->offset += n; |
+ } |
+ return n; |
+ } else if (handle->stream) { |
+ // TODO(vissi): more elaborate implementation |
+ return send(fd, buf, count, 0); |
} |
- return n; |
+ errno = EBADF; |
+ return -1; |
} |
int KernelProxy::fstat(int fd, struct stat *buf) { |
- FileHandle *handle; |
+ FileHandle* handle; |
// check if fd is valid and handle exists |
if (!(handle = GetFileHandle(fd))) { |
@@ -321,7 +430,7 @@ |
} |
int KernelProxy::getdents(int fd, void *buf, unsigned int count) { |
- FileHandle *handle; |
+ FileHandle* handle; |
// check if fd is valid and handle exists |
if (!(handle = GetFileHandle(fd))) { |
@@ -343,7 +452,7 @@ |
} |
int KernelProxy::fsync(int fd) { |
- FileHandle *handle; |
+ FileHandle* handle; |
if (!(handle = GetFileHandle(fd))) { |
errno = EBADF; |
@@ -354,19 +463,70 @@ |
} |
int KernelProxy::isatty(int fd) { |
- FileHandle *handle; |
+ FileHandle* handle; |
if (!(handle = GetFileHandle(fd))) { |
errno = EBADF; |
return 0; |
} |
- SimpleAutoLock(&handle->lock); |
return handle->mount->Isatty(handle->node); |
} |
+int KernelProxy::dup2(int fd, int newfd) { |
+ SimpleAutoLock lock(&kp_lock_); |
+ int handle_slot = fds_.At(fd)->handle; |
+ if (fds_.AllocAt(fd) != fd) return -1; |
+ FileDescriptor* file = fds_.At(newfd); |
+ file->handle = handle_slot; |
+ return 0; |
+} |
+ |
+int KernelProxy::IsReady(int nfds, fd_set* fds, |
+ bool (Socket::*is_ready)(), bool apply) { |
+ if (!fds) |
+ return 0; |
+ |
+ int nset = 0; |
+ for (int i = 0; i < nfds; i++) { |
+ if (FD_ISSET(i, fds)) { |
+ Socket* stream = GetFileHandle(i) > 0 ? |
+ GetFileHandle(i)->stream : NULL; |
+ if (!stream) |
+ return -1; |
+ if ((stream->*is_ready)()) { |
+ if (!apply) |
+ return 1; |
+ else |
+ nset++; |
+ } else { |
+ if (apply) |
+ FD_CLR(i, fds); |
+ } |
+ } |
+ } |
+ return nset; |
+} |
+ |
int KernelProxy::dup(int oldfd) { |
SimpleAutoLock lock(&kp_lock_); |
+ FileHandle* fh = GetFileHandle(oldfd); |
+ if (!fh) { |
+ errno = EBADF; |
+ return -1; |
+ } |
+ if (fh->mount == NULL) { |
+ Socket* stream = GetFileHandle(oldfd) > 0 |
+ ? GetFileHandle(oldfd)->stream : NULL; |
+ if (!stream) |
+ return EBADF; |
+ SimpleAutoLock lock(&kp_lock_); |
+ int handle_slot = fds_.At(oldfd)->handle; |
+ int newfd = fds_.Alloc(); |
+ FileDescriptor* file = fds_.At(newfd); |
+ file->handle = handle_slot; |
+ return newfd; |
+ } |
FileDescriptor* oldfile = fds_.At(oldfd); |
if (oldfile == NULL) { |
errno = EBADF; |
@@ -376,7 +536,7 @@ |
FileDescriptor *newfile = fds_.At(newfd); |
int h = oldfile->handle; |
newfile->handle = h; |
- FileHandle *handle = open_files_.At(h); |
+ FileHandle* handle = open_files_.At(h); |
// init should be safe because we have the kernel proxy lock |
if (pthread_mutex_init(&handle->lock, NULL)) assert(0); |
@@ -391,7 +551,7 @@ |
} |
off_t KernelProxy::lseek(int fd, off_t offset, int whence) { |
- FileHandle *handle; |
+ FileHandle* handle; |
// check if fd is valid and handle exists |
if (!(handle = GetFileHandle(fd))) { |
errno = EBADF; |
@@ -618,7 +778,7 @@ |
return 0; |
} |
-KernelProxy::FileHandle *KernelProxy::GetFileHandle(int fd) { |
+KernelProxy::FileHandle* KernelProxy::GetFileHandle(int fd) { |
SimpleAutoLock lock(&kp_lock_); |
FileDescriptor *file = fds_.At(fd); |
if (!file) { |
@@ -674,44 +834,64 @@ |
return mount->Rmdir(buf.st_ino); |
} |
-#ifdef __GLIBC__ |
int KernelProxy::socket(int domain, int type, int protocol) { |
- errno = ENOSYS; |
- fprintf(stderr, "socket has not been implemented!\n"); |
- return -1; |
+ SimpleAutoLock lock(&kp_lock_); |
+ int handle_slot = open_files_.Alloc(); |
+ int fd = fds_.Alloc(); |
+ FileDescriptor* file = fds_.At(fd); |
+ file->handle = handle_slot; |
+ FileHandle* handle = open_files_.At(handle_slot); |
+ // this means it is a socket (not a mount handle) and it's implementation |
+ // determined by handle->stream type is defined later |
+ handle->mount = NULL; |
+ handle->stream = NULL; |
+ handle->use_count = 1; |
+ return fd; |
} |
int KernelProxy::accept(int sockfd, struct sockaddr *addr, |
socklen_t* addrlen) { |
- errno = ENOSYS; |
- fprintf(stderr, "accept has not been implemented!\n"); |
- return -1; |
+ if (GetFileHandle(sockfd) == 0) |
+ return EBADF; |
+ Socket* ret = socket_subsystem_->accept(GetFileHandle(sockfd)->stream, |
+ addr, addrlen); |
+ if (ret) |
+ return AddSocket(ret); |
+ else |
+ return -1; |
} |
int KernelProxy::bind(int sockfd, const struct sockaddr *addr, |
socklen_t addrlen) { |
- errno = ENOSYS; |
- fprintf(stderr, "bind has not been implemented!\n"); |
- return -1; |
+ if (GetFileHandle(sockfd) == 0) |
+ return EBADF; |
+ struct sockaddr_in* in_addr = (struct sockaddr_in*)addr; |
+ return socket_subsystem_->bind(&(GetFileHandle(sockfd)->stream), |
+ addr, addrlen); |
} |
int KernelProxy::listen(int sockfd, int backlog) { |
- errno = ENOSYS; |
- fprintf(stderr, "listen has not been implemented!\n"); |
- return -1; |
+ if (GetFileHandle(sockfd) == 0) |
+ return EBADF; |
+ return socket_subsystem_->listen(GetFileHandle(sockfd)->stream, backlog); |
} |
int KernelProxy::connect(int sockfd, const struct sockaddr *addr, |
socklen_t addrlen) { |
- errno = ENOSYS; |
- fprintf(stderr, "connect has not been implemented!\n"); |
- return -1; |
+ if (GetFileHandle(sockfd) == 0) |
+ return EBADF; |
+ struct sockaddr_in* in_addr = (struct sockaddr_in*)addr; |
+ return socket_subsystem_->connect(&(GetFileHandle(sockfd)->stream), |
+ addr, addrlen); |
} |
int KernelProxy::send(int sockfd, const void *buf, size_t len, int flags) { |
- errno = ENOSYS; |
- fprintf(stderr, "send has not been implemented!\n"); |
- return -1; |
+ if (GetFileHandle(sockfd) == 0) |
+ return EBADF; |
+ size_t nwr; |
+ socket_subsystem_->write(GetFileHandle(sockfd)->stream, (const char*)buf, |
+ len, &nwr); |
+ return nwr; |
} |
int KernelProxy::sendmsg(int sockfd, const struct msghdr *msg, int flags) { |
@@ -728,9 +908,12 @@ |
} |
int KernelProxy::recv(int sockfd, void *buf, size_t len, int flags) { |
- errno = ENOSYS; |
- fprintf(stderr, "recv has not been implemented!\n"); |
- return -1; |
+ if (GetFileHandle(sockfd) == 0) |
+ return EBADF; |
+ size_t nread; |
+ socket_subsystem_->read(GetFileHandle(sockfd)->stream, |
+ reinterpret_cast<char*>(buf), len, &nread); |
+ return nread; |
} |
int KernelProxy::recvmsg(int sockfd, struct msghdr *msg, int flags) { |
@@ -746,11 +929,64 @@ |
return -1; |
} |
+#ifndef TIMEVAL_TO_TIMESPEC |
+/* Macros for converting between `struct timeval' and `struct timespec'. */ |
+# define TIMEVAL_TO_TIMESPEC(tv, ts) { \ |
+ (ts)->tv_sec = (tv)->tv_sec; \ |
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \ |
+} |
+# define TIMESPEC_TO_TIMEVAL(tv, ts) { \ |
+ (tv)->tv_sec = (ts)->tv_sec; \ |
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \ |
+} |
+#endif |
+ |
int KernelProxy::select(int nfds, fd_set *readfds, fd_set *writefds, |
fd_set* exceptfds, const struct timeval *timeout) { |
- errno = ENOSYS; |
- fprintf(stderr, "select has not been implemented!\n"); |
- return -1; |
+ timespec ts_abs; |
+ if (timeout) { |
+ timespec ts; |
+ TIMEVAL_TO_TIMESPEC(timeout, &ts); |
+ timeval tv_now; |
+ gettimeofday(&tv_now, NULL); |
+ int64_t current_time_us = |
+ tv_now.tv_sec * kMicrosecondsPerSecond + tv_now.tv_usec; |
+ int64_t wakeup_time_us = |
+ current_time_us + |
+ timeout->tv_sec * kMicrosecondsPerSecond + timeout->tv_usec; |
+ ts_abs.tv_sec = wakeup_time_us / kMicrosecondsPerSecond; |
+ ts_abs.tv_nsec = |
+ (wakeup_time_us - ts_abs.tv_sec * kMicrosecondsPerSecond) * |
+ kNanosecondsPerMicrosecond; |
+ } |
+ |
+ while (!(IsReady(nfds, readfds, &Socket::is_read_ready, false) || |
+ IsReady(nfds, writefds, &Socket::is_write_ready, false) || |
+ IsReady(nfds, exceptfds, &Socket::is_exception, false))) { |
+ SimpleAutoLock lock(select_mutex().get()); |
+ if (timeout) { |
+ if (!timeout->tv_sec && !timeout->tv_usec) |
+ break; |
+ |
+ if (select_cond().timedwait(select_mutex(), &ts_abs)) { |
+ if (errno == ETIMEDOUT) |
+ break; |
+ else |
+ return -1; |
+ } |
+ } else { |
+ select_cond().wait(select_mutex()); |
+ } |
+ } |
+ |
+ int nread = IsReady(nfds, readfds, &Socket::is_read_ready, true); |
+ int nwrite = IsReady(nfds, writefds, &Socket::is_write_ready, true); |
+ int nexcpt = IsReady(nfds, exceptfds, &Socket::is_exception, true); |
+ if (nread < 0 || nwrite < 0 || nexcpt < 0) { |
+ errno = EBADF; |
+ return -1; |
+ } |
+ return nread + nwrite + nexcpt; |
} |
int KernelProxy::pselect(int nfds, fd_set *readfds, fd_set *writefds, |
@@ -789,62 +1025,14 @@ |
} |
int KernelProxy::shutdown(int sockfd, int how) { |
- errno = ENOSYS; |
- fprintf(stderr, "shutdown has not been implemented!\n"); |
- return -1; |
+ if (GetFileHandle(sockfd) == 0) |
+ return EBADF; |
+ return socket_subsystem_->shutdown(GetFileHandle(sockfd)->stream, how); |
} |
-int KernelProxy::epoll_create(int size) { |
- errno = ENOSYS; |
- fprintf(stderr, "epoll_create has not been implemented!\n"); |
- return -1; |
-} |
- |
-int KernelProxy::epoll_create1(int flags) { |
- errno = ENOSYS; |
- fprintf(stderr, "epoll_create1 has not been implemented!\n"); |
- return -1; |
-} |
- |
-int KernelProxy::epoll_ctl(int epfd, int op, int fd, |
- struct epoll_event *event) { |
- errno = ENOSYS; |
- fprintf(stderr, "epoll_ctl has not been implemented!\n"); |
- return -1; |
-} |
- |
-int KernelProxy::epoll_wait(int epfd, struct epoll_event *events, int maxevents, |
- int timeout) { |
- errno = ENOSYS; |
- fprintf(stderr, "epoll_wait has not been implemented!\n"); |
- return -1; |
-} |
- |
-int KernelProxy::epoll_pwait(int epfd, struct epoll_event *events, |
- int maxevents, int timeout, const sigset_t *sigmask, size_t sigset_size) { |
- errno = ENOSYS; |
- fprintf(stderr, "epoll_pwait has not been implemented!\n"); |
- return -1; |
-} |
- |
int KernelProxy::socketpair(int domain, int type, int protocol, int sv[2]) { |
errno = ENOSYS; |
fprintf(stderr, "socketpair has not been implemented!\n"); |
return -1; |
} |
-int KernelProxy::poll(struct pollfd *fds, nfds_t nfds, int timeout) { |
- errno = ENOSYS; |
- fprintf(stderr, "poll has not been implemented!\n"); |
- return -1; |
-} |
- |
-int KernelProxy::ppoll(struct pollfd *fds, nfds_t nfds, |
- const struct timespec *timeout, |
- const sigset_t *sigmask, size_t sigset_size) { |
- errno = ENOSYS; |
- fprintf(stderr, "ppoll has not been implemented!\n"); |
- return -1; |
-} |
-#endif |
- |