blob: 68e6d61e11d08abaeeadec89882c7a4ffab50dbc [file] [log] [blame]
// 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 <dirent.h>
#include <sys/types.h>
#include "garnet/bin/bootstrap/app.h"
#include <zircon/process.h>
#include <zircon/processargs.h>
#include "lib/app/cpp/connect.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/fxl/logging.h"
namespace bootstrap {
namespace {
// We explicitly launch netstack because netstack registers itself as
// |/dev/socket|, which needs to happen eagerly, instead of being discovered
// via |/svc/net.Netstack|, which can happen asynchronously.
void LaunchNetstack(app::ServiceProvider* provider) {
zx::channel h1, h2;
zx::channel::create(0, &h1, &h2);
provider->ConnectToService("net.Netstack", std::move(h1));
}
// We explicitly launch wlanstack because we want it to start scanning if
// SSID is configured.
// TODO: Remove this hard-coded logic once we have a more sophisticated
// system service manager that can do this sort of thing using config files.
void LaunchWlanstack(app::ServiceProvider* provider) {
zx::channel h1, h2;
zx::channel::create(0, &h1, &h2);
provider->ConnectToService("wlan::WlanService", std::move(h1));
}
} // namespace
constexpr char kDefaultLabel[] = "boot";
constexpr char kConfigDir[] = "/system/data/bootstrap/";
App::App()
: application_context_(app::ApplicationContext::CreateFromStartupInfo()),
env_host_binding_(this) {
FXL_DCHECK(application_context_);
Config config;
char buf[PATH_MAX];
if (strlcpy(buf, kConfigDir, PATH_MAX) >= PATH_MAX) {
FXL_LOG(ERROR) << "Config directory path too long";
} else {
const size_t dir_len = strlen(buf);
DIR* cfg_dir = opendir(kConfigDir);
if (cfg_dir != NULL) {
for (dirent* cfg = readdir(cfg_dir); cfg != NULL;
cfg = readdir(cfg_dir)) {
if (strcmp(".", cfg->d_name) == 0 || strcmp("..", cfg->d_name) == 0) {
continue;
}
if (strlcat(buf, cfg->d_name, PATH_MAX) >= PATH_MAX) {
FXL_LOG(WARNING) << "Could not read config file, path too long";
continue;
}
config.ReadFrom(buf);
buf[dir_len] = '\0';
}
closedir(cfg_dir);
} else {
FXL_LOG(WARNING) << "Could not open config directory" << kConfigDir;
}
}
// Set up environment for the programs we will run.
app::ApplicationEnvironmentHostPtr env_host;
env_host_binding_.Bind(env_host.NewRequest());
application_context_->environment()->CreateNestedEnvironment(
std::move(env_host), env_.NewRequest(), env_controller_.NewRequest(),
kDefaultLabel);
env_->GetApplicationLauncher(env_launcher_.NewRequest());
// Register services.
for (auto& pair : config.TakeServices())
RegisterSingleton(pair.first, std::move(pair.second));
// Ordering note: The impl of CreateNestedEnvironment will resolve the
// delegating app loader. However, since its call back to the env host won't
// happen until the next (first) message loop iteration, we'll be set up by
// then.
RegisterAppLoaders(config.TakeAppLoaders());
// Launch startup applications.
for (auto& launch_info : config.TakeApps())
LaunchApplication(std::move(launch_info));
// TODO(abarth): Remove this hard-coded mention of netstack once netstack is
// fully converted to using service namespaces.
LaunchNetstack(&env_services_);
LaunchWlanstack(&env_services_);
}
App::~App() {}
void App::RegisterSingleton(std::string service_name,
app::ApplicationLaunchInfoPtr launch_info) {
env_services_.AddServiceForName(
fxl::MakeCopyable([
this, service_name, launch_info = std::move(launch_info),
controller = app::ApplicationControllerPtr()
](zx::channel client_handle) mutable {
FXL_VLOG(2) << "Servicing singleton service request for "
<< service_name;
auto it = services_.find(launch_info->url);
if (it == services_.end()) {
FXL_VLOG(1) << "Starting singleton " << launch_info->url
<< " for service " << service_name;
app::Services services;
auto dup_launch_info = app::ApplicationLaunchInfo::New();
dup_launch_info->url = launch_info->url;
dup_launch_info->arguments = launch_info->arguments.Clone();
dup_launch_info->service_request = services.NewRequest();
env_launcher_->CreateApplication(std::move(dup_launch_info),
controller.NewRequest());
controller.set_connection_error_handler(
[ this, url = launch_info->url, &controller ] {
FXL_LOG(ERROR) << "Singleton " << url << " died";
controller.reset(); // kills the singleton application
services_.erase(url);
});
std::tie(it, std::ignore) =
services_.emplace(launch_info->url, std::move(services));
}
it->second.ConnectToService(service_name, std::move(client_handle));
}),
service_name);
}
void App::RegisterAppLoaders(Config::ServiceMap app_loaders) {
app_loader_ = std::make_unique<DelegatingApplicationLoader>(
std::move(app_loaders), env_launcher_.get(),
application_context_
->ConnectToEnvironmentService<app::ApplicationLoader>());
env_services_.AddService<app::ApplicationLoader>(
[this](fidl::InterfaceRequest<app::ApplicationLoader> request) {
app_loader_bindings_.AddBinding(app_loader_.get(), std::move(request));
});
}
void App::LaunchApplication(app::ApplicationLaunchInfoPtr launch_info) {
FXL_VLOG(1) << "Bootstrapping application " << launch_info->url;
env_launcher_->CreateApplication(std::move(launch_info), nullptr);
}
void App::GetApplicationEnvironmentServices(
fidl::InterfaceRequest<app::ServiceProvider> environment_services) {
env_services_.AddBinding(std::move(environment_services));
}
} // namespace bootstrap