3 #define COMPLAINED_READ (1 << 0)
\r
4 #define COMPLAINED_WRITE (1 << 1)
\r
5 #define COMPLAINED_ROTATE (1 << 2)
\r
7 static int dup_handle(HANDLE source_handle, HANDLE *dest_handle_ptr, TCHAR *source_description, TCHAR *dest_description, unsigned long flags) {
\r
8 if (! dest_handle_ptr) return 1;
\r
10 if (! DuplicateHandle(GetCurrentProcess(), source_handle, GetCurrentProcess(), dest_handle_ptr, 0, true, flags)) {
\r
11 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, source_description, dest_description, error_string(GetLastError()), 0);
\r
17 static int dup_handle(HANDLE source_handle, HANDLE *dest_handle_ptr, TCHAR *source_description, TCHAR *dest_description) {
\r
18 return dup_handle(source_handle, dest_handle_ptr, source_description, dest_description, DUPLICATE_SAME_ACCESS);
\r
22 read_handle: read from application
\r
23 pipe_handle: stdout of application
\r
24 write_handle: to file
\r
26 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 rotate_delay, unsigned long *tid_ptr, unsigned long *rotate_online, bool copy_and_truncate) {
\r
29 /* Pipe between application's stdout/stderr and our logging handle. */
\r
30 if (read_handle_ptr && ! *read_handle_ptr) {
\r
31 if (pipe_handle_ptr && ! *pipe_handle_ptr) {
\r
32 if (CreatePipe(read_handle_ptr, pipe_handle_ptr, 0, 0)) {
\r
33 SetHandleInformation(*pipe_handle_ptr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
\r
36 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service_name, path, error_string(GetLastError()));
\r
42 logger_t *logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
\r
44 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("logger"), _T("create_logging_thread()"), 0);
\r
48 ULARGE_INTEGER size;
\r
49 size.LowPart = rotate_bytes_low;
\r
50 size.HighPart = rotate_bytes_high;
\r
52 logger->service_name = service_name;
\r
53 logger->path = path;
\r
54 logger->sharing = sharing;
\r
55 logger->disposition = disposition;
\r
56 logger->flags = flags;
\r
57 logger->read_handle = *read_handle_ptr;
\r
58 logger->write_handle = *write_handle_ptr;
\r
59 logger->size = (__int64) size.QuadPart;
\r
60 logger->tid_ptr = tid_ptr;
\r
61 logger->rotate_online = rotate_online;
\r
62 logger->rotate_delay = rotate_delay;
\r
63 logger->copy_and_truncate = copy_and_truncate;
\r
65 HANDLE thread_handle = CreateThread(NULL, 0, log_and_rotate, (void *) logger, 0, logger->tid_ptr);
\r
66 if (! thread_handle) {
\r
67 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
68 HeapFree(GetProcessHeap(), 0, logger);
\r
71 return thread_handle;
\r
74 static inline unsigned long guess_charsize(void *address, unsigned long bufsize) {
\r
75 if (IsTextUnicode(address, bufsize, 0)) return (unsigned long) sizeof(wchar_t);
\r
76 else return (unsigned long) sizeof(char);
\r
79 static inline void write_bom(logger_t *logger, unsigned long *out) {
\r
80 wchar_t bom = L'\ufeff';
\r
81 if (! WriteFile(logger->write_handle, (void *) &bom, sizeof(bom), out, 0)) {
\r
82 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SOMEBODY_SET_UP_US_THE_BOM, logger->service_name, logger->path, error_string(GetLastError()), 0);
\r
86 /* Get path, share mode, creation disposition and flags for a stream. */
\r
87 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, bool *copy_and_truncate) {
\r
88 TCHAR value[NSSM_STDIO_LENGTH];
\r
91 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
\r
92 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
\r
95 switch (expand_parameter(key, value, path, PATH_LENGTH, true, false)) {
\r
96 case 0: if (! path[0]) return 0; break; /* OK. */
\r
97 default: return 2; /* Error. */
\r
101 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
\r
102 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
\r
105 switch (get_number(key, value, sharing, false)) {
\r
106 case 0: *sharing = default_sharing; break; /* Missing. */
\r
107 case 1: break; /* Found. */
\r
108 case -2: return 4; /* Error. */
\r
111 /* CreationDisposition. */
\r
112 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
\r
113 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
\r
116 switch (get_number(key, value, disposition, false)) {
\r
117 case 0: *disposition = default_disposition; break; /* Missing. */
\r
118 case 1: break; /* Found. */
\r
119 case -2: return 6; /* Error. */
\r
123 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
\r
124 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
\r
127 switch (get_number(key, value, flags, false)) {
\r
128 case 0: *flags = default_flags; break; /* Missing. */
\r
129 case 1: break; /* Found. */
\r
130 case -2: return 8; /* Error. */
\r
133 /* Rotate with CopyFile() and SetEndOfFile(). */
\r
134 if (copy_and_truncate) {
\r
135 unsigned long data;
\r
136 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_COPY_AND_TRUNCATE) < 0) {
\r
137 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_COPY_AND_TRUNCATE, _T("get_createfile_parameters()"), 0);
\r
140 switch (get_number(key, value, &data, false)) {
\r
141 case 0: *copy_and_truncate = false; break; /* Missing. */
\r
142 case 1: /* Found. */
\r
143 if (data) *copy_and_truncate = true;
\r
144 else *copy_and_truncate = false;
\r
146 case -2: return 9; /* Error. */
\r
153 int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
\r
154 TCHAR value[NSSM_STDIO_LENGTH];
\r
156 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
157 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
\r
161 return set_number(key, value, number);
\r
164 int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
\r
165 TCHAR value[NSSM_STDIO_LENGTH];
\r
167 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
168 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
\r
172 if (RegDeleteValue(key, value)) return 0;
\r
176 HANDLE write_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
177 static LARGE_INTEGER offset = { 0 };
\r
178 HANDLE ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
179 if (ret != INVALID_HANDLE_VALUE) {
\r
180 if (SetFilePointerEx(ret, offset, 0, FILE_END)) SetEndOfFile(ret);
\r
184 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
\r
188 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
195 TCHAR buffer[PATH_LENGTH];
\r
196 memmove(buffer, path, sizeof(buffer));
\r
197 TCHAR *ext = PathFindExtension(buffer);
\r
198 TCHAR extension[PATH_LENGTH];
\r
199 _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
201 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
204 void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsigned long delay, unsigned long low, unsigned long high, bool copy_and_truncate) {
\r
205 unsigned long error;
\r
209 GetSystemTime(&st);
\r
211 BY_HANDLE_FILE_INFORMATION info;
\r
213 /* Try to open the file to check if it exists and to get attributes. */
\r
214 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
215 if (file != INVALID_HANDLE_VALUE) {
\r
216 /* Get file attributes. */
\r
217 if (! GetFileInformationByHandle(file, &info)) {
\r
218 /* Reuse current time for rotation timestamp. */
\r
219 seconds = low = high = 0;
\r
220 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
226 error = GetLastError();
\r
227 if (error == ERROR_FILE_NOT_FOUND) return;
\r
228 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
229 /* Reuse current time for rotation timestamp. */
\r
230 seconds = low = high = 0;
\r
231 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
234 /* Check file age. */
\r
237 SystemTimeToFileTime(&st, &ft);
\r
240 s.LowPart = ft.dwLowDateTime;
\r
241 s.HighPart = ft.dwHighDateTime;
\r
242 s.QuadPart -= seconds * 10000000LL;
\r
243 ft.dwLowDateTime = s.LowPart;
\r
244 ft.dwHighDateTime = s.HighPart;
\r
245 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
248 /* Check file size. */
\r
250 if (info.nFileSizeHigh < high) return;
\r
251 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
254 /* Get new filename. */
\r
255 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
257 TCHAR rotated[PATH_LENGTH];
\r
258 rotated_filename(path, rotated, _countof(rotated), &st);
\r
263 if (copy_and_truncate) {
\r
264 function = _T("CopyFile()");
\r
265 if (CopyFile(path, rotated, TRUE)) {
\r
266 file = write_to_file(path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
268 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
269 SetEndOfFile(file);
\r
275 function = _T("MoveFile()");
\r
276 if (! MoveFile(path, rotated)) ok = false;
\r
279 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
282 error = GetLastError();
\r
284 if (error == ERROR_FILE_NOT_FOUND) return;
\r
285 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, function, rotated, error_string(error), 0);
\r
289 int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
290 if (! si) return 1;
\r
292 /* Allocate a new console so we get a fresh stdin, stdout and stderr. */
\r
293 alloc_console(service);
\r
296 if (service->stdin_path[0]) {
\r
297 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
\r
298 if (si->hStdInput == INVALID_HANDLE_VALUE) {
\r
299 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
305 if (service->stdout_path[0]) {
\r
306 if (service->rotate_files) rotate_file(service->name, service->stdout_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high, service->rotate_delay, service->stdout_copy_and_truncate);
\r
307 HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
308 if (stdout_handle == INVALID_HANDLE_VALUE) return 4;
\r
309 service->stdout_si = 0;
\r
311 if (service->use_stdout_pipe) {
\r
312 service->stdout_pipe = si->hStdOutput = 0;
\r
313 service->stdout_thread = create_logging_thread(service->name, service->stdout_path, service->stdout_sharing, service->stdout_disposition, service->stdout_flags, &service->stdout_pipe, &service->stdout_si, &stdout_handle, service->rotate_bytes_low, service->rotate_bytes_high, service->rotate_delay, &service->stdout_tid, &service->rotate_stdout_online, service->stdout_copy_and_truncate);
\r
314 if (! service->stdout_thread) {
\r
315 CloseHandle(service->stdout_pipe);
\r
316 CloseHandle(service->stdout_si);
\r
319 else service->stdout_thread = 0;
\r
321 if (! service->stdout_thread) {
\r
322 if (dup_handle(stdout_handle, &service->stdout_si, NSSM_REG_STDOUT, _T("stdout"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 4;
\r
323 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
326 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_si"), _T("stdout"))) {
\r
327 if (service->stdout_thread) {
\r
328 CloseHandle(service->stdout_thread);
\r
329 service->stdout_thread = 0;
\r
335 if (service->stderr_path[0]) {
\r
336 /* Same as stdout? */
\r
337 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
338 service->stderr_sharing = service->stdout_sharing;
\r
339 service->stderr_disposition = service->stdout_disposition;
\r
340 service->stderr_flags = service->stdout_flags;
\r
341 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
343 /* Two handles to the same file will create a race. */
\r
344 /* XXX: Here we assume that either both or neither handle must be a pipe. */
\r
345 if (dup_handle(service->stdout_si, &service->stderr_si, _T("stdout"), _T("stderr"))) return 6;
\r
348 if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high, service->rotate_delay, service->stderr_copy_and_truncate);
\r
349 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
350 if (stderr_handle == INVALID_HANDLE_VALUE) return 7;
\r
351 service->stderr_si = 0;
\r
353 if (service->use_stderr_pipe) {
\r
354 service->stderr_pipe = si->hStdError = 0;
\r
355 service->stderr_thread = create_logging_thread(service->name, service->stderr_path, service->stderr_sharing, service->stderr_disposition, service->stderr_flags, &service->stderr_pipe, &service->stderr_si, &stderr_handle, service->rotate_bytes_low, service->rotate_bytes_high, service->rotate_delay, &service->stderr_tid, &service->rotate_stderr_online, service->stderr_copy_and_truncate);
\r
356 if (! service->stderr_thread) {
\r
357 CloseHandle(service->stderr_pipe);
\r
358 CloseHandle(service->stderr_si);
\r
361 else service->stderr_thread = 0;
\r
363 if (! service->stderr_thread) {
\r
364 if (dup_handle(stderr_handle, &service->stderr_si, NSSM_REG_STDERR, _T("stderr"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 7;
\r
365 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
369 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_si"), _T("stderr"))) {
\r
370 if (service->stderr_thread) {
\r
371 CloseHandle(service->stderr_thread);
\r
372 service->stderr_thread = 0;
\r
378 We need to set the startup_info flags to make the new handles
\r
379 inheritable by the new process.
\r
381 si->dwFlags |= STARTF_USESTDHANDLES;
\r
383 if (service->no_console) return 0;
\r
385 /* Redirect other handles. */
\r
386 if (! si->hStdInput) {
\r
387 if (dup_handle(GetStdHandle(STD_INPUT_HANDLE), &si->hStdInput, _T("STD_INPUT_HANDLE"), _T("stdin"))) return 8;
\r
389 if (! si->hStdOutput) {
\r
390 if (dup_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si->hStdOutput, _T("STD_OUTPUT_HANDLE"), _T("stdout"))) return 9;
\r
392 if (! si->hStdError) {
\r
393 if (dup_handle(GetStdHandle(STD_ERROR_HANDLE), &si->hStdError, _T("STD_ERROR_HANDLE"), _T("stderr"))) return 10;
\r
399 /* Reuse output handles for a hook. */
\r
400 int use_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
401 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
403 if (service->stdout_si) {
\r
404 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_pipe"), _T("hStdOutput"))) return 1;
\r
405 si->dwFlags |= STARTF_USESTDHANDLES;
\r
408 if (service->stderr_si) {
\r
409 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_pipe"), _T("hStdError"))) {
\r
410 if (si->hStdOutput) {
\r
411 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
412 CloseHandle(si->hStdOutput);
\r
416 si->dwFlags |= STARTF_USESTDHANDLES;
\r
422 void close_output_handles(STARTUPINFO *si) {
\r
423 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
424 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
425 if (si->hStdError) CloseHandle(si->hStdError);
\r
429 Try multiple times to read from a file.
\r
430 Returns: 0 on success.
\r
431 1 on non-fatal error.
\r
434 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
436 unsigned long error;
\r
437 for (int tries = 0; tries < 5; tries++) {
\r
438 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
440 error = GetLastError();
\r
442 /* Other end closed the pipe. */
\r
443 case ERROR_BROKEN_PIPE:
\r
445 goto complain_read;
\r
447 /* Couldn't lock the buffer. */
\r
448 case ERROR_NOT_ENOUGH_QUOTA:
\r
449 Sleep(2000 + tries * 3000);
\r
453 /* Write was cancelled by the other end. */
\r
454 case ERROR_OPERATION_ABORTED:
\r
456 goto complain_read;
\r
464 /* Ignore the error if we've been requested to exit anyway. */
\r
465 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
466 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
467 *complained |= COMPLAINED_READ;
\r
472 Try multiple times to write to a file.
\r
473 Returns: 0 on success.
\r
474 1 on non-fatal error.
\r
477 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
479 unsigned long error;
\r
480 for (int tries = 0; tries < 5; tries++) {
\r
481 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
483 error = GetLastError();
\r
484 if (error == ERROR_IO_PENDING) {
\r
485 /* Operation was successful pending flush to disk. */
\r
490 /* Other end closed the pipe. */
\r
491 case ERROR_BROKEN_PIPE:
\r
493 goto complain_write;
\r
495 /* Couldn't lock the buffer. */
\r
496 case ERROR_NOT_ENOUGH_QUOTA:
\r
497 /* Out of disk space. */
\r
498 case ERROR_DISK_FULL:
\r
499 Sleep(2000 + tries * 3000);
\r
504 /* We'll lose this line but try to read and write subsequent ones. */
\r
510 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
511 *complained |= COMPLAINED_WRITE;
\r
515 /* Wrapper to be called in a new thread for logging. */
\r
516 unsigned long WINAPI log_and_rotate(void *arg) {
\r
517 logger_t *logger = (logger_t *) arg;
\r
518 if (! logger) return 1;
\r
521 BY_HANDLE_FILE_INFORMATION info;
\r
523 /* Find initial file size. */
\r
524 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
527 l.HighPart = info.nFileSizeHigh;
\r
528 l.LowPart = info.nFileSizeLow;
\r
534 unsigned long in, out;
\r
535 unsigned long charsize = 0;
\r
536 unsigned long error;
\r
538 int complained = 0;
\r
541 /* Read data from the pipe. */
\r
543 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
545 CloseHandle(logger->read_handle);
\r
546 CloseHandle(logger->write_handle);
\r
547 HeapFree(GetProcessHeap(), 0, logger);
\r
550 else if (ret) continue;
\r
552 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
553 /* Look for newline. */
\r
555 for (i = 0; i < in; i++) {
\r
556 if (buffer[i] == '\n') {
\r
557 if (! charsize) charsize = guess_charsize(address, in);
\r
560 /* Write up to the newline. */
\r
561 ret = try_write(logger, address, i, &out, &complained);
\r
563 CloseHandle(logger->read_handle);
\r
564 CloseHandle(logger->write_handle);
\r
565 HeapFree(GetProcessHeap(), 0, logger);
\r
568 size += (__int64) out;
\r
571 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
572 TCHAR rotated[PATH_LENGTH];
\r
573 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
576 Ideally we'd try the rename first then close the handle but
\r
577 MoveFile() will fail if the handle is still open so we must
\r
578 risk losing everything.
\r
580 if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);
\r
581 CloseHandle(logger->write_handle);
\r
584 if (logger->copy_and_truncate) {
\r
585 function = _T("CopyFile()");
\r
586 if (CopyFile(logger->path, rotated, TRUE)) {
\r
587 HANDLE file = write_to_file(logger->path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
588 Sleep(logger->rotate_delay);
\r
589 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
590 SetEndOfFile(file);
\r
596 function = _T("MoveFile()");
\r
597 if (! MoveFile(logger->path, rotated)) ok = false;
\r
600 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
604 error = GetLastError();
\r
605 if (error != ERROR_FILE_NOT_FOUND) {
\r
606 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
607 complained |= COMPLAINED_ROTATE;
\r
608 /* We can at least try to re-open the existing file. */
\r
609 logger->disposition = OPEN_ALWAYS;
\r
614 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
615 if (logger->write_handle == INVALID_HANDLE_VALUE) {
\r
616 error = GetLastError();
\r
617 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
618 /* Oh dear. Now we can't log anything further. */
\r
619 CloseHandle(logger->read_handle);
\r
620 CloseHandle(logger->write_handle);
\r
621 HeapFree(GetProcessHeap(), 0, logger);
\r
625 /* Resume writing after the newline. */
\r
626 address = (void *) ((char *) address + i);
\r
633 /* Write a BOM to the new file. */
\r
634 if (! charsize) charsize = guess_charsize(address, in);
\r
635 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
636 size += (__int64) out;
\r
639 /* Write the data, if any. */
\r
640 if (! in) continue;
\r
642 ret = try_write(logger, address, in, &out, &complained);
\r
643 size += (__int64) out;
\r
645 CloseHandle(logger->read_handle);
\r
646 CloseHandle(logger->write_handle);
\r
647 HeapFree(GetProcessHeap(), 0, logger);
\r
652 CloseHandle(logger->read_handle);
\r
653 CloseHandle(logger->write_handle);
\r
654 HeapFree(GetProcessHeap(), 0, logger);
\r