namespaces/ns_exec.cThis is namespaces/ns_exec.c, an example to accompany the book, The Linux Programming Interface. This file is not printed in the book; it demonstrates Linux features that are not described in the book (typically features that have appeared since the book was published). The source code file is copyright 2024, Michael Kerrisk, and is licensed under the GNU General Public License, version 3. In the listing below, the names of Linux system calls and C library functions are hyperlinked to manual pages from the Linux man-pages project, and the names of functions implemented in the book are hyperlinked to the implementations of those functions.
|
/* ns_exec.c Join a namespace using setns() and execute a command in the namespace. This is program is similar in concept to nsenter(1) (however, that program allows multiple namespaces to be joined), but has a simpler command-line interface. See https://lwn.net/Articles/531381/ */ #define _GNU_SOURCE #include <fcntl.h> #include <sched.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <sys/wait.h> /* A simple error-handling function: print an error message based on the value in 'errno' and terminate the calling process */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0)
static void usage(char *pname) { fprintf(stderr, "%s [-f] /proc/PID/ns/FILE cmd [arg...]\n", pname); fprintf(stderr, " -f Do a fork() after entering the namespace\n"); exit(EXIT_FAILURE); }
int main(int argc, char *argv[]) { /* Parse command-line options. The initial '+' character in the final getopt(3) argument prevents GNU-style permutation of command-line options. Preventing that is useful, since sometimes the 'command' to be executed by this program itself has command-line options. We don't want getopt() to treat those as options to this program. */ int do_fork = 0; int opt; while ((opt = getopt(argc, argv, "+f")) != -1) { switch (opt) { case 'f': do_fork = true; break; default: usage(argv[0]); } } if (argc < optind + 2) usage(argv[0]); /* Get file descriptor for namespace; the file descriptor is opened with O_CLOEXEC so as to ensure that it is not inherited by the program that is later executed. */ int fd = open(argv[optind], O_RDONLY | O_CLOEXEC); if (fd == -1) errExit("open"); if (setns(fd, 0) == -1) /* Join that namespace */ errExit("setns"); /* If '-f' was specified, fork to create a child that is waited on by the parent. This is useful when entering a PID namespaces, since setns() into a PID namespace does not move the calling process into the namespace, but only changes the PID namespace in which the children of the caller will be created. */ if (do_fork) { pid_t pid = fork(); if (pid == -1) errExit("fork"); if (pid != 0) { /* Parent lands here; child falls through */ wait(NULL); exit(EXIT_SUCCESS); } } /* Execute a command in namespace */ execvp(argv[optind + 1], &argv[optind + 1]); errExit("execvp"); }
Note that, in most cases, the programs rendered in these web pages are not free standing: you'll typically also need a few other source files (mostly in the lib/ subdirectory) as well. Generally, it's easier to just download the entire source tarball and build the programs with make(1). By hovering your mouse over the various hyperlinked include files and function calls above, you can see which other source files this file depends on.