summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2014-12-29 16:47:59 -0500
committerBenety Goh <benety@mongodb.com>2015-01-05 15:52:47 -0500
commitd9ce31ea2237d4aa78656fed785e7e5b91c93843 (patch)
treefafb16c58e74b57fdb3387ea51a98f2d029c8369
parent9bfa0cc0f41dc8d425263cbf841f62b3e9d484da (diff)
downloadmongo-d9ce31ea2237d4aa78656fed785e7e5b91c93843.tar.gz
SERVER-16677 added platform-dependent class to management creation/locking of mongod.lock in data directory.
-rw-r--r--src/mongo/db/storage/SConscript18
-rw-r--r--src/mongo/db/storage/storage_engine_lock_file.h95
-rw-r--r--src/mongo/db/storage/storage_engine_lock_file_posix.cpp192
-rw-r--r--src/mongo/db/storage/storage_engine_lock_file_test.cpp173
-rw-r--r--src/mongo/db/storage/storage_engine_lock_file_win.cpp190
5 files changed, 668 insertions, 0 deletions
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index 7210c44f420..aec1069cf09 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -81,6 +81,24 @@ env.Library(
)
env.Library(
+ target='storage_engine_lock_file',
+ source=[
+ 'storage_engine_lock_file_${OS_FAMILY}.cpp',
+ ],
+ LIBDEPS=[
+ ]
+ )
+
+env.CppUnitTest(
+ target= 'storage_engine_lock_file_test',
+ source = 'storage_engine_lock_file_test.cpp',
+ LIBDEPS=[
+ 'storage_engine_lock_file',
+ '$BUILD_DIR/mongo/mongocommon',
+ ],
+)
+
+env.Library(
target='storage_engine_metadata',
source=[
'storage_engine_metadata.cpp',
diff --git a/src/mongo/db/storage/storage_engine_lock_file.h b/src/mongo/db/storage/storage_engine_lock_file.h
new file mode 100644
index 00000000000..b28ccc29823
--- /dev/null
+++ b/src/mongo/db/storage/storage_engine_lock_file.h
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2014 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.
+ */
+
+#pragma once
+
+#include <boost/scoped_ptr.hpp>
+#include <string>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+
+namespace mongo {
+
+ class StorageEngineLockFile {
+ MONGO_DISALLOW_COPYING(StorageEngineLockFile);
+ public:
+
+ /**
+ * Checks existing lock file, if present, to see if it contains data from a previous
+ * unclean shutdown. A clean shutdown should have produced a zero length lock file.
+ * Uses open() to read existing lock file or create new file.
+ * Uses boost::filesystem to check lock file so may throw boost::exception.
+ */
+ StorageEngineLockFile(const std::string& dbpath);
+
+ virtual ~StorageEngineLockFile();
+
+ /**
+ * Returns the path to the lock file.
+ */
+ std::string getFilespec() const;
+
+ /**
+ * Returns true if lock file was not zeroed out due to previous unclean shutdown.
+ * This state is evaluated at object initialization to allow storage engine
+ * to make decisions on recovery based on this information after open() has been called.
+ */
+ bool createdByUncleanShutdown() const;
+
+ /**
+ * Opens and locks 'mongod.lock' in 'dbpath' directory.
+ */
+ Status open();
+
+ /**
+ * Closes lock file handles.
+ */
+ void close();
+
+ /**
+ * Writes current process ID to file.
+ * Fails if lock file has not been opened.
+ */
+ Status writePid();
+
+ /**
+ * Truncates file contents and releases file locks.
+ */
+ void clearPidAndUnlock();
+
+ private:
+ std::string _dbpath;
+ std::string _filespec;
+ bool _uncleanShutdown;
+
+ class LockFileHandle;
+ boost::scoped_ptr<LockFileHandle> _lockFileHandle;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/db/storage/storage_engine_lock_file_posix.cpp b/src/mongo/db/storage/storage_engine_lock_file_posix.cpp
new file mode 100644
index 00000000000..126c53ec4c8
--- /dev/null
+++ b/src/mongo/db/storage/storage_engine_lock_file_posix.cpp
@@ -0,0 +1,192 @@
+/**
+ * Copyright (C) 2014 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/storage/storage_engine_lock_file.h"
+
+#include <boost/filesystem.hpp>
+#include <fcntl.h>
+#include <ostream>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sstream>
+
+#include "mongo/platform/process_id.h"
+#include "mongo/util/log.h"
+#include "mongo/util/paths.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+namespace {
+
+ const std::string kLockFileBasename = "mongod.lock";
+
+} // namespace
+
+ class StorageEngineLockFile::LockFileHandle {
+ public:
+ static const int kInvalidFd = -1;
+ LockFileHandle() : _fd(kInvalidFd) { }
+ bool isValid() const { return _fd != kInvalidFd; }
+ void clear() { _fd = kInvalidFd; }
+ int _fd;
+ };
+
+ StorageEngineLockFile::StorageEngineLockFile(const std::string& dbpath)
+ : _dbpath(dbpath),
+ _filespec((boost::filesystem::path(_dbpath) / kLockFileBasename).string()),
+ _uncleanShutdown(boost::filesystem::exists(_filespec) &&
+ boost::filesystem::file_size(_filespec) > 0),
+ _lockFileHandle(new LockFileHandle()) {
+ }
+
+ StorageEngineLockFile::~StorageEngineLockFile() { }
+
+ std::string StorageEngineLockFile::getFilespec() const {
+ return _filespec;
+ }
+
+ bool StorageEngineLockFile::createdByUncleanShutdown() const {
+ return _uncleanShutdown;
+ }
+
+ Status StorageEngineLockFile::open() {
+ try {
+ if (!boost::filesystem::exists(_dbpath)) {
+ return Status(ErrorCodes::NonExistentPath, str::stream()
+ << "Data directory " << _dbpath << " not found.");
+ }
+ }
+ catch (const std::exception& ex) {
+ return Status(ErrorCodes::UnknownError, str::stream()
+ << "Unable to check existence of data directory "
+ << _dbpath << ": " << ex.what());
+ }
+
+ int lockFile = ::open(_filespec.c_str(), O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (lockFile <= 0) {
+ int errorcode = errno;
+ return Status(ErrorCodes::DBPathInUse, str::stream()
+ << "Unable to create/open lock file: "
+ << _filespec << ' ' << errnoWithDescription(errorcode)
+ << " Is a mongod instance already running?");
+ }
+#if !defined(__sunos__)
+ int ret = ::flock(lockFile, LOCK_EX | LOCK_NB);
+#else
+ struct flock fileLockInfo = {0};
+ fileLockInfo.l_type = F_WRLCK;
+ fileLockInfo.l_whence = SEEK_SET;
+ int ret = ::fcntl(lockFile, F_SETLK, &fileLockInfo);
+#endif // !defined(__sunos__)
+ if (ret != 0) {
+ int errorcode = errno;
+ ::close(lockFile);
+ return Status(ErrorCodes::DBPathInUse, str::stream()
+ << "Unable to lock file: "
+ << _filespec << ' ' << errnoWithDescription(errorcode)
+ << ". Is a mongod instance already running?");
+ }
+ _lockFileHandle->_fd = lockFile;
+ return Status::OK();
+ }
+
+ void StorageEngineLockFile::close() {
+ if (!_lockFileHandle->isValid()) {
+ return;
+ }
+ ::close(_lockFileHandle->_fd);
+ _lockFileHandle->clear();
+ }
+
+ Status StorageEngineLockFile::writePid() {
+ if (!_lockFileHandle->isValid()) {
+ return Status(ErrorCodes::FileNotOpen, str::stream()
+ << "Unable to write process ID to " << _filespec
+ << " because file has not been opened.");
+ }
+
+ if (::ftruncate(_lockFileHandle->_fd, 0)) {
+ int errorcode = errno;
+ return Status(ErrorCodes::FileStreamFailed, str::stream()
+ << errnoWithDescription(errorcode));
+ }
+
+ ProcessId pid = ProcessId::getCurrent();
+ std::stringstream ss;
+ ss << pid << std::endl;
+ std::string pidStr = ss.str();
+ int bytesWritten = ::write(_lockFileHandle->_fd, pidStr.c_str(), pidStr.size());
+ if (bytesWritten < 0) {
+ int errorcode = errno;
+ return Status(ErrorCodes::FileStreamFailed, str::stream()
+ << "Unable to write process id " << pid.toString() << " to file: "
+ << _filespec << ' ' << errnoWithDescription(errorcode));
+
+ }
+ else if (bytesWritten == 0) {
+ return Status(ErrorCodes::FileStreamFailed, str::stream()
+ << "Unable to write process id " << pid.toString() << " to file: "
+ << _filespec << " no data written.");
+ }
+
+ ::fsync(_lockFileHandle->_fd);
+ flushMyDirectory(_filespec);
+
+ return Status::OK();
+ }
+
+ void StorageEngineLockFile::clearPidAndUnlock() {
+ if (!_lockFileHandle->isValid()) {
+ return;
+ }
+ log() << "shutdown: removing fs lock...";
+ // This ought to be an unlink(), but Eliot says the last
+ // time that was attempted, there was a race condition
+ // with acquirePathLock().
+ if(::ftruncate(_lockFileHandle->_fd, 0)) {
+ int errorcode = errno;
+ log() << "couldn't remove fs lock " << errnoWithDescription(errorcode);
+ }
+#if !defined(__sunos__)
+ ::flock(_lockFileHandle->_fd, LOCK_UN);
+#else
+ struct flock fileLockInfo = {0};
+ fileLockInfo.l_type = F_UNLCK;
+ fileLockInfo.l_whence = SEEK_SET;
+ ::fcntl(_lockFileHandle->_fd, F_SETLK, &fileLockInfo);
+#endif // !defined(__sunos__)
+ }
+
+} // namespace mongo
diff --git a/src/mongo/db/storage/storage_engine_lock_file_test.cpp b/src/mongo/db/storage/storage_engine_lock_file_test.cpp
new file mode 100644
index 00000000000..2ca432d0d71
--- /dev/null
+++ b/src/mongo/db/storage/storage_engine_lock_file_test.cpp
@@ -0,0 +1,173 @@
+/**
+ * Copyright 2014 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/platform/basic.h"
+
+#include <boost/filesystem.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <fstream>
+#include <ostream>
+
+#include "mongo/db/storage/storage_engine_lock_file.h"
+#include "mongo/platform/process_id.h"
+#include "mongo/unittest/temp_dir.h"
+#include "mongo/unittest/unittest.h"
+
+namespace {
+
+ using std::string;
+ using mongo::unittest::TempDir;
+
+ using namespace mongo;
+
+ TEST(StorageEngineLockFileTest, UncleanShutdownNoExistingFile) {
+ TempDir tempDir("StorageEngineLockFileTest_UncleanShutdownNoExistingFile");
+ StorageEngineLockFile lockFile(tempDir.path());
+ ASSERT_FALSE(lockFile.createdByUncleanShutdown());
+ }
+
+ TEST(StorageEngineLockFileTest, UncleanShutdownEmptyExistingFile) {
+ TempDir tempDir("StorageEngineLockFileTest_UncleanShutdownEmptyExistingFile");
+ {
+ std::string filename(tempDir.path() + "/mongod.lock");
+ std::ofstream(filename.c_str());
+ }
+ StorageEngineLockFile lockFile(tempDir.path());
+ ASSERT_FALSE(lockFile.createdByUncleanShutdown());
+ }
+
+ TEST(StorageEngineLockFileTest, UncleanShutdownNonEmptyExistingFile) {
+ TempDir tempDir("StorageEngineLockFileTest_UncleanShutdownNonEmptyExistingFile");
+ {
+ std::string filename(tempDir.path() + "/mongod.lock");
+ std::ofstream ofs(filename.c_str());
+ ofs << 12345 << std::endl;
+ }
+ StorageEngineLockFile lockFile(tempDir.path());
+ ASSERT_TRUE(lockFile.createdByUncleanShutdown());
+ }
+
+ TEST(StorageEngineLockFileTest, OpenInvalidDirectory) {
+ StorageEngineLockFile lockFile("no_such_directory");
+ ASSERT_EQUALS((boost::filesystem::path("no_such_directory") / "mongod.lock").string(),
+ lockFile.getFilespec());
+ Status status = lockFile.open();
+ ASSERT_NOT_OK(status);
+ ASSERT_EQUALS(ErrorCodes::NonExistentPath, status.code());
+ }
+
+ // Cause ::open() to fail by providing a regular file instead of a directory for 'dbpath'.
+ TEST(StorageEngineLockFileTest, OpenInvalidFilename) {
+ TempDir tempDir("StorageEngineLockFileTest_OpenInvalidFilename");
+ std::string filename(tempDir.path() + "/some_file");
+ std::ofstream(filename.c_str());
+ StorageEngineLockFile lockFile(filename);
+ Status status = lockFile.open();
+ ASSERT_NOT_OK(status);
+ ASSERT_EQUALS(ErrorCodes::DBPathInUse, status.code());
+ }
+
+ TEST(StorageEngineLockFileTest, OpenNoExistingLockFile) {
+ TempDir tempDir("StorageEngineLockFileTest_OpenNoExistingLockFile");
+ StorageEngineLockFile lockFile(tempDir.path());
+ ASSERT_OK(lockFile.open());
+ lockFile.close();
+ }
+
+ TEST(StorageEngineLockFileTest, OpenEmptyLockFile) {
+ TempDir tempDir("StorageEngineLockFileTest_OpenEmptyLockFile");
+ StorageEngineLockFile lockFile(tempDir.path());
+ std::string filename(lockFile.getFilespec());
+ std::ofstream(filename.c_str());
+ ASSERT_OK(lockFile.open());
+ lockFile.close();
+ }
+
+ TEST(StorageEngineLockFileTest, WritePidFileNotOpened) {
+ TempDir tempDir("StorageEngineLockFileTest_WritePidFileNotOpened");
+ StorageEngineLockFile lockFile(tempDir.path());
+ Status status = lockFile.writePid();
+ ASSERT_NOT_OK(status);
+ ASSERT_EQUALS(ErrorCodes::FileNotOpen, status.code());
+ }
+
+ TEST(StorageEngineLockFileTest, WritePidFileOpened) {
+ TempDir tempDir("StorageEngineLockFileTest_WritePidFileOpened");
+ StorageEngineLockFile lockFile(tempDir.path());
+ ASSERT_OK(lockFile.open());
+ ASSERT_OK(lockFile.writePid());
+ lockFile.close();
+
+ // Read PID from lock file.
+ std::string filename(lockFile.getFilespec());
+ std::ifstream ifs(filename.c_str());
+ int64_t pidFromLockFile = 0;
+ ASSERT_TRUE(ifs >> pidFromLockFile);
+ ASSERT_EQUALS(ProcessId::getCurrent().asInt64(), pidFromLockFile);
+ }
+
+ // Existing data in lock file must be removed before writing process ID.
+ TEST(StorageEngineLockFileTest, WritePidTruncateExistingFile) {
+ TempDir tempDir("StorageEngineLockFileTest_WritePidTruncateExistingFile");
+ StorageEngineLockFile lockFile(tempDir.path());
+ {
+ std::string filename(tempDir.path() + "/mongod.lock");
+ std::ofstream ofs(filename.c_str());
+ std::string currentPidStr = ProcessId::getCurrent().toString();
+ ASSERT_FALSE(currentPidStr.empty());
+ ofs << std::string(currentPidStr.size() * 100, 'X') << std::endl;
+ }
+ ASSERT_OK(lockFile.open());
+ ASSERT_OK(lockFile.writePid());
+ lockFile.close();
+
+ // Read PID from lock file.
+ std::string filename(lockFile.getFilespec());
+ std::ifstream ifs(filename.c_str());
+ int64_t pidFromLockFile = 0;
+ ASSERT_TRUE(ifs >> pidFromLockFile);
+ ASSERT_EQUALS(ProcessId::getCurrent().asInt64(), pidFromLockFile);
+
+ // There should not be any data in the file after the process ID.
+ std::string extraData;
+ ASSERT_FALSE(ifs >> extraData);
+ }
+
+ TEST(StorageEngineLockFileTest, ClearPidAndUnlock) {
+ TempDir tempDir("StorageEngineLockFileTest_ClearPidAndUnlock");
+ StorageEngineLockFile lockFile(tempDir.path());
+ ASSERT_OK(lockFile.open());
+ ASSERT_OK(lockFile.writePid());
+
+ // Clear lock file contents.
+ lockFile.clearPidAndUnlock();
+ ASSERT_TRUE(boost::filesystem::exists(lockFile.getFilespec()));
+ ASSERT_EQUALS(0U, boost::filesystem::file_size(lockFile.getFilespec()));
+ }
+
+} // namespace
diff --git a/src/mongo/db/storage/storage_engine_lock_file_win.cpp b/src/mongo/db/storage/storage_engine_lock_file_win.cpp
new file mode 100644
index 00000000000..41fc74ea736
--- /dev/null
+++ b/src/mongo/db/storage/storage_engine_lock_file_win.cpp
@@ -0,0 +1,190 @@
+/**
+ * Copyright (C) 2014 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/storage/storage_engine_lock_file.h"
+
+#include <boost/filesystem.hpp>
+#include <io.h>
+#include <ostream>
+#include <sstream>
+
+#include "mongo/platform/process_id.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+namespace {
+
+ const std::string kLockFileBasename = "mongod.lock";
+
+ Status _truncateFile(HANDLE handle) {
+ invariant(handle != INVALID_HANDLE_VALUE);
+
+ LARGE_INTEGER largeint;
+ largeint.QuadPart = 0;
+ if (::SetFilePointerEx(handle, largeint, NULL, FILE_BEGIN) == FALSE) {
+ int errorcode = GetLastError();
+ return Status(ErrorCodes::FileStreamFailed, str::stream()
+ << "Unable to truncate lock file (SetFilePointerEx failed) "
+ << errnoWithDescription(errorcode));
+ }
+
+ if (::SetEndOfFile(handle) == FALSE) {
+ int errorcode = GetLastError();
+ return Status(ErrorCodes::FileStreamFailed, str::stream()
+ << "Unable to truncate lock file (SetEndOfFile failed) "
+ << errnoWithDescription(errorcode));
+ }
+
+ return Status::OK();
+ }
+
+} // namespace
+
+ class StorageEngineLockFile::LockFileHandle {
+ public:
+ LockFileHandle() : _handle(INVALID_HANDLE_VALUE) { }
+ bool isValid() const { return _handle != INVALID_HANDLE_VALUE; }
+ void clear() { _handle = INVALID_HANDLE_VALUE; }
+ HANDLE _handle;
+ };
+
+ StorageEngineLockFile::StorageEngineLockFile(const std::string& dbpath)
+ : _dbpath(dbpath),
+ _filespec((boost::filesystem::path(_dbpath) / kLockFileBasename).string()),
+ _uncleanShutdown(boost::filesystem::exists(_filespec) &&
+ boost::filesystem::file_size(_filespec) > 0),
+ _lockFileHandle(new LockFileHandle()) {
+ }
+
+ StorageEngineLockFile::~StorageEngineLockFile() { }
+
+ std::string StorageEngineLockFile::getFilespec() const {
+ return _filespec;
+ }
+
+ bool StorageEngineLockFile::createdByUncleanShutdown() const {
+ return _uncleanShutdown;
+ }
+
+ Status StorageEngineLockFile::open() {
+ try {
+ if (!boost::filesystem::exists(_dbpath)) {
+ return Status(ErrorCodes::NonExistentPath, str::stream()
+ << "Data directory " << _dbpath << " not found.");
+ }
+ }
+ catch (const std::exception& ex) {
+ return Status(ErrorCodes::UnknownError, str::stream()
+ << "Unable to check existence of data directory "
+ << _dbpath << ": " << ex.what());
+ }
+
+ HANDLE lockFileHandle = CreateFileA(_filespec.c_str(), GENERIC_READ | GENERIC_WRITE,
+ 0 /* do not allow anyone else access */, NULL,
+ OPEN_ALWAYS /* success if fh can open */, 0, NULL);
+
+ if (lockFileHandle == INVALID_HANDLE_VALUE) {
+ int errorcode = GetLastError();
+ return Status(ErrorCodes::DBPathInUse, str::stream()
+ << "Unable to create/open lock file: " << _filespec << ' '
+ << errnoWithDescription(errorcode)
+ << ". Is a mongod instance already running?");
+ }
+ _lockFileHandle->_handle = lockFileHandle;
+ return Status::OK();
+ }
+
+ void StorageEngineLockFile::close() {
+ if (!_lockFileHandle->isValid()) {
+ return;
+ }
+ CloseHandle(_lockFileHandle->_handle);
+ _lockFileHandle->clear();
+ }
+
+ Status StorageEngineLockFile::writePid() {
+ if (!_lockFileHandle->isValid()) {
+ return Status(ErrorCodes::FileNotOpen, str::stream()
+ << "Unable to write process ID to " << _filespec
+ << " because file has not been opened.");
+ }
+
+ Status status = _truncateFile(_lockFileHandle->_handle);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ ProcessId pid = ProcessId::getCurrent();
+ std::stringstream ss;
+ ss << pid << std::endl;
+ std::string pidStr = ss.str();
+ DWORD bytesWritten = 0;
+ if (::WriteFile(_lockFileHandle->_handle,
+ static_cast<LPCVOID>(pidStr.c_str()),
+ static_cast<DWORD>(pidStr.size()),
+ &bytesWritten,
+ NULL) == FALSE) {
+ int errorcode = GetLastError();
+ return Status(ErrorCodes::FileStreamFailed, str::stream()
+ << "Unable to write process id " << pid.toString() << " to file: "
+ << _filespec << ' ' << errnoWithDescription(errorcode));
+ }
+ else if (bytesWritten == 0) {
+ return Status(ErrorCodes::FileStreamFailed, str::stream()
+ << "Unable to write process id " << pid.toString() << " to file: "
+ << _filespec << " no data written.");
+ }
+
+ ::FlushFileBuffers(_lockFileHandle->_handle);
+
+ return Status::OK();
+ }
+
+ void StorageEngineLockFile::clearPidAndUnlock() {
+ if (!_lockFileHandle->isValid()) {
+ return;
+ }
+ log() << "shutdown: removing fs lock...";
+ // This ought to be an unlink(), but Eliot says the last
+ // time that was attempted, there was a race condition
+ // with acquirePathLock().
+ Status status = _truncateFile(_lockFileHandle->_handle);
+ if (!status.isOK()) {
+ log() << "couldn't remove fs lock " << status.toString();
+ }
+ CloseHandle(_lockFileHandle->_handle);
+ _lockFileHandle->clear();
+ }
+
+} // namespace mongo