blob: 6c502afa901e3cc36d2f9a5bc251237a5947c453 [file]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "message_file.hpp"
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#include <cerrno>
#include <format>
#include <stdexcept>
namespace google
{
namespace hoth
{
MessageFile::MessageFile(const std::string& path, const internal::Sys* sys) :
sys(sys)
{
fd = sys->open(path.c_str(), O_RDWR);
if (fd < 0)
{
throw internal::errnoException(
std::format("Error opening Hoth file interface \"{}\"", path));
}
}
MessageFile::~MessageFile()
{
sys->close(fd);
}
void MessageFile::send(const uint8_t* buf, size_t size, size_t seek)
{
if (sys->lseek(fd, static_cast<off_t>(seek), SEEK_SET) < 0)
{
throw internal::errnoException("Error seeking on Hoth interface");
}
ssize_t ret = -1;
do
{
ret = sys->write(fd, buf, size);
// Retry if interrupted
} while (ret < 0 && errno == EINTR);
if (ret < 0)
{
throw internal::errnoException("Error writing to Hoth interface");
}
if (static_cast<size_t>(ret) != size)
{
// Fail on incomplete writes because they can confuse Hoth
throw std::runtime_error(std::format(
"Tried to send message of size {} but could only send {} bytes",
size, ret));
}
}
void MessageFile::recv(uint8_t* buf, size_t size, size_t seek)
{
if (sys->lseek(fd, static_cast<off_t>(seek), SEEK_SET) < 0)
{
throw internal::errnoException("Error seeking on Hoth interface");
}
while (size > 0)
{
auto ret = sys->read(fd, buf, size);
if (ret < 0)
{
if (errno == EINTR)
{
continue; // interrupted; try again
}
throw internal::errnoException("Error reading from Hoth interface");
}
if (ret == 0)
{
// EOF hit
throw std::runtime_error("Hit EOF while expecting data");
} // ret > 0
buf += ret;
size -= ret;
};
}
void MessageFile::claim_transport()
{
int ret = -1;
do
{
// Try to take exclusive lock on file in a non-blocking manner
ret = flock(fd, LOCK_EX | LOCK_NB);
} while ((ret < 0) && (errno == EINTR));
if (ret < 0)
{
throw std::runtime_error(std::format(
"Error while trying to take lock on file transport: {}/{}", errno,
strerror(errno)));
}
}
void MessageFile::release_transport()
{
int ret = -1;
do
{
// Try to release lock on file
ret = flock(fd, LOCK_UN);
} while ((ret < 0) && (errno == EINTR));
if (ret < 0)
{
throw std::runtime_error(std::format(
"Error while trying to release lock on file transport: {}/{}",
errno, strerror(errno)));
}
}
} // namespace hoth
} // namespace google