AU: Beginnings of dbus support.

The AU will be a daemon that runs as root. Non-root will communicate
via dbus with the updater to do things such as: query status, request
forced or full updates, etc.

New files for dbus:

UpdateEngine.conf - security configuration
dbus_constants.h - common constants
dbus_service.* - The object exposed over dbus
org.chromium.UpdateEngine.service - the dbus service file
udpate_attempter.* - Refactored this out of main.cc
update_engine_client.cc - Simple command line utility to interact with
Update Engine over dbus. Whereas Update Engine runs as root, this tool
runs as non-root user.

Review URL: http://codereview.chromium.org/1733013
diff --git a/SConstruct b/SConstruct
index 5c11b6d..9598975 100644
--- a/SConstruct
+++ b/SConstruct
@@ -39,6 +39,46 @@
                         single_source = 1,
                         suffix = '.pb.cc')
 
+""" Inputs:
+        target: unused
+        source: list containing the source .xml file
+        env: the scons environment in which we are compiling
+    Outputs:
+        target: the list of targets we'll emit
+        source: the list of sources we'll process"""
+def DbusBindingsEmitter(target, source, env):
+  output = str(source[0])
+  output = output[0:output.rfind('.xml')]
+  target = [
+    output + '.dbusserver.h',
+    output + '.dbusclient.h'
+  ]
+  return target, source
+
+""" Inputs:
+        source: list of sources to process
+        target: list of targets to generate
+        env: scons environment in which we are working
+        for_signature: unused
+    Outputs: a list of commands to execute to generate the targets from
+             the sources."""
+def DbusBindingsGenerator(source, target, env, for_signature):
+  commands = []
+  for target_file in target:
+    if str(target_file).endswith('.dbusserver.h'):
+      mode_flag = '--mode=glib-server '
+    else:
+      mode_flag = '--mode=glib-client '
+    cmd = '/usr/bin/dbus-binding-tool %s --prefix=update_engine_service ' \
+          '%s > %s' % (mode_flag, str(source[0]), str(target_file))
+    commands.append(cmd)
+  return commands
+
+dbus_bindings_builder = Builder(generator = DbusBindingsGenerator,
+                                emitter = DbusBindingsEmitter,
+                                single_source = 1,
+                                suffix = 'dbusclient.h')
+
 env = Environment()
 for key in Split('CC CXX AR RANLIB LD NM'):
   value = os.environ.get(key)
@@ -54,13 +94,17 @@
     env['ENV'][key] = os.environ[key]
 
 
+    # -Wclobbered
+    # -Wempty-body
+    # -Wignored-qualifiers
+    # -Wtype-limits
 env['CCFLAGS'] = ' '.join("""-g
                              -fno-exceptions
                              -fno-strict-aliasing
                              -Wall
-                             -Werror
                              -Wclobbered
                              -Wempty-body
+                             -Werror
                              -Wignored-qualifiers
                              -Wmissing-field-initializers
                              -Wsign-compare
@@ -87,25 +131,27 @@
 env['CPPPATH'] = ['..', '../../third_party/chrome/files', '../../common']
 env['LIBPATH'] = ['../../third_party/chrome']
 env['BUILDERS']['ProtocolBuffer'] = proto_builder
+env['BUILDERS']['DbusBindings'] = dbus_bindings_builder
 
 # Fix issue with scons not passing pkg-config vars through the environment.
 for key in Split('PKG_CONFIG_LIBDIR PKG_CONFIG_PATH'):
   if os.environ.has_key(key):
     env['ENV'][key] = os.environ[key]
 
-env.ParseConfig('pkg-config --cflags --libs glib-2.0')
+env.ParseConfig('pkg-config --cflags --libs glib-2.0 dbus-1 dbus-glib-1')
 env.ProtocolBuffer('update_metadata.pb.cc', 'update_metadata.proto')
 
+env.DbusBindings('update_engine.dbusclient.h', 'update_engine.xml')
+
 if ARGUMENTS.get('debug', 0):
   env['CCFLAGS'] += ' -fprofile-arcs -ftest-coverage'
   env['LIBS'] += ['bz2', 'gcov']
 
-
-
 sources = Split("""action_processor.cc
                    bzip.cc
                    bzip_extent_writer.cc
                    cycle_breaker.cc
+                   dbus_service.cc
                    decompressing_file_writer.cc
                    delta_diff_generator.cc
                    delta_performer.cc
@@ -126,6 +172,7 @@
                    subprocess.cc
                    tarjan.cc
                    topological_sort.cc
+                   update_attempter.cc
                    update_check_action.cc
 		               update_metadata.pb.cc
 		               utils.cc""")
@@ -161,12 +208,16 @@
                             zip_unittest.cc""")
 unittest_main = ['testrunner.cc']
 
