blob: 30528397e63a77d639dbb8ed907361bffce14486 [file] [log] [blame]
Ben Chanb6a75532012-09-07 20:30:49 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewart25379f12011-05-26 06:41:38 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Paul Stewartf0aae102011-10-19 12:11:44 -07005#include "shill/glib_io_input_handler.h"
Paul Stewart26b327e2011-10-19 11:38:09 -07006
Julius Werner22e244a2012-11-13 18:25:19 -08007#include <errno.h>
Paul Stewart25379f12011-05-26 06:41:38 -07008#include <stdio.h>
Julius Werner22e244a2012-11-13 18:25:19 -08009#include <string.h>
Paul Stewart25379f12011-05-26 06:41:38 -070010#include <glib.h>
11
Paul Stewart5f06a0e2012-12-20 11:11:33 -080012#include <string>
13#include <vector>
14
15#include <base/string_util.h>
16#include <base/stringprintf.h>
17
18#include "shill/error.h"
Ben Chanb6a75532012-09-07 20:30:49 -070019#include "shill/logging.h"
20
Eric Shienbrood3e20a232012-02-16 11:35:56 -050021using base::Callback;
Paul Stewart5f06a0e2012-12-20 11:11:33 -080022using base::StringPrintf;
23using std::string;
24using std::vector;
Eric Shienbrood3e20a232012-02-16 11:35:56 -050025
Paul Stewart25379f12011-05-26 06:41:38 -070026namespace shill {
27
28static gboolean DispatchIOHandler(GIOChannel *chan,
29 GIOCondition cond,
Paul Stewart25379f12011-05-26 06:41:38 -070030 gpointer data) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -050031 GlibIOInputHandler *handler = reinterpret_cast<GlibIOInputHandler *>(data);
Paul Stewart25379f12011-05-26 06:41:38 -070032 unsigned char buf[4096];
Ben Chanb6a75532012-09-07 20:30:49 -070033 gsize len = 0;
Julius Werner22e244a2012-11-13 18:25:19 -080034 gint fd = g_io_channel_unix_get_fd(chan);
35 GError *err = 0;
Paul Stewart5f06a0e2012-12-20 11:11:33 -080036 vector<string> error_conditions;
Paul Stewart25379f12011-05-26 06:41:38 -070037
Paul Stewart5f06a0e2012-12-20 11:11:33 -080038 if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
39 string condition = base::StringPrintf(
40 "Unexpected GLib error condition %#x on poll(%d): %s",
41 cond, fd, strerror(errno));
42 LOG(WARNING) << condition;
43 error_conditions.push_back(condition);
44 }
Paul Stewart25379f12011-05-26 06:41:38 -070045
Ben Chanb6a75532012-09-07 20:30:49 -070046 GIOStatus status = g_io_channel_read_chars(
Julius Werner22e244a2012-11-13 18:25:19 -080047 chan, reinterpret_cast<gchar *>(buf), sizeof(buf), &len, &err);
48 if (err) {
Paul Stewart5f06a0e2012-12-20 11:11:33 -080049 string condition = base::StringPrintf(
50 "GLib error code %d/%d (%s) on read(%d): %s",
51 err->domain, err->code, err->message, fd, strerror(errno));
52 LOG(WARNING) << condition;
53 error_conditions.push_back(condition);
Julius Werner22e244a2012-11-13 18:25:19 -080054 g_error_free(err);
Paul Stewart25379f12011-05-26 06:41:38 -070055 }
Julius Werner22e244a2012-11-13 18:25:19 -080056 if (status == G_IO_STATUS_AGAIN)
57 return TRUE;
Julius Wernerd69c9582012-12-27 14:42:56 -080058 if (status == G_IO_STATUS_ERROR) {
Paul Stewart5f06a0e2012-12-20 11:11:33 -080059 string condition = base::StringPrintf(
60 "Unexpected GLib return status: %d", status);
61 LOG(ERROR) << condition;
62 error_conditions.push_back(condition);
63 Error error(Error::kOperationFailed, JoinString(error_conditions, ';'));
64 handler->error_callback().Run(error);
65 return FALSE;
66 }
Paul Stewart25379f12011-05-26 06:41:38 -070067
Darin Petkov633ac6f2011-07-08 13:56:13 -070068 InputData input_data(buf, len);
Paul Stewart5f06a0e2012-12-20 11:11:33 -080069 handler->input_callback().Run(&input_data);
Paul Stewart25379f12011-05-26 06:41:38 -070070
Julius Wernerd69c9582012-12-27 14:42:56 -080071 if (status == G_IO_STATUS_EOF) {
72 LOG(INFO) << "InputHandler on fd " << fd << " closing due to EOF.";
73 CHECK(len == 0);
74 return FALSE;
75 }
Julius Werner22e244a2012-11-13 18:25:19 -080076 return TRUE;
Paul Stewart25379f12011-05-26 06:41:38 -070077}
78
Eric Shienbrood3e20a232012-02-16 11:35:56 -050079GlibIOInputHandler::GlibIOInputHandler(
Paul Stewart5f06a0e2012-12-20 11:11:33 -080080 int fd,
81 const InputCallback &input_callback,
82 const ErrorCallback &error_callback)
Paul Stewartf0aae102011-10-19 12:11:44 -070083 : channel_(g_io_channel_unix_new(fd)),
Paul Stewart5f06a0e2012-12-20 11:11:33 -080084 input_callback_(input_callback),
85 error_callback_(error_callback),
Paul Stewartf0aae102011-10-19 12:11:44 -070086 source_id_(G_MAXUINT) {
Ben Chanb6a75532012-09-07 20:30:49 -070087 // To avoid blocking in g_io_channel_read_chars() due to its internal buffer,
88 // set the channel to unbuffered, which in turns requires encoding to be NULL.
89 // This assumes raw binary data are read from |fd| via the channel.
90 CHECK_EQ(G_IO_STATUS_NORMAL, g_io_channel_set_encoding(channel_, NULL, NULL));
91 g_io_channel_set_buffered(channel_, FALSE);
Paul Stewartf0aae102011-10-19 12:11:44 -070092 g_io_channel_set_close_on_unref(channel_, TRUE);
93}
94
95GlibIOInputHandler::~GlibIOInputHandler() {
96 g_source_remove(source_id_);
97 g_io_channel_shutdown(channel_, TRUE, NULL);
98 g_io_channel_unref(channel_);
99}
100
101void GlibIOInputHandler::Start() {
102 if (source_id_ == G_MAXUINT) {
103 source_id_ = g_io_add_watch(channel_,
104 static_cast<GIOCondition>(
105 G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR),
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500106 DispatchIOHandler, this);
Paul Stewartf0aae102011-10-19 12:11:44 -0700107 }
108}
109
110void GlibIOInputHandler::Stop() {
111 if (source_id_ != G_MAXUINT) {
112 g_source_remove(source_id_);
113 source_id_ = G_MAXUINT;
114 }
115}
116
Paul Stewart25379f12011-05-26 06:41:38 -0700117} // namespace shill