1//! @file main.hpp
2//! @author ryftchen
3//! @brief The declarations (main) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#pragma once
8
9#ifndef _PRECOMPILED_HEADER
10#include <cxxabi.h>
11#include <dlfcn.h>
12#include <execinfo.h>
13#include <array>
14#include <csignal>
15#include <filesystem>
16#else
17#include "application/pch/precompiled_header.hpp"
18#endif
19
20//! @brief The application module.
21namespace application
22{
23//! @brief Status of the signal.
24static volatile std::sig_atomic_t signalStatus = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
25
26//! @brief Get the executable name.
27//! @return executable name
28[[gnu::always_inline]] inline static std::string executableName()
29{
30 return std::filesystem::canonical(p: std::filesystem::path{"/proc/self/exe"}).filename().string();
31}
32
33// NOLINTBEGIN(cppcoreguidelines-no-malloc, cppcoreguidelines-owning-memory)
34//! @brief Signal handler for SIGSEGV signal, etc.
35//! @param sig - signal type
36static void signalHandler(const int sig)
37{
38 signalStatus = sig;
39 constexpr std::uint16_t maxFrame = 128;
40 std::array<void*, maxFrame> callStack{};
41 const int numOfFrame = ::backtrace(array: callStack.data(), size: callStack.size());
42 char** const symbols = ::backtrace_symbols(array: callStack.data(), size: numOfFrame);
43
44 std::array<char, 1024> buffer{};
45 std::ostringstream originalTrace{};
46 std::ostringstream detailedTrace{};
47 originalTrace << "\033[7m\033[49m<" << ((numOfFrame < maxFrame) ? "" : "TRUNCATED ") << "BACKTRACE>\033[0m\n";
48 detailedTrace << "\033[7m\033[49m<VERBOSE>\033[0m\n";
49 for (int i = 1; i < numOfFrame; ++i)
50 {
51 originalTrace << symbols[i] << '\n';
52 ::Dl_info info{};
53 if ((::dladdr(address: callStack[i], info: &info) != 0) && info.dli_sname)
54 {
55 int status = -1;
56 char* const demangle =
57 (info.dli_sname[0] == '_') ? ::abi::__cxa_demangle(mangled_name: info.dli_sname, output_buffer: nullptr, length: nullptr, status: &status) : nullptr;
58 std::snprintf(
59 s: buffer.data(),
60 maxlen: buffer.size(),
61 format: "%-3d %*p %.960s + %zd\n",
62 i,
63 static_cast<int>(2 + (sizeof(void*) * 2)),
64 callStack[i],
65 (status == 0) ? demangle : (info.dli_sname ? info.dli_sname : symbols[i]),
66 static_cast<char*>(callStack[i]) - static_cast<char*>(info.dli_saddr));
67 std::free(ptr: demangle);
68 }
69 else
70 {
71 std::snprintf(
72 s: buffer.data(),
73 maxlen: buffer.size(),
74 format: "%-3d %*p %.960s\n",
75 i,
76 static_cast<int>(2 + (sizeof(void*) * 2)),
77 callStack[i],
78 symbols[i]);
79 }
80 detailedTrace << buffer.data();
81 }
82 std::free(ptr: static_cast<void*>(symbols));
83
84 std::fprintf(
85 stream: ::stderr,
86 format: "%s: Crash occurred.\n\n\033[7m\033[49m<SIGNAL>\033[0m\n%d\n\n%s\n%s",
87 executableName().c_str(),
88 sig,
89 originalTrace.str().c_str(),
90 detailedTrace.str().c_str());
91
92 std::signal(sig: sig, SIG_DFL);
93 std::raise(sig: sig);
94}
95// NOLINTEND(cppcoreguidelines-no-malloc, cppcoreguidelines-owning-memory)
96
97// NOLINTBEGIN(concurrency-mt-unsafe)
98//! @brief The constructor function before starting the main function. Switch to the target path.
99[[using gnu: constructor, noinline]] static void onInitial()
100{
101 std::signal(SIGABRT, handler: signalHandler);
102 std::signal(SIGSEGV, handler: signalHandler);
103 ::setenv(name: "TERM", value: "linux", replace: 1);
104 ::setenv(name: "TERMINFO", value: "/etc/terminfo", replace: 1);
105
106 const char* const homeEnv = std::getenv(name: "HOME");
107 const std::string_view defaultHome = homeEnv ? homeEnv : "/root";
108 const auto homePath = std::filesystem::absolute(p: defaultHome);
109 if (!std::filesystem::is_directory(p: homePath))
110 {
111 std::fprintf(stream: ::stdout, format: "%s: Could not find the home directory.\n", executableName().c_str());
112 std::exit(EXIT_FAILURE);
113 }
114
115 const auto processPath = homePath / ".foo";
116 if (!std::filesystem::is_directory(p: processPath))
117 {
118 std::filesystem::create_directory(p: processPath);
119 std::filesystem::permissions(
120 p: processPath, prms: std::filesystem::perms::owner_all, opts: std::filesystem::perm_options::add);
121 }
122 ::setenv(name: "FOO_HOME", value: processPath.string().c_str(), replace: 1);
123}
124// NOLINTEND(concurrency-mt-unsafe)
125
126//! @brief The destructor function before finishing the main function. Check the signal status.
127[[using gnu: destructor, noinline]] static void onFinal()
128{
129 if (signalStatus != 0)
130 {
131 std::fprintf(stream: ::stdout, format: "%s: Signal %d was the last signal received.\n", executableName().c_str(), signalStatus);
132 }
133}
134} // namespace application
135