Add Unix domain sockets for local IPC #37
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "arch/CPU.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "thread/Thread.h"
|
||||
#include <bits/socket.h>
|
||||
|
||||
class Socket : public VFS::FileInode
|
||||
@ -47,7 +49,11 @@ class Socket : public VFS::FileInode
|
||||
virtual Result<usize> recv(u8*, usize, int) const = 0;
|
||||
|
||||
virtual Result<void> bind(SharedPtr<Socket>, struct sockaddr*, socklen_t) = 0;
|
||||
virtual Result<void> connect(struct sockaddr*, socklen_t) = 0;
|
||||
virtual Result<void> connect(Registers*, int, struct sockaddr*, socklen_t) = 0;
|
||||
|
||||
virtual Result<SharedPtr<OpenFileDescription>> accept(Registers*, int, struct sockaddr**, socklen_t*) = 0;
|
||||
|
||||
virtual Result<void> listen(int backlog) = 0;
|
||||
|
||||
Result<void> truncate(usize) override
|
||||
{
|
||||
@ -105,7 +111,7 @@ class Socket : public VFS::FileInode
|
||||
virtual ~Socket() = default;
|
||||
|
||||
protected:
|
||||
VFS::FileSystem* m_fs;
|
||||
VFS::FileSystem* m_fs { nullptr };
|
||||
usize m_inode_number { 0 };
|
||||
mode_t m_mode;
|
||||
u32 m_uid { 0 };
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "net/UnixSocket.h"
|
||||
#include <bits/open-flags.h>
|
||||
#include <luna/PathParser.h>
|
||||
#include <thread/Scheduler.h>
|
||||
|
||||
@ -45,8 +46,8 @@ Result<usize> UnixSocket::send(const u8* buf, usize length, int)
|
||||
|
||||
Result<usize> UnixSocket::recv(u8* buf, usize length, int) const
|
||||
{
|
||||
if (m_state == State::Reset) return err(ECONNRESET);
|
||||
if (m_state != State::Connected) return err(ENOTCONN);
|
||||
if (m_state == State::Reset && !m_data.size()) return err(ECONNRESET);
|
||||
if (m_state != State::Connected && m_state != State::Reset) return err(ENOTCONN);
|
||||
|
||||
return m_data.dequeue_data(buf, length);
|
||||
}
|
||||
@ -104,7 +105,100 @@ Result<void> UnixSocket::bind(SharedPtr<Socket> socket, struct sockaddr* addr, s
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> UnixSocket::connect(struct sockaddr*, socklen_t)
|
||||
Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* addr, socklen_t addrlen)
|
||||
{
|
||||
return err(ENOSYS);
|
||||
if (!addr) return err(EINVAL);
|
||||
if (addr->sa_family != AF_UNIX) return err(EAFNOSUPPORT);
|
||||
if ((usize)addrlen > sizeof(sockaddr_un)) return err(EINVAL);
|
||||
|
||||
if (m_state == State::Connected) return err(EISCONN);
|
||||
if (m_state == State::Connecting) return err(EALREADY);
|
||||
if (m_state != State::Inactive) return err(EINVAL);
|
||||
|
||||
struct sockaddr_un* un_address = (struct sockaddr_un*)addr;
|
||||
|
||||
String path = TRY(String::from_string_view(
|
||||
StringView::from_fixed_size_cstring(un_address->sun_path, addrlen - sizeof(sa_family_t))));
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
|
||||
if (inode->type() != VFS::InodeType::Socket)
|
||||
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
|
||||
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
|
||||
|
||||
auto socket = (SharedPtr<UnixSocket>)inode;
|
||||
if (socket->m_state != State::Listening) return err(ECONNREFUSED);
|
||||
if (!socket->m_listen_queue.try_push(this)) return err(ECONNREFUSED);
|
||||
if (socket->m_blocked_thread) socket->m_blocked_thread->wake_up();
|
||||
|
||||
m_state = Connecting;
|
||||
if (flags & O_NONBLOCK) return err(EINPROGRESS);
|
||||
|
||||
while (1)
|
||||
{
|
||||
m_blocked_thread = current;
|
||||
kernel_wait_for_event();
|
||||
m_blocked_thread = nullptr;
|
||||
if (current->interrupted)
|
||||
{
|
||||
if (current->will_invoke_signal_handler()) return err(EINTR);
|
||||
current->process_pending_signals(regs);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
check(m_state == Connected);
|
||||
check(m_peer);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> UnixSocket::listen(int backlog)
|
||||
{
|
||||
if (backlog < 0) backlog = 0;
|
||||
if (m_state == State::Listening || m_state == State::Connected) return err(EINVAL);
|
||||
if (m_state != State::Bound) return err(EDESTADDRREQ);
|
||||
TRY(m_listen_queue.set_size(backlog));
|
||||
m_state = State::Listening;
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<SharedPtr<OpenFileDescription>> UnixSocket::accept(Registers* regs, int flags, struct sockaddr** addr,
|
||||
socklen_t* addrlen)
|
||||
{
|
||||
if (m_state != State::Listening) return err(EINVAL);
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
UnixSocket* peer = nullptr;
|
||||
while (!m_listen_queue.try_pop(peer))
|
||||
{
|
||||
if (flags & O_NONBLOCK) return err(EAGAIN);
|
||||
m_blocked_thread = current;
|
||||
kernel_wait_for_event();
|
||||
m_blocked_thread = nullptr;
|
||||
if (current->interrupted)
|
||||
{
|
||||
if (current->will_invoke_signal_handler()) return err(EINTR);
|
||||
current->process_pending_signals(regs);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
check(peer);
|
||||
|
||||
auto socket = TRY(make_shared<UnixSocket>(peer));
|
||||
auto description = TRY(make_shared<OpenFileDescription>(socket, O_RDWR));
|
||||
|
||||
peer->m_peer = socket.ptr();
|
||||
peer->m_state = State::Connected;
|
||||
|
||||
if (peer->m_blocked_thread) peer->m_blocked_thread->wake_up();
|
||||
|
||||
*addr = (struct sockaddr*)&peer->m_addr;
|
||||
*addrlen = peer->m_addrlen;
|
||||
|
||||
return description;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "net/Socket.h"
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/Buffer.h>
|
||||
#include <luna/CircularQueue.h>
|
||||
#include <luna/String.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
@ -12,14 +14,17 @@ class UnixSocket : public Socket
|
||||
|
||||
bool blocking() const override
|
||||
{
|
||||
return !m_data.size();
|
||||
return (m_state == Connected || m_state == Reset) && !m_data.size();
|
||||
}
|
||||
|
||||
Result<usize> send(const u8*, usize, int) override;
|
||||
Result<usize> recv(u8*, usize, int) const override;
|
||||
|
||||
Result<void> bind(SharedPtr<Socket>, struct sockaddr*, socklen_t) override;
|
||||
Result<void> connect(struct sockaddr*, socklen_t) override;
|
||||
Result<void> connect(Registers*, int, struct sockaddr*, socklen_t) override;
|
||||
Result<SharedPtr<OpenFileDescription>> accept(Registers*, int, struct sockaddr**, socklen_t*) override;
|
||||
|
||||
Result<void> listen(int backlog) override;
|
||||
|
||||
void did_close() override;
|
||||
|
||||
@ -39,10 +44,14 @@ class UnixSocket : public Socket
|
||||
};
|
||||
|
||||
State m_state = State::Inactive;
|
||||
UnixSocket* m_peer;
|
||||
UnixSocket* m_peer = nullptr;
|
||||
|
||||
mutable Buffer m_data;
|
||||
|
||||
Thread* m_blocked_thread { nullptr };
|
||||
|
||||
DynamicCircularQueue<UnixSocket*> m_listen_queue;
|
||||
|
||||
struct sockaddr_un m_addr = { .sun_family = AF_UNIX, .sun_path = {} };
|
||||
socklen_t m_addrlen = sizeof(sa_family_t);
|
||||
};
|
||||
|
@ -47,3 +47,83 @@ Result<u64> sys_bind(Registers*, SyscallArgs args)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result<u64> sys_connect(Registers* regs, SyscallArgs args)
|
||||
{
|
||||
int sockfd = (int)args[0];
|
||||
struct sockaddr* addr = (struct sockaddr*)args[1];
|
||||
socklen_t addrlen = (socklen_t)args[2];
|
||||
|
||||
struct sockaddr_storage storage;
|
||||
if ((usize)addrlen > sizeof(storage)) return err(EINVAL);
|
||||
if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT);
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto description = TRY(current->resolve_fd(sockfd))->description;
|
||||
|
||||
if (description->inode->type() != VFS::InodeType::Socket) return err(ENOTSOCK);
|
||||
|
||||
auto socket = (SharedPtr<Socket>)description->inode;
|
||||
|
||||
TRY(socket->connect(regs, description->flags, (struct sockaddr*)&storage, addrlen));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result<u64> sys_listen(Registers*, SyscallArgs args)
|
||||
{
|
||||
int sockfd = (int)args[0];
|
||||
int backlog = (int)args[1];
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto inode = TRY(current->resolve_fd(sockfd))->inode();
|
||||
|
||||
if (inode->type() != VFS::InodeType::Socket) return err(ENOTSOCK);
|
||||
|
||||
auto socket = (SharedPtr<Socket>)inode;
|
||||
|
||||
TRY(socket->listen(backlog));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result<u64> sys_accept(Registers* regs, SyscallArgs args)
|
||||
{
|
||||
int sockfd = (int)args[0];
|
||||
struct sockaddr* addr = (struct sockaddr*)args[1];
|
||||
socklen_t* addrlen = (socklen_t*)args[2];
|
||||
|
||||
if (addr && !addrlen) return err(EINVAL);
|
||||
|
||||
socklen_t len;
|
||||
if (addr)
|
||||
{
|
||||
if (!MemoryManager::copy_from_user_typed(addrlen, &len)) return err(EFAULT);
|
||||
}
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
|
||||
auto description = TRY(current->resolve_fd(sockfd))->description;
|
||||
|
||||
if (description->inode->type() != VFS::InodeType::Socket) return err(ENOTSOCK);
|
||||
|
||||
auto socket = (SharedPtr<Socket>)description->inode;
|
||||
|
||||
struct sockaddr* client;
|
||||
socklen_t client_len;
|
||||
auto new_description = TRY(socket->accept(regs, description->flags, &client, &client_len));
|
||||
|
||||
int fd = TRY(current->allocate_fd(0));
|
||||
current->fd_table[fd] = FileDescriptor { new_description, 0 };
|
||||
|
||||
if (client_len < len) len = client_len;
|
||||
if (addr)
|
||||
{
|
||||
MemoryManager::copy_to_user(addr, client, len);
|
||||
MemoryManager::copy_to_user_typed(addrlen, &client_len);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
_e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \
|
||||
_e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \
|
||||
_e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \
|
||||
_e(getpgid) _e(socket) _e(bind)
|
||||
_e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept)
|
||||
|
||||
enum Syscalls
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user