Tidy up French GUI.
[nssm.git] / io.cpp
diff --git a/io.cpp b/io.cpp
index 3460cdc..dd2641c 100644 (file)
--- a/io.cpp
+++ b/io.cpp
@@ -3,6 +3,8 @@
 #define COMPLAINED_READ (1 << 0)\r
 #define COMPLAINED_WRITE (1 << 1)\r
 #define COMPLAINED_ROTATE (1 << 2)\r
+#define TIMESTAMP_FORMAT "%04u-%02u-%02u %02u:%02u:%02u.%03u: "\r
+#define TIMESTAMP_LEN 25\r
 \r
 static int dup_handle(HANDLE source_handle, HANDLE *dest_handle_ptr, TCHAR *source_description, TCHAR *dest_description, unsigned long flags) {\r
   if (! dest_handle_ptr) return 1;\r
@@ -23,7 +25,7 @@ static int dup_handle(HANDLE source_handle, HANDLE *dest_handle_ptr, TCHAR *sour
   pipe_handle:  stdout of application\r
   write_handle: to file\r
 */\r
-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
+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
   *tid_ptr = 0;\r
 \r
   /* Pipe between application's stdout/stderr and our logging handle. */\r
@@ -58,6 +60,8 @@ static HANDLE create_logging_thread(TCHAR *service_name, TCHAR *path, unsigned l
   logger->write_handle = *write_handle_ptr;\r
   logger->size = (__int64) size.QuadPart;\r
   logger->tid_ptr = tid_ptr;\r
+  logger->timestamp_log = timestamp_log;\r
+  logger->line_length = 0;\r
   logger->rotate_online = rotate_online;\r
   logger->rotate_delay = rotate_delay;\r
   logger->copy_and_truncate = copy_and_truncate;\r
@@ -323,7 +327,7 @@ int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
 \r
     if (service->use_stdout_pipe) {\r
       service->stdout_pipe = si->hStdOutput = 0;\r
-      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
+      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
       if (! service->stdout_thread) {\r
         CloseHandle(service->stdout_pipe);\r
         CloseHandle(service->stdout_si);\r
@@ -336,12 +340,7 @@ int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
       service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;\r
     }\r
 \r
-    if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_si"), _T("stdout"))) {\r
-      if (service->stdout_thread) {\r
-        CloseHandle(service->stdout_thread);\r
-        service->stdout_thread = 0;\r
-      }\r
-    }\r
+    if (dup_handle(service->stdout_si, &si->hStdOutput, _T("stdout_si"), _T("stdout"))) close_handle(&service->stdout_thread);\r
   }\r
 \r
   /* stderr */\r
@@ -365,7 +364,7 @@ int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
 \r
       if (service->use_stderr_pipe) {\r
         service->stderr_pipe = si->hStdError = 0;\r
-        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
+        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
         if (! service->stderr_thread) {\r
           CloseHandle(service->stderr_pipe);\r
           CloseHandle(service->stderr_si);\r
@@ -379,12 +378,7 @@ int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
       }\r
     }\r
 \r
-    if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_si"), _T("stderr"))) {\r
-      if (service->stderr_thread) {\r
-        CloseHandle(service->stderr_thread);\r
-        service->stderr_thread = 0;\r
-      }\r
-    }\r
+    if (dup_handle(service->stderr_si, &si->hStdError, _T("stderr_si"), _T("stderr"))) close_handle(&service->stderr_thread);\r
   }\r
 \r
   /*\r
@@ -438,6 +432,24 @@ void close_output_handles(STARTUPINFO *si) {
   if (si->hStdError) CloseHandle(si->hStdError);\r
 }\r
 \r
+void cleanup_loggers(nssm_service_t *service) {\r
+  unsigned long interval = NSSM_CLEANUP_LOGGERS_DEADLINE;\r
+  HANDLE thread_handle = INVALID_HANDLE_VALUE;\r
+\r
+  close_handle(&service->stdout_thread, &thread_handle);\r
+  /* Close write end of the data pipe so logging thread can finalise read. */\r
+  close_handle(&service->stdout_si);\r
+  /* Await logging thread then close read end. */\r
+  if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);\r
+  close_handle(&service->stdout_pipe);\r
+\r
+  thread_handle = INVALID_HANDLE_VALUE;\r
+  close_handle(&service->stderr_thread, &thread_handle);\r
+  close_handle(&service->stderr_si);\r
+  if (thread_handle != INVALID_HANDLE_VALUE) WaitForSingleObject(thread_handle, interval);\r
+  close_handle(&service->stderr_pipe);\r
+}\r
+\r
 /*\r
   Try multiple times to read from a file.\r
   Returns:  0 on success.\r
@@ -525,6 +537,69 @@ complain_write:
   return ret;\r
 }\r
 \r
+/* Note that the timestamp is created in UTF-8. */\r
+static inline int write_timestamp(logger_t *logger, unsigned long charsize, unsigned long *out, int *complained) {\r
+  char timestamp[TIMESTAMP_LEN + 1];\r
+\r
+  SYSTEMTIME now;\r
+  GetSystemTime(&now);\r
+  _snprintf_s(timestamp, _countof(timestamp), _TRUNCATE, TIMESTAMP_FORMAT, now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond, now.wMilliseconds);\r
+\r
+  if (charsize == sizeof(char)) return try_write(logger, (void *) timestamp, TIMESTAMP_LEN, out, complained);\r
+\r
+  wchar_t *utf16;\r
+  unsigned long utf16len;\r
+  if (to_utf16(timestamp, &utf16, &utf16len)) return -1;\r
+  int ret = try_write(logger, (void *) *utf16, utf16len * sizeof(wchar_t), out, complained);\r
+  HeapFree(GetProcessHeap(), 0, utf16);\r
+  return ret;\r
+}\r
+\r
+static int write_with_timestamp(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained, unsigned long charsize) {\r
+  if (logger->timestamp_log) {\r
+    unsigned long log_out;\r
+    int log_complained;\r
+    unsigned long timestamp_out = 0;\r
+    int timestamp_complained;\r
+    if (! logger->line_length) {\r
+      write_timestamp(logger, charsize, &timestamp_out, &timestamp_complained);\r
+      logger->line_length += (__int64) timestamp_out;\r
+      *out += timestamp_out;\r
+      *complained |= timestamp_complained;\r
+    }\r
+\r
+    unsigned long i;\r
+    void *line = address;\r
+    unsigned long offset = 0;\r
+    int ret;\r
+    for (i = 0; i < bufsize; i++) {\r
+      if (((char *) address)[i] == '\n') {\r
+        ret = try_write(logger, line, i - offset + 1, &log_out, &log_complained);\r
+        line = (void *) ((char *) line + i - offset + 1);\r
+        logger->line_length = 0LL;\r
+        *out += log_out;\r
+        *complained |= log_complained;\r
+        offset = i + 1;\r
+        if (offset < bufsize) {\r
+          write_timestamp(logger, charsize, &timestamp_out, &timestamp_complained);\r
+          logger->line_length += (__int64) timestamp_out;\r
+          *out += timestamp_out;\r
+          *complained |= timestamp_complained;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (offset < bufsize) {\r
+      ret = try_write(logger, line, bufsize - offset, &log_out, &log_complained);\r
+      *out += log_out;\r
+      *complained |= log_complained;\r
+    }\r
+\r
+    return ret;\r
+  }\r
+  else return try_write(logger, address, bufsize, out, complained);\r
+}\r
+\r
 /* Wrapper to be called in a new thread for logging. */\r
 unsigned long WINAPI log_and_rotate(void *arg) {\r
   logger_t *logger = (logger_t *) arg;\r
@@ -555,8 +630,8 @@ unsigned long WINAPI log_and_rotate(void *arg) {
     address = &buffer;\r
     ret = try_read(logger, address, sizeof(buffer), &in, &complained);\r
     if (ret < 0) {\r
-      CloseHandle(logger->read_handle);\r
-      CloseHandle(logger->write_handle);\r
+      close_handle(&logger->read_handle);\r
+      close_handle(&logger->write_handle);\r
       HeapFree(GetProcessHeap(), 0, logger);\r
       return 2;\r
     }\r
@@ -573,8 +648,8 @@ unsigned long WINAPI log_and_rotate(void *arg) {
           /* Write up to the newline. */\r
           ret = try_write(logger, address, i, &out, &complained);\r
           if (ret < 0) {\r
-            CloseHandle(logger->read_handle);\r
-            CloseHandle(logger->write_handle);\r
+            close_handle(&logger->read_handle);\r
+            close_handle(&logger->write_handle);\r
             HeapFree(GetProcessHeap(), 0, logger);\r
             return 3;\r
           }\r
@@ -591,7 +666,7 @@ unsigned long WINAPI log_and_rotate(void *arg) {
             risk losing everything.\r
           */\r
           if (logger->copy_and_truncate) FlushFileBuffers(logger->write_handle);\r
-          CloseHandle(logger->write_handle);\r
+          close_handle(&logger->write_handle);\r
           bool ok = true;\r
           TCHAR *function;\r
           if (logger->copy_and_truncate) {\r
@@ -629,8 +704,8 @@ unsigned long WINAPI log_and_rotate(void *arg) {
             error = GetLastError();\r
             log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);\r
             /* Oh dear.  Now we can't log anything further. */\r
-            CloseHandle(logger->read_handle);\r
-            CloseHandle(logger->write_handle);\r
+            close_handle(&logger->read_handle);\r
+            close_handle(&logger->write_handle);\r
             HeapFree(GetProcessHeap(), 0, logger);\r
             return 4;\r
           }\r
@@ -642,9 +717,9 @@ unsigned long WINAPI log_and_rotate(void *arg) {
       }\r
     }\r
 \r
+    if (! size || logger->timestamp_log) if (! charsize) charsize = guess_charsize(address, in);\r
     if (! size) {\r
       /* Write a BOM to the new file. */\r
-      if (! charsize) charsize = guess_charsize(address, in);\r
       if (charsize == sizeof(wchar_t)) write_bom(logger, &out);\r
       size += (__int64) out;\r
     }\r
@@ -652,18 +727,18 @@ unsigned long WINAPI log_and_rotate(void *arg) {
     /* Write the data, if any. */\r
     if (! in) continue;\r
 \r
-    ret = try_write(logger, address, in, &out, &complained);\r
+    ret = write_with_timestamp(logger, address, in, &out, &complained, charsize);\r
     size += (__int64) out;\r
     if (ret < 0) {\r
-      CloseHandle(logger->read_handle);\r
-      CloseHandle(logger->write_handle);\r
+      close_handle(&logger->read_handle);\r
+      close_handle(&logger->write_handle);\r
       HeapFree(GetProcessHeap(), 0, logger);\r
       return 3;\r
     }\r
   }\r
 \r
-  CloseHandle(logger->read_handle);\r
-  CloseHandle(logger->write_handle);\r
+  close_handle(&logger->read_handle);\r
+  close_handle(&logger->write_handle);\r
   HeapFree(GetProcessHeap(), 0, logger);\r
   return 0;\r
 }\r