blob: 483cb4ec48558ef17fa358d741d34970b1ba9ca2 [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 "ipc/named_event.h"
#ifdef OS_WIN
#include <Windows.h>
#include <Sddl.h>
#else
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <semaphore.h>
#include <string.h>
#include <sys/types.h>
#endif
#include <algorithm>
#include <cstdio>
#include <string>
#include "base/const.h"
#include "base/logging.h"
#include "base/port.h"
#include "base/system_util.h"
#include "base/util.h"
#ifdef OS_WIN
#include "base/win_sandbox.h"
#endif // OS_WIN
namespace mozc {
namespace {
#ifndef OS_WIN
const pid_t kInvalidPid = 1; // We can safely use 1 as 1 is reserved for init.
// Returns true if the process is alive.
bool IsProcessAlive(pid_t pid) {
if (pid == kInvalidPid) {
return true; // return dummy value.
}
const int kSig = 0;
// As the signal number is 0, no signal is sent, but error checking is
// still performed.
return ::kill(pid, kSig) == 0;
}
#endif // !OS_WIN
} // namespace
const string NamedEventUtil::GetEventPath(const char *name) {
name = (name == NULL) ? "NULL" : name;
string event_name = kEventPathPrefix;
event_name += SystemUtil::GetUserSidAsString();
event_name += ".";
event_name += name;
#ifdef OS_WIN
return event_name;
#else
// To maximze mozc portability, (especailly on BSD including OSX),
// makes the length of path name shorter than 14byte.
// Please see the following man page for detail:
// http://www.freebsd.org/cgi/man.cgi?query=sem_open&manpath=FreeBSD+7.0-RELEASE
// "This implementation places strict requirements on the value of name: it
// must begin with a slash (`/'), contain no other slash characters, and be
// less than 14 characters in length not including the terminating null
// character."
const size_t kEventPathLength = 14;
char buf[32];
snprintf(buf, kEventPathLength, "/%llx", Util::Fingerprint(event_name));
return buf;
#endif
}
#ifdef OS_WIN
NamedEventListener::NamedEventListener(const char *name)
: is_owner_(false), handle_(NULL) {
wstring event_path;
Util::UTF8ToWide(NamedEventUtil::GetEventPath(name).c_str(), &event_path);
handle_ = ::OpenEventW(EVENT_ALL_ACCESS, false,
event_path.c_str());
if (handle_ == NULL) {
SECURITY_ATTRIBUTES security_attributes;
if (!WinSandbox::MakeSecurityAttributes(WinSandbox::kSharableEvent,
&security_attributes)) {
LOG(ERROR) << "Cannot make SecurityAttributes";
return;
}
handle_ = ::CreateEventW(&security_attributes,
true, false,
event_path.c_str());
::LocalFree(security_attributes.lpSecurityDescriptor);
if (handle_ == NULL) {
LOG(ERROR) << "CreateEvent() failed: " << ::GetLastError();
return;
}
is_owner_ = true;
}
VLOG(1) << "NamedEventListener " << name << " is created";
}
NamedEventListener::~NamedEventListener() {
if (NULL != handle_) {
::CloseHandle(handle_);
}
handle_ = NULL;
}
bool NamedEventListener::IsAvailable() const {
return (handle_ != NULL);
}
bool NamedEventListener::IsOwner() const {
return (IsAvailable() && is_owner_);
}
bool NamedEventListener::Wait(int msec) {
if (!IsAvailable()) {
LOG(ERROR) << "NamedEventListener is not available";
return false;
}
if (msec < 0) {
msec = INFINITE;
}
const DWORD result = ::WaitForSingleObject(handle_, msec);
if (result == WAIT_TIMEOUT) {
LOG(WARNING) << "NamedEvent timeout " << ::GetLastError();
return false;
}
return true;
}
int NamedEventListener::WaitEventOrProcess(int msec, size_t pid) {
if (!IsAvailable()) {
return TIMEOUT;
}
HANDLE handle = ::OpenProcess(SYNCHRONIZE,
FALSE, static_cast<DWORD>(pid));
if (NULL == handle) {
LOG(ERROR) << "OpenProcess: failed() " << ::GetLastError() << " " << pid;
if (::GetLastError() == ERROR_INVALID_PARAMETER) {
LOG(ERROR) << "No such process found: " << pid;
return PROCESS_SIGNALED;
}
}
if (msec < 0) {
msec = INFINITE;
}
HANDLE handles[2] = { handle_, handle };
const DWORD handles_size = (handle == NULL) ? 1 : 2;
const DWORD ret = ::WaitForMultipleObjects(handles_size,
handles,
FALSE,
msec);
int result = TIMEOUT;
switch (ret) {
case WAIT_OBJECT_0:
case WAIT_ABANDONED_0:
result = EVENT_SIGNALED;
break;
case WAIT_OBJECT_0 + 1:
case WAIT_ABANDONED_0 + 1:
result = PROCESS_SIGNALED;
break;
case WAIT_TIMEOUT:
LOG(WARNING) << "NamedEvent timeout " << ::GetLastError();
result = TIMEOUT;
break;
default:
result = TIMEOUT;
break;
}
if (NULL != handle) {
::CloseHandle(handle);
}
return result;
}
NamedEventNotifier::NamedEventNotifier(const char *name)
: handle_(NULL) {
wstring event_path;
Util::UTF8ToWide(NamedEventUtil::GetEventPath(name).c_str(), &event_path);
handle_ = ::OpenEventW(EVENT_MODIFY_STATE, false,
event_path.c_str());
if (handle_ == NULL) {
LOG(ERROR) << "Cannot open Event name: " << name;
return;
}
}
NamedEventNotifier::~NamedEventNotifier() {
if (NULL != handle_) {
::CloseHandle(handle_);
}
handle_ = NULL;
}
bool NamedEventNotifier::IsAvailable() const {
return handle_ != NULL;
}
bool NamedEventNotifier::Notify() {
if (!IsAvailable()) {
LOG(ERROR) << "NamedEventListener is not available";
return false;
}
if (0 == ::SetEvent(handle_)) {
LOG(ERROR) << "SetEvent() failed: " << ::GetLastError();
return false;
}
return true;
}
#else // OS_WIN
NamedEventListener::NamedEventListener(const char *name)
: is_owner_(false), sem_(SEM_FAILED) {
key_filename_ = NamedEventUtil::GetEventPath(name);
sem_ = ::sem_open(key_filename_.c_str(), O_CREAT | O_EXCL, 0600, 0);
if (sem_ == SEM_FAILED && errno == EEXIST) {
sem_ = ::sem_open(key_filename_.c_str(), O_CREAT, 0600, 0);
} else {
is_owner_ = true;
}
if (sem_ == SEM_FAILED) {
LOG(ERROR) << "sem_open() failed "
<< key_filename_ << " " << ::strerror(errno);
return;
}
VLOG(1) << "NamedEventNotifier " << name << " is created";
}
NamedEventListener::~NamedEventListener() {
if (IsAvailable()) {
::sem_close(sem_);
::sem_unlink(key_filename_.c_str());
}
sem_ = SEM_FAILED;
}
bool NamedEventListener::IsAvailable() const {
return sem_ != SEM_FAILED;
}
bool NamedEventListener::IsOwner() const {
return (IsAvailable() && is_owner_);
}
bool NamedEventListener::Wait(int msec) {
return WaitEventOrProcess(msec, kInvalidPid /* don't check pid */) ==
NamedEventListener::EVENT_SIGNALED;
}
int NamedEventListener::WaitEventOrProcess(int msec, size_t pid) {
if (!IsAvailable()) {
return NamedEventListener::TIMEOUT;
}
const bool inifinite = msec < 0 ? true : false;
const int kWaitMsec = 200;
while (inifinite || msec > 0) {
Util::Sleep(kWaitMsec);
if (!IsProcessAlive(pid)) {
return NamedEventListener::PROCESS_SIGNALED;
}
if (-1 == ::sem_trywait(sem_)) {
if (errno != EAGAIN) {
LOG(ERROR) << "sem_trywait failed: " << ::strerror(errno);
return EVENT_SIGNALED;
}
} else {
// raise other events recursively.
if (-1 == ::sem_post(sem_)) {
LOG(ERROR) << "sem_post failed: " << ::strerror(errno);
}
return EVENT_SIGNALED;
}
msec -= kWaitMsec;
}
// timeout.
return NamedEventListener::TIMEOUT;
}
NamedEventNotifier::NamedEventNotifier(const char *name)
: sem_(SEM_FAILED) {
const string key_filename = NamedEventUtil::GetEventPath(name);
sem_ = ::sem_open(key_filename.c_str(), 0);
if (sem_ == SEM_FAILED) {
LOG(ERROR) << "sem_open failed: " << ::strerror(errno);
}
}
NamedEventNotifier::~NamedEventNotifier() {
if (IsAvailable()) {
::sem_close(sem_);
}
sem_ = SEM_FAILED;
}
bool NamedEventNotifier::IsAvailable() const {
return sem_ != SEM_FAILED;
}
bool NamedEventNotifier::Notify() {
if (!IsAvailable()) {
LOG(ERROR) << "NamedEventNotifier is not available";
return false;
}
if (-1 == ::sem_post(sem_)) {
LOG(ERROR) << "semop failed: " << ::strerror(errno);
return false;
}
return true;
}
#endif
} // namespace mozc