blob: 10efa6eda8205908718620de91a6d66535196ee4 [file] [log] [blame]
Chris Sosa45d9f102014-03-24 11:18:54 -07001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Vakulenko7a1dc0b2014-08-15 11:45:46 -07005#include <memory>
Chris Sosa45d9f102014-03-24 11:18:54 -07006#include <string>
Christopher Wiley48e37282014-05-08 14:43:58 -07007#include <sysexits.h>
Chris Sosa45d9f102014-03-24 11:18:54 -07008
Chris Sosaababc5c2014-04-09 15:42:01 -07009#include <base/command_line.h>
Alex Vakulenko61ad4db2015-01-20 10:50:04 -080010#include <base/json/json_reader.h>
Chris Sosa45d9f102014-03-24 11:18:54 -070011#include <base/logging.h>
Christopher Wiley48e37282014-05-08 14:43:58 -070012#include <base/memory/ref_counted.h>
Alex Vakulenko420e49f2014-12-01 17:53:27 -080013#include <base/strings/stringprintf.h>
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070014#include <base/values.h>
Alex Vakulenkoa9044342014-08-23 19:31:27 -070015#include <chromeos/any.h>
Alex Vakulenko420e49f2014-12-01 17:53:27 -080016#include <chromeos/daemons/dbus_daemon.h>
Alex Vakulenkoe4879a22014-08-20 15:47:36 -070017#include <chromeos/data_encoding.h>
Alex Vakulenko63cd5fa2014-08-27 08:04:22 -070018#include <chromeos/dbus/data_serialization.h>
Alex Vakulenko07216fe2014-09-19 15:31:09 -070019#include <chromeos/dbus/dbus_method_invoker.h>
20#include <chromeos/errors/error.h>
Alex Vakulenko576c9792014-09-22 16:49:45 -070021#include <chromeos/variant_dictionary.h>
Chris Sosa45d9f102014-03-24 11:18:54 -070022#include <dbus/bus.h>
Christopher Wileya4915c42014-03-27 14:45:37 -070023#include <dbus/message.h>
Christopher Wiley48e37282014-05-08 14:43:58 -070024#include <dbus/object_proxy.h>
Christopher Wileyadb901d2014-05-07 09:58:45 -070025#include <dbus/object_manager.h>
Alex Vakulenko3cb466c2014-04-15 11:36:32 -070026#include <dbus/values_util.h>
Chris Sosa45d9f102014-03-24 11:18:54 -070027
Alex Vakulenko2348e422014-11-21 08:57:57 -080028#include "buffet/dbus-proxies.h"
Chris Sosa45d9f102014-03-24 11:18:54 -070029
Alex Vakulenko420e49f2014-12-01 17:53:27 -080030using chromeos::Error;
Alex Vakulenko07216fe2014-09-19 15:31:09 -070031using chromeos::ErrorPtr;
32
Chris Sosa45d9f102014-03-24 11:18:54 -070033namespace {
34
Christopher Wileya4915c42014-03-27 14:45:37 -070035void usage() {
Alex Vakulenko420e49f2014-12-01 17:53:27 -080036 printf(R"(Possible commands:
37 - TestMethod <message>
38 - StartDevice
39 - CheckDeviceRegistered
40 - GetDeviceInfo
41 - RegisterDevice param1=val1&param2=val2...
42 - AddCommand '{"name":"command_name","parameters":{}}'
Nathan Bullocked53c662015-01-07 09:57:42 -050043 - UpdateState prop_name prop_value
Alex Vakulenko61ad4db2015-01-20 10:50:04 -080044 - GetState
Alex Vakulenko420e49f2014-12-01 17:53:27 -080045 - PendingCommands
46)");
Christopher Wileya4915c42014-03-27 14:45:37 -070047}
48
Alex Vakulenko61ad4db2015-01-20 10:50:04 -080049// Helpers for JsonToAny().
50template<typename T>
51chromeos::Any GetJsonValue(const base::Value& json,
52 bool(base::Value::*fnc)(T*) const) {
53 T val;
54 CHECK((json.*fnc)(&val));
55 return val;
56}
57
58template<typename T>
59chromeos::Any GetJsonList(const base::ListValue& list); // Prototype.
60
61// Converts a JSON value into an Any so it can be sent over D-Bus using
62// UpdateState D-Bus method from Buffet.
63chromeos::Any JsonToAny(const base::Value& json) {
64 chromeos::Any prop_value;
65 switch (json.GetType()) {
66 case base::Value::TYPE_NULL:
67 prop_value = nullptr;
68 break;
69 case base::Value::TYPE_BOOLEAN:
70 prop_value = GetJsonValue<bool>(json, &base::Value::GetAsBoolean);
71 break;
72 case base::Value::TYPE_INTEGER:
73 prop_value = GetJsonValue<int>(json, &base::Value::GetAsInteger);
74 break;
75 case base::Value::TYPE_DOUBLE:
76 prop_value = GetJsonValue<double>(json, &base::Value::GetAsDouble);
77 break;
78 case base::Value::TYPE_STRING:
79 prop_value = GetJsonValue<std::string>(json, &base::Value::GetAsString);
80 break;
81 case base::Value::TYPE_BINARY:
82 LOG(FATAL) << "Binary values should not happen";
83 break;
84 case base::Value::TYPE_DICTIONARY: {
85 const base::DictionaryValue* dict = nullptr; // Still owned by |json|.
86 CHECK(json.GetAsDictionary(&dict));
87 chromeos::VariantDictionary var_dict;
88 base::DictionaryValue::Iterator it(*dict);
89 while (!it.IsAtEnd()) {
90 var_dict.emplace(it.key(), JsonToAny(it.value()));
91 it.Advance();
92 }
93 prop_value = var_dict;
94 break;
95 }
96 case base::Value::TYPE_LIST: {
97 const base::ListValue* list = nullptr; // Still owned by |json|.
98 CHECK(json.GetAsList(&list));
99 CHECK(!list->empty()) << "Unable to deduce the type of list elements.";
100 switch ((*list->begin())->GetType()) {
101 case base::Value::TYPE_BOOLEAN:
102 prop_value = GetJsonList<bool>(*list);
103 break;
104 case base::Value::TYPE_INTEGER:
105 prop_value = GetJsonList<int>(*list);
106 break;
107 case base::Value::TYPE_DOUBLE:
108 prop_value = GetJsonList<double>(*list);
109 break;
110 case base::Value::TYPE_STRING:
111 prop_value = GetJsonList<std::string>(*list);
112 break;
113 case base::Value::TYPE_DICTIONARY:
114 prop_value = GetJsonList<chromeos::VariantDictionary>(*list);
115 break;
116 default:
117 LOG(FATAL) << "Unsupported JSON value type for list element: "
118 << (*list->begin())->GetType();
119 }
120 break;
121 }
122 default:
123 LOG(FATAL) << "Unexpected JSON value type: " << json.GetType();
124 break;
125 }
126 return prop_value;
127}
128
129template<typename T>
130chromeos::Any GetJsonList(const base::ListValue& list) {
131 std::vector<T> val;
132 val.reserve(list.GetSize());
133 for (const base::Value* v : list)
134 val.push_back(JsonToAny(*v).Get<T>());
135 return val;
136}
137
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800138class Daemon : public chromeos::DBusDaemon {
Christopher Wiley48e37282014-05-08 14:43:58 -0700139 public:
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800140 Daemon() = default;
141
142 protected:
143 int OnInit() override {
144 int return_code = chromeos::DBusDaemon::OnInit();
145 if (return_code != EX_OK)
146 return return_code;
147
148 object_manager_.reset(new org::chromium::Buffet::ObjectManagerProxy{bus_});
Alex Vakulenkoc3bac7d2014-11-25 14:04:27 -0800149 manager_proxy_.reset(new org::chromium::Buffet::ManagerProxy{bus_});
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800150
151 auto args = CommandLine::ForCurrentProcess()->GetArgs();
152
153 // Pop the command off of the args list.
154 std::string command = args.front();
155 args.erase(args.begin());
156
157 if (command.compare("TestMethod") == 0) {
158 if (args.empty() || CheckArgs(command, args, 1)) {
159 std::string message;
160 if (!args.empty())
161 message = args.back();
162 PostTask(&Daemon::CallTestMethod, message);
163 }
164 } else if (command.compare("StartDevice") == 0 ||
165 command.compare("sd") == 0) {
166 if (CheckArgs(command, args, 0))
167 PostTask(&Daemon::CallStartDevice);
168 } else if (command.compare("CheckDeviceRegistered") == 0 ||
169 command.compare("cr") == 0) {
170 if (CheckArgs(command, args, 0))
171 PostTask(&Daemon::CallCheckDeviceRegistered);
172 } else if (command.compare("GetDeviceInfo") == 0 ||
173 command.compare("di") == 0) {
174 if (CheckArgs(command, args, 0))
175 PostTask(&Daemon::CallGetDeviceInfo);
176 } else if (command.compare("RegisterDevice") == 0 ||
177 command.compare("rd") == 0) {
178 if (args.empty() || CheckArgs(command, args, 1)) {
179 std::string dict;
180 if (!args.empty())
181 dict = args.back();
182 PostTask(&Daemon::CallRegisterDevice, dict);
183 }
184 } else if (command.compare("UpdateState") == 0 ||
185 command.compare("us") == 0) {
186 if (CheckArgs(command, args, 2))
187 PostTask(&Daemon::CallUpdateState, args.front(), args.back());
Alex Vakulenko61ad4db2015-01-20 10:50:04 -0800188 } else if (command.compare("GetState") == 0 ||
189 command.compare("gs") == 0) {
190 if (CheckArgs(command, args, 0))
191 PostTask(&Daemon::CallGetState);
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800192 } else if (command.compare("AddCommand") == 0 ||
193 command.compare("ac") == 0) {
194 if (CheckArgs(command, args, 1))
195 PostTask(&Daemon::CallAddCommand, args.back());
196 } else if (command.compare("PendingCommands") == 0 ||
197 command.compare("pc") == 0) {
198 if (CheckArgs(command, args, 0))
199 // CallGetPendingCommands relies on ObjectManager but it is being
200 // initialized asynchronously without a way to get a callback when
201 // it is ready to be used. So, just wait a bit before calling its
202 // methods.
203 PostDelayedTask(&Daemon::CallGetPendingCommands,
204 base::TimeDelta::FromMilliseconds(100));
205 } else {
206 fprintf(stderr, "Unknown command: '%s'\n", command.c_str());
207 usage();
208 Quit();
209 return EX_USAGE;
210 }
211 return EX_OK;
Christopher Wileya4915c42014-03-27 14:45:37 -0700212 }
213
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800214 void OnShutdown(int* return_code) override {
215 if (*return_code == EX_OK)
216 *return_code = exit_code_;
217 }
Alex Vakulenko7a1dc0b2014-08-15 11:45:46 -0700218
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800219 private:
220 void ReportError(Error* error) {
221 fprintf(stderr, "Failed to receive a response: %s\n",
222 error->GetMessage().c_str());
223 exit_code_ = EX_UNAVAILABLE;
224 Quit();
225 }
226
227 bool CheckArgs(const std::string& command,
228 const std::vector<std::string>& args,
229 size_t expected_arg_count) {
230 if (args.size() == expected_arg_count)
231 return true;
232 fprintf(stderr, "Invalid number of arguments for command '%s'\n",
233 command.c_str());
234 usage();
235 exit_code_ = EX_USAGE;
236 Quit();
237 return false;
238 }
239
240 template<typename... Args>
241 void PostTask(void (Daemon::*fn)(const Args&...), const Args&... args) {
242 base::MessageLoop::current()->PostTask(
243 FROM_HERE, base::Bind(fn, base::Unretained(this), args...));
244 }
245
246 template<typename... Args>
247 void PostDelayedTask(void (Daemon::*fn)(const Args&...),
248 const base::TimeDelta& delay,
249 const Args&... args) {
250 base::MessageLoop::current()->PostDelayedTask(
251 FROM_HERE, base::Bind(fn, base::Unretained(this), args...), delay);
252 }
253
254 void CallTestMethod(const std::string& message) {
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700255 ErrorPtr error;
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700256 std::string response_message;
Alex Vakulenko2348e422014-11-21 08:57:57 -0800257 if (!manager_proxy_->TestMethod(message, &response_message, &error)) {
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800258 return ReportError(error.get());
Christopher Wiley48e37282014-05-08 14:43:58 -0700259 }
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800260 printf("Received a response: %s\n", response_message.c_str());
261 Quit();
Christopher Wiley48e37282014-05-08 14:43:58 -0700262 }
263
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800264 void CallStartDevice() {
Anton Muhin86d67fe2014-10-01 18:06:54 +0400265 ErrorPtr error;
Alex Vakulenko2348e422014-11-21 08:57:57 -0800266 if (!manager_proxy_->StartDevice(&error)) {
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800267 return ReportError(error.get());
Anton Muhin86d67fe2014-10-01 18:06:54 +0400268 }
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800269 Quit();
Anton Muhin86d67fe2014-10-01 18:06:54 +0400270 }
271
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800272 void CallCheckDeviceRegistered() {
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700273 ErrorPtr error;
Christopher Wiley48e37282014-05-08 14:43:58 -0700274 std::string device_id;
Alex Vakulenko2348e422014-11-21 08:57:57 -0800275 if (!manager_proxy_->CheckDeviceRegistered(&device_id, &error)) {
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800276 return ReportError(error.get());
Christopher Wiley48e37282014-05-08 14:43:58 -0700277 }
278
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800279 printf("Device ID: %s\n",
280 device_id.empty() ? "<unregistered>" : device_id.c_str());
281 Quit();
Christopher Wiley48e37282014-05-08 14:43:58 -0700282 }
283
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800284 void CallGetDeviceInfo() {
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700285 ErrorPtr error;
Christopher Wiley48e37282014-05-08 14:43:58 -0700286 std::string device_info;
Alex Vakulenko2348e422014-11-21 08:57:57 -0800287 if (!manager_proxy_->GetDeviceInfo(&device_info, &error)) {
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800288 return ReportError(error.get());
Christopher Wiley48e37282014-05-08 14:43:58 -0700289 }
290
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800291 printf("Device Info: %s\n",
292 device_info.empty() ? "<unregistered>" : device_info.c_str());
293 Quit();
Christopher Wiley48e37282014-05-08 14:43:58 -0700294 }
295
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800296 void CallRegisterDevice(const std::string& args) {
297 chromeos::VariantDictionary params;
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700298 if (!args.empty()) {
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800299 auto key_values = chromeos::data_encoding::WebParamsDecode(args);
Alex Vakulenkoa0424dd2014-06-13 16:10:17 -0700300 for (const auto& pair : key_values) {
Alex Vakulenkoa9044342014-08-23 19:31:27 -0700301 params.insert(std::make_pair(pair.first, chromeos::Any(pair.second)));
Alex Vakulenko3cb466c2014-04-15 11:36:32 -0700302 }
303 }
304
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700305 ErrorPtr error;
Christopher Wiley48e37282014-05-08 14:43:58 -0700306 std::string device_id;
Alex Vakulenko2348e422014-11-21 08:57:57 -0800307 if (!manager_proxy_->RegisterDevice(params, &device_id, &error)) {
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800308 return ReportError(error.get());
Christopher Wiley48e37282014-05-08 14:43:58 -0700309 }
310
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800311 printf("Device registered: %s\n", device_id.c_str());
312 Quit();
Christopher Wiley48e37282014-05-08 14:43:58 -0700313 }
314
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800315 void CallUpdateState(const std::string& prop, const std::string& value) {
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700316 ErrorPtr error;
Alex Vakulenko61ad4db2015-01-20 10:50:04 -0800317 std::string error_message;
318 std::unique_ptr<base::Value> json(base::JSONReader::ReadAndReturnError(
319 value, base::JSON_PARSE_RFC, nullptr, &error_message));
320 if (!json) {
321 Error::AddTo(&error, FROM_HERE, chromeos::errors::json::kDomain,
322 chromeos::errors::json::kParseError, error_message);
323 return ReportError(error.get());
324 }
325
326 chromeos::VariantDictionary property_set{{prop, JsonToAny(*json)}};
Alex Vakulenko2348e422014-11-21 08:57:57 -0800327 if (!manager_proxy_->UpdateState(property_set, &error)) {
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800328 return ReportError(error.get());
Christopher Wiley48e37282014-05-08 14:43:58 -0700329 }
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800330 Quit();
Christopher Wiley48e37282014-05-08 14:43:58 -0700331 }
332
Alex Vakulenko61ad4db2015-01-20 10:50:04 -0800333 void CallGetState() {
334 std::string json;
335 ErrorPtr error;
336 if (!manager_proxy_->GetState(&json, &error)) {
337 return ReportError(error.get());
338 }
339 printf("Device State: %s\n", json.c_str());
340 Quit();
341 }
342
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800343 void CallAddCommand(const std::string& command) {
Alex Vakulenko07216fe2014-09-19 15:31:09 -0700344 ErrorPtr error;
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800345 if (!manager_proxy_->AddCommand(command, &error)) {
346 return ReportError(error.get());
Alex Vakulenko665c8852014-09-11 16:57:24 -0700347 }
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800348 Quit();
Alex Vakulenko665c8852014-09-11 16:57:24 -0700349 }
350
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800351 void CallGetPendingCommands() {
352 printf("Pending commands:\n");
353 for (auto* cmd : object_manager_->GetCommandInstances()) {
354 printf("%10s (%-3d) - '%s' (id:%s)\n",
355 cmd->status().c_str(),
356 cmd->progress(),
357 cmd->name().c_str(),
358 cmd->id().c_str());
Christopher Wileyadb901d2014-05-07 09:58:45 -0700359 }
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800360 Quit();
Christopher Wileyadb901d2014-05-07 09:58:45 -0700361 }
362
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800363 std::unique_ptr<org::chromium::Buffet::ObjectManagerProxy> object_manager_;
Alex Vakulenko2348e422014-11-21 08:57:57 -0800364 std::unique_ptr<org::chromium::Buffet::ManagerProxy> manager_proxy_;
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800365 int exit_code_ = EX_OK;
366
367 DISALLOW_COPY_AND_ASSIGN(Daemon);
Christopher Wiley48e37282014-05-08 14:43:58 -0700368};
369
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800370} // anonymous namespace
Christopher Wiley48e37282014-05-08 14:43:58 -0700371
372int main(int argc, char** argv) {
373 CommandLine::Init(argc, argv);
374 CommandLine* cl = CommandLine::ForCurrentProcess();
375 CommandLine::StringVector args = cl->GetArgs();
376 if (args.empty()) {
Christopher Wileya4915c42014-03-27 14:45:37 -0700377 usage();
Chris Sosaababc5c2014-04-09 15:42:01 -0700378 return EX_USAGE;
Christopher Wileya4915c42014-03-27 14:45:37 -0700379 }
380
Alex Vakulenko420e49f2014-12-01 17:53:27 -0800381 Daemon daemon;
382 return daemon.Run();
Christopher Wiley48e37282014-05-08 14:43:58 -0700383}