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