Added GPIO support for update engine automated test.

New functionality in update engine for discovering and reading the
dut_flaga/b GPIOs; we use the dut_flaga value as trigger for using
a different update server URL.

Note: in the future, we will migrate all GPIO functionality outside of
update engine, into its own dedicated package.

CQ-DEPEND=I82cdd28a87f5227e63586810534b92922d43ae52
BUG=chromium-os:25397
TEST=GPIOs were discovered and read on x86-alex, w/ and w/o servo.

Change-Id: Ice3a7ee9669c0916956b492c9524e4b5808d6fb3
Reviewed-on: https://gerrit.chromium.org/gerrit/16554
Commit-Ready: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
diff --git a/update_attempter.cc b/update_attempter.cc
index e79f692..210e78a 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -14,7 +14,9 @@
 #include <tr1/memory>
 #include <vector>
 
+#include <base/eintr_wrapper.h>
 #include <base/rand_util.h>
+#include <base/string_util.h>
 #include <glib.h>
 #include <metrics/metrics_library.h>
 #include <policy/libpolicy.h>
@@ -46,11 +48,54 @@
 
 const int UpdateAttempter::kMaxDeltaUpdateFailures = 3;
 
+// TODO(garnold) this is currently an arbitrary address and will change based on
+// discussion about the actual test lab configuration.
+const char* const UpdateAttempter::kTestUpdateUrl("https://10.0.0.1/update");
+
 const char* kUpdateCompletedMarker =
     "/var/run/update_engine_autoupdate_completed";
 
 namespace {
 const int kMaxConsecutiveObeyProxyRequests = 20;
+
+// Names of udev properties that are linked to the GPIO chip device and identify
+// the two dutflag GPIOs on different boards.
+const char kIdGpioDutflaga[] = "ID_GPIO_DUTFLAGA";
+const char kIdGpioDutflagb[] = "ID_GPIO_DUTFLAGB";
+
+// Scoped closer for udev and udev_enumerate objects.
+// TODO(garnold) chromium-os:26934: it would be nice to generalize the different
+// ScopedFooCloser implementations in update engine using a single template.
+class ScopedUdevCloser {
+ public:
+  explicit ScopedUdevCloser(udev **udev_p) : udev_p_(udev_p) {}
+  ~ScopedUdevCloser() {
+    if (udev_p_ && *udev_p_) {
+      udev_unref(*udev_p_);
+      *udev_p_ = NULL;
+    }
+  }
+ private:
+  struct udev **udev_p_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedUdevCloser);
+};
+
+class ScopedUdevEnumerateCloser {
+ public:
+  explicit ScopedUdevEnumerateCloser(udev_enumerate **udev_enum_p) :
+    udev_enum_p_(udev_enum_p) {}
+  ~ScopedUdevEnumerateCloser() {
+    if (udev_enum_p_ && *udev_enum_p_) {
+      udev_enumerate_unref(*udev_enum_p_);
+      *udev_enum_p_ = NULL;
+    }
+  }
+ private:
+  struct udev_enumerate **udev_enum_p_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedUdevEnumerateCloser);
+};
 }  // namespace {}
 
 const char* UpdateStatusToString(UpdateStatus status) {
@@ -131,8 +176,8 @@
   CleanupPriorityManagement();
 }
 
