[sessionctl] Add ability to restart session

This uses basemgr to restart the current session by logging out the
current user. The authenticated user will be automatically logged back in.

TEST=manual: fx shell sessionctl restart_session

Change-Id: Iab5139b2ff5870d2d2d0a4d7e74bc275e3f2c5b9
diff --git a/bin/basemgr/basemgr_impl.cc b/bin/basemgr/basemgr_impl.cc
index 2d122a1..51ed71e 100644
--- a/bin/basemgr/basemgr_impl.cc
+++ b/bin/basemgr/basemgr_impl.cc
@@ -62,6 +62,11 @@
 
 BasemgrImpl::~BasemgrImpl() = default;
 
+void BasemgrImpl::Connect(
+    fidl::InterfaceRequest<fuchsia::modular::internal::BasemgrDebug> request) {
+  basemgr_bindings_.AddBinding(this, std::move(request));
+}
+
 void BasemgrImpl::InitializePresentation(
     fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> view_owner) {
   if (settings_.test && !settings_.enable_presenter) {
@@ -251,13 +256,13 @@
   // to.
   user_provider_impl_.Teardown(kUserProviderTimeout, [this] {
     FXL_DLOG(INFO) << "- fuchsia::modular::UserProvider down";
-      StopTokenManagerFactoryApp()->Then([this] {
-        FXL_DLOG(INFO) << "- fuchsia::auth::TokenManagerFactory down";
-        StopBaseShell()->Then([this] {
-          FXL_LOG(INFO) << "Clean Shutdown";
-          on_shutdown_();
-        });
+    StopTokenManagerFactoryApp()->Then([this] {
+      FXL_DLOG(INFO) << "- fuchsia::auth::TokenManagerFactory down";
+      StopBaseShell()->Then([this] {
+        FXL_LOG(INFO) << "Clean Shutdown";
+        on_shutdown_();
       });
+    });
   });
 }
 
@@ -526,4 +531,6 @@
       });
 }
 
+void BasemgrImpl::RestartSession() { user_provider_impl_->RestartSession(); }
+
 }  // namespace modular
diff --git a/bin/basemgr/basemgr_impl.h b/bin/basemgr/basemgr_impl.h
index 6f4ad89..be0f6ee 100644
--- a/bin/basemgr/basemgr_impl.h
+++ b/bin/basemgr/basemgr_impl.h
@@ -40,6 +40,7 @@
 // 3) Manages the lifecycle of sessions, represented as |sessionmgr| processes.
 class BasemgrImpl : fuchsia::modular::BaseShellContext,
                     fuchsia::auth::AuthenticationContextProvider,
+                    fuchsia::modular::internal::BasemgrDebug,
                     fuchsia::ui::policy::KeyboardCaptureListenerHACK,
                     modular::UserProviderImpl::Delegate {
  public:
@@ -64,6 +65,9 @@
 
   ~BasemgrImpl() override;
 
+  void Connect(
+      fidl::InterfaceRequest<fuchsia::modular::internal::BasemgrDebug> request);
+
  private:
   void InitializePresentation(
       fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> view_owner);
@@ -127,6 +131,9 @@
   // on initialization and every time the session shells are swapped.
   void UpdateSessionShellConfig();
 
+  // |BasemgrDebug|
+  void RestartSession() override;
+
   const modular::BasemgrSettings& settings_;  // Not owned nor copied.
   const std::vector<SessionShellSettings>& session_shell_settings_;
   fuchsia::modular::AppConfig session_shell_config_;
@@ -145,6 +152,7 @@
 
   AsyncHolder<UserProviderImpl> user_provider_impl_;
 
+  fidl::BindingSet<fuchsia::modular::internal::BasemgrDebug> basemgr_bindings_;
   fidl::Binding<fuchsia::modular::BaseShellContext> base_shell_context_binding_;
   fidl::Binding<fuchsia::auth::AuthenticationContextProvider>
       authentication_context_provider_binding_;
diff --git a/bin/basemgr/main.cc b/bin/basemgr/main.cc
index cf22c78..e1b89be 100644
--- a/bin/basemgr/main.cc
+++ b/bin/basemgr/main.cc
@@ -27,6 +27,8 @@
   return modular::InitializeCobalt(dispatcher, context);
 };
 
