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(TCHAR *service_name, TCHAR *path, unsigned long sharing, unsigned long disposition, unsigned long flags, HANDLE *read_handle_ptr, HANDLE *pipe_handle_ptr, HANDLE *write_handle_ptr, unsigned long rotate_bytes_low, unsigned long rotate_bytes_high, unsigned long *tid_ptr, unsigned long *rotate_online) {
\r
10 /* Pipe between application's stdout/stderr and our logging handle. */
\r
11 if (read_handle_ptr && ! *read_handle_ptr) {
\r
12 if (pipe_handle_ptr && ! *pipe_handle_ptr) {
\r
13 if (CreatePipe(read_handle_ptr, pipe_handle_ptr, 0, 0)) {
\r
14 SetHandleInformation(*pipe_handle_ptr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
\r
17 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service_name, path, error_string(GetLastError()));
\r
23 logger_t *logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
\r
25 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("logger"), _T("create_logging_thread()"), 0);
\r
29 ULARGE_INTEGER size;
\r
30 size.LowPart = rotate_bytes_low;
\r
31 size.HighPart = rotate_bytes_high;
\r
33 logger->service_name = service_name;
\r
34 logger->path = path;
\r
35 logger->sharing = sharing;
\r
36 logger->disposition = disposition;
\r
37 logger->flags = flags;
\r
38 logger->read_handle = *read_handle_ptr;
\r
39 logger->write_handle = *write_handle_ptr;
\r
40 logger->size = (__int64) size.QuadPart;
\r
41 logger->tid_ptr = tid_ptr;
\r
42 logger->rotate_online = rotate_online;
\r
44 HANDLE thread_handle = CreateThread(NULL, 0, log_and_rotate, (void *) logger, 0, logger->tid_ptr);
\r
45 if (! thread_handle) {
\r
46 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
47 HeapFree(GetProcessHeap(), 0, logger);
\r
50 return thread_handle;
\r
53 static inline unsigned long guess_charsize(void *address, unsigned long bufsize) {
\r
54 if (IsTextUnicode(address, bufsize, 0)) return (unsigned long) sizeof(wchar_t);
\r
55 else return (unsigned long) sizeof(char);
\r
58 static inline void write_bom(logger_t *logger, unsigned long *out) {
\r
59 wchar_t bom = L'\ufeff';
\r
60 if (! WriteFile(logger->write_handle, (void *) &bom, sizeof(bom), out, 0)) {
\r
61 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SOMEBODY_SET_UP_US_THE_BOM, logger->service_name, logger->path, error_string(GetLastError()), 0);
\r
65 /* Get path, share mode, creation disposition and flags for a stream. */
\r
66 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
67 TCHAR value[NSSM_STDIO_LENGTH];
\r
70 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
\r
71 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
\r
74 switch (expand_parameter(key, value, path, PATH_LENGTH, true, false)) {
\r
75 case 0: if (! path[0]) return 0; break; /* OK. */
\r
76 default: return 2; /* Error. */
\r
80 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
\r
81 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
\r
84 switch (get_number(key, value, sharing, false)) {
\r
85 case 0: *sharing = default_sharing; break; /* Missing. */
\r
86 case 1: break; /* Found. */
\r
87 case -2: return 4; break; /* Error. */
\r
90 /* CreationDisposition. */
\r
91 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
\r
92 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
\r
95 switch (get_number(key, value, disposition, false)) {
\r
96 case 0: *disposition = default_disposition; break; /* Missing. */
\r
97 case 1: break; /* Found. */
\r
98 case -2: return 6; break; /* Error. */
\r
102 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
\r
103 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
\r
106 switch (get_number(key, value, flags, false)) {
\r
107 case 0: *flags = default_flags; break; /* Missing. */
\r
108 case 1: break; /* Found. */
\r
109 case -2: return 8; break; /* Error. */
\r
115 int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
\r
116 TCHAR value[NSSM_STDIO_LENGTH];
\r
118 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
119 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
\r
123 return set_number(key, value, number);
\r
126 int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
\r
127 TCHAR value[NSSM_STDIO_LENGTH];
\r
129 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
130 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
\r
134 if (RegDeleteValue(key, value)) return 0;
\r
138 HANDLE write_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
139 HANDLE ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
141 if (SetFilePointer(ret, 0, 0, FILE_END) != INVALID_SET_FILE_POINTER) SetEndOfFile(ret);
\r
145 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
\r
149 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
156 TCHAR buffer[PATH_LENGTH];
\r
157 memmove(buffer, path, sizeof(buffer));
\r
158 TCHAR *ext = PathFindExtension(buffer);
\r
159 TCHAR extension[PATH_LENGTH];
\r
160 _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
162 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
165 void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsigned long low, unsigned long high) {
\r
166 unsigned long error;
\r
170 GetSystemTime(&st);
\r
172 BY_HANDLE_FILE_INFORMATION info;
\r
174 /* Try to open the file to check if it exists and to get attributes. */
\r
175 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
177 /* Get file attributes. */
\r
178 if (! GetFileInformationByHandle(file, &info)) {
\r
179 /* Reuse current time for rotation timestamp. */
\r
180 seconds = low = high = 0;
\r
181 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
187 error = GetLastError();
\r
188 if (error == ERROR_FILE_NOT_FOUND) return;
\r
189 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
190 /* Reuse current time for rotation timestamp. */
\r
191 seconds = low = high = 0;
\r
192 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
195 /* Check file age. */
\r
198 SystemTimeToFileTime(&st, &ft);
\r
201 s.LowPart = ft.dwLowDateTime;
\r
202 s.HighPart = ft.dwHighDateTime;
\r
203 s.QuadPart -= seconds * 10000000LL;
\r
204 ft.dwLowDateTime = s.LowPart;
\r
205 ft.dwHighDateTime = s.HighPart;
\r
206 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
209 /* Check file size. */
\r
211 if (info.nFileSizeHigh < high) return;
\r
212 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
215 /* Get new filename. */
\r
216 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
218 TCHAR rotated[PATH_LENGTH];
\r
219 rotated_filename(path, rotated, _countof(rotated), &st);
\r
222 if (MoveFile(path, rotated)) {
\r
223 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
226 error = GetLastError();
\r
228 if (error == ERROR_FILE_NOT_FOUND) return;
\r
229 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
233 int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
\r
234 bool set_flags = false;
\r
236 /* Standard security attributes allowing inheritance. */
\r
237 SECURITY_ATTRIBUTES attributes;
\r
238 ZeroMemory(&attributes, sizeof(attributes));
\r
239 attributes.bInheritHandle = true;
\r
242 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
243 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
244 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
247 if (si && service->stdout_path[0]) {
\r
248 if (service->rotate_files) rotate_file(service->name, service->stdout_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
249 HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
250 if (! stdout_handle) return 4;
\r
252 if (service->rotate_files && service->rotate_stdout_online) {
\r
253 service->stdout_pipe = si->hStdOutput = 0;
\r
254 service->stdout_thread = create_logging_thread(service->name, service->stdout_path, service->stdout_sharing, service->stdout_disposition, service->stdout_flags, &service->stdout_pipe, &si->hStdOutput, &stdout_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stdout_tid, &service->rotate_stdout_online);
\r
255 if (! service->stdout_thread) {
\r
256 CloseHandle(service->stdout_pipe);
\r
257 CloseHandle(si->hStdOutput);
\r
260 else service->stdout_thread = 0;
\r
262 if (! service->stdout_thread) {
\r
263 if (! DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
264 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stdout"), error_string(GetLastError()), 0);
\r
267 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
274 if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stderr_sharing, NSSM_STDERR_SHARING, &service->stderr_disposition, NSSM_STDERR_DISPOSITION, &service->stderr_flags, NSSM_STDERR_FLAGS)) {
\r
275 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
276 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
279 if (service->stderr_path[0]) {
\r
280 /* Same as stdout? */
\r
281 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
282 service->stderr_sharing = service->stdout_sharing;
\r
283 service->stderr_disposition = service->stdout_disposition;
\r
284 service->stderr_flags = service->stdout_flags;
\r
285 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
288 /* Two handles to the same file will create a race. */
\r
289 if (! DuplicateHandle(GetCurrentProcess(), si->hStdOutput, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
290 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stderr"), error_string(GetLastError()), 0);
\r
296 if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
297 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
298 if (! stderr_handle) return 7;
\r
300 if (service->rotate_files && service->rotate_stderr_online) {
\r
301 service->stderr_pipe = si->hStdError = 0;
\r
302 service->stderr_thread = create_logging_thread(service->name, service->stderr_path, service->stderr_sharing, service->stderr_disposition, service->stderr_flags, &service->stderr_pipe, &si->hStdError, &stderr_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stderr_tid, &service->rotate_stderr_online);
\r
303 if (! service->stderr_thread) {
\r
304 CloseHandle(service->stderr_pipe);
\r
305 CloseHandle(si->hStdError);
\r
308 else service->stderr_thread = 0;
\r
310 if (! service->stderr_thread) {
\r
311 if (! DuplicateHandle(GetCurrentProcess(), stderr_handle, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
312 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDERR, _T("stderr"), error_string(GetLastError()), 0);
\r
315 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
323 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
324 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
325 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
328 if (si && service->stdin_path[0]) {
\r
329 if (str_equiv(service->stdin_path, _T("|"))) {
\r
330 /* Fake stdin with a pipe. */
\r
333 None of this is necessary if we aren't redirecting stdout and/or
\r
336 If we don't redirect any handles the application will start and be
\r
337 quite happy with its console. If we start it with
\r
338 STARTF_USESTDHANDLES set it will interpret a NULL value for
\r
339 hStdInput as meaning no input. Because the service starts with
\r
340 no stdin we can't just pass GetStdHandle(STD_INPUT_HANDLE) to
\r
343 The only way we can successfully redirect the application's output
\r
344 while preventing programs which exit after reading all input from
\r
345 exiting prematurely is to create a pipe between ourselves and the
\r
346 application but write nothing to it.
\r
348 if (! CreatePipe(&si->hStdInput, &service->stdin_pipe, 0, 0)) {
\r
349 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_STDIN_CREATEPIPE_FAILED, service->name, error_string(GetLastError()), 0);
\r
352 SetHandleInformation(si->hStdInput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
\r
356 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, &attributes, service->stdin_disposition, service->stdin_flags, 0);
\r
357 if (! si->hStdInput) {
\r
358 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
366 if (! set_flags) return 0;
\r
369 We need to set the startup_info flags to make the new handles
\r
370 inheritable by the new process.
\r
372 if (si) si->dwFlags |= STARTF_USESTDHANDLES;
\r
377 void close_output_handles(STARTUPINFO *si) {
\r
378 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
379 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
380 if (si->hStdError) CloseHandle(si->hStdError);
\r
384 Try multiple times to read from a file.
\r
385 Returns: 0 on success.
\r
386 1 on non-fatal error.
\r
389 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
391 unsigned long error;
\r
392 for (int tries = 0; tries < 5; tries++) {
\r
393 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
395 error = GetLastError();
\r
397 /* Other end closed the pipe. */
\r
398 case ERROR_BROKEN_PIPE:
\r
400 goto complain_read;
\r
402 /* Couldn't lock the buffer. */
\r
403 case ERROR_NOT_ENOUGH_QUOTA:
\r
404 Sleep(2000 + tries * 3000);
\r
408 /* Write was cancelled by the other end. */
\r
409 case ERROR_OPERATION_ABORTED:
\r
411 goto complain_read;
\r
419 /* Ignore the error if we've been requested to exit anyway. */
\r
420 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
421 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
422 *complained |= COMPLAINED_READ;
\r
427 Try multiple times to write to a file.
\r
428 Returns: 0 on success.
\r
429 1 on non-fatal error.
\r
432 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
434 unsigned long error;
\r
435 for (int tries = 0; tries < 5; tries++) {
\r
436 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
438 error = GetLastError();
\r
439 if (error == ERROR_IO_PENDING) {
\r
440 /* Operation was successful pending flush to disk. */
\r
445 /* Other end closed the pipe. */
\r
446 case ERROR_BROKEN_PIPE:
\r
448 goto complain_write;
\r
450 /* Couldn't lock the buffer. */
\r
451 case ERROR_NOT_ENOUGH_QUOTA:
\r
452 /* Out of disk space. */
\r
453 case ERROR_DISK_FULL:
\r
454 Sleep(2000 + tries * 3000);
\r
459 /* We'll lose this line but try to read and write subsequent ones. */
\r
465 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
466 *complained |= COMPLAINED_WRITE;
\r
470 /* Wrapper to be called in a new thread for logging. */
\r
471 unsigned long WINAPI log_and_rotate(void *arg) {
\r
472 logger_t *logger = (logger_t *) arg;
\r
473 if (! logger) return 1;
\r
476 BY_HANDLE_FILE_INFORMATION info;
\r
478 /* Find initial file size. */
\r
479 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
482 l.HighPart = info.nFileSizeHigh;
\r
483 l.LowPart = info.nFileSizeLow;
\r
489 unsigned long in, out;
\r
490 unsigned long charsize = 0;
\r
491 unsigned long error;
\r
493 int complained = 0;
\r
496 /* Read data from the pipe. */
\r
498 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
500 CloseHandle(logger->read_handle);
\r
501 CloseHandle(logger->write_handle);
\r
502 HeapFree(GetProcessHeap(), 0, logger);
\r
505 else if (ret) continue;
\r
507 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
508 /* Look for newline. */
\r
510 for (i = 0; i < in; i++) {
\r
511 if (buffer[i] == '\n') {
\r
512 if (! charsize) charsize = guess_charsize(address, in);
\r
515 /* Write up to the newline. */
\r
516 ret = try_write(logger, address, i, &out, &complained);
\r
518 HeapFree(GetProcessHeap(), 0, logger);
\r
519 CloseHandle(logger->read_handle);
\r
520 CloseHandle(logger->write_handle);
\r
523 size += (__int64) out;
\r
526 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
527 TCHAR rotated[PATH_LENGTH];
\r
528 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
531 Ideally we'd try the rename first then close the handle but
\r
532 MoveFile() will fail if the handle is still open so we must
\r
533 risk losing everything.
\r
535 CloseHandle(logger->write_handle);
\r
536 if (MoveFile(logger->path, rotated)) {
\r
537 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
541 error = GetLastError();
\r
542 if (error != ERROR_FILE_NOT_FOUND) {
\r
543 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
544 complained |= COMPLAINED_ROTATE;
\r
545 /* We can at least try to re-open the existing file. */
\r
546 logger->disposition = OPEN_ALWAYS;
\r
551 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
552 if (! logger->write_handle) {
\r
553 error = GetLastError();
\r
554 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
555 /* Oh dear. Now we can't log anything further. */
\r
556 HeapFree(GetProcessHeap(), 0, logger);
\r
557 CloseHandle(logger->read_handle);
\r
558 CloseHandle(logger->write_handle);
\r
562 /* Resume writing after the newline. */
\r
563 address = (void *) ((char *) address + i);
\r
570 /* Write a BOM to the new file. */
\r
571 if (! charsize) charsize = guess_charsize(address, in);
\r
572 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
573 size += (__int64) out;
\r
576 /* Write the data, if any. */
\r
577 if (! in) continue;
\r
579 ret = try_write(logger, address, in, &out, &complained);
\r
580 size += (__int64) out;
\r
582 HeapFree(GetProcessHeap(), 0, logger);
\r
583 CloseHandle(logger->read_handle);
\r
584 CloseHandle(logger->write_handle);
\r
589 HeapFree(GetProcessHeap(), 0, logger);
\r
590 CloseHandle(logger->read_handle);
\r
591 CloseHandle(logger->write_handle);
\r