3 #define COMPLAINED_READ (1 << 0)
\r
4 #define COMPLAINED_WRITE (1 << 1)
\r
5 #define COMPLAINED_ROTATE (1 << 2)
\r
7 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 *tid_ptr, unsigned long *rotate_online) {
\r
10 /* Pipe between application's stdout/stderr and our logging handle. */
\r
11 if (read_handle_ptr && ! *read_handle_ptr) {
\r
12 if (pipe_handle_ptr && ! *pipe_handle_ptr) {
\r
13 if (CreatePipe(read_handle_ptr, pipe_handle_ptr, 0, 0)) {
\r
14 SetHandleInformation(*pipe_handle_ptr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
\r
17 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service_name, path, error_string(GetLastError()));
\r
23 logger_t *logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
\r
25 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("logger"), _T("create_logging_thread()"), 0);
\r
29 ULARGE_INTEGER size;
\r
30 size.LowPart = rotate_bytes_low;
\r
31 size.HighPart = rotate_bytes_high;
\r
33 logger->service_name = service_name;
\r
34 logger->path = path;
\r
35 logger->sharing = sharing;
\r
36 logger->disposition = disposition;
\r
37 logger->flags = flags;
\r
38 logger->read_handle = *read_handle_ptr;
\r
39 logger->write_handle = *write_handle_ptr;
\r
40 logger->size = (__int64) size.QuadPart;
\r
41 logger->tid_ptr = tid_ptr;
\r
42 logger->rotate_online = rotate_online;
\r
44 HANDLE thread_handle = CreateThread(NULL, 0, log_and_rotate, (void *) logger, 0, logger->tid_ptr);
\r
45 if (! thread_handle) {
\r
46 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
47 HeapFree(GetProcessHeap(), 0, logger);
\r
50 return thread_handle;
\r
53 static inline unsigned long guess_charsize(void *address, unsigned long bufsize) {
\r
54 if (IsTextUnicode(address, bufsize, 0)) return (unsigned long) sizeof(wchar_t);
\r
55 else return (unsigned long) sizeof(char);
\r
58 static inline void write_bom(logger_t *logger, unsigned long *out) {
\r
59 wchar_t bom = L'\ufeff';
\r
60 if (! WriteFile(logger->write_handle, (void *) &bom, sizeof(bom), out, 0)) {
\r
61 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SOMEBODY_SET_UP_US_THE_BOM, logger->service_name, logger->path, error_string(GetLastError()), 0);
\r
65 /* Get path, share mode, creation disposition and flags for a stream. */
\r
66 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) {
\r
67 TCHAR value[NSSM_STDIO_LENGTH];
\r
70 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
\r
71 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
\r
74 switch (expand_parameter(key, value, path, PATH_LENGTH, true, false)) {
\r
75 case 0: if (! path[0]) return 0; break; /* OK. */
\r
76 default: return 2; /* Error. */
\r
80 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
\r
81 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
\r
84 switch (get_number(key, value, sharing, false)) {
\r
85 case 0: *sharing = default_sharing; break; /* Missing. */
\r
86 case 1: break; /* Found. */
\r
87 case -2: return 4; break; /* Error. */
\r
90 /* CreationDisposition. */
\r
91 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
\r
92 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
\r
95 switch (get_number(key, value, disposition, false)) {
\r
96 case 0: *disposition = default_disposition; break; /* Missing. */
\r
97 case 1: break; /* Found. */
\r
98 case -2: return 6; break; /* Error. */
\r
102 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
\r
103 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
\r
106 switch (get_number(key, value, flags, false)) {
\r
107 case 0: *flags = default_flags; break; /* Missing. */
\r
108 case 1: break; /* Found. */
\r
109 case -2: return 8; break; /* Error. */
\r
115 int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
\r
116 TCHAR value[NSSM_STDIO_LENGTH];
\r
118 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
119 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
\r
123 return set_number(key, value, number);
\r
126 int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
\r
127 TCHAR value[NSSM_STDIO_LENGTH];
\r
129 if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
\r
130 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
\r
134 if (RegDeleteValue(key, value)) return 0;
\r
138 HANDLE write_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
\r
139 HANDLE ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
\r
141 if (SetFilePointer(ret, 0, 0, FILE_END) != INVALID_SET_FILE_POINTER) SetEndOfFile(ret);
\r
145 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
\r
149 static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
\r
156 TCHAR buffer[PATH_LENGTH];
\r
157 memmove(buffer, path, sizeof(buffer));
\r
158 TCHAR *ext = PathFindExtension(buffer);
\r
159 TCHAR extension[PATH_LENGTH];
\r
160 _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
162 _sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
\r
165 void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsigned long low, unsigned long high) {
\r
166 unsigned long error;
\r
170 GetSystemTime(&st);
\r
172 BY_HANDLE_FILE_INFORMATION info;
\r
174 /* Try to open the file to check if it exists and to get attributes. */
\r
175 HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
\r
177 /* Get file attributes. */
\r
178 if (! GetFileInformationByHandle(file, &info)) {
\r
179 /* Reuse current time for rotation timestamp. */
\r
180 seconds = low = high = 0;
\r
181 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
187 error = GetLastError();
\r
188 if (error == ERROR_FILE_NOT_FOUND) return;
\r
189 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
\r
190 /* Reuse current time for rotation timestamp. */
\r
191 seconds = low = high = 0;
\r
192 SystemTimeToFileTime(&st, &info.ftLastWriteTime);
\r
195 /* Check file age. */
\r
198 SystemTimeToFileTime(&st, &ft);
\r
201 s.LowPart = ft.dwLowDateTime;
\r
202 s.HighPart = ft.dwHighDateTime;
\r
203 s.QuadPart -= seconds * 10000000LL;
\r
204 ft.dwLowDateTime = s.LowPart;
\r
205 ft.dwHighDateTime = s.HighPart;
\r
206 if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
\r
209 /* Check file size. */
\r
211 if (info.nFileSizeHigh < high) return;
\r
212 if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
\r
215 /* Get new filename. */
\r
216 FileTimeToSystemTime(&info.ftLastWriteTime, &st);
\r
218 TCHAR rotated[PATH_LENGTH];
\r
219 rotated_filename(path, rotated, _countof(rotated), &st);
\r
222 if (MoveFile(path, rotated)) {
\r
223 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
\r
226 error = GetLastError();
\r
228 if (error == ERROR_FILE_NOT_FOUND) return;
\r
229 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
233 int get_output_handles(nssm_service_t *service, HKEY key, STARTUPINFO *si) {
\r
234 /* Allocate a new console so we get a fresh stdin, stdout and stderr. */
\r
235 if (si && ! service->no_console) {
\r
240 /* Set a title like "[NSSM] Jenkins" */
\r
241 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
242 unsigned long len = _countof(displayname);
\r
243 SC_HANDLE services = open_service_manager();
\r
245 if (! GetServiceDisplayName(services, service->name, displayname, &len)) ZeroMemory(displayname, sizeof(displayname));
\r
246 CloseServiceHandle(services);
\r
248 if (! displayname[0]) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name);
\r
250 TCHAR title[65535];
\r
251 _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s"), NSSM, displayname);
\r
252 SetConsoleTitle(title);
\r
256 if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS)) {
\r
257 service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
\r
258 ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
\r
261 if (si && service->stdin_path[0]) {
\r
262 si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
\r
263 if (! si->hStdInput) {
\r
264 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
\r
270 if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS)) {
\r
271 service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
\r
272 ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
\r
275 if (si && service->stdout_path[0]) {
\r
276 if (service->rotate_files) rotate_file(service->name, service->stdout_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
277 HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
\r
278 if (! stdout_handle) return 4;
\r
280 if (service->rotate_files && service->rotate_stdout_online) {
\r
281 service->stdout_pipe = si->hStdOutput = 0;
\r
282 service->stdout_thread = create_logging_thread(service->name, service->stdout_path, service->stdout_sharing, service->stdout_disposition, service->stdout_flags, &service->stdout_pipe, &si->hStdOutput, &stdout_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stdout_tid, &service->rotate_stdout_online);
\r
283 if (! service->stdout_thread) {
\r
284 CloseHandle(service->stdout_pipe);
\r
285 CloseHandle(si->hStdOutput);
\r
288 else service->stdout_thread = 0;
\r
290 if (! service->stdout_thread) {
\r
291 if (! DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
292 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stdout"), error_string(GetLastError()), 0);
\r
295 service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
\r
300 if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stderr_sharing, NSSM_STDERR_SHARING, &service->stderr_disposition, NSSM_STDERR_DISPOSITION, &service->stderr_flags, NSSM_STDERR_FLAGS)) {
\r
301 service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
\r
302 ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
\r
305 if (service->stderr_path[0]) {
\r
306 /* Same as stdout? */
\r
307 if (str_equiv(service->stderr_path, service->stdout_path)) {
\r
308 service->stderr_sharing = service->stdout_sharing;
\r
309 service->stderr_disposition = service->stdout_disposition;
\r
310 service->stderr_flags = service->stdout_flags;
\r
311 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
314 /* Two handles to the same file will create a race. */
\r
315 if (! DuplicateHandle(GetCurrentProcess(), si->hStdOutput, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
316 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stderr"), error_string(GetLastError()), 0);
\r
322 if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
\r
323 HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
\r
324 if (! stderr_handle) return 7;
\r
326 if (service->rotate_files && service->rotate_stderr_online) {
\r
327 service->stderr_pipe = si->hStdError = 0;
\r
328 service->stderr_thread = create_logging_thread(service->name, service->stderr_path, service->stderr_sharing, service->stderr_disposition, service->stderr_flags, &service->stderr_pipe, &si->hStdError, &stderr_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stderr_tid, &service->rotate_stderr_online);
\r
329 if (! service->stderr_thread) {
\r
330 CloseHandle(service->stderr_pipe);
\r
331 CloseHandle(si->hStdError);
\r
334 else service->stderr_thread = 0;
\r
336 if (! service->stderr_thread) {
\r
337 if (! DuplicateHandle(GetCurrentProcess(), stderr_handle, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
\r
338 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDERR, _T("stderr"), error_string(GetLastError()), 0);
\r
341 service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
346 if (! si) return 0;
\r
349 We need to set the startup_info flags to make the new handles
\r
350 inheritable by the new process.
\r
352 si->dwFlags |= STARTF_USESTDHANDLES;
\r
354 if (service->no_console) return 0;
\r
356 /* Redirect other handles. */
\r
357 if (! si->hStdInput) {
\r
358 if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &si->hStdInput, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
359 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_INPUT_HANDLE"), _T("stdin"), error_string(GetLastError()), 0);
\r
363 if (! si->hStdOutput) {
\r
364 if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
365 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_OUTPUT_HANDLE"), _T("stdout"), error_string(GetLastError()), 0);
\r
369 if (! si->hStdError) {
\r
370 if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE), GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
\r
371 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_ERROR_HANDLE"), _T("stderr"), error_string(GetLastError()), 0);
\r
379 void close_output_handles(STARTUPINFO *si) {
\r
380 if (si->hStdInput) CloseHandle(si->hStdInput);
\r
381 if (si->hStdOutput) CloseHandle(si->hStdOutput);
\r
382 if (si->hStdError) CloseHandle(si->hStdError);
\r
386 Try multiple times to read from a file.
\r
387 Returns: 0 on success.
\r
388 1 on non-fatal error.
\r
391 static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
\r
393 unsigned long error;
\r
394 for (int tries = 0; tries < 5; tries++) {
\r
395 if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
\r
397 error = GetLastError();
\r
399 /* Other end closed the pipe. */
\r
400 case ERROR_BROKEN_PIPE:
\r
402 goto complain_read;
\r
404 /* Couldn't lock the buffer. */
\r
405 case ERROR_NOT_ENOUGH_QUOTA:
\r
406 Sleep(2000 + tries * 3000);
\r
410 /* Write was cancelled by the other end. */
\r
411 case ERROR_OPERATION_ABORTED:
\r
413 goto complain_read;
\r
421 /* Ignore the error if we've been requested to exit anyway. */
\r
422 if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
\r
423 if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
424 *complained |= COMPLAINED_READ;
\r
429 Try multiple times to write to a file.
\r
430 Returns: 0 on success.
\r
431 1 on non-fatal error.
\r
434 static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
\r
436 unsigned long error;
\r
437 for (int tries = 0; tries < 5; tries++) {
\r
438 if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
\r
440 error = GetLastError();
\r
441 if (error == ERROR_IO_PENDING) {
\r
442 /* Operation was successful pending flush to disk. */
\r
447 /* Other end closed the pipe. */
\r
448 case ERROR_BROKEN_PIPE:
\r
450 goto complain_write;
\r
452 /* Couldn't lock the buffer. */
\r
453 case ERROR_NOT_ENOUGH_QUOTA:
\r
454 /* Out of disk space. */
\r
455 case ERROR_DISK_FULL:
\r
456 Sleep(2000 + tries * 3000);
\r
461 /* We'll lose this line but try to read and write subsequent ones. */
\r
467 if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
\r
468 *complained |= COMPLAINED_WRITE;
\r
472 /* Wrapper to be called in a new thread for logging. */
\r
473 unsigned long WINAPI log_and_rotate(void *arg) {
\r
474 logger_t *logger = (logger_t *) arg;
\r
475 if (! logger) return 1;
\r
478 BY_HANDLE_FILE_INFORMATION info;
\r
480 /* Find initial file size. */
\r
481 if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
\r
484 l.HighPart = info.nFileSizeHigh;
\r
485 l.LowPart = info.nFileSizeLow;
\r
491 unsigned long in, out;
\r
492 unsigned long charsize = 0;
\r
493 unsigned long error;
\r
495 int complained = 0;
\r
498 /* Read data from the pipe. */
\r
500 ret = try_read(logger, address, sizeof(buffer), &in, &complained);
\r
502 CloseHandle(logger->read_handle);
\r
503 CloseHandle(logger->write_handle);
\r
504 HeapFree(GetProcessHeap(), 0, logger);
\r
507 else if (ret) continue;
\r
509 if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
\r
510 /* Look for newline. */
\r
512 for (i = 0; i < in; i++) {
\r
513 if (buffer[i] == '\n') {
\r
514 if (! charsize) charsize = guess_charsize(address, in);
\r
517 /* Write up to the newline. */
\r
518 ret = try_write(logger, address, i, &out, &complained);
\r
520 HeapFree(GetProcessHeap(), 0, logger);
\r
521 CloseHandle(logger->read_handle);
\r
522 CloseHandle(logger->write_handle);
\r
525 size += (__int64) out;
\r
528 *logger->rotate_online = NSSM_ROTATE_ONLINE;
\r
529 TCHAR rotated[PATH_LENGTH];
\r
530 rotated_filename(logger->path, rotated, _countof(rotated), 0);
\r
533 Ideally we'd try the rename first then close the handle but
\r
534 MoveFile() will fail if the handle is still open so we must
\r
535 risk losing everything.
\r
537 CloseHandle(logger->write_handle);
\r
538 if (MoveFile(logger->path, rotated)) {
\r
539 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
\r
543 error = GetLastError();
\r
544 if (error != ERROR_FILE_NOT_FOUND) {
\r
545 if (! (complained & COMPLAINED_ROTATE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, logger->service_name, logger->path, _T("MoveFile()"), rotated, error_string(error), 0);
\r
546 complained |= COMPLAINED_ROTATE;
\r
547 /* We can at least try to re-open the existing file. */
\r
548 logger->disposition = OPEN_ALWAYS;
\r
553 logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
\r
554 if (! logger->write_handle) {
\r
555 error = GetLastError();
\r
556 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
\r
557 /* Oh dear. Now we can't log anything further. */
\r
558 HeapFree(GetProcessHeap(), 0, logger);
\r
559 CloseHandle(logger->read_handle);
\r
560 CloseHandle(logger->write_handle);
\r
564 /* Resume writing after the newline. */
\r
565 address = (void *) ((char *) address + i);
\r
572 /* Write a BOM to the new file. */
\r
573 if (! charsize) charsize = guess_charsize(address, in);
\r
574 if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
\r
575 size += (__int64) out;
\r
578 /* Write the data, if any. */
\r
579 if (! in) continue;
\r
581 ret = try_write(logger, address, in, &out, &complained);
\r
582 size += (__int64) out;
\r
584 HeapFree(GetProcessHeap(), 0, logger);
\r
585 CloseHandle(logger->read_handle);
\r
586 CloseHandle(logger->write_handle);
\r
591 HeapFree(GetProcessHeap(), 0, logger);
\r
592 CloseHandle(logger->read_handle);
\r
593 CloseHandle(logger->write_handle);
\r