3 * Copyright © 2013 Oracle.
6 * Dwight Engen <dwight.engen@oracle.com>
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.
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.
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.
26 #include <sys/syscall.h>
27 #include <sys/types.h>
33 #include <lxc/lxccontainer.h>
39 #define TSTNAME "lxc-attach-test"
40 #define TSTOUT(fmt, ...) do { \
41 fprintf(stdout, fmt, ##__VA_ARGS__); fflush(NULL); \
43 #define TSTERR(fmt, ...) do { \
44 fprintf(stderr, "%s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); fflush(NULL); \
47 static const char *lsm_config_key
= NULL
;
48 static const char *lsm_label
= NULL
;
50 struct lsm_ops
*lsm_ops
;
52 static void test_lsm_detect(void)
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";
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";
64 lsm_label
= "lxc-container-default";
67 TSTERR("unknown lsm %s enabled, add test code here", lsm_ops
->name
);
73 #if HAVE_APPARMOR || HAVE_SELINUX
74 static void test_attach_lsm_set_config(struct lxc_container
*ct
)
76 ct
->load_config(ct
, NULL
);
77 ct
->set_config_item(ct
, lsm_config_key
, lsm_label
);
78 ct
->save_config(ct
, NULL
);
81 static int test_attach_lsm_func_func(void* payload
)
83 TSTOUT("%s", lsm_ops
->process_label_get(lsm_ops
, syscall(SYS_getpid
)));
87 static int test_attach_lsm_func(struct lxc_container
*ct
)
93 lxc_attach_options_t attach_options
= LXC_ATTACH_OPTIONS_DEFAULT
;
95 TSTOUT("Testing attach lsm label with func...\n");
99 TSTERR("pipe failed %d", ret
);
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
);
107 TSTERR("attach failed");
111 ret
= read(pipefd
[0], result
, sizeof(result
)-1);
113 TSTERR("read failed %d", ret
);
118 if (strcmp(lsm_label
, result
)) {
119 TSTERR("LSM label mismatch expected:%s got:%s", lsm_label
, result
);
126 (void)wait_for_pid(pid
);
133 static int test_attach_lsm_cmd(struct lxc_container
*ct
)
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
;
144 TSTOUT("Testing attach lsm label with cmd...\n");
148 TSTERR("pipe failed %d", ret
);
151 attach_options
.stdout_fd
= pipefd
[1];
153 ret
= ct
->attach(ct
, lxc_attach_run_command
, &command
, &attach_options
, &pid
);
155 TSTERR("attach failed");
159 ret
= read(pipefd
[0], result
, sizeof(result
)-1);
161 TSTERR("read failed %d", ret
);
165 space
= strchr(result
, '\n');
168 space
= strchr(result
, ' ');
173 if (strcmp(lsm_label
, result
)) {
174 TSTERR("LSM label mismatch expected:%s got:%s", lsm_label
, result
);
180 (void)wait_for_pid(pid
);
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 */
192 static int test_attach_func_func(void* payload
)
194 TSTOUT("%d", (int)syscall(SYS_getpid
));
198 static int test_attach_func(struct lxc_container
*ct
)
204 lxc_attach_options_t attach_options
= LXC_ATTACH_OPTIONS_DEFAULT
;
206 TSTOUT("Testing attach with func...\n");
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.
214 TSTERR("pipe failed %d", ret
);
217 attach_options
.stdout_fd
= pipefd
[1];
219 ret
= ct
->attach(ct
, test_attach_func_func
, NULL
, &attach_options
, &pid
);
221 TSTERR("attach failed");
225 ret
= read(pipefd
[0], result
, sizeof(result
)-1);
227 TSTERR("read failed %d", ret
);
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
235 * if (pid == nspid) TSTERR(...)
237 nspid
= atoi(result
);
238 TSTOUT("Pid:%d in NS:%d\n", pid
, nspid
);
242 (void)wait_for_pid(pid
);
249 static int test_attach_cmd(struct lxc_container
*ct
)
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
;
257 TSTOUT("Testing attach with success command...\n");
258 ret
= ct
->attach(ct
, lxc_attach_run_command
, &command
, &attach_options
, &pid
);
260 TSTERR("attach failed");
264 ret
= wait_for_pid(pid
);
266 TSTERR("attach success command got bad return %d", ret
);
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
);
274 TSTERR("attach failed");
278 ret
= wait_for_pid(pid
);
280 TSTERR("attach failure command got bad return %d", ret
);
286 /* test_ct_destroy: stop and destroy the test container
288 * @ct : the container
290 static void test_ct_destroy(struct lxc_container
*ct
)
294 lxc_container_put(ct
);
297 /* test_ct_create: create and start test container
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
304 static struct lxc_container
*test_ct_create(const char *lxcpath
,
305 const char *group
, const char *name
,
306 const char *template)
309 struct lxc_container
*ct
= NULL
;
312 ret
= mkdir(lxcpath
, 0755);
313 if (ret
< 0 && errno
!= EEXIST
) {
314 TSTERR("failed to mkdir %s %s", lxcpath
, strerror(errno
));
319 if ((ct
= lxc_container_new(name
, lxcpath
)) == NULL
) {
320 TSTERR("instantiating container %s", name
);
323 if (ct
->is_defined(ct
)) {
325 ct
= lxc_container_new(name
, lxcpath
);
327 if (!ct
->createl(ct
, template, NULL
, NULL
, 0, NULL
)) {
328 TSTERR("creating container %s", name
);
332 if (lsm_ops
->enabled(lsm_ops
))
333 test_attach_lsm_set_config(ct
);
335 ct
->want_daemonize(ct
, true);
336 if (!ct
->startl(ct
, 0, NULL
)) {
337 TSTERR("starting container %s", name
);
350 static int test_attach(const char *lxcpath
, const char *name
, const char *template)
353 struct lxc_container
*ct
;
355 TSTOUT("Testing attach with on lxcpath:%s\n", lxcpath
? lxcpath
: "<default>");
356 ct
= test_ct_create(lxcpath
, NULL
, name
, template);
360 ret
= test_attach_cmd(ct
);
362 TSTERR("attach cmd test failed");
366 ret
= test_attach_func(ct
);
368 TSTERR("attach func test failed");
372 if (lsm_ops
->enabled(lsm_ops
)) {
373 ret
= test_attach_lsm_cmd(ct
);
375 TSTERR("attach lsm cmd test failed");
379 ret
= test_attach_lsm_func(ct
);
381 TSTERR("attach lsm func test failed");
393 int main(int argc
, char *argv
[])
397 char template[sizeof(P_tmpdir
"/attach_XXXXXX")];
398 int fret
= EXIT_FAILURE
;
400 (void)strlcpy(template, P_tmpdir
"/attach_XXXXXX", sizeof(template));
402 lsm_ops
= lsm_init_static();
404 i
= lxc_make_tmpfile(template, false);
406 lxc_error("Failed to create temporary log file for container %s\n", TSTNAME
);
409 lxc_debug("Using \"%s\" as temporary log file for container %s\n", template, TSTNAME
);
416 log
.prefix
= "attach";
419 if (lxc_log_init(&log
))
423 ret
= test_attach(NULL
, TSTNAME
, "busybox");
428 ret
= test_attach(LXCPATH
"/alternate-path-test", TSTNAME
, "busybox");
432 TSTOUT("All tests passed\n");
436 if (fret
!= EXIT_SUCCESS
) {
439 fd
= open(template, O_RDONLY
);
443 while ((buflen
= read(fd
, buf
, 1024)) > 0) {
444 buflen
= write(STDERR_FILENO
, buf
, buflen
);
451 (void)rmdir(LXCPATH
"/alternate-path-test");
452 (void)unlink(template);