summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorKevin Pulo <kevin.pulo@mongodb.com>2016-08-16 22:08:49 +1000
committerKevin Pulo <kevin.pulo@mongodb.com>2016-08-16 22:10:46 +1000
commit512a1d57f6b3560cc3531355676cf06d362c93a1 (patch)
tree4a4c8585f07e48a6c3c6b009e28d65ea402c9e56 /src/mongo/db
parent51fe71a4169d8ac01dca915b482c94e228d8a746 (diff)
downloadmongo-512a1d57f6b3560cc3531355676cf06d362c93a1.tar.gz
SERVER-24914 SERVER-24915 improve getting cwd at server startup
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/mongod_options.cpp4
-rw-r--r--src/mongo/db/mongod_options.h2
-rw-r--r--src/mongo/db/mongod_options_init.cpp2
-rw-r--r--src/mongo/db/server_options_helpers.cpp24
-rw-r--r--src/mongo/db/server_options_helpers.h16
-rw-r--r--src/mongo/db/server_options_init.cpp41
-rw-r--r--src/mongo/db/server_options_test.cpp245
8 files changed, 311 insertions, 24 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index cdd7a006249..13152dcc249 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -399,6 +399,7 @@ env.Library(
"dbwebserver.cpp",
"initialize_server_global_state.cpp",
"server_extra_log_context.cpp",
+ "server_options_init.cpp",
],
LIBDEPS=[
"$BUILD_DIR/mongo/client/clientdriver",
diff --git a/src/mongo/db/mongod_options.cpp b/src/mongo/db/mongod_options.cpp
index b030a26d3a9..7b38f8ce066 100644
--- a/src/mongo/db/mongod_options.cpp
+++ b/src/mongo/db/mongod_options.cpp
@@ -985,8 +985,8 @@ Status canonicalizeMongodOptions(moe::Environment* params) {
return Status::OK();
}
-Status storeMongodOptions(const moe::Environment& params, const std::vector<std::string>& args) {
- Status ret = storeServerOptions(params, args);
+Status storeMongodOptions(const moe::Environment& params) {
+ Status ret = storeServerOptions(params);
if (!ret.isOK()) {
return ret;
}
diff --git a/src/mongo/db/mongod_options.h b/src/mongo/db/mongod_options.h
index 18a370f7f75..27fb778cfdf 100644
--- a/src/mongo/db/mongod_options.h
+++ b/src/mongo/db/mongod_options.h
@@ -82,7 +82,7 @@ Status canonicalizeMongodOptions(moe::Environment* params);
// Must be called after "storeMongodOptions"
StatusWith<repl::ReplSettings> parseMongodReplicationOptions(const moe::Environment& params);
-Status storeMongodOptions(const moe::Environment& params, const std::vector<std::string>& args);
+Status storeMongodOptions(const moe::Environment& params);
void setGlobalReplSettings(const repl::ReplSettings& settings);
const repl::ReplSettings& getGlobalReplSettings();
diff --git a/src/mongo/db/mongod_options_init.cpp b/src/mongo/db/mongod_options_init.cpp
index 1c5d78a8da7..bbd5de47858 100644
--- a/src/mongo/db/mongod_options_init.cpp
+++ b/src/mongo/db/mongod_options_init.cpp
@@ -69,7 +69,7 @@ MONGO_INITIALIZER_GENERAL(MongodOptions_Store,
("BeginStartupOptionStorage"),
("EndStartupOptionStorage"))
(InitializerContext* context) {
- Status ret = storeMongodOptions(moe::startupOptionsParsed, context->args());
+ Status ret = storeMongodOptions(moe::startupOptionsParsed);
if (!ret.isOK()) {
std::cerr << ret.toString() << std::endl;
std::cerr << "try '" << context->args()[0] << " --help' for more information" << std::endl;
diff --git a/src/mongo/db/server_options_helpers.cpp b/src/mongo/db/server_options_helpers.cpp
index 6956c8454f0..c82311f65a2 100644
--- a/src/mongo/db/server_options_helpers.cpp
+++ b/src/mongo/db/server_options_helpers.cpp
@@ -449,7 +449,7 @@ namespace {
// Helpers for option storage
Status setupBinaryName(const std::vector<std::string>& argv) {
if (argv.empty()) {
- return Status(ErrorCodes::InternalError, "Cannot get binary name: argv array is empty");
+ return Status(ErrorCodes::UnknownError, "Cannot get binary name: argv array is empty");
}
// setup binary name
@@ -463,13 +463,13 @@ Status setupBinaryName(const std::vector<std::string>& argv) {
Status setupCwd() {
// setup cwd
- char buffer[1024];
-#ifdef _WIN32
- verify(_getcwd(buffer, 1000));
-#else
- verify(getcwd(buffer, 1000));
-#endif
- serverGlobalParams.cwd = buffer;
+ boost::system::error_code ec;
+ boost::filesystem::path cwd = boost::filesystem::current_path(ec);
+ if (ec) {
+ return Status(ErrorCodes::UnknownError,
+ "Cannot get current working directory: " + ec.message());
+ }
+ serverGlobalParams.cwd = cwd.string();
return Status::OK();
}
@@ -730,7 +730,7 @@ Status canonicalizeServerOptions(moe::Environment* params) {
return Status::OK();
}
-Status storeServerOptions(const moe::Environment& params, const std::vector<std::string>& args) {
+Status setupServerOptions(const std::vector<std::string>& args) {
Status ret = setupBinaryName(args);
if (!ret.isOK()) {
return ret;
@@ -746,7 +746,11 @@ Status storeServerOptions(const moe::Environment& params, const std::vector<std:
return ret;
}
- ret = setParsedOpts(params);
+ return Status::OK();
+}
+
+Status storeServerOptions(const moe::Environment& params) {
+ Status ret = setParsedOpts(params);
if (!ret.isOK()) {
return ret;
}
diff --git a/src/mongo/db/server_options_helpers.h b/src/mongo/db/server_options_helpers.h
index f97ab00e795..88002c7228b 100644
--- a/src/mongo/db/server_options_helpers.h
+++ b/src/mongo/db/server_options_helpers.h
@@ -62,7 +62,21 @@ Status validateServerOptions(const moe::Environment& params);
*/
Status canonicalizeServerOptions(moe::Environment* params);
-Status storeServerOptions(const moe::Environment& params, const std::vector<std::string>& args);
+/**
+ * Sets up the global server state necessary to be able to store the server options, based on how
+ * the server was started.
+ *
+ * For example, saves the current working directory in serverGlobalParams.cwd so that relative paths
+ * in server options can be interpreted correctly.
+ */
+Status setupServerOptions(const std::vector<std::string>& args);
+
+/**
+ * Store the given parsed params in global server state.
+ *
+ * For example, sets the serverGlobalParams.port variable based on the net.port config parameter.
+ */
+Status storeServerOptions(const moe::Environment& params);
void printCommandLineOpts();
diff --git a/src/mongo/db/server_options_init.cpp b/src/mongo/db/server_options_init.cpp
new file mode 100644
index 00000000000..e0f747c62c5
--- /dev/null
+++ b/src/mongo/db/server_options_init.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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/base/init.h"
+#include "mongo/db/server_options_helpers.h"
+
+namespace mongo {
+
+MONGO_INITIALIZER_GENERAL(ServerOptions_Setup,
+ ("BeginStartupOptionSetup"),
+ ("EndStartupOptionSetup"))
+(InitializerContext* context) {
+ return setupServerOptions(context->args());
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/server_options_test.cpp b/src/mongo/db/server_options_test.cpp
index df9cfdf17e4..a9295d33798 100644
--- a/src/mongo/db/server_options_test.cpp
+++ b/src/mongo/db/server_options_test.cpp
@@ -25,16 +25,32 @@
* then also delete it in the license file.
*/
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
+
#include "mongo/platform/basic.h"
+#include "mongo/config.h"
+
+#if defined(MONGO_CONFIG_HAVE_HEADER_UNISTD_H)
+#include <unistd.h>
+#endif
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+#include <boost/filesystem.hpp>
+
#include "mongo/bson/util/builder.h"
#include "mongo/db/server_options.h"
#include "mongo/db/server_options_helpers.h"
#include "mongo/logger/logger.h"
#include "mongo/unittest/unittest.h"
+#include "mongo/util/log.h"
#include "mongo/util/options_parser/environment.h"
#include "mongo/util/options_parser/option_section.h"
#include "mongo/util/options_parser/options_parser.h"
+#include "mongo/util/scopeguard.h"
namespace {
@@ -84,7 +100,8 @@ TEST(Verbosity, Default) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
// Make sure the log level didn't change since we didn't specify any verbose options
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -111,7 +128,8 @@ TEST(Verbosity, CommandLineImplicit) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
int verbosity = 1;
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -139,7 +157,8 @@ TEST(Verbosity, CommandLineString) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
int verbosity = 4;
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -167,7 +186,8 @@ TEST(Verbosity, CommandLineStringDisguisedLongForm) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
int verbosity = 4;
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -277,7 +297,8 @@ TEST(Verbosity, INIConfigString) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
int verbosity = 4;
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -331,7 +352,8 @@ TEST(Verbosity, INIConfigEmptyString) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
int verbosity = 0;
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -361,7 +383,8 @@ TEST(Verbosity, JSONConfigString) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
int verbosity = 4;
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -393,7 +416,8 @@ TEST(Verbosity, MultipleSourcesMultipleOptions) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
int verbosity = 3;
ASSERT_EQUALS(::mongo::logger::globalLogDomain()->getMinimumLogSeverity(),
@@ -435,7 +459,8 @@ TEST(Verbosity, YAMLConfigStringLogComponent) {
ASSERT_OK(::mongo::validateServerOptions(environment));
ASSERT_OK(::mongo::canonicalizeServerOptions(&environment));
- ASSERT_OK(::mongo::storeServerOptions(environment, argv));
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+ ASSERT_OK(::mongo::storeServerOptions(environment));
// Verify component log levels using global log domain.
int verbosity = 4;
@@ -476,4 +501,206 @@ TEST(Verbosity, YAMLConfigStringLogComponent) {
::mongo::logger::LogSeverity::Debug(2));
}
+TEST(SetupOptions, Default) {
+ std::vector<std::string> argv;
+ argv.push_back("binaryname");
+
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+
+ ASSERT_EQUALS(::mongo::serverGlobalParams.binaryName, "binaryname");
+ ASSERT_FALSE(::mongo::serverGlobalParams.cwd.empty());
+ ASSERT_EQUALS(::mongo::serverGlobalParams.argvArray.nFields(), static_cast<int>(argv.size()));
+}
+
+TEST(SetupOptions, RelativeBinaryPath) {
+ std::vector<std::string> argv;
+ argv.push_back("foo/bar/baz/binaryname");
+
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+
+ ASSERT_EQUALS(::mongo::serverGlobalParams.binaryName, "binaryname");
+ ASSERT_FALSE(::mongo::serverGlobalParams.cwd.empty());
+ ASSERT_EQUALS(::mongo::serverGlobalParams.argvArray.nFields(), static_cast<int>(argv.size()));
+}
+
+TEST(SetupOptions, AbsoluteBinaryPath) {
+ std::vector<std::string> argv;
+ argv.push_back("/foo/bar/baz/binaryname");
+
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+
+ ASSERT_EQUALS(::mongo::serverGlobalParams.binaryName, "binaryname");
+ ASSERT_FALSE(::mongo::serverGlobalParams.cwd.empty());
+ ASSERT_EQUALS(::mongo::serverGlobalParams.argvArray.nFields(), static_cast<int>(argv.size()));
+}
+
+TEST(SetupOptions, EmptyBinaryName) {
+ std::vector<std::string> argv;
+ argv.push_back("");
+
+ ASSERT_OK(::mongo::setupServerOptions(argv));
+
+ ASSERT_EQUALS(::mongo::serverGlobalParams.binaryName, "");
+ ASSERT_FALSE(::mongo::serverGlobalParams.cwd.empty());
+ ASSERT_EQUALS(::mongo::serverGlobalParams.argvArray.nFields(), static_cast<int>(argv.size()));
+}
+
+TEST(SetupOptions, MissingBinaryName) {
+ std::vector<std::string> argv;
+
+ ASSERT_NOT_OK(::mongo::setupServerOptions(argv));
+}
+
+#if !defined(_WIN32)
+
+#define ASSERT_BOOST_SUCCESS(ec) ASSERT_FALSE(ec) << ec.message()
+
+TEST(SetupOptions, DeepCwd) {
+ std::vector<std::string> argv;
+ argv.push_back("binaryname");
+
+ // Save the original cwd.
+ boost::system::error_code ec;
+ boost::filesystem::path cwd = boost::filesystem::current_path(ec);
+ ASSERT_BOOST_SUCCESS(ec);
+
+ // All work is done under a temporary base directory.
+ ::mongo::StringBuilder sb;
+ sb << "/tmp/deepcwd-" << getpid();
+ boost::filesystem::path deepBaseDir = sb.str();
+
+ auto cleanup = ::mongo::MakeGuard([&] {
+ boost::filesystem::current_path(cwd, ec);
+ boost::filesystem::remove_all(deepBaseDir, ec);
+ });
+
+ // Clear out any old base dir, and create an empty dir.
+ boost::filesystem::remove_all(deepBaseDir, ec);
+ ASSERT_BOOST_SUCCESS(ec);
+ boost::filesystem::create_directories(deepBaseDir, ec);
+ ASSERT_BOOST_SUCCESS(ec);
+
+ // Resolve the canonical base dir, in case /tmp is a symlink (eg. on OSX).
+ // This involves chdir into it, then getcwd() again (since getcwd() returns a canonical path).
+ boost::filesystem::current_path(deepBaseDir, ec);
+ ASSERT_BOOST_SUCCESS(ec);
+ deepBaseDir = boost::filesystem::current_path(ec);
+ ASSERT_BOOST_SUCCESS(ec);
+
+ boost::filesystem::path deepCwd = deepBaseDir;
+ boost::filesystem::path pathComponent = std::string(40, 'a');
+ while (deepCwd.size() < 3800) {
+ if (!boost::filesystem::create_directory(deepCwd / pathComponent, ec)) {
+ // Could not mkdir, so we are done and will stick with the previous level, thanks.
+ break;
+ }
+
+ boost::filesystem::current_path(deepCwd / pathComponent, ec);
+ if (ec) {
+ // Could not chdir, so we are done and will stick with the previous level, thanks.
+ break;
+ }
+
+ deepCwd /= pathComponent;
+ }
+
+ // Run the actual test.
+ Status res = ::mongo::setupServerOptions(argv);
+
+ ASSERT_OK(res);
+ ASSERT_FALSE(::mongo::serverGlobalParams.cwd.empty());
+ ASSERT_EQUALS(::mongo::serverGlobalParams.cwd, deepCwd.native())
+ << "serverGlobalParams.cwd is " << ::mongo::serverGlobalParams.cwd.size()
+ << " bytes long and deepCwd is " << deepCwd.size() << " bytes long.";
+ ASSERT_EQUALS(::mongo::serverGlobalParams.argvArray.nFields(), static_cast<int>(argv.size()));
+}
+
+TEST(SetupOptions, UnlinkedCwd) {
+ std::vector<std::string> argv;
+ argv.push_back("binaryname");
+
+ // Save the original cwd.
+ boost::system::error_code ec;
+ boost::filesystem::path cwd = boost::filesystem::current_path(ec);
+ ASSERT_BOOST_SUCCESS(ec);
+
+ std::string unlinkDir;
+
+ auto cleanup = ::mongo::MakeGuard([&] {
+ boost::filesystem::current_path(cwd, ec);
+ if (!unlinkDir.empty()) {
+ boost::filesystem::remove(cwd / unlinkDir, ec);
+ }
+ });
+
+ // mkdir our own unlink dir
+ unsigned int i = 0;
+ while (1) {
+ ::mongo::StringBuilder sb;
+ sb << "unlink" << i;
+ unlinkDir = sb.str();
+ if (boost::filesystem::create_directory(unlinkDir, ec)) {
+ // New directory was created, great.
+ break;
+ } else {
+ // Directory failed to create, or already existed (so try the next one).
+ ASSERT_BOOST_SUCCESS(ec);
+ i++;
+ }
+ }
+
+ // chdir into the unlink dir
+ boost::filesystem::current_path(unlinkDir, ec);
+ ASSERT_BOOST_SUCCESS(ec);
+
+ // Naive "rmdir ." doesn't work on Linux.
+ // Naive rmdir of cwd doesn't work on Solaris doesn't work (no matter how it's specified).
+ // So we use a subprocess to unlink the dir.
+ pid_t pid = fork();
+ ASSERT_NOT_EQUALS(pid, -1) << "unable to fork: " << ::mongo::errnoWithDescription();
+ if (pid == 0) {
+ // Subprocess
+ // No exceptions, ASSERT(), FAIL() or logging.
+ // Only signalling to the parent process via exit status.
+ // Only quickexit via _exit() (which prevents the cleanup guard from running, which is
+ // what we want).
+
+ // cd to root
+ boost::filesystem::current_path("/", ec);
+ if (ec) {
+ _exit(1);
+ }
+
+ // rmdir using absolute path
+ boost::filesystem::remove(cwd / unlinkDir, ec);
+ if (ec) {
+ _exit(2);
+ }
+
+ _exit(0);
+ }
+
+ // Wait for the subprocess to finish running the above code and exit, and check its result.
+ int status;
+ ASSERT_NOT_EQUALS(-1, waitpid(pid, &status, 0));
+ ASSERT(WIFEXITED(status));
+ int exitstatus = WEXITSTATUS(status);
+ if (exitstatus == 1) {
+ FAIL("subprocess was unable to cd to /");
+ } else if (exitstatus == 2) {
+ FAIL("subprocess was unable to remove unlink dir");
+ } else {
+ ASSERT_EQUALS(0, exitstatus);
+ }
+
+ // Run the actual test.
+ Status res = ::mongo::setupServerOptions(argv);
+
+ ASSERT_NOT_OK(res);
+ ASSERT_STRING_CONTAINS(res.toString(), "Cannot get current working directory");
+ ASSERT_STRING_CONTAINS(res.toString(), "No such file or directory"); // message for ENOENT
+}
+
+#endif // !defined(_WIN32)
+
} // unnamed namespace