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
431 void cleanup_loggers(nssm_service_t *service) {
\r
432 unsigned long interval = NSSM_CLEANUP_LOGGERS_DEADLINE;
\r
433 HANDLE thread_handle = INVALID_HANDLE_VALUE;
\r
435 close_handle(&service->stdout_thread, &thread_handle);
\r
436 /* Close write end of the data pipe so logging thread can finalise read. */
\r
437 close_handle(&service->stdout_si);
\r
438 /* Await logging thread then close read end. */
\r
439 if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);
\r
440 close_handle(&service->stdout_pipe);
\r
442 thread_handle = INVALID_HANDLE_VALUE;
\r
443 close_handle(&service->stderr_thread, &thread_handle);
\r
444 close_handle(&service->stderr_si);
\r
445 if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);
\r
446 close_handle(&service->stderr_pipe);
\r
450 Try multiple times to read from a file.
\r
451 Returns: 0 on success.
\r
452 1 on non-fatal error.
\r
455 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
457 unsigned long error;
\r
458 for (int tries = 0; tries < 5; tries++) {
\r
459 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
461 error = GetLastError();
\r
463 /* Other end closed the pipe. */
\r
464 case ERROR_BROKEN_PIPE:
\r
466 goto complain_read;
\r
468 /* Couldn't lock the buffer. */
\r
469 case ERROR_NOT_ENOUGH_QUOTA:
\r
470 Sleep(2000 + tries * 3000);
\r
474 /* Write was cancelled by the other end. */
\r
475 case ERROR_OPERATION_ABORTED:
\r
477 goto complain_read;
\r
485 /* Ignore the error if we've been requested to exit anyway. */
\r
486 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
487 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
488 *complained |= COMPLAINED_READ;
\r
493 Try multiple times to write to a file.
\r
494 Returns: 0 on success.
\r
495 1 on non-fatal error.
\r
498 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
500 unsigned long error;
\r
501 for (int tries = 0; tries < 5; tries++) {
\r
502 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
504 error = GetLastError();
\r
505 if (error == ERROR_IO_PENDING) {
\r
506 /* Operation was successful pending flush to disk. */
\r
511 /* Other end closed the pipe. */
\r
512 case ERROR_BROKEN_PIPE:
\r
514 goto complain_write;
\r
516 /* Couldn't lock the buffer. */
\r
517 case ERROR_NOT_ENOUGH_QUOTA:
\r
518 /* Out of disk space. */
\r
519 case ERROR_DISK_FULL:
\r
520 Sleep(2000 + tries * 3000);
\r
525 /* We'll lose this line but try to read and write subsequent ones. */
\r
531 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
532 *complained |= COMPLAINED_WRITE;
\r
536 /* Wrapper to be called in a new thread for logging. */
\r
537 unsigned long WINAPI log_and_rotate(void *arg) {
\r
538 logger_t *logger = (logger_t *) arg;
\r
539 if (! logger) return 1;
\r
542 BY_HANDLE_FILE_INFORMATION info;
\r
544 /* Find initial file size. */
\r
545 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
548 l.HighPart = info.nFileSizeHigh;
\r
549 l.LowPart = info.nFileSizeLow;
\r
555 unsigned long in, out;
\r
556 unsigned long charsize = 0;
\r
557 unsigned long error;
\r
559 int complained = 0;
\r
562 /* Read data from the pipe. */
\r
564 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
566 close_handle(&logger->read_handle);
\r
567 close_handle(&logger->write_handle);
\r
568 HeapFree(GetProcessHeap(), 0, logger);
\r
571 else if (ret) continue;
\r
573 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
574 /* Look for newline. */
\r
576 for (i = 0; i < in; i++) {
\r
577 if (buffer[i] == '\n') {
\r
578 if (! charsize) charsize = guess_charsize(address, in);
\r
581 /* Write up to the newline. */
\r
582 ret = try_write(logger, address, i, &out, &complained);
\r
584 close_handle(&logger->read_handle);
\r
585 close_handle(&logger->write_handle);
\r
586 HeapFree(GetProcessHeap(), 0, logger);
\r
589 size += (__int64) out;
\r
592 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
593 TCHAR rotated[PATH_LENGTH];
\r
594 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
597 Ideally we'd try the rename first then close the handle but
\r
598 MoveFile() will fail if the handle is still open so we must
\r
599 risk losing everything.
\r
601 if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);
\r
602 close_handle(&logger->write_handle);
\r
605 if (logger->copy_and_truncate) {
\r
606 function = _T("CopyFile()");
\r
607 if (CopyFile(logger->path, rotated, TRUE)) {
\r
608 HANDLE file = write_to_file(logger->path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
609 Sleep(logger->rotate_delay);
\r
610 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
611 SetEndOfFile(file);
\r
617 function = _T("MoveFile()");
\r
618 if (! MoveFile(logger->path, rotated)) ok = false;
\r
621 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
625 error = GetLastError();
\r
626 if (error != ERROR_FILE_NOT_FOUND) {
\r
627 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
628 complained |= COMPLAINED_ROTATE;
\r
629 /* We can at least try to re-open the existing file. */
\r
630 logger->disposition = OPEN_ALWAYS;
\r
635 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
636 if (logger->write_handle == INVALID_HANDLE_VALUE) {
\r
637 error = GetLastError();
\r
638 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
639 /* Oh dear. Now we can't log anything further. */
\r
640 close_handle(&logger->read_handle);
\r
641 close_handle(&logger->write_handle);
\r
642 HeapFree(GetProcessHeap(), 0, logger);
\r
646 /* Resume writing after the newline. */
\r
647 address = (void *) ((char *) address + i);
\r
654 /* Write a BOM to the new file. */
\r
655 if (! charsize) charsize = guess_charsize(address, in);
\r
656 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
657 size += (__int64) out;
\r
660 /* Write the data, if any. */
\r
661 if (! in) continue;
\r
663 ret = try_write(logger, address, in, &out, &complained);
\r
664 size += (__int64) out;
\r
666 close_handle(&logger->read_handle);
\r
667 close_handle(&logger->write_handle);
\r
668 HeapFree(GetProcessHeap(), 0, logger);
\r
673 close_handle(&logger->read_handle);
\r
674 close_handle(&logger->write_handle);
\r
675 HeapFree(GetProcessHeap(), 0, logger);
\r