| // 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 <glib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/logging.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.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[IOHandler::kDataBufferSize]; |
| 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); |
| handler->error_callback().Run(JoinString(error_conditions, ';')); |
| 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_EQ(0U, len); |
| 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_, nullptr, nullptr)); |
| 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, nullptr); |
| 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 |