blob: b944f8439c13ea742b78e1f7cb99de7de35a964a [file] [log] [blame]
// Copyright (c) 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.
#include "login_manager/session_manager_dbus_adaptor.h"
#include <map>
#include <string>
#include <vector>
#include <base/basictypes.h>
#include <base/bind.h>
#include <base/callback.h>
#include <base/file_util.h>
#include <base/memory/scoped_ptr.h>
#include <base/stl_util.h>
#include <dbus/exported_object.h>
#include <dbus/message.h>
#include "login_manager/session_manager_impl.h"
#include "login_manager/policy_service.h"
namespace login_manager {
namespace {
const char kBindingsPath[] =
"/usr/share/dbus-1/interfaces/org.chromium.SessionManagerInterface.xml";
const char kDBusIntrospectableInterface[] =
"org.freedesktop.DBus.Introspectable";
const char kDBusIntrospectMethod[] = "Introspect";
// Passes |method_call| to |handler| and passes the response to
// |response_sender|. If |handler| returns NULL, an empty response is created
// and sent.
void HandleSynchronousDBusMethodCall(
base::Callback<scoped_ptr<dbus::Response>(dbus::MethodCall*)> handler,
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
scoped_ptr<dbus::Response> response = handler.Run(method_call);
if (!response)
response = dbus::Response::FromMethodCall(method_call);
response_sender.Run(response.Pass());
}
scoped_ptr<dbus::Response> CreateError(dbus::MethodCall* call,
const std::string& name,
const std::string& message) {
return dbus::ErrorResponse::FromMethodCall(call,
name,
message).PassAs<dbus::Response>();
}
// Creates a new "invalid args" reply to call.
scoped_ptr<dbus::Response> CreateInvalidArgsError(dbus::MethodCall* call,
std::string message) {
return CreateError(call, DBUS_ERROR_INVALID_ARGS, "Signature is: " + message);
}
// Craft a Response to call that is appropriate, given the contents of error.
// If error is set, this will be an ErrorResponse. Otherwise, it will be a
// Response containing payload.
scoped_ptr<dbus::Response> CraftAppropriateResponseWithBool(
dbus::MethodCall* call,
const SessionManagerImpl::Error& error,
bool payload) {
scoped_ptr<dbus::Response> response;
if (error.is_set()) {
response = CreateError(call, error.name(), error.message());
} else {
response = dbus::Response::FromMethodCall(call);
dbus::MessageWriter writer(response.get());
writer.AppendBool(payload);
}
return response.Pass();
}
scoped_ptr<dbus::Response> CraftAppropriateResponseWithString(
dbus::MethodCall* call,
const SessionManagerImpl::Error& error,
const std::string& payload) {
scoped_ptr<dbus::Response> response;
if (error.is_set()) {
response = CreateError(call, error.name(), error.message());
} else {
response = dbus::Response::FromMethodCall(call);
dbus::MessageWriter writer(response.get());
writer.AppendString(payload);
}
return response.Pass();
}
scoped_ptr<dbus::Response> CraftAppropriateResponseWithBytes(
dbus::MethodCall* call,
const SessionManagerImpl::Error& error,
const std::vector<uint8>& payload) {
scoped_ptr<dbus::Response> response;
if (error.is_set()) {
response = CreateError(call, error.name(), error.message());
} else {
response = dbus::Response::FromMethodCall(call);
dbus::MessageWriter writer(response.get());
writer.AppendArrayOfBytes(vector_as_array(&payload), payload.size());
}
return response.Pass();
}
} // namespace
// PolicyService::Completion implementation that forwards the result to a DBus
// invocation context.
class DBusMethodCompletion : public PolicyService::Completion {
public:
DBusMethodCompletion(dbus::MethodCall* call,
const dbus::ExportedObject::ResponseSender& sender);
virtual ~DBusMethodCompletion();
virtual void ReportSuccess();
virtual void ReportFailure(const PolicyService::Error& error);
private:
dbus::MethodCall* call_;
dbus::ExportedObject::ResponseSender sender_;
DISALLOW_COPY_AND_ASSIGN(DBusMethodCompletion);
};
// Apparently, call is owned by sender, so it's safe to hang on to it.
DBusMethodCompletion::DBusMethodCompletion(
dbus::MethodCall* call,
const dbus::ExportedObject::ResponseSender& sender)
: call_(call),
sender_(sender) {
}
DBusMethodCompletion::~DBusMethodCompletion() {
if (call_) {
NOTREACHED() << "Unfinished DBUS call!";
sender_.Run(dbus::Response::FromMethodCall(call_));
}
}
void DBusMethodCompletion::ReportSuccess() {
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call_));
dbus::MessageWriter writer(response.get());
writer.AppendBool(true);
sender_.Run(response.Pass());
call_ = NULL;
delete this;
}
void DBusMethodCompletion::ReportFailure(const PolicyService::Error& error) {
sender_.Run(dbus::ErrorResponse::FromMethodCall(
call_,
error.code(),
error.message()).PassAs<dbus::Response>());
call_ = NULL;
delete this;
}
SessionManagerDBusAdaptor::SessionManagerDBusAdaptor(SessionManagerImpl* impl)
: impl_(impl) {
CHECK(impl_);
}
SessionManagerDBusAdaptor::~SessionManagerDBusAdaptor() {}
void SessionManagerDBusAdaptor::ExportDBusMethods(
dbus::ExportedObject* object) {
ExportSyncDBusMethod(object, kSessionManagerEmitLoginPromptVisible,
&SessionManagerDBusAdaptor::EmitLoginPromptVisible);
ExportSyncDBusMethod(object, "EnableChromeTesting",
&SessionManagerDBusAdaptor::EnableChromeTesting);
ExportSyncDBusMethod(object, kSessionManagerStartSession,
&SessionManagerDBusAdaptor::StartSession);
ExportSyncDBusMethod(object, kSessionManagerStopSession,
&SessionManagerDBusAdaptor::StopSession);
ExportAsyncDBusMethod(object, kSessionManagerStorePolicy,
&SessionManagerDBusAdaptor::StorePolicy);
ExportSyncDBusMethod(object, kSessionManagerRetrievePolicy,
&SessionManagerDBusAdaptor::RetrievePolicy);
ExportAsyncDBusMethod(object, kSessionManagerStorePolicyForUser,
&SessionManagerDBusAdaptor::StorePolicyForUser);
ExportSyncDBusMethod(object, kSessionManagerRetrievePolicyForUser,
&SessionManagerDBusAdaptor::RetrievePolicyForUser);
ExportAsyncDBusMethod(
object,
kSessionManagerStoreDeviceLocalAccountPolicy,
&SessionManagerDBusAdaptor::StoreDeviceLocalAccountPolicy);
ExportSyncDBusMethod(
object,
kSessionManagerRetrieveDeviceLocalAccountPolicy,
&SessionManagerDBusAdaptor::RetrieveDeviceLocalAccountPolicy);
ExportSyncDBusMethod(object, kSessionManagerRetrieveSessionState,
&SessionManagerDBusAdaptor::RetrieveSessionState);
ExportSyncDBusMethod(object, kSessionManagerRetrieveActiveSessions,
&SessionManagerDBusAdaptor::RetrieveActiveSessions);
ExportSyncDBusMethod(object, kSessionManagerLockScreen,
&SessionManagerDBusAdaptor::LockScreen);
ExportSyncDBusMethod(object, kSessionManagerHandleLockScreenShown,
&SessionManagerDBusAdaptor::HandleLockScreenShown);
ExportSyncDBusMethod(object, kSessionManagerHandleLockScreenDismissed,
&SessionManagerDBusAdaptor::HandleLockScreenDismissed);
ExportSyncDBusMethod(object, kSessionManagerRestartJob,
&SessionManagerDBusAdaptor::RestartJob);
ExportSyncDBusMethod(object, kSessionManagerStartDeviceWipe,
&SessionManagerDBusAdaptor::StartDeviceWipe);
ExportSyncDBusMethod(object, kSessionManagerSetFlagsForUser,
&SessionManagerDBusAdaptor::SetFlagsForUser);
CHECK(object->ExportMethodAndBlock(
kDBusIntrospectableInterface, kDBusIntrospectMethod,
base::Bind(&HandleSynchronousDBusMethodCall,
base::Bind(&SessionManagerDBusAdaptor::Introspect,
base::Unretained(this)))));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::EmitLoginPromptVisible(
dbus::MethodCall* call) {
SessionManagerImpl::Error error;
impl_->EmitLoginPromptVisible(&error);
if (error.is_set())
return CreateError(call, error.name(), error.message());
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::EnableChromeTesting(
dbus::MethodCall* call) {
dbus::MessageReader reader(call);
bool relaunch;
std::vector<std::string> extra_args;
if (!reader.PopBool(&relaunch) || !reader.PopArrayOfStrings(&extra_args))
return CreateInvalidArgsError(call, call->GetSignature());
SessionManagerImpl::Error error;
std::string testing_path = impl_->EnableChromeTesting(relaunch,
extra_args,
&error);
return CraftAppropriateResponseWithString(call, error, testing_path);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::StartSession(
dbus::MethodCall* call) {
dbus::MessageReader reader(call);
std::string email, unique_id;
if (!reader.PopString(&email) || !reader.PopString(&unique_id))
return CreateInvalidArgsError(call, call->GetSignature());
SessionManagerImpl::Error error;
bool success = impl_->StartSession(email, unique_id, &error);
return CraftAppropriateResponseWithBool(call, error, success);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::StopSession(
dbus::MethodCall* call) {
// Though this takes a string (unique_id), it is ignored.
bool success = impl_->StopSession();
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
writer.AppendBool(success);
return response.Pass();
}
void SessionManagerDBusAdaptor::StorePolicy(
dbus::MethodCall* call,
dbus::ExportedObject::ResponseSender sender) {
uint8* policy_blob = NULL;
size_t policy_blob_len = 0;
dbus::MessageReader reader(call);
// policy_blob points into reader after pop.
if (!reader.PopArrayOfBytes(&policy_blob, &policy_blob_len)) {
sender.Run(CreateInvalidArgsError(call, call->GetSignature()).Pass());
} else {
impl_->StorePolicy(policy_blob, policy_blob_len,
new DBusMethodCompletion(call, sender));
// Response will be sent asynchronously.
}
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrievePolicy(
dbus::MethodCall* call) {
std::vector<uint8> policy_data;
SessionManagerImpl::Error error;
impl_->RetrievePolicy(&policy_data, &error);
return CraftAppropriateResponseWithBytes(call, error, policy_data);
}
void SessionManagerDBusAdaptor::StorePolicyForUser(
dbus::MethodCall* call,
dbus::ExportedObject::ResponseSender sender) {
std::string user_email;
uint8* policy_blob = NULL;
size_t policy_blob_len = 0;
dbus::MessageReader reader(call);
// policy_blob points into reader after pop.
if (!reader.PopString(&user_email) ||
!reader.PopArrayOfBytes(&policy_blob, &policy_blob_len)) {
sender.Run(CreateInvalidArgsError(call, call->GetSignature()).Pass());
} else {
impl_->StorePolicyForUser(user_email, policy_blob, policy_blob_len,
new DBusMethodCompletion(call, sender));
// Response will normally be sent asynchronously.
}
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrievePolicyForUser(
dbus::MethodCall* call) {
std::string user_email;
dbus::MessageReader reader(call);
if (!reader.PopString(&user_email))
return CreateInvalidArgsError(call, call->GetSignature());
std::vector<uint8> policy_data;
SessionManagerImpl::Error error;
impl_->RetrievePolicyForUser(user_email, &policy_data, &error);
return CraftAppropriateResponseWithBytes(call, error, policy_data);
}
void SessionManagerDBusAdaptor::StoreDeviceLocalAccountPolicy(
dbus::MethodCall* call,
dbus::ExportedObject::ResponseSender sender) {
std::string account_id;
uint8* policy_blob = NULL;
size_t policy_blob_len = 0;
dbus::MessageReader reader(call);
// policy_blob points into reader after pop.
if (!reader.PopString(&account_id) ||
!reader.PopArrayOfBytes(&policy_blob, &policy_blob_len)) {
sender.Run(CreateInvalidArgsError(call, call->GetSignature()).Pass());
} else {
impl_->StoreDeviceLocalAccountPolicy(
account_id, policy_blob, policy_blob_len,
new DBusMethodCompletion(call, sender));
// Response will be sent asynchronously.
}
}
scoped_ptr<dbus::Response>
SessionManagerDBusAdaptor::RetrieveDeviceLocalAccountPolicy(
dbus::MethodCall* call) {
std::string account_id;
dbus::MessageReader reader(call);
if (!reader.PopString(&account_id))
return CreateInvalidArgsError(call, call->GetSignature());
std::vector<uint8> policy_data;
SessionManagerImpl::Error error;
impl_->RetrieveDeviceLocalAccountPolicy(account_id, &policy_data, &error);
return CraftAppropriateResponseWithBytes(call, error, policy_data);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrieveSessionState(
dbus::MethodCall* call) {
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
writer.AppendString(impl_->RetrieveSessionState());
return response.Pass();
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RetrieveActiveSessions(
dbus::MethodCall* call) {
std::map<std::string, std::string> sessions;
impl_->RetrieveActiveSessions(&sessions);
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
dbus::MessageWriter array_writer(NULL);
writer.OpenArray("{ss}", &array_writer);
for (std::map<std::string, std::string>::const_iterator it = sessions.begin();
it != sessions.end();
++it) {
dbus::MessageWriter entry_writer(NULL);
array_writer.OpenDictEntry(&entry_writer);
entry_writer.AppendString(it->first);
entry_writer.AppendString(it->second);
array_writer.CloseContainer(&entry_writer);
}
writer.CloseContainer(&array_writer);
return response.Pass();
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::LockScreen(
dbus::MethodCall* call) {
SessionManagerImpl::Error error;
impl_->LockScreen(&error);
if (error.is_set())
return CreateError(call, error.name(), error.message());
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::HandleLockScreenShown(
dbus::MethodCall* call) {
impl_->HandleLockScreenShown();
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response>
SessionManagerDBusAdaptor::HandleLockScreenDismissed(dbus::MethodCall* call) {
impl_->HandleLockScreenDismissed();
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::RestartJob(
dbus::MethodCall* call) {
int pid;
std::string arguments;
dbus::MessageReader reader(call);
if (!reader.PopInt32(&pid) || !reader.PopString(&arguments))
return CreateInvalidArgsError(call, call->GetSignature());
SessionManagerImpl::Error error;
bool success = impl_->RestartJob(static_cast<pid_t>(pid), arguments, &error);
return CraftAppropriateResponseWithBool(call, error, success);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::StartDeviceWipe(
dbus::MethodCall* call) {
SessionManagerImpl::Error error;
impl_->StartDeviceWipe(&error);
return CraftAppropriateResponseWithBool(call, error, true);
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::SetFlagsForUser(
dbus::MethodCall* call) {
dbus::MessageReader reader(call);
std::string user_email;
std::vector<std::string> session_user_flags;
if (!reader.PopString(&user_email) ||
!reader.PopArrayOfStrings(&session_user_flags)) {
return CreateInvalidArgsError(call, call->GetSignature());
}
impl_->SetFlagsForUser(user_email, session_user_flags);
return scoped_ptr<dbus::Response>(dbus::Response::FromMethodCall(call));
}
scoped_ptr<dbus::Response> SessionManagerDBusAdaptor::Introspect(
dbus::MethodCall* call) {
std::string output;
if (!base::ReadFileToString(base::FilePath(kBindingsPath), &output)) {
PLOG(ERROR) << "Can't read XML bindings from disk:";
return CreateError(call, "Can't read XML bindings from disk.", "");
}
scoped_ptr<dbus::Response> response(dbus::Response::FromMethodCall(call));
dbus::MessageWriter writer(response.get());
writer.AppendString(output);
return response.Pass();
}
void SessionManagerDBusAdaptor::ExportSyncDBusMethod(
dbus::ExportedObject* object,
const std::string& method_name,
SyncDBusMethodCallMemberFunction member) {
DCHECK(object);
CHECK(object->ExportMethodAndBlock(
kSessionManagerInterface, method_name,
base::Bind(&HandleSynchronousDBusMethodCall,
base::Bind(member, base::Unretained(this)))));
}
void SessionManagerDBusAdaptor::ExportAsyncDBusMethod(
dbus::ExportedObject* object,
const std::string& method_name,
AsyncDBusMethodCallMemberFunction member) {
DCHECK(object);
CHECK(object->ExportMethodAndBlock(
kSessionManagerInterface, method_name,
base::Bind(member, base::Unretained(this))));
}
} // namespace login_manager