blob: 6855025723153262a1c00860c1cadac2f352125a [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.
use {
failure::{Error, ResultExt},
fidl_fuchsia_io::DirectoryProxy,
fidl_fuchsia_pkg::PackageCacheMarker,
fuchsia_async as fasync,
fuchsia_component::client::connect_to_service,
fuchsia_component::server::ServiceFs,
fuchsia_inspect as inspect,
fuchsia_syslog::{self, fx_log_err, fx_log_info},
futures::{StreamExt, TryFutureExt},
parking_lot::RwLock,
std::{io, sync::Arc},
sysconfig_client,
};
mod amber_connector;
mod cache;
mod config;
mod experiment;
mod font_package_manager;
mod repository_manager;
mod repository_service;
mod resolver_service;
mod rewrite_manager;
mod rewrite_service;
#[cfg(test)]
mod test_util;
#[allow(dead_code)]
mod queue;
use crate::amber_connector::AmberConnector;
use crate::cache::PackageCache;
use crate::experiment::Experiments;
use crate::font_package_manager::{FontPackageManager, FontPackageManagerBuilder};
use crate::repository_manager::{RepositoryManager, RepositoryManagerBuilder};
use crate::repository_service::RepositoryService;
use crate::rewrite_manager::{RewriteManager, RewriteManagerBuilder};
use crate::rewrite_service::RewriteService;
const SERVER_THREADS: usize = 2;
const STATIC_REPO_DIR: &str = "/config/data/repositories";
const DYNAMIC_REPO_PATH: &str = "/data/repositories.json";
const STATIC_RULES_PATH: &str = "/config/data/rewrites.json";
const DYNAMIC_RULES_PATH: &str = "/data/rewrites.json";
const STATIC_FONT_REGISTRY_PATH: &str = "/config/data/font_packages.json";
fn main() -> Result<(), Error> {
fuchsia_syslog::init_with_tags(&["pkg_resolver"]).expect("can't init logger");
fx_log_info!("starting package resolver");
let mut executor = fasync::Executor::new().context("error creating executor")?;
let config = config::Config::load_from_config_data_or_default();
let pkg_cache =
connect_to_service::<PackageCacheMarker>().context("error connecting to package cache")?;
let pkgfs_install = connect_to_pkgfs("install").context("error connecting to pkgfs/install")?;
let pkgfs_needs = connect_to_pkgfs("needs").context("error connecting to pkgfs/needs")?;
let cache = PackageCache::new(pkg_cache, pkgfs_install, pkgfs_needs);
let inspector = fuchsia_inspect::Inspector::new();
let rewrite_inspect_node = inspector.root().create_child("rewrite_manager");
let experiment_inspect_node = inspector.root().create_child("experiments");
let amber_connector = AmberConnector::new();
let experiment_state = Arc::new(RwLock::new(experiment::State::new(experiment_inspect_node)));
let experiments = Arc::clone(&experiment_state).into();
let font_package_manager = Arc::new(load_font_package_manager());
let repo_manager = Arc::new(RwLock::new(load_repo_manager(amber_connector, experiments)));
let rewrite_manager = Arc::new(RwLock::new(load_rewrite_manager(
rewrite_inspect_node,
&repo_manager.read(),
config.disable_dynamic_configuration(),
)));
let resolver_cb = {
// Capture a clone of repo and rewrite manager's Arc so the new client callback has a copy
// from which to make new clones.
let repo_manager = Arc::clone(&repo_manager);
let rewrite_manager = Arc::clone(&rewrite_manager);
let cache = cache.clone();
move |stream| {
fasync::spawn(
resolver_service::run_resolver_service(
Arc::clone(&rewrite_manager),
Arc::clone(&repo_manager),
cache.clone(),
stream,
)
.unwrap_or_else(|e| fx_log_err!("failed to spawn {:?}", e)),
)
}
};
let font_resolver_fb = {
let repo_manager = Arc::clone(&repo_manager);
let rewrite_manager = Arc::clone(&rewrite_manager);
let cache = cache.clone();
move |stream| {
fasync::spawn(
resolver_service::run_font_resolver_service(
Arc::clone(&font_package_manager),
Arc::clone(&rewrite_manager),
Arc::clone(&repo_manager),
cache.clone(),
stream,
)
.unwrap_or_else(|e| fx_log_err!("Failed to spawn font_resolver_service {:?}", e)),
)
}
};
let repo_cb = move |stream| {
let repo_manager = Arc::clone(&repo_manager);
fasync::spawn(
async move {
let mut repo_service = RepositoryService::new(repo_manager);
repo_service.run(stream).await
}
.unwrap_or_else(|e| fx_log_err!("error encountered: {:?}", e)),
)
};
let rewrite_cb = move |stream| {
let mut rewrite_service = RewriteService::new(Arc::clone(&rewrite_manager));
fasync::spawn(
async move { rewrite_service.handle_client(stream).await }
.unwrap_or_else(|e| fx_log_err!("while handling rewrite client {:?}", e)),
)
};
let admin_cb = move |stream| {
let experiment_state = Arc::clone(&experiment_state);
fasync::spawn(async move {
experiment::run_admin_service(experiment_state, stream)
.await
.unwrap_or_else(|e| fx_log_err!("while handling admin client {:?}", e))
});
};
let mut fs = ServiceFs::new();
fs.dir("svc")
.add_fidl_service(resolver_cb)
.add_fidl_service(font_resolver_fb)
.add_fidl_service(repo_cb)
.add_fidl_service(rewrite_cb)
.add_fidl_service(admin_cb);
inspector.export(&mut fs);
fs.take_and_serve_directory_handle()?;
let () = executor.run(fs.collect(), SERVER_THREADS);
Ok(())
}
fn connect_to_pkgfs(subdir: &str) -> Result<DirectoryProxy, Error> {
io_util::open_directory_in_namespace(
&format!("/pkgfs/{}", subdir),
io_util::OPEN_RIGHT_READABLE | io_util::OPEN_RIGHT_WRITABLE,
)
}
fn load_repo_manager(
amber_connector: AmberConnector,
experiments: Experiments,
) -> RepositoryManager<AmberConnector> {
// report any errors we saw, but don't error out because otherwise we won't be able
// to update the system.
RepositoryManagerBuilder::new(DYNAMIC_REPO_PATH, amber_connector, experiments)
.unwrap_or_else(|(builder, err)| {
fx_log_err!("error loading dynamic repo config: {}", err);
builder
})
.load_static_configs_dir(STATIC_REPO_DIR)
.unwrap_or_else(|(builder, errs)| {
for err in errs {
match err {
crate::repository_manager::LoadError::Io { path: _, error }
if error.kind() == io::ErrorKind::NotFound =>
{
fx_log_info!("no statically configured repositories present");
}
_ => fx_log_err!("error loading static repo config: {}", err),
};
}
builder
})
.build()
}
fn load_rewrite_manager(
node: inspect::Node,
repo_manager: &RepositoryManager<AmberConnector>,
disable_dynamic_configuration: bool,
) -> RewriteManager {
let dynamic_rules_path =
if disable_dynamic_configuration { None } else { Some(DYNAMIC_RULES_PATH) };
let builder = RewriteManagerBuilder::new(dynamic_rules_path)
.unwrap_or_else(|(builder, err)| {
if err.kind() != io::ErrorKind::NotFound {
fx_log_err!(
"unable to load dynamic rewrite rules from disk, using defaults: {}",
err
);
}
builder
})
.inspect_node(node)
.static_rules_path(STATIC_RULES_PATH)
.unwrap_or_else(|(builder, err)| {
if err.kind() != io::ErrorKind::NotFound {
fx_log_err!("unable to load static rewrite rules from disk: {}", err);
}
builder
});
// If we have a channel in sysconfig, we don't want to load the dynamic configs. Instead, we'll
// construct a unique rule for that channel.
let channel = match sysconfig_client::channel::read_channel_config() {
Ok(channel) => channel,
Err(err) => {
fx_log_info!("unable to load channel from sysconfig, using defaults: {}", err);
return builder.build();
}
};
let tuf_config_name = channel.tuf_config_name();
fx_log_info!("current TUF config name is {}", tuf_config_name);
let repo = match repo_manager.get_repo_for_channel(tuf_config_name) {
Some(repo) => repo,
None => {
fx_log_err!("unable to find repo for channel, using defaults");
return builder.build();
}
};
fx_log_info!("channel repo is {}", repo.repo_url());
match fuchsia_url_rewrite::Rule::new("fuchsia.com", repo.repo_url().host(), "/", "/") {
Ok(rule) => builder.replace_dynamic_rules(vec![rule]).build(),
Err(err) => {
fx_log_err!(
"failed to make rewrite rule for {}, using defaults: {}",
repo.repo_url(),
err
);
builder.build()
}
}
}
fn load_font_package_manager() -> FontPackageManager {
FontPackageManagerBuilder::new()
.add_registry_file(STATIC_FONT_REGISTRY_PATH)
.unwrap_or_else(|(builder, errs)| {
let errors = errs
.iter()
.filter(|err| {
if err.is_not_found() {
fx_log_info!("no font package registry present");
false
} else {
true
}
})
.fold(String::new(), |acc, err| acc + "\n" + format!("{}", err).as_str());
if !errors.is_empty() {
fx_log_err!("error(s) loading font package registry:{}", errors);
}
builder
})
.build()
}