[logging] Make fx_log_get_logger() always return a valid pointer

It currently only returns null when duping STDERR_FILENO fails
but we can avoid the dup altogether by not providing a fd at all
and triggering the logger's fallback behaviour.

Bug: 49001
Change-Id: I86702b3b1e23b7812d0cb95f5c3e3a7689f85770
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/377391
Testability-Review: Adam Barth <abarth@google.com>
Commit-Queue: Saman Sami <samans@google.com>
Reviewed-by: Adam Barth <abarth@google.com>
Reviewed-by: Adam Perry <adamperry@google.com>
API-Review: Adam Barth <abarth@google.com>
diff --git a/sdk/lib/syslog/syslog.api b/sdk/lib/syslog/syslog.api
index 31ef4de..9169e6c 100644
--- a/sdk/lib/syslog/syslog.api
+++ b/sdk/lib/syslog/syslog.api
@@ -1,5 +1,5 @@
 {
-  "pkg/syslog/include/lib/syslog/global.h": "3136d8d600ca6b381233584ab12b1141",
-  "pkg/syslog/include/lib/syslog/logger.h": "2abc3a24ebace570e47e40d13c805afd",
+  "pkg/syslog/include/lib/syslog/global.h": "2ce5b4c42fe5f394e4f13e1bc92219ee",
+  "pkg/syslog/include/lib/syslog/logger.h": "b50163886e109aa12f8c971cd89b4501",
   "pkg/syslog/include/lib/syslog/wire_format.h": "d47e76b6b62a4af22f978067c0401e92"
 }
\ No newline at end of file
diff --git a/src/lib/syslog/cpp/logger.cc b/src/lib/syslog/cpp/logger.cc
index d4aedc2..a6dac5e 100644
--- a/src/lib/syslog/cpp/logger.cc
+++ b/src/lib/syslog/cpp/logger.cc
@@ -42,16 +42,10 @@
 
 LogMessage::~LogMessage() {
   fx_logger_t* logger = fx_log_get_logger();
-  if (logger) {
-    if (status_ != INT32_MAX) {
-      stream_ << ": " << status_ << " (" << zx_status_get_string(status_) << ")";
-    }
-
-    fx_logger_log(logger, severity_, tag_, stream_.str().c_str());
-  } else if (severity_ == FX_LOG_FATAL) {
-    // FX_LOG_FATAL should abort regardless of whether a logger exists
-    FxLogFatal();
+  if (status_ != INT32_MAX) {
+    stream_ << ": " << status_ << " (" << zx_status_get_string(status_) << ")";
   }
+  fx_logger_log(logger, severity_, tag_, stream_.str().c_str());
 }
 
 // Note that this implementation allows a data race on counter_, but
diff --git a/zircon/system/ulib/syslog/fx_logger.h b/zircon/system/ulib/syslog/fx_logger.h
index edbbc72..ea322e4 100644
--- a/zircon/system/ulib/syslog/fx_logger.h
+++ b/zircon/system/ulib/syslog/fx_logger.h
@@ -43,6 +43,8 @@
     pid_ = GetCurrentProcessKoid();
     dropped_logs_.store(0, std::memory_order_relaxed);
     Reconfigure(config);
+    if (config->log_service_channel == ZX_HANDLE_INVALID && config->console_fd == -1)
+      ActivateFallback(-1);
   }
 
   ~fx_logger() = default;
diff --git a/zircon/system/ulib/syslog/global.cc b/zircon/system/ulib/syslog/global.cc
index 7333950..8844a6d 100644
--- a/zircon/system/ulib/syslog/global.cc
+++ b/zircon/system/ulib/syslog/global.cc
@@ -27,7 +27,9 @@
                                .tags = &tag,
                                .num_tags = 1};
   fx_logger_t* logger = NULL;
-  fx_logger_create(&config, &logger);
+  status = fx_logger_create(&config, &logger);
+  // Making the default logger should never fail.
+  ZX_DEBUG_ASSERT(status == ZX_OK);
   return logger;
 }
 
@@ -40,18 +42,11 @@
 }
 
 SYSLOG_EXPORT
-zx_status_t fx_log_init(void) {
-  if (!fx_log_get_logger())
-    return ZX_ERR_INTERNAL;
-  return ZX_OK;
-}
+zx_status_t fx_log_init(void) { return ZX_OK; }
 
 SYSLOG_EXPORT
 zx_status_t fx_log_init_with_config(const fx_logger_config_t* config) {
-  fx_logger_t* logger = fx_log_get_logger();
-  if (!logger)
-    return ZX_ERR_INTERNAL;
-  return logger->Reconfigure(config);
+  return fx_log_get_logger()->Reconfigure(config);
 }
 
 // This is here to force a definition to be included here for C99.
