3 #define COMPLAINED_READ (1 << 0)
\r
4 #define COMPLAINED_WRITE (1 << 1)
\r
5 #define COMPLAINED_ROTATE (1 << 2)
\r
6 #define TIMESTAMP_FORMAT "%04u-%02u-%02u %02u:%02u:%02u.%03u: "
\r
7 #define TIMESTAMP_LEN 25
\r
9 static int dup_handle(HANDLE source_handle, HANDLE *dest_handle_ptr, TCHAR *source_description, TCHAR *dest_description, unsigned long flags) {
\r
10 if (! dest_handle_ptr) return 1;
\r
12 if (! DuplicateHandle(GetCurrentProcess(), source_handle, GetCurrentProcess(), dest_handle_ptr, 0, true, flags)) {
\r
13 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, source_description, dest_description, error_string(GetLastError()), 0);
\r
19 static int dup_handle(HANDLE source_handle, HANDLE *dest_handle_ptr, TCHAR *source_description, TCHAR *dest_description) {
\r
20 return dup_handle(source_handle, dest_handle_ptr, source_description, dest_description, DUPLICATE_SAME_ACCESS);
\r
24 read_handle: read from application
\r
25 pipe_handle: stdout of application
\r
26 write_handle: to file
\r
28 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 timestamp_log, bool copy_and_truncate) {
\r
31 /* Pipe between application's stdout/stderr and our logging handle. */
\r
32 if (read_handle_ptr && ! *read_handle_ptr) {
\r
33 if (pipe_handle_ptr && ! *pipe_handle_ptr) {
\r
34 if (CreatePipe(read_handle_ptr, pipe_handle_ptr, 0, 0)) {
\r
35 SetHandleInformation(*pipe_handle_ptr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
\r
38 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service_name, path, error_string(GetLastError()));
\r
44 logger_t *logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
\r
46 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("logger"), _T("create_logging_thread()"), 0);
\r
50 ULARGE_INTEGER size;
\r
51 size.LowPart = rotate_bytes_low;
\r
52 size.HighPart = rotate_bytes_high;
\r
54 logger->service_name = service_name;
\r
55 logger->path = path;
\r
56 logger->sharing = sharing;
\r
57 logger->disposition = disposition;
\r
58 logger->flags = flags;
\r
59 logger->read_handle = *read_handle_ptr;
\r
60 logger->write_handle = *write_handle_ptr;
\r
61 logger->size = (__int64) size.QuadPart;
\r
62 logger->tid_ptr = tid_ptr;
\r
63 logger->timestamp_log = timestamp_log;
\r
64 logger->line_length = 0;
\r
65 logger->rotate_online = rotate_online;
\r
66 logger->rotate_delay = rotate_delay;
\r
67 logger->copy_and_truncate = copy_and_truncate;
\r
69 HANDLE thread_handle = CreateThread(NULL, 0, log_and_rotate, (void *) logger, 0, logger->tid_ptr);
\r
70 if (! thread_handle) {
\r
71 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
72 HeapFree(GetProcessHeap(), 0, logger);
\r
75 return thread_handle;
\r
78 static inline unsigned long guess_charsize(void *address, unsigned long bufsize) {
\r
79 if (IsTextUnicode(address, bufsize, 0)) return (unsigned long) sizeof(wchar_t);
\r
80 else return (unsigned long) sizeof(char);
\r
83 static inline void write_bom(logger_t *logger, unsigned long *out) {
\r
84 wchar_t bom = L'\ufeff';
\r
85 if (! WriteFile(logger->write_handle, (void *) &bom, sizeof(bom), out, 0)) {
\r
86 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SOMEBODY_SET_UP_US_THE_BOM, logger->service_name, logger->path, error_string(GetLastError()), 0);
\r
90 void close_handle(HANDLE *handle, HANDLE *remember) {
\r
91 if (remember) *remember = INVALID_HANDLE_VALUE;
\r
92 if (! handle) return;
\r
93 if (! *handle) return;
\r
94 CloseHandle(*handle);
\r
95 if (remember) *remember = *handle;
\r
99 void close_handle(HANDLE *handle) {
\r
100 close_handle(handle, NULL);
\r
103 /* Get path, share mode, creation disposition and flags for a stream. */
\r
104 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
105 TCHAR value[NSSM_STDIO_LENGTH];
\r
108 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
\r
109 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
\r
112 switch (expand_parameter(key, value, path, PATH_LENGTH, true, false)) {
\r
113 case 0: if (! path[0]) return 0; break; /* OK. */
\r
114 default: return 2; /* Error. */
\r
118 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
\r
119 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
\r
122 switch (get_number(key, value, sharing, false)) {
\r
123 case 0: *sharing = default_sharing; break; /* Missing. */
\r
124 case 1: break; /* Found. */
\r
125 case -2: return 4; /* Error. */
\r
128 /* CreationDisposition. */
\r
129 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
\r
130 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
\r
133 switch (get_number(key, value, disposition, false)) {
\r
134 case 0: *disposition = default_disposition; break; /* Missing. */
\r
135 case 1: break; /* Found. */
\r
136 case -2: return 6; /* Error. */
\r
140 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
\r
141 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
\r
144 switch (get_number(key, value, flags, false)) {
\r
145 case 0: *flags = default_flags; break; /* Missing. */
\r
146 case 1: break; /* Found. */
\r
147 case -2: return 8; /* Error. */
\r
150 /* Rotate with CopyFile() and SetEndOfFile(). */
\r
151 if (copy_and_truncate) {
\r
152 unsigned long data;
\r
153 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_COPY_AND_TRUNCATE) < 0) {
\r
154 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_COPY_AND_TRUNCATE, _T("get_createfile_parameters()"), 0);
\r
157 switch (get_number(key, value, &data, false)) {
\r
158 case 0: *copy_and_truncate = false; break; /* Missing. */
\r
159 case 1: /* Found. */
\r
160 if (data) *copy_and_truncate = true;
\r
161 else *copy_and_truncate = false;
\r
163 case -2: return 9; /* Error. */
\r
170 int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
\r
171 TCHAR value[NSSM_STDIO_LENGTH];
\r
173 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
174 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
\r
178 return set_number(key, value, number);
\r
181 int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
\r
182 TCHAR value[NSSM_STDIO_LENGTH];
\r
184 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
185 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
\r
189 if (RegDeleteValue(key, value)) return 0;
\r
193 HANDLE write_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
194 static LARGE_INTEGER offset = { 0 };
\r
195 HANDLE ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
196 if (ret != INVALID_HANDLE_VALUE) {
\r
197 if (SetFilePointerEx(ret, offset, 0, FILE_END)) SetEndOfFile(ret);
\r
201 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
\r
205 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
212 TCHAR buffer[PATH_LENGTH];
\r
213 memmove(buffer, path, sizeof(buffer));
\r
214 TCHAR *ext = PathFindExtension(buffer);
\r
215 TCHAR extension[PATH_LENGTH];
\r
216 _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
218 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
221 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
222 unsigned long error;
\r
226 GetSystemTime(&st);
\r
228 BY_HANDLE_FILE_INFORMATION info;
\r
230 /* Try to open the file to check if it exists and to get attributes. */
\r
231 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
232 if (file != INVALID_HANDLE_VALUE) {
\r
233 /* Get file attributes. */
\r
234 if (! GetFileInformationByHandle(file, &info)) {
\r
235 /* Reuse current time for rotation timestamp. */
\r
236 seconds = low = high = 0;
\r
237 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
243 error = GetLastError();
\r
244 if (error == ERROR_FILE_NOT_FOUND) return;
\r
245 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
246 /* Reuse current time for rotation timestamp. */
\r
247 seconds = low = high = 0;
\r
248 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
251 /* Check file age. */
\r
254 SystemTimeToFileTime(&st, &ft);
\r
257 s.LowPart = ft.dwLowDateTime;
\r
258 s.HighPart = ft.dwHighDateTime;
\r
259 s.QuadPart -= seconds * 10000000LL;
\r
260 ft.dwLowDateTime = s.LowPart;
\r
261 ft.dwHighDateTime = s.HighPart;
\r
262 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
265 /* Check file size. */
\r
267 if (info.nFileSizeHigh < high) return;
\r
268 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
271 /* Get new filename. */
\r
272 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
274 TCHAR rotated[PATH_LENGTH];
\r
275 rotated_filename(path, rotated, _countof(rotated), &st);
\r
280 if (copy_and_truncate) {
\r
281 function = _T("CopyFile()");
\r
282 if (CopyFile(path, rotated, TRUE)) {
\r
283 file = write_to_file(path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
285 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
286 SetEndOfFile(file);
\r
292 function = _T("MoveFile()");
\r
293 if (! MoveFile(path, rotated)) ok = false;
\r
296 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
299 error = GetLastError();
\r
301 if (error == ERROR_FILE_NOT_FOUND) return;
\r
302 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, function, rotated, error_string(error), 0);
\r
306 int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
307 if (! si) return 1;
\r
309 /* Allocate a new console so we get a fresh stdin, stdout and stderr. */
\r
310 alloc_console(service);
\r
313 if (service->stdin_path[0]) {
\r
314 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
\r
315 if (si->hStdInput == INVALID_HANDLE_VALUE) {
\r
316 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
322 if (service->stdout_path[0]) {
\r
323 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
324 HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
325 if (stdout_handle == INVALID_HANDLE_VALUE) return 4;
\r
326 service->stdout_si = 0;
\r
328 if (service->use_stdout_pipe) {
\r
329 service->stdout_pipe = si->hStdOutput = 0;
\r
330 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->timestamp_log, service->stdout_copy_and_truncate);
\r
331 if (! service->stdout_thread) {
\r
332 CloseHandle(service->stdout_pipe);
\r
333 CloseHandle(service->stdout_si);
\r
336 else service->stdout_thread = 0;
\r
338 if (! service->stdout_thread) {
\r
339 if (dup_handle(stdout_handle, &service->stdout_si, NSSM_REG_STDOUT, _T("stdout"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 4;
\r
340 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
343 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_si"), _T("stdout"))) close_handle(&service->stdout_thread);
\r
347 if (service->stderr_path[0]) {
\r
348 /* Same as stdout? */
\r
349 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
350 service->stderr_sharing = service->stdout_sharing;
\r
351 service->stderr_disposition = service->stdout_disposition;
\r
352 service->stderr_flags = service->stdout_flags;
\r
353 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
355 /* Two handles to the same file will create a race. */
\r
356 /* XXX: Here we assume that either both or neither handle must be a pipe. */
\r
357 if (dup_handle(service->stdout_si, &service->stderr_si, _T("stdout"), _T("stderr"))) return 6;
\r
360 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
361 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
362 if (stderr_handle == INVALID_HANDLE_VALUE) return 7;
\r
363 service->stderr_si = 0;
\r
365 if (service->use_stderr_pipe) {
\r
366 service->stderr_pipe = si->hStdError = 0;
\r
367 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->timestamp_log, service->stderr_copy_and_truncate);
\r
368 if (! service->stderr_thread) {
\r
369 CloseHandle(service->stderr_pipe);
\r
370 CloseHandle(service->stderr_si);
\r
373 else service->stderr_thread = 0;
\r
375 if (! service->stderr_thread) {
\r
376 if (dup_handle(stderr_handle, &service->stderr_si, NSSM_REG_STDERR, _T("stderr"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 7;
\r
377 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
381 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_si"), _T("stderr"))) close_handle(&service->stderr_thread);
\r
385 We need to set the startup_info flags to make the new handles
\r
386 inheritable by the new process.
\r
388 si->dwFlags |= STARTF_USESTDHANDLES;
\r
390 if (service->no_console) return 0;
\r
392 /* Redirect other handles. */
\r
393 if (! si->hStdInput) {
\r
394 if (dup_handle(GetStdHandle(STD_INPUT_HANDLE), &si->hStdInput, _T("STD_INPUT_HANDLE"), _T("stdin"))) return 8;
\r
396 if (! si->hStdOutput) {
\r
397 if (dup_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si->hStdOutput, _T("STD_OUTPUT_HANDLE"), _T("stdout"))) return 9;
\r
399 if (! si->hStdError) {
\r
400 if (dup_handle(GetStdHandle(STD_ERROR_HANDLE), &si->hStdError, _T("STD_ERROR_HANDLE"), _T("stderr"))) return 10;
\r
406 /* Reuse output handles for a hook. */
\r
407 int use_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
408 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
410 if (service->stdout_si) {
\r
411 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_pipe"), _T("hStdOutput"))) return 1;
\r
412 si->dwFlags |= STARTF_USESTDHANDLES;
\r
415 if (service->stderr_si) {
\r
416 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_pipe"), _T("hStdError"))) {
\r
417 if (si->hStdOutput) {
\r
418 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
419 CloseHandle(si->hStdOutput);
\r
423 si->dwFlags |= STARTF_USESTDHANDLES;
\r
429 void close_output_handles(STARTUPINFO *si) {
\r
430 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
431 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
432 if (si->hStdError) CloseHandle(si->hStdError);
\r
435 void cleanup_loggers(nssm_service_t *service) {
\r
436 unsigned long interval = NSSM_CLEANUP_LOGGERS_DEADLINE;
\r
437 HANDLE thread_handle = INVALID_HANDLE_VALUE;
\r
439 close_handle(&service->stdout_thread, &thread_handle);
\r
440 /* Close write end of the data pipe so logging thread can finalise read. */
\r
441 close_handle(&service->stdout_si);
\r
442 /* Await logging thread then close read end. */
\r
443 if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);
\r
444 close_handle(&service->stdout_pipe);
\r
446 thread_handle = INVALID_HANDLE_VALUE;
\r
447 close_handle(&service->stderr_thread, &thread_handle);
\r
448 close_handle(&service->stderr_si);
\r
449 if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);
\r
450 close_handle(&service->stderr_pipe);
\r
454 Try multiple times to read from a file.
\r
455 Returns: 0 on success.
\r
456 1 on non-fatal error.
\r
459 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
461 unsigned long error;
\r
462 for (int tries = 0; tries < 5; tries++) {
\r
463 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
465 error = GetLastError();
\r
467 /* Other end closed the pipe. */
\r
468 case ERROR_BROKEN_PIPE:
\r
470 goto complain_read;
\r
472 /* Couldn't lock the buffer. */
\r
473 case ERROR_NOT_ENOUGH_QUOTA:
\r
474 Sleep(2000 + tries * 3000);
\r
478 /* Write was cancelled by the other end. */
\r
479 case ERROR_OPERATION_ABORTED:
\r
481 goto complain_read;
\r
489 /* Ignore the error if we've been requested to exit anyway. */
\r
490 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
491 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
492 *complained |= COMPLAINED_READ;
\r
497 Try multiple times to write to a file.
\r
498 Returns: 0 on success.
\r
499 1 on non-fatal error.
\r
502 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
504 unsigned long error;
\r
505 for (int tries = 0; tries < 5; tries++) {
\r
506 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
508 error = GetLastError();
\r
509 if (error == ERROR_IO_PENDING) {
\r
510 /* Operation was successful pending flush to disk. */
\r
515 /* Other end closed the pipe. */
\r
516 case ERROR_BROKEN_PIPE:
\r
518 goto complain_write;
\r
520 /* Couldn't lock the buffer. */
\r
521 case ERROR_NOT_ENOUGH_QUOTA:
\r
522 /* Out of disk space. */
\r
523 case ERROR_DISK_FULL:
\r
524 Sleep(2000 + tries * 3000);
\r
529 /* We'll lose this line but try to read and write subsequent ones. */
\r
535 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
536 *complained |= COMPLAINED_WRITE;
\r
540 /* Note that the timestamp is created in UTF-8. */
\r
541 static inline int write_timestamp(logger_t *logger, unsigned long charsize, unsigned long *out, int *complained) {
\r
542 char timestamp[TIMESTAMP_LEN + 1];
\r
545 GetSystemTime(&now);
\r
546 _snprintf_s(timestamp, _countof(timestamp), _TRUNCATE, TIMESTAMP_FORMAT, now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond, now.wMilliseconds);
\r
548 if (charsize == sizeof(char)) return try_write(logger, (void *) timestamp, TIMESTAMP_LEN, out, complained);
\r
551 unsigned long utf16len;
\r
552 if (to_utf16(timestamp, &utf16, &utf16len)) return -1;
\r
553 int ret = try_write(logger, (void *) *utf16, utf16len * sizeof(wchar_t), out, complained);
\r
554 HeapFree(GetProcessHeap(), 0, utf16);
\r
558 static int write_with_timestamp(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained, unsigned long charsize) {
\r
559 if (logger->timestamp_log) {
\r
560 unsigned long log_out;
\r
561 int log_complained;
\r
562 unsigned long timestamp_out = 0;
\r
563 int timestamp_complained;
\r
564 if (! logger->line_length) {
\r
565 write_timestamp(logger, charsize, ×tamp_out, ×tamp_complained);
\r
566 logger->line_length += (__int64) timestamp_out;
\r
567 *out += timestamp_out;
\r
568 *complained |= timestamp_complained;
\r
572 void *line = address;
\r
573 unsigned long offset = 0;
\r
575 for (i = 0; i < bufsize; i++) {
\r
576 if (((char *) address)[i] == '\n') {
\r
577 ret = try_write(logger, line, i - offset + 1, &log_out, &log_complained);
\r
578 line = (void *) ((char *) line + i - offset + 1);
\r
579 logger->line_length = 0LL;
\r
581 *complained |= log_complained;
\r
583 if (offset < bufsize) {
\r
584 write_timestamp(logger, charsize, ×tamp_out, ×tamp_complained);
\r
585 logger->line_length += (__int64) timestamp_out;
\r
586 *out += timestamp_out;
\r
587 *complained |= timestamp_complained;
\r
592 if (offset < bufsize) {
\r
593 ret = try_write(logger, line, bufsize - offset, &log_out, &log_complained);
\r
595 *complained |= log_complained;
\r
600 else return try_write(logger, address, bufsize, out, complained);
\r
603 /* Wrapper to be called in a new thread for logging. */
\r
604 unsigned long WINAPI log_and_rotate(void *arg) {
\r
605 logger_t *logger = (logger_t *) arg;
\r
606 if (! logger) return 1;
\r
609 BY_HANDLE_FILE_INFORMATION info;
\r
611 /* Find initial file size. */
\r
612 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
615 l.HighPart = info.nFileSizeHigh;
\r
616 l.LowPart = info.nFileSizeLow;
\r
622 unsigned long in, out;
\r
623 unsigned long charsize = 0;
\r
624 unsigned long error;
\r
626 int complained = 0;
\r
629 /* Read data from the pipe. */
\r
631 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
633 close_handle(&logger->read_handle);
\r
634 close_handle(&logger->write_handle);
\r
635 HeapFree(GetProcessHeap(), 0, logger);
\r
638 else if (ret) continue;
\r
640 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
641 /* Look for newline. */
\r
643 for (i = 0; i < in; i++) {
\r
644 if (buffer[i] == '\n') {
\r
645 if (! charsize) charsize = guess_charsize(address, in);
\r
648 /* Write up to the newline. */
\r
649 ret = try_write(logger, address, i, &out, &complained);
\r
651 close_handle(&logger->read_handle);
\r
652 close_handle(&logger->write_handle);
\r
653 HeapFree(GetProcessHeap(), 0, logger);
\r
656 size += (__int64) out;
\r
659 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
660 TCHAR rotated[PATH_LENGTH];
\r
661 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
664 Ideally we'd try the rename first then close the handle but
\r
665 MoveFile() will fail if the handle is still open so we must
\r
666 risk losing everything.
\r
668 if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);
\r
669 close_handle(&logger->write_handle);
\r
672 if (logger->copy_and_truncate) {
\r
673 function = _T("CopyFile()");
\r
674 if (CopyFile(logger->path, rotated, TRUE)) {
\r
675 HANDLE file = write_to_file(logger->path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
676 Sleep(logger->rotate_delay);
\r
677 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
678 SetEndOfFile(file);
\r
684 function = _T("MoveFile()");
\r
685 if (! MoveFile(logger->path, rotated)) ok = false;
\r
688 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
692 error = GetLastError();
\r
693 if (error != ERROR_FILE_NOT_FOUND) {
\r
694 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
695 complained |= COMPLAINED_ROTATE;
\r
696 /* We can at least try to re-open the existing file. */
\r
697 logger->disposition = OPEN_ALWAYS;
\r
702 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
703 if (logger->write_handle == INVALID_HANDLE_VALUE) {
\r
704 error = GetLastError();
\r
705 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
706 /* Oh dear. Now we can't log anything further. */
\r
707 close_handle(&logger->read_handle);
\r
708 close_handle(&logger->write_handle);
\r
709 HeapFree(GetProcessHeap(), 0, logger);
\r
713 /* Resume writing after the newline. */
\r
714 address = (void *) ((char *) address + i);
\r
720 if (! size || logger->timestamp_log) if (! charsize) charsize = guess_charsize(address, in);
\r
722 /* Write a BOM to the new file. */
\r
723 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
724 size += (__int64) out;
\r
727 /* Write the data, if any. */
\r
728 if (! in) continue;
\r
730 ret = write_with_timestamp(logger, address, in, &out, &complained, charsize);
\r
731 size += (__int64) out;
\r
733 close_handle(&logger->read_handle);
\r
734 close_handle(&logger->write_handle);
\r
735 HeapFree(GetProcessHeap(), 0, logger);
\r
740 close_handle(&logger->read_handle);
\r
741 close_handle(&logger->write_handle);
\r
742 HeapFree(GetProcessHeap(), 0, logger);
\r