+ if (si->hStdInput) CloseHandle(si->hStdInput);\r
+ if (si->hStdOutput) CloseHandle(si->hStdOutput);\r
+ if (si->hStdError) CloseHandle(si->hStdError);\r
+}\r
+\r
+void cleanup_loggers(nssm_service_t *service) {\r
+ unsigned long interval = NSSM_CLEANUP_LOGGERS_DEADLINE;\r
+ HANDLE thread_handle = INVALID_HANDLE_VALUE;\r
+\r
+ close_handle(&service->stdout_thread, &thread_handle);\r
+ /* Close write end of the data pipe so logging thread can finalise read. */\r
+ close_handle(&service->stdout_si);\r
+ /* Await logging thread then close read end. */\r
+ if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);\r
+ close_handle(&service->stdout_pipe);\r
+\r
+ thread_handle = INVALID_HANDLE_VALUE;\r
+ close_handle(&service->stderr_thread, &thread_handle);\r
+ close_handle(&service->stderr_si);\r
+ if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);\r
+ close_handle(&service->stderr_pipe);\r
+}\r
+\r
+/*\r
+ Try multiple times to read from a file.\r
+ Returns: 0 on success.\r
+ 1 on non-fatal error.\r
+ -1 on fatal error.\r
+*/\r
+static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {\r
+ int ret = 1;\r
+ unsigned long error;\r
+ for (int tries = 0; tries < 5; tries++) {\r
+ if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;\r
+\r
+ error = GetLastError();\r
+ switch (error) {\r
+ /* Other end closed the pipe. */\r
+ case ERROR_BROKEN_PIPE:\r
+ ret = -1;\r
+ goto complain_read;\r
+\r
+ /* Couldn't lock the buffer. */\r
+ case ERROR_NOT_ENOUGH_QUOTA:\r
+ Sleep(2000 + tries * 3000);\r
+ ret = 1;\r
+ continue;\r
+\r
+ /* Write was cancelled by the other end. */\r
+ case ERROR_OPERATION_ABORTED:\r
+ ret = 1;\r
+ goto complain_read;\r
+\r
+ default:\r
+ ret = -1;\r
+ }\r
+ }\r
+\r
+complain_read:\r
+ /* Ignore the error if we've been requested to exit anyway. */\r
+ if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;\r
+ if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);\r
+ *complained |= COMPLAINED_READ;\r
+ return ret;\r
+}\r
+\r
+/*\r
+ Try multiple times to write to a file.\r
+ Returns: 0 on success.\r
+ 1 on non-fatal error.\r
+ -1 on fatal error.\r
+*/\r
+static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {\r
+ int ret = 1;\r
+ unsigned long error;\r
+ for (int tries = 0; tries < 5; tries++) {\r
+ if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;\r
+\r
+ error = GetLastError();\r
+ if (error == ERROR_IO_PENDING) {\r
+ /* Operation was successful pending flush to disk. */\r
+ return 0;\r
+ }\r
+\r
+ switch (error) {\r
+ /* Other end closed the pipe. */\r
+ case ERROR_BROKEN_PIPE:\r
+ ret = -1;\r
+ goto complain_write;\r
+\r
+ /* Couldn't lock the buffer. */\r
+ case ERROR_NOT_ENOUGH_QUOTA:\r
+ /* Out of disk space. */\r
+ case ERROR_DISK_FULL:\r
+ Sleep(2000 + tries * 3000);\r
+ ret = 1;\r
+ continue;\r
+\r
+ default:\r
+ /* We'll lose this line but try to read and write subsequent ones. */\r
+ ret = 1;\r
+ }\r
+ }\r
+\r
+complain_write:\r
+ if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);\r
+ *complained |= COMPLAINED_WRITE;\r
+ return ret;\r
+}\r
+\r
+/* Note that the timestamp is created in UTF-8. */\r
+static inline int write_timestamp(logger_t *logger, unsigned long charsize, unsigned long *out, int *complained) {\r
+ char timestamp[TIMESTAMP_LEN + 1];\r
+\r
+ SYSTEMTIME now;\r
+ GetSystemTime(&now);\r
+ _snprintf_s(timestamp, _countof(timestamp), _TRUNCATE, TIMESTAMP_FORMAT, now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond, now.wMilliseconds);\r
+\r
+ if (charsize == sizeof(char)) return try_write(logger, (void *) timestamp, TIMESTAMP_LEN, out, complained);\r
+\r
+ wchar_t *utf16;\r
+ unsigned long utf16len;\r
+ if (to_utf16(timestamp, &utf16, &utf16len)) return -1;\r
+ int ret = try_write(logger, (void *) *utf16, utf16len * sizeof(wchar_t), out, complained);\r
+ HeapFree(GetProcessHeap(), 0, utf16);\r
+ return ret;\r
+}\r
+\r
+static int write_with_timestamp(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained, unsigned long charsize) {\r
+ if (logger->timestamp_log) {\r
+ unsigned long log_out;\r
+ int log_complained;\r
+ unsigned long timestamp_out = 0;\r
+ int timestamp_complained;\r
+ if (! logger->line_length) {\r
+ write_timestamp(logger, charsize, ×tamp_out, ×tamp_complained);\r
+ logger->line_length += (__int64) timestamp_out;\r
+ *out += timestamp_out;\r
+ *complained |= timestamp_complained;\r
+ }\r
+\r
+ unsigned long i;\r
+ void *line = address;\r
+ unsigned long offset = 0;\r
+ int ret;\r
+ for (i = 0; i < bufsize; i++) {\r
+ if (((char *) address)[i] == '\n') {\r
+ ret = try_write(logger, line, i - offset + 1, &log_out, &log_complained);\r
+ line = (void *) ((char *) line + i - offset + 1);\r
+ logger->line_length = 0LL;\r
+ *out += log_out;\r
+ *complained |= log_complained;\r
+ offset = i + 1;\r
+ if (offset < bufsize) {\r
+ write_timestamp(logger, charsize, ×tamp_out, ×tamp_complained);\r
+ logger->line_length += (__int64) timestamp_out;\r
+ *out += timestamp_out;\r
+ *complained |= timestamp_complained;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (offset < bufsize) {\r
+ ret = try_write(logger, line, bufsize - offset, &log_out, &log_complained);\r
+ *out += log_out;\r
+ *complained |= log_complained;\r
+ }\r
+\r
+ return ret;\r
+ }\r
+ else return try_write(logger, address, bufsize, out, complained);\r