// Display images inside a terminal
// Copyright (C) 2023 JustKidding
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#include "util/socket.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include "os.hpp"
#include "util.hpp"
namespace fs = std::filesystem;
UnixSocket::UnixSocket(const std::string_view endpoint)
: fd(socket(AF_UNIX, SOCK_STREAM, 0))
{
if (fd == -1) {
throw std::system_error(errno, std::generic_category());
}
connect_to_endpoint(endpoint);
}
UnixSocket::UnixSocket()
: fd(socket(AF_UNIX, SOCK_STREAM, 0))
{
if (fd == -1) {
throw std::system_error(errno, std::generic_category());
}
const int bufsize = 8192;
buffer.reserve(bufsize);
}
void UnixSocket::connect_to_endpoint(const std::string_view endpoint)
{
if (!fs::exists(endpoint)) {
connected = false;
return;
}
struct sockaddr_un sock;
std::memset(&sock, 0, sizeof(sockaddr_un));
sock.sun_family = AF_UNIX;
endpoint.copy(sock.sun_path, endpoint.size());
int res = connect(fd, reinterpret_cast(&sock), sizeof(struct sockaddr_un));
if (res == -1) {
throw std::system_error(errno, std::generic_category());
}
}
void UnixSocket::bind_to_endpoint(const std::string_view endpoint) const
{
struct sockaddr_un sock;
std::memset(&sock, 0, sizeof(sockaddr_un));
sock.sun_family = AF_UNIX;
endpoint.copy(sock.sun_path, endpoint.size());
int res = bind(fd, reinterpret_cast(&sock), sizeof(struct sockaddr_un));
if (res == -1) {
throw std::system_error(errno, std::generic_category());
}
res = listen(fd, SOMAXCONN);
if (res == -1) {
throw std::system_error(errno, std::generic_category());
}
}
auto UnixSocket::wait_for_connections(int waitms) const -> int
{
const auto in_event = os::wait_for_data_on_fd(fd, waitms);
if (in_event) {
return accept(fd, nullptr, nullptr);
}
return -1;
}
auto UnixSocket::read_data_from_connection(int filde) -> std::vector
{
// a single connection could send multiples commands at once
// each command should end with a '\n' character
const int read_buffer_size = 4096;
std::array read_buffer;
while (true) {
const auto status = recv(filde, read_buffer.data(), read_buffer_size, 0);
if (status <= 0) {
break;
}
buffer.append(read_buffer.data(), status);
}
auto cmds = util::str_split(buffer, "\n");
buffer.clear();
close(filde);
return cmds;
}
void UnixSocket::write(const void *data, std::size_t len) const
{
if (!connected) {
return;
}
const auto *runner = static_cast(data);
while (len != 0) {
const auto status = send(fd, runner, len, MSG_NOSIGNAL);
if (status == -1) {
throw std::system_error(errno, std::generic_category());
}
len -= status;
runner += status;
}
}
void UnixSocket::read(void *data, std::size_t len) const
{
if (!connected) {
return;
}
auto *runner = static_cast(data);
while (len != 0) {
const auto status = recv(fd, runner, len, 0);
if (status == 0) {
return; // no data
}
if (status == -1) {
throw std::system_error(errno, std::generic_category());
}
len -= status;
runner += status;
}
}
auto UnixSocket::read_until_empty() const -> std::string
{
std::string result;
const int read_buffer_size = 4096;
std::array read_buffer;
result.reserve(read_buffer_size);
while (true) {
const auto status = recv(fd, read_buffer.data(), read_buffer_size, 0);
if (status <= 0) {
break;
}
result.append(read_buffer.data(), status);
}
return result;
}
UnixSocket::~UnixSocket()
{
close(fd);
}