+constexpr char kBasemgrDir[] = "basemgr";
+
 int main(int argc, const char** argv) {
   auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
   if (command_line.HasOption("help")) {
@@ -55,13 +57,23 @@
   fuchsia::devicesettings::DeviceSettingsManagerPtr device_settings_manager;
   context->ConnectToEnvironmentService(device_settings_manager.NewRequest());
 
-  modular::BasemgrImpl basemgr(settings, session_shell_settings,
-                               context->launcher().get(), std::move(presenter),
-                               std::move(device_settings_manager),
-                               [&loop, &cobalt_cleanup] {
-                                 cobalt_cleanup.call();
-                                 loop.Quit();
-                               });
+  modular::BasemgrImpl basemgr(
+      settings, session_shell_settings, context->launcher().get(),
+      std::move(presenter), std::move(device_settings_manager),
+      [&loop, &cobalt_cleanup, &context] {
+        cobalt_cleanup.call();
+        context->outgoing().debug_dir()->RemoveEntry(kBasemgrDir);
+        loop.Quit();
+      });
+  context->outgoing().debug_dir()->AddEntry(
+      kBasemgrDir,
+      fbl::AdoptRef(new fs::Service([&basemgr](zx::channel channel) {
+        fidl::InterfaceRequest<fuchsia::modular::internal::BasemgrDebug>
+            request(std::move(channel));
+        basemgr.Connect(std::move(request));
+        return ZX_OK;
+      })));
+
   loop.Run();
 
   return 0;
diff --git a/bin/basemgr/user_provider_impl.cc b/bin/basemgr/user_provider_impl.cc
index 7b9475a..7bfb4e4 100644
--- a/bin/basemgr/user_provider_impl.cc
+++ b/bin/basemgr/user_provider_impl.cc
@@ -492,10 +492,10 @@
   auto account_id = account ? account->id.get() : GetRandomId();
   FXL_DLOG(INFO) << "Login() User:" << account_id;
 
-    // Instead of passing token_manager_factory all the way to agents and
-    // runners with all auth provider configurations, send two
-    // |fuchsia::auth::TokenManager| handles, one for ledger and one for agents
-    // for the given user account |account_id|.
+  // Instead of passing token_manager_factory all the way to agents and
+  // runners with all auth provider configurations, send two
+  // |fuchsia::auth::TokenManager| handles, one for ledger and one for agents
+  // for the given user account |account_id|.
   fuchsia::auth::TokenManagerPtr ledger_token_manager =
       CreateTokenManager(account_id);
   fuchsia::auth::TokenManagerPtr agent_token_manager =
@@ -508,10 +508,10 @@
 
   auto controller = std::make_unique<UserControllerImpl>(
       launcher_, CloneStruct(sessionmgr_), CloneStruct(session_shell_),
-      CloneStruct(story_shell_),
-      std::move(ledger_token_manager), std::move(agent_token_manager),
-      std::move(account), std::move(view_owner), std::move(service_provider),
-      std::move(params.user_controller), [this](UserControllerImpl* c) {
+      CloneStruct(story_shell_), std::move(ledger_token_manager),
+      std::move(agent_token_manager), std::move(account), std::move(view_owner),
+      std::move(service_provider), std::move(params.user_controller),
+      [this](UserControllerImpl* c) {
         user_controllers_.erase(c);
         delegate_->DidLogout();
       });
@@ -534,4 +534,20 @@
   return user_controller->SwapSessionShell(std::move(session_shell_config));
 }
 
+void UserProviderImpl::RestartSession() {
+  // Callback to log the user back in if login is not automatic
+  auto login = [this] {
+    if (user_controllers_.size() < 1 && users_storage_) {
+      auto account = Convert(users_storage_->users()->Get(0));
+
+      fuchsia::modular::UserLoginParams params;
+      params.account_id = account->id;
+      Login(std::move(params));
+    }
+  };
+
+  // Log the user out to shut down sessionmgr
+  user_controllers_.begin()->first->Logout(login);
+}
+
 }  // namespace modular
diff --git a/bin/basemgr/user_provider_impl.h b/bin/basemgr/user_provider_impl.h
index a7839ad..815315b 100644
--- a/bin/basemgr/user_provider_impl.h
+++ b/bin/basemgr/user_provider_impl.h
@@ -75,6 +75,10 @@
   FuturePtr<> SwapSessionShell(
       fuchsia::modular::AppConfig session_shell_config);
 
+  // Restarts the current session by logging out the current user and logging
+  // that user back in.
+  void RestartSession();
+
   // |fuchsia::modular::UserProvider|, also called by |basemgr_impl|.
   void Login(fuchsia::modular::UserLoginParams params) override;
 
diff --git a/bin/sessionctl/BUILD.gn b/bin/sessionctl/BUILD.gn
index 26c7fcd..7bdf776 100644
--- a/bin/sessionctl/BUILD.gn
+++ b/bin/sessionctl/BUILD.gn
@@ -20,6 +20,7 @@
     "//garnet/public/lib/fxl",
     "//peridot/lib/rapidjson",
     "//peridot/public/fidl/fuchsia.modular",
+    "//peridot/public/fidl/fuchsia.modular.internal",
     "//peridot/public/lib/async/cpp:future",
     "//zircon/public/lib/async-loop-cpp",
     "//zircon/public/lib/fdio",
@@ -45,6 +46,7 @@
   deps = [
     "//peridot/lib/rapidjson",
     "//peridot/public/fidl/fuchsia.modular",
+    "//peridot/public/fidl/fuchsia.modular.internal",
     "//peridot/public/lib/async/cpp:future",
     "//zircon/public/lib/async-loop-cpp",
   ]
diff --git a/bin/sessionctl/main.cc b/bin/sessionctl/main.cc
index 900a4b9..4ee5456 100644
--- a/bin/sessionctl/main.cc
+++ b/bin/sessionctl/main.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include <fuchsia/modular/cpp/fidl.h>
+#include <fuchsia/modular/internal/cpp/fidl.h>
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/async/cpp/future.h>
 #include <lib/async/cpp/task.h>
@@ -28,44 +29,11 @@
 using ::fuchsia::modular::PuppetMaster;
 using ::fuchsia::modular::PuppetMasterPtr;
 
-struct ActiveSession {
+struct DebugService {
   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> <argument>
 Example:
@@ -124,10 +92,47 @@
     The name of the story.
 
 list_stories
-  List all the stories in the current session.)";
+  List all the stories in the current session.
+
+restart_session
+  Restarts the current session.)";
 }
 
