]> git.proxmox.com Git - mirror_lxc.git/blob - src/tests/attach.c
tests: use busybox in lxc-test-unpriv
[mirror_lxc.git] / src / tests / attach.c
1 /* liblxcapi
2 *
3 * Copyright © 2013 Oracle.
4 *
5 * Authors:
6 * Dwight Engen <dwight.engen@oracle.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2, as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include <errno.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/syscall.h>
27 #include <sys/types.h>
28
29 #include "lxctest.h"
30 #include "utils.h"
31 #include "lsm/lsm.h"
32
33 #include <lxc/lxccontainer.h>
34
35 #ifndef HAVE_STRLCPY
36 #include "include/strlcpy.h"
37 #endif
38
39 #define TSTNAME "lxc-attach-test"
40 #define TSTOUT(fmt, ...) do { \
41 fprintf(stdout, fmt, ##__VA_ARGS__); fflush(NULL); \
42 } while (0)
43 #define TSTERR(fmt, ...) do { \
44 fprintf(stderr, "%s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); fflush(NULL); \
45 } while (0)
46
47 static const char *lsm_config_key = NULL;
48 static const char *lsm_label = NULL;
49
50 struct lsm_ops *lsm_ops;
51
52 static void test_lsm_detect(void)
53 {
54 if (lsm_ops->enabled(lsm_ops)) {
55 if (!strcmp(lsm_ops->name, "SELinux")) {
56 lsm_config_key = "lxc.selinux.context";
57 lsm_label = "unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023";
58 }
59 else if (!strcmp(lsm_ops->name, "AppArmor")) {
60 lsm_config_key = "lxc.apparmor.profile";
61 if (file_exists("/proc/self/ns/cgroup"))
62 lsm_label = "lxc-container-default-cgns";
63 else
64 lsm_label = "lxc-container-default";
65 }
66 else {
67 TSTERR("unknown lsm %s enabled, add test code here", lsm_ops->name);
68 exit(EXIT_FAILURE);
69 }
70 }
71 }
72
73 #if HAVE_APPARMOR || HAVE_SELINUX
74 static void test_attach_lsm_set_config(struct lxc_container *ct)
75 {
76 ct->load_config(ct, NULL);
77 ct->set_config_item(ct, lsm_config_key, lsm_label);
78 ct->save_config(ct, NULL);
79 }
80
81 static int test_attach_lsm_func_func(void* payload)
82 {
83 TSTOUT("%s", lsm_ops->process_label_get(lsm_ops, syscall(SYS_getpid)));
84 return 0;
85 }
86
87 static int test_attach_lsm_func(struct lxc_container *ct)
88 {
89 int ret;
90 pid_t pid;
91 int pipefd[2];
92 char result[1024];
93 lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
94
95 TSTOUT("Testing attach lsm label with func...\n");
96
97 ret = pipe(pipefd);
98 if (ret < 0) {
99 TSTERR("pipe failed %d", ret);
100 return ret;
101 }
102 attach_options.stdout_fd = pipefd[1];
103 attach_options.attach_flags &= ~(LXC_ATTACH_LSM_EXEC|LXC_ATTACH_DROP_CAPABILITIES);
104 attach_options.attach_flags |= LXC_ATTACH_LSM_NOW;
105 ret = ct->attach(ct, test_attach_lsm_func_func, NULL, &attach_options, &pid);
106 if (ret < 0) {
107 TSTERR("attach failed");
108 goto err1;
109 }
110
111 ret = read(pipefd[0], result, sizeof(result)-1);
112 if (ret < 0) {
113 TSTERR("read failed %d", ret);
114 goto err2;
115 }
116
117 result[ret] = '\0';
118 if (strcmp(lsm_label, result)) {
119 TSTERR("LSM label mismatch expected:%s got:%s", lsm_label, result);
120 ret = -1;
121 goto err2;
122 }
123 ret = 0;
124
125 err2:
126 (void)wait_for_pid(pid);
127 err1:
128 close(pipefd[0]);
129 close(pipefd[1]);
130 return ret;
131 }
132
133 static int test_attach_lsm_cmd(struct lxc_container *ct)
134 {
135 int ret;
136 pid_t pid;
137 int pipefd[2];
138 char result[1024];
139 char *space;
140 char *argv[] = {"cat", "/proc/self/attr/current", NULL};
141 lxc_attach_command_t command = {"cat", argv};
142 lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
143
144 TSTOUT("Testing attach lsm label with cmd...\n");
145
146 ret = pipe(pipefd);
147 if (ret < 0) {
148 TSTERR("pipe failed %d", ret);
149 return ret;
150 }
151 attach_options.stdout_fd = pipefd[1];
152
153 ret = ct->attach(ct, lxc_attach_run_command, &command, &attach_options, &pid);
154 if (ret < 0) {
155 TSTERR("attach failed");
156 goto err1;
157 }
158
159 ret = read(pipefd[0], result, sizeof(result)-1);
160 if (ret < 0) {
161 TSTERR("read failed %d", ret);
162 goto err2;
163 }
164 result[ret] = '\0';
165 space = strchr(result, '\n');
166 if (space)
167 *space = '\0';
168 space = strchr(result, ' ');
169 if (space)
170 *space = '\0';
171
172 ret = -1;
173 if (strcmp(lsm_label, result)) {
174 TSTERR("LSM label mismatch expected:%s got:%s", lsm_label, result);
175 goto err2;
176 }
177 ret = 0;
178
179 err2:
180 (void)wait_for_pid(pid);
181 err1:
182 close(pipefd[0]);
183 close(pipefd[1]);
184 return ret;
185 }
186 #else
187 static void test_attach_lsm_set_config(struct lxc_container *ct) {}
188 static int test_attach_lsm_func(struct lxc_container *ct) { return 0; }
189 static int test_attach_lsm_cmd(struct lxc_container *ct) { return 0; }
190 #endif /* HAVE_APPARMOR || HAVE_SELINUX */
191
192 static int test_attach_func_func(void* payload)
193 {
194 TSTOUT("%d", (int)syscall(SYS_getpid));
195 return 0;
196 }
197
198 static int test_attach_func(struct lxc_container *ct)
199 {
200 int ret;
201 pid_t pid,nspid;
202 int pipefd[2];
203 char result[1024];
204 lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
205
206 TSTOUT("Testing attach with func...\n");
207
208 /* XXX: We can't just use &nspid and have test_attach_func_func fill
209 * it in because the function doesn't run in our process context but
210 * in a fork()ed from us context. We read the result through a pipe.
211 */
212 ret = pipe(pipefd);
213 if (ret < 0) {
214 TSTERR("pipe failed %d", ret);
215 return ret;
216 }
217 attach_options.stdout_fd = pipefd[1];
218
219 ret = ct->attach(ct, test_attach_func_func, NULL, &attach_options, &pid);
220 if (ret < 0) {
221 TSTERR("attach failed");
222 goto err1;
223 }
224
225 ret = read(pipefd[0], result, sizeof(result)-1);
226 if (ret < 0) {
227 TSTERR("read failed %d", ret);
228 goto err2;
229 }
230 result[ret] = '\0';
231
232 /* There is a small chance the pid is reused inside the NS, so we
233 * just print it and don't actually do this check
234 *
235 * if (pid == nspid) TSTERR(...)
236 */
237 nspid = atoi(result);
238 TSTOUT("Pid:%d in NS:%d\n", pid, nspid);
239 ret = 0;
240
241 err2:
242 (void)wait_for_pid(pid);
243 err1:
244 close(pipefd[0]);
245 close(pipefd[1]);
246 return ret;
247 }
248
249 static int test_attach_cmd(struct lxc_container *ct)
250 {
251 int ret;
252 pid_t pid;
253 char *argv[] = {"cmp", "-s", "/sbin/init", "/bin/busybox", NULL};
254 lxc_attach_command_t command = {"cmp", argv};
255 lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
256
257 TSTOUT("Testing attach with success command...\n");
258 ret = ct->attach(ct, lxc_attach_run_command, &command, &attach_options, &pid);
259 if (ret < 0) {
260 TSTERR("attach failed");
261 return ret;
262 }
263
264 ret = wait_for_pid(pid);
265 if (ret < 0) {
266 TSTERR("attach success command got bad return %d", ret);
267 return ret;
268 }
269
270 TSTOUT("Testing attach with failure command...\n");
271 argv[2] = "/etc/fstab";
272 ret = ct->attach(ct, lxc_attach_run_command, &command, &attach_options, &pid);
273 if (ret < 0) {
274 TSTERR("attach failed");
275 return ret;
276 }
277
278 ret = wait_for_pid(pid);
279 if (ret == 0) {
280 TSTERR("attach failure command got bad return %d", ret);
281 return -1;
282 }
283 return 0;
284 }
285
286 /* test_ct_destroy: stop and destroy the test container
287 *
288 * @ct : the container
289 */
290 static void test_ct_destroy(struct lxc_container *ct)
291 {
292 ct->stop(ct);
293 ct->destroy(ct);
294 lxc_container_put(ct);
295 }
296
297 /* test_ct_create: create and start test container
298 *
299 * @lxcpath : the lxcpath in which to create the container
300 * @group : name of the container group or NULL for default "lxc"
301 * @name : name of the container
302 * @template : template to use when creating the container
303 */
304 static struct lxc_container *test_ct_create(const char *lxcpath,
305 const char *group, const char *name,
306 const char *template)
307 {
308 int ret;
309 struct lxc_container *ct = NULL;
310
311 if (lxcpath) {
312 ret = mkdir(lxcpath, 0755);
313 if (ret < 0 && errno != EEXIST) {
314 TSTERR("failed to mkdir %s %s", lxcpath, strerror(errno));
315 goto out1;
316 }
317 }
318
319 if ((ct = lxc_container_new(name, lxcpath)) == NULL) {
320 TSTERR("instantiating container %s", name);
321 goto out1;
322 }
323 if (ct->is_defined(ct)) {
324 test_ct_destroy(ct);
325 ct = lxc_container_new(name, lxcpath);
326 }
327 if (!ct->createl(ct, template, NULL, NULL, 0, NULL)) {
328 TSTERR("creating container %s", name);
329 goto out2;
330 }
331
332 if (lsm_ops->enabled(lsm_ops))
333 test_attach_lsm_set_config(ct);
334
335 ct->want_daemonize(ct, true);
336 if (!ct->startl(ct, 0, NULL)) {
337 TSTERR("starting container %s", name);
338 goto out2;
339 }
340 return ct;
341
342 out2:
343 test_ct_destroy(ct);
344 ct = NULL;
345 out1:
346 return ct;
347 }
348
349
350 static int test_attach(const char *lxcpath, const char *name, const char *template)
351 {
352 int ret = -1;
353 struct lxc_container *ct;
354
355 TSTOUT("Testing attach with on lxcpath:%s\n", lxcpath ? lxcpath : "<default>");
356 ct = test_ct_create(lxcpath, NULL, name, template);
357 if (!ct)
358 goto err1;
359
360 ret = test_attach_cmd(ct);
361 if (ret < 0) {
362 TSTERR("attach cmd test failed");
363 goto err2;
364 }
365
366 ret = test_attach_func(ct);
367 if (ret < 0) {
368 TSTERR("attach func test failed");
369 goto err2;
370 }
371
372 if (lsm_ops->enabled(lsm_ops)) {
373 ret = test_attach_lsm_cmd(ct);
374 if (ret < 0) {
375 TSTERR("attach lsm cmd test failed");
376 goto err2;
377 }
378
379 ret = test_attach_lsm_func(ct);
380 if (ret < 0) {
381 TSTERR("attach lsm func test failed");
382 goto err2;
383 }
384 }
385 ret = 0;
386
387 err2:
388 test_ct_destroy(ct);
389 err1:
390 return ret;
391 }
392
393 int main(int argc, char *argv[])
394 {
395 int i, ret;
396 struct lxc_log log;
397 char template[sizeof(P_tmpdir"/attach_XXXXXX")];
398 int fret = EXIT_FAILURE;
399
400 (void)strlcpy(template, P_tmpdir"/attach_XXXXXX", sizeof(template));
401
402 lsm_ops = lsm_init_static();
403
404 i = lxc_make_tmpfile(template, false);
405 if (i < 0) {
406 lxc_error("Failed to create temporary log file for container %s\n", TSTNAME);
407 exit(EXIT_FAILURE);
408 } else {
409 lxc_debug("Using \"%s\" as temporary log file for container %s\n", template, TSTNAME);
410 close(i);
411 }
412
413 log.name = TSTNAME;
414 log.file = template;
415 log.level = "TRACE";
416 log.prefix = "attach";
417 log.quiet = false;
418 log.lxcpath = NULL;
419 if (lxc_log_init(&log))
420 goto on_error;
421
422 test_lsm_detect();
423 ret = test_attach(NULL, TSTNAME, "busybox");
424 if (ret < 0)
425 goto on_error;
426
427 TSTOUT("\n");
428 ret = test_attach(LXCPATH "/alternate-path-test", TSTNAME, "busybox");
429 if (ret < 0)
430 goto on_error;
431
432 TSTOUT("All tests passed\n");
433 fret = EXIT_SUCCESS;
434
435 on_error:
436 if (fret != EXIT_SUCCESS) {
437 int fd;
438
439 fd = open(template, O_RDONLY);
440 if (fd >= 0) {
441 char buf[4096];
442 ssize_t buflen;
443 while ((buflen = read(fd, buf, 1024)) > 0) {
444 buflen = write(STDERR_FILENO, buf, buflen);
445 if (buflen <= 0)
446 break;
447 }
448 close(fd);
449 }
450 }
451 (void)rmdir(LXCPATH "/alternate-path-test");
452 (void)unlink(template);
453 exit(fret);
454 }