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"))) {
\r
340 if (service->stdout_thread) {
\r
341 CloseHandle(service->stdout_thread);
\r
342 service->stdout_thread = 0;
\r
348 if (service->stderr_path[0]) {
\r
349 /* Same as stdout? */
\r
350 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
351 service->stderr_sharing = service->stdout_sharing;
\r
352 service->stderr_disposition = service->stdout_disposition;
\r
353 service->stderr_flags = service->stdout_flags;
\r
354 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
356 /* Two handles to the same file will create a race. */
\r
357 /* XXX: Here we assume that either both or neither handle must be a pipe. */
\r
358 if (dup_handle(service->stdout_si, &service->stderr_si, _T("stdout"), _T("stderr"))) return 6;
\r
361 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
362 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
363 if (stderr_handle == INVALID_HANDLE_VALUE) return 7;
\r
364 service->stderr_si = 0;
\r
366 if (service->use_stderr_pipe) {
\r
367 service->stderr_pipe = si->hStdError = 0;
\r
368 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
369 if (! service->stderr_thread) {
\r
370 CloseHandle(service->stderr_pipe);
\r
371 CloseHandle(service->stderr_si);
\r
374 else service->stderr_thread = 0;
\r
376 if (! service->stderr_thread) {
\r
377 if (dup_handle(stderr_handle, &service->stderr_si, NSSM_REG_STDERR, _T("stderr"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 7;
\r
378 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
382 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_si"), _T("stderr"))) {
\r
383 if (service->stderr_thread) {
\r
384 CloseHandle(service->stderr_thread);
\r
385 service->stderr_thread = 0;
\r
391 We need to set the startup_info flags to make the new handles
\r
392 inheritable by the new process.
\r
394 si->dwFlags |= STARTF_USESTDHANDLES;
\r
396 if (service->no_console) return 0;
\r
398 /* Redirect other handles. */
\r
399 if (! si->hStdInput) {
\r
400 if (dup_handle(GetStdHandle(STD_INPUT_HANDLE), &si->hStdInput, _T("STD_INPUT_HANDLE"), _T("stdin"))) return 8;
\r
402 if (! si->hStdOutput) {
\r
403 if (dup_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si->hStdOutput, _T("STD_OUTPUT_HANDLE"), _T("stdout"))) return 9;
\r
405 if (! si->hStdError) {
\r
406 if (dup_handle(GetStdHandle(STD_ERROR_HANDLE), &si->hStdError, _T("STD_ERROR_HANDLE"), _T("stderr"))) return 10;
\r
412 /* Reuse output handles for a hook. */
\r
413 int use_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
414 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
416 if (service->stdout_si) {
\r
417 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_pipe"), _T("hStdOutput"))) return 1;
\r
418 si->dwFlags |= STARTF_USESTDHANDLES;
\r
421 if (service->stderr_si) {
\r
422 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_pipe"), _T("hStdError"))) {
\r
423 if (si->hStdOutput) {
\r
424 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
425 CloseHandle(si->hStdOutput);
\r
429 si->dwFlags |= STARTF_USESTDHANDLES;
\r
435 void close_output_handles(STARTUPINFO *si) {
\r
436 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
437 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
438 if (si->hStdError) CloseHandle(si->hStdError);
\r
442 Try multiple times to read from a file.
\r
443 Returns: 0 on success.
\r
444 1 on non-fatal error.
\r
447 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
449 unsigned long error;
\r
450 for (int tries = 0; tries < 5; tries++) {
\r
451 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
453 error = GetLastError();
\r
455 /* Other end closed the pipe. */
\r
456 case ERROR_BROKEN_PIPE:
\r
458 goto complain_read;
\r
460 /* Couldn't lock the buffer. */
\r
461 case ERROR_NOT_ENOUGH_QUOTA:
\r
462 Sleep(2000 + tries * 3000);
\r
466 /* Write was cancelled by the other end. */
\r
467 case ERROR_OPERATION_ABORTED:
\r
469 goto complain_read;
\r
477 /* Ignore the error if we've been requested to exit anyway. */
\r
478 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
479 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
480 *complained |= COMPLAINED_READ;
\r
485 Try multiple times to write to a file.
\r
486 Returns: 0 on success.
\r
487 1 on non-fatal error.
\r
490 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
492 unsigned long error;
\r
493 for (int tries = 0; tries < 5; tries++) {
\r
494 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
496 error = GetLastError();
\r
497 if (error == ERROR_IO_PENDING) {
\r
498 /* Operation was successful pending flush to disk. */
\r
503 /* Other end closed the pipe. */
\r
504 case ERROR_BROKEN_PIPE:
\r
506 goto complain_write;
\r
508 /* Couldn't lock the buffer. */
\r
509 case ERROR_NOT_ENOUGH_QUOTA:
\r
510 /* Out of disk space. */
\r
511 case ERROR_DISK_FULL:
\r
512 Sleep(2000 + tries * 3000);
\r
517 /* We'll lose this line but try to read and write subsequent ones. */
\r
523 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
524 *complained |= COMPLAINED_WRITE;
\r
528 /* Wrapper to be called in a new thread for logging. */
\r
529 unsigned long WINAPI log_and_rotate(void *arg) {
\r
530 logger_t *logger = (logger_t *) arg;
\r
531 if (! logger) return 1;
\r
534 BY_HANDLE_FILE_INFORMATION info;
\r
536 /* Find initial file size. */
\r
537 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
540 l.HighPart = info.nFileSizeHigh;
\r
541 l.LowPart = info.nFileSizeLow;
\r
547 unsigned long in, out;
\r
548 unsigned long charsize = 0;
\r
549 unsigned long error;
\r
551 int complained = 0;
\r
554 /* Read data from the pipe. */
\r
556 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
558 CloseHandle(logger->read_handle);
\r
559 CloseHandle(logger->write_handle);
\r
560 HeapFree(GetProcessHeap(), 0, logger);
\r
563 else if (ret) continue;
\r
565 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
566 /* Look for newline. */
\r
568 for (i = 0; i < in; i++) {
\r
569 if (buffer[i] == '\n') {
\r
570 if (! charsize) charsize = guess_charsize(address, in);
\r
573 /* Write up to the newline. */
\r
574 ret = try_write(logger, address, i, &out, &complained);
\r
576 CloseHandle(logger->read_handle);
\r
577 CloseHandle(logger->write_handle);
\r
578 HeapFree(GetProcessHeap(), 0, logger);
\r
581 size += (__int64) out;
\r
584 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
585 TCHAR rotated[PATH_LENGTH];
\r
586 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
589 Ideally we'd try the rename first then close the handle but
\r
590 MoveFile() will fail if the handle is still open so we must
\r
591 risk losing everything.
\r
593 if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);
\r
594 CloseHandle(logger->write_handle);
\r
597 if (logger->copy_and_truncate) {
\r
598 function = _T("CopyFile()");
\r
599 if (CopyFile(logger->path, rotated, TRUE)) {
\r
600 HANDLE file = write_to_file(logger->path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
601 Sleep(logger->rotate_delay);
\r
602 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
603 SetEndOfFile(file);
\r
609 function = _T("MoveFile()");
\r
610 if (! MoveFile(logger->path, rotated)) ok = false;
\r
613 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
617 error = GetLastError();
\r
618 if (error != ERROR_FILE_NOT_FOUND) {
\r
619 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
620 complained |= COMPLAINED_ROTATE;
\r
621 /* We can at least try to re-open the existing file. */
\r
622 logger->disposition = OPEN_ALWAYS;
\r
627 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
628 if (logger->write_handle == INVALID_HANDLE_VALUE) {
\r
629 error = GetLastError();
\r
630 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
631 /* Oh dear. Now we can't log anything further. */
\r
632 CloseHandle(logger->read_handle);
\r
633 CloseHandle(logger->write_handle);
\r
634 HeapFree(GetProcessHeap(), 0, logger);
\r
638 /* Resume writing after the newline. */
\r
639 address = (void *) ((char *) address + i);
\r
646 /* Write a BOM to the new file. */
\r
647 if (! charsize) charsize = guess_charsize(address, in);
\r
648 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
649 size += (__int64) out;
\r
652 /* Write the data, if any. */
\r
653 if (! in) continue;
\r
655 ret = try_write(logger, address, in, &out, &complained);
\r
656 size += (__int64) out;
\r
658 CloseHandle(logger->read_handle);
\r
659 CloseHandle(logger->write_handle);
\r
660 HeapFree(GetProcessHeap(), 0, logger);
\r
665 CloseHandle(logger->read_handle);
\r
666 CloseHandle(logger->write_handle);
\r
667 HeapFree(GetProcessHeap(), 0, logger);
\r