| // 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 <dirent.h> |
| #include <glob.h> |
| #include <sys/types.h> |
| #include <iostream> |
| #include <regex> |
| #include <string> |
| #include <vector> |
| |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async/cpp/future.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/fdio/util.h> |
| #include <lib/fxl/command_line.h> |
| #include <lib/fxl/files/file.h> |
| #include <lib/fxl/log_settings_command_line.h> |
| #include <lib/fxl/strings/string_printf.h> |
| #include "lib/fxl/functional/make_copyable.h" |
| |
| #include "peridot/bin/sessionctl/logger.h" |
| #include "peridot/bin/sessionctl/session_ctl_app.h" |
| #include "peridot/bin/sessionctl/session_ctl_constants.h" |
| |
| using ::fuchsia::modular::PuppetMaster; |
| using ::fuchsia::modular::PuppetMasterPtr; |
| |
| struct ActiveSession { |
| std::string name; |
| std::string service_path; |
| }; |
| |
| void FindSessionsForPath(const char* glob_str, const char* regex_str, |
| std::vector<ActiveSession>* sessions) { |
| glob_t globbuf; |
| bool sessionmgr_exists = glob(glob_str, 0, nullptr, &globbuf) == 0; |
| std::regex name_regex(regex_str); |
| if (sessionmgr_exists) { |
| for (size_t i = 0; i < globbuf.gl_pathc; ++i) { |
| ActiveSession s; |
| s.service_path = globbuf.gl_pathv[i]; |
| std::smatch match; |
| FXL_CHECK(std::regex_search(s.service_path, match, name_regex)) |
| << s.service_path; |
| s.name = match[1]; |
| sessions->push_back(std::move(s)); |
| } |
| globfree(&globbuf); |
| } |
| } |
| |
| // Returns a list of all running sessions. |
| std::vector<ActiveSession> FindAllSessions() { |
| const char kRegex[] = "/sessionmgr.cmx/(\\d+)"; |
| // See peridot/bin/sessionmgr/sessionmgr_impl.cc's definition of |
| // kSessionCtlDir for "sessionctl". These must match. |
| std::vector<ActiveSession> sessions; |
| FindSessionsForPath("/hub/c/sessionmgr.cmx/*/out/debug/sessionctl", kRegex, |
| &sessions); |
| |
| FindSessionsForPath("/hub/r/sys/*/c/sessionmgr.cmx/*/out/debug/sessionctl", |
| kRegex, &sessions); |
| return sessions; |
| } |
| |
| std::string GetUsage() { |
| return R"(sessionctl <flags> <command> |
| Example: |
| sessionctl --mod_url=slider_mod --mod_name=mod1 --story_name=story1 |
| --focus_mod --focus_story add_mod |
| |
| sessionctl --mod_name=mod1 --story_name=story1 remove_mod |
| |
| <flags> |
| --story_name=STORY_NAME |
| --mod_name=MOD_NAME |
| --mod_url=MOD_URL |
| mods have a unique "mod_url". |
| It is the mod package's name. |
| In BUILD.gn fuchsia_package_name = "mod_url" or mod_url comes from |
| flutter_app("mod_url") when there is no fuchsia_package_name set. |
| --focus_mod |
| If flag is set then the mod is focused. |
| --focus_story |
| If flag is set then the story is focused. |
| --json_out |
| If flag is set output json for consuming instead of text. |
| |
| <command> |
| add_mod |
| Add new mod or update an existing mod if found. |
| required: --story_name, --mod_name, --mod_url |
| optional: --focus_mod, --focus_story, --json_out |
| |
| remove_mod |
| Remove the mod. |
| required: --mod_name, --story_name |
| |
| delete_story |
| Delete the story. |
| required: --story_name |
| optional: --json_out |
| |
| list_stories |
| List all the stories in the current session.)"; |
| } |
| |
| PuppetMasterPtr ConnectToPuppetMaster(const ActiveSession& session) { |
| PuppetMasterPtr puppet_master; |
| auto request = puppet_master.NewRequest().TakeChannel(); |
| std::string service_path = session.service_path + "/" + PuppetMaster::Name_; |
| if (fdio_service_connect(service_path.c_str(), request.get()) != ZX_OK) { |
| FXL_LOG(FATAL) << "Could not connect to PuppetMaster service in " |
| << session.service_path; |
| } |
| return puppet_master; |
| } |
| |
| int main(int argc, const char** argv) { |
| async::Loop loop(&kAsyncLoopConfigAttachToThread); |
| |
| const auto command_line = fxl::CommandLineFromArgcArgv(argc, argv); |
| fxl::SetLogSettingsFromCommandLine(command_line); |
| const auto& positional_args = command_line.positional_args(); |
| const auto& cmd = positional_args.empty() ? "" : positional_args[0]; |
| |
| const modular::Logger logger( |
| command_line.HasOption(modular::kJsonOutFlagString)); |
| auto sessions = FindAllSessions(); |
| |
| if (sessions.empty()) { |
| logger.LogError( |
| cmd, "Could not find a running sessionmgr. Is the user logged in?"); |
| return 1; |
| } |
| |
| if (!command_line.HasOption(modular::kJsonOutFlagString)) { |
| std::cout << "Found the following sessions:\n\n"; |
| for (const auto& session : sessions) { |
| std::cout << "\t" << session.name << ": " << session.service_path |
| << std::endl; |
| } |
| std::cout << std::endl; |
| } |
| |
| // To get a PuppetMaster service for a session, use the following code: |
| PuppetMasterPtr puppet_master = ConnectToPuppetMaster(sessions[0]); |
| modular::SessionCtlApp app(puppet_master.get(), logger, loop.dispatcher(), |
| [&loop] { loop.Quit(); }); |
| |
| std::string parsing_error = app.ExecuteCommand(cmd, command_line); |
| if (parsing_error == modular::kGetUsageErrorString) { |
| // Print help if command doesn't match a valid command. |
| std::cout << GetUsage() << std::endl; |
| return 1; |
| } |
| |
| if (!parsing_error.empty()) { |
| return 1; |
| } |
| |
| loop.Run(); |
| |
| return 0; |
| } |