-PuppetMasterPtr ConnectToPuppetMaster(const ActiveSession& session) {
+void FindDebugServicesForPath(const char* glob_str, const char* regex_str,
+                              std::vector<DebugService>* services) {
+  glob_t globbuf;
+  bool service_exists = glob(glob_str, 0, nullptr, &globbuf) == 0;
+  std::regex name_regex(regex_str);
+  if (service_exists) {
+    for (size_t i = 0; i < globbuf.gl_pathc; ++i) {
+      DebugService 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];
+      services->push_back(std::move(s));
+    }
+    globfree(&globbuf);
+  }
+}
+
+// Returns a list of all running sessions.
+std::vector<DebugService> 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<DebugService> sessions;
+  FindDebugServicesForPath("/hub/c/sessionmgr.cmx/*/out/debug/sessionctl",
+                           kRegex, &sessions);
+
+  FindDebugServicesForPath(
+      "/hub/r/sys/*/c/sessionmgr.cmx/*/out/debug/sessionctl", kRegex,
+      &sessions);
+  return sessions;
+}
+
+PuppetMasterPtr ConnectToPuppetMaster(const DebugService& session) {
   PuppetMasterPtr puppet_master;
   auto request = puppet_master.NewRequest().TakeChannel();
   std::string service_path = session.service_path + "/" + PuppetMaster::Name_;
@@ -138,6 +143,25 @@
   return puppet_master;
 }
 
+fuchsia::modular::internal::BasemgrDebugPtr ConnectToBasemgr() {
+  fuchsia::modular::internal::BasemgrDebugPtr basemgr;
+  auto request = basemgr.NewRequest().TakeChannel();
+
+  std::vector<DebugService> services;
+  FindDebugServicesForPath("/hub/c/basemgr/*/out/debug/basemgr", "basemgr",
+                           &services);
+  // There should only be one basemgr
+  FXL_CHECK(services.size() == 1);
+
+  std::string service_path = services[0].service_path;
+  if (fdio_service_connect(service_path.c_str(), request.get()) != ZX_OK) {
+    FXL_LOG(FATAL) << "Could not connect to basemgr service in "
+                   << service_path;
+  }
+
+  return basemgr;
+}
+
 int main(int argc, const char** argv) {
   async::Loop loop(&kAsyncLoopConfigAttachToThread);
 
@@ -165,10 +189,13 @@
     std::cout << std::endl;
   }
 
