1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fcntl.h>
6 #include <lib/fdio/limits.h>
7 #include <lib/fdio/namespace.h>
8 #include <lib/fdio/spawn.h>
9 #include <lib/fdio/util.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <zircon/dlfcn.h>
14 #include <zircon/process.h>
15 #include <zircon/processargs.h>
16 #include <zircon/syscalls.h>
17 
has_fd(int fd)18 static bool has_fd(int fd) {
19     zx_handle_t handles[FDIO_MAX_HANDLES];
20     uint32_t ids[FDIO_MAX_HANDLES];
21     zx_status_t status = fdio_clone_fd(fd, fd + 50, handles, ids);
22     if (status > 0) {
23         size_t n = (size_t)status;
24         zx_handle_close_many(handles, n);
25         return true;
26     }
27     return false;
28 }
29 
has_ns(const char * path)30 static bool has_ns(const char* path) {
31     zx_handle_t h1, h2;
32     zx_status_t status = zx_channel_create(0, &h1, &h2);
33     if (status != ZX_OK)
34         return false;
35     status = fdio_service_connect(path, h1);
36     zx_handle_close(h2);
37     return status == ZX_OK;
38 }
39 
has_arg(uint32_t arg)40 static bool has_arg(uint32_t arg) {
41     return zx_take_startup_handle(arg) != ZX_HANDLE_INVALID;
42 }
43 
check_flags(uint32_t flags,int success)44 static int check_flags(uint32_t flags, int success) {
45     // We can't actually load the process without FDIO_SPAWN_DEFAULT_LDSVC, so we
46     // always add it into the flags.
47     flags |= FDIO_SPAWN_DEFAULT_LDSVC;
48 
49     bool should_have_job = (flags & FDIO_SPAWN_CLONE_JOB) != 0;
50     bool has_job = zx_job_default() != ZX_HANDLE_INVALID;
51     if (has_job != should_have_job)
52         return -1;
53 
54     bool should_have_ldsvc = (flags & FDIO_SPAWN_DEFAULT_LDSVC) != 0;
55     zx_handle_t ldsvc;
56     bool has_ldsvc = dl_clone_loader_service(&ldsvc) != ZX_ERR_UNAVAILABLE;
57     if (has_ldsvc != should_have_ldsvc)
58         return -2;
59 
60     bool should_have_namespace = (flags & FDIO_SPAWN_CLONE_NAMESPACE) != 0;
61     fdio_ns_t* ns = NULL;
62     bool has_namespace = fdio_ns_get_installed(&ns) != ZX_ERR_NOT_FOUND;
63     if (has_namespace != should_have_namespace)
64         return -3;
65 
66     bool should_have_stdio = (flags & FDIO_SPAWN_CLONE_STDIO) != 0;
67     bool has_stdio = has_fd(0) || has_fd(1) || has_fd(2);
68     if (has_stdio != should_have_stdio)
69         return -4;
70 
71     bool should_have_environ = (flags & FDIO_SPAWN_CLONE_ENVIRON) != 0;
72     bool has_environ = environ[0] != NULL;
73     if (has_environ != should_have_environ)
74         return -5;
75 
76     return success;
77 }
78 
check_env(const char * name,const char * expected)79 static bool check_env(const char* name, const char* expected) {
80     const char* actual = getenv(name);
81     if (!actual)
82         return false;
83     return !strcmp(actual, expected);
84 }
85 
main(int argc,char ** argv)86 int main(int argc, char** argv) {
87     if (argc == 0)
88         return 42;
89     if (argc == 1)
90         return 43;
91     const char* cmd = argv[1];
92     if (!strcmp(cmd, "--argc"))
93         return argc;
94     if (!strcmp(cmd, "--flags")) {
95         if (argc != 3)
96             return -251;
97         const char* flags = argv[2];
98         if (!strcmp(flags, "none"))
99             return check_flags(0, 51);
100         if (!strcmp(flags, "job"))
101             return check_flags(FDIO_SPAWN_CLONE_JOB, 52);
102         if (!strcmp(flags, "namespace"))
103             return check_flags(FDIO_SPAWN_CLONE_NAMESPACE, 53);
104         if (!strcmp(flags, "stdio"))
105             return check_flags(FDIO_SPAWN_CLONE_STDIO, 54);
106         if (!strcmp(flags, "environ"))
107             return check_flags(FDIO_SPAWN_CLONE_ENVIRON, 55);
108         if (!strcmp(flags, "all"))
109             return check_flags(FDIO_SPAWN_CLONE_ALL, 56);
110     }
111     if (!strcmp(cmd, "--env")) {
112         if (argc != 3)
113             return -252;
114         const char* env = argv[2];
115         if (!strcmp(env, "empty"))
116             return environ[0] == NULL ? 61 : -1;
117         if (!strcmp(env, "one")) {
118             bool pass = environ[0] != NULL && !strcmp(environ[0], "SPAWN_TEST_CHILD=1") &&
119                         environ[1] == NULL;
120             return pass ? 62 : -2;
121         }
122         if (!strcmp(env, "two")) {
123             bool pass = environ[0] != NULL && !strcmp(environ[0], "SPAWN_TEST_CHILD=1") &&
124                         environ[1] != NULL && !strcmp(environ[1], "SPAWN_TEST_CHILD2=1") &&
125                         environ[2] == NULL;
126             return pass ? 63 : -3;
127         }
128         if (!strcmp(env, "clone")) {
129             bool pass = check_env("SPAWN_TEST_PARENT", "1");
130             return pass ? 64 : -4;
131         }
132     }
133     if (!strcmp(cmd, "--action")) {
134         if (argc != 3)
135             return -252;
136         const char* action = argv[2];
137         if (!strcmp(action, "clone-fd"))
138             return has_fd(21) && !has_fd(22) ? 71 : -1;
139         if (!strcmp(action, "transfer-fd"))
140             return has_fd(21) && !has_fd(22) ? 72 : -2;
141         if (!strcmp(action, "clone-and-transfer-fd"))
142             return has_fd(21) && has_fd(22) && !has_fd(23) ? 73 : -3;
143         if (!strcmp(action, "ns-entry"))
144             return has_ns("/foo/bar/baz") && !has_ns("/baz/bar/foo") ? 74 : -4;
145         if (!strcmp(action, "add-handle"))
146             return has_arg(PA_USER0) && !has_arg(PA_USER1) ? 75 : -5;
147     }
148 
149     return -250;
150 }
151