shill: Add File IO wrapper

Add a layer of indirection over basic system file IO calls like
write/read so that this behavior can be mocked out in tests.  This is
especially needed because we register Ready/InputIO handlers with the
event loop based on the raw file descriptors.  We must later use those
raw descriptors to do I/O since higher order abstractions seem to have
blocking semantics.

BUG=chromium-os:39055
TEST=Unit tests still pass.

Change-Id: Ic653436b39ecbe21b275b8f3bceb4b14f162e903
Reviewed-on: https://gerrit.chromium.org/gerrit/44127
Tested-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
diff --git a/Makefile b/Makefile
index 21db018..559bd3c 100644
--- a/Makefile
+++ b/Makefile
@@ -218,6 +218,7 @@
 	ethernet.o \
 	ethernet_service.o \
 	event_dispatcher.o \
+	file_io.o \
 	file_reader.o \
 	geolocation_info.o \
 	glib.o \
diff --git a/file_io.cc b/file_io.cc
new file mode 100644
index 0000000..5bab442
--- /dev/null
+++ b/file_io.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 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/file_io.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <base/posix/eintr_wrapper.h>
+
+namespace shill {
+
+namespace {
+
+static base::LazyInstance<FileIO> g_file_io =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+FileIO::FileIO() {}
+
+FileIO::~FileIO() {}
+
+// static
+FileIO *FileIO::GetInstance() {
+  return g_file_io.Pointer();
+}
+
+ssize_t FileIO::Write(int fd, const void *buf, size_t count) {
+  return HANDLE_EINTR(write(fd, buf, count));
+}
+
+ssize_t FileIO::Read(int fd, void *buf, size_t count) {
+  return HANDLE_EINTR(read(fd, buf, count));
+}
+
+int FileIO::Close(int fd) {
+  return HANDLE_EINTR(close(fd));
+}
+
+int FileIO::SetFdNonBlocking(int fd) {
+  const int flags = HANDLE_EINTR(fcntl(fd, F_GETFL)) | O_NONBLOCK;
+  return HANDLE_EINTR(fcntl(fd, F_SETFL,  flags));
+}
+
+}  // namespace shill
diff --git a/file_io.h b/file_io.h
new file mode 100644
index 0000000..8b654ab
--- /dev/null
+++ b/file_io.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2013 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_FILE_IO_H_
+#define SHILL_FILE_IO_H_
+
+#include <base/lazy_instance.h>
+
+namespace shill {
+
+// A POSIX file IO wrapper to allow mocking in unit tests.
+class FileIO {
+ public:
+  virtual ~FileIO();
+
+  // This is a singleton -- use FileIO::GetInstance()->Foo()
+  static FileIO *GetInstance();
+
+  virtual ssize_t Write(int fd, const void *buf, size_t count);
+  virtual ssize_t Read(int fd, void *buf, size_t count);
+  virtual int Close(int fd);
+  virtual int SetFdNonBlocking(int fd);
+
+ protected:
+  FileIO();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<FileIO>;
+
+  DISALLOW_COPY_AND_ASSIGN(FileIO);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_FILE_IO_H_
diff --git a/mock_file_io.h b/mock_file_io.h
new file mode 100644
index 0000000..eb11e59
--- /dev/null
+++ b/mock_file_io.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 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_FILE_IO_H_
+#define SHILL_MOCK_FILE_IO_H_
+
+#include "shill/file_io.h"
+
+#include <gmock/gmock.h>
+
+namespace shill {
+
+class MockFileIO : public FileIO {
+ public:
+  MockFileIO() {};
+  virtual ~MockFileIO() {}
+  MOCK_METHOD3(Write, ssize_t(int fd, const void *buf, size_t count));
+  MOCK_METHOD3(Read, ssize_t(int fd, void *buf, size_t count));
+  MOCK_METHOD1(Close, int(int fd));
+  MOCK_METHOD1(SetFdNonBlocking, int(int fd));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockFileIO);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_FILE_IO_H_