3 #define COMPLAINED_READ (1 << 0)
\r
4 #define COMPLAINED_WRITE (1 << 1)
\r
5 #define COMPLAINED_ROTATE (1 << 2)
\r
7 static HANDLE create_logging_thread(logger_t *logger) {
\r
8 HANDLE thread_handle = CreateThread(NULL, 0, log_and_rotate, (void *) logger, 0, logger->tid_ptr);
\r
9 if (! thread_handle) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
10 return thread_handle;
\r
13 static inline unsigned long guess_charsize(void *address, unsigned long bufsize) {
\r
14 if (IsTextUnicode(address, bufsize, 0)) return (unsigned long) sizeof(wchar_t);
\r
15 else return (unsigned long) sizeof(char);
\r
18 static inline void write_bom(logger_t *logger, unsigned long *out) {
\r
19 wchar_t bom = L'\ufeff';
\r
20 if (! WriteFile(logger->write_handle, (void *) &bom, sizeof(bom), out, 0)) {
\r
21 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SOMEBODY_SET_UP_US_THE_BOM, logger->service_name, logger->path, error_string(GetLastError()), 0);
\r
25 /* Get path, share mode, creation disposition and flags for a stream. */
\r
26 int get_createfile_parameters(HKEY key, TCHAR *prefix, TCHAR *path, unsigned long *sharing, unsigned long default_sharing, unsigned long *disposition, unsigned long default_disposition, unsigned long *flags, unsigned long default_flags) {
\r
27 TCHAR value[NSSM_STDIO_LENGTH];
\r
30 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
\r
31 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
\r
34 switch (expand_parameter(key, value, path, MAX_PATH, true, false)) {
\r
35 case 0: if (! path[0]) return 0; break; /* OK. */
\r
36 default: return 2; /* Error. */
\r
40 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
\r
41 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
\r
44 switch (get_number(key, value, sharing, false)) {
\r
45 case 0: *sharing = default_sharing; break; /* Missing. */
\r
46 case 1: break; /* Found. */
\r
47 case -2: return 4; break; /* Error. */
\r
50 /* CreationDisposition. */
\r
51 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
\r
52 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
\r
55 switch (get_number(key, value, disposition, false)) {
\r
56 case 0: *disposition = default_disposition; break; /* Missing. */
\r
57 case 1: break; /* Found. */
\r
58 case -2: return 6; break; /* Error. */
\r
62 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
\r
63 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
\r
66 switch (get_number(key, value, flags, false)) {
\r
67 case 0: *flags = default_flags; break; /* Missing. */
\r
68 case 1: break; /* Found. */
\r
69 case -2: return 8; break; /* Error. */
\r
75 int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
\r
76 TCHAR value[NSSM_STDIO_LENGTH];
\r
78 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
79 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
\r
83 return set_number(key, value, number);
\r
86 int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
\r
87 TCHAR value[NSSM_STDIO_LENGTH];
\r
89 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
90 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
\r
94 if (RegDeleteValue(key, value)) return 0;
\r
98 HANDLE append_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
101 /* Try to append to the file first. */
\r
102 ret = CreateFile(path, FILE_APPEND_DATA, sharing, attributes, disposition, flags, 0);
\r
108 unsigned long error = GetLastError();
\r
109 if (error != ERROR_FILE_NOT_FOUND) {
\r
110 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(error), 0);
\r
114 /* It didn't exist. Create it. */
\r
115 return CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
118 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
125 TCHAR buffer[MAX_PATH];
\r
126 memmove(buffer, path, sizeof(buffer));
\r
127 TCHAR *ext = PathFindExtension(buffer);
\r
128 TCHAR extension[MAX_PATH];
\r
129 _sntprintf_s(extension, _countof(extension), _TRUNCATE, _T("-%04u%02u%02uT%02u%02u%02u.%03u%s"), st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond, st->wMilliseconds, ext);
\r
131 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
134 void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsigned long low, unsigned long high) {
\r
135 unsigned long error;
\r
139 GetSystemTime(&st);
\r
141 BY_HANDLE_FILE_INFORMATION info;
\r
143 /* Try to open the file to check if it exists and to get attributes. */
\r
144 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
146 /* Get file attributes. */
\r
147 if (! GetFileInformationByHandle(file, &info)) {
\r
148 /* Reuse current time for rotation timestamp. */
\r
149 seconds = low = high = 0;
\r
150 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
156 error = GetLastError();
\r
157 if (error == ERROR_FILE_NOT_FOUND) return;
\r
158 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
159 /* Reuse current time for rotation timestamp. */
\r
160 seconds = low = high = 0;
\r
161 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
164 /* Check file age. */
\r
167 SystemTimeToFileTime(&st, &ft);
\r
170 s.LowPart = ft.dwLowDateTime;
\r
171 s.HighPart = ft.dwHighDateTime;
\r
172 s.QuadPart -= seconds * 10000000LL;
\r
173 ft.dwLowDateTime = s.LowPart;
\r
174 ft.dwHighDateTime = s.HighPart;
\r
175 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
178 /* Check file size. */
\r
180 if (info.nFileSizeHigh < high) return;
\r
181 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
184 /* Get new filename. */
\r
185 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
187 TCHAR rotated[MAX_PATH];
\r
188 rotated_filename(path, rotated, _countof(rotated), &st);
\r
191 if (MoveFile(path, rotated)) {
\r
192 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
195 error = GetLastError();
\r
197 if (error == ERROR_FILE_NOT_FOUND) return;
\r
198 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
202 int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
\r
203 bool set_flags = false;
\r
205 /* Standard security attributes allowing inheritance. */
\r
206 SECURITY_ATTRIBUTES attributes;
\r
207 ZeroMemory(&attributes, sizeof(attributes));
\r
208 attributes.bInheritHandle = true;
\r
211 if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS)) {
\r
212 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
213 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
216 if (si && service->stdin_path[0]) {
\r
217 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, &attributes, service->stdin_disposition, service->stdin_flags, 0);
\r
218 if (! si->hStdInput) {
\r
219 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
225 ULARGE_INTEGER size;
\r
226 size.LowPart = service->rotate_bytes_low;
\r
227 size.HighPart = service->rotate_bytes_high;
\r
230 if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS)) {
\r
231 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
232 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
235 if (si && service->stdout_path[0]) {
\r
236 if (service->rotate_files) rotate_file(service->name, service->stdout_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
237 HANDLE stdout_handle = append_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
238 if (! stdout_handle) {
\r
239 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdout_path, error_string(GetLastError()), 0);
\r
243 /* Try online rotation only if a size threshold is set. */
\r
244 logger_t *stdout_logger = 0;
\r
245 if (service->rotate_files && service->rotate_stdout_online && size.QuadPart) {
\r
246 stdout_logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
\r
247 if (stdout_logger) {
\r
248 /* Pipe between application's stdout and our logging handle. */
\r
249 if (CreatePipe(&service->stdout_pipe, &si->hStdOutput, &attributes, 0)) {
\r
250 stdout_logger->service_name = service->name;
\r
251 stdout_logger->path = service->stdout_path;
\r
252 stdout_logger->sharing = service->stdout_sharing;
\r
253 stdout_logger->disposition = service->stdout_disposition;
\r
254 stdout_logger->flags = service->stdout_flags;
\r
255 stdout_logger->read_handle = service->stdout_pipe;
\r
256 stdout_logger->write_handle = stdout_handle;
\r
257 stdout_logger->size = (__int64) size.QuadPart;
\r
258 stdout_logger->tid_ptr = &service->stdout_tid;
\r
260 /* Logging thread. */
\r
261 service->stdout_thread = create_logging_thread(stdout_logger);
\r
262 if (! service->stdout_thread) {
\r
263 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service->name, service->stdout_path, error_string(GetLastError()));
\r
264 CloseHandle(service->stdout_pipe);
\r
265 CloseHandle(si->hStdOutput);
\r
266 service->stdout_tid = 0;
\r
269 else service->stdout_tid = 0;
\r
272 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("stdout_logger"), _T("get_output_handles()"), 0);
\r
273 service->stdout_tid = 0;
\r
276 /* Fall through to try direct I/O. */
\r
277 if (! service->stdout_tid) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service->name, service->stdout_path, error_string(GetLastError()));
\r
280 if (! service->stdout_tid) {
\r
281 if (stdout_logger) HeapFree(GetProcessHeap(), 0, stdout_logger);
\r
282 if (! DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
283 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, error_string(GetLastError()), 0);
\r
286 service->rotate_stdout_online = false;
\r
293 if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stdout_sharing, NSSM_STDERR_SHARING, &service->stdout_disposition, NSSM_STDERR_DISPOSITION, &service->stdout_flags, NSSM_STDERR_FLAGS)) {
\r
294 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
295 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
298 if (service->stderr_path[0]) {
\r
299 /* Same as stdout? */
\r
300 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
301 service->stderr_sharing = service->stdout_sharing;
\r
302 service->stderr_disposition = service->stdout_disposition;
\r
303 service->stderr_flags = service->stdout_flags;
\r
304 service->rotate_stderr_online = false;
\r
307 /* Two handles to the same file will create a race. */
\r
308 if (! DuplicateHandle(GetCurrentProcess(), si->hStdOutput, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
309 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, error_string(GetLastError()), 0);
\r
315 if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
316 HANDLE stderr_handle = append_to_file(service->stderr_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
317 if (! stderr_handle) {
\r
318 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stderr_path, error_string(GetLastError()), 0);
\r
322 /* Try online rotation only if a size threshold is set. */
\r
323 logger_t *stderr_logger = 0;
\r
324 if (service->rotate_files && service->rotate_stderr_online && size.QuadPart) {
\r
325 stderr_logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
\r
326 if (stderr_logger) {
\r
327 /* Pipe between application's stderr and our logging handle. */
\r
328 if (CreatePipe(&service->stderr_pipe, &si->hStdError, &attributes, 0)) {
\r
329 stderr_logger->service_name = service->name;
\r
330 stderr_logger->path = service->stderr_path;
\r
331 stderr_logger->sharing = service->stderr_sharing;
\r
332 stderr_logger->disposition = service->stderr_disposition;
\r
333 stderr_logger->flags = service->stderr_flags;
\r
334 stderr_logger->read_handle = service->stderr_pipe;
\r
335 stderr_logger->write_handle = stderr_handle;
\r
336 stderr_logger->size = (__int64) size.QuadPart;
\r
337 stderr_logger->tid_ptr = &service->stderr_tid;
\r
339 /* Logging thread. */
\r
340 service->stderr_thread = create_logging_thread(stderr_logger);
\r
341 if (! service->stderr_thread) {
\r
342 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service->name, service->stderr_path, error_string(GetLastError()));
\r
343 CloseHandle(service->stderr_pipe);
\r
344 CloseHandle(si->hStdError);
\r
345 service->stderr_tid = 0;
\r
348 else service->stderr_tid = 0;
\r
351 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("stderr_logger"), _T("get_output_handles()"), 0);
\r
352 service->stderr_tid = 0;
\r
355 /* Fall through to try direct I/O. */
\r
356 if (! service->stderr_tid) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service->name, service->stderr_path, error_string(GetLastError()));
\r
359 if (! service->stderr_tid) {
\r
360 if (stderr_logger) HeapFree(GetProcessHeap(), 0, stderr_logger);
\r
361 if (! DuplicateHandle(GetCurrentProcess(), stderr_handle, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
362 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDERR, error_string(GetLastError()), 0);
\r
365 service->rotate_stderr_online = false;
\r
372 if (! set_flags) return 0;
\r
375 We need to set the startup_info flags to make the new handles
\r
376 inheritable by the new process.
\r
378 if (si) si->dwFlags |= STARTF_USESTDHANDLES;
\r
383 void close_output_handles(STARTUPINFO *si, bool close_stdout, bool close_stderr) {
\r
384 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
385 if (si->hStdOutput && close_stdout) CloseHandle(si->hStdOutput);
\r
386 if (si->hStdError && close_stderr) CloseHandle(si->hStdError);
\r
389 void close_output_handles(STARTUPINFO *si) {
\r
390 return close_output_handles(si, true, true);
\r
394 Try multiple times to read from a file.
\r
395 Returns: 0 on success.
\r
396 1 on non-fatal error.
\r
399 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
401 unsigned long error;
\r
402 for (int tries = 0; tries < 5; tries++) {
\r
403 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
405 error = GetLastError();
\r
407 /* Other end closed the pipe. */
\r
408 case ERROR_BROKEN_PIPE:
\r
410 goto complain_read;
\r
412 /* Couldn't lock the buffer. */
\r
413 case ERROR_NOT_ENOUGH_QUOTA:
\r
414 Sleep(2000 + tries * 3000);
\r
418 /* Write was cancelled by the other end. */
\r
419 case ERROR_OPERATION_ABORTED:
\r
421 goto complain_read;
\r
429 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
430 *complained |= COMPLAINED_READ;
\r
435 Try multiple times to write to a file.
\r
436 Returns: 0 on success.
\r
437 1 on non-fatal error.
\r
440 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
442 unsigned long error;
\r
443 for (int tries = 0; tries < 5; tries++) {
\r
444 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
446 error = GetLastError();
\r
447 if (error == ERROR_IO_PENDING) {
\r
448 /* Operation was successful pending flush to disk. */
\r
453 /* Other end closed the pipe. */
\r
454 case ERROR_BROKEN_PIPE:
\r
456 goto complain_write;
\r
458 /* Couldn't lock the buffer. */
\r
459 case ERROR_NOT_ENOUGH_QUOTA:
\r
460 /* Out of disk space. */
\r
461 case ERROR_DISK_FULL:
\r
462 Sleep(2000 + tries * 3000);
\r
467 /* We'll lose this line but try to read and write subsequent ones. */
\r
473 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
474 *complained |= COMPLAINED_WRITE;
\r
478 /* Wrapper to be called in a new thread for logging. */
\r
479 unsigned long WINAPI log_and_rotate(void *arg) {
\r
480 logger_t *logger = (logger_t *) arg;
\r
481 if (! logger) return 1;
\r
484 BY_HANDLE_FILE_INFORMATION info;
\r
486 /* Find initial file size. */
\r
487 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
490 l.HighPart = info.nFileSizeHigh;
\r
491 l.LowPart = info.nFileSizeLow;
\r
497 unsigned long in, out;
\r
498 unsigned long charsize = 0;
\r
499 unsigned long error;
\r
501 int complained = 0;
\r
504 /* Read data from the pipe. */
\r
506 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
508 HeapFree(GetProcessHeap(), 0, logger);
\r
511 else if (ret) continue;
\r
513 if (size + (__int64) in >= logger->size) {
\r
514 /* Look for newline. */
\r
516 for (i = 0; i < in; i++) {
\r
517 if (buffer[i] == '\n') {
\r
518 if (! charsize) charsize = guess_charsize(address, in);
\r
521 /* Write up to the newline. */
\r
522 ret = try_write(logger, address, i, &out, &complained);
\r
524 HeapFree(GetProcessHeap(), 0, logger);
\r
527 size += (__int64) out;
\r
530 TCHAR rotated[MAX_PATH];
\r
531 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
534 Ideally we'd try the rename first then close the handle but
\r
535 MoveFile() will fail if the handle is still open so we must
\r
536 risk losing everything.
\r
538 CloseHandle(logger->write_handle);
\r
539 if (MoveFile(logger->path, rotated)) {
\r
540 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
544 error = GetLastError();
\r
545 if (error != ERROR_FILE_NOT_FOUND) {
\r
546 if (! (complained & COMPLAINED_ROTATE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, logger->service_name, logger->path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
547 complained |= COMPLAINED_ROTATE;
\r
548 /* We can at least try to re-open the existing file. */
\r
549 logger->disposition = OPEN_ALWAYS;
\r
554 logger->write_handle = append_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
555 if (! logger->write_handle) {
\r
556 error = GetLastError();
\r
557 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
558 /* Oh dear. Now we can't log anything further. */
\r
559 HeapFree(GetProcessHeap(), 0, logger);
\r
563 /* Resume writing after the newline. */
\r
564 address = (void *) ((char *) address + i);
\r
571 /* Write a BOM to the new file. */
\r
572 if (! charsize) charsize = guess_charsize(address, in);
\r
573 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
574 size += (__int64) out;
\r
577 /* Write the data, if any. */
\r
578 if (! in) continue;
\r
580 ret = try_write(logger, address, in, &out, &complained);
\r
581 size += (__int64) out;
\r
583 HeapFree(GetProcessHeap(), 0, logger);
\r
588 HeapFree(GetProcessHeap(), 0, logger);
\r