1/* Copyright (c) 2015 Red Hat, Inc.
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of version 2 the GNU General Public License as
5 * published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
14 *
15 * Written by Matus Marhefka <mmarhefk@redhat.com>
16 *
17 ***********************************************************************
18 * Enters the namespace(s) of a process specified by a PID and then executes
19 * the indicated program inside that namespace(s).
20 *
21 */
22
23#define _GNU_SOURCE
24#include <sched.h>
25#include <sys/syscall.h>
26#include <sys/types.h>
27#include <sys/wait.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <string.h>
31#include <errno.h>
32#include "test.h"
33#include "linux_syscall_numbers.h"
34#include "lapi/namespaces_constants.h"
35#include "ns_common.h"
36
37char *TCID = "ns_exec";
38int ns_fd[NS_TOTAL];
39int ns_fds;
40
41
42void print_help(void)
43{
44	int i;
45
46	printf("usage: ns_exec <NS_PID> <%s", params[0].name);
47
48	for (i = 1; params[i].name; i++)
49		printf("|,%s", params[i].name);
50	printf("> <PROGRAM> [ARGS]\nSecond argument indicates the types"
51	       " of a namespaces maintained by NS_PID\nand is specified"
52	       " as a comma separated list.\nExample: ns_exec 1234 net,ipc"
53	       " ip a\n");
54}
55
56static int open_ns_fd(const char *pid, const char *ns)
57{
58	int fd;
59	char file_buf[30];
60
61	sprintf(file_buf, "%s/%s/ns/%s", PROC_PATH, pid, ns);
62
63	fd = open(file_buf, O_RDONLY);
64	if (fd > 0) {
65		ns_fd[ns_fds] = fd;
66		++ns_fds;
67		return 0;
68	} else if (fd == -1 && errno != ENOENT) {
69		tst_resm(TINFO | TERRNO, "open");
70		return -1;
71	}
72
73	return 0;
74}
75
76static void close_ns_fd(void)
77{
78	int i;
79
80	for (i = 0; i < ns_fds; i++)
81		close(ns_fd[i]);
82}
83
84static int child_fn(void *arg)
85{
86	char **args = (char **)arg;
87
88	execvp(args[3], args+3);
89	tst_resm(TINFO | TERRNO, "execvp");
90	return 1;
91}
92
93/*
94 * ./ns_exec <NS_PID> <ipc,mnt,net,pid,user,uts> <PROGRAM> [ARGS]
95 */
96int main(int argc, char *argv[])
97{
98	int i, rv, pid;
99	char *token;
100
101	rv = syscall(__NR_setns, -1, 0);
102	if (rv == -1 && errno == ENOSYS) {
103		tst_resm(TINFO, "setns is not supported in the kernel");
104		return 1;
105	}
106
107	if (argc < 4) {
108		print_help();
109		return 1;
110	}
111
112	memset(ns_fd, 0, sizeof(ns_fd));
113	while ((token = strsep(&argv[2], ","))) {
114		struct param *p = get_param(token);
115
116		if (!p) {
117			tst_resm(TINFO, "Unknown namespace: %s", token);
118			print_help();
119			return 1;
120		}
121
122		if (open_ns_fd(argv[1], token) != 0)
123			return 1;
124	}
125
126	if (ns_fds == 0) {
127		tst_resm(TINFO, "no namespace entries in /proc/%s/ns/",
128			 argv[1]);
129		return 1;
130	}
131
132	for (i = 0; i < ns_fds; i++) {
133		if (syscall(__NR_setns, ns_fd[i], 0) == -1) {
134			tst_resm(TINFO | TERRNO, "setns");
135			close_ns_fd();
136			return 1;
137		}
138	}
139
140	pid = ltp_clone_quick(SIGCHLD, (void *)child_fn, (void *)argv);
141	if (pid == -1) {
142		tst_resm(TINFO | TERRNO, "ltp_clone_quick");
143		close_ns_fd();
144		return 1;
145	}
146
147	if (waitpid(pid, &rv, 0) == -1) {
148		tst_resm(TINFO | TERRNO, "waitpid");
149		return 1;
150	}
151
152	close_ns_fd();
153
154	if (WIFEXITED(rv))
155		return WEXITSTATUS(rv);
156
157	return 0;
158}
159