| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "../libnetdata.h" |
| 4 | |
| 5 | static volatile sig_atomic_t exit_initiated = EXIT_REASON_NONE; |
| 6 | |
| 7 | ENUM_STR_MAP_DEFINE(EXIT_REASON) = { |
| 8 | { EXIT_REASON_SIGBUS, "signal-bus-error"}, |
| 9 | { EXIT_REASON_SIGSEGV, "signal-segmentation-fault"}, |
| 10 | { EXIT_REASON_SIGFPE, "signal-floating-point-exception"}, |
| 11 | { EXIT_REASON_SIGILL, "signal-illegal-instruction"}, |
| 12 | { EXIT_REASON_SIGABRT, "signal-abort"}, |
| 13 | { EXIT_REASON_SIGSYS, "signal-bad-system-call"}, |
| 14 | { EXIT_REASON_SIGXCPU, "signal-cpu-time-limit-exceeded"}, |
| 15 | { EXIT_REASON_SIGXFSZ, "signal-file-size-limit-exceeded"}, |
| 16 | |
| 17 | { EXIT_REASON_OUT_OF_MEMORY, "out-of-memory"}, |
| 18 | { EXIT_REASON_ALREADY_RUNNING, "already-running"}, |
| 19 | |
| 20 | { EXIT_REASON_FATAL, "fatal"}, |
| 21 | |
| 22 | { EXIT_REASON_API_QUIT, "api-quit"}, |
| 23 | { EXIT_REASON_CMD_EXIT, "cmd-exit"}, |
| 24 | |
| 25 | { EXIT_REASON_SIGQUIT, "signal-quit"}, |
| 26 | { EXIT_REASON_SIGTERM, "signal-terminate"}, |
| 27 | { EXIT_REASON_SIGINT, "signal-interrupt"}, |
| 28 | |
| 29 | { EXIT_REASON_SERVICE_STOP, "service-stop"}, |
| 30 | |
| 31 | { EXIT_REASON_SYSTEM_SHUTDOWN, "system-shutdown"}, |
| 32 | |
| 33 | { EXIT_REASON_UPDATE, "update"}, |
| 34 | { EXIT_REASON_SHUTDOWN_TIMEOUT, "shutdown-timeout"}, |
| 35 | |
| 36 | // terminator |
| 37 | {0, NULL}, |
| 38 | }; |
| 39 | |
| 40 | BITMAP_STR_DEFINE_FUNCTIONS(EXIT_REASON, EXIT_REASON_NONE, "none"); |
| 41 | |
| 42 | #if defined(OS_LINUX) |
| 43 | static bool is_system_shutdown_sysv(void) { |
| 44 | const char *shutdown_files[] = { |
| 45 | "/etc/nologin", // Created during shutdown |
| 46 | "/etc/halt", // SysV shutdown indicator |
| 47 | "/run/nologin", // Modern systems shutdown indicator |
| 48 | NULL |
| 49 | }; |
| 50 | |
| 51 | for (const char **file = shutdown_files; *file != NULL; file++) { |
| 52 | if (access(*file, F_OK) == 0) |
| 53 | return true; |
| 54 | } |
| 55 | |
| 56 | return false; |
| 57 | } |
| 58 | |
| 59 | static bool is_system_shutdown(void) { |
| 60 | return is_system_shutdown_sysv(); |
| 61 | } |
| 62 | #endif |
| 63 | |
| 64 | #if defined(OS_FREEBSD) |
| 65 | #include <sys/sysctl.h> |
| 66 | static bool is_system_shutdown(void) { |
| 67 | int state = 0; |
| 68 | size_t state_len = sizeof(state); |
| 69 | |
| 70 | if (sysctlbyname("kern.shutdown", &state, &state_len, NULL, 0) == 0) |
| 71 | return state != 0; |
| 72 | |
| 73 | return false; |
| 74 | } |
| 75 | #endif |
| 76 | |
| 77 | #if defined(OS_MACOS) |
| 78 | #include <sys/sysctl.h> |
| 79 | static bool is_system_shutdown(void) { |
| 80 | char buf[1024]; |
| 81 | size_t len = sizeof(buf); |
| 82 | |
| 83 | if (sysctlbyname("kern.shutdownstate", buf, &len, NULL, 0) == 0) |
| 84 | return true; |
| 85 | |
| 86 | if (access("/var/db/.SystemShutdown", F_OK) == 0) |
| 87 | return true; |
| 88 | |
| 89 | return false; |
| 90 | } |
| 91 | #endif |
| 92 | |
| 93 | #if defined(OS_WINDOWS) |
| 94 | #include <windows.h> |
| 95 | static bool is_system_shutdown(void) { |
| 96 | return GetSystemMetrics(SM_SHUTTINGDOWN) != 0; |
| 97 | } |
| 98 | #endif |
| 99 | |
| 100 | static const char *self_path = NULL; |
| 101 | static OS_FILE_METADATA self = { 0 }; |
| 102 | |
| 103 | void exit_initiated_init(void) { |
| 104 | exit_initiated = EXIT_REASON_NONE; |
| 105 | |
| 106 | freez((char *)self_path); |
| 107 | self_path = os_get_process_path(); |
| 108 | if(self_path) |
| 109 | self = os_get_file_metadata(self_path); |
| 110 | } |
| 111 | |
| 112 | ALWAYS_INLINE |
| 113 | EXIT_REASON exit_initiated_get(void) { |
| 114 | return (EXIT_REASON)exit_initiated; |
| 115 | } |
| 116 | |
| 117 | void exit_initiated_add(EXIT_REASON reason) { |
| 118 | exit_initiated |= (sig_atomic_t)reason; |
| 119 | } |
| 120 | |
| 121 | void exit_initiated_set(EXIT_REASON reason) { |
| 122 | EXIT_REASON old = exit_initiated_get(); |
| 123 | |
| 124 | if(old == EXIT_REASON_NONE && !(reason & EXIT_REASON_SYSTEM_SHUTDOWN) && is_system_shutdown()) |
| 125 | reason |= EXIT_REASON_SYSTEM_SHUTDOWN; |
| 126 | |
| 127 | if(old == EXIT_REASON_NONE && self_path && OS_FILE_METADATA_OK(self)) { |
| 128 | OS_FILE_METADATA self_now = os_get_file_metadata(self_path); |
| 129 | if(OS_FILE_METADATA_OK(self_now) && (self_now.modified_time != self.modified_time || self_now.size_bytes != self.size_bytes)) |
| 130 | reason |= EXIT_REASON_UPDATE; |
| 131 | } |
| 132 | |
| 133 | // we combine all of them together |
| 134 | // so that if this is called multiple times, |
| 135 | // we will have all of them |
| 136 | exit_initiated_add(reason); |
| 137 | } |