+  auto basemgr = ConnectToBasemgr();
+
   // 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(); });
+
+  modular::SessionCtlApp app(basemgr.get(), puppet_master.get(), logger,
+                             loop.dispatcher(), [&loop] { loop.Quit(); });
 
   std::string parsing_error = app.ExecuteCommand(cmd, command_line);
   if (parsing_error == modular::kGetUsageErrorString) {
diff --git a/bin/sessionctl/session_ctl_app.cc b/bin/sessionctl/session_ctl_app.cc
index f05a8e6..e9ffbb6 100644
--- a/bin/sessionctl/session_ctl_app.cc
+++ b/bin/sessionctl/session_ctl_app.cc
@@ -8,10 +8,12 @@
 namespace modular {
 
 SessionCtlApp::SessionCtlApp(
+    fuchsia::modular::internal::BasemgrDebug* const basemgr,
     fuchsia::modular::PuppetMaster* const puppet_master,
     const modular::Logger& logger, async_dispatcher_t* const dispatcher,
     const std::function<void()>& on_command_executed)
-    : puppet_master_(puppet_master),
+    : basemgr_(basemgr),
+      puppet_master_(puppet_master),
       logger_(logger),
       dispatcher_(dispatcher),
       on_command_executed_(on_command_executed) {}
@@ -26,6 +28,8 @@
     return ExecuteDeleteStoryCommand(command_line);
   } else if (cmd == kListStoriesCommandString) {
     return ExecuteListStoriesCommand();
+  } else if (cmd == kRestartSessionCommandString) {
+    return ExecuteRestartSessionCommand();
   } else {
     return kGetUsageErrorString;
   }
@@ -142,6 +146,14 @@
   return "";
 }
 
+std::string SessionCtlApp::ExecuteRestartSessionCommand() {
+  basemgr_->RestartSession();
+  logger_.Log(kRestartSessionCommandString, nullptr);
+  on_command_executed_();
+
+  return "";
+}
+
 fuchsia::modular::StoryCommand SessionCtlApp::MakeFocusStoryCommand() {
   fuchsia::modular::StoryCommand command;
   fuchsia::modular::SetFocusState set_focus_state;
@@ -240,16 +252,4 @@
   return fut;
 }
 
-std::string SessionCtlApp::GenerateMissingFlagString(
-    const std::vector<std::string>& missing_flags) {
-  std::string error;
-  if (!missing_flags.empty()) {
-    error += "flags missing:";
-    for (auto flag : missing_flags) {
-      error += " --" + flag;
-    }
-  }
-  return error;
-}
-
 }  // namespace modular
diff --git a/bin/sessionctl/session_ctl_app.h b/bin/sessionctl/session_ctl_app.h
index 58e39e0..20fabb9 100644
--- a/bin/sessionctl/session_ctl_app.h
+++ b/bin/sessionctl/session_ctl_app.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include <fuchsia/modular/cpp/fidl.h>
+#include <fuchsia/modular/internal/cpp/fidl.h>
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/async/cpp/future.h>
 #include <lib/async/cpp/task.h>
@@ -26,29 +27,32 @@
  public:
   // Constructs a SessionCtlApp which can read and execute session commands.
   // |puppet_master| The interface used to execute commands.
+  // |basemgr| The basemgr to use to restart sessions.
   // |command_line| The command line used to read commands and arguments.
   // |logger| The logger used to log the results of commands.
   // |dispatcher| The dispatcher which is used to post the command tasks.
   // |on_command_executed| A callback which is called whenever a command has
   // finished executing.
