diff options
-rw-r--r-- | etc/evergreen.yml | 1 | ||||
-rw-r--r-- | jstests/noPassthroughWithMongod/logpath.js | 1 | ||||
-rw-r--r-- | src/mongo/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/generic_servers.cpp | 2 | ||||
-rw-r--r-- | src/mongo/logv2/file_rotate_sink.cpp | 128 | ||||
-rw-r--r-- | src/mongo/logv2/file_rotate_sink.h | 57 | ||||
-rw-r--r-- | src/mongo/logv2/log_domain_global.cpp | 56 | ||||
-rw-r--r-- | src/mongo/logv2/log_domain_global.h | 2 | ||||
-rw-r--r-- | src/mongo/logv2/shared_access_fstream.cpp | 296 | ||||
-rw-r--r-- | src/mongo/logv2/shared_access_fstream.h | 137 | ||||
-rw-r--r-- | src/mongo/util/log.cpp | 20 | ||||
-rw-r--r-- | src/mongo/util/log.h | 2 | ||||
-rw-r--r-- | src/mongo/util/signal_handlers.cpp | 2 | ||||
-rw-r--r-- | src/mongo/util/text.cpp | 23 | ||||
-rw-r--r-- | src/mongo/util/text.h | 1 |
15 files changed, 673 insertions, 57 deletions
diff --git a/etc/evergreen.yml b/etc/evergreen.yml index b499595afd7..92f572029cf 100644 --- a/etc/evergreen.yml +++ b/etc/evergreen.yml @@ -9285,7 +9285,6 @@ buildvariants: # spawning a large number of linker processes. num_scons_link_jobs_available: $(( $(grep -c ^processor /proc/cpuinfo) / 4 )) python: '/cygdrive/c/python/python37/python.exe' - test_flags: --excludeWithAnyTags=SERVER-45989 ext: zip scons_cache_scope: shared multiversion_platform: windows diff --git a/jstests/noPassthroughWithMongod/logpath.js b/jstests/noPassthroughWithMongod/logpath.js index 7f7aecd0bf6..bb39282871f 100644 --- a/jstests/noPassthroughWithMongod/logpath.js +++ b/jstests/noPassthroughWithMongod/logpath.js @@ -1,5 +1,4 @@ // check replica set authentication -// @tags: [SERVER-45989] var name = "logpath"; var token = "logpath_token"; diff --git a/src/mongo/SConscript b/src/mongo/SConscript index c94521e3886..f1d4f64e9e3 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -112,6 +112,7 @@ baseEnv.Library( 'logv2/attributes.cpp', 'logv2/bson_formatter.cpp', 'logv2/console.cpp', + 'logv2/file_rotate_sink.cpp', 'logv2/json_formatter.cpp', 'logv2/log_component.cpp', 'logv2/log_component_settings.cpp', @@ -123,6 +124,7 @@ baseEnv.Library( 'logv2/log_severity.cpp', 'logv2/log_tag.cpp', 'logv2/plain_formatter.cpp', + 'logv2/shared_access_fstream.cpp', 'logv2/ramlog.cpp', 'logv2/text_formatter.cpp', 'platform/decimal128.cpp', diff --git a/src/mongo/db/commands/generic_servers.cpp b/src/mongo/db/commands/generic_servers.cpp index 5ddd102c366..b398e6d59f2 100644 --- a/src/mongo/db/commands/generic_servers.cpp +++ b/src/mongo/db/commands/generic_servers.cpp @@ -196,7 +196,7 @@ public: const std::string& ns, const BSONObj& cmdObj, BSONObjBuilder& result) { - bool didRotate = rotateLogs(serverGlobalParams.logRenameOnRotate, logV2Enabled()); + bool didRotate = rotateLogs(serverGlobalParams.logRenameOnRotate); if (didRotate) logProcessDetailsForLogRotate(opCtx->getServiceContext()); return didRotate; diff --git a/src/mongo/logv2/file_rotate_sink.cpp b/src/mongo/logv2/file_rotate_sink.cpp new file mode 100644 index 00000000000..c0ca2ac91ee --- /dev/null +++ b/src/mongo/logv2/file_rotate_sink.cpp @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2020-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/logv2/file_rotate_sink.h" + +#include <boost/filesystem/operations.hpp> +#include <boost/make_shared.hpp> +#include <fmt/format.h> +#include <fstream> + +#include "mongo/logv2/shared_access_fstream.h" +#include "mongo/util/string_map.h" + + +namespace mongo::logv2 { +namespace { +#if _WIN32 +using stream_t = Win32SharedAccessOfstream; +#else +using stream_t = std::ofstream; +#endif + +StatusWith<boost::shared_ptr<stream_t>> openFile(const std::string& filename, bool append) { + std::ios_base::openmode mode = std::ios_base::out; + if (append) + mode |= std::ios_base::app; + else + mode |= std::ios_base::trunc; + auto file = boost::make_shared<stream_t>(filename, mode); + if (file->fail()) + return Status(ErrorCodes::FileNotOpen, fmt::format("Failed to open {}", filename)); + return file; +} +} // namespace + +struct FileRotateSink::Impl { + StringMap<boost::shared_ptr<stream_t>> files; +}; + +FileRotateSink::FileRotateSink() : _impl(std::make_unique<Impl>()) {} +FileRotateSink::~FileRotateSink() {} + +Status FileRotateSink::addFile(const std::string& filename, bool append) { + auto statusWithFile = openFile(filename, append); + if (statusWithFile.isOK()) { + add_stream(statusWithFile.getValue()); + _impl->files[filename] = statusWithFile.getValue(); + } + + return statusWithFile.getStatus(); +} +void FileRotateSink::removeFile(const std::string& filename) { + auto it = _impl->files.find(filename); + if (it != _impl->files.cend()) { + remove_stream(it->second); + _impl->files.erase(it); + } +} + +Status FileRotateSink::rotate(bool rename, StringData renameSuffix) { + for (auto& file : _impl->files) { + const std::string& filename = file.first; + if (rename) { + std::string renameTarget = filename + renameSuffix; + try { + if (boost::filesystem::exists(renameTarget)) { + return Status( + ErrorCodes::FileRenameFailed, + fmt::format("Renaming file {} to {} failed; destination already exists", + filename, + renameTarget)); + } + } catch (const std::exception& e) { + return Status(ErrorCodes::FileRenameFailed, + fmt::format("Renaming file {} to {} failed; Cannot verify whether " + "destination already exists: {}", + filename, + renameTarget, + e.what())); + } + + boost::system::error_code ec; + boost::filesystem::rename(filename, renameTarget, ec); + if (ec) { + return Status( + ErrorCodes::FileRenameFailed, + fmt::format( + "Failed to rename {} to {}: {}", filename, renameTarget, ec.message())); + } + } + + auto newFile = openFile(filename, false); + if (newFile.isOK()) { + file.second = newFile.getValue(); + } + return newFile.getStatus(); + } + + return Status::OK(); +} + +} // namespace mongo::logv2 diff --git a/src/mongo/logv2/file_rotate_sink.h b/src/mongo/logv2/file_rotate_sink.h new file mode 100644 index 00000000000..56f12215750 --- /dev/null +++ b/src/mongo/logv2/file_rotate_sink.h @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2020-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <boost/log/sinks/text_ostream_backend.hpp> +#include <memory> +#include <string> + +#include "mongo/base/status.h" + +namespace mongo::logv2 { +// boost::log backend sink to provide MongoDB style file rotation. +// Uses custom stream type to open log files with shared access on Windows, somthing the built-in +// boost file rotation sink does not do. +class FileRotateSink : public boost::log::sinks::text_ostream_backend { +public: + FileRotateSink(); + ~FileRotateSink(); + + Status addFile(const std::string& filename, bool append); + void removeFile(const std::string& filename); + + Status rotate(bool rename, StringData renameSuffix); + +private: + struct Impl; + std::unique_ptr<Impl> _impl; +}; + +} // namespace mongo::logv2 diff --git a/src/mongo/logv2/log_domain_global.cpp b/src/mongo/logv2/log_domain_global.cpp index bc9d512f176..d80be9f65d7 100644 --- a/src/mongo/logv2/log_domain_global.cpp +++ b/src/mongo/logv2/log_domain_global.cpp @@ -32,9 +32,11 @@ #include "mongo/logv2/component_settings_filter.h" #include "mongo/logv2/composite_backend.h" #include "mongo/logv2/console.h" +#include "mongo/logv2/file_rotate_sink.h" #include "mongo/logv2/json_formatter.h" #include "mongo/logv2/log_source.h" #include "mongo/logv2/ramlog_sink.h" +#include "mongo/logv2/shared_access_fstream.h" #include "mongo/logv2/tagged_severity_filter.h" #include "mongo/logv2/text_formatter.h" @@ -45,35 +47,6 @@ namespace mongo { namespace logv2 { -namespace { - -class RotateCollector : public boost::log::sinks::file::collector { -public: - explicit RotateCollector(LogDomainGlobal::ConfigurationOptions const& options) - : _mode{options._fileRotationMode} {} - - void store_file(boost::filesystem::path const& file) override { - if (_mode == LogDomainGlobal::ConfigurationOptions::RotationMode::kRename) { - auto renameTarget = file.string() + "." + terseCurrentTime(false); - boost::system::error_code ec; - boost::filesystem::rename(file, renameTarget, ec); - if (ec) { - // throw here or propagate this error in another way? - } - } - } - - uintmax_t scan_for_files(boost::log::sinks::file::scan_method, - boost::filesystem::path const&, - unsigned int*) override { - return 0; - } - -private: - LogDomainGlobal::ConfigurationOptions::RotationMode _mode; -}; - -} // namespace void LogDomainGlobal::ConfigurationOptions::makeDisabled() { _consoleEnabled = false; @@ -86,12 +59,11 @@ struct LogDomainGlobal::Impl { typedef CompositeBackend<boost::log::sinks::syslog_backend, RamLogSink, RamLogSink> SyslogBackend; #endif - typedef CompositeBackend<boost::log::sinks::text_file_backend, RamLogSink, RamLogSink> - RotatableFileBackend; + typedef CompositeBackend<FileRotateSink, RamLogSink, RamLogSink> RotatableFileBackend; Impl(LogDomainGlobal& parent); Status configure(LogDomainGlobal::ConfigurationOptions const& options); - Status rotate(); + Status rotate(bool rename, StringData renameSuffix); LogDomainGlobal& _parent; LogComponentSettings _settings; @@ -171,16 +143,16 @@ Status LogDomainGlobal::Impl::configure(LogDomainGlobal::ConfigurationOptions co if (options._fileEnabled) { auto backend = boost::make_shared<RotatableFileBackend>( - boost::make_shared<boost::log::sinks::text_file_backend>( - boost::log::keywords::file_name = options._filePath), + boost::make_shared<FileRotateSink>(), boost::make_shared<RamLogSink>(RamLog::get("global")), boost::make_shared<RamLogSink>(RamLog::get("startupWarnings"))); - + Status ret = backend->lockedBackend<0>()->addFile( + options._filePath, + options._fileOpenMode == ConfigurationOptions::OpenMode::kAppend ? true : false); + if (!ret.isOK()) + return ret; backend->lockedBackend<0>()->auto_flush(true); - backend->lockedBackend<0>()->set_file_collector( - boost::make_shared<RotateCollector>(options)); - _rotatableFileSink = boost::make_shared<boost::log::sinks::unlocked_sink<RotatableFileBackend>>(backend); _rotatableFileSink->set_filter(ComponentSettingsFilter(_parent, _settings)); @@ -214,10 +186,10 @@ Status LogDomainGlobal::Impl::configure(LogDomainGlobal::ConfigurationOptions co return Status::OK(); } -Status LogDomainGlobal::Impl::rotate() { +Status LogDomainGlobal::Impl::rotate(bool rename, StringData renameSuffix) { if (_rotatableFileSink) { auto backend = _rotatableFileSink->locked_backend()->lockedBackend<0>(); - backend->rotate_file(); + return backend->rotate(rename, renameSuffix); } return Status::OK(); } @@ -239,8 +211,8 @@ Status LogDomainGlobal::configure(LogDomainGlobal::ConfigurationOptions const& o return _impl->configure(options); } -Status LogDomainGlobal::rotate() { - return _impl->rotate(); +Status LogDomainGlobal::rotate(bool rename, StringData renameSuffix) { + return _impl->rotate(rename, renameSuffix); } LogComponentSettings& LogDomainGlobal::settings() { diff --git a/src/mongo/logv2/log_domain_global.h b/src/mongo/logv2/log_domain_global.h index afc943db4d3..03a09562c07 100644 --- a/src/mongo/logv2/log_domain_global.h +++ b/src/mongo/logv2/log_domain_global.h @@ -58,7 +58,7 @@ public: LogSource& source() override; Status configure(ConfigurationOptions const& options); - Status rotate(); + Status rotate(bool rename, StringData renameSuffix); LogComponentSettings& settings(); diff --git a/src/mongo/logv2/shared_access_fstream.cpp b/src/mongo/logv2/shared_access_fstream.cpp new file mode 100644 index 00000000000..2179e27fe6c --- /dev/null +++ b/src/mongo/logv2/shared_access_fstream.cpp @@ -0,0 +1,296 @@ +/** + * Copyright (C) 2020-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logv2/shared_access_fstream.h" + +#if defined(_WIN32) && defined(_MSC_VER) + +#include <fcntl.h> +#include <filesystem> +#include <io.h> + +#include "mongo/util/text.h" + +namespace mongo { +FILE* Win32SharedAccessFileDescriptor::_open(const wchar_t* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) { + const char* fdmode = nullptr; + int openflags = 0; + uint32_t desiredAccess = 0; + uint32_t creationDisposition = 0; + uint32_t shareMode = FILE_SHARE_DELETE | FILE_SHARE_READ; + if (sharedWriteAccess) + shareMode |= FILE_SHARE_WRITE; + + // The combinations are taken from https://en.cppreference.com/w/cpp/io/basic_filebuf/open + auto modeWithoutAte = mode & ~std::ios_base::ate; + if (modeWithoutAte == std::ios_base::in) { + fdmode = "r"; + creationDisposition = OPEN_EXISTING; + openflags |= _O_RDONLY; + } else if (modeWithoutAte == std::ios_base::out || + modeWithoutAte == (std::ios_base::out | std::ios_base::trunc)) { + fdmode = "w"; + creationDisposition = CREATE_ALWAYS; + openflags |= _O_WRONLY; + } else if (modeWithoutAte == std::ios_base::app || + modeWithoutAte == (std::ios_base::out | std::ios_base::app)) { + + fdmode = "a"; + creationDisposition = OPEN_ALWAYS; + openflags |= _O_WRONLY | _O_APPEND; + } else if (modeWithoutAte == (std::ios_base::out | std::ios_base::in)) { + + fdmode = "r+"; + creationDisposition = OPEN_EXISTING; + openflags |= _O_RDWR; + } else if (modeWithoutAte == (std::ios_base::out | std::ios_base::in | std::ios_base::trunc)) { + + fdmode = "w+"; + creationDisposition = CREATE_ALWAYS; + openflags |= _O_RDWR; + } else if (modeWithoutAte == (std::ios_base::out | std::ios_base::in | std::ios_base::app) || + modeWithoutAte == (std::ios_base::in | std::ios_base::app)) { + fdmode = "a+"; + creationDisposition = OPEN_ALWAYS; + openflags |= _O_RDWR; + } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::in)) { + + fdmode = "rb"; + creationDisposition = OPEN_EXISTING; + openflags |= _O_RDONLY; + } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::out) || + modeWithoutAte == + (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc)) { + + fdmode = "wb"; + creationDisposition = CREATE_ALWAYS; + openflags |= _O_WRONLY; + } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::app) || + modeWithoutAte == + (std::ios_base::binary | std::ios_base::out | std::ios_base::app)) { + + fdmode = "ab"; + creationDisposition = OPEN_ALWAYS; + openflags |= _O_WRONLY; + } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::out | std::ios_base::in)) { + + fdmode = "r+b"; + creationDisposition = OPEN_EXISTING; + openflags |= _O_RDWR; + } else if (modeWithoutAte == + (std::ios_base::binary | std::ios_base::out | std::ios_base::in | + std::ios_base::trunc)) { + + fdmode = "w+b"; + creationDisposition = CREATE_ALWAYS; + openflags |= _O_RDWR; + } else if (modeWithoutAte == + (std::ios_base::binary | std::ios_base::out | std::ios_base::in | + std::ios_base::app) || + modeWithoutAte == (std::ios_base::binary | std::ios_base::in | std::ios_base::app)) { + + fdmode = "a+b"; + creationDisposition = OPEN_ALWAYS; + openflags |= _O_RDWR; + } else + return nullptr; + + if (mode & std::ios_base::in) + desiredAccess |= GENERIC_READ; + if (mode & std::ios_base::out || mode & std::ios_base::app) + desiredAccess |= GENERIC_WRITE; + + if (mode & std::ios_base::binary) + openflags |= _O_BINARY; + else + openflags |= _O_U8TEXT; + + if (mode & std::ios_base::trunc) + openflags |= _O_TRUNC; + + // Open file with share access to get a Windows HANDLE + HANDLE handle = CreateFileW(filename, // lpFileName + desiredAccess, // dwDesiredAccess + shareMode, // dwShareMode + nullptr, // lpSecurityAttributes + creationDisposition, // dwCreationDisposition + FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes + nullptr // hTemplateFile + ); + if (handle == INVALID_HANDLE_VALUE) { + return nullptr; + } + + LARGE_INTEGER zero; + zero.QuadPart = 0LL; + if (mode & std::ios_base::ate || + ((mode & std::ios_base::app) && (openflags & _O_APPEND) == 0)) { + SetFilePointerEx(handle, zero, nullptr, FILE_END); + } + + if (mode & std::ios_base::trunc) { + SetEndOfFile(handle); + } + + // Convert the HANDLE to a file descriptor + int fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), openflags); + if (fd == -1) { + CloseHandle(handle); + return nullptr; + } + + // Open the file descriptor to get a FILE* + return _fdopen(fd, fdmode); +} + +FILE* Win32SharedAccessFileDescriptor::_open(StringData filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) { + return _open(toWideStringFromStringData(filename).c_str(), mode, sharedWriteAccess); +} + +Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor(const wchar_t* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : _file(_open(filename, mode, sharedWriteAccess)) {} +Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor(const char* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : _file(_open(filename, mode, sharedWriteAccess)) {} +Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor(const std::string& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : _file(_open(filename.c_str(), mode, sharedWriteAccess)) {} +Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor( + const std::filesystem::path& filename, std::ios_base::openmode mode, bool sharedWriteAccess) + : _file(_open(filename.c_str(), mode, sharedWriteAccess)) {} + +Win32SharedAccessOfstream::Win32SharedAccessOfstream(const char* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessOfstream::Win32SharedAccessOfstream(const wchar_t* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessOfstream::Win32SharedAccessOfstream(const std::string& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessOfstream::Win32SharedAccessOfstream(const std::filesystem::path& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessIfstream::Win32SharedAccessIfstream(const char* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessIfstream::Win32SharedAccessIfstream(const wchar_t* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessIfstream::Win32SharedAccessIfstream(const std::string& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessIfstream::Win32SharedAccessIfstream(const std::filesystem::path& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessFstream::Win32SharedAccessFstream(const char* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessFstream::Win32SharedAccessFstream(const wchar_t* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessFstream::Win32SharedAccessFstream(const std::string& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) { + if (!_file) + setstate(failbit); +} + +Win32SharedAccessFstream::Win32SharedAccessFstream(const std::filesystem::path& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess) + : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) { + if (!_file) + setstate(failbit); +} + +} // namespace mongo + +#endif // _WIN32 && _MSC_VER diff --git a/src/mongo/logv2/shared_access_fstream.h b/src/mongo/logv2/shared_access_fstream.h new file mode 100644 index 00000000000..6157705a5be --- /dev/null +++ b/src/mongo/logv2/shared_access_fstream.h @@ -0,0 +1,137 @@ +/** + * Copyright (C) 2020-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#if defined(_WIN32) && defined(_MSC_VER) + +#include <fstream> + +#include "mongo/base/string_data.h" + +namespace mongo { +// Helper class to open a FILE* with shared access. Used by the stream classes below. +class Win32SharedAccessFileDescriptor { +protected: + Win32SharedAccessFileDescriptor(const wchar_t* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess); + Win32SharedAccessFileDescriptor(const char* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess); + Win32SharedAccessFileDescriptor(const std::string& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess); + Win32SharedAccessFileDescriptor(const std::filesystem::path& filename, + std::ios_base::openmode mode, + bool sharedWriteAccess); + + static FILE* _open(const wchar_t* filename, + std::ios_base::openmode mode, + bool sharedWriteAccess); + static FILE* _open(StringData filename, std::ios_base::openmode mode, bool sharedWriteAccess); + + FILE* _file = nullptr; +}; + +// File stream classes that extends the regular file streams with two capabilities: +// (1) Opens files with shared delete and read access with optional shared write access. +// (2) Uses UTF-8 as the encoding for the filename parameter +class Win32SharedAccessOfstream : private Win32SharedAccessFileDescriptor, public std::ofstream { +public: + explicit Win32SharedAccessOfstream(const char* filename, + ios_base::openmode mode = ios_base::out, + bool sharedWriteAccess = false); + explicit Win32SharedAccessOfstream(const wchar_t* filename, + ios_base::openmode mode = ios_base::out, + bool sharedWriteAccess = false); + explicit Win32SharedAccessOfstream(const std::string& filename, + ios_base::openmode mode = ios_base::out, + bool sharedWriteAccess = false); + explicit Win32SharedAccessOfstream(const std::filesystem::path& filename, + ios_base::openmode mode = ios_base::out, + bool sharedWriteAccess = false); + + // The Visual Studio extension that operates on FILE* does not have an open overload. Needs to + // open with constructor. + void open(const char* filename, ios_base::openmode mode) = delete; + void open(const wchar_t* filename, ios_base::openmode mode) = delete; + void open(const std::string& filename, ios_base::openmode mode) = delete; + void open(const std::filesystem::path& filename, ios_base::openmode mode) = delete; +}; + +class Win32SharedAccessIfstream : private Win32SharedAccessFileDescriptor, public std::ifstream { +public: + explicit Win32SharedAccessIfstream(const char* filename, + ios_base::openmode mode = ios_base::in, + bool sharedWriteAccess = false); + explicit Win32SharedAccessIfstream(const wchar_t* filename, + ios_base::openmode mode = ios_base::in, + bool sharedWriteAccess = false); + explicit Win32SharedAccessIfstream(const std::string& filename, + ios_base::openmode mode = ios_base::in, + bool sharedWriteAccess = false); + explicit Win32SharedAccessIfstream(const std::filesystem::path& filename, + ios_base::openmode mode = ios_base::in, + bool sharedWriteAccess = false); + + // The Visual Studio extension that operates on FILE* does not have an open overload. Needs to + // open with constructor. + void open(const char* filename, ios_base::openmode mode) = delete; + void open(const wchar_t* filename, ios_base::openmode mode) = delete; + void open(const std::string& filename, ios_base::openmode mode) = delete; + void open(const std::filesystem::path& filename, ios_base::openmode mode) = delete; +}; + +class Win32SharedAccessFstream : private Win32SharedAccessFileDescriptor, public std::fstream { +public: + explicit Win32SharedAccessFstream(const char* filename, + ios_base::openmode mode = ios_base::in | ios_base::out, + bool sharedWriteAccess = false); + explicit Win32SharedAccessFstream(const wchar_t* filename, + ios_base::openmode mode = ios_base::in | ios_base::out, + bool sharedWriteAccess = false); + explicit Win32SharedAccessFstream(const std::string& filename, + ios_base::openmode mode = ios_base::in | ios_base::out, + bool sharedWriteAccess = false); + explicit Win32SharedAccessFstream(const std::filesystem::path& filename, + ios_base::openmode mode = ios_base::in | ios_base::out, + bool sharedWriteAccess = false); + + // The Visual Studio extension that operates on FILE* does not have an open overload. Needs to + // open with constructor. + void open(const char* filename, ios_base::openmode mode) = delete; + void open(const wchar_t* filename, ios_base::openmode mode) = delete; + void open(const std::string& filename, ios_base::openmode mode) = delete; + void open(const std::filesystem::path& filename, ios_base::openmode mode) = delete; +}; + +} // namespace mongo + +#endif // _WIN32 diff --git a/src/mongo/util/log.cpp b/src/mongo/util/log.cpp index 837f80cafd8..9a9d7099e4a 100644 --- a/src/mongo/util/log.cpp +++ b/src/mongo/util/log.cpp @@ -82,22 +82,24 @@ Status logger::registerExtraLogContextFn(logger::ExtraLogContextFn contextFn) { return Status::OK(); } -bool rotateLogs(bool renameFiles, bool useLogV2) { - if (useLogV2) { - log() << "Logv2 rotation initiated"; - return logv2::LogManager::global().getGlobalDomainInternal().rotate().isOK(); - } +bool rotateLogs(bool renameFiles) { + // Rotate on both logv1 and logv2 so all files that need rotation gets rotated + log() << "Log rotation initiated"; + std::string suffix = "." + terseCurrentTime(false); + Status resultv2 = + logv2::LogManager::global().getGlobalDomainInternal().rotate(renameFiles, suffix); + if (!resultv2.isOK()) + warning() << "Log rotation failed: " << resultv2; + using logger::RotatableFileManager; RotatableFileManager* manager = logger::globalRotatableFileManager(); - log() << "Log rotation initiated"; - RotatableFileManager::FileNameStatusPairVector result( - manager->rotateAll(renameFiles, "." + terseCurrentTime(false))); + RotatableFileManager::FileNameStatusPairVector result(manager->rotateAll(renameFiles, suffix)); for (RotatableFileManager::FileNameStatusPairVector::iterator it = result.begin(); it != result.end(); it++) { warning() << "Rotating log file " << it->first << " failed: " << it->second.toString(); } - return result.empty(); + return resultv2.isOK() && result.empty(); } void logContext(const char* errmsg) { diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h index 145b41c5a6f..cfe42dd8695 100644 --- a/src/mongo/util/log.h +++ b/src/mongo/util/log.h @@ -214,7 +214,7 @@ inline bool shouldLog(logger::LogSeverity severity) { * We expect logrotate to rename the existing file before we rotate, and so the next open * we do should result in a file create. */ -bool rotateLogs(bool renameFiles, bool useLogV2); +bool rotateLogs(bool renameFiles); extern Tee* const warnings; // Things put here go in serverStatus extern Tee* const startupWarningsLog; // Things put here get reported in MMS diff --git a/src/mongo/util/signal_handlers.cpp b/src/mongo/util/signal_handlers.cpp index 47582a9ae84..20e82cbc0ea 100644 --- a/src/mongo/util/signal_handlers.cpp +++ b/src/mongo/util/signal_handlers.cpp @@ -215,7 +215,7 @@ void handleOneSignal(const SignalWaitResult& waited, LogRotationState* rotation) return; rotation->previous = now; } - fassert(16782, rotateLogs(serverGlobalParams.logRenameOnRotate, logV2Enabled())); + fassert(16782, rotateLogs(serverGlobalParams.logRenameOnRotate)); if (rotation->logFileStatus == LogFileStatus::kNeedToRotateLogFile) { logProcessDetailsForLogRotate(getGlobalServiceContext()); } diff --git a/src/mongo/util/text.cpp b/src/mongo/util/text.cpp index be0e18eea3a..dccd4d3fb49 100644 --- a/src/mongo/util/text.cpp +++ b/src/mongo/util/text.cpp @@ -177,6 +177,29 @@ std::string toUtf8String(const std::wstring& wide) { return ""; } +std::wstring toWideStringFromStringData(StringData utf8String) { + int bufferSize = MultiByteToWideChar(CP_UTF8, // Code page + 0, // Flags + utf8String.rawData(), // Input string + utf8String.size(), // Count, -1 for NUL-terminated + nullptr, // No output buffer + 0 // Zero means "compute required size" + ); + if (bufferSize == 0) { + return std::wstring(); + } + std::unique_ptr<wchar_t[]> tempBuffer(new wchar_t[bufferSize]); + tempBuffer[0] = L'0'; + MultiByteToWideChar(CP_UTF8, // Code page + 0, // Flags + utf8String.rawData(), // Input string + utf8String.size(), // Count, -1 for NUL-terminated + tempBuffer.get(), // UTF-16 output buffer + bufferSize // Buffer size in wide characters + ); + return std::wstring(tempBuffer.get(), bufferSize); +} + std::wstring toWideString(const char* utf8String) { int bufferSize = MultiByteToWideChar(CP_UTF8, // Code page 0, // Flags diff --git a/src/mongo/util/text.h b/src/mongo/util/text.h index 76309541d0c..40493fa9d4f 100644 --- a/src/mongo/util/text.h +++ b/src/mongo/util/text.h @@ -75,6 +75,7 @@ bool isValidUTF8(StringData s); std::string toUtf8String(const std::wstring& wide); +std::wstring toWideStringFromStringData(StringData s); std::wstring toWideString(const char* s); bool writeUtf8ToWindowsConsole(const char* utf8String, unsigned int utf8StringSize); |