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