-  explicit SessionCtlApp(fuchsia::modular::PuppetMaster* const puppet_master,
-                         const modular::Logger& logger,
-                         async_dispatcher_t* const dispatcher,
-                         const std::function<void()>& on_command_executed);
+  explicit SessionCtlApp(
+      fuchsia::modular::internal::BasemgrDebug* const basemgr,
+      fuchsia::modular::PuppetMaster* const puppet_master,
+      const modular::Logger& logger, async_dispatcher_t* const dispatcher,
+      const std::function<void()>& on_command_executed);
 
   // Dispatches the |cmd| and returns an empty string on success, "GetUsage" if
   // |cmd| is not valid, and a string of missing flags on failure.
   std::string ExecuteCommand(std::string cmd,
                              const fxl::CommandLine& command_line);
 
+ private:
   // Executes the respective command and returns an empty string on success and
   // a string of missing flags on failure.
   std::string ExecuteAddModCommand(const fxl::CommandLine& command_line);
   std::string ExecuteRemoveModCommand(const fxl::CommandLine& command_line);
   std::string ExecuteDeleteStoryCommand(const fxl::CommandLine& command_line);
   std::string ExecuteListStoriesCommand();
+  std::string ExecuteRestartSessionCommand();
 
- private:
   // Focus the story to which the mod we are adding belongs.
   fuchsia::modular::StoryCommand MakeFocusStoryCommand();
 
@@ -82,6 +86,7 @@
   std::string GenerateMissingFlagString(
       const std::vector<std::string>& missing_flags);
 
+  fuchsia::modular::internal::BasemgrDebug* const basemgr_;
   fuchsia::modular::PuppetMaster* const puppet_master_;
   fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_;
   const fxl::CommandLine command_line_;
diff --git a/bin/sessionctl/session_ctl_app_unittest.cc b/bin/sessionctl/session_ctl_app_unittest.cc
index e40575c..2b998e5 100644
--- a/bin/sessionctl/session_ctl_app_unittest.cc
+++ b/bin/sessionctl/session_ctl_app_unittest.cc
@@ -37,8 +37,8 @@
   SessionCtlApp CreateSessionCtl(fxl::CommandLine command_line) {
     logger_ =
         std::make_unique<Logger>(command_line.HasOption(kJsonOutFlagString));
-    SessionCtlApp sessionctl(puppet_master_impl_.get(), *(logger_.get()),
-                             async_get_default_dispatcher(),
+    SessionCtlApp sessionctl(nullptr /* basemgr */, puppet_master_impl_.get(),
+                             *(logger_.get()), async_get_default_dispatcher(),
                              [&] { done_ = true; });
     return sessionctl;
   }
diff --git a/bin/sessionctl/session_ctl_constants.h b/bin/sessionctl/session_ctl_constants.h
index 890a223..9bcc580 100644
--- a/bin/sessionctl/session_ctl_constants.h
+++ b/bin/sessionctl/session_ctl_constants.h
@@ -12,6 +12,7 @@
 constexpr char kDeleteStoryCommandString[] = "delete_story";
 constexpr char kRemoveModCommandString[] = "remove_mod";
 constexpr char kListStoriesCommandString[] = "list_stories";
+constexpr char kRestartSessionCommandString[] = "restart_session";
 
 // Flags to pass to SessionCtlApp.
 constexpr char kJsonOutFlagString[] = "json_out";
diff --git a/public/fidl/fuchsia.modular.internal/BUILD.gn b/public/fidl/fuchsia.modular.internal/BUILD.gn
index 06a0781..3391903 100644
--- a/public/fidl/fuchsia.modular.internal/BUILD.gn
+++ b/public/fidl/fuchsia.modular.internal/BUILD.gn
@@ -8,6 +8,7 @@
   cpp_legacy_callbacks = true
 
   sources = [
+    "basemgr_debug.fidl",
     "sessionmgr.fidl",
     "story_data.fidl",
   ]
diff --git a/public/fidl/fuchsia.modular.internal/basemgr_debug.fidl b/public/fidl/fuchsia.modular.internal/basemgr_debug.fidl
new file mode 100644
index 0000000..91efe4d
--- /dev/null
+++ b/public/fidl/fuchsia.modular.internal/basemgr_debug.fidl
@@ -0,0 +1,12 @@
+// 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.
+
+library fuchsia.modular.internal;
+
+// A debug interface exposed by `basemgr` to allow developer tools to control
+// state within the `basemgr` process.
+[Discoverable]
+interface BasemgrDebug {
+    RestartSession();
+};