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 void close_handle(HANDLE *handle, HANDLE *remember) {
\r
87 if (remember) *remember = INVALID_HANDLE_VALUE;
\r
88 if (! handle) return;
\r
89 if (! *handle) return;
\r
90 CloseHandle(*handle);
\r
91 if (remember) *remember = *handle;
\r
95 void close_handle(HANDLE *handle) {
\r
96 close_handle(handle, NULL);
\r
99 /* Get path, share mode, creation disposition and flags for a stream. */
\r
100 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
101 TCHAR value[NSSM_STDIO_LENGTH];
\r
104 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
\r
105 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
\r
108 switch (expand_parameter(key, value, path, PATH_LENGTH, true, false)) {
\r
109 case 0: if (! path[0]) return 0; break; /* OK. */
\r
110 default: return 2; /* Error. */
\r
114 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
\r
115 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
\r
118 switch (get_number(key, value, sharing, false)) {
\r
119 case 0: *sharing = default_sharing; break; /* Missing. */
\r
120 case 1: break; /* Found. */
\r
121 case -2: return 4; /* Error. */
\r
124 /* CreationDisposition. */
\r
125 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
\r
126 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
\r
129 switch (get_number(key, value, disposition, false)) {
\r
130 case 0: *disposition = default_disposition; break; /* Missing. */
\r
131 case 1: break; /* Found. */
\r
132 case -2: return 6; /* Error. */
\r
136 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
\r
137 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
\r
140 switch (get_number(key, value, flags, false)) {
\r
141 case 0: *flags = default_flags; break; /* Missing. */
\r
142 case 1: break; /* Found. */
\r
143 case -2: return 8; /* Error. */
\r
146 /* Rotate with CopyFile() and SetEndOfFile(). */
\r
147 if (copy_and_truncate) {
\r
148 unsigned long data;
\r
149 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_COPY_AND_TRUNCATE) < 0) {
\r
150 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_COPY_AND_TRUNCATE, _T("get_createfile_parameters()"), 0);
\r
153 switch (get_number(key, value, &data, false)) {
\r
154 case 0: *copy_and_truncate = false; break; /* Missing. */
\r
155 case 1: /* Found. */
\r
156 if (data) *copy_and_truncate = true;
\r
157 else *copy_and_truncate = false;
\r
159 case -2: return 9; /* Error. */
\r
166 int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
\r
167 TCHAR value[NSSM_STDIO_LENGTH];
\r
169 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
170 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
\r
174 return set_number(key, value, number);
\r
177 int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
\r
178 TCHAR value[NSSM_STDIO_LENGTH];
\r
180 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
181 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
\r
185 if (RegDeleteValue(key, value)) return 0;
\r
189 HANDLE write_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
190 static LARGE_INTEGER offset = { 0 };
\r
191 HANDLE ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
192 if (ret != INVALID_HANDLE_VALUE) {
\r
193 if (SetFilePointerEx(ret, offset, 0, FILE_END)) SetEndOfFile(ret);
\r
197 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
\r
201 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
208 TCHAR buffer[PATH_LENGTH];
\r
209 memmove(buffer, path, sizeof(buffer));
\r
210 TCHAR *ext = PathFindExtension(buffer);
\r
211 TCHAR extension[PATH_LENGTH];
\r
212 _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
214 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
217 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
218 unsigned long error;
\r
222 GetSystemTime(&st);
\r
224 BY_HANDLE_FILE_INFORMATION info;
\r
226 /* Try to open the file to check if it exists and to get attributes. */
\r
227 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
228 if (file != INVALID_HANDLE_VALUE) {
\r
229 /* Get file attributes. */
\r
230 if (! GetFileInformationByHandle(file, &info)) {
\r
231 /* Reuse current time for rotation timestamp. */
\r
232 seconds = low = high = 0;
\r
233 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
239 error = GetLastError();
\r
240 if (error == ERROR_FILE_NOT_FOUND) return;
\r
241 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
242 /* Reuse current time for rotation timestamp. */
\r
243 seconds = low = high = 0;
\r
244 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
247 /* Check file age. */
\r
250 SystemTimeToFileTime(&st, &ft);
\r
253 s.LowPart = ft.dwLowDateTime;
\r
254 s.HighPart = ft.dwHighDateTime;
\r
255 s.QuadPart -= seconds * 10000000LL;
\r
256 ft.dwLowDateTime = s.LowPart;
\r
257 ft.dwHighDateTime = s.HighPart;
\r
258 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
261 /* Check file size. */
\r
263 if (info.nFileSizeHigh < high) return;
\r
264 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
267 /* Get new filename. */
\r
268 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
270 TCHAR rotated[PATH_LENGTH];
\r
271 rotated_filename(path, rotated, _countof(rotated), &st);
\r
276 if (copy_and_truncate) {
\r
277 function = _T("CopyFile()");
\r
278 if (CopyFile(path, rotated, TRUE)) {
\r
279 file = write_to_file(path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
281 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
282 SetEndOfFile(file);
\r
288 function = _T("MoveFile()");
\r
289 if (! MoveFile(path, rotated)) ok = false;
\r
292 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
295 error = GetLastError();
\r
297 if (error == ERROR_FILE_NOT_FOUND) return;
\r
298 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, function, rotated, error_string(error), 0);
\r
302 int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
303 if (! si) return 1;
\r
305 /* Allocate a new console so we get a fresh stdin, stdout and stderr. */
\r
306 alloc_console(service);
\r
309 if (service->stdin_path[0]) {
\r
310 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
\r
311 if (si->hStdInput == INVALID_HANDLE_VALUE) {
\r
312 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
318 if (service->stdout_path[0]) {
\r
319 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
320 HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
321 if (stdout_handle == INVALID_HANDLE_VALUE) return 4;
\r
322 service->stdout_si = 0;
\r
324 if (service->use_stdout_pipe) {
\r
325 service->stdout_pipe = si->hStdOutput = 0;
\r
326 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
327 if (! service->stdout_thread) {
\r
328 CloseHandle(service->stdout_pipe);
\r
329 CloseHandle(service->stdout_si);
\r
332 else service->stdout_thread = 0;
\r
334 if (! service->stdout_thread) {
\r
335 if (dup_handle(stdout_handle, &service->stdout_si, NSSM_REG_STDOUT, _T("stdout"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 4;
\r
336 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
339 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_si"), _T("stdout"))) close_handle(&service->stdout_thread);
\r
343 if (service->stderr_path[0]) {
\r
344 /* Same as stdout? */
\r
345 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
346 service->stderr_sharing = service->stdout_sharing;
\r
347 service->stderr_disposition = service->stdout_disposition;
\r
348 service->stderr_flags = service->stdout_flags;
\r
349 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
351 /* Two handles to the same file will create a race. */
\r
352 /* XXX: Here we assume that either both or neither handle must be a pipe. */
\r
353 if (dup_handle(service->stdout_si, &service->stderr_si, _T("stdout"), _T("stderr"))) return 6;
\r
356 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
357 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
358 if (stderr_handle == INVALID_HANDLE_VALUE) return 7;
\r
359 service->stderr_si = 0;
\r
361 if (service->use_stderr_pipe) {
\r
362 service->stderr_pipe = si->hStdError = 0;
\r
363 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
364 if (! service->stderr_thread) {
\r
365 CloseHandle(service->stderr_pipe);
\r
366 CloseHandle(service->stderr_si);
\r
369 else service->stderr_thread = 0;
\r
371 if (! service->stderr_thread) {
\r
372 if (dup_handle(stderr_handle, &service->stderr_si, NSSM_REG_STDERR, _T("stderr"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 7;
\r
373 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
377 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_si"), _T("stderr"))) close_handle(&service->stderr_thread);
\r
381 We need to set the startup_info flags to make the new handles
\r
382 inheritable by the new process.
\r
384 si->dwFlags |= STARTF_USESTDHANDLES;
\r
386 if (service->no_console) return 0;
\r
388 /* Redirect other handles. */
\r
389 if (! si->hStdInput) {
\r
390 if (dup_handle(GetStdHandle(STD_INPUT_HANDLE), &si->hStdInput, _T("STD_INPUT_HANDLE"), _T("stdin"))) return 8;
\r
392 if (! si->hStdOutput) {
\r
393 if (dup_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si->hStdOutput, _T("STD_OUTPUT_HANDLE"), _T("stdout"))) return 9;
\r
395 if (! si->hStdError) {
\r
396 if (dup_handle(GetStdHandle(STD_ERROR_HANDLE), &si->hStdError, _T("STD_ERROR_HANDLE"), _T("stderr"))) return 10;
\r
402 /* Reuse output handles for a hook. */
\r
403 int use_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
404 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
406 if (service->stdout_si) {
\r
407 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_pipe"), _T("hStdOutput"))) return 1;
\r
408 si->dwFlags |= STARTF_USESTDHANDLES;
\r
411 if (service->stderr_si) {
\r
412 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_pipe"), _T("hStdError"))) {
\r
413 if (si->hStdOutput) {
\r
414 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
415 CloseHandle(si->hStdOutput);
\r
419 si->dwFlags |= STARTF_USESTDHANDLES;
\r
425 void close_output_handles(STARTUPINFO *si) {
\r
426 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
427 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
428 if (si->hStdError) CloseHandle(si->hStdError);
\r
432 Try multiple times to read from a file.
\r
433 Returns: 0 on success.
\r
434 1 on non-fatal error.
\r
437 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
439 unsigned long error;
\r
440 for (int tries = 0; tries < 5; tries++) {
\r
441 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
443 error = GetLastError();
\r
445 /* Other end closed the pipe. */
\r
446 case ERROR_BROKEN_PIPE:
\r
448 goto complain_read;
\r
450 /* Couldn't lock the buffer. */
\r
451 case ERROR_NOT_ENOUGH_QUOTA:
\r
452 Sleep(2000 + tries * 3000);
\r
456 /* Write was cancelled by the other end. */
\r
457 case ERROR_OPERATION_ABORTED:
\r
459 goto complain_read;
\r
467 /* Ignore the error if we've been requested to exit anyway. */
\r
468 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
469 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
470 *complained |= COMPLAINED_READ;
\r
475 Try multiple times to write to a file.
\r
476 Returns: 0 on success.
\r
477 1 on non-fatal error.
\r
480 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
482 unsigned long error;
\r
483 for (int tries = 0; tries < 5; tries++) {
\r
484 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
486 error = GetLastError();
\r
487 if (error == ERROR_IO_PENDING) {
\r
488 /* Operation was successful pending flush to disk. */
\r
493 /* Other end closed the pipe. */
\r
494 case ERROR_BROKEN_PIPE:
\r
496 goto complain_write;
\r
498 /* Couldn't lock the buffer. */
\r
499 case ERROR_NOT_ENOUGH_QUOTA:
\r
500 /* Out of disk space. */
\r
501 case ERROR_DISK_FULL:
\r
502 Sleep(2000 + tries * 3000);
\r
507 /* We'll lose this line but try to read and write subsequent ones. */
\r
513 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
514 *complained |= COMPLAINED_WRITE;
\r
518 /* Wrapper to be called in a new thread for logging. */
\r
519 unsigned long WINAPI log_and_rotate(void *arg) {
\r
520 logger_t *logger = (logger_t *) arg;
\r
521 if (! logger) return 1;
\r
524 BY_HANDLE_FILE_INFORMATION info;
\r
526 /* Find initial file size. */
\r
527 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
530 l.HighPart = info.nFileSizeHigh;
\r
531 l.LowPart = info.nFileSizeLow;
\r
537 unsigned long in, out;
\r
538 unsigned long charsize = 0;
\r
539 unsigned long error;
\r
541 int complained = 0;
\r
544 /* Read data from the pipe. */
\r
546 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
548 close_handle(&logger->read_handle);
\r
549 close_handle(&logger->write_handle);
\r
550 HeapFree(GetProcessHeap(), 0, logger);
\r
553 else if (ret) continue;
\r
555 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
556 /* Look for newline. */
\r
558 for (i = 0; i < in; i++) {
\r
559 if (buffer[i] == '\n') {
\r
560 if (! charsize) charsize = guess_charsize(address, in);
\r
563 /* Write up to the newline. */
\r
564 ret = try_write(logger, address, i, &out, &complained);
\r
566 close_handle(&logger->read_handle);
\r
567 close_handle(&logger->write_handle);
\r
568 HeapFree(GetProcessHeap(), 0, logger);
\r
571 size += (__int64) out;
\r
574 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
575 TCHAR rotated[PATH_LENGTH];
\r
576 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
579 Ideally we'd try the rename first then close the handle but
\r
580 MoveFile() will fail if the handle is still open so we must
\r
581 risk losing everything.
\r
583 if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);
\r
584 close_handle(&logger->write_handle);
\r
587 if (logger->copy_and_truncate) {
\r
588 function = _T("CopyFile()");
\r
589 if (CopyFile(logger->path, rotated, TRUE)) {
\r
590 HANDLE file = write_to_file(logger->path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
591 Sleep(logger->rotate_delay);
\r
592 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
593 SetEndOfFile(file);
\r
599 function = _T("MoveFile()");
\r
600 if (! MoveFile(logger->path, rotated)) ok = false;
\r
603 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
607 error = GetLastError();
\r
608 if (error != ERROR_FILE_NOT_FOUND) {
\r
609 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
610 complained |= COMPLAINED_ROTATE;
\r
611 /* We can at least try to re-open the existing file. */
\r
612 logger->disposition = OPEN_ALWAYS;
\r
617 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
618 if (logger->write_handle == INVALID_HANDLE_VALUE) {
\r
619 error = GetLastError();
\r
620 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
621 /* Oh dear. Now we can't log anything further. */
\r
622 close_handle(&logger->read_handle);
\r
623 close_handle(&logger->write_handle);
\r
624 HeapFree(GetProcessHeap(), 0, logger);
\r
628 /* Resume writing after the newline. */
\r
629 address = (void *) ((char *) address + i);
\r
636 /* Write a BOM to the new file. */
\r
637 if (! charsize) charsize = guess_charsize(address, in);
\r
638 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
639 size += (__int64) out;
\r
642 /* Write the data, if any. */
\r
643 if (! in) continue;
\r
645 ret = try_write(logger, address, in, &out, &complained);
\r
646 size += (__int64) out;
\r
648 close_handle(&logger->read_handle);
\r
649 close_handle(&logger->write_handle);
\r
650 HeapFree(GetProcessHeap(), 0, logger);
\r
655 close_handle(&logger->read_handle);
\r
656 close_handle(&logger->write_handle);
\r
657 HeapFree(GetProcessHeap(), 0, logger);
\r