buffet: GCD command manager to load command .json files at startup

buffet daemon now loads the base command definition file from
/etc/buffet/gcd.json at startup. Also, static device command
defintions are load from .json file from /etc/buffet/commands
(if found).

BUG=chromium:374861
TEST=USE=buffet P2_TEST_FILTER="buffet::*" FEATURES=test emerge-link platform2
TEST=Manually deployed test image and copied /etc/buffet/gcd.json to
     /etc/buffet/commands/gcd.json and verified in /var/log/buffet/*
     that the static command definition is loaded at startup.

Change-Id: I1e6b13cabe8cc2ba1a941cc05cb1d0cfeedda480
Reviewed-on: https://chromium-review.googlesource.com/209311
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/commands/command_manager.cc b/buffet/commands/command_manager.cc
index d7cf34c..48a323f 100644
--- a/buffet/commands/command_manager.cc
+++ b/buffet/commands/command_manager.cc
@@ -4,6 +4,9 @@
 
 #include "buffet/commands/command_manager.h"
 
+#include <base/at_exit.h>
+#include <base/bind.h>
+#include <base/files/file_enumerator.h>
 #include <base/file_util.h>
 #include <base/values.h>
 #include <base/json/json_reader.h>
@@ -13,6 +16,8 @@
 
 namespace buffet {
 
+CommandManager* CommandManager::instance_ = nullptr;
+
 const CommandDictionary& CommandManager::GetCommandDictionary() const {
   return dictionary_;
 }
@@ -47,6 +52,49 @@
   return LoadCommands(*json, category, error);
 }
 
+CommandManager* CommandManager::GetInstance() {
+  CHECK(instance_) << "CommandManager instance not initialized.";
+  return instance_;
+}
+
+void CommandManager::Startup() {
+  CHECK(!instance_) << "CommandManager instance already initialized.";
+  LOG(INFO) << "Initializing CommandManager.";
+  std::unique_ptr<CommandManager> inst(new CommandManager);
+
+  // Load global standard GCD command dictionary.
+  base::FilePath base_command_file("/etc/buffet/gcd.json");
+  LOG(INFO) << "Loading standard commands from " << base_command_file.value();
+  CHECK(inst->LoadBaseCommands(base_command_file, nullptr))
+      << "Failed to load the standard command definitions.";
+
+  // Load static device command definitions.
+  base::FilePath device_command_dir("/etc/buffet/commands");
+  base::FileEnumerator enumerator(device_command_dir, false,
+                                  base::FileEnumerator::FILES,
+                                  FILE_PATH_LITERAL("*.json"));
+  base::FilePath json_file_path = enumerator.Next();
+  while (!json_file_path.empty()) {
+    LOG(INFO) << "Loading command schema from " << json_file_path.value();
+    CHECK(inst->LoadCommands(json_file_path, nullptr))
+        << "Failed to load the command definition file.";
+    json_file_path = enumerator.Next();
+  }
+
+  // Register a cleanup callback to be executed at shut-down.
+  base::AtExitManager::RegisterTask(base::Bind(&CommandManager::Shutdown));
+  // Transfer the object instance pointer from the smart pointer to
+  // the global pointer.
+  instance_ = inst.release();
+}
+
+void CommandManager::Shutdown() {
+  CHECK(instance_) << "CommandManager instance not initialized.";
+  LOG(INFO) << "Shutting down CommandManager.";
+  delete instance_;
+  instance_ = nullptr;
+}
+
 std::unique_ptr<const base::DictionaryValue> CommandManager::LoadJsonDict(
     const base::FilePath& json_file_path, ErrorPtr* error) {
   std::string json_string;
diff --git a/buffet/commands/command_manager.h b/buffet/commands/command_manager.h
index 64037b8..02d92fd 100644
--- a/buffet/commands/command_manager.h
+++ b/buffet/commands/command_manager.h
@@ -52,15 +52,28 @@
   bool LoadCommands(const base::FilePath& json_file_path,
                     ErrorPtr* error);
 
+  // Factory static method to get the global singleton instance of the object.
+  static CommandManager* GetInstance();
+  // Global startup method to be called by buffet daemon at startup.
+  // Initializes the global object singleton and loads the standard GCD command
+  // dictionary as well as static vendor-provided command definitions for
+  // the current device.
+  static void Startup();
+
  private:
   // Helper function to load a JSON file that is expected to be
   // an object/dictionary. In case of error, returns empty unique ptr and fills
   // in error details in |error|.
   std::unique_ptr<const base::DictionaryValue> LoadJsonDict(
       const base::FilePath& json_file_path, ErrorPtr* error);
+  // Helper method to be called at buffet shutdown to clean up the global
+  // singleton instance of this class.
+  static void Shutdown();
 
   CommandDictionary base_dictionary_;  // Base/std command definitions/schemas.
   CommandDictionary dictionary_;  // Command definitions/schemas.
+
+  static CommandManager* instance_;  // Global singleton instance of the object.
   DISALLOW_COPY_AND_ASSIGN(CommandManager);
 };
 
diff --git a/buffet/main.cc b/buffet/main.cc
index 43ba4ab..cc2fea7 100644
--- a/buffet/main.cc
+++ b/buffet/main.cc
@@ -16,6 +16,7 @@
 #include <sysexits.h>
 
 #include "buffet/async_event_sequencer.h"
+#include "buffet/commands/command_manager.h"
 #include "buffet/dbus_constants.h"
 #include "buffet/exported_object_manager.h"
 #include "buffet/manager.h"
@@ -101,6 +102,9 @@
   // Release our handle on the sequencer so that it gets deleted after
   // both callbacks return.
   sequencer = nullptr;
+
+  buffet::CommandManager::Startup();
+
   LOG(INFO) << "Entering mainloop.";
   message_loop->Run();
 }