First batch of cros-disk changes

A couple of things in this CL.

1. Adds an upstart script that runs the daemon as cros-disks.
2. Adds some functions to the d-bus API.
3. Adds support for watching udev for changes.
4. Adds a DiskManager class for processing disk changes.
5. Adds a Disk class to store the properties that we care about.
6. Some Makefile changes to fix NDEBUG and gflags issues.

Change-Id: I92f7d9199b71aa0150ad82cabe36c5396a4603c8

BUG=13698
TEST=Ran the autotest and manually tested that I was getting udev changes on a cr-48.

Review URL: http://codereview.chromium.org/6824032
diff --git a/Makefile b/Makefile
index 03e1930..8121151 100644
--- a/Makefile
+++ b/Makefile
@@ -10,19 +10,28 @@
 include common.mk
 
 PKG_CONFIG ?= pkg-config
+DBUSXX_XML2CPP = dbusxx-xml2cpp
 
-INCLUDE_DIRS = -I.. $(shell $(PKG_CONFIG) --cflags dbus-1 dbus-glib-1\
+INCLUDE_DIRS = -I.. -I$(OUT)include $(shell $(PKG_CONFIG) --cflags dbus-1 dbus-glib-1\
 	dbus-c++-1 glib-2.0)
 LIB_DIRS = $(shell $(PKG_CONFIG) --libs dbus-1 dbus-glib-1 dbus-c++-1 glib-2.0)
 
 CFLAGS := -Iinclude $(CFLAGS)
 CXXFLAGS := -Iinclude -I../ $(INCLUDE_DIRS) $(CXXFLAGS)
-LDFLAGS += -lbase -lgflags -lmetrics $(LIB_DIRS)
+LDFLAGS += -lbase -lgflags -lmetrics -ludev $(LIB_DIRS)
 
-disks: $(OUT)disks
+$(OUT)include/cros-disks-server.h: cros-disks.xml
+	mkdir -p $(OUT)include
+	$(DBUSXX_XML2CPP) cros-disks.xml --adaptor=$@
+RM_ON_CLEAN += $(OUT)include/cros-disks-server.h
 
 $(OUT)disks: $(filter-out %_testrunner.o %_unittest.o,$(C_OBJECTS)) \
+              $(OUT)include/cros-disks-server.h \
               $(CXX_OBJECTS)
 	$(call cxx_binary)
 all: $(OUT)disks
 RM_ON_CLEAN += $(OUT)disks
+
+# Some shortcuts
+disks: $(OUT)disks
+dbus-headers: $(OUT)include/cros-disks-server.h
diff --git a/common.mk b/common.mk
index 36d8532..1a55220 100644
--- a/common.mk
+++ b/common.mk
@@ -165,10 +165,10 @@
 #  CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list
 #  CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value
 # The same goes for CFLAGS.
-CXXFLAGS := $(CXXFLAGS) -Wall -Werror -fstack-protector-all  -DFORTIFY_SOURCE \
-  -O2 -ggdb3 -DNDEBUG -Wa,--noexecstack
-CFLAGS := $(CFLAGS) -Wall -Werror -fstack-protector-all -DFORTIFY_SOURCE \
-  -O2 -ggdb3 -DNDEBUG -Wa,--noexecstack
+CXXFLAGS := $(CXXFLAGS) -Wall -Werror -fstack-protector-all -fno-strict-aliasing -DFORTIFY_SOURCE \
+  -ggdb3 -Wa,--noexecstack
+CFLAGS := $(CFLAGS) -Wall -Werror -fstack-protector-all -fno-strict-aliasing -DFORTIFY_SOURCE \
+  -ggdb3 -Wa,--noexecstack
 
 ifeq ($(PROFILING),1)
   CFLAGS := -pg 
diff --git a/cros-disks-server-impl.cc b/cros-disks-server-impl.cc
new file mode 100644
index 0000000..4b61906
--- /dev/null
+++ b/cros-disks-server-impl.cc
@@ -0,0 +1,45 @@
+// 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 "cros-disks-server-impl.h"
+
+#include "disk.h"
+
+#include <sys/mount.h>
+
+namespace cros_disks {
+
+// TODO(rtc): this should probably be a flag.
+static const char* kServicePath = "/org/chromium/CrosDisks";
+
+CrosDisksServer::CrosDisksServer(DBus::Connection& connection)  
+    : DBus::ObjectAdaptor(connection, kServicePath) { }
+
+CrosDisksServer::~CrosDisksServer() { }
+
+bool CrosDisksServer::IsAlive(DBus::Error& error) {  // NOLINT
+  return true;
+}
+
+void CrosDisksServer::FilesystemMount(
+    const std::string& nullArgument, 
+    const std::vector<std::string>& mountOptions,
+    DBus::Error& error) {  // NOLINT
+
+  return;
+}
+
+void CrosDisksServer::FilesystemUnmount(
+    const std::vector<std::string>& mountOptions,
+    DBus::Error& error) {  // NOLINT
+
+  return;
+}
+
+DBusDisks CrosDisksServer::GetAll(DBus::Error& error) { // NOLINT
+  DBusDisks v;
+  return v;
+}
+
+} // namespace cros_disks
diff --git a/cros-disks-server-impl.h b/cros-disks-server-impl.h
new file mode 100644
index 0000000..cd2af8d
--- /dev/null
+++ b/cros-disks-server-impl.h
@@ -0,0 +1,50 @@
+// 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 CROS_DISKS_SERVER_IMPL_H__
+#define CROS_DISKS_SERVER_IMPL_H__
+
+#include "cros-disks-server.h"
+#include "disk.h"
+
+#include <string>
+#include <vector>
+
+namespace cros_disks {
+
+// The d-bus server for the cros-disks daemon. 
+//
+// Example Usage:
+// 
+// DBus::Connection server_conn = DBus::Connection::SystemBus();
+// server_conn.request_name("org.chromium.CrosDisks");
+// CrosDisksServer* server = new(std::nothrow) CrosDisksServer(server_conn);
+// 
+// At this point the server should be attached to the main loop.
+//
+class CrosDisksServer : public org::chromium::CrosDisks_adaptor,
+                        public DBus::IntrospectableAdaptor,
+                        public DBus::ObjectAdaptor {
+ public:
+  CrosDisksServer(DBus::Connection& connection);
+  virtual ~CrosDisksServer();
+
+  // A method for checking if the daemon is running. Always returns true.
+  virtual bool IsAlive(DBus::Error& error);  // NOLINT
+
+  // Unmounts a device when invoked.
+  virtual void FilesystemUnmount(const std::vector<std::string>& mountOptions,
+                                 DBus::Error& error); // NOLINT
+
+  // Mounts a device when invoked.
+  virtual void FilesystemMount(const std::string& nullArgument, 
+                               const std::vector<std::string>& mountOptions,
+                               DBus::Error& error); // NOLINT
+
+  // Returns a description of every disk device attached to the sytem.
+  virtual DBusDisks GetAll(DBus::Error& error); // NOLINT
+};
+} // namespace cros_disks
+
+#endif // CROS_DISKS_SERVER_IMPL_H__
diff --git a/cros-disks.conf b/cros-disks.conf
new file mode 100644
index 0000000..a04a6df
--- /dev/null
+++ b/cros-disks.conf
@@ -0,0 +1,23 @@
+# 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.
+
+# cros-disks upstart job
+
+env CROS_DISKS_LOG_DIR=/var/log/cros-disks
+env CROS_DISKS_UID=213
+env CROS_DISKS_GID=213
+
+start on started dbus
+stop on starting halt or starting reboot
+
+respawn
+expect fork
+
+pre-start script
+  mkdir -p -m 0755 "${CROS_DISKS_LOG_DIR}"
+  chown -R cros-disks:cros-disks "${CROS_DISKS_LOG_DIR}"
+end script
+
+exec /sbin/minijail --uid="${CROS_DISKS_UID}" --gid="${CROS_DISKS_GID}" -- /opt/google/cros-disks/disks 
+
diff --git a/cros-disks.xml b/cros-disks.xml
new file mode 100644
index 0000000..9c6eb29
--- /dev/null
+++ b/cros-disks.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.CrosDisks">
+    <tp:struct name="DiskDevice" array-name="DiskDevices">
+      <tp:member name="deviceIsDrive" type="b"> </tp:member>
+      <tp:member name="devicePresentationHide" type="b"> </tp:member>
+      <tp:member name="deviceIsMounted" type="b"> </tp:member>
+      <tp:member name="deviceMountPath" type="s"> </tp:member>
+      <tp:member name="label" type="s"> </tp:member>
+      <tp:member name="driveModel" type="s"> </tp:member>
+      <tp:member name="isRotational" type="b"> </tp:member>
+      <tp:member name="isOptical" type="b"> </tp:member>
+      <tp:member name="isReadOnly" type="b"> </tp:member>
+      <tp:member name="capacity" type="d"> </tp:member>
+      <tp:member name="bytes_remaining" type="d"> </tp:member>
+    </tp:struct>
+    <method name="FilesystemMount">
+      <arg name="nullArgument" type="s" direction="in">
+      </arg>
+      <arg name="mountOptions" type="as" direction="in">
+      </arg>
+    </method>
+    <method name="FilesystemUnmount">
+      <arg name="mountOptions" type="as" direction="in">
+      </arg>
+    </method>
+    <method name="GetAll">
+      <arg name="disks" type="aa{sv}" direction="out" tp:type="DiskDevices">
+      </arg>
+    </method>
+    <method name="IsAlive">
+      <tp:docstring>
+        Test method to verify that Cashew service is working.
+      </tp:docstring>
+      <arg name="result" type="b" direction="out">
+        <tp:docstring>
+          Boolean indicating whether Cashew service is alive.
+        </tp:docstring>
+      </arg>
+    </method>
+  </interface>
+</node>
diff --git a/disk-manager.cc b/disk-manager.cc
new file mode 100644
index 0000000..1568565
--- /dev/null
+++ b/disk-manager.cc
@@ -0,0 +1,50 @@
+// 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 "disk-manager.h"
+
+#include "disk.h"
+
+#include <base/logging.h>
+#include <libudev.h>
+#include <vector>
+
+
+namespace cros_disks {
+
+DiskManager::DiskManager() 
+    : udev_(udev_new()), 
+      udev_monitor_fd_(0) { 
+
+  CHECK(udev_) << "Failed to initialize udev";
+  udev_monitor_ = udev_monitor_new_from_netlink(udev_, "udev");
+  udev_monitor_filter_add_match_subsystem_devtype(udev_monitor_, "block", NULL);
+  udev_monitor_enable_receiving(udev_monitor_);
+  udev_monitor_fd_ = udev_monitor_get_fd(udev_monitor_);
+}
+
+DiskManager::~DiskManager() {
+  udev_monitor_unref(udev_monitor_);
+  udev_unref(udev_);
+}
+
+std::vector<Disk> DiskManager::EnumerateDisks() {
+  //TODO(rtc): implement this...
+  std::vector<Disk> disks;
+  return disks;
+}
+
+bool DiskManager::ProcessUdevChanges() {
+  struct udev_device *dev = udev_monitor_receive_device(udev_monitor_);
+  CHECK(dev) << "Unknown udev device";
+  LOG(INFO) << "Got Device";
+  LOG(INFO) << "   Node: " << udev_device_get_devnode(dev);
+  LOG(INFO) << "   Subsystem: " << udev_device_get_subsystem(dev);
+  LOG(INFO) << "   Devtype: " << udev_device_get_devtype(dev);
+  LOG(INFO) << "   Action: " << udev_device_get_action(dev);
+  udev_device_unref(dev);
+  return true;
+}
+
+} // namespace cros_disks
diff --git a/disk-manager.h b/disk-manager.h
new file mode 100644
index 0000000..1714558
--- /dev/null
+++ b/disk-manager.h
@@ -0,0 +1,56 @@
+// 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 DISK_MANAGER_H__
+#define DISK_MANAGER_H__
+
+#include <libudev.h>
+#include <vector>
+
+namespace cros_disks {
+
+class Disk;
+
+// The DiskManager is responsible for reading device state from udev. 
+// Said changes could be the result of a udev notification or a synchronous 
+// call to enumerate the relevant storage devices attached to the system. 
+//
+// Sample Usage:
+//
+// DiskManager manager;
+// manager.EnumerateDisks();
+// select(manager.udev_monitor_fd())...
+//
+// This class is designed to run within a single-threaded GMainLoop application
+// and should not be considered thread safe.
+class DiskManager {
+ public:
+  DiskManager();
+  virtual ~DiskManager();
+
+  // Lists the current block devices attached to the system.
+  virtual std::vector<Disk> EnumerateDisks();
+
+  // Reads the changes from udev. Must be called to clear the 
+  // fd.
+  bool ProcessUdevChanges();
+
+  // A file descriptor that can be select()ed or poll()ed for system changes.
+  int udev_monitor_fd() const { return udev_monitor_fd_; }
+
+ private:
+
+  // The root udev object.
+  struct udev* udev_;
+
+  // Provides access to udev changes as they occur.
+  struct udev_monitor* udev_monitor_;
+
+  // A file descriptor that indicates changes to the system.
+  int udev_monitor_fd_;
+};
+
+} // namespace cros_disks
+
+#endif // DISK_MANAGER_H__
diff --git a/disk.cc b/disk.cc
new file mode 100644
index 0000000..fe0bf8c
--- /dev/null
+++ b/disk.cc
@@ -0,0 +1,35 @@
+// 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 "disk.h"
+
+namespace cros_disks {
+
+// Keys that libcros expects to see on the wire. 
+// TODO(rtc): We should probably stuff these in a shared header...
+const char kDeviceIsDrive[] = "DeviceIsDrive";
+const char kDevicePresentationHide[] = "DevicePresentationHide";
+const char kDeviceIsMounted[] = "DeviceIsMounted";
+const char kDeviceMountPaths[] = "DeviceMountPaths";
+const char kDeviceIsMediaAvailable[] = "DeviceIsMediaAvailable";
+const char kNativePath[] = "NativePath";
+const char kDeviceFile[] = "DeviceFile";
+const char kLabel[] = "IdLabel";
+const char kDriveModel[] = "DriveModel";
+const char kPartitionSlave[] = "PartitionSlave";
+const char kDriveIsRotational[] = "DriveIsRotational";
+const char kDeviceIsOpticalDisc[] = "DeviceIsOpticalDisc";
+const char kDeviceSize[] = "DeviceSize";
+const char kReadOnly[] = "DeviceIsReadOnly";
+
+
+// TODO(rtc): The constructor should set some defaults, but I'm still iterating
+// on the data model.
+Disk::Disk() { 
+}
+
+Disk::~Disk() {
+}
+
+} // namespace cros_disks
diff --git a/disk.h b/disk.h
new file mode 100644
index 0000000..14cc059
--- /dev/null
+++ b/disk.h
@@ -0,0 +1,106 @@
+// 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 DISK_H__
+#define DISK_H__
+
+#include <base/basictypes.h>
+#include <dbus-c++/dbus.h>  // NOLINT
+#include <map>
+#include <string>
+#include <vector>
+
+namespace cros_disks {
+
+typedef std::map<std::string, DBus::Variant> DBusDisk;
+typedef std::vector<DBusDisk> DBusDisks;
+
+// A simple type that describes a storage device attached to our system.
+//
+// This class was designed to run in a single threaded context and should not
+// be considered thread safe.
+class Disk {
+ public:
+
+  Disk();
+  virtual ~Disk();
+
+  bool is_drive() const { return is_drive_; }
+  void set_is_drive(bool is_drive) { is_drive_ = is_drive; }
+
+  bool is_hidden() const { return is_hidden_; }
+  void set_is_hidden(bool is_hidden) { is_hidden_ = is_hidden; }
+  
+  bool is_mounted() const { return is_mounted_; }
+  void set_is_mounted(bool is_mounted) { is_mounted_ = is_mounted; }
+
+  bool is_media_available() const { return is_media_available_; }
+  void set_is_media_available(bool is_media_available) {
+    is_media_available_ = is_media_available;
+  }
+
+  bool is_rotational() const { return is_rotational_; }
+  void set_is_rotational(bool is_rotational) { is_rotational_ = is_rotational; }
+
+  bool is_optical_disk() const { return is_optical_disk_; }
+  void set_is_optical_disk(bool is_optical_disk) { 
+    is_optical_disk_ = is_optical_disk; 
+  }
+
+  bool is_read_only() const { return is_read_only_; }
+  void set_is_read_only(bool is_read_only) { is_read_only_ = is_read_only; }
+
+  std::string mount_path() const { return mount_path_; }
+  void set_mount_path(const std::string& mount_path) { mount_path_ = mount_path; }
+
+  std::string native_path() const { return native_path_; }
+  void set_native_path(const std::string& native_path) {
+    native_path_ = native_path;
+  }
+
+  std::string device_file() const { return device_file_; }
+  void set_device_file(const std::string& device_file) {
+    device_file_ = device_file;
+  }
+  
+  std::string label() const { return label_; }
+  void set_label(const std::string& label) { label_ = label; }
+
+  std::string drive_model() const { return drive_model_; }
+  void set_drive_model(const std::string& drive_model) {
+    drive_model_ = drive_model; 
+  }
+
+  uint64 device_capacity() const { return device_capacity_; }
+  void set_device_capacity(uint64 device_capacity) {
+    device_capacity_ = device_capacity; 
+  }
+
+  uint64 bytes_remaining() { return bytes_remaining_; }
+  void set_bytes_remaining(uint64 bytes_remaining) { 
+    bytes_remaining_ = bytes_remaining; 
+  }
+
+ private:
+
+  bool is_drive_;
+  bool is_hidden_;
+  bool is_mounted_;
+  bool is_media_available_;
+  bool is_rotational_;
+  bool is_optical_disk_;
+  bool is_read_only_;
+  std::string mount_path_;
+  std::string native_path_;
+  std::string device_file_;
+  std::string label_;
+  std::string drive_model_;
+  uint64 device_capacity_;
+  uint64 bytes_remaining_;
+};
+
+} // namespace cros_disks
+
+
+#endif // DISK_H__
diff --git a/main.cc b/main.cc
index a38faa0..56bb11f 100644
--- a/main.cc
+++ b/main.cc
@@ -4,6 +4,9 @@
 
 // A simple daemon to detect, mount, and eject removable storage devices.
 
+#include "cros-disks-server-impl.h"
+#include "disk-manager.h"
+#include <base/basictypes.h>
 #include <base/file_util.h>
 #include <base/logging.h>
 #include <base/string_util.h>
@@ -12,23 +15,86 @@
 #include <gflags/gflags.h>
 #include <glib-object.h>
 #include <glib.h>
+#include <libudev.h>
 #include <metrics/metrics_library.h>
 
+
+using cros_disks::CrosDisksServer;
+using cros_disks::DiskManager;
+
+DEFINE_bool(foreground, false, 
+            "Don't daemon()ize; run in foreground.");
+
+// TODO(rtc): gflags string defines require the use of
+// -fno-strict-aliasing for some reason. Verify that disabling this check
+// is sane.
+DEFINE_string(log_dir, "/var/log/cros-disks", "log directory");
+
+void SetupLogging() {
+  logging::InitLogging(FLAGS_log_dir.c_str(),
+                       logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
+                       logging::DONT_LOCK_LOG_FILE,
+                       logging::APPEND_TO_OLD_LOG_FILE);
+}
+
+// This callback will be invoked once udev has data about 
+// new, changed, or removed devices.
+gboolean UdevCallback(GIOChannel* source, 
+                      GIOCondition condition, 
+                      gpointer data) {
+  DiskManager* mgr = static_cast<DiskManager*>(data);
+  mgr->ProcessUdevChanges();
+  return true;
+}
+
 int main(int argc, char** argv) {
   ::g_type_init();
   g_thread_init(NULL);
   google::ParseCommandLineFlags(&argc, &argv, true);
 
+  if(!FLAGS_foreground) {
+    PLOG_IF(FATAL, daemon(0, 0) == 1) << "daemon() failed";
+    // SetupLogging();
+  }
+
   LOG(INFO) << "Creating a GMainLoop";
   GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
+  CHECK(loop) << "Failed to create a GMainLoop";
+
+  LOG(INFO) << "Creating the dbus dispatcher";
+  DBus::Glib::BusDispatcher* dispatcher = 
+      new(std::nothrow) DBus::Glib::BusDispatcher();
+  CHECK(dispatcher) << "Failed to create a dbus-dispatcher";
+  DBus::default_dispatcher = dispatcher;
+  dispatcher->attach(NULL);
+
+  LOG(INFO) << "creating server";
+  DBus::Connection server_conn = DBus::Connection::SystemBus();
+  server_conn.request_name("org.chromium.CrosDisks");
+  CrosDisksServer* server = new(std::nothrow) CrosDisksServer(server_conn);
+  CHECK(server) << "Failed to create the cros-disks server";
 
   LOG(INFO) << "Initializing the metrics library";
   MetricsLibrary metrics_lib;
   metrics_lib.Init();
 
-  // Run the main loop until exit time:
-  // TODO(rtc): daemonize this
+
+  DiskManager manager;
+  manager.EnumerateDisks();
+
+  // Setup a monitor
+  g_io_add_watch_full(g_io_channel_unix_new(manager.udev_monitor_fd()),
+                      G_PRIORITY_HIGH_IDLE,
+                      GIOCondition(G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_NVAL),
+                      UdevCallback,
+                      &manager,
+                      NULL);
   g_main_loop_run(loop);
 
+  LOG(INFO) << "Cleaining up and exiting";
+  g_main_loop_unref(loop);
+  delete server;
+  delete dispatcher;
+
   return 0;
 }
diff --git a/org.chromium.CrosDisks.conf b/org.chromium.CrosDisks.conf
new file mode 100644
index 0000000..5c07baa
--- /dev/null
+++ b/org.chromium.CrosDisks.conf
@@ -0,0 +1,12 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <policy user="cros-disks">
+    <allow own="org.chromium.CrosDisks"/>
+  </policy>
+  <policy context="default">
+    <allow send_destination="org.chromium.CrosDisks"/>
+  </policy>
+  <limit name="max_replies_per_connection">512</limit>
+</busconfig>