text: // Copyright 2016 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/sys/appmgr/runner_holder.h"
  
  #include <lib/fit/function.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  #include <utility>
  
  #include "lib/sys/cpp/termination_reason.h"
  #include "src/lib/fsl/vmo/file.h"
  #include "src/sys/appmgr/component_controller_impl.h"
  #include "src/sys/appmgr/realm.h"
  #include "src/sys/appmgr/util.h"
  
  using fuchsia::sys::TerminationReason;
  
  namespace component {
  
  RunnerHolder::RunnerHolder(std::shared_ptr<sys::ServiceDirectory> services,
                             fuchsia::sys::ComponentControllerPtr controller,
                             fuchsia::sys::LaunchInfo launch_info, Realm* realm,
                             fit::function<void()> error_handler)
      : services_(std::move(services)),
        controller_(std::move(controller)),
        error_handler_(std::move(error_handler)),
        component_id_counter_(0) {
    auto url = launch_info.url;
    realm->CreateComponent(std::move(launch_info), controller_.NewRequest(),
                           [this](std::weak_ptr<ComponentControllerImpl> component) {
                             CreateComponentCallback(component);
                           });
  
    controller_.events().OnTerminated = [this, url](int64_t return_code,
                                                    TerminationReason termination_reason) {
      if (termination_reason != TerminationReason::EXITED) {
        FXL_LOG(ERROR) << "Runner (" << url << ") terminating, reason: "
                       << sys::HumanReadableTerminationReason(termination_reason);
      }
      Cleanup();
  
      if (error_handler_) {
        error_handler_();
      }
    };
  
    services_->Connect(runner_.NewRequest());
  }
  
  RunnerHolder::~RunnerHolder() = default;
  
  void RunnerHolder::Cleanup() {
    impl_object_.reset();
    // Terminate all bridges currently owned by this runner.
    for (auto& component : components_) {
      component.second->SetTerminationReason(TerminationReason::RUNNER_TERMINATED);
    }
    components_.clear();
  }
  
  void RunnerHolder::CreateComponentCallback(std::weak_ptr<ComponentControllerImpl> component) {
    impl_object_ = component;
  
    if (auto impl = impl_object_.lock()) {
      koid_ = impl->koid();
      // update hub
      for (auto& n : components_) {
        n.second->SetParentJobId(koid_);
        impl->AddSubComponentHub(n.second->HubInfo());
      }
    }
  }
  
  void RunnerHolder::StartComponent(
      fuchsia::sys::Package package, fuchsia::sys::StartupInfo startup_info,
      fxl::RefPtr<Namespace> ns,
      fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) {
    auto url = startup_info.launch_info.url;
    const std::string args = Util::GetArgsString(startup_info.launch_info.arguments);
    auto channels = Util::BindDirectory(&startup_info.launch_info);
  
    fuchsia::sys::ComponentControllerPtr remote_controller;
    auto remote_controller_request = remote_controller.NewRequest();
  
    // TODO(anmittal): Create better unique instance id, instead of 1,2,3,4,...
    auto component = std::make_shared<ComponentBridge>(
        std::move(controller), std::move(remote_controller), this, url, std::move(args),
        Util::GetLabelFromURL(url), std::to_string(++component_id_counter_), std::move(ns),
        std::move(channels.exported_dir), std::move(channels.client_request));
  
    // update hub
    if (auto impl = impl_object_.lock()) {
      component->SetParentJobId(koid_);
      impl->AddSubComponentHub(component->HubInfo());
    }
  
    ComponentBridge* key = component.get();
    components_.emplace(key, std::move(component));
  
    runner_->StartComponent(std::move(package), std::move(startup_info),
                            std::move(remote_controller_request));
  }
  
  std::shared_ptr<ComponentBridge> RunnerHolder::ExtractComponent(ComponentBridge* controller) {
    auto it = components_.find(controller);
    if (it == components_.end()) {
      return nullptr;
    }
    auto component = std::move(it->second);
  
    // update hub
    if (auto impl = impl_object_.lock()) {
      impl->RemoveSubComponentHub(component->HubInfo());
    }
  
    components_.erase(it);
    return component;
  }
  
  }  // namespace component
  