| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <fidl/fuchsia.logger/cpp/wire.h> |
| #include <lib/syslog/global.h> |
| #include <lib/zx/socket.h> |
| #include <poll.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <zircon/errors.h> |
| |
| #include <climits> |
| |
| #include <fbl/string_printf.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "zircon/system/ulib/syslog/helpers.h" |
| |
| namespace { |
| |
| const char* kFileName = syslog::internal::StripPath(__FILE__); |
| const char* kFilePath = syslog::internal::StripDots(__FILE__); |
| |
| constexpr std::array<const char*, FX_LOG_MAX_TAGS + 1> kTooManyTags = {"1", "2", "3", "4", "5"}; |
| |
| void smallest_unused_fd(int* fd_out) { |
| for (int fd = 0; fd < INT_MAX; ++fd) { |
| if (fcntl(fd, F_GETFD, nullptr) < 0) { |
| ASSERT_EQ(errno, EBADF, "%s", strerror(errno)); |
| *fd_out = fd; |
| return; |
| } |
| } |
| FAIL("did not find unused FD"); |
| } |
| |
| // Ensure accessing the global logger is safe when a global object is being torn down. |
| class LogDuringTeardownTest { |
| public: |
| ~LogDuringTeardownTest() { |
| // This should not crash. |
| FX_LOG(INFO, NULL, "message"); |
| } |
| } g_log_during_teardown; |
| |
| TEST(SyslogTests, test_log_init_with_socket) { |
| zx::socket socket0, socket1; |
| EXPECT_OK(zx::socket::create(0, &socket0, &socket1)); |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_socket = socket1.release(), |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| EXPECT_OK(fx_log_reconfigure(&config), ""); |
| } |
| |
| TEST(SyslogTests, test_log_enabled_macro) { |
| zx::socket socket0, socket1; |
| EXPECT_OK(zx::socket::create(0, &socket0, &socket1)); |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_socket = socket1.release(), |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| EXPECT_OK(fx_log_reconfigure(&config), ""); |
| if (FX_VLOG_IS_ENABLED(4)) { |
| EXPECT_TRUE(false, "control should not reach this line"); |
| } |
| if (!FX_LOG_IS_ENABLED(INFO)) { |
| EXPECT_TRUE(false, "control should not reach this line"); |
| } |
| if (!FX_LOG_IS_ENABLED(ERROR)) { |
| EXPECT_TRUE(false, "control should not reach this line"); |
| } |
| } |
| |
| TEST(SyslogTestsEdgeCases, test_global_tag_limit) { |
| fx_logger_config_t config = { |
| .num_tags = FX_LOG_MAX_TAGS + 1, |
| }; |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_log_reconfigure(&config)); |
| } |
| |
| TEST(SyslogTests, test_log_dont_dup) { |
| // Remember the current lowest ununsed fd. |
| int fd_before; |
| ASSERT_NO_FATAL_FAILURE(smallest_unused_fd(&fd_before)); |
| |
| // Create a logger |
| fx_logger_t* logger; |
| zx_status_t status; |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_socket = ZX_HANDLE_INVALID, |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| status = fx_logger_create(&config, &logger); |
| ASSERT_OK(status); |
| |
| // No fd must be taken by the logger. |
| int fd_after; |
| ASSERT_NO_FATAL_FAILURE(smallest_unused_fd(&fd_after)); |
| EXPECT_EQ(fd_before, fd_after); |
| |
| // Cleanup |
| fx_logger_destroy(logger); |
| } |
| |
| TEST(SyslogTests, test_fx_logger_create_with_null_config) { |
| fx_logger_t* logger; |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_logger_create(nullptr, &logger)); |
| } |
| |
| TEST(SyslogTests, test_fx_logger_create_with_null_output_pointer) { |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_socket = ZX_HANDLE_INVALID, |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_logger_create(&config, nullptr)); |
| } |
| |
| TEST(SyslogTests, test_fx_logger_reconfigure_with_null_config) { |
| // Create a logger |
| fx_logger_t* logger; |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_socket = ZX_HANDLE_INVALID, |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| ASSERT_OK(fx_logger_create(&config, &logger)); |
| |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_logger_reconfigure(logger, nullptr)); |
| |
| fx_logger_destroy(logger); |
| } |
| |
| TEST(SyslogTests, test_log_sink_channel_closed_on_create_fail) { |
| EXPECT_LT(FX_LOG_MAX_TAGS, kTooManyTags.size()); |
| |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_handle = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = passed_handle, |
| .log_sink_socket = ZX_HANDLE_INVALID, |
| .tags = kTooManyTags.data(), |
| .num_tags = kTooManyTags.size(), |
| }; |
| |
| // This should fail because there are too many tags, and closing the |
| // handle should fail because it is already closed. |
| fx_logger_t* logger; |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_logger_create(&config, &logger)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_handle); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_handle)); |
| } |
| |
| TEST(SyslogTests, test_log_sink_socket_closed_on_create_fail) { |
| EXPECT_LT(FX_LOG_MAX_TAGS, kTooManyTags.size()); |
| |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_handle = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = ZX_HANDLE_INVALID, |
| .log_sink_socket = passed_handle, |
| .tags = kTooManyTags.data(), |
| .num_tags = kTooManyTags.size(), |
| }; |
| |
| // This should fail because there are too many tags, and closing the |
| // handle should fail because it is already closed. |
| fx_logger_t* logger; |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_logger_create(&config, &logger)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_handle); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_handle)); |
| } |
| |
| TEST(SyslogTests, test_both_handles_specified_fails_create_and_handles_closed) { |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_log_sink_channel = local.release(); |
| const zx_handle_t passed_log_sink_socket = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = passed_log_sink_channel, |
| .log_sink_socket = passed_log_sink_socket, |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| |
| // This should fail because both handles were specified, and closing the |
| // handle should fail because it is already closed. |
| fx_logger_t* logger; |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_logger_create(&config, &logger)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_log_sink_channel); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_log_sink_channel)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_log_sink_socket); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_log_sink_socket)); |
| } |
| |
| TEST(SyslogTests, test_log_sink_channel_closed_on_reconfigure_fail) { |
| EXPECT_LT(FX_LOG_MAX_TAGS, kTooManyTags.size()); |
| |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_handle = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = passed_handle, |
| .log_sink_socket = ZX_HANDLE_INVALID, |
| .tags = kTooManyTags.data(), |
| .num_tags = kTooManyTags.size(), |
| }; |
| |
| // This should fail because there are too many tags, and closing the |
| // handle should fail because it is already closed. |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_log_reconfigure(&config)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_handle); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_handle)); |
| } |
| |
| TEST(SyslogTests, test_log_sink_socket_closed_on_reconfigure_fail) { |
| EXPECT_LT(FX_LOG_MAX_TAGS, kTooManyTags.size()); |
| |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_handle = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = ZX_HANDLE_INVALID, |
| .log_sink_socket = passed_handle, |
| .tags = kTooManyTags.data(), |
| .num_tags = kTooManyTags.size(), |
| }; |
| |
| // This should fail because there are too many tags, and closing the |
| // handle should fail because it is already closed. |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_log_reconfigure(&config)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_handle); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_handle)); |
| } |
| |
| TEST(SyslogTests, test_both_handles_specified_fails_reconfigure_and_handles_closed) { |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_log_sink_channel = local.release(); |
| const zx_handle_t passed_log_sink_socket = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = passed_log_sink_channel, |
| .log_sink_socket = passed_log_sink_socket, |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| |
| // This should fail because both handles were specified, and closing the |
| // handle should fail because it is already closed. |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_log_reconfigure(&config)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_log_sink_channel); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_log_sink_channel)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_log_sink_socket); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_log_sink_socket)); |
| } |
| |
| #ifdef SYSLOG_STATIC |
| TEST(SyslogTests, test_create_with_log_sink_channel_not_supported) { |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_handle = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = passed_handle, |
| .log_sink_socket = ZX_HANDLE_INVALID, |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| |
| // This should fail because log_sink_channel is not supported, and closing the |
| // handle should fail because it is already closed. |
| fx_logger_t* logger; |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_logger_create(&config, &logger)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_handle); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_handle)); |
| } |
| |
| TEST(SyslogTests, test_reconfigure_with_log_sink_channel_not_supported) { |
| zx::socket local, remote; |
| EXPECT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| const zx_handle_t passed_handle = remote.release(); |
| |
| fx_logger_config_t config = { |
| .min_severity = FX_LOG_INFO, |
| .log_sink_channel = passed_handle, |
| .log_sink_socket = ZX_HANDLE_INVALID, |
| .tags = nullptr, |
| .num_tags = 0, |
| }; |
| |
| // This should fail because log_sink_channel is not supported, and closing the |
| // handle should fail because it is already closed. |
| EXPECT_STATUS(ZX_ERR_INVALID_ARGS, fx_log_reconfigure(&config)); |
| EXPECT_NE(ZX_HANDLE_INVALID, passed_handle); |
| EXPECT_STATUS(ZX_ERR_BAD_HANDLE, zx_handle_close(passed_handle)); |
| } |
| #endif |
| |
| } // namespace |