1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "base/process_mutex.h"
31
32 #ifdef OS_WIN
33 #include <windows.h>
34 #else
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #endif // OS_WIN
41
42 #include <map>
43 #include <string>
44 #include <utility>
45
46 #include "base/file_util.h"
47 #include "base/logging.h"
48 #include "base/mutex.h"
49 #include "base/singleton.h"
50 #include "base/system_util.h"
51 #include "base/util.h"
52 #ifdef OS_WIN
53 #include "base/win_sandbox.h"
54 #endif // OS_WIN
55
56 namespace mozc {
57
58 namespace {
59
CreateProcessMutexFileName(const char * name)60 string CreateProcessMutexFileName(const char *name) {
61 name = (name == NULL) ? "NULL" : name;
62
63 #ifdef OS_WIN
64 string basename;
65 #else
66 string basename = ".";
67 #endif
68
69 basename += name;
70 basename += ".lock";
71
72 return FileUtil::JoinPath(SystemUtil::GetUserProfileDirectory(), basename);
73 }
74 } // namespace
75
Lock()76 bool ProcessMutex::Lock() {
77 return LockAndWrite("");
78 }
79
80 #ifdef OS_WIN
81
ProcessMutex(const char * name)82 ProcessMutex::ProcessMutex(const char *name)
83 : handle_(INVALID_HANDLE_VALUE), locked_(false) {
84 filename_ = CreateProcessMutexFileName(name);
85 }
86
~ProcessMutex()87 ProcessMutex::~ProcessMutex() {
88 UnLock();
89 }
90
LockAndWrite(const string & message)91 bool ProcessMutex::LockAndWrite(const string &message) {
92 if (locked_) {
93 VLOG(1) << filename_ << " is already locked";
94 return false;
95 }
96
97 std::wstring wfilename;
98 Util::UTF8ToWide(filename_, &wfilename);
99 const DWORD kAttribute =
100 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
101 FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
102 FILE_FLAG_DELETE_ON_CLOSE;
103
104 SECURITY_ATTRIBUTES serucity_attributes = {};
105 if (!WinSandbox::MakeSecurityAttributes(WinSandbox::kSharableFileForRead,
106 &serucity_attributes)) {
107 return false;
108 }
109 handle_.reset(::CreateFileW(
110 wfilename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, &serucity_attributes,
111 CREATE_ALWAYS, kAttribute, NULL));
112 ::LocalFree(serucity_attributes.lpSecurityDescriptor);
113
114 locked_ = (handle_.get() != NULL);
115
116 if (!locked_) {
117 VLOG(1) << "already locked";
118 return locked_;
119 }
120
121 if (!message.empty()) {
122 DWORD size = 0;
123 if (!::WriteFile(handle_.get(), message.data(), message.size(),
124 &size, NULL)) {
125 const int last_error = ::GetLastError();
126 LOG(ERROR) << "Cannot write message: " << message
127 << ", last_error:" << last_error;
128 UnLock();
129 return false;
130 }
131 }
132
133 return locked_;
134 }
135
UnLock()136 bool ProcessMutex::UnLock() {
137 handle_.reset(NULL);
138 FileUtil::Unlink(filename_);
139 locked_ = false;
140 return true;
141 }
142
143 #elif defined(MOZC_USE_PEPPER_FILE_IO) // OS_WIN
144 namespace {
145
146 // In NaCl we can't consider about the processes.
147 // So we just implement inprocess named lock service.
148 class NamedLockManager {
149 public:
NamedLockManager()150 NamedLockManager() {}
~NamedLockManager()151 ~NamedLockManager() {}
Lock(const string & filename,const string & message)152 bool Lock(const string &filename, const string &message) {
153 scoped_lock l(&mutex_);
154 return lock_map_.insert(std::make_pair(filename, message)).second;
155 }
UnLock(const string & filename)156 void UnLock(const string &filename) {
157 scoped_lock l(&mutex_);
158 lock_map_.erase(filename);
159 return;
160 }
161
162 private:
163 Mutex mutex_;
164 std::map<string, string> lock_map_;
165 DISALLOW_COPY_AND_ASSIGN(NamedLockManager);
166 };
167
168 } // namespace
169
ProcessMutex(const char * name)170 ProcessMutex::ProcessMutex(const char *name) : locked_(false) {
171 filename_ = CreateProcessMutexFileName(name);
172 }
173
~ProcessMutex()174 ProcessMutex::~ProcessMutex() {
175 UnLock();
176 }
177
LockAndWrite(const string & message)178 bool ProcessMutex::LockAndWrite(const string &message) {
179 if (!Singleton<NamedLockManager>::get()->Lock(filename_, message)) {
180 VLOG(1) << filename_ << " is already locked";
181 return false;
182 }
183 locked_ = true;
184 return true;
185 }
186
UnLock()187 bool ProcessMutex::UnLock() {
188 Singleton<NamedLockManager>::get()->UnLock(filename_);
189 locked_ = false;
190 return true;
191 }
192
193 #else // !OS_WIN && !MOZC_USE_PEPPER_FILE_IO
194
195 namespace {
196 // Special workaround for the bad treatment of fcntl.
197 // Basically, fcntl provides "per-process" file-locking.
198 // When a process has multiple fds on the same file,
199 // file lock is released if one fd is closed. This is not
200 // an expected behavior for Mozc.
201 //
202 // Linux man says
203 // "As well as being removed by an explicit F_UNLCK, record locks are
204 // automatically released when the process terminates or
205 // if it closes any file descriptor referring to a file on which
206 // locks are held. This is bad: it means that a process can lose
207 // the locks on a file like /etc/passwd or /etc/mtab when
208 // for some reason a library function decides to open,
209 // read and close it."
210 //
211 // FileLockerManager a wrapper class providing per-filename
212 // file locking implemented upon fcntl. Multiple threads on
213 // the same process share one fd for file locking.
214 //
215 // We will be able to use flock() instead of fcntl since,
216 // flock() provides "per-fd" file locking. However, we don't
217 // use it because flock() doesn't work on NFS.
218 class FileLockManager {
219 public:
Lock(const string & filename,int * fd)220 bool Lock(const string &filename, int *fd) {
221 scoped_lock l(&mutex_);
222
223 if (fd == NULL) {
224 LOG(ERROR) << "fd is NULL";
225 return false;
226 }
227
228 if (filename.empty()) {
229 LOG(ERROR) << "filename is empty";
230 return false;
231 }
232
233 if (fdmap_.find(filename) != fdmap_.end()) {
234 VLOG(1) << filename << " is already locked by the same process";
235 return false;
236 }
237
238 chmod(filename.c_str(), 0600); // write temporary
239 *fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
240 if (-1 == *fd) {
241 LOG(ERROR) << "open() failed:" << ::strerror(errno);
242 return false;
243 }
244
245 struct flock command;
246 command.l_type = F_WRLCK;
247 command.l_whence = SEEK_SET;
248 command.l_start = 0;
249 command.l_len = 0;
250
251 const int result = ::fcntl(*fd, F_SETLK, &command);
252 if (-1 == result) { // failed
253 ::close(*fd);
254 LOG(WARNING) << "already locked";
255 return false; // another server is already running
256 }
257
258 fdmap_.insert(std::make_pair(filename, *fd));
259
260 return true;
261 }
262
UnLock(const string & filename)263 void UnLock(const string &filename) {
264 scoped_lock l(&mutex_);
265 std::map<string, int>::iterator it = fdmap_.find(filename);
266 if (it == fdmap_.end()) {
267 LOG(ERROR) << filename << " is not locked";
268 return;
269 }
270 ::close(it->second);
271 FileUtil::Unlink(filename);
272 fdmap_.erase(it);
273 }
274
FileLockManager()275 FileLockManager() {}
276
~FileLockManager()277 ~FileLockManager() {
278 for (std::map<string, int>::const_iterator it = fdmap_.begin();
279 it != fdmap_.end(); ++it) {
280 ::close(it->second);
281 }
282 fdmap_.clear();
283 }
284
285 private:
286 Mutex mutex_;
287 std::map<string, int> fdmap_;
288 };
289
290 } // namespace
291
ProcessMutex(const char * name)292 ProcessMutex::ProcessMutex(const char *name) : locked_(false) {
293 filename_ = CreateProcessMutexFileName(name);
294 }
295
~ProcessMutex()296 ProcessMutex::~ProcessMutex() {
297 if (locked_) {
298 UnLock();
299 }
300 }
301
LockAndWrite(const string & message)302 bool ProcessMutex::LockAndWrite(const string &message) {
303 int fd = -1;
304 if (!Singleton<FileLockManager>::get()->Lock(filename_, &fd)) {
305 VLOG(1) << filename_ << " is already locked";
306 return false;
307 }
308
309 if (fd == -1) {
310 LOG(ERROR) << "file descriptor is -1";
311 return false;
312 }
313
314 if (!message.empty()) {
315 if (write(fd, message.data(), message.size()) !=
316 message.size()) {
317 LOG(ERROR) << "Cannot write message: " << message;
318 UnLock();
319 return false;
320 }
321 }
322
323 // changed it to read only mode.
324 chmod(filename_.c_str(), 0400);
325 locked_ = true;
326 return true;
327 }
328
UnLock()329 bool ProcessMutex::UnLock() {
330 if (locked_) {
331 Singleton<FileLockManager>::get()->UnLock(filename_);
332 }
333 locked_ = false;
334 return true;
335 }
336 #endif
337 } // namespace mozc
338