Populate disk information from udev to disk manager.

BUG=chromium-os:13698
TEST=Built and manually tested udev code.

Review URL: http://codereview.chromium.org/6824082

Change-Id: Ib658cdeb5b68962970dcfaa1d97cdbc08e0fb48d
diff --git a/disk-manager.cc b/disk-manager.cc
index 1568565..2eda988 100644
--- a/disk-manager.cc
+++ b/disk-manager.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "disk-manager.h"
-
 #include "disk.h"
+#include "udev-device.h"
 
 #include <base/logging.h>
 #include <libudev.h>
@@ -30,8 +30,40 @@
 }
 
 std::vector<Disk> DiskManager::EnumerateDisks() {
-  //TODO(rtc): implement this...
   std::vector<Disk> disks;
+
+  struct udev_enumerate *enumerate = udev_enumerate_new(udev_);
+  udev_enumerate_add_match_subsystem(enumerate, "block");
+  udev_enumerate_scan_devices(enumerate);
+
+  struct udev_list_entry *device_list, *device_list_entry;
+  device_list = udev_enumerate_get_list_entry(enumerate);
+  udev_list_entry_foreach(device_list_entry, device_list) {
+    const char *path = udev_list_entry_get_name(device_list_entry);
+    udev_device *dev = udev_device_new_from_syspath(udev_, path);
+
+    LOG(INFO) << "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) << "   Devpath: " << udev_device_get_devpath(dev);
+    LOG(INFO) << "   Sysname: " << udev_device_get_sysname(dev);
+    LOG(INFO) << "   Syspath: " << udev_device_get_syspath(dev);
+    LOG(INFO) << "   Properties: ";
+    struct udev_list_entry *property_list, *property_list_entry;
+    property_list = udev_device_get_properties_list_entry(dev);
+    udev_list_entry_foreach (property_list_entry, property_list) {
+      const char *key = udev_list_entry_get_name(property_list_entry);
+      const char *value = udev_list_entry_get_value(property_list_entry);
+      LOG(INFO) << "      " << key << " = " << value;
+    }
+
+    disks.push_back(UdevDevice(dev).ToDisk());
+    udev_device_unref(dev);
+  }
+
+  udev_enumerate_unref(enumerate);
+
   return disks;
 }
 
