| // 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 "peridot/lib/module_manifest_source/module_package_source.h" |
| |
| #include <dirent.h> |
| #include <sys/types.h> |
| |
| #include <fs/service.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/fxl/files/directory.h> |
| #include <lib/fxl/files/file.h> |
| #include <lib/fxl/functional/make_copyable.h> |
| #include <lib/fxl/logging.h> |
| #include <lib/fxl/memory/weak_ptr.h> |
| #include <lib/fxl/strings/split_string.h> |
| #include <lib/fxl/strings/string_printf.h> |
| |
| #include "peridot/lib/module_manifest_source/json.h" |
| #include "peridot/lib/module_manifest_source/package_util.h" |
| |
| namespace modular { |
| namespace { |
| // NOTE: This must match the path specified in |
| // //peridot/build/module_manifest.gni |
| constexpr char kInitialModulePackagesIndexDir[] = |
| "/system/data/initial_module_packages"; |
| } // namespace |
| |
| using ::fuchsia::maxwell::internal::ModulePackageIndexer; |
| |
| ModulePackageSource::ModulePackageSource( |
| component::StartupContext* const context) |
| : weak_factory_(this) { |
| context->outgoing().debug_dir()->AddEntry( |
| ModulePackageIndexer::Name_, |
| fbl::AdoptRef(new fs::Service([this](zx::channel channel) { |
| indexer_bindings_.AddBinding( |
| this, |
| fidl::InterfaceRequest<ModulePackageIndexer>(std::move(channel))); |
| return ZX_OK; |
| }))); |
| } |
| |
| ModulePackageSource::~ModulePackageSource() {} |
| |
| void ModulePackageSource::IndexManifest(std::string package_name, |
| std::string module_manifest_path) { |
| FXL_DCHECK(dispatcher_); |
| FXL_DCHECK(new_entry_fn_); |
| |
| std::string data; |
| if (!files::ReadFileToString(module_manifest_path, &data)) { |
| FXL_LOG(ERROR) << "Couldn't read module manifest from: " |
| << module_manifest_path; |
| return; |
| } |
| |
| fuchsia::modular::ModuleManifest entry; |
| if (!ModuleManifestEntryFromJson(data, &entry)) { |
| FXL_LOG(WARNING) << "Couldn't parse module manifest from: " |
| << module_manifest_path; |
| return; |
| } |
| |
| async::PostTask( |
| dispatcher_, |
| fxl::MakeCopyable([weak_this = weak_factory_.GetWeakPtr(), package_name, |
| entry = std::move(entry)]() mutable { |
| if (!weak_this) { |
| return; |
| } |
| |
| weak_this->new_entry_fn_(entry.binary, std::move(entry)); |
| })); |
| } |
| |
| // TODO(vardhan): Move this into garnet's fxl. |
| void IterateDirectory(fxl::StringView dirname, |
| std::function<void(fxl::StringView)> callback) { |
| DIR* fd = opendir(dirname.data()); |
| if (fd == nullptr) { |
| perror("Could not open module package index directory: "); |
| return; |
| } |
| struct dirent* dp = nullptr; |
| while ((dp = readdir(fd)) != nullptr) { |
| if (dp->d_name[0] != '.') { |
| callback(dp->d_name); |
| } |
| } |
| closedir(fd); |
| } |
| |
| void ModulePackageSource::Watch(async_dispatcher_t* dispatcher, IdleFn idle_fn, |
| NewEntryFn new_fn, RemovedEntryFn removed_fn) { |
| dispatcher_ = dispatcher; |
| new_entry_fn_ = new_fn; |
| |
| IterateDirectory( |
| kInitialModulePackagesIndexDir, [this](fxl::StringView filename) { |
| std::string contents; |
| if (!files::ReadFileToString( |
| fxl::StringPrintf("%s/%s", kInitialModulePackagesIndexDir, |
| filename.data()), |
| &contents)) { |
| FXL_LOG(ERROR) << "Could not read module package index: " |
| << filename.data(); |
| } |
| auto module_pkgs = fxl::SplitString( |
| contents, "\n", fxl::kTrimWhitespace, fxl::kSplitWantNonEmpty); |
| for (auto module_pkg_view : module_pkgs) { |
| auto module_pkg = module_pkg_view.ToString(); |
| // TODO(vardhan): We only index module package with version=0. Do |
| // better. |
| IndexManifest(module_pkg, |
| GetModuleManifestPathFromPackage(module_pkg, "0")); |
| } |
| }); |
| |
| idle_fn(); |
| } |
| |
| } // namespace modular |