diff --git a/zircon/system/ulib/syslog/include/lib/syslog/global.h b/zircon/system/ulib/syslog/include/lib/syslog/global.h
index 3c93477..4c848d2 100644
--- a/zircon/system/ulib/syslog/include/lib/syslog/global.h
+++ b/zircon/system/ulib/syslog/include/lib/syslog/global.h
@@ -13,14 +13,16 @@
 __BEGIN_CDECLS
 
 // Gets the global logger for the process to which log messages emitted
-// using the FX_LOG macros will be written. This function is thread-safe.
+// using the FX_LOG macros will be written. This function returns the same
+// logger on all threads and is thread-safe. The returned pointer is never
+// null and it does not get invalidated when the logger is reconfigured.
 fx_logger_t* fx_log_get_logger(void);
 
 // Returns true if writing messages with the given severity is enabled in the
 // global logger.
 static inline bool fx_log_is_enabled(fx_log_severity_t severity) {
   fx_logger_t* logger = fx_log_get_logger();
-  return logger && severity >= fx_logger_get_min_severity(logger);
+  return severity >= fx_logger_get_min_severity(logger);
 }
 
 // Reconfigures the global logger for this process with the specified
@@ -31,15 +33,13 @@
 // This function is NOT thread-safe and must be called early in the program
 // before other threads are spawned.
 // Returns:
-// - ZX_INTERNAL_ERR if the global logger had failed to instantiate,
 // - ZX_ERR_INVALID_ARGS if config is invalid (i.e. is null or has more than
 //   FX_LOG_MAX_TAGS tags),
 // - ZX_OK if the reconfiguration succeeds.
 // TODO(samans): Rename this function. https://fxbug.dev/49001
 zx_status_t fx_log_init_with_config(const fx_logger_config_t* config);
 
-// Deprecated. Doesn't do anything. Returns ZX_INTERNAL_ERR if the global logger
-// had failed to instantiate, and ZX_OK otherwise.
+// Deprecated. Doesn't do anything and always returns ZX_OK.
 // TODO(samans): Remove this function. https://fxbug.dev/49001
 zx_status_t fx_log_init(void);
 
@@ -67,28 +67,28 @@
 // |verbosity| is positive number. Logger severity is set to -verbosity
 #define FX_LOG_SET_VERBOSITY(verbosity) _FX_LOG_SET_SEVERITY(-(verbosity))
 
