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