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-2025 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 // _PRECOMPILED_HEADER
18
19namespace application
20{
21//! @brief Anonymous namespace.
22inline namespace
23{
24//! @brief Interrupt flag for the SIGALRM signal.
25volatile std::sig_atomic_t alarmInterrupted = 0;
26//! @brief Interrupt flag for the SIGCHLD signal.
27volatile std::sig_atomic_t childInterrupted = 0;
28} // namespace
29
30//! @brief The run function.
31//! @param argc - argument count
32//! @param argv - argument vector
33//! @return 0 if successful, otherwise 1
34static int run(const int argc, const char* const argv[])
35try
36{
37 auto running = std::async(
38 policy: std::launch::async,
39 fn: [=]()
40 {
41 ::pthread_setname_np(target_thread: ::pthread_self(), name: command::title.c_str());
42 return configure::loadSettings() && command::executeCLI(argc, argv);
43 });
44 return running.get() ? EXIT_SUCCESS : EXIT_FAILURE;
45}
46catch (const std::exception& err)
47{
48 std::cerr << executableName() << ": " << err.what() << std::endl;
49 return EXIT_FAILURE;
50}
51
52//! @brief The watchdog function.
53//! @param pid - pid by fork
54//! @return 0 if successful, otherwise 1
55static int watchdog(const ::pid_t pid)
56{
57 std::signal(SIGALRM, handler: [](const int sig) { application::alarmInterrupted = sig; });
58 std::signal(SIGCHLD, handler: [](const int sig) { application::childInterrupted = sig; });
59
60 constexpr std::uint8_t timeout = 60;
61 ::alarm(seconds: timeout);
62 ::pause();
63 if (application::alarmInterrupted != 0)
64 {
65 if (::waitpid(pid: pid, stat_loc: nullptr, WNOHANG) == 0)
66 {
67 ::kill(pid: pid, SIGKILL);
68 std::cerr << application::executableName() << ": Kill the child process (" << pid << ") due to timeout."
69 << std::endl;
70 }
71 return EXIT_FAILURE;
72 }
73 if (application::childInterrupted != 0)
74 {
75 int status = 0;
76 ::wait(stat_loc: &status);
77 if (WIFEXITED(status) && (WEXITSTATUS(status) != EXIT_SUCCESS))
78 {
79 return EXIT_FAILURE;
80 }
81 }
82 return EXIT_SUCCESS;
83}
84} // namespace application
85
86//! @brief The main function.
87//! @param argc - argument count
88//! @param argv - argument vector
89//! @return the argument to the implicit call to exit()
90int main(int argc, char* argv[])
91{
92 if (argc == 1)
93 {
94 return application::run(argc, argv);
95 }
96
97 const ::pid_t ppidBeforeFork = ::getpid();
98 const ::pid_t pid = ::fork();
99 if (pid < 0)
100 {
101 return EXIT_FAILURE;
102 }
103 if (pid == 0)
104 {
105 ::prctl(PR_SET_PDEATHSIG, SIGTERM);
106 return (::getppid() == ppidBeforeFork) ? application::run(argc, argv) : EXIT_FAILURE;
107 }
108 return application::watchdog(pid);
109}
110