| 1 | extern "C" { |
| 2 | |
| 3 | #include "daemon.h" |
| 4 | #include "libnetdata/libnetdata.h" |
| 5 | #include "daemon/daemon-shutdown.h" |
| 6 | |
| 7 | int netdata_main(int argc, char *argv[]); |
| 8 | void nd_process_signals(void); |
| 9 | |
| 10 | } |
| 11 | |
| 12 | __attribute__((format(printf, 1, 2))) |
| 13 | static void netdata_service_log(const char *fmt, ...) |
| 14 | { |
| 15 | char path[FILENAME_MAX + 1]; |
| 16 | snprintfz(path, FILENAME_MAX, "%s/service.log", LOG_DIR); |
| 17 | |
| 18 | FILE *fp = fopen(path, "a"); |
| 19 | if (fp == NULL) { |
| 20 | return; |
| 21 | } |
| 22 | |
| 23 | SYSTEMTIME time; |
| 24 | GetSystemTime(&time); |
| 25 | fprintf(fp, "%d:%d:%d - ", time.wHour, time.wMinute, time.wSecond); |
| 26 | |
| 27 | va_list args; |
| 28 | va_start(args, fmt); |
| 29 | vfprintf(fp, fmt, args); |
| 30 | va_end(args); |
| 31 | |
| 32 | fprintf(fp, "\n"); |
| 33 | |
| 34 | fflush(fp); |
| 35 | fclose(fp); |
| 36 | } |
| 37 | |
| 38 | static SERVICE_STATUS_HANDLE svc_status_handle = nullptr; |
| 39 | static SERVICE_STATUS svc_status = {}; |
| 40 | |
| 41 | static HANDLE svc_stop_event_handle = nullptr; |
| 42 | |
| 43 | static ND_THREAD *cleanup_thread = nullptr; |
| 44 | |
| 45 | static bool ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint, DWORD dwControlsAccepted) |
| 46 | { |
| 47 | static DWORD dwCheckPoint = 1; |
| 48 | svc_status.dwCurrentState = dwCurrentState; |
| 49 | svc_status.dwWin32ExitCode = dwWin32ExitCode; |
| 50 | svc_status.dwWaitHint = dwWaitHint; |
| 51 | svc_status.dwControlsAccepted = dwControlsAccepted; |
| 52 | |
| 53 | if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED) |
| 54 | { |
| 55 | svc_status.dwCheckPoint = 0; |
| 56 | } |
| 57 | else |
| 58 | { |
| 59 | svc_status.dwCheckPoint = dwCheckPoint++; |
| 60 | } |
| 61 | |
| 62 | if (!SetServiceStatus(svc_status_handle, &svc_status)) { |
| 63 | netdata_service_log("@ReportSvcStatus: SetServiceStatusFailed (%d)", GetLastError()); |
| 64 | return false; |
| 65 | } |
| 66 | |
| 67 | return true; |
| 68 | } |
| 69 | |
| 70 | static HANDLE CreateEventHandle(const char *msg) |
| 71 | { |
| 72 | HANDLE h = CreateEvent(NULL, TRUE, FALSE, NULL); |
| 73 | |
| 74 | if (!h) |
| 75 | { |
| 76 | netdata_service_log("%s", msg); |
| 77 | |
| 78 | if (!ReportSvcStatus(SERVICE_STOPPED, GetLastError(), 1000, 0)) |
| 79 | { |
| 80 | netdata_service_log("Failed to set service status to stopped."); |
| 81 | } |
| 82 | |
| 83 | return NULL; |
| 84 | } |
| 85 | |
| 86 | return h; |
| 87 | } |
| 88 | |
| 89 | static void call_netdata_cleanup(void *arg) |
| 90 | { |
| 91 | DWORD controlCode = *((DWORD *)arg); |
| 92 | |
| 93 | // Wait until we have to stop the service |
| 94 | netdata_service_log("Cleanup thread waiting for stop event..."); |
| 95 | WaitForSingleObject(svc_stop_event_handle, INFINITE); |
| 96 | |
| 97 | // Stop the agent |
| 98 | netdata_service_log("Running netdata cleanup..."); |
| 99 | EXIT_REASON reason; |
| 100 | switch(controlCode) { |
| 101 | case SERVICE_CONTROL_SHUTDOWN: |
| 102 | reason = (EXIT_REASON)(EXIT_REASON_SERVICE_STOP|EXIT_REASON_SYSTEM_SHUTDOWN); |
| 103 | break; |
| 104 | |
| 105 | case SERVICE_CONTROL_STOP: |
| 106 | // fall-through |
| 107 | |
| 108 | default: |
| 109 | reason = EXIT_REASON_SERVICE_STOP; |
| 110 | break; |
| 111 | } |
| 112 | netdata_exit_gracefully(reason, false); |
| 113 | |
| 114 | // Close event handle |
| 115 | netdata_service_log("Closing stop event handle..."); |
| 116 | CloseHandle(svc_stop_event_handle); |
| 117 | |
| 118 | // Set status to stopped |
| 119 | netdata_service_log("Reporting the service as stopped..."); |
| 120 | ReportSvcStatus(SERVICE_STOPPED, 0, 0, 0); |
| 121 | } |
| 122 | |
| 123 | static void WINAPI ServiceControlHandler(DWORD controlCode) |
| 124 | { |
| 125 | switch (controlCode) |
| 126 | { |
| 127 | case SERVICE_CONTROL_SHUTDOWN: |
| 128 | case SERVICE_CONTROL_STOP: |
| 129 | { |
| 130 | if (svc_status.dwCurrentState != SERVICE_RUNNING) |
| 131 | return; |
| 132 | |
| 133 | // Set service status to stop-pending |
| 134 | netdata_service_log("Setting service status to stop-pending..."); |
| 135 | if (!ReportSvcStatus(SERVICE_STOP_PENDING, 0, 5000, 0)) |
| 136 | return; |
| 137 | |
| 138 | // Create cleanup thread |
| 139 | netdata_service_log("Creating cleanup thread..."); |
| 140 | char tag[NETDATA_THREAD_TAG_MAX + 1]; |
| 141 | snprintfz(tag, NETDATA_THREAD_TAG_MAX, "%s", "CLEANUP"); |
| 142 | cleanup_thread = nd_thread_create(tag, NETDATA_THREAD_OPTION_DEFAULT, call_netdata_cleanup, &controlCode); |
| 143 | |
| 144 | // Signal the stop request |
| 145 | netdata_service_log("Signalling the cleanup thread..."); |
| 146 | SetEvent(svc_stop_event_handle); |
| 147 | break; |
| 148 | } |
| 149 | case SERVICE_CONTROL_INTERROGATE: |
| 150 | { |
| 151 | ReportSvcStatus(svc_status.dwCurrentState, svc_status.dwWin32ExitCode, svc_status.dwWaitHint, svc_status.dwControlsAccepted); |
| 152 | break; |
| 153 | } |
| 154 | default: |
| 155 | break; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | void WINAPI ServiceMain(DWORD argc, LPSTR* argv) |
| 160 | { |
| 161 | UNUSED(argc); |
| 162 | UNUSED(argv); |
| 163 | |
| 164 | // Create service status handle |
| 165 | netdata_service_log("Creating service status handle..."); |
| 166 | svc_status_handle = RegisterServiceCtrlHandler("Netdata", ServiceControlHandler); |
| 167 | if (!svc_status_handle) |
| 168 | { |
| 169 | netdata_service_log("@ServiceMain() - RegisterServiceCtrlHandler() failed..."); |
| 170 | return; |
| 171 | } |
| 172 | |
| 173 | // Set status to start-pending |
| 174 | netdata_service_log("Setting service status to start-pending..."); |
| 175 | svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
| 176 | svc_status.dwServiceSpecificExitCode = 0; |
| 177 | svc_status.dwCheckPoint = 0; |
| 178 | if (!ReportSvcStatus(SERVICE_START_PENDING, 0, 5000, 0)) |
| 179 | { |
| 180 | netdata_service_log("Failed to set service status to start pending."); |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | // Create stop service event handle |
| 185 | netdata_service_log("Creating stop service event handle..."); |
| 186 | svc_stop_event_handle = CreateEventHandle("Failed to create stop event handle"); |
| 187 | if (!svc_stop_event_handle) |
| 188 | return; |
| 189 | |
| 190 | // Set status to running |
| 191 | netdata_service_log("Setting service status to running..."); |
| 192 | if (!ReportSvcStatus(SERVICE_RUNNING, 0, 5000, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN)) |
| 193 | { |
| 194 | netdata_service_log("Failed to set service status to running."); |
| 195 | return; |
| 196 | } |
| 197 | |
| 198 | // Run the agent |
| 199 | netdata_service_log("Running the agent..."); |
| 200 | netdata_main(argc, argv); |
| 201 | |
| 202 | netdata_service_log("Agent has been started..."); |
| 203 | } |
| 204 | |
| 205 | static bool update_path() { |
| 206 | const char *old_path = getenv("PATH"); |
| 207 | |
| 208 | if (!old_path) { |
| 209 | if (setenv("PATH", "/usr/bin", 1) != 0) { |
| 210 | netdata_service_log("Failed to set PATH to /usr/bin"); |
| 211 | return false; |
| 212 | } |
| 213 | |
| 214 | return true; |
| 215 | } |
| 216 | |
| 217 | size_t new_path_length = strlen(old_path) + strlen("/usr/bin") + 2; |
| 218 | char *new_path = (char *) callocz(new_path_length, sizeof(char)); |
| 219 | snprintfz(new_path, new_path_length, "/usr/bin:%s", old_path); |
| 220 | |
| 221 | if (setenv("PATH", new_path, 1) != 0) { |
| 222 | netdata_service_log("Failed to add /usr/bin to PATH"); |
| 223 | freez(new_path); |
| 224 | return false; |
| 225 | } |
| 226 | |
| 227 | freez(new_path); |
| 228 | return true; |
| 229 | } |
| 230 | |
| 231 | int main(int argc, char *argv[]) |
| 232 | { |
| 233 | #if defined(OS_WINDOWS) && defined(RUN_UNDER_CLION) |
| 234 | bool tty = true; |
| 235 | #else |
| 236 | bool tty = isatty(fileno(stdin)) == 1; |
| 237 | #endif |
| 238 | |
| 239 | if (!update_path()) { |
| 240 | return 1; |
| 241 | } |
| 242 | |
| 243 | if (tty) |
| 244 | { |
| 245 | int rc = netdata_main(argc, argv); |
| 246 | if (rc != 10) |
| 247 | return rc; |
| 248 | |
| 249 | nd_process_signals(); |
| 250 | return 1; |
| 251 | } |
| 252 | else |
| 253 | { |
| 254 | SERVICE_TABLE_ENTRY serviceTable[] = { |
| 255 | { strdupz("Netdata"), ServiceMain }, |
| 256 | { nullptr, nullptr } |
| 257 | }; |
| 258 | |
| 259 | if (!StartServiceCtrlDispatcher(serviceTable)) |
| 260 | { |
| 261 | netdata_service_log("@main() - StartServiceCtrlDispatcher() failed..."); |
| 262 | return 1; |
| 263 | } |
| 264 | |
| 265 | return 0; |
| 266 | } |
| 267 | } |