+client_main = ['update_engine_client.cc']
+
 delta_generator_main = ['generate_delta_main.cc']
 
 env.Program('update_engine', sources + main)
 unittest_cmd = env.Program('update_engine_unittests',
                            sources + unittest_sources + unittest_main)
 
+client_cmd = env.Program('update_engine_client', sources + client_main);
+
 Clean(unittest_cmd, Glob('*.gcda') + Glob('*.gcno') + Glob('*.gcov') +
                     Split('html app.info'))
 
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
new file mode 100644
index 0000000..88d6dc4
--- /dev/null
+++ b/UpdateEngine.conf
@@ -0,0 +1,23 @@
+<!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="root">
+    <allow own="org.chromium.UpdateEngine" />
+    <allow send_destination="org.chromium.UpdateEngine" />
+  </policy>
+  <policy user="chronos">
+    <!-- introspection is denied -->
+    <deny send_destination="org.chromium.UpdateEngine"
+          send_interface="org.freedesktop.DBus.Introspectable" />
+    <!-- properties denied -->
+    <deny send_destination="org.chromium.UpdateEngine"
+          send_interface="org.freedesktop.DBus.Properties" />
+    <!-- allow explicit methods -->
+    <allow send_destination="org.chromium.UpdateEngine"
+           send_interface="org.chromium.UpdateEngineInterface"
+           send_member="GetStatus"/>
+  </policy>
+  <policy context="default">
+    <deny send_destination="org.chromium.UpdateEngine" />
+  </policy>
+</busconfig>
diff --git a/dbus_constants.h b/dbus_constants.h
new file mode 100644
index 0000000..bd5b551
--- /dev/null
+++ b/dbus_constants.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2010 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 CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_CONSTANTS_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_CONSTANTS_H__
+
+namespace chromeos_update_engine {
+
+static const char* const kUpdateEngineServiceName = "org.chromium.UpdateEngine";
+static const char* const kUpdateEngineServicePath =
+    "/org/chromium/UpdateEngine";
+static const char* const kUpdateEngineServiceInterface =
+    "org.chromium.UpdateEngineInterface";
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_CONSTANTS_H__
diff --git a/dbus_service.cc b/dbus_service.cc
new file mode 100644
index 0000000..8dc239f
--- /dev/null
+++ b/dbus_service.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 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 "update_engine/dbus_service.h"
+#include <string>
+#include "base/logging.h"
+
+using std::string;
+
+G_DEFINE_TYPE(UpdateEngineService, update_engine_service, G_TYPE_OBJECT)
+
+static void update_engine_service_finalize(GObject* object) {
+  G_OBJECT_CLASS(update_engine_service_parent_class)->finalize(object);
+}
+
+static void update_engine_service_class_init(UpdateEngineServiceClass* klass) {
+  GObjectClass *object_class;
+  object_class = G_OBJECT_CLASS(klass);
+  object_class->finalize = update_engine_service_finalize;
+}
+
+static void update_engine_service_init(UpdateEngineService* object) {
+}
+
+UpdateEngineService* update_engine_service_new(void) {
+  return reinterpret_cast<UpdateEngineService*>(
+      g_object_new(UPDATE_ENGINE_TYPE_SERVICE, NULL));
+}
+
+gboolean update_engine_service_get_status(UpdateEngineService* self,
+                                          int64_t* last_checked_time,
+                                          double* progress,
+                                          gchar** current_operation,
+                                          gchar** new_version,
+                                          int64_t* new_size,
+                                          GError **error) {
+  string current_op;
+  string new_version_str;
+  
+  CHECK(self->update_attempter_->GetStatus(last_checked_time,
+                                           progress,
+                                           &current_op,
+                                           &new_version_str,
+                                           new_size));
+  
+  *current_operation = strdup(current_op.c_str());
+  *new_version = strdup(new_version_str.c_str());
+  return TRUE;
+}
+
diff --git a/dbus_service.h b/dbus_service.h
new file mode 100644
index 0000000..1b2604e
--- /dev/null
+++ b/dbus_service.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 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 CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_SERVICE_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_SERVICE_H__
+
+#include <inttypes.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib-object.h>
+
+#include "update_engine/update_attempter.h"
+
+// Type macros:
+#define UPDATE_ENGINE_TYPE_SERVICE (update_engine_service_get_type())
+#define UPDATE_ENGINE_SERVICE(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), UPDATE_ENGINE_TYPE_SERVICE, \
+                              UpdateEngineService))
+#define UPDATE_ENGINE_IS_SERVICE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), UPDATE_ENGINE_TYPE_SERVICE))
+#define UPDATE_ENGINE_SERVICE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), UPDATE_ENGINE_TYPE_SERVICE, \
+                           UpdateEngineService))
+#define UPDATE_ENGINE_IS_SERVICE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), UPDATE_ENGINE_TYPE_SERVICE))
+#define UPDATE_ENGINE_SERVICE_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS((obj), UPDATE_ENGINE_TYPE_SERVICE, \
+                             UpdateEngineService))
+
+G_BEGIN_DECLS
+
+struct UpdateEngineService {
+  GObject parent_instance;
+
+  chromeos_update_engine::UpdateAttempter* update_attempter_;
+};
+
+struct UpdateEngineServiceClass {
+  GObjectClass parent_class;
+};
+
+UpdateEngineService* update_engine_service_new(void);
+GType update_engine_service_get_type(void);
+
+// Methods
+
+gboolean update_engine_service_get_status(UpdateEngineService* self,
+  int64_t* last_checked_time,
+  double* progress,
+  gchar** current_operation,
+  gchar** new_version,
+  int64_t* new_size,
+  GError **error);
+
+G_END_DECLS
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_SERVICE_H__
diff --git a/main.cc b/main.cc
index cbd6e44..a796a98 100644
--- a/main.cc
+++ b/main.cc
@@ -1,26 +1,24 @@
-// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
-// TODO(adlr): get rid of commented out lines or comment them back in.
-// Look for "// re-add" next to those comments.
-
 #include <string>
 #include <tr1/memory>
 #include <vector>
 #include <gflags/gflags.h>
 #include <glib.h>