diff --git a/udev-device.cc b/udev-device.cc
new file mode 100644
index 0000000..e324706
--- /dev/null
+++ b/udev-device.cc
@@ -0,0 +1,158 @@
+// 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 <base/string_number_conversions.h>
+#include <base/string_util.h>
+#include <fcntl.h>
+#include <libudev.h>
+#include <sys/statvfs.h>
+#include <fstream>
+
+#include "udev-device.h"
+
+namespace cros_disks {
+
+UdevDevice::UdevDevice(struct udev_device *dev)
+    : dev_(dev) {
+
+  CHECK(dev_) << "Invalid udev device";
+  udev_device_ref(dev_);
+}
+
+UdevDevice::~UdevDevice() {
+  udev_device_unref(dev_);
+}
+
+bool UdevDevice::IsValueBooleanTrue(const char *value) const {
+  return value && strcmp(value, "1") == 0;
+}
+
+std::string UdevDevice::GetAttribute(const char *key) const {
+  const char *value = udev_device_get_sysattr_value(dev_, key);
+  return (value) ? value : "";
+}
+
+bool UdevDevice::IsAttributeTrue(const char *key) const {
+  const char *value = udev_device_get_sysattr_value(dev_, key);
+  return IsValueBooleanTrue(value);
+}
+
+bool UdevDevice::HasAttribute(const char *key) const {
+  const char *value = udev_device_get_sysattr_value(dev_, key);
+  return value != NULL;
+}
+
+std::string UdevDevice::GetProperty(const char *key) const {
+  const char *value = udev_device_get_property_value(dev_, key);
+  return (value) ? value : "";
+}
+
+bool UdevDevice::IsPropertyTrue(const char *key) const {
+  const char *value = udev_device_get_property_value(dev_, key);
+  return IsValueBooleanTrue(value);
+}
+
+bool UdevDevice::HasProperty(const char *key) const {
+  const char *value = udev_device_get_property_value(dev_, key);
+  return value != NULL;
+}
+
+void UdevDevice::GetSizeInfo(uint64 *total_size, uint64 *remaining_size) const {
+  const char *dev_file = udev_device_get_devnode(dev_);
+
+  struct statvfs stat;
+  bool stat_available = (statvfs(dev_file, &stat) == 0);
+
+  if (total_size) {
+    *total_size = (stat_available) ? (stat.f_blocks * stat.f_frsize) : 0;
+    const char *partition_size = udev_device_get_property_value(dev_,
+            "UDISKS_PARTITION_SIZE");
+    int64 size = 0;
+    if (partition_size) {
+      base::StringToInt64(partition_size, &size);
+      *total_size = size;
+    } else {
+      const char *size_attr = udev_device_get_sysattr_value(dev_, "size");
+      if (size_attr) {
+        base::StringToInt64(size_attr, &size);
+        *total_size = size;
+      }
+    }
+  }
+
+  if (remaining_size)
+    *remaining_size = (stat_available) ? (stat.f_bfree * stat.f_frsize) : 0;
+}
+
+bool UdevDevice::IsMediaAvailable() const {
+  bool is_media_available = true;
+  if (IsAttributeTrue("removable")) {
+    if (IsPropertyTrue("ID_CDROM")) {
+      is_media_available = IsPropertyTrue("ID_CDROM_MEDIA");
+    } else {
+      const char *dev_file = udev_device_get_devnode(dev_);
+      int fd = open(dev_file, O_RDONLY);
+      if (fd < 0) {
+        is_media_available = true;
+      } else {
+        close(fd);
+      }
+    }
+  }
+  return is_media_available;
+}
+
+std::vector<std::string> UdevDevice::GetMountedPaths() const {
+  const std::string dev_file = udev_device_get_devnode(dev_);
+  std::ifstream fs("/proc/mounts");
+  if (fs.is_open()) {
+    return ParseMountedPaths(dev_file, fs);
+  }
+  return std::vector<std::string>();
+}
+
+std::vector<std::string> UdevDevice::ParseMountedPaths(
+    const std::string& device_path, std::istream& stream) {
+  std::vector<std::string> mounted_paths;
+  std::string line;
+  while (std::getline(stream, line)) {
+    std::vector<std::string> tokens;
+    SplitString(line, ' ', &tokens);
+    if (tokens.size() >= 2) {
+      if (tokens[0] == device_path)
+        mounted_paths.push_back(tokens[1]);
+    }
+  }
+  return mounted_paths;
+}
+
+Disk UdevDevice::ToDisk() const {
+  Disk disk;
+
+  disk.set_is_read_only(IsAttributeTrue("ro"));
+  disk.set_is_drive(HasAttribute("range"));
+  disk.set_is_rotational(HasProperty("ID_ATA_ROTATION_RATE_RPM"));
+  disk.set_is_optical_disk(IsPropertyTrue("ID_CDROM"));
+  disk.set_is_hidden(IsPropertyTrue("UDISKS_PRESENTATION_HIDE"));
+  disk.set_is_media_available(IsMediaAvailable());
+  disk.set_drive_model(GetProperty("ID_MODEL"));
+  disk.set_label(GetProperty("ID_FS_LABEL"));
+  disk.set_native_path(udev_device_get_syspath(dev_));
+
+  const char *dev_file = udev_device_get_devnode(dev_);
+  disk.set_device_file(dev_file);
+
+  std::vector<std::string> mounted_paths = GetMountedPaths();
+  disk.set_is_mounted(!mounted_paths.empty());
+  disk.set_mount_path(mounted_paths[0]);  // TODO(benchan): multiple paths
+
+  uint64 total_size, remaining_size;
+  GetSizeInfo(&total_size, &remaining_size);
+  disk.set_device_capacity(total_size);
+  disk.set_bytes_remaining(remaining_size);
+
+  return disk;
+}
+
+}  // namespace cros_disks
diff --git a/udev-device.h b/udev-device.h
new file mode 100644
index 0000000..ef5c6c1
--- /dev/null
+++ b/udev-device.h
@@ -0,0 +1,70 @@
+// 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 CHOS_DISKS_UDEV_DEVICE_H_
+#define CROS_DISKS_UDEV_DEVICE_H_
+
+#include <base/basictypes.h>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "disk.h"
+
+struct udev_device;
+
+namespace cros_disks {
+
+// A utility class that helps query information about a udev device.
+class UdevDevice {
+ public:
+
+  explicit UdevDevice(struct udev_device *dev);
+  ~UdevDevice();
+
+  // Gets the string value of a device attribute.
+  std::string GetAttribute(const char *key) const;
+
+  // Checks if the value of a device attribute represents a Boolean true.
+  bool IsAttributeTrue(const char *key) const;
+
+  // Checks if a device attribute exists.
+  bool HasAttribute(const char *key) const;
+
+  // Gets the string value of a device property.
+  std::string GetProperty(const char *key) const;
+
+  // Checks if the value of a device property represents a Boolean true.
+  bool IsPropertyTrue(const char *key) const;
+
+  // Checks if a device property exists.
+  bool HasProperty(const char *key) const;
+
+  // Gets the total and remaining capacity of the device.
+  void GetSizeInfo(uint64 *total_size, uint64 *remaining_size) const;
+
+  // Checks if any media is available in the device.
+  bool IsMediaAvailable() const;
+
+  // Gets the mounted paths for the device.
+  std::vector<std::string> GetMountedPaths() const;
+
+  // Gets the mounted paths for an input stream that has the
+  // same format as /proc/mounts
+  static std::vector<std::string> ParseMountedPaths(
+      const std::string& device_path, std::istream& stream);
+
+  // Returns a Disk object based on the device information.
+  Disk ToDisk() const;
+
+ private:
+
+  bool IsValueBooleanTrue(const char *value) const;
+
+  mutable struct udev_device *dev_;
+};
+
+}  // namespace cros_disks
+
+#endif  // CROS_DISKS_UDEV_DEVICE_H_