-void UpdateAttempter::Update(const std::string& app_version,
-                             const std::string& omaha_url,
+void UpdateAttempter::Update(const string& app_version,
+                             const string& omaha_url,
                              bool obey_proxies,
                              bool interactive) {
   chrome_proxy_resolver_.Init();
@@ -163,7 +208,27 @@
   if (policy_provider_->device_policy_is_loaded())
     policy_provider_->GetDevicePolicy().GetReleaseChannel(&release_track);
 
-  if (!omaha_request_params_.Init(app_version, omaha_url, release_track)) {
+  // Force alternate default address for automated test case, based on GPIO
+  // signal. We replicate the URL string so as not to overwrite the argument.
+  string omaha_url_to_use = omaha_url;
+  if (omaha_url_to_use.empty()) {
+    bool dutflaga_gpio_status;
+    if (GetDutflagaGpio(&dutflaga_gpio_status)) {
+      LOG(INFO) << "dutflaga GPIO status: "
+                << (dutflaga_gpio_status ? "on" : "off");
+
+      // The dut_flaga GPIO is actually signaled when in the 'off' position.
+      if (!dutflaga_gpio_status) {
+        LOG(INFO) << "using alternative server address: " << kTestUpdateUrl;
+        omaha_url_to_use = kTestUpdateUrl;
+      }
+    } else {
+      LOG(ERROR) << "reading dutflaga GPIO status failed";
+    }
+  }
+
+  if (!omaha_request_params_.Init(app_version, omaha_url_to_use,
+                                  release_track)) {
     LOG(ERROR) << "Unable to initialize Omaha request device params.";
     return;
   }
@@ -291,8 +356,8 @@
   UpdateBootFlags();
 }
 
-void UpdateAttempter::CheckForUpdate(const std::string& app_version,
-                                     const std::string& omaha_url) {
+void UpdateAttempter::CheckForUpdate(const string& app_version,
+                                     const string& omaha_url) {
   if (status_ != UPDATE_STATUS_IDLE) {
     LOG(INFO) << "Check for update requested, but status is "
               << UpdateStatusToString(status_) << ", so not checking.";
@@ -464,8 +529,8 @@
 
 bool UpdateAttempter::GetStatus(int64_t* last_checked_time,
                                 double* progress,
-                                std::string* current_operation,
-                                std::string* new_version,
+                                string* current_operation,
+                                string* new_version,
                                 int64_t* new_size) {
   *last_checked_time = last_checked_time_;
   *progress = download_progress_;
@@ -475,6 +540,261 @@
   return true;
 }
 
+// Discovers the dut_flag GPIO identified by |gpio_dutflag_str| and stores the
+// full device name in |dutflag_dev_name_p|.  The function uses an open libudev
+// instance |udev|.  Returns zero on success, -1 otherwise.
+bool UpdateAttempter::GetDutflagGpioDevName(struct udev* udev,
+                                            const string& gpio_dutflag_str,
+                                            string* dutflag_dev_name_p) {
+  CHECK(udev && dutflag_dev_name_p);
+
+  struct udev_enumerate* udev_enum = NULL;
+  int num_gpio_dutflags = 0;
+  const string gpio_dutflag_pattern = "*" + gpio_dutflag_str;
+  int ret;
+
+  // Initialize udev enumerate context and closer.
+  if (!(udev_enum = udev_enumerate_new(udev))) {
+    LOG(ERROR) << "failed to obtain udev enumerate context";
+    return false;
+  }
+  ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
+
+  // Populate filters for find an initialized GPIO chip.
+  if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
+      (ret = udev_enumerate_add_match_sysname(udev_enum,
+                                              gpio_dutflag_pattern.c_str()))) {
+    LOG(ERROR) << "failed to initialize udev enumerate context (" << ret << ")";
+    return false;
+  }
+
+  // Obtain list of matching devices.
+  if ((ret = udev_enumerate_scan_devices(udev_enum))) {
+    LOG(ERROR) << "udev enumerate context scan failed (error code "
+               << ret << ")";
+    return false;
+  }
+
+  // Iterate over matching devices, obtain GPIO dut_flaga identifier.
+  struct udev_list_entry* list_entry;
+  udev_list_entry_foreach(list_entry,
+                          udev_enumerate_get_list_entry(udev_enum)) {
+    // Make sure we're not enumerating more than one device.
+    num_gpio_dutflags++;
+    if (num_gpio_dutflags > 1) {
+      LOG(WARNING) <<
+        "enumerated multiple dutflag GPIOs, ignoring this one";
+      continue;
+    }
+
+    // Obtain device name.
+    const char* dev_name = udev_list_entry_get_name(list_entry);
+    if (!dev_name) {
+      LOG(WARNING) << "enumerated device has a null name string, skipping";
+      continue;
+    }
+
+    // Obtain device object.
+    struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
+    if (!dev) {
+      LOG(WARNING) <<
+        "obtained a null device object for enumerated device, skipping";
+      continue;
+    }
+
+    // Obtain device syspath.
+    const char* dev_syspath = udev_device_get_syspath(dev);
+    if (dev_syspath) {
+      LOG(INFO) << "obtained device syspath: " << dev_syspath;
+      *dutflag_dev_name_p = dev_syspath;
+    } else {
+      LOG(WARNING) << "could not obtain device syspath";
+    }
+
+    udev_device_unref(dev);
+  }
+
+  return true;
+}
+
+// Discovers and stores the device names of the two dut_flag GPIOs. Returns zero
+// upon success, -1 otherwise.
+bool UpdateAttempter::GetDutflagGpioDevNames(string* dutflaga_dev_name_p,
+                                             string* dutflagb_dev_name_p) {
+  if (!(dutflaga_dev_name_p || dutflagb_dev_name_p))
+    return true;  // No output pointers, nothing to do.
+
+  string gpio_dutflaga_str, gpio_dutflagb_str;
+
+  if (dutflaga_dev_name_.empty() || dutflagb_dev_name_.empty()) {
+    struct udev* udev = NULL;
+    struct udev_enumerate* udev_enum = NULL;
+    int num_gpio_chips = 0;
+    const char* id_gpio_dutflaga = NULL;
+    const char* id_gpio_dutflagb = NULL;
+    int ret;
+
+    LOG(INFO) << "begin discovery of dut_flaga/b devices";
+
+    // Obtain libudev instance and closer.
+    if (!(udev = udev_new())) {
+      LOG(ERROR) << "failed to obtain libudev instance";
+      return false;
+    }
+    ScopedUdevCloser udev_closer(&udev);
+
+    // Initialize a udev enumerate object and closer with a bounded lifespan.
+    {
+      if (!(udev_enum = udev_enumerate_new(udev))) {
+        LOG(ERROR) << "failed to obtain udev enumerate context";
+        return false;
+      }
+      ScopedUdevEnumerateCloser udev_enum_closer(&udev_enum);
+
+      // Populate filters for find an initialized GPIO chip.
+      if ((ret = udev_enumerate_add_match_subsystem(udev_enum, "gpio")) ||
+          (ret = udev_enumerate_add_match_sysname(udev_enum, "gpiochip*")) ||
+          (ret = udev_enumerate_add_match_property(udev_enum,
+                                                   kIdGpioDutflaga, "*")) ||
+          (ret = udev_enumerate_add_match_property(udev_enum,
+                                                   kIdGpioDutflagb, "*"))) {
+        LOG(ERROR) << "failed to initialize udev enumerate context ("
+                   << ret << ")";
+        return false;
+      }
+
+      // Obtain list of matching devices.
+      if ((ret = udev_enumerate_scan_devices(udev_enum))) {
+        LOG(ERROR) << "udev enumerate context scan failed (" << ret << ")";
+        return false;
+      }
+
+      // Iterate over matching devices, obtain GPIO dut_flaga identifier.
+      struct udev_list_entry* list_entry;
+      udev_list_entry_foreach(list_entry,
+                              udev_enumerate_get_list_entry(udev_enum)) {
+        // Make sure we're not enumerating more than one device.
+        num_gpio_chips++;
+        if (num_gpio_chips > 1) {
+          LOG(WARNING) << "enumerated multiple GPIO chips, ignoring this one";
+          continue;
+        }
+
+        // Obtain device name.
+        const char* dev_name = udev_list_entry_get_name(list_entry);
+        if (!dev_name) {
+          LOG(WARNING) << "enumerated device has a null name string, skipping";
+          continue;
+        }
+
+        // Obtain device object.
+        struct udev_device* dev = udev_device_new_from_syspath(udev, dev_name);
+        if (!dev) {
+          LOG(WARNING) <<
+            "obtained a null device object for enumerated device, skipping";
+          continue;
+        }
+
+        // Obtain dut_flaga/b identifiers.
+        id_gpio_dutflaga =
+          udev_device_get_property_value(dev, kIdGpioDutflaga);
+        id_gpio_dutflagb =
+          udev_device_get_property_value(dev, kIdGpioDutflagb);
+        if (id_gpio_dutflaga && id_gpio_dutflagb) {
+          LOG(INFO) << "found dut_flaga/b identifiers: a=" << id_gpio_dutflaga
+                    << " b=" << id_gpio_dutflagb;
+
+          gpio_dutflaga_str = id_gpio_dutflaga;
+          gpio_dutflagb_str = id_gpio_dutflagb;
+        } else {
+          LOG(ERROR) << "GPIO chip missing dut_flaga/b properties";
+        }
+
+        udev_device_unref(dev);
+      }
+    }
+
+    // Obtain dut_flaga, reusing the same udev instance.
+    if (dutflaga_dev_name_.empty() && !gpio_dutflaga_str.empty()) {
+      LOG(INFO) << "discovering device for GPIO dut_flaga ";
+      if (!GetDutflagGpioDevName(udev, gpio_dutflaga_str,
+                                 &dutflaga_dev_name_)) {
+        LOG(ERROR) << "discovery of dut_flaga GPIO device failed";
+        return false;
+      }
+    }
+
+    // Now obtain dut_flagb.
+    if (dutflagb_dev_name_.empty() && !gpio_dutflagb_str.empty()) {
+      LOG(INFO) << "discovering device for GPIO dut_flagb";
+      if (!GetDutflagGpioDevName(udev, gpio_dutflagb_str,
+                                 &dutflagb_dev_name_)) {
+        LOG(ERROR) << "discovery of dut_flagb GPIO device failed";
+        return false;
+      }
+    }
+
+    LOG(INFO) << "end discovery of dut_flaga/b devices";
+  }
+
+  // Write cached GPIO dutflag(s) to output strings.
+  if (dutflaga_dev_name_p)
+    *dutflaga_dev_name_p = dutflaga_dev_name_;
+  if (dutflagb_dev_name_p)
+    *dutflagb_dev_name_p = dutflagb_dev_name_;
+
+  return true;
+}
+
+// Reads the value of the dut_flaga GPIO and stores it in |status_p|. Returns
+// true upon success, false otherwise (which also means that the GPIO value was
+// not stored and should not be used).
+bool UpdateAttempter::GetDutflagaGpio(bool* status_p) {
+  // Obtain GPIO device file name.
+  string dutflaga_dev_name;
+  GetDutflagGpioDevNames(&dutflaga_dev_name, NULL);
+  if (dutflaga_dev_name.empty()) {
+    LOG(WARNING) << "could not find dutflaga GPIO device";
+    return false;
+  }
+
+  // Open device for reading.
+  string dutflaga_value_dev_name = dutflaga_dev_name + "/value";
+  int dutflaga_fd;
+  HANDLE_EINTR((dutflaga_fd = open(dutflaga_value_dev_name.c_str(), 0)));
+  if (dutflaga_fd < 0) {
+    PLOG(ERROR) << "opening dutflaga GPIO device file failed";
+    return false;
+  }
+  ScopedFdCloser dutflaga_fd_closer(&dutflaga_fd);
+
+  // Read the dut_flaga GPIO signal. We attempt to read more than---but expect
+  // to receive exactly---two characters: a '0' or '1', and a newline. This is
+  // to ensure that the GPIO device returns a legible result.
+  char buf[3];
+  int ret;
+  HANDLE_EINTR((ret = read(dutflaga_fd, buf, 3)));
+  if (ret != 2) {
+    if (ret < 0)
+      PLOG(ERROR) << "reading dutflaga GPIO status failed";
+    else
+      LOG(ERROR) << "read more than one byte (" << ret << ")";
+    return false;
+  }
+
+  // Identify and write GPIO status.
+  char c = buf[0];
+  if ((c == '0' || c == '1') && buf[1] == '\n') {
+    *status_p = (c == '1');
+  } else {
+    buf[2] = '\0';
+    LOG(ERROR) << "read unexpected value from dutflaga GPIO: " << buf;
+    return false;
+  }
+
+  return true;
+}
+
 void UpdateAttempter::UpdateBootFlags() {
   if (update_boot_flags_running_) {
     LOG(INFO) << "Update boot flags running, nothing to do.";
@@ -508,7 +828,7 @@
 
 void UpdateAttempter::StaticCompleteUpdateBootFlags(
     int return_code,
-    const std::string& output,
+    const string& output,
     void* p) {
   reinterpret_cast<UpdateAttempter*>(p)->CompleteUpdateBootFlags(return_code);
 }