-#define _FX_LOG(severity, tag, message)                               \
-  do {                                                                \
-    fx_logger_t* logger = fx_log_get_logger();                        \
-    if (logger && fx_logger_get_min_severity(logger) <= (severity)) { \
-      fx_logger_log(logger, (severity), (tag), (message));            \
-    }                                                                 \
+#define _FX_LOG(severity, tag, message)                     \
+  do {                                                      \
+    fx_logger_t* logger = fx_log_get_logger();              \
+    if (fx_logger_get_min_severity(logger) <= (severity)) { \
+      fx_logger_log(logger, (severity), (tag), (message));  \
+    }                                                       \
   } while (0)
 
 #define _FX_LOGF(severity, tag, message, ...)                            \
   do {                                                                   \
     fx_logger_t* logger = fx_log_get_logger();                           \
-    if (logger && fx_logger_get_min_severity(logger) <= (severity)) {    \
+    if (fx_logger_get_min_severity(logger) <= (severity)) {              \
       fx_logger_logf(logger, (severity), (tag), (message), __VA_ARGS__); \
     }                                                                    \
   } while (0)
 
-#define _FX_LOGVF(severity, tag, message, args)                       \
-  do {                                                                \
-    fx_logger_t* logger = fx_log_get_logger();                        \
-    if (logger && fx_logger_get_min_severity(logger) <= (severity)) { \
-      fx_logger_logvf(logger, (severity), (tag), (message), (args));  \
-    }                                                                 \
+#define _FX_LOGVF(severity, tag, message, args)                      \
+  do {                                                               \
+    fx_logger_t* logger = fx_log_get_logger();                       \
+    if (fx_logger_get_min_severity(logger) <= (severity)) {          \
+      fx_logger_logvf(logger, (severity), (tag), (message), (args)); \
+    }                                                                \
   } while (0)
 
 // Writes a message to the global logger.
diff --git a/zircon/system/ulib/syslog/include/lib/syslog/logger.h b/zircon/system/ulib/syslog/include/lib/syslog/logger.h
index d712ea2..c5cb89e 100644
--- a/zircon/system/ulib/syslog/include/lib/syslog/logger.h
+++ b/zircon/system/ulib/syslog/include/lib/syslog/logger.h
@@ -10,7 +10,6 @@
 
 #include <stdarg.h>
 #include <unistd.h>
-
 #include <zircon/types.h>
 
 // Max no of tags associated with a logger.
@@ -62,7 +61,7 @@
 // Creates a logger object from the specified configuration.
 //
 // This will return ZX_ERR_INVALID_ARGS if |num_tags| is more than
-// |FX_LOG_MAX_TAGS| and return |ZX_ERR_INTERNAL} if dup fails.
+// |FX_LOG_MAX_TAGS|.
 // |config| can be safely deleted after this function returns.
 zx_status_t fx_logger_create(const fx_logger_config_t* config, fx_logger_t** out_logger);
 
diff --git a/zircon/system/ulib/syslog/logger.cc b/zircon/system/ulib/syslog/logger.cc
index 87b879d..cbbd586 100644
--- a/zircon/system/ulib/syslog/logger.cc
+++ b/zircon/system/ulib/syslog/logger.cc
@@ -67,20 +67,14 @@
     return ZX_ERR_INVALID_ARGS;
   }
   fx_logger_config_t c = *config;
-  // In the SYSLOG_STATIC mode, we cannot connect to the logging service. We should continue to
-  // instantiate the logger and the client will provide the appropriate channel / fd later.
+  // In the SYSLOG_STATIC mode, we cannot connect to the logging service. We
+  // should continue to instantiate the logger (which defaults to using stderr)
+  // and the client can provide the appropriate channel / fd later.
 #ifndef SYSLOG_STATIC
   if (config->console_fd == -1 && config->log_service_channel == ZX_HANDLE_INVALID) {
     zx::socket sock = connect_to_logger();
-    if (sock.is_valid()) {
+    if (sock.is_valid())
       c.log_service_channel = sock.release();
-    } else {
-      int newfd = dup(STDERR_FILENO);
-      if (newfd < 0) {
-        return ZX_ERR_INTERNAL;
-      }
-      c.console_fd = newfd;
-    }
   }
 #endif
   *out_logger = new fx_logger(&c);
diff --git a/zircon/system/ulib/syslog/test/syslog_tests.cc b/zircon/system/ulib/syslog/test/syslog_tests.cc
index 8665603..b3c27ab 100644
--- a/zircon/system/ulib/syslog/test/syslog_tests.cc
+++ b/zircon/system/ulib/syslog/test/syslog_tests.cc
@@ -7,6 +7,7 @@
 #include <lib/syslog/global.h>
 #include <lib/zx/socket.h>
 #include <poll.h>
+#include <stdlib.h>
 #include <unistd.h>
 
 #include <unittest/unittest.h>
@@ -28,6 +29,16 @@
   return strcmp(str, suffix) == 0;
 }
 
+static int smallest_unused_fd() {
+  char name[] = "/tmp/syslog_test.XXXXXX";
+  int fd = mkstemp(name);
+  ASSERT_GT(fd, -1);
+  close(fd);
+  int status = remove(name);
+  ASSERT_EQ(0, status);
+  return fd;
+}
+
 bool test_log_init(void) {
   BEGIN_TEST;
   fx_log_reset_global_for_testing();
@@ -304,6 +315,32 @@
   END_TEST;
 }
 
+bool test_log_dont_dup(void) {
+  BEGIN_TEST;
+
+  // Remember the current lowest ununsed fd.
+  int fd = smallest_unused_fd();
+
+  // Create a logger
+  fx_logger_t* logger;
+  zx_status_t status;
+  fx_logger_config_t config = {.min_severity = FX_LOG_INFO,
+                               .console_fd = -1,
+                               .log_service_channel = ZX_HANDLE_INVALID,
+                               .tags = nullptr,
+                               .num_tags = 0};
+  status = fx_logger_create(&config, &logger);
+  ASSERT_EQ(ZX_OK, status);
+
+  // No fd must be taken by the logger.
+  EXPECT_EQ(fd, smallest_unused_fd());
+
+  // Cleanup
+  fx_logger_destroy(logger);
+
+  END_TEST;
+}
+
 BEGIN_TEST_CASE(syslog_tests)
 RUN_TEST(test_log_init)
 RUN_TEST(test_log_init_with_socket)
@@ -318,6 +355,7 @@
 RUN_TEST(test_vlog_simple_write)
 RUN_TEST(test_vlog_write)
 RUN_TEST(test_log_reconfiguration)
+RUN_TEST(test_log_dont_dup)
 END_TEST_CASE(syslog_tests)
 
 BEGIN_TEST_CASE(syslog_tests_edge_cases)