+\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
+/* Wrapper to be called in a new thread for logging. */\r
+unsigned long WINAPI log_and_rotate(void *arg) {\r
+ logger_t *logger = (logger_t *) arg;\r
+ if (! logger) return 1;\r
+\r
+ __int64 size;\r
+ BY_HANDLE_FILE_INFORMATION info;\r
+\r
+ /* Find initial file size. */\r
+ if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;\r
+ else {\r
+ ULARGE_INTEGER l;\r
+ l.HighPart = info.nFileSizeHigh;\r
+ l.LowPart = info.nFileSizeLow;\r
+ size = l.QuadPart;\r
+ }\r
+\r
+ char buffer[1024];\r
+ void *address;\r
+ unsigned long in, out;\r
+ unsigned long charsize = 0;\r
+ unsigned long error;\r
+ int ret;\r
+ int complained = 0;\r
+\r
+ while (true) {\r
+ /* Read data from the pipe. */\r
+ address = &buffer;\r
+ ret = try_read(logger, address, sizeof(buffer), &in, &complained);\r
+ if (ret < 0) {\r
+ CloseHandle(logger->read_handle);\r
+ CloseHandle(logger->write_handle);\r
+ HeapFree(GetProcessHeap(), 0, logger);\r
+ return 2;\r
+ }\r
+ else if (ret) continue;\r
+\r
+ if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {\r
+ /* Look for newline. */\r
+ unsigned long i;\r
+ for (i = 0; i < in; i++) {\r
+ if (buffer[i] == '\n') {\r
+ if (! charsize) charsize = guess_charsize(address, in);\r
+ i += charsize;\r
+\r
+ /* Write up to the newline. */\r
+ ret = try_write(logger, address, i, &out, &complained);\r
+ if (ret < 0) {\r
+ CloseHandle(logger->read_handle);\r
+ CloseHandle(logger->write_handle);\r
+ HeapFree(GetProcessHeap(), 0, logger);\r
+ return 3;\r
+ }\r
+ size += (__int64) out;\r
+\r
+ /* Rotate. */\r
+ *logger->rotate_online = NSSM_ROTATE_ONLINE;\r
+ TCHAR rotated[PATH_LENGTH];\r
+ rotated_filename(logger->path, rotated, _countof(rotated), 0);\r
+\r
+ /*\r
+ Ideally we'd try the rename first then close the handle but\r
+ MoveFile() will fail if the handle is still open so we must\r
+ risk losing everything.\r
+ */\r
+ if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);\r
+ CloseHandle(logger->write_handle);\r
+ bool ok = true;\r
+ TCHAR *function;\r
+ if (logger->copy_and_truncate) {\r
+ function = _T("CopyFile()");\r
+ if (CopyFile(logger->path, rotated, TRUE)) {\r
+ HANDLE file = write_to_file(logger->path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);\r
+ Sleep(logger->rotate_delay);\r
+ SetFilePointer(file, 0, 0, FILE_BEGIN);\r
+ SetEndOfFile(file);\r
+ CloseHandle(file);\r
+ }\r
+ else ok = false;\r
+ }\r
+ else {\r
+ function = _T("MoveFile()");\r
+ if (! MoveFile(logger->path, rotated)) ok = false;\r
+ }\r
+ if (ok) {\r
+ log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);\r
+ size = 0LL;\r
+ }\r
+ else {\r
+ error = GetLastError();\r
+ if (error != ERROR_FILE_NOT_FOUND) {\r
+ if (! (complained & COMPLAINED_ROTATE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, logger->service_name, logger->path, function, rotated, error_string(error), 0);\r
+ complained |= COMPLAINED_ROTATE;\r
+ /* We can at least try to re-open the existing file. */\r
+ logger->disposition = OPEN_ALWAYS;\r
+ }\r
+ }\r
+\r
+ /* Reopen. */\r
+ logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);\r
+ if (logger->write_handle == INVALID_HANDLE_VALUE) {\r
+ error = GetLastError();\r
+ log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);\r
+ /* Oh dear. Now we can't log anything further. */\r
+ CloseHandle(logger->read_handle);\r
+ CloseHandle(logger->write_handle);\r
+ HeapFree(GetProcessHeap(), 0, logger);\r
+ return 4;\r
+ }\r
+\r
+ /* Resume writing after the newline. */\r
+ address = (void *) ((char *) address + i);\r
+ in -= i;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (! size) {\r
+ /* Write a BOM to the new file. */\r
+ if (! charsize) charsize = guess_charsize(address, in);\r
+ if (charsize == sizeof(wchar_t)) write_bom(logger, &out);\r
+ size += (__int64) out;\r
+ }\r
+\r
+ /* Write the data, if any. */\r
+ if (! in) continue;\r
+\r
+ ret = try_write(logger, address, in, &out, &complained);\r
+ size += (__int64) out;\r
+ if (ret < 0) {\r
+ CloseHandle(logger->read_handle);\r
+ CloseHandle(logger->write_handle);\r
+ HeapFree(GetProcessHeap(), 0, logger);\r
+ return 3;\r
+ }\r
+ }\r
+\r
+ CloseHandle(logger->read_handle);\r
+ CloseHandle(logger->write_handle);\r
+ HeapFree(GetProcessHeap(), 0, logger);\r
+ return 0;\r
+}\r