buffet: Add ability to add commands via buffet_client

Implemented "AddCommand" parameter in buffet_client that queues
a GCD command in buffet daemon.

BUG=chromium:412583
TEST=FEATURES=test emerge-link buffet

Change-Id: Ie5a6561efc8675ba5fb234c1151a0f1dbda39e5d
Reviewed-on: https://chromium-review.googlesource.com/217829
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Anton Muhin <antonm@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/buffet_client.cc b/buffet/buffet_client.cc
index 38ea239..14636f8 100644
--- a/buffet/buffet_client.cc
+++ b/buffet/buffet_client.cc
@@ -37,6 +37,9 @@
                     << " param1 = val1&param2 = val2..." << std::endl;
   std::cerr << "  " << kManagerFinishRegisterDevice
                     << " device_id" << std::endl;
+  std::cerr << "  " << kManagerAddCommand
+                    << " '{\"name\":\"command_name\",\"parameters\":{}}'"
+                    << std::endl;
   std::cerr << "  " << kManagerUpdateStateMethod << std::endl;
   std::cerr << "  " << dbus::kObjectManagerGetManagedObjects << std::endl;
 }
@@ -235,6 +238,26 @@
     return EX_OK;
   }
 
+  int CallManagerAddCommand(const CommandLine::StringVector& args) {
+    if (args.size() != 1) {
+      std::cerr << "Invalid number of arguments for "
+                << "Manager." << kManagerAddCommand << std::endl;
+      usage();
+      return EX_USAGE;
+    }
+    dbus::MethodCall method_call(
+        kManagerInterface, kManagerAddCommand);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(args.front());
+    scoped_ptr<dbus::Response> response(
+        manager_proxy_->CallMethodAndBlock(&method_call, default_timeout_ms));
+    if (!response) {
+      std::cout << "Failed to receive a response." << std::endl;
+      return EX_UNAVAILABLE;
+    }
+    return EX_OK;
+  }
+
   int CallRootGetManagedObjects(const CommandLine::StringVector& args) {
     if (!args.empty()) {
       std::cerr << "Invalid number of arguments for "
@@ -299,6 +322,9 @@
   } else if (command.compare(kManagerUpdateStateMethod) == 0 ||
              command.compare("us") == 0) {
     err = helper.CallManagerUpdateState(args);
+  } else if (command.compare(kManagerAddCommand) == 0 ||
+             command.compare("ac") == 0) {
+    err = helper.CallManagerAddCommand(args);
   } else if (command.compare(dbus::kObjectManagerGetManagedObjects) == 0) {
     err = helper.CallRootGetManagedObjects(args);
   } else {
diff --git a/buffet/dbus_constants.cc b/buffet/dbus_constants.cc
index c3921f9..07b3b52 100644
--- a/buffet/dbus_constants.cc
+++ b/buffet/dbus_constants.cc
@@ -20,6 +20,7 @@
 const char kManagerStartRegisterDevice[]    = "StartRegisterDevice";
 const char kManagerFinishRegisterDevice[]   = "FinishRegisterDevice";
 const char kManagerUpdateStateMethod[]      = "UpdateState";
+const char kManagerAddCommand[]             = "AddCommand";
 const char kManagerTestMethod[]             = "TestMethod";
 
 const char kCommandInterface[] = "org.chromium.Buffet.Command";
diff --git a/buffet/dbus_constants.h b/buffet/dbus_constants.h
index 398f848..593866b 100644
--- a/buffet/dbus_constants.h
+++ b/buffet/dbus_constants.h
@@ -25,6 +25,7 @@
 extern const char kManagerStartRegisterDevice[];
 extern const char kManagerFinishRegisterDevice[];
 extern const char kManagerUpdateStateMethod[];
+extern const char kManagerAddCommand[];
 extern const char kManagerTestMethod[];
 
 // Interface implemented by the command instance objects.
diff --git a/buffet/manager.cc b/buffet/manager.cc
index 7c3cbd5..3b0c4ef 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -9,6 +9,7 @@
 
 #include <base/bind.h>
 #include <base/bind_helpers.h>
+#include <base/json/json_reader.h>
 #include <base/json/json_writer.h>
 #include <chromeos/dbus/async_event_sequencer.h>
 #include <chromeos/dbus/exported_object_manager.h>
@@ -17,6 +18,7 @@
 #include <dbus/object_path.h>
 #include <dbus/values_util.h>
 
+#include "buffet/commands/command_instance.h"
 #include "buffet/commands/command_manager.h"
 #include "buffet/dbus_constants.h"
 
@@ -48,6 +50,9 @@
   itf->AddMethodHandler(dbus_constants::kManagerUpdateStateMethod,
                         base::Unretained(this),
                         &Manager::HandleUpdateState);
+  itf->AddMethodHandler(dbus_constants::kManagerAddCommand,
+                        base::Unretained(this),
+                        &Manager::HandleAddCommand);
   itf->AddMethodHandler(dbus_constants::kManagerTestMethod,
                         base::Unretained(this),
                         &Manager::HandleTestMethod);
@@ -118,6 +123,22 @@
   state_.SetValue(json_state_fragment);
 }
 
+void Manager::HandleAddCommand(
+    chromeos::ErrorPtr* error, const std::string& json_command) {
+  std::string error_message;
+  std::unique_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
+      json_command, base::JSON_PARSE_RFC, nullptr, &error_message));
+  if (!value) {
+    chromeos::Error::AddTo(error, chromeos::errors::json::kDomain,
+                           chromeos::errors::json::kParseError, error_message);
+    return;
+  }
+  auto command_instance = buffet::CommandInstance::FromJson(
+      value.get(), command_manager_->GetCommandDictionary(), error);
+  if (command_instance)
+    command_manager_->AddCommand(std::move(command_instance));
+}
+
 std::string Manager::HandleTestMethod(chromeos::ErrorPtr* error,
                                       const std::string& message) {
   LOG(INFO) << "Received call to test method: " << message;
diff --git a/buffet/manager.h b/buffet/manager.h
index 35a5534..be399d5 100644
--- a/buffet/manager.h
+++ b/buffet/manager.h
@@ -58,6 +58,9 @@
   // Handles calls to org.chromium.Buffet.Manager.UpdateState().
   void HandleUpdateState(chromeos::ErrorPtr* error,
                          const std::string& json_state_fragment);
+  // Handles calls to org.chromium.Buffet.Manager.AddCommand().
+  void HandleAddCommand(chromeos::ErrorPtr* error,
+                        const std::string& json_command);
   // Handles calls to org.chromium.Buffet.Manager.Test()
   std::string HandleTestMethod(chromeos::ErrorPtr* error,
                                const std::string& message);