1//! @file main.cpp
2//! @author ryftchen
3//! @brief The definitions (main) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#include "main.hpp"
8#include "command.hpp"
9#include "configure.hpp"
10
11#ifndef _PRECOMPILED_HEADER
12#include <sys/prctl.h>
13#include <sys/wait.h>
14#include <iostream>
15#else
16#include "application/pch/precompiled_header.hpp"
17#endif
18
19namespace application
20{
21//! @brief Anonymous namespace.
22inline namespace
23{
24// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
25//! @brief Interrupt flag for the SIGALRM signal.
26volatile std::sig_atomic_t alarmInterrupted = 0;
27//! @brief Interrupt flag for the SIGCHLD signal.
28volatile std::sig_atomic_t childInterrupted = 0;
29// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
30} // namespace
31
32//! @brief The run function.
33//! @param argc - argument count
34//! @param argv - argument vector
35//! @return 0 if successful, otherwise 1
36static int run(const int argc, const char* const argv[])
37try
38{
39 auto running = std::async(
40 policy: std::launch::async,
41 fn: [=]()
42 {
43 ::pthread_setname_np(target_thread: ::pthread_self(), name: command::title.c_str());
44 return configure::loadSettings() && command::executeCLI(argc, argv);
45 });
46 return running.get() ? EXIT_SUCCESS : EXIT_FAILURE;
47}
48catch (const std::exception& err)
49{
50 std::cerr << executableName() << ": " << err.what() << std::endl;
51 return EXIT_FAILURE;
52}
53
54//! @brief The watchdog function.
55//! @param pid - pid by fork
56//! @return 0 if successful, otherwise 1
57static int watchdog(const ::pid_t pid)
58{
59 std::signal(SIGALRM, handler: [](const int sig) { application::alarmInterrupted = sig; });
60 std::signal(SIGCHLD, handler: [](const int sig) { application::childInterrupted = sig; });
61
62 constexpr std::uint8_t timeout = 60;
63 ::alarm(seconds: timeout);
64 ::pause();
65 if (application::alarmInterrupted != 0)
66 {
67 if (::waitpid(pid: pid, stat_loc: nullptr, WNOHANG) == 0)
68 {
69 ::kill(pid: pid, SIGKILL);
70 std::cerr << application::executableName() << ": Kill the child process (" << pid << ") due to timeout."
71 << std::endl;
72 }
73 return EXIT_FAILURE;
74 }
75 if (application::childInterrupted != 0)
76 {
77 int status = 0;
78 ::wait(stat_loc: &status);
79 if (WIFEXITED(status) && (WEXITSTATUS(status) != EXIT_SUCCESS))
80 {
81 return EXIT_FAILURE;
82 }
83 }
84 return EXIT_SUCCESS;
85}
86} // namespace application
87
88//! @brief The main function.
89//! @param argc - argument count
90//! @param argv - argument vector
91//! @return the argument to the implicit call to exit()
92int main(int argc, char* argv[])
93{
94 if (argc == 1)
95 {
96 return application::run(argc, argv);
97 }
98
99 const ::pid_t ppidBeforeFork = ::getpid();
100 const ::pid_t pid = ::fork();
101 if (pid < 0)
102 {
103 return EXIT_FAILURE;
104 }
105 if (pid == 0)
106 {
107 ::prctl(PR_SET_PDEATHSIG, SIGTERM);
108 return (::getppid() == ppidBeforeFork) ? application::run(argc, argv) : EXIT_FAILURE;
109 }
110 return application::watchdog(pid);
111}
112