shill: Create and register a Cellular device for each ModemManager.Modem.
BUG=chromium-os:17818
TEST=unit tests
Change-Id: Ic35adf35c8021f4c9689e72ddd03776948d036c1
Reviewed-on: http://gerrit.chromium.org/gerrit/4711
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/Makefile b/Makefile
index 247df7f..3124197 100644
--- a/Makefile
+++ b/Makefile
@@ -58,6 +58,7 @@
crypto_rot47.o \
dbus_adaptor.o \
dbus_control.o \
+ dbus_properties.o \
dbus_properties_proxy.o \
default_profile.o \
device.o \
@@ -112,6 +113,7 @@
crypto_provider_unittest.o \
crypto_rot47_unittest.o \
dbus_adaptor_unittest.o \
+ dbus_properties_unittest.o \
default_profile_unittest.o \
device_info_unittest.o \
device_unittest.o \
@@ -128,9 +130,11 @@
mock_service.o \
modem_info_unittest.o \
modem_manager_unittest.o \
+ modem_unittest.o \
profile_unittest.o \
property_accessor_unittest.o \
property_store_unittest.o \
+ rtnl_handler_unittest.o \
rtnl_listener_unittest.o \
service_unittest.o \
shill_unittest.o \
diff --git a/dbus_properties.cc b/dbus_properties.cc
new file mode 100644
index 0000000..6d639a5
--- /dev/null
+++ b/dbus_properties.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 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/dbus_properties.h"
+
+namespace shill {
+
+// static
+bool DBusProperties::GetString(const DBusPropertiesMap &properties,
+ const std::string &key,
+ std::string *value) {
+ DBusPropertiesMap::const_iterator it = properties.find(key);
+ if (it == properties.end()) {
+ return false;
+ }
+ *value = it->second.reader().get_string();
+ return true;
+}
+
+// static
+bool DBusProperties::GetUint32(const DBusPropertiesMap &properties,
+ const std::string &key,
+ uint32 *value) {
+ DBusPropertiesMap::const_iterator it = properties.find(key);
+ if (it == properties.end()) {
+ return false;
+ }
+ *value = it->second.reader().get_uint32();
+ return true;
+}
+
+} // namespace shill
diff --git a/dbus_properties.h b/dbus_properties.h
new file mode 100644
index 0000000..ca22db5
--- /dev/null
+++ b/dbus_properties.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 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.
+
+#ifndef SHILL_DBUS_PROPERTIES_
+#define SHILL_DBUS_PROPERTIES_
+
+#include <map>
+#include <string>
+
+#include <base/basictypes.h>
+#include <dbus-c++/types.h>
+
+namespace shill {
+
+typedef std::map<std::string, DBus::Variant> DBusPropertiesMap;
+
+class DBusProperties {
+ public:
+ static bool GetString(const DBusPropertiesMap &properties,
+ const std::string &key,
+ std::string *value);
+
+ static bool GetUint32(const DBusPropertiesMap &properties,
+ const std::string &key,
+ uint32 *value);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DBusProperties);
+};
+
+} // namespace shill
+
+#endif // SHILL_DBUS_PROPERTIES_
diff --git a/dbus_properties_proxy.cc b/dbus_properties_proxy.cc
index 620ec58..6f43fbc 100644
--- a/dbus_properties_proxy.cc
+++ b/dbus_properties_proxy.cc
@@ -10,7 +10,6 @@
namespace shill {
-using std::map;
using std::string;
using std::vector;
@@ -22,8 +21,7 @@
DBusPropertiesProxy::~DBusPropertiesProxy() {}
-map<string, DBus::Variant> DBusPropertiesProxy::GetAll(
- const string &interface_name) {
+DBusPropertiesMap DBusPropertiesProxy::GetAll(const string &interface_name) {
return proxy_.GetAll(interface_name);
}
@@ -38,13 +36,13 @@
void DBusPropertiesProxy::Proxy::MmPropertiesChanged(
const string &interface,
- const map<string, DBus::Variant> &properties) {
+ const DBusPropertiesMap &properties) {
LOG(INFO) << "MmPropertiesChanged: " << interface;
}
void DBusPropertiesProxy::Proxy::PropertiesChanged(
const string &interface,
- const map<string, DBus::Variant> &changed_properties,
+ const DBusPropertiesMap &changed_properties,
const vector<string> &invalidated_properties) {
LOG(INFO) << "PropertiesChanged: " << interface;
}
diff --git a/dbus_properties_proxy.h b/dbus_properties_proxy.h
index 7b4efb9..f3ab239 100644
--- a/dbus_properties_proxy.h
+++ b/dbus_properties_proxy.h
@@ -23,8 +23,7 @@
virtual ~DBusPropertiesProxy();
// Inherited from DBusPropertiesProxyInterface.
- virtual std::map<std::string, DBus::Variant> GetAll(
- const std::string &interface_name);
+ virtual DBusPropertiesMap GetAll(const std::string &interface_name);
private:
class Proxy : public org::freedesktop::DBus::Properties_proxy,
@@ -38,13 +37,12 @@
private:
// Signal callbacks inherited from DBusProperties_proxy.
- virtual void MmPropertiesChanged(
- const std::string &interface,
- const std::map<std::string, DBus::Variant> &properties);
+ virtual void MmPropertiesChanged(const std::string &interface,
+ const DBusPropertiesMap &properties);
virtual void PropertiesChanged(
const std::string &interface,
- const std::map<std::string, DBus::Variant> &changed_properties,
+ const DBusPropertiesMap &changed_properties,
const std::vector<std::string> &invalidated_properties);
Modem *modem_;
diff --git a/dbus_properties_proxy_interface.h b/dbus_properties_proxy_interface.h
index 6f2c503..7aeea03 100644
--- a/dbus_properties_proxy_interface.h
+++ b/dbus_properties_proxy_interface.h
@@ -5,10 +5,7 @@
#ifndef SHILL_DBUS_PROPERTIES_PROXY_INTERFACE_
#define SHILL_DBUS_PROPERTIES_PROXY_INTERFACE_
-#include <map>
-#include <string>
-
-#include <dbus-c++/types.h>
+#include "shill/dbus_properties.h"
namespace shill {
@@ -18,8 +15,7 @@
public:
virtual ~DBusPropertiesProxyInterface() {}
- virtual std::map<std::string, DBus::Variant> GetAll(
- const std::string &interface_name) = 0;
+ virtual DBusPropertiesMap GetAll(const std::string &interface_name) = 0;
};
} // namespace shill
diff --git a/dbus_properties_unittest.cc b/dbus_properties_unittest.cc
new file mode 100644
index 0000000..a843734
--- /dev/null
+++ b/dbus_properties_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 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 <gtest/gtest.h>
+
+#include "shill/dbus_properties.h"
+
+using std::string;
+using testing::Test;
+
+namespace shill {
+
+class DBusPropertiesTest : public Test {
+};
+
+TEST_F(DBusPropertiesTest, GetString) {
+ static const char kTestProperty[] = "RandomKey";
+ static const char kTestValue[] = "random-value";
+ static const char kOldValue[] = "old-value";
+ string value = kOldValue;
+ DBusPropertiesMap props;
+ EXPECT_FALSE(DBusProperties::GetString(props, kTestProperty, &value));
+ EXPECT_EQ(kOldValue, value);
+ props[kTestProperty].writer().append_string(kTestValue);
+ EXPECT_TRUE(DBusProperties::GetString(props, kTestProperty, &value));
+ EXPECT_EQ(kTestValue, value);
+}
+
+TEST_F(DBusPropertiesTest, GetUint32) {
+ static const char kTestProperty[] = "AKey";
+ const uint32 kTestValue = 35;
+ const uint32 kOldValue = 74;
+ uint32 value = kOldValue;
+ DBusPropertiesMap props;
+ EXPECT_FALSE(DBusProperties::GetUint32(props, kTestProperty, &value));
+ EXPECT_EQ(kOldValue, value);
+ props[kTestProperty].writer().append_uint32(kTestValue);
+ EXPECT_TRUE(DBusProperties::GetUint32(props, kTestProperty, &value));
+ EXPECT_EQ(kTestValue, value);
+}
+
+} // namespace shill
diff --git a/device.h b/device.h
index caa49aa..28da466 100644
--- a/device.h
+++ b/device.h
@@ -62,6 +62,9 @@
std::string GetRpcIdentifier();
+ const std::string &link_name() const { return link_name_; }
+ int interface_index() const { return interface_index_; }
+
const std::string &FriendlyName() const;
// Returns a string that is guaranteed to uniquely identify this Device
@@ -101,7 +104,7 @@
PropertyStore store_;
std::vector<ServiceRefPtr> services_;
- int interface_index_;
+ const int interface_index_;
bool running_;
const std::string link_name_;
const std::string unique_id_;
diff --git a/mock_control.h b/mock_control.h
index 8590796..b9c2c78 100644
--- a/mock_control.h
+++ b/mock_control.h
@@ -5,6 +5,7 @@
#ifndef SHILL_MOCK_CONTROL_
#define SHILL_MOCK_CONTROL_
+#include <base/basictypes.h>
#include <base/scoped_ptr.h>
#include "shill/control_interface.h"
diff --git a/mock_dbus_properties_proxy.h b/mock_dbus_properties_proxy.h
new file mode 100644
index 0000000..18b5d6e
--- /dev/null
+++ b/mock_dbus_properties_proxy.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 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.
+
+#ifndef SHILL_MOCK_DBUS_PROPERTIES_PROXY_H_
+#define SHILL_MOCK_DBUS_PROPERTIES_PROXY_H_
+
+#include <gmock/gmock.h>
+
+#include "shill/dbus_properties_proxy_interface.h"
+
+namespace shill {
+
+class MockDBusPropertiesProxy : public DBusPropertiesProxyInterface {
+ public:
+ MOCK_METHOD1(GetAll, DBusPropertiesMap(const std::string &interface_name));
+};
+
+} // namespace shill
+
+#endif // SHILL_MOCK_DBUS_PROPERTIES_PROXY_H_
diff --git a/mock_sockets.h b/mock_sockets.h
index cca5435..a54beef 100644
--- a/mock_sockets.h
+++ b/mock_sockets.h
@@ -16,6 +16,7 @@
MOCK_METHOD3(Bind,
int(int sockfd, const struct sockaddr *addr, socklen_t addrlen));
MOCK_METHOD1(Close, int(int fd));
+ MOCK_METHOD3(Ioctl, int(int d, int request, void *argp));
MOCK_METHOD4(Send,
ssize_t(int sockfd, const void *buf, size_t len, int flags));
MOCK_METHOD6(SendTo, ssize_t(int sockfd,
diff --git a/modem.cc b/modem.cc
index c640789..9c8b4ad 100644
--- a/modem.cc
+++ b/modem.cc
@@ -4,22 +4,92 @@
#include "shill/modem.h"
-#include "shill/dbus_properties_proxy_interface.h"
+#include <base/logging.h>
+#include <mm/mm-modem.h>
+
+#include "shill/cellular.h"
+#include "shill/manager.h"
#include "shill/proxy_factory.h"
+#include "shill/rtnl_handler.h"
+
+using std::string;
namespace shill {
+// TODO(petkov): Consider generating these in mm/mm-modem.h.
+const char Modem::kPropertyLinkName[] = "Device";
+const char Modem::kPropertyIPMethod[] = "IpMethod";
+const char Modem::kPropertyType[] = "Type";
+const char Modem::kPropertyUnlockRequired[] = "UnlockRequired";
+const char Modem::kPropertyUnlockRetries[] = "UnlockRetries";
+
Modem::Modem(const std::string &owner,
const std::string &path,
ControlInterface *control_interface,
EventDispatcher *dispatcher,
Manager *manager)
- : dbus_properties_proxy_(
- ProxyFactory::factory()->CreateDBusPropertiesProxy(this, path, owner)),
+ : owner_(owner),
+ path_(path),
control_interface_(control_interface),
dispatcher_(dispatcher),
- manager_(manager) {}
+ manager_(manager) {
+ LOG(INFO) << "Modem created: " << owner << " at " << path;
+}
Modem::~Modem() {}
+void Modem::Init() {
+ CHECK(!device_.get());
+ dbus_properties_proxy_.reset(
+ ProxyFactory::factory()->CreateDBusPropertiesProxy(this, path_, owner_));
+ // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
+ DBusPropertiesMap properties =
+ dbus_properties_proxy_->GetAll(MM_MODEM_INTERFACE);
+ CreateCellularDevice(properties);
+}
+
+void Modem::CreateCellularDevice(const DBusPropertiesMap &properties) {
+ uint32 ip_method = kuint32max;
+ if (!DBusProperties::GetUint32(properties, kPropertyIPMethod, &ip_method) ||
+ ip_method != MM_MODEM_IP_METHOD_DHCP) {
+ LOG(ERROR) << "Unsupported IP method: " << ip_method;
+ return;
+ }
+
+ string link_name;
+ if (!DBusProperties::GetString(properties, kPropertyLinkName, &link_name)) {
+ LOG(ERROR) << "Unable to create cellular device without a link name.";
+ return;
+ }
+ int interface_index =
+ RTNLHandler::GetInstance()->GetInterfaceIndex(link_name);
+ if (interface_index < 0) {
+ LOG(ERROR) << "Unable to create cellular device -- no interface index.";
+ return;
+ }
+
+ uint32 type = 0;
+ DBusProperties::GetUint32(properties, kPropertyType, &type);
+ // TODO(petkov): Use type.
+
+ LOG(INFO) << "Creating a cellular device on link " << link_name
+ << " interface index " << interface_index << ".";
+ device_ = new Cellular(control_interface_,
+ dispatcher_,
+ manager_,
+ link_name,
+ interface_index);
+ manager_->RegisterDevice(device_);
+
+ string unlock_required;
+ if (DBusProperties::GetString(
+ properties, kPropertyUnlockRequired, &unlock_required)) {
+ uint32 unlock_retries = 0;
+ DBusProperties::GetUint32(properties,
+ kPropertyUnlockRetries,
+ &unlock_retries);
+ // TODO(petkov): Set these properties on the device instance.
+ }
+}
+
} // namespace shill
diff --git a/modem.h b/modem.h
index a8f6adf..110d8f7 100644
--- a/modem.h
+++ b/modem.h
@@ -9,17 +9,24 @@
#include <base/basictypes.h>
#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h> // for FRIEND_TEST
+
+#include "shill/dbus_properties_proxy_interface.h"
+#include "shill/refptr_types.h"
namespace shill {
class ControlInterface;
-class DBusPropertiesProxyInterface;
class EventDispatcher;
class Manager;
-// Handles an instance of ModemManager.Modem and an instance of CellularDevice.
+// Handles an instance of ModemManager.Modem and an instance of a Cellular
+// device.
class Modem {
public:
+ // |owner| is the ModemManager DBus service owner (e.g., ":1.17"). |path| is
+ // the ModemManager.Modem DBus object path (e.g.,
+ // "/org/chromium/ModemManager/Gobi/0").
Modem(const std::string &owner,
const std::string &path,
ControlInterface *control_interface,
@@ -27,9 +34,32 @@
Manager *manager);
~Modem();
+ // Initializes support for the modem, possibly constructing a Cellular device.
+ void Init();
+
private:
+ friend class ModemManagerTest;
+ friend class ModemTest;
+ FRIEND_TEST(ModemTest, CreateCellularDevice);
+ FRIEND_TEST(ModemTest, Init);
+
+ static const char kPropertyLinkName[];
+ static const char kPropertyIPMethod[];
+ static const char kPropertyType[];
+ static const char kPropertyUnlockRequired[];
+ static const char kPropertyUnlockRetries[];
+
+ void CreateCellularDevice(const DBusPropertiesMap &properties);
+
+ // A proxy to the org.freedesktop.DBus.Properties interface used to obtain
+ // ModemManager.Modem properties and watch for property changes.
scoped_ptr<DBusPropertiesProxyInterface> dbus_properties_proxy_;
+ const std::string owner_;
+ const std::string path_;
+
+ CellularRefPtr device_;
+
ControlInterface *control_interface_;
EventDispatcher *dispatcher_;
Manager *manager_;
diff --git a/modem_manager.cc b/modem_manager.cc
index 2e151cd..d8c30dd 100644
--- a/modem_manager.cc
+++ b/modem_manager.cc
@@ -99,12 +99,10 @@
LOG(INFO) << "Modem already exists; ignored.";
return;
}
- shared_ptr<Modem> modem(new Modem(owner_,
- path,
- control_interface_,
- dispatcher_,
- manager_));
+ shared_ptr<Modem> modem(
+ new Modem(owner_, path, control_interface_, dispatcher_, manager_));
modems_[path] = modem;
+ modem->Init();
}
void ModemManager::RemoveModem(const std::string &path) {
diff --git a/modem_manager_unittest.cc b/modem_manager_unittest.cc
index 3ad94ee..aa4961a 100644
--- a/modem_manager_unittest.cc
+++ b/modem_manager_unittest.cc
@@ -4,11 +4,14 @@
#include <base/stl_util-inl.h>
#include <gtest/gtest.h>
+#include <mm/mm-modem.h>
#include "shill/manager.h"
#include "shill/mock_control.h"
+#include "shill/mock_dbus_properties_proxy.h"
#include "shill/mock_glib.h"
#include "shill/mock_modem_manager_proxy.h"
+#include "shill/modem.h"
#include "shill/modem_manager.h"
#include "shill/proxy_factory.h"
@@ -31,27 +34,25 @@
&dispatcher_,
&manager_,
&glib_),
- proxy_factory_(&proxy_) {
+ proxy_factory_(&proxy_, &dbus_properties_proxy_) {
ProxyFactory::set_factory(&proxy_factory_);
}
- virtual void TearDown() {
- modem_manager_.watcher_id_ = 0;
- ModemManagerProxyInterface *proxy = modem_manager_.proxy_.release();
- EXPECT_TRUE(proxy == NULL || proxy == &proxy_);
- ProxyFactory::set_factory(NULL);
- }
+ virtual void TearDown();
protected:
class TestProxyFactory : public ProxyFactory {
public:
- TestProxyFactory(ModemManagerProxyInterface *proxy) : proxy_(proxy) {}
+ TestProxyFactory(ModemManagerProxyInterface *proxy,
+ DBusPropertiesProxyInterface *dbus_properties_proxy)
+ : proxy_(proxy),
+ dbus_properties_proxy_(dbus_properties_proxy) {}
virtual DBusPropertiesProxyInterface *CreateDBusPropertiesProxy(
Modem *modem,
const string &path,
const string &service) {
- return NULL;
+ return dbus_properties_proxy_;
}
virtual ModemManagerProxyInterface *CreateModemManagerProxy(
@@ -63,6 +64,7 @@
private:
ModemManagerProxyInterface *proxy_;
+ DBusPropertiesProxyInterface *dbus_properties_proxy_;
};
static const char kService[];
@@ -70,12 +72,15 @@
static const char kOwner[];
static const char kModemPath[];
+ void ReleaseDBusPropertiesProxy();
+
MockGLib glib_;
MockControl control_interface_;
EventDispatcher dispatcher_;
Manager manager_;
ModemManager modem_manager_;
MockModemManagerProxy proxy_;
+ MockDBusPropertiesProxy dbus_properties_proxy_;
TestProxyFactory proxy_factory_;
};
@@ -84,6 +89,26 @@
const char ModemManagerTest::kOwner[] = ":1.17";
const char ModemManagerTest::kModemPath[] = "/org/chromium/ModemManager/Gobi/0";
+void ModemManagerTest::TearDown() {
+ modem_manager_.watcher_id_ = 0;
+ ModemManagerProxyInterface *proxy = modem_manager_.proxy_.release();
+ EXPECT_TRUE(proxy == NULL || proxy == &proxy_);
+ ReleaseDBusPropertiesProxy();
+ ProxyFactory::set_factory(NULL);
+}
+
+void ModemManagerTest::ReleaseDBusPropertiesProxy() {
+ if (modem_manager_.modems_.empty()) {
+ return;
+ }
+ EXPECT_EQ(1, modem_manager_.modems_.size());
+ ModemManager::Modems::iterator iter = modem_manager_.modems_.begin();
+ DBusPropertiesProxyInterface *dbus_properties_proxy =
+ iter->second->dbus_properties_proxy_.release();
+ EXPECT_TRUE(dbus_properties_proxy == NULL ||
+ dbus_properties_proxy == &dbus_properties_proxy_);
+}
+
TEST_F(ModemManagerTest, Start) {
const int kWatcher = 123;
EXPECT_CALL(glib_, BusWatchName(G_BUS_TYPE_SYSTEM,
@@ -113,6 +138,8 @@
EXPECT_EQ("", modem_manager_.owner_);
EXPECT_CALL(proxy_, EnumerateDevices())
.WillOnce(Return(vector<DBus::Path>(1, kModemPath)));
+ EXPECT_CALL(dbus_properties_proxy_, GetAll(MM_MODEM_INTERFACE))
+ .WillOnce(Return(DBusPropertiesMap()));
modem_manager_.Connect(kOwner);
EXPECT_EQ(kOwner, modem_manager_.owner_);
EXPECT_EQ(1, modem_manager_.modems_.size());
@@ -141,9 +168,13 @@
TEST_F(ModemManagerTest, AddRemoveModem) {
modem_manager_.owner_ = kOwner;
+ EXPECT_CALL(dbus_properties_proxy_, GetAll(MM_MODEM_INTERFACE))
+ .WillOnce(Return(DBusPropertiesMap()));
modem_manager_.AddModem(kModemPath);
EXPECT_EQ(1, modem_manager_.modems_.size());
EXPECT_TRUE(ContainsKey(modem_manager_.modems_, kModemPath));
+
+ ReleaseDBusPropertiesProxy();
modem_manager_.RemoveModem(kModemPath);
EXPECT_EQ(0, modem_manager_.modems_.size());
}
diff --git a/modem_unittest.cc b/modem_unittest.cc
new file mode 100644
index 0000000..bb26344
--- /dev/null
+++ b/modem_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright (c) 2011 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 <gtest/gtest.h>
+#include <mm/mm-modem.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "shill/cellular.h"
+#include "shill/manager.h"
+#include "shill/mock_control.h"
+#include "shill/mock_dbus_properties_proxy.h"
+#include "shill/mock_glib.h"
+#include "shill/mock_sockets.h"
+#include "shill/modem.h"
+#include "shill/proxy_factory.h"
+#include "shill/rtnl_handler.h"
+#include "shill/shill_event.h"
+
+using std::string;
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::StrictMock;
+using testing::Test;
+
+namespace shill {
+
+namespace {
+
+const int kTestInterfaceIndex = 5;
+
+ACTION(SetInterfaceIndex) {
+ if (arg2) {
+ reinterpret_cast<struct ifreq *>(arg2)->ifr_ifindex = kTestInterfaceIndex;
+ }
+}
+
+} // namespace
+
+class ModemTest : public Test {
+ public:
+ ModemTest()
+ : manager_(&control_interface_, &dispatcher_, &glib_),
+ proxy_factory_(&dbus_properties_proxy_),
+ modem_(kOwner, kPath, &control_interface_, &dispatcher_, &manager_) {}
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ void SetSockets(Sockets *sockets) {
+ RTNLHandler::GetInstance()->sockets_ = sockets;
+ }
+
+ protected:
+ class TestProxyFactory : public ProxyFactory {
+ public:
+ TestProxyFactory(DBusPropertiesProxyInterface *dbus_properties_proxy)
+ : dbus_properties_proxy_(dbus_properties_proxy) {}
+
+ virtual DBusPropertiesProxyInterface *CreateDBusPropertiesProxy(
+ Modem *modem,
+ const string &path,
+ const string &service) {
+ return dbus_properties_proxy_;
+ }
+
+ private:
+ DBusPropertiesProxyInterface *dbus_properties_proxy_;
+ };
+
+ static const char kOwner[];
+ static const char kPath[];
+
+ void ReleaseDBusPropertiesProxy();
+
+ MockGLib glib_;
+ MockControl control_interface_;
+ EventDispatcher dispatcher_;
+ Manager manager_;
+ MockDBusPropertiesProxy dbus_properties_proxy_;
+ TestProxyFactory proxy_factory_;
+ Modem modem_;
+ StrictMock<MockSockets> sockets_;
+};
+
+const char ModemTest::kOwner[] = ":1.18";
+const char ModemTest::kPath[] = "/org/chromium/ModemManager/Gobi/0";
+
+void ModemTest::SetUp() {
+ EXPECT_EQ(kOwner, modem_.owner_);
+ EXPECT_EQ(kPath, modem_.path_);
+ ProxyFactory::set_factory(&proxy_factory_);
+ SetSockets(&sockets_);
+}
+
+void ModemTest::TearDown() {
+ ReleaseDBusPropertiesProxy();
+ ProxyFactory::set_factory(NULL);
+ SetSockets(NULL);
+}
+
+void ModemTest::ReleaseDBusPropertiesProxy() {
+ DBusPropertiesProxyInterface *dbus_properties_proxy =
+ modem_.dbus_properties_proxy_.release();
+ EXPECT_TRUE(dbus_properties_proxy == NULL ||
+ dbus_properties_proxy == &dbus_properties_proxy_);
+}
+
+TEST_F(ModemTest, Init) {
+ static const char kLinkName[] = "usb1";
+ const int kTestSocket = 10;
+ DBusPropertiesMap props;
+ props[Modem::kPropertyIPMethod].writer().append_uint32(
+ MM_MODEM_IP_METHOD_DHCP);
+ props[Modem::kPropertyLinkName].writer().append_string("usb1");
+ EXPECT_CALL(dbus_properties_proxy_, GetAll(MM_MODEM_INTERFACE))
+ .WillOnce(Return(props));
+ EXPECT_CALL(sockets_, Socket(PF_INET, SOCK_DGRAM, 0)).WillOnce(Return(-1));
+ modem_.Init();
+}
+
+TEST_F(ModemTest, CreateCellularDevice) {
+ DBusPropertiesMap props;
+
+ modem_.CreateCellularDevice(props);
+ EXPECT_FALSE(modem_.device_.get());
+
+ props[Modem::kPropertyIPMethod].writer().append_uint32(
+ MM_MODEM_IP_METHOD_PPP);
+ modem_.CreateCellularDevice(props);
+ EXPECT_FALSE(modem_.device_.get());
+
+ props.erase(Modem::kPropertyIPMethod);
+ props[Modem::kPropertyIPMethod].writer().append_uint32(
+ MM_MODEM_IP_METHOD_DHCP);
+ modem_.CreateCellularDevice(props);
+ EXPECT_FALSE(modem_.device_.get());
+
+ static const char kLinkName[] = "usb0";
+ const int kTestSocket = 10;
+ props[Modem::kPropertyLinkName].writer().append_string(kLinkName);
+ EXPECT_CALL(sockets_, Socket(PF_INET, SOCK_DGRAM, 0))
+ .WillOnce(Return(kTestSocket));
+ EXPECT_CALL(sockets_, Ioctl(kTestSocket, SIOCGIFINDEX, _))
+ .WillOnce(DoAll(SetInterfaceIndex(), Return(0)));
+ EXPECT_CALL(sockets_, Close(kTestSocket))
+ .WillOnce(Return(0));
+ modem_.CreateCellularDevice(props);
+ EXPECT_TRUE(modem_.device_.get());
+ EXPECT_EQ(kLinkName, modem_.device_->link_name());
+ EXPECT_EQ(kTestInterfaceIndex, modem_.device_->interface_index());
+ // TODO(petkov): Confirm the device is register by the manager.
+}
+
+} // namespace shill
diff --git a/rtnl_handler.cc b/rtnl_handler.cc
index 089180e..443d1cf 100644
--- a/rtnl_handler.cc
+++ b/rtnl_handler.cc
@@ -6,6 +6,7 @@
#include <time.h>
#include <unistd.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ether.h>
@@ -14,7 +15,6 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <fcntl.h>
-#include <string>
#include <base/logging.h>
@@ -336,4 +336,30 @@
return AddressRequest(interface_index, RTM_DELADDR, 0, ipconfig);
}
+int RTNLHandler::GetInterfaceIndex(const string &interface_name) {
+ if (interface_name.empty()) {
+ LOG(ERROR) << "Empty interface name -- unable to obtain index.";
+ return -1;
+ }
+ struct ifreq ifr;
+ if (interface_name.size() >= sizeof(ifr.ifr_name)) {
+ LOG(ERROR) << "Interface name too long: " << interface_name.size() << " >= "
+ << sizeof(ifr.ifr_name);
+ return -1;
+ }
+ int socket = sockets_->Socket(PF_INET, SOCK_DGRAM, 0);
+ if (socket < 0) {
+ PLOG(ERROR) << "Unable to open INET socket";
+ return -1;
+ }
+ ScopedSocketCloser socket_closer(sockets_, socket);
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, interface_name.c_str(), sizeof(ifr.ifr_name));
+ if (sockets_->Ioctl(socket, SIOCGIFINDEX, &ifr) < 0) {
+ PLOG(ERROR) << "SIOCGIFINDEX error for " << interface_name;
+ return -1;
+ }
+ return ifr.ifr_ifindex;
+}
+
} // namespace shill
diff --git a/rtnl_handler.h b/rtnl_handler.h
index 905676b..62a7389 100644
--- a/rtnl_handler.h
+++ b/rtnl_handler.h
@@ -5,6 +5,7 @@
#ifndef SHILL_RTNL_HANDLER_
#define SHILL_RTNL_HANDLER_
+#include <string>
#include <vector>
#include <base/callback_old.h>
@@ -49,12 +50,14 @@
// function requires an EventDispatcher pointer so it can add itself to the
// event loop.
void Start(EventDispatcher *dispatcher, Sockets *sockets);
+
// This stops the event-monitoring function of the RTNL handler
void Stop();
// Add an RTNL event listener to the list of entities that will
// be notified of RTNL events.
void AddListener(RTNLListener *to_add);
+
// Remove a previously added RTNL event listener
void RemoveListener(RTNLListener *to_remove);
@@ -63,20 +66,29 @@
// be set, and they will be set to the corresponding bit in 'flags'.
void SetInterfaceFlags(int interface_index, unsigned int flags,
unsigned int change);
+
// Set address of a network interface that has a kernel index of
// 'interface_index'.
bool AddInterfaceAddress(int interface_index, const IPConfig &config);
+
// Remove address from a network interface that has a kernel index of
// 'interface_index'.
bool RemoveInterfaceAddress(int interface_index, const IPConfig &config);
+
// Request that various tables (link, address, routing) tables be
// exhaustively dumped via RTNL. As results arrive from the kernel
// they will be broadcast to all listeners. The possible values
// (multiple can be ORred together) are below.
void RequestDump(int request_flags);
+ // Returns the index of interface |interface_name|, or -1 if unable to
+ // determine the index.
+ int GetInterfaceIndex(const std::string &interface_name);
+
private:
friend class DeviceInfoTest;
+ friend class ModemTest;
+ friend class RTNLHandlerTest;
friend struct DefaultSingletonTraits<RTNLHandler>;
FRIEND_TEST(RTNLListenerTest, NoRun);
FRIEND_TEST(RTNLListenerTest, Run);
diff --git a/rtnl_handler_unittest.cc b/rtnl_handler_unittest.cc
new file mode 100644
index 0000000..c730423
--- /dev/null
+++ b/rtnl_handler_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2011 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 <string>
+
+#include <gtest/gtest.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "shill/mock_sockets.h"
+#include "shill/rtnl_handler.h"
+
+using std::string;
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::StrictMock;
+using testing::Test;
+
+namespace shill {
+
+namespace {
+
+const int kTestInterfaceIndex = 4;
+
+ACTION(SetInterfaceIndex) {
+ if (arg2) {
+ reinterpret_cast<struct ifreq *>(arg2)->ifr_ifindex = kTestInterfaceIndex;
+ }
+}
+
+} // namespace
+
+class RTNLHandlerTest : public Test {
+ protected:
+ void SetSockets(Sockets *sockets) {
+ RTNLHandler::GetInstance()->sockets_ = sockets;
+ }
+
+ virtual void SetUp() {
+ SetSockets(&sockets_);
+ }
+
+ virtual void TearDown() {
+ SetSockets(NULL);
+ }
+
+ StrictMock<MockSockets> sockets_;
+};
+
+TEST_F(RTNLHandlerTest, GetInterfaceName) {
+ EXPECT_EQ(-1, RTNLHandler::GetInstance()->GetInterfaceIndex(""));
+ {
+ struct ifreq ifr;
+ string name(sizeof(ifr.ifr_name), 'x');
+ EXPECT_EQ(-1, RTNLHandler::GetInstance()->GetInterfaceIndex(name));
+ }
+
+ const int kTestSocket = 123;
+ EXPECT_CALL(sockets_, Socket(PF_INET, SOCK_DGRAM, 0))
+ .Times(3)
+ .WillOnce(Return(-1))
+ .WillRepeatedly(Return(kTestSocket));
+ EXPECT_CALL(sockets_, Ioctl(kTestSocket, SIOCGIFINDEX, _))
+ .WillOnce(Return(-1))
+ .WillOnce(DoAll(SetInterfaceIndex(), Return(0)));
+ EXPECT_CALL(sockets_, Close(kTestSocket))
+ .Times(2)
+ .WillRepeatedly(Return(0));
+ EXPECT_EQ(-1, RTNLHandler::GetInstance()->GetInterfaceIndex("eth0"));
+ EXPECT_EQ(-1, RTNLHandler::GetInstance()->GetInterfaceIndex("wlan0"));
+ EXPECT_EQ(kTestInterfaceIndex,
+ RTNLHandler::GetInstance()->GetInterfaceIndex("usb0"));
+}
+
+} // namespace shill
diff --git a/sockets.cc b/sockets.cc
index b23e021..b1662f7 100644
--- a/sockets.cc
+++ b/sockets.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
@@ -19,6 +20,10 @@
return close(fd);
}
+int Sockets::Ioctl(int d, int request, void *argp) {
+ return ioctl(d, request, argp);
+}
+
ssize_t Sockets::Send(int sockfd, const void *buf, size_t len, int flags) {
return send(sockfd, buf, len, flags);
}
@@ -32,4 +37,13 @@
return socket(domain, type, protocol);
}
+ScopedSocketCloser::ScopedSocketCloser(Sockets *sockets, int fd)
+ : sockets_(sockets),
+ fd_(fd) {}
+
+ScopedSocketCloser::~ScopedSocketCloser() {
+ sockets_->Close(fd_);
+ fd_ = -1;
+}
+
} // namespace shill
diff --git a/sockets.h b/sockets.h
index 833157d..bface16 100644
--- a/sockets.h
+++ b/sockets.h
@@ -7,6 +7,8 @@
#include <sys/types.h>
+#include <base/basictypes.h>
+
namespace shill {
// A "sys/socket.h" abstraction allowing mocking in tests.
@@ -20,6 +22,9 @@
// close
virtual int Close(int fd);
+ // ioctl
+ virtual int Ioctl(int d, int request, void *argp);
+
// send
virtual ssize_t Send(int sockfd, const void *buf, size_t len, int flags);
@@ -31,6 +36,18 @@
virtual int Socket(int domain, int type, int protocol);
};
+class ScopedSocketCloser {
+ public:
+ ScopedSocketCloser(Sockets *sockets, int fd);
+ ~ScopedSocketCloser();
+
+ private:
+ Sockets *sockets_;
+ int fd_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSocketCloser);
+};
+
} // namespace shill
#endif // SHILL_SOCKETS_H_