Don't crash on EREMOTEIO in FileReader

Change-Id: I050e57dc08a9ba4b4606101c30f8c0132b4ed106
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/util/file.cc b/aos/util/file.cc
index 52657a9..973ab3c 100644
--- a/aos/util/file.cc
+++ b/aos/util/file.cc
@@ -200,11 +200,19 @@
   PCHECK(file_.get() != -1) << ": opening " << filename;
 }
 
-absl::Span<char> FileReader::ReadContents(absl::Span<char> buffer) {
+std::optional<absl::Span<char>> FileReader::ReadContents(
+    absl::Span<char> buffer) {
   PCHECK(0 == lseek(file_.get(), 0, SEEK_SET));
   const ssize_t result = read(file_.get(), buffer.data(), buffer.size());
+  if (result < 0) {
+    // Read timeout for an i2c request returns this.
+    if (errno == EREMOTEIO) {
+      return std::nullopt;
+    }
+  }
+
   PCHECK(result >= 0);
-  return {buffer.data(), static_cast<size_t>(result)};
+  return absl::Span<char>{buffer.data(), static_cast<size_t>(result)};
 }
 
 FileWriter::FileWriter(std::string_view filename, mode_t permissions)
@@ -216,21 +224,25 @@
 // absl::SimpleAtoi doesn't interpret a leading 0x as hex, which we need here.
 // Instead, we use the flatbufers API, which unfortunately relies on NUL
 // termination.
-int32_t FileReader::ReadInt32() {
+std::optional<int32_t> FileReader::ReadInt32() {
   // Maximum characters for a 32-bit integer, +1 for the NUL.
   // Hex is the same size with the leading 0x.
   std::array<char, 11> buffer;
   int32_t result;
-  const auto string_span =
+  const std::optional<absl::Span<char>> string_span =
       ReadContents(absl::Span<char>(buffer.data(), buffer.size())
                        .subspan(0, buffer.size() - 1));
+  if (!string_span.has_value()) {
+    return std::nullopt;
+  }
+
   // Verify we found the newline.
-  CHECK_EQ(buffer[string_span.size() - 1], '\n');
+  CHECK_EQ(buffer[string_span->size() - 1], '\n');
   // Truncate the newline.
-  buffer[string_span.size() - 1] = '\0';
+  buffer[string_span->size() - 1] = '\0';
   CHECK(flatbuffers::StringToNumber(buffer.data(), &result))
       << ": Error parsing string to integer: "
-      << std::string_view(string_span.data(), string_span.size());
+      << std::string_view(string_span->data(), string_span->size());
 
   return result;
 }
diff --git a/aos/util/file.h b/aos/util/file.h
index da5b29f..e825e4c 100644
--- a/aos/util/file.h
+++ b/aos/util/file.h
@@ -64,18 +64,22 @@
  public:
   FileReader(std::string_view filename);
   // Reads the entire contents of the file into the internal buffer and returns
-  // a string_view of it.
-  // Note: The result may not be null-terminated.
-  absl::Span<char> ReadContents(absl::Span<char> buffer);
+  // a string_view of it.  Returns nullopt if we failed to read the contents
+  // (currently only on EREMOTEIO). Note: The result may not be null-terminated.
+  std::optional<absl::Span<char>> ReadContents(absl::Span<char> buffer);
   // Returns the value of the file as a string, for a fixed-length file.
   // Returns nullopt if the result is smaller than kSize. Ignores any
   // bytes beyond kSize.
   template <int kSize>
   std::optional<std::array<char, kSize>> ReadString() {
     std::array<char, kSize> result;
-    const absl::Span<char> used_span =
+    const std::optional<absl::Span<char>> used_span =
         ReadContents(absl::Span<char>(result.data(), result.size()));
-    if (used_span.size() == kSize) {
+    if (!used_span.has_value()) {
+      return std::nullopt;
+    }
+
+    if (used_span->size() == kSize) {
       return result;
     } else {
       return std::nullopt;
@@ -83,8 +87,8 @@
   }
   // Returns the value of the file as an integer. Crashes if it doesn't fit in a
   // 32-bit integer. The value may start with 0x for a hex value, otherwise it
-  // must be base 10.
-  int32_t ReadInt32();
+  // must be base 10.  Returns nullopt if we failed to read the file.
+  std::optional<int32_t> ReadInt32();
 
  private:
   aos::ScopedFD file_;
diff --git a/aos/util/file_test.cc b/aos/util/file_test.cc
index ec4bfe4..905a2cd 100644
--- a/aos/util/file_test.cc
+++ b/aos/util/file_test.cc
@@ -91,10 +91,10 @@
   aos::ScopedRealtime realtime;
   {
     std::array<char, 20> contents;
-    absl::Span<char> read_result =
+    std::optional<absl::Span<char>> read_result =
         reader.ReadContents({contents.data(), contents.size()});
     EXPECT_EQ("123456789\n",
-              std::string_view(read_result.data(), read_result.size()));
+              std::string_view(read_result->data(), read_result->size()));
   }
   {
     std::optional<std::array<char, 10>> read_result = reader.ReadString<10>();