|  | // Copyright 2014 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. | 
|  |  | 
|  | // This is a sample daemon that "handles" Buffet commands. | 
|  | // It just prints the information about the command received to stdout and | 
|  | // marks the command as processed. | 
|  |  | 
|  | #include <string> | 
|  | #include <sysexits.h> | 
|  |  | 
|  | #include <base/bind.h> | 
|  | #include <base/command_line.h> | 
|  | #include <base/format_macros.h> | 
|  | #include <base/json/json_writer.h> | 
|  | #include <base/values.h> | 
|  | #include <chromeos/daemons/dbus_daemon.h> | 
|  | #include <chromeos/map_utils.h> | 
|  | #include <chromeos/strings/string_utils.h> | 
|  | #include <chromeos/syslog_logging.h> | 
|  |  | 
|  | #include "buffet/dbus-proxies.h" | 
|  |  | 
|  | namespace { | 
|  | const char kTestCommandCategory[] = "test"; | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> DictionaryToJson( | 
|  | const chromeos::VariantDictionary& dictionary); | 
|  |  | 
|  | std::unique_ptr<base::Value> AnyToJson(const chromeos::Any& value) { | 
|  | if (value.IsTypeCompatible<chromeos::VariantDictionary>()) | 
|  | return DictionaryToJson(value.Get<chromeos::VariantDictionary>()); | 
|  |  | 
|  | if (value.IsTypeCompatible<std::string>()) { | 
|  | return std::unique_ptr<base::Value>{ | 
|  | new base::StringValue(value.Get<std::string>())}; | 
|  | } | 
|  |  | 
|  | if (value.IsTypeCompatible<double>()) { | 
|  | return std::unique_ptr<base::Value>{ | 
|  | new base::FundamentalValue(value.Get<double>())}; | 
|  | } | 
|  |  | 
|  | if (value.IsTypeCompatible<bool>()) { | 
|  | return std::unique_ptr<base::Value>{ | 
|  | new base::FundamentalValue(value.Get<bool>())}; | 
|  | } | 
|  |  | 
|  | if (value.IsTypeCompatible<int>()) { | 
|  | return std::unique_ptr<base::Value>{ | 
|  | new base::FundamentalValue(value.Get<int>())}; | 
|  | } | 
|  |  | 
|  | LOG(FATAL) << "Unsupported type:" << value.GetType().name(); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<base::DictionaryValue> DictionaryToJson( | 
|  | const chromeos::VariantDictionary& dictionary) { | 
|  | std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue}; | 
|  | for (const auto& it : dictionary) | 
|  | result->Set(it.first, AnyToJson(it.second).release()); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | std::string DictionaryToString(const chromeos::VariantDictionary& dictionary) { | 
|  | std::unique_ptr<base::DictionaryValue> json{DictionaryToJson(dictionary)}; | 
|  | std::string str; | 
|  | base::JSONWriter::Write(json.get(), &str); | 
|  | return str; | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | class Daemon : public chromeos::DBusDaemon { | 
|  | public: | 
|  | Daemon() = default; | 
|  |  | 
|  | protected: | 
|  | int OnInit() override; | 
|  | void OnShutdown(int* return_code) override; | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<org::chromium::Buffet::ObjectManagerProxy> object_manager_; | 
|  |  | 
|  | void OnBuffetCommand(org::chromium::Buffet::CommandProxy* command); | 
|  | void OnBuffetCommandRemoved(const dbus::ObjectPath& object_path); | 
|  | void OnPropertyChange(org::chromium::Buffet::CommandProxy* command, | 
|  | const std::string& property_name); | 
|  | void OnCommandProgress(org::chromium::Buffet::CommandProxy* command, | 
|  | int progress); | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(Daemon); | 
|  | }; | 
|  |  | 
|  | int Daemon::OnInit() { | 
|  | int return_code = chromeos::DBusDaemon::OnInit(); | 
|  | if (return_code != EX_OK) | 
|  | return return_code; | 
|  |  | 
|  | object_manager_.reset(new org::chromium::Buffet::ObjectManagerProxy{bus_}); | 
|  | object_manager_->SetCommandAddedCallback( | 
|  | base::Bind(&Daemon::OnBuffetCommand, base::Unretained(this))); | 
|  | object_manager_->SetCommandRemovedCallback( | 
|  | base::Bind(&Daemon::OnBuffetCommandRemoved, base::Unretained(this))); | 
|  |  | 
|  | printf("Waiting for commands...\n"); | 
|  | return EX_OK; | 
|  | } | 
|  |  | 
|  | void Daemon::OnShutdown(int* return_code) { | 
|  | printf("Shutting down...\n"); | 
|  | } | 
|  |  | 
|  | void Daemon::OnPropertyChange(org::chromium::Buffet::CommandProxy* command, | 
|  | const std::string& property_name) { | 
|  | printf("Notification: property '%s' on command '%s' changed.\n", | 
|  | property_name.c_str(), command->id().c_str()); | 
|  | printf("  Current command status: '%s'\n", command->status().c_str()); | 
|  | std::string progress = DictionaryToString(command->progress()); | 
|  | printf("  Current command progress: %s\n", progress.c_str()); | 
|  | std::string results = DictionaryToString(command->results()); | 
|  | printf("  Current command results: %s\n", results.c_str()); | 
|  | } | 
|  |  | 
|  | void Daemon::OnBuffetCommand(org::chromium::Buffet::CommandProxy* command) { | 
|  | // "Handle" only commands that belong to this daemon's category. | 
|  | if (command->category() != kTestCommandCategory || | 
|  | command->status() == "done") { | 
|  | return; | 
|  | } | 
|  |  | 
|  | command->SetPropertyChangedCallback(base::Bind(&Daemon::OnPropertyChange, | 
|  | base::Unretained(this))); | 
|  | printf("++++++++++++++++++++++++++++++++++++++++++++++++\n"); | 
|  | printf("Command received: %s\n", command->name().c_str()); | 
|  | printf("DBus Object Path: %s\n", command->GetObjectPath().value().c_str()); | 
|  | printf("        category: %s\n", command->category().c_str()); | 
|  | printf("              ID: %s\n", command->id().c_str()); | 
|  | printf("          status: %s\n", command->status().c_str()); | 
|  | printf("          origin: %s\n", command->origin().c_str()); | 
|  | std::string param_names = DictionaryToString(command->parameters()); | 
|  | printf(" parameters: %s\n", param_names.c_str()); | 
|  | OnCommandProgress(command, 0); | 
|  | } | 
|  |  | 
|  | void Daemon::OnCommandProgress(org::chromium::Buffet::CommandProxy* command, | 
|  | int progress) { | 
|  | printf("Updating command '%s' progress to %d%%\n", command->id().c_str(), | 
|  | progress); | 
|  | auto new_progress = command->progress(); | 
|  | new_progress["progress"] = progress; | 
|  | command->SetProgress(new_progress, nullptr); | 
|  |  | 
|  | if (progress >= 100) { | 
|  | command->Done(nullptr); | 
|  | } else { | 
|  | base::MessageLoop::current()->PostDelayedTask( | 
|  | FROM_HERE, base::Bind(&Daemon::OnCommandProgress, | 
|  | base::Unretained(this), command, progress + 10), | 
|  | base::TimeDelta::FromSeconds(1)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Daemon::OnBuffetCommandRemoved(const dbus::ObjectPath& object_path) { | 
|  | printf("------------------------------------------------\n"); | 
|  | printf("Command removed\n"); | 
|  | printf("DBus Object Path: %s\n", object_path.value().c_str()); | 
|  | } | 
|  |  | 
|  | int main(int argc, char* argv[]) { | 
|  | base::CommandLine::Init(argc, argv); | 
|  | chromeos::InitLog(chromeos::kLogToSyslog | | 
|  | chromeos::kLogToStderr | | 
|  | chromeos::kLogHeader); | 
|  | Daemon daemon; | 
|  | return daemon.Run(); | 
|  | } |