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
308 bool inherit_handles = false;
\r
310 /* Allocate a new console so we get a fresh stdin, stdout and stderr. */
\r
311 alloc_console(service);
\r
314 if (service->stdin_path[0]) {
\r
315 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
\r
316 if (si->hStdInput == INVALID_HANDLE_VALUE) {
\r
317 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
321 inherit_handles = true;
\r\r
325 if (service->stdout_path[0]) {
\r
326 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
327 HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
328 if (stdout_handle == INVALID_HANDLE_VALUE) return 4;
\r
329 service->stdout_si = 0;
\r
331 if (service->use_stdout_pipe) {
\r
332 service->stdout_pipe = si->hStdOutput = 0;
\r
333 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
334 if (! service->stdout_thread) {
\r
335 CloseHandle(service->stdout_pipe);
\r
336 CloseHandle(service->stdout_si);
\r
339 else service->stdout_thread = 0;
\r
341 if (! service->stdout_thread) {
\r
342 if (dup_handle(stdout_handle, &service->stdout_si, NSSM_REG_STDOUT, _T("stdout"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 4;
\r
343 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
346 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_si"), _T("stdout"))) close_handle(&service->stdout_thread);
\r
348 inherit_handles = true;
\r\r
352 if (service->stderr_path[0]) {
\r
353 /* Same as stdout? */
\r
354 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
355 service->stderr_sharing = service->stdout_sharing;
\r
356 service->stderr_disposition = service->stdout_disposition;
\r
357 service->stderr_flags = service->stdout_flags;
\r
358 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
360 /* Two handles to the same file will create a race. */
\r
361 /* XXX: Here we assume that either both or neither handle must be a pipe. */
\r
362 if (dup_handle(service->stdout_si, &service->stderr_si, _T("stdout"), _T("stderr"))) return 6;
\r
365 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
366 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
367 if (stderr_handle == INVALID_HANDLE_VALUE) return 7;
\r
368 service->stderr_si = 0;
\r
370 if (service->use_stderr_pipe) {
\r
371 service->stderr_pipe = si->hStdError = 0;
\r
372 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
373 if (! service->stderr_thread) {
\r
374 CloseHandle(service->stderr_pipe);
\r
375 CloseHandle(service->stderr_si);
\r
378 else service->stderr_thread = 0;
\r
380 if (! service->stderr_thread) {
\r
381 if (dup_handle(stderr_handle, &service->stderr_si, NSSM_REG_STDERR, _T("stderr"), DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) return 7;
\r
382 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
386 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_si"), _T("stderr"))) close_handle(&service->stderr_thread);
\r
388 inherit_handles = true;
\r\r
392 We need to set the startup_info flags to make the new handles
\r
393 inheritable by the new process.
\r
395 if (inherit_handles) si->dwFlags |= STARTF_USESTDHANDLES;
\r
400 /* Reuse output handles for a hook. */
\r
401 int use_output_handles(nssm_service_t *service, STARTUPINFO *si) {
\r
402 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
404 if (service->stdout_si) {
\r
405 if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_pipe"), _T("hStdOutput"))) return 1;
\r
406 si->dwFlags |= STARTF_USESTDHANDLES;
\r
409 if (service->stderr_si) {
\r
410 if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_pipe"), _T("hStdError"))) {
\r
411 if (si->hStdOutput) {
\r
412 si->dwFlags &= ~STARTF_USESTDHANDLES;
\r
413 CloseHandle(si->hStdOutput);
\r
417 si->dwFlags |= STARTF_USESTDHANDLES;
\r
423 void close_output_handles(STARTUPINFO *si) {
\r
424 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
425 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
426 if (si->hStdError) CloseHandle(si->hStdError);
\r
429 void cleanup_loggers(nssm_service_t *service) {
\r
430 unsigned long interval = NSSM_CLEANUP_LOGGERS_DEADLINE;
\r
431 HANDLE thread_handle = INVALID_HANDLE_VALUE;
\r
433 close_handle(&service->stdout_thread, &thread_handle);
\r
434 /* Close write end of the data pipe so logging thread can finalise read. */
\r
435 close_handle(&service->stdout_si);
\r
436 /* Await logging thread then close read end. */
\r
437 if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);
\r
438 close_handle(&service->stdout_pipe);
\r
440 thread_handle = INVALID_HANDLE_VALUE;
\r
441 close_handle(&service->stderr_thread, &thread_handle);
\r
442 close_handle(&service->stderr_si);
\r
443 if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);
\r
444 close_handle(&service->stderr_pipe);
\r
448 Try multiple times to read from a file.
\r
449 Returns: 0 on success.
\r
450 1 on non-fatal error.
\r
453 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
455 unsigned long error;
\r
456 for (int tries = 0; tries < 5; tries++) {
\r
457 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
459 error = GetLastError();
\r
461 /* Other end closed the pipe. */
\r
462 case ERROR_BROKEN_PIPE:
\r
464 goto complain_read;
\r
466 /* Couldn't lock the buffer. */
\r
467 case ERROR_NOT_ENOUGH_QUOTA:
\r
468 Sleep(2000 + tries * 3000);
\r
472 /* Write was cancelled by the other end. */
\r
473 case ERROR_OPERATION_ABORTED:
\r
475 goto complain_read;
\r
483 /* Ignore the error if we've been requested to exit anyway. */
\r
484 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
485 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
486 *complained |= COMPLAINED_READ;
\r
491 Try multiple times to write to a file.
\r
492 Returns: 0 on success.
\r
493 1 on non-fatal error.
\r
496 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
498 unsigned long error;
\r
499 for (int tries = 0; tries < 5; tries++) {
\r
500 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
502 error = GetLastError();
\r
503 if (error == ERROR_IO_PENDING) {
\r
504 /* Operation was successful pending flush to disk. */
\r
509 /* Other end closed the pipe. */
\r
510 case ERROR_BROKEN_PIPE:
\r
512 goto complain_write;
\r
514 /* Couldn't lock the buffer. */
\r
515 case ERROR_NOT_ENOUGH_QUOTA:
\r
516 /* Out of disk space. */
\r
517 case ERROR_DISK_FULL:
\r
518 Sleep(2000 + tries * 3000);
\r
523 /* We'll lose this line but try to read and write subsequent ones. */
\r
529 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
530 *complained |= COMPLAINED_WRITE;
\r
534 /* Note that the timestamp is created in UTF-8. */
\r
535 static inline int write_timestamp(logger_t *logger, unsigned long charsize, unsigned long *out, int *complained) {
\r
536 char timestamp[TIMESTAMP_LEN + 1];
\r
539 GetSystemTime(&now);
\r
540 _snprintf_s(timestamp, _countof(timestamp), _TRUNCATE, TIMESTAMP_FORMAT, now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond, now.wMilliseconds);
\r
542 if (charsize == sizeof(char)) return try_write(logger, (void *) timestamp, TIMESTAMP_LEN, out, complained);
\r
545 unsigned long utf16len;
\r
546 if (to_utf16(timestamp, &utf16, &utf16len)) return -1;
\r
547 int ret = try_write(logger, (void *) *utf16, utf16len * sizeof(wchar_t), out, complained);
\r
548 HeapFree(GetProcessHeap(), 0, utf16);
\r
552 static int write_with_timestamp(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained, unsigned long charsize) {
\r
553 if (logger->timestamp_log) {
\r
554 unsigned long log_out;
\r
555 int log_complained;
\r
556 unsigned long timestamp_out = 0;
\r
557 int timestamp_complained;
\r
558 if (! logger->line_length) {
\r
559 write_timestamp(logger, charsize, ×tamp_out, ×tamp_complained);
\r
560 logger->line_length += (__int64) timestamp_out;
\r
561 *out += timestamp_out;
\r
562 *complained |= timestamp_complained;
\r
566 void *line = address;
\r
567 unsigned long offset = 0;
\r
569 for (i = 0; i < bufsize; i++) {
\r
570 if (((char *) address)[i] == '\n') {
\r
571 ret = try_write(logger, line, i - offset + 1, &log_out, &log_complained);
\r
572 line = (void *) ((char *) line + i - offset + 1);
\r
573 logger->line_length = 0LL;
\r
575 *complained |= log_complained;
\r
577 if (offset < bufsize) {
\r
578 write_timestamp(logger, charsize, ×tamp_out, ×tamp_complained);
\r
579 logger->line_length += (__int64) timestamp_out;
\r
580 *out += timestamp_out;
\r
581 *complained |= timestamp_complained;
\r
586 if (offset < bufsize) {
\r
587 ret = try_write(logger, line, bufsize - offset, &log_out, &log_complained);
\r
589 *complained |= log_complained;
\r
594 else return try_write(logger, address, bufsize, out, complained);
\r
597 /* Wrapper to be called in a new thread for logging. */
\r
598 unsigned long WINAPI log_and_rotate(void *arg) {
\r
599 logger_t *logger = (logger_t *) arg;
\r
600 if (! logger) return 1;
\r
603 BY_HANDLE_FILE_INFORMATION info;
\r
605 /* Find initial file size. */
\r
606 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
609 l.HighPart = info.nFileSizeHigh;
\r
610 l.LowPart = info.nFileSizeLow;
\r
616 unsigned long in, out;
\r
617 unsigned long charsize = 0;
\r
618 unsigned long error;
\r
620 int complained = 0;
\r
623 /* Read data from the pipe. */
\r
625 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
627 close_handle(&logger->read_handle);
\r
628 close_handle(&logger->write_handle);
\r
629 HeapFree(GetProcessHeap(), 0, logger);
\r
632 else if (ret) continue;
\r
634 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
635 /* Look for newline. */
\r
637 for (i = 0; i < in; i++) {
\r
638 if (buffer[i] == '\n') {
\r
639 if (! charsize) charsize = guess_charsize(address, in);
\r
642 /* Write up to the newline. */
\r
643 ret = try_write(logger, address, i, &out, &complained);
\r
645 close_handle(&logger->read_handle);
\r
646 close_handle(&logger->write_handle);
\r
647 HeapFree(GetProcessHeap(), 0, logger);
\r
650 size += (__int64) out;
\r
653 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
654 TCHAR rotated[PATH_LENGTH];
\r
655 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
658 Ideally we'd try the rename first then close the handle but
\r
659 MoveFile() will fail if the handle is still open so we must
\r
660 risk losing everything.
\r
662 if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);
\r
663 close_handle(&logger->write_handle);
\r
666 if (logger->copy_and_truncate) {
\r
667 function = _T("CopyFile()");
\r
668 if (CopyFile(logger->path, rotated, TRUE)) {
\r
669 HANDLE file = write_to_file(logger->path, NSSM_STDOUT_SHARING, 0, NSSM_STDOUT_DISPOSITION, NSSM_STDOUT_FLAGS);
\r
670 Sleep(logger->rotate_delay);
\r
671 SetFilePointer(file, 0, 0, FILE_BEGIN);
\r
672 SetEndOfFile(file);
\r
678 function = _T("MoveFile()");
\r
679 if (! MoveFile(logger->path, rotated)) ok = false;
\r
682 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
686 error = GetLastError();
\r
687 if (error != ERROR_FILE_NOT_FOUND) {
\r
688 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
689 complained |= COMPLAINED_ROTATE;
\r
690 /* We can at least try to re-open the existing file. */
\r
691 logger->disposition = OPEN_ALWAYS;
\r
696 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
697 if (logger->write_handle == INVALID_HANDLE_VALUE) {
\r
698 error = GetLastError();
\r
699 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
700 /* Oh dear. Now we can't log anything further. */
\r
701 close_handle(&logger->read_handle);
\r
702 close_handle(&logger->write_handle);
\r
703 HeapFree(GetProcessHeap(), 0, logger);
\r
707 /* Resume writing after the newline. */
\r
708 address = (void *) ((char *) address + i);
\r
714 if (! size || logger->timestamp_log) if (! charsize) charsize = guess_charsize(address, in);
\r
716 /* Write a BOM to the new file. */
\r
717 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
718 size += (__int64) out;
\r
721 /* Write the data, if any. */
\r
722 if (! in) continue;
\r
724 ret = write_with_timestamp(logger, address, in, &out, &complained, charsize);
\r
725 size += (__int64) out;
\r
727 close_handle(&logger->read_handle);
\r
728 close_handle(&logger->write_handle);
\r
729 HeapFree(GetProcessHeap(), 0, logger);
\r
734 close_handle(&logger->read_handle);
\r
735 close_handle(&logger->write_handle);
\r
736 HeapFree(GetProcessHeap(), 0, logger);
\r