blob: df0770c8f10659bdfec98f849d0f6723e55edf2c [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 "shill/glib_io_input_handler.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#include <string>
#include <vector>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include "shill/error.h"
#include "shill/logging.h"
using base::Callback;
using base::StringPrintf;
using std::string;
using std::vector;
namespace shill {
static gboolean DispatchIOHandler(GIOChannel *chan,
GIOCondition cond,
gpointer data) {
GlibIOInputHandler *handler = reinterpret_cast<GlibIOInputHandler *>(data);
unsigned char buf[4096];
gsize len = 0;
gint fd = g_io_channel_unix_get_fd(chan);
GError *err = 0;
vector<string> error_conditions;
if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
string condition = base::StringPrintf(
"Unexpected GLib error condition %#x on poll(%d): %s",
cond, fd, strerror(errno));
LOG(WARNING) << condition;
error_conditions.push_back(condition);
}
GIOStatus status = g_io_channel_read_chars(
chan, reinterpret_cast<gchar *>(buf), sizeof(buf), &len, &err);
if (err) {
string condition = base::StringPrintf(
"GLib error code %d/%d (%s) on read(%d): %s",
err->domain, err->code, err->message, fd, strerror(errno));
LOG(WARNING) << condition;
error_conditions.push_back(condition);
g_error_free(err);
}
if (status == G_IO_STATUS_AGAIN)
return TRUE;
if (status == G_IO_STATUS_ERROR) {
string condition = base::StringPrintf(
"Unexpected GLib return status: %d", status);
LOG(ERROR) << condition;
error_conditions.push_back(condition);
Error error(Error::kOperationFailed, JoinString(error_conditions, ';'));
handler->error_callback().Run(error);
return FALSE;
}
InputData input_data(buf, len);
handler->input_callback().Run(&input_data);
if (status == G_IO_STATUS_EOF) {
LOG(INFO) << "InputHandler on fd " << fd << " closing due to EOF.";
CHECK(len == 0);
return FALSE;
}
return TRUE;
}
GlibIOInputHandler::GlibIOInputHandler(
int fd,
const InputCallback &input_callback,
const ErrorCallback &error_callback)
: channel_(g_io_channel_unix_new(fd)),
input_callback_(input_callback),
error_callback_(error_callback),
source_id_(G_MAXUINT) {
// To avoid blocking in g_io_channel_read_chars() due to its internal buffer,
// set the channel to unbuffered, which in turns requires encoding to be NULL.
// This assumes raw binary data are read from |fd| via the channel.
CHECK_EQ(G_IO_STATUS_NORMAL, g_io_channel_set_encoding(channel_, NULL, NULL));
g_io_channel_set_buffered(channel_, FALSE);
g_io_channel_set_close_on_unref(channel_, TRUE);
}
GlibIOInputHandler::~GlibIOInputHandler() {
g_source_remove(source_id_);
g_io_channel_shutdown(channel_, TRUE, NULL);
g_io_channel_unref(channel_);
}
void GlibIOInputHandler::Start() {
if (source_id_ == G_MAXUINT) {
source_id_ = g_io_add_watch(channel_,
static_cast<GIOCondition>(
G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR),
DispatchIOHandler, this);
}
}
void GlibIOInputHandler::Stop() {
if (source_id_ != G_MAXUINT) {
g_source_remove(source_id_);
source_id_ = G_MAXUINT;
}
}
} // namespace shill