blob: 87065e9c7509418f053852c40d132525beef055c [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "src/modular/bin/sessionctl/session_ctl_app.h"
#include <regex>
#include "src/modular/bin/sessionctl/session_ctl_constants.h"
namespace modular {
SessionCtlApp::SessionCtlApp(fuchsia::modular::internal::BasemgrDebugPtr basemgr,
fuchsia::modular::PuppetMaster* const puppet_master,
const modular::Logger& logger, async_dispatcher_t* const dispatcher)
: basemgr_(std::move(basemgr)),
puppet_master_(puppet_master),
logger_(logger),
dispatcher_(dispatcher) {}
void SessionCtlApp::ExecuteCommand(std::string cmd, const fxl::CommandLine& command_line,
fit::function<void(std::string error)> done) {
if (cmd == kAddModCommandString) {
ExecuteAddModCommand(command_line, std::move(done));
} else if (cmd == kRemoveModCommandString) {
ExecuteRemoveModCommand(command_line, std::move(done));
} else if (cmd == kDeleteStoryCommandString) {
ExecuteDeleteStoryCommand(command_line, std::move(done));
} else if (cmd == kDeleteAllStoriesCommandString) {
ExecuteDeleteAllStoriesCommand(std::move(done));
} else if (cmd == kListStoriesCommandString) {
ExecuteListStoriesCommand(std::move(done));
} else if (cmd == kRestartSessionCommandString) {
ExecuteRestartSessionCommand(std::move(done));
} else if (cmd == kSelectNextSessionCommandString) {
ExecuteSelectNextSessionShellCommand(command_line, std::move(done));
} else if (cmd == kShutdownBasemgrCommandString) {
ExecuteShutdownBasemgrCommand(command_line, std::move(done));
} else {
done(kGetUsageErrorString);
}
}
void SessionCtlApp::ExecuteRemoveModCommand(const fxl::CommandLine& command_line,
fit::function<void(std::string)> done) {
if (command_line.positional_args().size() == 1) {
auto parsing_error = "Missing MOD_NAME. Ex: sessionctl remove_mod slider_mod";
logger_.LogError(kRemoveModCommandString, parsing_error);
done(parsing_error);
return;
}
// Get the mod name and default the story name to the mod name's hash
std::string mod_name = command_line.positional_args().at(1);
if (mod_name.find(":") == std::string::npos) {
mod_name = fxl::StringPrintf(kFuchsiaPkgPath, mod_name.c_str(), mod_name.c_str());
}
std::string story_name = std::to_string(std::hash<std::string>{}(mod_name));
// If the story_name flag isn't set, the story name will remain defaulted to
// the mod name
command_line.GetOptionValue(kStoryNameFlagString, &story_name);
auto commands = MakeRemoveModCommands(mod_name);
std::map<std::string, std::string> params = {{kModNameFlagString, mod_name},
{kStoryNameFlagString, story_name}};
puppet_master_->ControlStory(story_name, story_puppet_master_.NewRequest());
PostTaskExecuteStoryCommand(kRemoveModCommandString, std::move(commands), params,
std::move(done));
}
void SessionCtlApp::ExecuteAddModCommand(const fxl::CommandLine& command_line,
fit::function<void(std::string)> done) {
if (command_line.positional_args().size() == 1) {
auto parsing_error = "Missing MOD_URL. Ex: sessionctl add_mod slider_mod";
logger_.LogError(kAddModCommandString, parsing_error);
done(parsing_error);
return;
}
// Get the mod url and default the mod name and story name to the mod url
std::string mod_url = command_line.positional_args().at(1);
// If there's not a colon, resolve to the fuchsia package path
if (mod_url.find(":") == std::string::npos) {
mod_url = fxl::StringPrintf(kFuchsiaPkgPath, mod_url.c_str(), mod_url.c_str());
}
std::string mod_name = mod_url;
std::string story_name = std::to_string(std::hash<std::string>{}(mod_url));
if (command_line.HasOption(kStoryNameFlagString)) {
command_line.GetOptionValue(kStoryNameFlagString, &story_name);
// regex from src/sys/appmgr/realm.cc:168
std::regex story_name_regex("[0-9a-zA-Z\\.\\-_:#]+");
std::smatch story_name_match;
if (!std::regex_search(story_name, story_name_match, story_name_regex)) {
auto parsing_error = "Bad characters in story_name: " + story_name;
logger_.LogError(kStoryNameFlagString, parsing_error);
done(parsing_error);
return;
}
} else {
std::cout << "Using auto-generated --story_name value of " << story_name << std::endl;
}
command_line.GetOptionValue(kModNameFlagString, &mod_name);
if (!command_line.HasOption(kModNameFlagString)) {
std::cout << "Using auto-generated --mod_name value of " << mod_name << std::endl;
}
auto commands = MakeAddModCommands(mod_url, mod_name);
// Focus the mod and story by default
std::string focus_mod;
command_line.GetOptionValue(kFocusModFlagString, &focus_mod);
if (focus_mod == "" || focus_mod == "true") {
commands.push_back(MakeFocusModCommand(mod_name));
}
std::string focus_story;
command_line.GetOptionValue(kFocusStoryFlagString, &focus_story);
if (focus_story == "" || focus_story == "true") {
commands.push_back(MakeFocusStoryCommand());
}
std::map<std::string, std::string> params = {{kModUrlFlagString, mod_url},
{kModNameFlagString, mod_name},
{kStoryNameFlagString, story_name}};
puppet_master_->ControlStory(story_name, story_puppet_master_.NewRequest());
PostTaskExecuteStoryCommand(kAddModCommandString, std::move(commands), params, std::move(done));
}
void SessionCtlApp::ExecuteDeleteStoryCommand(const fxl::CommandLine& command_line,
fit::function<void(std::string)> done) {
if (command_line.positional_args().size() == 1) {
auto parsing_error = "Missing STORY_NAME. Ex. sessionctl delete_story story";
logger_.LogError(kStoryNameFlagString, parsing_error);
done(parsing_error);
return;
}
// Get the story name
std::string story_name = command_line.positional_args().at(1);
std::map<std::string, std::string> params = {{kStoryNameFlagString, story_name}};
async::PostTask(dispatcher_, [this, story_name, params, done = std::move(done)]() mutable {
puppet_master_->GetStories([this, story_name, params, done = std::move(done)](
std::vector<std::string> story_names) mutable {
auto story_exists = std::find(story_names.begin(), story_names.end(), story_name);
if (story_exists != story_names.end()) {
puppet_master_->DeleteStory(story_name, [] {});
logger_.Log(kDeleteStoryCommandString, params);
} else {
done("Non-existent story_name " + story_name);
return;
}
done("");
});
});
}
void SessionCtlApp::ExecuteDeleteAllStoriesCommand(fit::function<void(std::string)> done) {
async::PostTask(dispatcher_, [this, done = std::move(done)]() mutable {
puppet_master_->GetStories(
[this, done = std::move(done)](std::vector<std::string> story_names) {
for (auto story : story_names) {
puppet_master_->DeleteStory(story, [] {});
}
logger_.Log(kDeleteAllStoriesCommandString, std::move(story_names));
done("");
});
});
}
void SessionCtlApp::ExecuteListStoriesCommand(fit::function<void(std::string)> done) {
async::PostTask(dispatcher_, [this, done = std::move(done)]() mutable {
puppet_master_->GetStories(
[this, done = std::move(done)](std::vector<std::string> story_names) {
logger_.Log(kListStoriesCommandString, std::move(story_names));
done("");
});
});
}
void SessionCtlApp::ExecuteRestartSessionCommand(fit::function<void(std::string)> done) {
basemgr_->RestartSession([this, done = std::move(done)]() {
logger_.Log(kRestartSessionCommandString, std::vector<std::string>());
done("");
});
}
void SessionCtlApp::ExecuteSelectNextSessionShellCommand(const fxl::CommandLine& command_line,
fit::function<void(std::string)> done) {
basemgr_->SelectNextSessionShell([this, done = std::move(done)]() {
logger_.Log(kSelectNextSessionCommandString, std::vector<std::string>());
done("");
});
}
void SessionCtlApp::ExecuteShutdownBasemgrCommand(const fxl::CommandLine& command_line,
fit::function<void(std::string)> done) {
if (basemgr_) {
basemgr_->Shutdown();
basemgr_.set_error_handler([done = std::move(done)](zx_status_t status) { done(""); });
return;
}
done("Could not find a running basemgr. Is it running?");
}
fuchsia::modular::StoryCommand SessionCtlApp::MakeFocusStoryCommand() {
fuchsia::modular::StoryCommand command;
fuchsia::modular::SetFocusState set_focus_state;
set_focus_state.focused = true;
command.set_set_focus_state(std::move(set_focus_state));
return command;
}
fuchsia::modular::StoryCommand SessionCtlApp::MakeFocusModCommand(const std::string& mod_name) {
fuchsia::modular::StoryCommand command;
fuchsia::modular::FocusMod focus_mod;
focus_mod.mod_name_transitional = mod_name;
command.set_focus_mod(std::move(focus_mod));
return command;
}
std::vector<fuchsia::modular::StoryCommand> SessionCtlApp::MakeAddModCommands(
const std::string& mod_url, const std::string& mod_name) {
fuchsia::modular::Intent intent;
intent.handler = mod_url;
std::vector<fuchsia::modular::StoryCommand> commands;
fuchsia::modular::StoryCommand command;
// Add command to add or update the mod (it will be updated if the mod_name
// already exists in the story).
fuchsia::modular::AddMod add_mod;
add_mod.mod_name_transitional = mod_name;
intent.Clone(&add_mod.intent);
// TODO(MI4-953): Sessionctl takes in inital intent and other fields.
command.set_add_mod(std::move(add_mod));
commands.push_back(std::move(command));
return commands;
}
std::vector<fuchsia::modular::StoryCommand> SessionCtlApp::MakeRemoveModCommands(
const std::string& mod_name) {
std::vector<fuchsia::modular::StoryCommand> commands;
fuchsia::modular::StoryCommand command;
fuchsia::modular::RemoveMod remove_mod;
remove_mod.mod_name_transitional = mod_name;
command.set_remove_mod(std::move(remove_mod));
commands.push_back(std::move(command));
return commands;
}
void SessionCtlApp::PostTaskExecuteStoryCommand(
const std::string command_name, std::vector<fuchsia::modular::StoryCommand> commands,
std::map<std::string, std::string> params, fit::function<void(std::string)> done) {
async::PostTask(dispatcher_, [this, command_name, commands = std::move(commands), params,
done = std::move(done)]() mutable {
ExecuteStoryCommand(std::move(commands), params.at(kStoryNameFlagString))
->Then([this, command_name, params, done = std::move(done)](bool has_error,
std::string result) {
std::string error = "";
if (has_error) {
error = result;
logger_.LogError(command_name, result);
} else {
auto params_copy = params;
params_copy.emplace(kStoryIdFlagString, result);
logger_.Log(command_name, params_copy);
}
done(error);
});
});
}
modular::FuturePtr<bool, std::string> SessionCtlApp::ExecuteStoryCommand(
std::vector<fuchsia::modular::StoryCommand> commands, const std::string& story_name) {
story_puppet_master_->Enqueue(std::move(commands));
auto fut = modular::Future<bool, std::string>::Create("Sessionctl StoryPuppetMaster::Execute");
story_puppet_master_->Execute([fut](fuchsia::modular::ExecuteResult result) mutable {
if (result.status == fuchsia::modular::ExecuteStatus::OK) {
fut->Complete(false, result.story_id->c_str());
} else {
std::string error = fxl::StringPrintf("Puppet master returned status: %d and error: %s",
(uint32_t)result.status, result.error_message->c_str());
FXL_LOG(WARNING) << error << std::endl;
fut->Complete(true, std::move(error));
}
});
return fut;
}
} // namespace modular