libos: Add a new LocalServer class for local domain sockets
This commit is contained in:
parent
15192837c0
commit
b6fe96e364
@ -14,6 +14,7 @@ set(SOURCES
|
||||
src/Mode.cpp
|
||||
src/Prompt.cpp
|
||||
src/Security.cpp
|
||||
src/LocalServer.cpp
|
||||
)
|
||||
|
||||
add_library(os ${SOURCES})
|
||||
|
132
libos/include/os/LocalServer.h
Normal file
132
libos/include/os/LocalServer.h
Normal file
@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
#include <luna/Result.h>
|
||||
#include <luna/SharedPtr.h>
|
||||
#include <luna/StringView.h>
|
||||
|
||||
namespace os
|
||||
{
|
||||
/**
|
||||
* @brief A local domain server, used to communicate between processes on the same machine.
|
||||
*/
|
||||
class LocalServer : public Shareable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Create a new server object and bind it to a local address.
|
||||
*
|
||||
* @param path The path to use for the server socket.
|
||||
* @param blocking Whether the server should block if no connections are available when calling accept().
|
||||
* @return Result<SharedPtr<LocalServer>> An error, or a new server object.
|
||||
*/
|
||||
static Result<SharedPtr<LocalServer>> create(StringView path, bool blocking);
|
||||
|
||||
/**
|
||||
* @brief Activate the server and start listening for connections.
|
||||
*
|
||||
* @param backlog The number of unaccepted connections to keep.
|
||||
* @return Result<void> Whether the operation succeded.
|
||||
*/
|
||||
Result<void> listen(int backlog);
|
||||
|
||||
/**
|
||||
* @brief Return the underlying socket file descriptor used by this object.
|
||||
*
|
||||
* @return int The file descriptor.
|
||||
*/
|
||||
int fd() const
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief An interface to communicate with clients connected to a local server.
|
||||
*/
|
||||
class Client : public Shareable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Read arbitrary data from the client. The call will block if there is no data and the parent server
|
||||
* object has not been created as non-blocking.
|
||||
*
|
||||
* @param buf The buffer to read data into.
|
||||
* @param length The maximum amount of bytes to read.
|
||||
* @return Result<usize> An error, or the number of bytes read.
|
||||
*/
|
||||
Result<usize> recv(u8* buf, usize length);
|
||||
|
||||
/**
|
||||
* @brief Read an object from the client. The call will block if there is no data and the parent server
|
||||
* object has not been created as non-blocking.
|
||||
*
|
||||
* @tparam T The type of the object.
|
||||
* @param out A reference to the object to read data into.
|
||||
* @return Result<void> Whether the operation succeded.
|
||||
*/
|
||||
template <typename T> Result<void> recv_typed(T& out)
|
||||
{
|
||||
TRY(recv((u8*)&out, sizeof(T)));
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send arbitrary data to the client.
|
||||
*
|
||||
* @param buf The buffer to send data from.
|
||||
* @param length The amount of bytes to send.
|
||||
* @return Result<usize> An error, or the number of bytes actually sent.
|
||||
*/
|
||||
Result<usize> send(const u8* buf, usize length);
|
||||
|
||||
/**
|
||||
* @brief Send an object to the client.
|
||||
*
|
||||
* @tparam T The type of the object.
|
||||
* @param out A reference to the object to send data from.
|
||||
* @return Result<void> Whether the operation succeded.
|
||||
*/
|
||||
template <typename T> Result<void> send_typed(const T& out)
|
||||
{
|
||||
TRY(send((const u8*)&out, sizeof(T)));
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the attached client.
|
||||
*
|
||||
* This will make any further reads on the client return ECONNRESET, and will make this object invalid.
|
||||
*/
|
||||
void disconnect();
|
||||
|
||||
/**
|
||||
* @brief Return the underlying socket file descriptor used by this object.
|
||||
*
|
||||
* @return int The file descriptor.
|
||||
*/
|
||||
int fd() const
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
Client(int fd);
|
||||
~Client();
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Accept a new incoming connection and return a handle to it. If there are no incoming connections,
|
||||
* accept() either blocks until there is one (if the object was created with blocking=true), or returns EAGAIN
|
||||
* (if the object was created with blocking=false).
|
||||
*
|
||||
* @return Result<SharedPtr<Client>> An error, or a handle to the new connection.
|
||||
*/
|
||||
Result<SharedPtr<Client>> accept();
|
||||
|
||||
~LocalServer();
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
bool m_blocking;
|
||||
};
|
||||
}
|
87
libos/src/LocalServer.cpp
Normal file
87
libos/src/LocalServer.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/LocalServer.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace os
|
||||
{
|
||||
Result<SharedPtr<LocalServer>> LocalServer::create(StringView path, bool blocking)
|
||||
{
|
||||
auto server = TRY(make_shared<LocalServer>());
|
||||
|
||||
(void)os::FileSystem::remove(path); // We explicitly ignore any error here, either it doesn't exist (which is
|
||||
// fine), or it cannot be removed, which will make bind() fail later.
|
||||
|
||||
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) return err(errno);
|
||||
|
||||
struct sockaddr_un un;
|
||||
un.sun_family = AF_UNIX;
|
||||
strncpy(un.sun_path, path.chars(), sizeof(un.sun_path));
|
||||
|
||||
if (bind(sockfd, (struct sockaddr*)&un, sizeof(un)) < 0)
|
||||
{
|
||||
close(sockfd);
|
||||
return err(errno);
|
||||
}
|
||||
|
||||
if (!blocking) { fcntl(sockfd, F_SETFL, O_NONBLOCK); }
|
||||
server->m_blocking = blocking;
|
||||
|
||||
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
server->m_fd = sockfd;
|
||||
return server;
|
||||
}
|
||||
|
||||
Result<void> LocalServer::listen(int backlog)
|
||||
{
|
||||
if (::listen(m_fd, backlog) < 0) return err(errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<SharedPtr<LocalServer::Client>> LocalServer::accept()
|
||||
{
|
||||
int fd = ::accept(m_fd, nullptr, nullptr);
|
||||
if (fd < 0) return err(errno);
|
||||
if (!m_blocking) fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
return make_shared<Client>(fd);
|
||||
}
|
||||
|
||||
LocalServer::~LocalServer()
|
||||
{
|
||||
close(m_fd);
|
||||
}
|
||||
|
||||
LocalServer::Client::Client(int fd) : m_fd(fd)
|
||||
{
|
||||
}
|
||||
|
||||
LocalServer::Client::~Client()
|
||||
{
|
||||
if (m_fd >= 0) close(m_fd);
|
||||
}
|
||||
|
||||
Result<usize> LocalServer::Client::recv(u8* buf, usize length)
|
||||
{
|
||||
ssize_t nread = read(m_fd, buf, length);
|
||||
if (nread < 0) return err(errno);
|
||||
return nread;
|
||||
}
|
||||
|
||||
Result<usize> LocalServer::Client::send(const u8* buf, usize length)
|
||||
{
|
||||
ssize_t nwrite = write(m_fd, buf, length);
|
||||
if (nwrite < 0) return err(errno);
|
||||
return nwrite;
|
||||
}
|
||||
|
||||
void LocalServer::Client::disconnect()
|
||||
{
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user