blob: 85df28b624196961b9362757eaedd675999080f1 [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.
#include <fcntl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/limits.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/spawn.h>
#include <lib/zx/handle.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zircon/dlfcn.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <zircon/utc.h>
static bool has_fd(int fd) {
zx_handle_t handle = ZX_HANDLE_INVALID;
zx_status_t status = fdio_fd_clone(fd, &handle);
if (status == ZX_OK) {
zx_handle_close(handle);
return true;
}
return false;
}
static bool has_ns(const char* path) {
zx_handle_t h1, h2;
zx_status_t status = zx_channel_create(0, &h1, &h2);
if (status != ZX_OK)
return false;
status = fdio_service_connect(path, h1);
zx_handle_close(h2);
return status == ZX_OK;
}
static bool has_arg(uint32_t arg) { return zx_take_startup_handle(arg) != ZX_HANDLE_INVALID; }
static int check_flags(uint32_t flags, int success) {
// We can't actually load the process without FDIO_SPAWN_DEFAULT_LDSVC, so we
// always add it into the flags.
flags |= FDIO_SPAWN_DEFAULT_LDSVC;
bool should_have_job = (flags & FDIO_SPAWN_CLONE_JOB) != 0;
bool has_job = zx_job_default() != ZX_HANDLE_INVALID;
if (has_job != should_have_job)
return -1;
bool should_have_ldsvc = (flags & FDIO_SPAWN_DEFAULT_LDSVC) != 0;
zx_handle_t ldsvc;
bool has_ldsvc = dl_clone_loader_service(&ldsvc) != ZX_ERR_UNAVAILABLE;
if (has_ldsvc != should_have_ldsvc)
return -2;
bool should_have_namespace = (flags & FDIO_SPAWN_CLONE_NAMESPACE) != 0;
fdio_flat_namespace_t* flat = NULL;
if (fdio_ns_export_root(&flat) != ZX_OK)
return -3;
bool has_namespace = flat->count > 0;
fdio_ns_free_flat_ns(flat);
if (has_namespace != should_have_namespace)
return -4;
bool should_have_stdio = (flags & FDIO_SPAWN_CLONE_STDIO) != 0;
bool has_stdio = has_fd(0) || has_fd(1) || has_fd(2);
if (has_stdio != should_have_stdio)
return -5;
bool should_have_environ = (flags & FDIO_SPAWN_CLONE_ENVIRON) != 0;
bool has_environ = environ[0] != NULL;
if (has_environ != should_have_environ)
return -6;
return success;
}
static bool check_env(const char* name, const char* expected) {
const char* actual = getenv(name);
if (!actual)
return false;
return !strcmp(actual, expected);
}
static bool do_stat(const char* path) {
struct stat statbuf;
return stat(path, &statbuf) == 0;
}
int do_spawn(int argc, char** argv) {
zx::handle subprocess;
zx_status_t status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, argv[0], argv,
subprocess.reset_and_get_address());
if (status != ZX_OK)
return status;
status = subprocess.wait_one(ZX_TASK_TERMINATED, zx::time(ZX_TIME_INFINITE), nullptr);
if (status != ZX_OK)
return -102;
size_t actual, avail;
zx_info_process_t proc_info;
status = subprocess.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), &actual, &avail);
if (status != ZX_OK)
return -103;
if (actual != 1)
return -104;
return int(proc_info.return_code);
}
zx_koid_t koid_of_global_utc_clock() {
zx_handle_t clock_handle = zx_utc_reference_get();
if (clock_handle == ZX_HANDLE_INVALID) {
return ZX_KOID_INVALID;
}
zx_info_handle_basic_t info;
zx_status_t status =
zx_object_get_info(clock_handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
return ZX_KOID_INVALID;
}
return info.koid;
}
int main(int argc, char** argv) {
if (argc == 0)
return 42;
if (argc == 1)
return 43;
const char* cmd = argv[1];
if (!strcmp(cmd, "--argc"))
return argc;
if (!strcmp(cmd, "--flags")) {
if (argc != 3)
return -251;
const char* flags = argv[2];
if (!strcmp(flags, "none"))
return check_flags(0, 51);
if (!strcmp(flags, "job"))
return check_flags(FDIO_SPAWN_CLONE_JOB, 52);
if (!strcmp(flags, "namespace"))
return check_flags(FDIO_SPAWN_CLONE_NAMESPACE, 53);
if (!strcmp(flags, "stdio"))
return check_flags(FDIO_SPAWN_CLONE_STDIO, 54);
if (!strcmp(flags, "environ"))
return check_flags(FDIO_SPAWN_CLONE_ENVIRON, 55);
if (!strcmp(flags, "all"))
return check_flags(FDIO_SPAWN_CLONE_ALL, 56);
}
if (!strcmp(cmd, "--env")) {
if (argc != 3)
return -252;
const char* env = argv[2];
if (!strcmp(env, "empty"))
return environ[0] == NULL ? 61 : -1;
if (!strcmp(env, "one")) {
bool pass =
environ[0] != NULL && !strcmp(environ[0], "SPAWN_TEST_CHILD=1") && environ[1] == NULL;
return pass ? 62 : -2;
}
if (!strcmp(env, "two")) {
bool pass = environ[0] != NULL && !strcmp(environ[0], "SPAWN_TEST_CHILD=1") &&
environ[1] != NULL && !strcmp(environ[1], "SPAWN_TEST_CHILD2=1") &&
environ[2] == NULL;
return pass ? 63 : -3;
}
if (!strcmp(env, "clone")) {
bool pass = check_env("SPAWN_TEST_PARENT", "1");
return pass ? 64 : -4;
}
}
if (!strcmp(cmd, "--action")) {
if (argc != 3)
return -252;
const char* action = argv[2];
if (!strcmp(action, "clone-fd"))
return has_fd(21) && !has_fd(22) ? 71 : -1;
if (!strcmp(action, "transfer-fd"))
return has_fd(21) && !has_fd(22) ? 72 : -2;
if (!strcmp(action, "clone-and-transfer-fd"))
return has_fd(21) && has_fd(22) && !has_fd(23) ? 73 : -3;
if (!strcmp(action, "ns-entry"))
return has_ns("/foo/bar/baz") && !has_ns("/baz/bar/foo") ? 74 : -4;
if (!strcmp(action, "add-handle"))
return has_arg(PA_USER0) && !has_arg(PA_USER1) ? 75 : -5;
if (!strcmp(action, "add-handle-clock-utc"))
return koid_of_global_utc_clock();
}
if (!strcmp(cmd, "--stat")) {
if (argc != 3)
return -253;
return do_stat(argv[2]) ? 76 : -6;
}
if (!strcmp(cmd, "--spawn")) {
if (argc < 3)
return -254;
return do_spawn(argc - 2, argv + 2);
}
return -250;
}