+#include "base/command_line.h"
 #include "chromeos/obsolete_logging.h"
-#include "update_engine/action_processor.h"
-#include "update_engine/download_action.h"
-#include "update_engine/filesystem_copier_action.h"
-// #include "update_engine/install_action.h"  // re-add
-#include "update_engine/libcurl_http_fetcher.h"
-#include "update_engine/omaha_request_prep_action.h"
-#include "update_engine/omaha_response_handler_action.h"
-#include "update_engine/postinstall_runner_action.h"
-#include "update_engine/set_bootable_flag_action.h"
-#include "update_engine/update_check_action.h"
+#include "update_engine/dbus_constants.h"
+#include "update_engine/dbus_service.h"
+#include "update_engine/update_attempter.h"
+
+extern "C" {
+#include "update_engine/update_engine.dbusserver.h"
+}
+
+DEFINE_bool(logtostderr, false,
+            "Write logs to stderr instead of to a file in log_dir.");
 
 using std::string;
 using std::tr1::shared_ptr;
@@ -28,121 +26,64 @@
 
 namespace chromeos_update_engine {
 
-class UpdateAttempter : public ActionProcessorDelegate {
- public:
-  UpdateAttempter(GMainLoop *loop)
-      : full_update_(false),
-        loop_(loop) {}
-  void Update(bool force_full_update);
-  
-  // Delegate method:
-  void ProcessingDone(const ActionProcessor* processor, bool success);
- private:
-  bool full_update_;
-  vector<shared_ptr<AbstractAction> > actions_;
-  ActionProcessor processor_;
-  GMainLoop *loop_;
+gboolean SetupInMainLoop(void* arg) {
+  // TODO(adlr): Tell update_attempter to start working.
+  // Comment this in for that:
+  //UpdateAttempter* update_attempter = reinterpret_cast<UpdateAttempter*>(arg);
 
-  // pointer to the OmahaResponseHandlerAction in the actions_ vector;
-  shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
-  DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
-};
-
-// Returns true on success. If there was no update available, that's still
-// success.
-// If force_full is true, try to force a full update.
-void UpdateAttempter::Update(bool force_full_update) {
-  full_update_ = force_full_update;
-  CHECK(!processor_.IsRunning());
-  processor_.set_delegate(this);
-
-  // Actions:
-  shared_ptr<OmahaRequestPrepAction> request_prep_action(
-      new OmahaRequestPrepAction(force_full_update));
-  shared_ptr<UpdateCheckAction> update_check_action(
-      new UpdateCheckAction(new LibcurlHttpFetcher));
-  shared_ptr<OmahaResponseHandlerAction> response_handler_action(
-      new OmahaResponseHandlerAction);
-  shared_ptr<FilesystemCopierAction> filesystem_copier_action(
-      new FilesystemCopierAction);
-  shared_ptr<DownloadAction> download_action(
-      new DownloadAction(new LibcurlHttpFetcher));
-  // shared_ptr<InstallAction> install_action(  // re-add
-  //     new InstallAction);
-  shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
-      new PostinstallRunnerAction);
-  shared_ptr<SetBootableFlagAction> set_bootable_flag_action(
-      new SetBootableFlagAction);
-      
-  response_handler_action_ = response_handler_action;
-
-  actions_.push_back(shared_ptr<AbstractAction>(request_prep_action));
-  actions_.push_back(shared_ptr<AbstractAction>(update_check_action));
-  actions_.push_back(shared_ptr<AbstractAction>(response_handler_action));
-  actions_.push_back(shared_ptr<AbstractAction>(filesystem_copier_action));
-  actions_.push_back(shared_ptr<AbstractAction>(download_action));
-  // actions_.push_back(shared_ptr<AbstractAction>(install_action));  // re-add
-  actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
-  actions_.push_back(shared_ptr<AbstractAction>(set_bootable_flag_action));
-  
-  // Enqueue the actions
-  for (vector<shared_ptr<AbstractAction> >::iterator it = actions_.begin();
-       it != actions_.end(); ++it) {
-    processor_.EnqueueAction(it->get());
-  }
-
-  // Bond them together. We have to use the leaf-types when calling
-  // BondActions().
-  BondActions(request_prep_action.get(), update_check_action.get());
-  BondActions(update_check_action.get(), response_handler_action.get());
-  BondActions(response_handler_action.get(), filesystem_copier_action.get());
-  BondActions(filesystem_copier_action.get(), download_action.get());
-  // BondActions(download_action.get(), install_action.get());  // re-add
-  // BondActions(install_action.get(), postinstall_runner_action.get());
-  BondActions(postinstall_runner_action.get(), set_bootable_flag_action.get());
-
-  processor_.StartProcessing();
-}
-
-void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
-                                     bool success) {
-  CHECK(response_handler_action_);
-  if (response_handler_action_->GotNoUpdateResponse()) {
-    // All done.
-    g_main_loop_quit(loop_);
-    return;
-  }
-  if (!success) {
-    if (!full_update_) {
-      LOG(ERROR) << "Update failed. Attempting full update";
-      actions_.clear();
-      response_handler_action_.reset();
-      Update(true);
-      return;
-    } else {
-      LOG(ERROR) << "Full update failed. Aborting";
-    }
-  }
-  g_main_loop_quit(loop_);
-}
-
-gboolean UpdateInMainLoop(void* arg) {
-  UpdateAttempter* update_attempter = reinterpret_cast<UpdateAttempter*>(arg);
-  update_attempter->Update(false);
   return FALSE;  // Don't call this callback function again
 }
 
