#include "contrib/ankerl/svector.h"
#include "fmt/core.h"
#include "libutil/cxx/file_util.hxx"
+#include "libutil/cxx/error.hxx"
#include "hs.h"
#include "logger.h"
#include "worker_util.h"
};
static auto
-hs_shared_from_unserialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, std::string>
+hs_shared_from_unserialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, error>
{
auto ptr = map.get_map();
- return tl::expected<hs_shared_database, std::string>{tl::in_place, std::move(map), (hs_database_t *)ptr};
+ return tl::expected<hs_shared_database, error>{tl::in_place, std::move(map), (hs_database_t *)ptr};
}
static auto
-hs_shared_from_serialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, std::string>
+hs_shared_from_serialized(raii_mmaped_file &&map) -> tl::expected<hs_shared_database, error>
{
hs_database_t *target = nullptr;
if (auto ret = hs_deserialize_database((const char *)map.get_map(), map.get_size(), &target); ret != HS_SUCCESS) {
- return tl::make_unexpected("cannot deserialize database");
+ return tl::make_unexpected(error {"cannot deserialize database", ret});
}
- return tl::expected<hs_shared_database, std::string>{tl::in_place, target};
+ return tl::expected<hs_shared_database, error>{tl::in_place, target};
}
-auto load_cached_hs_file(const char *fname) -> tl::expected<hs_shared_database, std::string>
+auto load_cached_hs_file(const char *fname) -> tl::expected<hs_shared_database, error>
{
auto &hs_cache = hs_known_files_cache::get();
return raii_mmaped_file::mmap_shared(fname, O_RDONLY, PROT_READ)
- .and_then([&]<class T>(T &&cached_serialized) -> tl::expected<hs_shared_database, std::string> {
+ .and_then([&]<class T>(T &&cached_serialized) -> tl::expected<hs_shared_database, error> {
#if defined(HS_MAJOR) && defined(HS_MINOR) && HS_MAJOR >= 5 && HS_MINOR >= 4
auto unserialized_fname = fmt::format("{}.unser", fname);
auto unserialized_file = raii_locked_file::create(unserialized_fname.c_str(), O_CREAT | O_RDWR | O_EXCL,
00644)
- .and_then([&](auto &&new_file_locked) -> tl::expected<raii_file, std::string> {
+ .and_then([&](auto &&new_file_locked) -> tl::expected<raii_file, error> {
auto tmpfile_pattern = fmt::format("{}{}hsmp-XXXXXXXXXXXXXXXXXX",
cached_serialized.get_file().get_dir(), G_DIR_SEPARATOR);
auto tmpfile = raii_locked_file::mkstemp(tmpfile_pattern.data(), O_CREAT | O_RDWR | O_EXCL,
void *buf;
posix_memalign(&buf, 16, unserialized_size);
if (buf == NULL) {
- return tl::make_unexpected("Cannot allocate memory");
+ return tl::make_unexpected(error {"Cannot allocate memory", errno, error_category::CRITICAL });
}
// Store owned string
if (auto ret = hs_deserialize_database_at((const char *)cached_serialized.get_map(),
cached_serialized.get_size(), (hs_database_t *) buf); ret != HS_SUCCESS) {
- return tl::make_unexpected(
- fmt::format("cannot deserialize hyperscan database: {}", ret));
+ return tl::make_unexpected(error {
+ fmt::format("cannot deserialize hyperscan database: {}", ret), ret });
}
else {
if (write(tmpfile_checked.get_fd(), buf, unserialized_size) == -1) {
free(buf);
- return tl::make_unexpected(fmt::format("cannot write to {}: {}",
- tmpfile_name, ::strerror(errno)));
+ return tl::make_unexpected(error { fmt::format("cannot write to {}: {}",
+ tmpfile_name, ::strerror(errno)), errno, error_category::CRITICAL });
}
else {
free(buf);
return raii_file::open(unserialized_fname.c_str(), O_RDONLY);
};
})
- .or_else([&](auto unused) -> tl::expected<raii_file, std::string> {
+ .or_else([&](auto unused) -> tl::expected<raii_file, error> {
// Cannot create file, so try to open it in RO mode
return raii_file::open(unserialized_fname.c_str(), O_RDONLY);
});
O_RDONLY, PROT_READ);
if (!cached_map.has_value()) {
- msg_info_cache("%s", cached_map.error().c_str());
+ if (cached_map.error().category == util::error_category::CRITICAL) {
+ msg_err_cache("%s", cached_map.error().error_message.data());
+ }
+ else {
+ msg_info_cache("%s", cached_map.error().error_message.data());
+ }
return false;
}
return false;
}
- msg_err_cache("%s", file_sink.error().c_str());
+ msg_err_cache("%s", file_sink.error().error_message.data());
return false;
}
--- /dev/null
+/*-
+ * Copyright 2022 Vsevolod Stakhov
+ *
+ * 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.
+ */
+
+#ifndef RSPAMD_ERROR_HXX
+#define RSPAMD_ERROR_HXX
+#pragma once
+
+#include "config.h"
+#include <string>
+#include <string_view>
+#include <cstdint>
+#include <optional>
+
+/***
+ * This unit is used to represent Rspamd C++ errors in a way to interoperate
+ * with C code if needed and avoid allocations for static strings
+ */
+namespace rspamd::util {
+
+enum class error_category : std::uint8_t {
+ INFORMAL,
+ IMPORTANT,
+ CRITICAL
+};
+
+struct error {
+public:
+ /**
+ * Construct from a static string, this string must live long enough to outlive this object
+ * @param msg
+ * @param code
+ * @param category
+ */
+ error(const char *msg, int code, error_category category = error_category::INFORMAL) :
+ error_message(msg), error_code(code), category(category) {}
+ /**
+ * Construct error from a temporary string taking membership
+ * @param msg
+ * @param code
+ * @param category
+ */
+ error(std::string &&msg, int code, error_category category = error_category::INFORMAL)
+ : error_code(code), category(category) {
+ static_storage = std::move(msg);
+ error_message = static_storage.value();
+ }
+ /**
+ * Construct error from another string copying it into own storage
+ * @param msg
+ * @param code
+ * @param category
+ */
+ error(const std::string &msg, int code, error_category category = error_category::INFORMAL)
+ : error_code(code), category(category) {
+ static_storage = msg;
+ error_message = static_storage.value();
+ }
+
+ /**
+ * Convert into GError
+ * @return
+ */
+ auto into_g_error() const -> GError * {
+ return g_error_new(g_quark_from_static_string("rspamd"), error_code, "%s",
+ error_message.data());
+ }
+
+ /**
+ * Convenience alias for the `into_g_error`
+ * @param err
+ */
+ auto into_g_error_set(GError **err) const -> void {
+ if (err && *err == nullptr) {
+ *err = into_g_error();
+ }
+ }
+public:
+ std::string_view error_message;
+ int error_code;
+ error_category category;
+private:
+ std::optional<std::string> static_storage;
+};
+
+} // namespace rspamd::util
+
+#endif //RSPAMD_ERROR_HXX
namespace rspamd::util {
-auto raii_file::open(const char *fname, int flags) -> tl::expected<raii_file, std::string>
+auto raii_file::open(const char *fname, int flags) -> tl::expected<raii_file, error>
{
int oflags = flags;
#ifdef O_CLOEXEC
#endif
if (fname == nullptr) {
- return tl::make_unexpected("cannot open file; filename is nullptr");
+ return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
}
auto fd = ::open(fname, oflags);
if (fd == -1) {
- return tl::make_unexpected(fmt::format("cannot open file {}: {}", fname, ::strerror(errno)));
+ return tl::make_unexpected(error{fmt::format("cannot open file {}: {}", fname, ::strerror(errno)), errno});
}
auto ret = raii_file{fname, fd, false};
if (fstat(ret.fd, &ret.st) == -1) {
- return tl::make_unexpected(fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)));
+ return tl::make_unexpected(error {fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)), errno});
}
return ret;
}
-auto raii_file::create(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>
+auto raii_file::create(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>
{
int oflags = flags;
#ifdef O_CLOEXEC
#endif
if (fname == nullptr) {
- return tl::make_unexpected("cannot open file; filename is nullptr");
+ return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
}
auto fd = ::open(fname, oflags, perms);
if (fd == -1) {
- return tl::make_unexpected(fmt::format("cannot create file {}: {}", fname, ::strerror(errno)));
+ return tl::make_unexpected(error{fmt::format("cannot create file {}: {}", fname, ::strerror(errno)), errno});
}
auto ret = raii_file{fname, fd, false};
if (fstat(ret.fd, &ret.st) == -1) {
- return tl::make_unexpected(fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)));
+ return tl::make_unexpected(error{fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)), errno});
}
return ret;
}
-auto raii_file::create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>
+auto raii_file::create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>
{
int oflags = flags;
#ifdef O_CLOEXEC
oflags |= O_CLOEXEC | O_CREAT | O_EXCL;
#endif
if (fname == nullptr) {
- return tl::make_unexpected("cannot open file; filename is nullptr");
+ return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
}
auto fd = ::open(fname, oflags, perms);
if (fd == -1) {
- return tl::make_unexpected(fmt::format("cannot create file {}: {}", fname, ::strerror(errno)));
+ return tl::make_unexpected(error {fmt::format("cannot create file {}: {}", fname, ::strerror(errno)), errno});
}
auto ret = raii_file{fname, fd, true};
if (fstat(ret.fd, &ret.st) == -1) {
- return tl::make_unexpected(fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)));
+ return tl::make_unexpected(error {fmt::format("cannot stat file {}: {}", fname, ::strerror(errno)), errno});
}
return ret;
}
-auto raii_file::mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, std::string>
+auto raii_file::mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, error>
{
int oflags = flags;
#ifdef O_CLOEXEC
oflags |= O_CLOEXEC | O_CREAT | O_EXCL;
#endif
if (pattern == nullptr) {
- return tl::make_unexpected("cannot open file; pattern is nullptr");
+ return tl::make_unexpected(error {"cannot open file; pattern is nullptr", EINVAL, error_category::CRITICAL});
+
}
std::string mutable_pattern = pattern;
auto fd = g_mkstemp_full(mutable_pattern.data(), oflags, perms);
if (fd == -1) {
- return tl::make_unexpected(fmt::format("cannot create file {}: {}", pattern, ::strerror(errno)));
+ return tl::make_unexpected(error {fmt::format("cannot create file {}: {}", pattern, ::strerror(errno)), errno});
}
auto ret = raii_file{mutable_pattern.c_str(), fd, true};
if (fstat(ret.fd, &ret.st) == -1) {
- return tl::make_unexpected(fmt::format("cannot stat file {}: {}",
- mutable_pattern.c_str(), ::strerror(errno)));
+ return tl::make_unexpected(error { fmt::format("cannot stat file {}: {}",
+ mutable_pattern, ::strerror(errno)), errno} );
}
return ret;
}
}
-auto raii_locked_file::lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, std::string>
+auto raii_locked_file::lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, error>
{
if (!rspamd_file_lock(unlocked.get_fd(), TRUE)) {
- return tl::make_unexpected(fmt::format("cannot lock file {}: {}", unlocked.get_name(), ::strerror(errno)));
+ return tl::make_unexpected(
+ error { fmt::format("cannot lock file {}: {}", unlocked.get_name(), ::strerror(errno)), errno});
}
return raii_locked_file{std::move(unlocked)};
}
auto raii_mmaped_file::mmap_shared(raii_file &&file,
- int flags) -> tl::expected<raii_mmaped_file, std::string>
+ int flags) -> tl::expected<raii_mmaped_file, error>
{
void *map;
map = mmap(NULL, file.get_stat().st_size, flags, MAP_SHARED, file.get_fd(), 0);
if (map == MAP_FAILED) {
- return tl::make_unexpected(fmt::format("cannot mmap file at fd: {}: {}",
- file.get_fd(), ::strerror(errno)));
+ return tl::make_unexpected(error { fmt::format("cannot mmap file at fd: {}: {}",
+ file.get_fd(), ::strerror(errno)), errno });
}
}
auto raii_mmaped_file::mmap_shared(const char *fname, int open_flags,
- int mmap_flags) -> tl::expected<raii_mmaped_file, std::string>
+ int mmap_flags) -> tl::expected<raii_mmaped_file, error>
{
auto file = raii_file::open(fname, open_flags);
}
auto raii_file_sink::create(const char *fname, int flags, int perms,
- const char *suffix) -> tl::expected<raii_file_sink, std::string>
+ const char *suffix) -> tl::expected<raii_file_sink, error>
{
if (!fname || !suffix) {
- return tl::make_unexpected("cannot create file sink: bad input arguments");
+ return tl::make_unexpected(error {"cannot open file; filename is nullptr", EINVAL, error_category::CRITICAL});
}
auto tmp_fname = fmt::format("{}.{}", fname, suffix);
#include "config.h"
#include "contrib/expected/expected.hpp"
+#include "libutil/cxx/error.hxx"
#include <string>
#include <sys/stat.h>
public:
virtual ~raii_file() noexcept;
- static auto open(const char *fname, int flags) -> tl::expected<raii_file, std::string>;
- static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>;
- static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, std::string>;
- static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, std::string>;
+ static auto open(const char *fname, int flags) -> tl::expected<raii_file, error>;
+ static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
+ static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_file, error>;
+ static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_file, error>;
auto get_fd() const -> int {
return fd;
public:
~raii_locked_file() noexcept override;
- static auto open(const char *fname, int flags) -> tl::expected<raii_locked_file, std::string> {
+ static auto open(const char *fname, int flags) -> tl::expected<raii_locked_file, error> {
auto locked = raii_file::open(fname, flags).and_then([]<class T>(T &&file) {
return lock_raii_file(std::forward<T>(file));
});
return locked;
}
- static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, std::string> {
+ static auto create(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error> {
auto locked = raii_file::create(fname, flags, perms).and_then([]<class T>(T &&file) {
return lock_raii_file(std::forward<T>(file));
});
return locked;
}
- static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, std::string> {
+ static auto create_temp(const char *fname, int flags, int perms) -> tl::expected<raii_locked_file, error> {
auto locked = raii_file::create_temp(fname, flags, perms).and_then([]<class T>(T &&file) {
return lock_raii_file(std::forward<T>(file));
});
return locked;
}
- static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_locked_file, std::string> {
+ static auto mkstemp(const char *pattern, int flags, int perms) -> tl::expected<raii_locked_file, error> {
auto locked = raii_file::mkstemp(pattern, flags, perms).and_then([]<class T>(T &&file) {
return lock_raii_file(std::forward<T>(file));
});
raii_locked_file() = delete;
raii_locked_file(const raii_locked_file &other) = delete;
private:
- static auto lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, std::string>;
+ static auto lock_raii_file(raii_file &&unlocked) -> tl::expected<raii_locked_file, error>;
raii_locked_file(raii_file &&other) noexcept : raii_file(std::move(other)) {}
explicit raii_locked_file(const char *fname, int fd, bool temp) : raii_file(fname, fd, temp) {}
};
*/
struct raii_mmaped_file final {
~raii_mmaped_file();
- static auto mmap_shared(raii_file &&file, int flags) -> tl::expected<raii_mmaped_file, std::string>;
- static auto mmap_shared(const char *fname, int open_flags, int mmap_flags) -> tl::expected<raii_mmaped_file, std::string>;
+ static auto mmap_shared(raii_file &&file, int flags) -> tl::expected<raii_mmaped_file, error>;
+ static auto mmap_shared(const char *fname, int open_flags, int mmap_flags) -> tl::expected<raii_mmaped_file, error>;
// Returns a constant pointer to the underlying map
auto get_map() const -> void* {return map;}
auto get_file() const -> const raii_file& { return file; }
*/
struct raii_file_sink final {
static auto create(const char *fname, int flags, int perms, const char *suffix = "new")
- -> tl::expected<raii_file_sink, std::string>;
+ -> tl::expected<raii_file_sink, error>;
auto write_output() -> bool;
~raii_file_sink();
auto get_fd() const -> int