blob: 679eab7d30e5f2864072acdc415b8b3618ddcb53 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "base/process_mutex.h"
#ifdef OS_WIN
#include <windows.h>
#else
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#endif // OS_WIN
#include <map>
#include <string>
#include <utility>
#include "base/file_util.h"
#include "base/logging.h"
#include "base/mutex.h"
#include "base/singleton.h"
#include "base/system_util.h"
#include "base/util.h"
#ifdef OS_WIN
#include "base/win_sandbox.h"
#endif // OS_WIN
namespace mozc {
namespace {
string CreateProcessMutexFileName(const char *name) {
name = (name == NULL) ? "NULL" : name;
#ifdef OS_WIN
string basename;
#else
string basename = ".";
#endif
basename += name;
basename += ".lock";
return FileUtil::JoinPath(SystemUtil::GetUserProfileDirectory(), basename);
}
} // namespace
bool ProcessMutex::Lock() {
return LockAndWrite("");
}
#ifdef OS_WIN
ProcessMutex::ProcessMutex(const char *name)
: handle_(INVALID_HANDLE_VALUE), locked_(false) {
filename_ = CreateProcessMutexFileName(name);
}
ProcessMutex::~ProcessMutex() {
UnLock();
}
bool ProcessMutex::LockAndWrite(const string &message) {
if (locked_) {
VLOG(1) << filename_ << " is already locked";
return false;
}
wstring wfilename;
Util::UTF8ToWide(filename_.c_str(), &wfilename);
const DWORD kAttribute =
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
FILE_FLAG_DELETE_ON_CLOSE;
SECURITY_ATTRIBUTES serucity_attributes = {};
if (!WinSandbox::MakeSecurityAttributes(WinSandbox::kSharableFileForRead,
&serucity_attributes)) {
return false;
}
handle_.reset(::CreateFileW(
wfilename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, &serucity_attributes,
CREATE_ALWAYS, kAttribute, NULL));
::LocalFree(serucity_attributes.lpSecurityDescriptor);
locked_ = (handle_.get() != NULL);
if (!locked_) {
VLOG(1) << "already locked";
return locked_;
}
if (!message.empty()) {
DWORD size = 0;
if (!::WriteFile(handle_.get(), message.data(), message.size(),
&size, NULL)) {
const int last_error = ::GetLastError();
LOG(ERROR) << "Cannot write message: " << message
<< ", last_error:" << last_error;
UnLock();
return false;
}
}
return locked_;
}
bool ProcessMutex::UnLock() {
handle_.reset(NULL);
FileUtil::Unlink(filename_);
locked_ = false;
return true;
}
#elif defined(MOZC_USE_PEPPER_FILE_IO) // OS_WIN
namespace {
// In NaCl we can't consider about the processes.
// So we just implement inprocess named lock service.
class NamedLockManager {
public:
NamedLockManager() {}
~NamedLockManager() {}
bool Lock(const string &filename, const string &message) {
scoped_lock l(&mutex_);
return lock_map_.insert(make_pair(filename, message)).second;
}
void UnLock(const string &filename) {
scoped_lock l(&mutex_);
lock_map_.erase(filename);
return;
}
private:
Mutex mutex_;
map<string, string> lock_map_;
DISALLOW_COPY_AND_ASSIGN(NamedLockManager);
};
} // namespace
ProcessMutex::ProcessMutex(const char *name) : locked_(false) {
filename_ = CreateProcessMutexFileName(name);
}
ProcessMutex::~ProcessMutex() {
UnLock();
}
bool ProcessMutex::LockAndWrite(const string &message) {
return Singleton<NamedLockManager>::get()->Lock(filename_, message);
}
bool ProcessMutex::UnLock() {
Singleton<NamedLockManager>::get()->UnLock(filename_);
return true;
}
#else // !OS_WIN && !MOZC_USE_PEPPER_FILE_IO
namespace {
// Special workaround for the bad treatment of fcntl.
// Basically, fcntl provides "per-process" file-locking.
// When a process has multiple fds on the same file,
// file lock is released if one fd is closed. This is not
// an expected behavior for Mozc.
//
// Linux man says
// "As well as being removed by an explicit F_UNLCK, record locks are
// automatically released when the process terminates or
// if it closes any file descriptor referring to a file on which
// locks are held. This is bad: it means that a process can lose
// the locks on a file like /etc/passwd or /etc/mtab when
// for some reason a library function decides to open,
// read and close it."
//
// FileLockerManager a wrapper class providing per-filename
// file locking implemented upon fcntl. Multiple threads on
// the same process share one fd for file locking.
//
// We will be able to use flock() instead of fcntl since,
// flock() provides "per-fd" file locking. However, we don't
// use it because flock() doesn't work on NFS.
class FileLockManager {
public:
bool Lock(const string &filename, int *fd) {
scoped_lock l(&mutex_);
if (fd == NULL) {
LOG(ERROR) << "fd is NULL";
return false;
}
if (filename.empty()) {
LOG(ERROR) << "filename is empty";
return false;
}
if (fdmap_.find(filename) != fdmap_.end()) {
VLOG(1) << filename << " is already locked by the same process";
return false;
}
chmod(filename.c_str(), 0600); // write temporary
*fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
if (-1 == *fd) {
LOG(ERROR) << "open() failed:" << ::strerror(errno);
return false;
}
struct flock command;
command.l_type = F_WRLCK;
command.l_whence = SEEK_SET;
command.l_start = 0;
command.l_len = 0;
const int result = ::fcntl(*fd, F_SETLK, &command);
if (-1 == result) { // failed
::close(*fd);
LOG(WARNING) << "already locked";
return false; // another server is already running
}
fdmap_.insert(make_pair(filename, *fd));
return true;
}
void UnLock(const string &filename) {
scoped_lock l(&mutex_);
map<string, int>::iterator it = fdmap_.find(filename);
if (it == fdmap_.end()) {
LOG(ERROR) << filename << " is not locked";
return;
}
::close(it->second);
FileUtil::Unlink(filename);
fdmap_.erase(it);
}
FileLockManager() {}
~FileLockManager() {
for (map<string, int>::const_iterator it = fdmap_.begin();
it != fdmap_.end(); ++it) {
::close(it->second);
}
fdmap_.clear();
}
private:
Mutex mutex_;
map<string, int> fdmap_;
};
} // namespace
ProcessMutex::ProcessMutex(const char *name) : locked_(false) {
filename_ = CreateProcessMutexFileName(name);
}
ProcessMutex::~ProcessMutex() {
if (locked_) {
UnLock();
}
}
bool ProcessMutex::LockAndWrite(const string &message) {
int fd = -1;
if (!Singleton<FileLockManager>::get()->Lock(filename_, &fd)) {
VLOG(1) << filename_ << " is already locked";
return false;
}
if (fd == -1) {
LOG(ERROR) << "file descriptor is -1";
return false;
}
if (!message.empty()) {
if (write(fd, message.data(), message.size()) !=
message.size()) {
LOG(ERROR) << "Cannot write message: " << message;
UnLock();
return false;
}
}
// changed it to read only mode.
chmod(filename_.c_str(), 0400);
locked_ = true;
return true;
}
bool ProcessMutex::UnLock() {
if (locked_) {
Singleton<FileLockManager>::get()->UnLock(filename_);
}
locked_ = false;
return true;
}
#endif
} // namespace mozc