+void SetupDbusService(UpdateEngineService* service) {
+  DBusGConnection *bus;
+  DBusGProxy *proxy;
+  GError *error = NULL;
+
+  bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+  if (!bus) {
+    LOG(FATAL) << "Failed to get bus";
+  }
+  proxy = dbus_g_proxy_new_for_name(bus,
+                                    DBUS_SERVICE_DBUS,
+                                    DBUS_PATH_DBUS,
+                                    DBUS_INTERFACE_DBUS);
+
+  guint32 request_name_ret;
+  if (!org_freedesktop_DBus_request_name(proxy,
+                                         kUpdateEngineServiceName,
+                                         0,
+                                         &request_name_ret,
+                                         &error)) {
+    LOG(FATAL) << "Failed to get name: " << error->message;
+  }
+  if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+    g_warning("Got result code %u from requesting name", request_name_ret);
+    g_error_free(error);
+    exit(1);
+    LOG(FATAL) << "Got result code " << request_name_ret
+               << " from requesting name, but expected "
+               << DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+  }
+  dbus_g_connection_register_g_object(bus,
+                                      "/org/chromium/UpdateEngine",
+                                      G_OBJECT(service));
+}
+
 }  // namespace chromeos_update_engine
 
 #include "update_engine/subprocess.h"
 
 int main(int argc, char** argv) {
+  ::g_type_init();
   g_thread_init(NULL);
+  dbus_g_thread_init();
   chromeos_update_engine::Subprocess::Init();
   google::ParseCommandLineFlags(&argc, &argv, true);
-  // TODO(adlr): figure out log file
-  logging::InitLogging("",
-                       logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+  CommandLine::Init(argc, argv);
+  logging::InitLogging("logfile.txt",
+                       FLAGS_logtostderr ?
+                         logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG :
+                         logging::LOG_ONLY_TO_FILE,
                        logging::DONT_LOCK_LOG_FILE,
                        logging::APPEND_TO_OLD_LOG_FILE);
   LOG(INFO) << "Chrome OS Update Engine starting";
@@ -150,13 +91,26 @@
   // Create the single GMainLoop
   GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
 
+  // Create the update attempter:
   chromeos_update_engine::UpdateAttempter update_attempter(loop);
 
-  g_timeout_add(0, &chromeos_update_engine::UpdateInMainLoop,
-                &update_attempter);
+  // Create the dbus service object:
+  dbus_g_object_type_install_info(UPDATE_ENGINE_TYPE_SERVICE,
+                                  &dbus_glib_update_engine_service_object_info);
+  UpdateEngineService* service =
+      UPDATE_ENGINE_SERVICE(g_object_new(UPDATE_ENGINE_TYPE_SERVICE, NULL));
+  service->update_attempter_ = &update_attempter;
+  chromeos_update_engine::SetupDbusService(service);
 
+  // Set up init routine to run within the main loop.
+  g_timeout_add(0, &chromeos_update_engine::SetupInMainLoop, &update_attempter);
+
+  // Run the main loop until exit time:
   g_main_loop_run(loop);
+
+  // Cleanup:
   g_main_loop_unref(loop);
+  g_object_unref(G_OBJECT(service));
 
   LOG(INFO) << "Chrome OS Update Engine terminating";
   return 0;
diff --git a/org.chromium.UpdateEngine.service b/org.chromium.UpdateEngine.service
new file mode 100644
index 0000000..e8b5fdc
--- /dev/null
+++ b/org.chromium.UpdateEngine.service
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.chromium.UpdateEngine
+Exec=/usr/sbin/update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
new file mode 100644
index 0000000..6b8c361
--- /dev/null
+++ b/update_attempter.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2010 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 "update_engine/update_attempter.h"
+#include <tr1/memory>
+#include <string>
+#include <vector>
+#include <glib.h>
+#include "update_engine/download_action.h"
+#include "update_engine/filesystem_copier_action.h"
+#include "update_engine/libcurl_http_fetcher.h"
+#include "update_engine/omaha_request_prep_action.h"
+#include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/postinstall_runner_action.h"
+#include "update_engine/set_bootable_flag_action.h"
+#include "update_engine/update_check_action.h"
+
+using std::tr1::shared_ptr;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+void UpdateAttempter::Update(bool force_full_update) {
+  full_update_ = force_full_update;
+  CHECK(!processor_.IsRunning());
+  processor_.set_delegate(this);
+
+  // Actions:
+  shared_ptr<OmahaRequestPrepAction> request_prep_action(
+      new OmahaRequestPrepAction(force_full_update));
+  shared_ptr<UpdateCheckAction> update_check_action(
+      new UpdateCheckAction(new LibcurlHttpFetcher));
+  shared_ptr<OmahaResponseHandlerAction> response_handler_action(
+      new OmahaResponseHandlerAction);
+  shared_ptr<FilesystemCopierAction> filesystem_copier_action(
+      new FilesystemCopierAction);
+  shared_ptr<DownloadAction> download_action(
+      new DownloadAction(new LibcurlHttpFetcher));
+  shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
+      new PostinstallRunnerAction);
+  shared_ptr<SetBootableFlagAction> set_bootable_flag_action(
+      new SetBootableFlagAction);
+      
+  response_handler_action_ = response_handler_action;
+
+  actions_.push_back(shared_ptr<AbstractAction>(request_prep_action));
+  actions_.push_back(shared_ptr<AbstractAction>(update_check_action));
+  actions_.push_back(shared_ptr<AbstractAction>(response_handler_action));
+  actions_.push_back(shared_ptr<AbstractAction>(filesystem_copier_action));
+  actions_.push_back(shared_ptr<AbstractAction>(download_action));
+  actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
+  actions_.push_back(shared_ptr<AbstractAction>(set_bootable_flag_action));
+  
+  // Enqueue the actions
+  for (vector<shared_ptr<AbstractAction> >::iterator it = actions_.begin();
+       it != actions_.end(); ++it) {
+    processor_.EnqueueAction(it->get());
+  }
+
+  // Bond them together. We have to use the leaf-types when calling
+  // BondActions().
+  BondActions(request_prep_action.get(), update_check_action.get());
+  BondActions(update_check_action.get(), response_handler_action.get());
+  BondActions(response_handler_action.get(), filesystem_copier_action.get());
+  BondActions(filesystem_copier_action.get(), download_action.get());
+  // TODO(adlr): Bond these actions together properly
+  // BondActions(download_action.get(), install_action.get());
+  // BondActions(install_action.get(), postinstall_runner_action.get());
+  BondActions(postinstall_runner_action.get(), set_bootable_flag_action.get());
+
+  processor_.StartProcessing();
+}
+
+// Delegate method:
+void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
+                                     bool success) {
+  CHECK(response_handler_action_);
+  if (response_handler_action_->GotNoUpdateResponse()) {
+    // All done.
+    g_main_loop_quit(loop_);
+    return;
+  }
+  if (!success) {
+    if (!full_update_) {
+      LOG(ERROR) << "Update failed. Attempting full update";
+      actions_.clear();
+      response_handler_action_.reset();
+      Update(true);
+      return;
+    } else {
+      LOG(ERROR) << "Full update failed. Aborting";
+    }
+  }
+  g_main_loop_quit(loop_);
+}
+
+// Stop updating. An attempt will be made to record status to the disk
+// so that updates can be resumed later.
+void UpdateAttempter::Terminate() {
+  // TODO(adlr): implement this method.
+  NOTIMPLEMENTED();
+}
+
+// Try to resume from a previously Terminate()d update.
+void UpdateAttempter::ResumeUpdating() {
+  // TODO(adlr): implement this method.
+  NOTIMPLEMENTED();
+}
+
+bool UpdateAttempter::GetStatus(int64_t* last_checked_time,
+                                double* progress,
+                                std::string* current_operation,
+                                std::string* new_version,
+                                int64_t* new_size) {
+  // TODO(adlr): Return actual status.
+  *last_checked_time = 123;
+  *progress = 0.2223;
+  *current_operation = "DOWNLOADING";
+  *new_version = "0.2.3.8";
+  *new_size = 10002;
+  return true;
+}
+
+
+}  // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
new file mode 100644
index 0000000..83e4737
--- /dev/null
+++ b/update_attempter.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2010 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 CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
+
+#include <tr1/memory>
+#include <string>
+#include <vector>
+#include <glib.h>
+#include "update_engine/action_processor.h"
+#include "update_engine/omaha_response_handler_action.h"
+
+namespace chromeos_update_engine {
+
+class UpdateAttempter : public ActionProcessorDelegate {
+ public:
+  explicit UpdateAttempter(GMainLoop *loop)
+      : full_update_(false),
+        loop_(loop) {}
+  void Update(bool force_full_update);
+  
+  // Delegate method:
+  void ProcessingDone(const ActionProcessor* processor, bool success);
+  
+  // Stop updating. An attempt will be made to record status to the disk
+  // so that updates can be resumed later.
+  void Terminate();
+  
+  // Try to resume from a previously Terminate()d update.
+  void ResumeUpdating();
+  
+  // Returns the current status in the out params. Returns true on success.
+  bool GetStatus(int64_t* last_checked_time,
+                 double* progress,
+                 std::string* current_operation,
+                 std::string* new_version,
+                 int64_t* new_size);
+
+ private:
+  bool full_update_;
+  std::vector<std::tr1::shared_ptr<AbstractAction> > actions_;
+  ActionProcessor processor_;
+  GMainLoop *loop_;
+
+  // pointer to the OmahaResponseHandlerAction in the actions_ vector;
+  std::tr1::shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
+  DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
diff --git a/update_engine.xml b/update_engine.xml
new file mode 100644
index 0000000..11c804f
--- /dev/null
+++ b/update_engine.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- COPYRIGHT HERE
+     dbus-binding-tool -mode=glib-server -prefix=update_engine update_engine.xml
+                        &gt; glib_server.h
+-->
+<node name="/org/chromium">
+  <interface name="org.chromium.UpdateEngineInterface">
+    <annotation name="org.freedesktop.DBus.GLib.CSymbol"
+                value="update_engine_service"/>
+    <method name="GetStatus">
+      <arg type="x" name="last_checked_time" direction="out" />
+      <arg type="d" name="progress" direction="out" />
+      <arg type="s" name="current_operation" direction="out" />
+      <arg type="s" name="new_version" direction="out" />
+      <arg type="x" name="new_size" direction="out" />
+    </method>
+  </interface>
+</node>
diff --git a/update_engine_client.cc b/update_engine_client.cc
new file mode 100644
index 0000000..1e273ed
--- /dev/null
+++ b/update_engine_client.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2010 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 <gflags/gflags.h>
+#include <glib.h>
+
+#include "update_engine/dbus_constants.h"
+#include "update_engine/subprocess.h"
+#include "update_engine/utils.h"
+
+extern "C" {
+#include "update_engine/update_engine.dbusclient.h"
+}
+
+using chromeos_update_engine::kUpdateEngineServiceName;
+using chromeos_update_engine::kUpdateEngineServicePath;
+using chromeos_update_engine::kUpdateEngineServiceInterface;
+
+DEFINE_bool(status, false, "Print the status to stdout.");
+DEFINE_bool(force_update, false,
+            "Force an update, even over an expensive network.");
+DEFINE_bool(check_for_update, false,
+            "Initiate check for updates.");
+
+namespace {
+
+const char* GetErrorMessage(const GError* error) {
+  if (!error)
+    return "Unknown error.";
+  return error->message;
+}
+
+bool GetStatus() {
+  DBusGConnection *bus;
+  DBusGProxy *proxy;
+  GError *error = NULL;
+
+  bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+  if (!bus) {
+    LOG(FATAL) << "Failed to get bus";
+  }
+  proxy = dbus_g_proxy_new_for_name_owner(bus,
+                                          kUpdateEngineServiceName,
+                                          kUpdateEngineServicePath,
+                                          kUpdateEngineServiceInterface,
+                                          &error);
+  if (!proxy) {
+    LOG(FATAL) << "Error getting proxy: " << GetErrorMessage(error);
+  }
+
+  gint64 last_checked_time = 0;
+  gdouble progress = 0.0;
+  char* current_op = NULL;
+  char* new_version = NULL;
+  gint64 new_size = 0;
+
+  gboolean rc = org_chromium_UpdateEngineInterface_get_status(
+      proxy,
+      &last_checked_time,
+      &progress,
+      &current_op,
+      &new_version,
+      &new_size,
+      &error);
+  if (rc == FALSE) {
+    LOG(INFO) << "Error getting status: " << GetErrorMessage(error);
+  }
+  printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
+         "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
+         last_checked_time,
+         progress,
+         current_op,
+         new_version,
+         new_size);
+  return true;
+}
+
+bool CheckForUpdates(bool force) {
+  return true;
+}
+
+}  // namespace {}
+
+int main(int argc, char** argv) {
+  // Boilerplate init commands.
+  g_type_init();
+  g_thread_init(NULL);
+  dbus_g_thread_init();
+  chromeos_update_engine::Subprocess::Init();
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  
+  if (FLAGS_status) {
+    LOG(INFO) << "Querying Update Engine status...";
+    if (!GetStatus()) {
+      LOG(FATAL) << "GetStatus() failed.";
+    }
+    return 0;
+  }
+  if (FLAGS_force_update || FLAGS_check_for_update) {
+    LOG(INFO) << "Initiating update check and install.";
+    if (FLAGS_force_update) {
+      LOG(INFO) << "Will not abort due to being on expensive network.";
+    }
+    CHECK(CheckForUpdates(FLAGS_force_update))
+        << "Update check/initiate update failed.";
+    return 0;
+  }
+  
+  LOG(INFO) << "No flags specified. Exiting.";
+  return 0;
+}