iproute2/lib/exec.c
Matteo Croce 903818fbf9 netns: switch netns in the child when executing commands
'ip netns exec' changes the current netns just before executing a child
process, and restores it after forking. This is needed if we're running
in batch or do_all mode.
Some cleanups must be done both in the parent and in the child: the
parent must restore the previous netns, while the child must reset any
VRF association.
Unfortunately, if do_all is set, the VRF are not reset in the child, and
the spawned processes are started with the wrong VRF context. This can
be triggered with this script:

	# ip -b - <<-'EOF'
		link add type vrf table 100
		link set vrf0 up
		link add type dummy
		link set dummy0 vrf vrf0 up
		netns add ns1
	EOF
	# ip -all -b - <<-'EOF'
		vrf exec vrf0 true
		netns exec setsid -f sleep 1h
	EOF
	# ip vrf pids vrf0
	  314  sleep
	# ps 314
	  PID TTY      STAT   TIME COMMAND
	  314 ?        Ss     0:00 sleep 1h

Refactor cmd_exec() and pass to it a function pointer which is called in
the child before the final exec. In the netns exec case the function just
resets the VRF and switches netns.

Doing it in the child is less error prone and safer, because the parent
environment is always kept unaltered.

After this refactor some utility functions became unused, so remove them.

Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
2019-06-20 14:30:41 -07:00

47 lines
751 B
C

/* SPDX-License-Identifier: GPL-2.0 */
#include <sys/wait.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include "utils.h"
#include "namespace.h"
int cmd_exec(const char *cmd, char **argv, bool do_fork,
int (*setup)(void *), void *arg)
{
fflush(stdout);
if (do_fork) {
int status;
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid != 0) {
/* Parent */
if (waitpid(pid, &status, 0) < 0) {
perror("waitpid");
exit(1);
}
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
exit(1);
}
}
if (setup && setup(arg))
return -1;
if (execvp(cmd, argv) < 0)
fprintf(stderr, "exec of \"%s\" failed: %s\n",
cmd, strerror(errno));
_exit(1);
}