initial revision for imageloader

- also add unit tests (run them by FEATURES="test" emerge-${BOARD} ...)

BUG=chromium:616816
TEST=emerge-cyan chromeos-base/imageloader && cros deploy <ip for cyan> chromeos-base/imageloader
Change-Id: Idb3593ac65a83dedb85d78c42104d1b4ead5c7e0
Reviewed-on: https://chromium-review.googlesource.com/348080
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Commit-Queue: Ashish Gaurav <ashishgaurav@chromium.org>
Tested-by: Ashish Gaurav <ashishgaurav@chromium.org>
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8f2fe3a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# src/platform/imageloader
+
+This aims to provide a generic utility to load (mount) and unload (unmount)
+verified disk images through DBus IPC.
+
+# Binaries
+
+* `imageloader`
+* `imageloadclient`
+
+`imageloader` can be run as root and can handle mounting and unmounting of
+disk images. `imageloadclient` is a simple client (intended to be run as
+chronos) that can talk to `imageloader` and ask it to mount and unmount stuff.
+When `imageloader` is not running, DBus can invoke it via the one time
+run option (`imageloader -o`) and get the task done.
diff --git a/imageloadclient-glue.xml b/imageloadclient-glue.xml
new file mode 100644
index 0000000..f78ae28
--- /dev/null
+++ b/imageloadclient-glue.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" ?>
+<!--
+  Copyright 2016 The Chromium Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+-->
+<node name="/org/chromium/ImageLoader">
+  <interface name="org.chromium.ImageLoaderInterface">
+    <method name="RegisterComponent">
+      <arg type="s" name="name" direction="in"/>
+      <arg type="s" name="version" direction="in" />
+      <arg type="s" name="fs_image_abs_path" direction="in"/>
+      <arg type="b" name="success" direction="out" />
+    </method>
+    <method name="GetComponentVersion">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="version" direction="out" />
+    </method>
+    <method name="LoadComponent">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="mount_point" direction="out" />
+    </method>
+    <method name="UnloadComponent">
+      <arg type="s" name="name" direction="in" />
+      <arg type="b" name="success" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/imageloadclient.cc b/imageloadclient.cc
new file mode 100644
index 0000000..42620c1
--- /dev/null
+++ b/imageloadclient.cc
@@ -0,0 +1,124 @@
+// Copyright 2016 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 "imageloadclient.h"
+
+#include <signal.h>
+
+#include <iostream>
+#include <string>
+
+#include <base/command_line.h>
+#include <base/files/file_path.h>
+#include <base/logging.h>
+#include <pthread.h>
+
+#include "imageloader_common.h"
+
+namespace imageloader {
+
+ImageLoadClient::ImageLoadClient(DBus::Connection* conn, const char* path,
+                                 const char* name)
+    : DBus::ObjectProxy(*conn, path, name) {}
+
+void ImageLoadClient::RegisterComponentCallback(const bool& success,
+                                                const ::DBus::Error& err,
+                                                void*) {
+  if (success) {
+    LOG(INFO) << "Success.";
+  } else {
+    LOG(INFO) << "Failure.";
+  }
+}
+
+void ImageLoadClient::GetComponentVersionCallback(const std::string& version,
+                                                  const ::DBus::Error& err,
+                                                  void*) {
+  if (version == kBadResult) {
+    LOG(INFO) << "Failure.";
+  } else {
+    LOG(INFO) << "Version = " << version;
+  }
+}
+
+void ImageLoadClient::LoadComponentCallback(const std::string& mount_point,
+                                            const ::DBus::Error& err, void*) {
+  if (mount_point == kBadResult) {
+    LOG(INFO) << "Could not mount.";
+  } else {
+    LOG(INFO) << "Mounted at " << mount_point << ".";
+  }
+}
+
+void ImageLoadClient::UnloadComponentCallback(const bool& success,
+                                              const ::DBus::Error& err,
+                                              void*) {
+  if (success) {
+    LOG(INFO) << "Success.";
+  } else {
+    LOG(INFO) << "Failure.";
+  }
+}
+
+namespace {
+
+void *TestCalls(void *arg) {
+  ImageLoadClient *client = reinterpret_cast<ImageLoadClient *>(arg);
+  while (1) {
+    std::string inp, name, abs_path, rel_path, version;
+    std::cin >> inp;
+    if (inp == "rc") {  // RegisterComponent
+      std::cin >> name;
+      std::cin >> version;
+      std::cin >> rel_path;
+      char* c_abs_path = realpath(rel_path.c_str(), NULL);
+      if (c_abs_path != NULL) {
+        abs_path = std::string(c_abs_path);
+        client->RegisterComponentAsync(name, version, abs_path, NULL);
+        free(c_abs_path);
+      } else {
+        PLOG(ERROR) << "realpath : " << rel_path;
+      }
+    } else if (inp == "gcv") {  // GetComponentVersion
+      std::cin >> name;
+      client->GetComponentVersionAsync(name, NULL);
+    } else if (inp == "lc") {  // LoadComponent
+      std::cin >> name;
+      client->LoadComponentAsync(name, NULL);
+    } else if (inp == "uc") {  // UnloadComponent
+      std::cin >> name;
+      client->UnloadComponentAsync(name, NULL);
+    }
+  }
+  return NULL;
+}
+
+}  // namespace {}
+
+}  // namespace imageloader
+
+int main(int argc, char** argv) {
+  signal(SIGTERM, imageloader::OnQuit);
+  signal(SIGINT, imageloader::OnQuit);
+
+  base::CommandLine::Init(argc, argv);
+  logging::LoggingSettings settings;
+  logging::InitLogging(settings);
+
+  DBus::_init_threading();
+  DBus::BusDispatcher dispatcher;
+  DBus::default_dispatcher = &dispatcher;
+  DBus::Connection conn = DBus::Connection::SystemBus();
+
+  imageloader::ImageLoadClient client(&conn, imageloader::kImageLoaderPath,
+                                      imageloader::kImageLoaderName);
+
+  pthread_t thread;
+  pthread_create(&thread, NULL, imageloader::TestCalls, &client);
+
+  dispatcher.enter();
+  LOG(INFO) << "Exiting ...";
+
+  return 0;
+}
diff --git a/imageloadclient.h b/imageloadclient.h
new file mode 100644
index 0000000..c948aef
--- /dev/null
+++ b/imageloadclient.h
@@ -0,0 +1,39 @@
+// Copyright 2016 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 IMAGELOADER_IMAGELOADCLIENT_H_
+#define IMAGELOADER_IMAGELOADCLIENT_H_
+
+#include <string>
+
+#include <dbus-c++/dbus.h>
+
+#include "imageloadclient-glue.h"
+
+namespace imageloader {
+
+// This is a simple client to use imageloader's service in non-root mode.
+class ImageLoadClient
+    : public org::chromium::ImageLoaderInterface_proxy,
+      public DBus::ObjectProxy {
+ public:
+  // Initialize the ImageLoadClient instance.
+  ImageLoadClient(DBus::Connection* conn, const char* path, const char* name);
+
+  void RegisterComponentCallback(const bool& success, const ::DBus::Error& err,
+                                 void* data);
+
+  void GetComponentVersionCallback(const std::string& version,
+                                   const ::DBus::Error& err, void* data);
+
+  void LoadComponentCallback(const std::string& mount_point,
+                             const ::DBus::Error& err, void* data);
+
+  void UnloadComponentCallback(const bool& success, const ::DBus::Error& err,
+                               void* data);
+};
+
+}  // namespace imageloader
+
+#endif  // IMAGELOADER_IMAGELOADCLIENT_H_
diff --git a/imageloader-glue.xml b/imageloader-glue.xml
new file mode 100644
index 0000000..f78ae28
--- /dev/null
+++ b/imageloader-glue.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" ?>
+<!--
+  Copyright 2016 The Chromium Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+-->
+<node name="/org/chromium/ImageLoader">
+  <interface name="org.chromium.ImageLoaderInterface">
+    <method name="RegisterComponent">
+      <arg type="s" name="name" direction="in"/>
+      <arg type="s" name="version" direction="in" />
+      <arg type="s" name="fs_image_abs_path" direction="in"/>
+      <arg type="b" name="success" direction="out" />
+    </method>
+    <method name="GetComponentVersion">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="version" direction="out" />
+    </method>
+    <method name="LoadComponent">
+      <arg type="s" name="name" direction="in" />
+      <arg type="s" name="mount_point" direction="out" />
+    </method>
+    <method name="UnloadComponent">
+      <arg type="s" name="name" direction="in" />
+      <arg type="b" name="success" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/imageloader.cc b/imageloader.cc
new file mode 100644
index 0000000..c9a9916
--- /dev/null
+++ b/imageloader.cc
@@ -0,0 +1,216 @@
+// Copyright 2016 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 "imageloader.h"
+
+#include <fcntl.h>
+#include <linux/loop.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include <base/command_line.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_file.h>
+#include <base/guid.h>
+#include <base/logging.h>
+#include <brillo/flag_helper.h>
+#include <dbus-c++/error.h>
+
+#include "imageloader_common.h"
+
+namespace imageloader {
+
+using base::CreateDirectory;
+using base::DeleteFile;
+using base::FilePath;
+using base::GenerateGUID;
+using base::PathExists;
+using base::ScopedFD;
+
+namespace {
+
+using imageloader::kBadResult;
+
+// Generate a good enough (unique) mount point.
+FilePath GenerateMountPoint(const char prefix[]) {
+  return FilePath(prefix + GenerateGUID());
+}
+
+}  // namespace {}
+
+// Mount component at location generated.
+std::string ImageLoader::LoadComponentUtil(const std::string& name) {
+  FilePath mount_point = GenerateMountPoint("/mnt/");
+  // Is this somehow taken up by any other name or mount?
+  for (auto it = mounts.begin(); it != mounts.end(); ++it) {
+    if ((it->second).first == mount_point) {
+      return kBadResult;
+    }
+  }
+  if (PathExists(mount_point)) {
+    LOG(INFO) << "Generated mount_point is already stat-able : "
+              << mount_point.value();
+    return kBadResult;
+  }
+  // The mount point is not yet taken, so go ahead.
+  ScopedFD loopctl_fd(open("/dev/loop-control", O_RDONLY | O_CLOEXEC));
+  if (!loopctl_fd.is_valid()) {
+    PLOG(ERROR) << "loopctl_fd";
+    return kBadResult;
+  }
+  int device_free_number = ioctl(loopctl_fd.get(), LOOP_CTL_GET_FREE);
+  if (device_free_number < 0) {
+    PLOG(ERROR) << "ioctl : LOOP_CTL_GET_FREE";
+    return kBadResult;
+  }
+  std::ostringstream device_path;
+  device_path << "/dev/loop" << device_free_number;
+  ScopedFD device_path_fd(open(device_path.str().c_str(),
+                          O_RDONLY | O_CLOEXEC));
+  if (!device_path_fd.is_valid()) {
+    PLOG(ERROR) << "device_path_fd";
+    return kBadResult;
+  }
+  ScopedFD fs_image_fd(open(reg[name].second.value().c_str(),
+                       O_RDONLY | O_CLOEXEC));
+  if (!fs_image_fd.is_valid()) {
+    PLOG(ERROR) << "fs_image_fd";
+    return kBadResult;
+  }
+  if (ioctl(device_path_fd.get(), LOOP_SET_FD, fs_image_fd.get()) < 0) {
+    PLOG(ERROR) << "ioctl: LOOP_SET_FD";
+    return kBadResult;
+  }
+  if (!CreateDirectory(mount_point)) {
+    PLOG(ERROR) << "CreateDirectory : " << mount_point.value();
+    ioctl(device_path_fd.get(), LOOP_CLR_FD, 0);
+    return kBadResult;
+  }
+  if (mount(device_path.str().c_str(), mount_point.value().c_str(), "squashfs",
+            MS_RDONLY | MS_NOSUID | MS_NODEV, "") < 0) {
+    PLOG(ERROR) << "mount";
+    ioctl(device_path_fd.get(), LOOP_CLR_FD, 0);
+    return kBadResult;
+  }
+  mounts[name] = std::make_pair(mount_point, FilePath(device_path.str()));
+  return mount_point.value();
+}
+
+// Unmount the given component.
+bool ImageLoader::UnloadComponentUtil(const std::string& name) {
+  std::string device_path = mounts[name].second.value();
+  if (umount(mounts[name].first.value().c_str()) < 0) {
+    PLOG(ERROR) << "umount";
+    return false;
+  }
+  const FilePath fp_mount_point(mounts[name].first);
+  if (!DeleteFile(fp_mount_point, false)) {
+    PLOG(ERROR) << "DeleteFile : " << fp_mount_point.value();
+    return false;
+  }
+  ScopedFD device_path_fd(open(device_path.c_str(), O_RDONLY | O_CLOEXEC));
+  if (!device_path_fd.is_valid()) {
+    PLOG(ERROR) << "device_path_fd";
+    return false;
+  }
+  if (ioctl(device_path_fd.get(), LOOP_CLR_FD, 0) < 0) {
+    PLOG(ERROR) << "ioctl: LOOP_CLR_FD";
+    return false;
+  }
+  mounts.erase(mounts.find(name));
+  return true;
+}
+
+// Following functions are required directly for the DBus functionality.
+
+ImageLoader::ImageLoader(DBus::Connection* conn)
+    : DBus::ObjectAdaptor(*conn, kImageLoaderPath) {}
+
+bool ImageLoader::RegisterComponent(const std::string& name,
+                                    const std::string& version,
+                                    const std::string& fs_image_abs_path,
+                                    ::DBus::Error& err) {
+  if (reg.find(name) == reg.end()) {
+    reg[name] = std::make_pair(version, FilePath(fs_image_abs_path));
+    LOG(INFO) << "Registered (" << name << ", " << version << ", "
+              << fs_image_abs_path << ")";
+    return true;
+  }
+  LOG(ERROR) <<
+      "Couldn't register, entry with specified name already exists : "
+      << name;
+  return false;
+}
+
+std::string ImageLoader::GetComponentVersion(const std::string& name,
+                                             ::DBus::Error& err) {
+  if (reg.find(name) != reg.end()) {
+    LOG(INFO) << "Found entry (" << name << ", " << reg[name].first << ", "
+              << reg[name].second.value() << ")";
+    return reg[name].first;
+  }
+  LOG(ERROR) << "Entry not found : " << name;
+  return kBadResult;
+}
+
+std::string ImageLoader::LoadComponent(const std::string& name,
+                                       ::DBus::Error& err) {
+  if (reg.find(name) != reg.end()) {
+    if (mounts.find(name) != mounts.end()) {
+      LOG(ERROR) << "Already mounted at " << mounts[name].first.value() << ".";
+      return kBadResult;
+    }
+    std::string mount_point = LoadComponentUtil(name);
+    if (mount_point == kBadResult) {
+      LOG(ERROR) << "Unable to mount : " << mount_point;
+      return kBadResult;
+    }
+    LOG(INFO) << "Mounted successfully at " << mount_point << ".";
+    return mount_point;
+  }
+  LOG(ERROR) << "Entry not found : " << name;
+  return kBadResult;
+}
+
+bool ImageLoader::UnloadComponent(const std::string& name,
+                                  ::DBus::Error& err) {
+  if (UnloadComponentUtil(name)) {
+    LOG(INFO) << "Unmount " << name << " successful.";
+    return true;
+  }
+  LOG(ERROR) << "Unmount " << name << " unsucessful.";
+  return false;
+}
+
+}  // namespace imageloader
+
+int main(int argc, char **argv) {
+  signal(SIGTERM, imageloader::OnQuit);
+  signal(SIGINT, imageloader::OnQuit);
+
+  DEFINE_bool(o, false, "run once");
+  brillo::FlagHelper::Init(argc, argv, "imageloader");
+
+  logging::LoggingSettings settings;
+  logging::InitLogging(settings);
+
+  DBus::BusDispatcher dispatcher;
+  DBus::default_dispatcher = &dispatcher;
+  DBus::Connection conn = DBus::Connection::SystemBus();
+  conn.request_name(imageloader::kImageLoaderName);
+  imageloader::ImageLoader helper(&conn);
+
+  if (FLAGS_o) {
+    dispatcher.dispatch_pending();
+  } else {
+    dispatcher.enter();
+  }
+  return 0;
+}
diff --git a/imageloader.gyp b/imageloader.gyp
new file mode 100644
index 0000000..f69f018
--- /dev/null
+++ b/imageloader.gyp
@@ -0,0 +1,100 @@
+# Copyright 2016 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.
+
+{
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'libchrome-<(libbase_ver)',
+        'dbus-c++-1',
+      ],
+      # imageloader uses try/catch to interact with dbus-c++
+      'enable_exceptions': 1,
+    },
+  },
+  'targets': [
+    {
+      'target_name': 'libimageloader_common',
+      'type': 'static_library',
+      'sources': [
+        'imageloader_common.cc',
+        'imageloader_common.h',
+      ],
+    },
+    {
+      'target_name': 'imageloader-glue',
+      'type': 'none',
+      'variables': {
+        'xml2cpp_type': 'adaptor',
+        'xml2cpp_in_dir': '.',
+        'xml2cpp_out_dir': 'include',
+      },
+      'sources': [
+        '<(xml2cpp_in_dir)/imageloader-glue.xml',
+      ],
+      'includes': ['../../platform2/common-mk/xml2cpp.gypi'],
+    },
+    {
+      'target_name': 'imageloadclient-glue',
+      'type': 'none',
+      'variables': {
+        'xml2cpp_type': 'proxy',
+        'xml2cpp_in_dir': '.',
+        'xml2cpp_out_dir': 'include',
+      },
+      'sources': [
+        '<(xml2cpp_in_dir)/imageloadclient-glue.xml',
+      ],
+      'includes': ['../../platform2/common-mk/xml2cpp.gypi'],
+    },
+    {
+      'target_name': 'imageloader',
+      'type': 'executable',
+      'variables': {
+        'deps': ['libbrillo-<(libbase_ver)'],
+      },
+      'dependencies': [
+        'imageloader-glue',
+        'libimageloader_common',
+      ],
+      'sources': [
+        'imageloader.cc',
+        'imageloader.h',
+        'imageloader-glue.h',
+      ],
+    },
+    {
+      'target_name': 'imageloadclient',
+      'type': 'executable',
+      'dependencies': [
+        'imageloadclient-glue',
+        'libimageloader_common',
+      ],
+      'sources': [
+        'imageloadclient.cc',
+        'imageloadclient.h',
+        'imageloadclient-glue.h',
+      ],
+      'libraries': ['-lpthread'],
+    },
+  ],
+  'conditions': [
+    ['USE_test == 1', {
+      'targets': [
+        {
+          'target_name': 'run_tests',
+          'type': 'executable',
+          'includes': ['../../platform2/common-mk/common_test.gypi'],
+          'dependencies': [
+            'libimageloader_common',
+          ],
+          'sources': [
+            'run_tests.cc',
+            'imageloader_common_unittest.cc'
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/imageloader.h b/imageloader.h
new file mode 100644
index 0000000..49a7abc
--- /dev/null
+++ b/imageloader.h
@@ -0,0 +1,54 @@
+// Copyright 2016 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 IMAGELOADER_IMAGELOADER_H_
+#define IMAGELOADER_IMAGELOADER_H_
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include <base/files/file_path.h>
+#include <dbus-c++/dbus.h>
+
+#include "imageloader-glue.h"
+
+namespace imageloader {
+
+// This is a utility that handles mounting and unmounting of
+// verified filesystem images that might include binaries intended
+// to be run as read only.
+class ImageLoader
+    : org::chromium::ImageLoaderInterface_adaptor,
+      public DBus::ObjectAdaptor {
+ public:
+  // Instantiate a D-Bus Helper Instance
+  explicit ImageLoader(DBus::Connection* conn);
+
+  // Register a component.
+  bool RegisterComponent(const std::string& name, const std::string& version,
+                         const std::string& fs_image_abs_path,
+                         ::DBus::Error& err);
+
+  // Get component version given component name.
+  std::string GetComponentVersion(const std::string& name, ::DBus::Error& err);
+
+  // Load the specified component.
+  std::string LoadComponent(const std::string& name, ::DBus::Error& err);
+  std::string LoadComponentUtil(const std::string& name);
+
+  // Unload the specified component.
+  bool UnloadComponent(const std::string& name, ::DBus::Error& err);
+  bool UnloadComponentUtil(const std::string& name);
+ private:
+  // "mounts" keeps track of what has been mounted.
+  // mounts = (name, (mount_point, device_path))
+  std::map<std::string, std::pair<base::FilePath, base::FilePath>> mounts;
+  // "reg" keeps track of registered components.
+  // reg = (name, (version, fs_image_abs_path))
+  std::map<std::string, std::pair<std::string, base::FilePath>> reg;
+};
+
+}  // namespace imageloader
+
+#endif  // IMAGELOADER_IMAGELOADER_H_
diff --git a/imageloader_common.cc b/imageloader_common.cc
new file mode 100644
index 0000000..af96772
--- /dev/null
+++ b/imageloader_common.cc
@@ -0,0 +1,16 @@
+// Copyright 2016 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 "imageloader_common.h"
+
+#include <dbus-c++/dbus.h>
+
+namespace imageloader {
+
+void OnQuit(int sig) {
+  if (DBus::default_dispatcher)
+    DBus::default_dispatcher->leave();
+}
+
+}  // namespace imageloader
diff --git a/imageloader_common.h b/imageloader_common.h
new file mode 100644
index 0000000..bd51e58
--- /dev/null
+++ b/imageloader_common.h
@@ -0,0 +1,18 @@
+// Copyright 2016 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 IMAGELOADER_IMAGELOADER_COMMON_H_
+#define IMAGELOADER_IMAGELOADER_COMMON_H_
+
+namespace imageloader {
+
+const char kBadResult[] = "";
+const char kImageLoaderName[] = "org.chromium.ImageLoader";
+const char kImageLoaderPath[] = "/org/chromium/ImageLoader";
+
+void OnQuit(int sig);
+
+}  // namespace imageloader
+
+#endif  // IMAGELOADER_IMAGELOADER_COMMON_H_
diff --git a/org.chromium.ImageLoader.conf b/org.chromium.ImageLoader.conf
new file mode 100644
index 0000000..2d1f65d
--- /dev/null
+++ b/org.chromium.ImageLoader.conf
@@ -0,0 +1,33 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<!--
+  Copyright 2016 The Chromium Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+
+  This file will be installed at /etc/dbus-1/system.d on Chromium OS.
+-->
+<busconfig>
+  <policy user="root">
+    <allow own="org.chromium.ImageLoader" />
+    <allow receive_sender="org.chromium.ImageLoader" />
+    <allow send_destination="org.chromium.ImageLoader" />
+  </policy>
+  <policy user="chronos">
+    <allow receive_sender="org.chromium.ImageLoader" />
+    <allow send_destination="org.chromium.ImageLoader"
+      send_interface="org.chromium.ImageLoaderInterface"
+      send_member="RegisterComponent" />
+    <allow send_destination="org.chromium.ImageLoader"
+      send_interface="org.chromium.ImageLoaderInterface"
+      send_member="GetComponentVersion" />
+    <allow send_destination="org.chromium.ImageLoader"
+      send_interface="org.chromium.ImageLoaderInterface"
+      send_member="LoadComponent" />
+    <allow send_destination="org.chromium.ImageLoader"
+      send_interface="org.chromium.ImageLoaderInterface"
+      send_member="UnloadComponent" />
+  </policy>
+  <!-- deny others maybe? -->
+</busconfig>
diff --git a/org.chromium.ImageLoader.service b/org.chromium.ImageLoader.service
new file mode 100644
index 0000000..4806839
--- /dev/null
+++ b/org.chromium.ImageLoader.service
@@ -0,0 +1,7 @@
+# Copyright (c) 2016 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.i
+[D-BUS Service]
+Name=org.chromium.ImageLoader
+Exec=/usr/sbin/imageloader -o
+User=root
diff --git a/run_tests.cc b/run_tests.cc
new file mode 100644
index 0000000..451d8eb
--- /dev/null
+++ b/run_tests.cc
@@ -0,0 +1,10 @@
+// Copyright 2016 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 <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}