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 "ipc/named_event.h"
31 
32 #ifdef OS_WIN
33 #include <Windows.h>
34 #include <Sddl.h>
35 #else
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <signal.h>
39 #include <semaphore.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #endif
43 
44 #include <algorithm>
45 #include <cstdio>
46 #include <string>
47 
48 #include "base/const.h"
49 #include "base/hash.h"
50 #include "base/logging.h"
51 #include "base/port.h"
52 #include "base/system_util.h"
53 #include "base/util.h"
54 #ifdef OS_WIN
55 #include "base/win_sandbox.h"
56 #endif  // OS_WIN
57 
58 namespace mozc {
59 namespace {
60 
61 #ifndef OS_WIN
62 const pid_t kInvalidPid = 1;  // We can safely use 1 as 1 is reserved for init.
63 
64 // Returns true if the process is alive.
IsProcessAlive(pid_t pid)65 bool IsProcessAlive(pid_t pid) {
66   if (pid == kInvalidPid) {
67     return true;  // return dummy value.
68   }
69   const int kSig = 0;
70   // As the signal number is 0, no signal is sent, but error checking is
71   // still performed.
72   return ::kill(pid, kSig) == 0;
73 }
74 #endif  // !OS_WIN
75 }  // namespace
76 
GetEventPath(const char * name)77 const string NamedEventUtil::GetEventPath(const char *name) {
78   name = (name == NULL) ? "NULL" : name;
79   string event_name = kEventPathPrefix;
80   event_name += SystemUtil::GetUserSidAsString();
81   event_name += ".";
82   event_name += name;
83 #ifdef OS_WIN
84   return event_name;
85 #else
86   // To maximze mozc portability, (especailly on BSD including OSX),
87   // makes the length of path name shorter than 14byte.
88   // Please see the following man page for detail:
89   // http://www.freebsd.org/cgi/man.cgi?query=sem_open&manpath=FreeBSD+7.0-RELEASE
90   // "This implementation places strict requirements on the value of name: it
91   //  must begin with a slash (`/'), contain no other slash characters, and be
92   //  less than 14 characters in length not including the terminating null
93   //  character."
94   const size_t kEventPathLength = 14;
95   char buf[32];
96   snprintf(buf, kEventPathLength, "/%" MOZC_PRIx64,
97            Hash::Fingerprint(event_name));
98   return buf;
99 #endif
100 }
101 
102 #ifdef OS_WIN
NamedEventListener(const char * name)103 NamedEventListener::NamedEventListener(const char *name)
104     : is_owner_(false), handle_(NULL) {
105   std::wstring event_path;
106   Util::UTF8ToWide(NamedEventUtil::GetEventPath(name), &event_path);
107 
108   handle_ = ::OpenEventW(EVENT_ALL_ACCESS, false,
109                          event_path.c_str());
110 
111   if (handle_ == NULL) {
112     SECURITY_ATTRIBUTES security_attributes;
113     if (!WinSandbox::MakeSecurityAttributes(WinSandbox::kSharableEvent,
114                                             &security_attributes)) {
115       LOG(ERROR) << "Cannot make SecurityAttributes";
116       return;
117     }
118 
119     handle_ = ::CreateEventW(&security_attributes,
120                              true, false,
121                              event_path.c_str());
122     ::LocalFree(security_attributes.lpSecurityDescriptor);
123     if (handle_ == NULL) {
124       LOG(ERROR) << "CreateEvent() failed: " << ::GetLastError();
125       return;
126     }
127 
128     is_owner_ = true;
129   }
130 
131   VLOG(1) << "NamedEventListener " << name << " is created";
132 }
133 
~NamedEventListener()134 NamedEventListener::~NamedEventListener() {
135   if (NULL != handle_) {
136     ::CloseHandle(handle_);
137   }
138   handle_ = NULL;
139 }
140 
IsAvailable() const141 bool NamedEventListener::IsAvailable() const {
142   return (handle_ != NULL);
143 }
144 
IsOwner() const145 bool NamedEventListener::IsOwner() const {
146   return (IsAvailable() && is_owner_);
147 }
148 
Wait(int msec)149 bool NamedEventListener::Wait(int msec) {
150   if (!IsAvailable()) {
151     LOG(ERROR) << "NamedEventListener is not available";
152     return false;
153   }
154 
155   if (msec < 0) {
156     msec = INFINITE;
157   }
158 
159   const DWORD result = ::WaitForSingleObject(handle_, msec);
160   if (result == WAIT_TIMEOUT) {
161     LOG(WARNING) << "NamedEvent timeout " << ::GetLastError();
162     return false;
163   }
164 
165   return true;
166 }
167 
WaitEventOrProcess(int msec,size_t pid)168 int NamedEventListener::WaitEventOrProcess(int msec, size_t pid) {
169   if (!IsAvailable()) {
170     return TIMEOUT;
171   }
172 
173   HANDLE handle = ::OpenProcess(SYNCHRONIZE,
174                                 FALSE, static_cast<DWORD>(pid));
175   if (NULL == handle) {
176     LOG(ERROR) << "OpenProcess: failed() " << ::GetLastError() << " " << pid;
177     if (::GetLastError() == ERROR_INVALID_PARAMETER) {
178       LOG(ERROR) << "No such process found: " << pid;
179       return PROCESS_SIGNALED;
180     }
181   }
182 
183   if (msec < 0) {
184     msec = INFINITE;
185   }
186 
187   HANDLE handles[2] = { handle_, handle };
188 
189   const DWORD handles_size = (handle == NULL) ? 1 : 2;
190 
191   const DWORD ret = ::WaitForMultipleObjects(handles_size,
192                                              handles,
193                                              FALSE,
194                                              msec);
195   int result = TIMEOUT;
196   switch (ret) {
197     case WAIT_OBJECT_0:
198     case WAIT_ABANDONED_0:
199       result = EVENT_SIGNALED;
200       break;
201     case WAIT_OBJECT_0 + 1:
202     case WAIT_ABANDONED_0 + 1:
203       result = PROCESS_SIGNALED;
204       break;
205     case WAIT_TIMEOUT:
206       LOG(WARNING) << "NamedEvent timeout " << ::GetLastError();
207       result = TIMEOUT;
208       break;
209     default:
210       result = TIMEOUT;
211       break;
212   }
213 
214   if (NULL != handle) {
215     ::CloseHandle(handle);
216   }
217 
218   return result;
219 }
220 
NamedEventNotifier(const char * name)221 NamedEventNotifier::NamedEventNotifier(const char *name)
222     : handle_(NULL) {
223   std::wstring event_path;
224   Util::UTF8ToWide(NamedEventUtil::GetEventPath(name), &event_path);
225   handle_ = ::OpenEventW(EVENT_MODIFY_STATE, false,
226                          event_path.c_str());
227   if (handle_ == NULL) {
228     LOG(ERROR) << "Cannot open Event name: " << name;
229     return;
230   }
231 }
232 
~NamedEventNotifier()233 NamedEventNotifier::~NamedEventNotifier() {
234   if (NULL != handle_) {
235     ::CloseHandle(handle_);
236   }
237   handle_ = NULL;
238 }
239 
IsAvailable() const240 bool NamedEventNotifier::IsAvailable() const {
241   return handle_ != NULL;
242 }
243 
Notify()244 bool NamedEventNotifier::Notify() {
245   if (!IsAvailable()) {
246     LOG(ERROR) << "NamedEventListener is not available";
247     return false;
248   }
249 
250   if (0 == ::SetEvent(handle_)) {
251     LOG(ERROR) << "SetEvent() failed: " << ::GetLastError();
252     return false;
253   }
254 
255   return true;
256 }
257 
258 #else   // OS_WIN
259 
NamedEventListener(const char * name)260 NamedEventListener::NamedEventListener(const char *name)
261     : is_owner_(false), sem_(SEM_FAILED) {
262   key_filename_ = NamedEventUtil::GetEventPath(name);
263 
264   sem_ = ::sem_open(key_filename_.c_str(), O_CREAT | O_EXCL, 0600, 0);
265 
266   if (sem_ == SEM_FAILED && errno == EEXIST) {
267     sem_ = ::sem_open(key_filename_.c_str(), O_CREAT, 0600, 0);
268   } else {
269     is_owner_ = true;
270   }
271 
272   if (sem_ == SEM_FAILED) {
273     LOG(ERROR) << "sem_open() failed "
274                << key_filename_ << " " << ::strerror(errno);
275     return;
276   }
277 
278   VLOG(1) << "NamedEventNotifier " << name << " is created";
279 }
280 
~NamedEventListener()281 NamedEventListener::~NamedEventListener() {
282   if (IsAvailable()) {
283     ::sem_close(sem_);
284     ::sem_unlink(key_filename_.c_str());
285   }
286   sem_ = SEM_FAILED;
287 }
288 
IsAvailable() const289 bool NamedEventListener::IsAvailable() const {
290   return sem_ != SEM_FAILED;
291 }
292 
IsOwner() const293 bool NamedEventListener::IsOwner() const {
294   return (IsAvailable() && is_owner_);
295 }
296 
Wait(int msec)297 bool NamedEventListener::Wait(int msec) {
298   return WaitEventOrProcess(msec, kInvalidPid /* don't check pid */) ==
299       NamedEventListener::EVENT_SIGNALED;
300 }
301 
WaitEventOrProcess(int msec,size_t pid)302 int NamedEventListener::WaitEventOrProcess(int msec, size_t pid) {
303   if (!IsAvailable()) {
304     return NamedEventListener::TIMEOUT;
305   }
306 
307   const bool inifinite = msec < 0 ? true : false;
308   const int kWaitMsec = 200;
309 
310   while (inifinite || msec > 0) {
311     Util::Sleep(kWaitMsec);
312 
313     if (!IsProcessAlive(pid)) {
314       return NamedEventListener::PROCESS_SIGNALED;
315     }
316 
317     if (-1 == ::sem_trywait(sem_)) {
318       if (errno != EAGAIN) {
319         LOG(ERROR) << "sem_trywait failed: " << ::strerror(errno);
320         return EVENT_SIGNALED;
321       }
322     } else {
323       // raise other events recursively.
324       if (-1 == ::sem_post(sem_)) {
325         LOG(ERROR) << "sem_post failed: " << ::strerror(errno);
326       }
327       return EVENT_SIGNALED;
328     }
329 
330     msec -= kWaitMsec;
331   }
332 
333   // timeout.
334   return NamedEventListener::TIMEOUT;
335 }
336 
NamedEventNotifier(const char * name)337 NamedEventNotifier::NamedEventNotifier(const char *name)
338     : sem_(SEM_FAILED) {
339   const string key_filename = NamedEventUtil::GetEventPath(name);
340   sem_ = ::sem_open(key_filename.c_str(), 0);
341   if (sem_ == SEM_FAILED) {
342     LOG(ERROR) << "sem_open failed: " << ::strerror(errno);
343   }
344 }
345 
~NamedEventNotifier()346 NamedEventNotifier::~NamedEventNotifier() {
347   if (IsAvailable()) {
348     ::sem_close(sem_);
349   }
350   sem_ = SEM_FAILED;
351 }
352 
IsAvailable() const353 bool NamedEventNotifier::IsAvailable() const {
354   return sem_ != SEM_FAILED;
355 }
356 
Notify()357 bool NamedEventNotifier::Notify() {
358   if (!IsAvailable()) {
359     LOG(ERROR) << "NamedEventNotifier is not available";
360     return false;
361   }
362 
363   if (-1 == ::sem_post(sem_)) {
364     LOG(ERROR) << "semop failed: " << ::strerror(errno);
365     return false;
366   }
367 
368   return true;
369 }
370 #endif
371 }  // namespace mozc
372