blob: 0eb46c235e8efe59c1b99b3638cbe080854862f8 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/cryptohome_event_source.h"
#include <poll.h>
#include <unistd.h>
#include <memory>
#include <utility>
#include <base/logging.h>
namespace cryptohome {
GSourceFuncs CryptohomeEventSource::source_functions_ = {
CryptohomeEventSource::Prepare, CryptohomeEventSource::Check,
CryptohomeEventSource::Dispatch, nullptr};
CryptohomeEventSource::CryptohomeEventSource() {
pipe_fds_[0] = -1;
pipe_fds_[1] = -1;
}
CryptohomeEventSource::~CryptohomeEventSource() {
Clear();
}
void CryptohomeEventSource::Reset(CryptohomeEventSourceSink* sink,
GMainContext* main_context) {
sink_ = sink;
source_.reset();
for (int i = 0; i < 2; i++) {
if (pipe_fds_[i] != -1) {
close(pipe_fds_[i]);
pipe_fds_[i] = -1;
}
}
Clear();
if (!pipe(pipe_fds_)) {
source_.reset(
static_cast<Source*>(g_source_new(&source_functions_, sizeof(Source))));
source_->event_source = this;
source_->poll_fd.fd = pipe_fds_[0];
source_->poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
source_->poll_fd.revents = 0;
g_source_add_poll(source_.get(), &source_->poll_fd);
if (main_context) {
g_source_attach(source_.get(), main_context);
}
g_source_set_can_recurse(source_.get(), true);
} else {
LOG(ERROR) << "Couldn't set up pipe for notifications.";
}
}
bool CryptohomeEventSource::EventsPending() {
bool result = true;
if (events_lock_.Try()) {
result = !events_.empty();
events_lock_.Release();
}
return result;
}
void CryptohomeEventSource::HandleDispatch() {
// Clear pending notifications from the pipe. This is done in reverse order
// of the AddEvent() function so that we cannot get into a state where there
// is an event queued but no pending read on the pipe.
bool more = false;
do {
struct pollfd fds = {pipe_fds_[0], POLLIN, 0};
if (poll(&fds, 1, 0) && (fds.revents & POLLIN)) {
char c;
if (read(pipe_fds_[0], &c, 1) == 1) {
more = true;
} else {
more = false;
}
} else {
more = false;
}
} while (more);
// Now handle pending events.
std::vector<std::unique_ptr<CryptohomeEventBase>> events;
{
base::AutoLock lock(events_lock_);
events_.swap(events);
}
for (auto& event : events) {
if (sink_) {
sink_->NotifyEvent(event.get());
}
}
}
void CryptohomeEventSource::AddEvent(
std::unique_ptr<CryptohomeEventBase> event) {
base::AutoLock lock(events_lock_);
events_.push_back(std::move(event));
if (write(pipe_fds_[1], "G", 1) != 1) {
LOG(INFO) << "Couldn't notify of pending events through the message pipe."
<< " Events will be cleared on next call to Prepare().";
}
}
void CryptohomeEventSource::Clear() {
base::AutoLock lock(events_lock_);
events_.clear();
}
void CryptohomeEventSource::SourceDeleter::operator()(Source* source) {
g_source_destroy(source);
g_source_unref(source);
}
gboolean CryptohomeEventSource::Prepare(GSource* source, gint* timeout_ms) {
if (static_cast<Source*>(source)->event_source->EventsPending()) {
*timeout_ms = 0;
return true;
}
*timeout_ms = -1;
return false;
}
gboolean CryptohomeEventSource::Check(GSource* source) {
return static_cast<Source*>(source)->event_source->EventsPending();
}
gboolean CryptohomeEventSource::Dispatch(GSource* source,
GSourceFunc unused_func,
gpointer unused_data) {
static_cast<Source*>(source)->event_source->HandleDispatch();
return true;
}
} // namespace cryptohome