| // Copyright 2017 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/storage/fshost/pkgfs-launcher.h" |
| |
| #include <fuchsia/io/llcpp/fidl.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/process.h> |
| #include <stdio.h> |
| #include <zircon/errors.h> |
| #include <zircon/processargs.h> |
| |
| #include "src/storage/fshost/fdio.h" |
| #include "src/storage/fshost/filesystem-mounter.h" |
| #include "src/storage/fshost/fshost-fs-provider.h" |
| #include "src/storage/fshost/pkgfs-loader-service.h" |
| |
| namespace fio = ::llcpp::fuchsia::io; |
| |
| namespace devmgr { |
| |
| namespace { |
| |
| zx::status<> WaitForPkgfsLaunchCompletion(zx::process proc) { |
| auto deadline = zx::deadline_after(zx::sec(100)); |
| zx_signals_t observed; |
| auto status = |
| zx::make_status(proc.wait_one(ZX_USER_SIGNAL_0 | ZX_PROCESS_TERMINATED, deadline, &observed)); |
| if (status.is_error()) { |
| FX_LOGS(ERROR) << "pkgfs did not signal completion: " << status.status_string(); |
| return status; |
| } |
| if (!(observed & ZX_USER_SIGNAL_0)) { |
| FX_LOGS(ERROR) << "pkgfs terminated prematurely"; |
| return zx::error(ZX_ERR_BAD_STATE); |
| } |
| return zx::ok(); |
| } |
| |
| zx::status<> FinishPkgfsLaunch(FilesystemMounter* filesystems, zx::channel pkgfs_root) { |
| // re-export /pkgfs/system as /system |
| zx::channel system_channel, system_req; |
| zx_status_t status = zx::channel::create(0, &system_channel, &system_req); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| status = fdio_open_at(pkgfs_root.get(), "system", FS_READ_EXEC_DIR_FLAGS, system_req.release()); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| // re-export /pkgfs/packages/shell-commands/0/bin as /bin |
| zx::channel bin_chan, bin_req; |
| status = zx::channel::create(0, &bin_chan, &bin_req); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| status = fdio_open_at(pkgfs_root.get(), "packages/shell-commands/0/bin", FS_READ_EXEC_DIR_FLAGS, |
| bin_req.release()); |
| if (status != ZX_OK) { |
| // non-fatal. |
| FX_LOGS(ERROR) << "failed to install /bin (could not open shell-commands)"; |
| } |
| status = filesystems->InstallFs(FsManager::MountPoint::kPkgfs, std::move(pkgfs_root)); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "failed to install /pkgfs"; |
| return zx::error(status); |
| } |
| status = filesystems->InstallFs(FsManager::MountPoint::kSystem, std::move(system_channel)); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "failed to install /system"; |
| return zx::error(status); |
| } |
| // as above, failure of /bin export is non-fatal. |
| status = filesystems->InstallFs(FsManager::MountPoint::kBin, std::move(bin_chan)); |
| if (status != ZX_OK) { |
| FX_LOGS(ERROR) << "failed to install /bin"; |
| } |
| // start the delayed vfs |
| filesystems->FuchsiaStart(); |
| return zx::ok(); |
| } |
| |
| } // namespace |
| |
| zx::status<> LaunchPkgfs(FilesystemMounter* filesystems) { |
| // Get the pkgfs.cmd boot argument |
| auto cmd_status = filesystems->boot_args()->pkgfs_cmd(); |
| if (cmd_status.is_error()) { |
| FX_LOGS(ERROR) << "unable to launch pkgfs, missing \"zircon.system.pkgfs.cmd\" boot argument"; |
| return zx::error(ZX_ERR_INVALID_ARGS); |
| } |
| const char* cmd = cmd_status.value().c_str(); |
| |
| fbl::unique_fd blob_dir; |
| auto status = zx::make_status(fdio_open_fd("/fs/blob", |
| fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_EXECUTABLE, |
| blob_dir.reset_and_get_address())); |
| if (status.is_error()) { |
| FX_LOGS(ERROR) << "fdio_open_fd(/fs/blob) failed: " << status.status_string(); |
| return status; |
| } |
| |
| auto args = ArgumentVector::FromCmdline(cmd); |
| auto argv = args.argv(); |
| // Remove leading slashes before asking pkgfs_ldsvc_load_blob to load the |
| // file. |
| const char* file = argv[0]; |
| while (file[0] == '/') { |
| ++file; |
| } |
| |
| auto loader = PkgfsLoaderService::Create(std::move(blob_dir), filesystems->boot_args()); |
| auto executable = loader->LoadPkgfsFile(argv[0]); |
| if (executable.is_error()) { |
| FX_LOGS(ERROR) << "cannot load pkgfs executable: " << executable.status_string(); |
| return executable.take_error(); |
| } |
| |
| auto loader_conn = loader->Connect(); |
| if (loader_conn.is_error()) { |
| FX_LOGS(ERROR) << "failed to connect to pkgfs loader: " << loader_conn.status_string(); |
| return loader_conn.take_error(); |
| } |
| |
| zx::channel h0, h1; |
| status = zx::make_status(zx::channel::create(0, &h0, &h1)); |
| if (status.is_error()) { |
| FX_LOGS(ERROR) << "cannot create pkgfs root channel: " << status.status_string(); |
| return status; |
| } |
| |
| const zx_handle_t handles[] = {h1.release()}; |
| const uint32_t handle_types[] = {PA_HND(PA_USER0, 0)}; |
| size_t hcount = sizeof(handles) / sizeof(*handles); |
| zx::process proc; |
| FX_LOGS(INFO) << "starting " << args << "..."; |
| |
| FshostFsProvider fs_provider; |
| DevmgrLauncher launcher(&fs_provider); |
| status = zx::make_status( |
| launcher.LaunchWithLoader(*zx::job::default_job(), "pkgfs", std::move(executable).value(), |
| std::move(loader_conn).value(), argv, nullptr, -1, |
| /* TODO(fxbug.dev/32044) */ zx::resource(), handles, handle_types, |
| hcount, &proc, FS_DATA | FS_BLOB_EXEC | FS_SVC)); |
| if (status.is_error()) { |
| FX_LOGS(ERROR) << "failed to launch " << cmd << ": " << status.status_string(); |
| return status; |
| } |
| |
| status = WaitForPkgfsLaunchCompletion(std::move(proc)); |
| if (status.is_error()) { |
| return status; |
| } |
| return FinishPkgfsLaunch(filesystems, std::move(h0)); |
| } |
| |
| } // namespace devmgr |