I'm trying to run some code inside a separate UTS namespace, and I've been trying to do it in C.
I have a parent function with the following code:
void run (char **args, int arglen) {
printf("[*] Starting PID: %d\n", getpid());
int i=1;
cmd = concat_args(args, arglen);
pid_t child_pid = clone(child_container, child_stack STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID, NULL);
int status;
wait(&status);
printf("[*] Exit code: %d", status);
return;
}
which creates a new process and SHOULD wait for it to finish its execution.
The child has the following code:
void child_container () {
printf("This is the container!\n");
printf("[*] Child PID: %d\n", getpid());
system(cmd);
printf("test\n");
}
and I'd like to run any command before the parent shutting up.
The problem is that some commands (e.g. /bin/ls
) manage to execute, other (slower) commands (e.g. /bin/sh
) are shut down by the end of parent.
Since I used a wait function, I can't understand why the execution is stopping.
I tried using a sleep in the parent after the clone, and it delays the end of the program. This should mean that the problem is that the parent doesn't wait for its child.
I tried both wait and waitpid, but the process still ended.
Can the problem be the spawn of a new process (/bin/sh
)? How can I fix?
Edit:
This should be a reproducible example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 0x3ff
#define STACK_SIZE 8192
static char child_stack[STACK_SIZE];
char *cmd;
char *concat_args(char **args, int arglen);
void child_container ();
void parse_command(char *cmd, char **args, int argc);
void run (char **args, int arglen);
char *concat_args(char **args, int arglen) {
char *cmd = (char *)malloc((BUF_SIZE 1)*sizeof(char));
memset(cmd, 0, BUF_SIZE 1);
strncat(cmd, args[0], BUF_SIZE);
for (int i=1; i<arglen; i ) {
strncat(cmd, " ", BUF_SIZE);
strncat(cmd, args[i], BUF_SIZE);
}
return cmd;
}
void run (char **args, int arglen) {
printf("[*] Starting PID: %d\n", getpid());
int i=1;
cmd = concat_args(args, arglen);
pid_t child_pid = clone(child_container, child_stack STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID, NULL);
sleep(1);
int status, e_code;
e_code = wait(&status);
if (e_code == -1) {
perror("wait");
}
printf("[*] Exit code: %d\n[*] Status: %d\n", e_code, status);
return;
}
void child_container () {
printf("This is the container!\n");
printf("[*] Child PID: %d\n", getpid());
system(cmd);
printf("test\n");
}
void parse_command(char *cmd, char **args, int argc) {
if (!strcmp(cmd, "run")) {
run(args, argc-2);
} else {
error("No such command!");
}
}
int main(int argc, char **argv) {
const char *format = "./container run [cmd] [args]";
if (argc < 3) {
error("Wrong format!\nThe commands must be formatted in the following way:\t./container run [cmd] [args]");
return 0;
}
char *cmd = argv[1];
char **args = &argv[2];
parse_command(cmd, args, argc);
return 0;
}
CodePudding user response:
Extract from man page of clone
The child termination signal
When the child process terminates, a signal may be sent to the parent. The termination signal is specified in the low byte of flags (clone()) or in cl_args.exit_signal (clone3()). If this signal is specified as anything other than SIGCHLD, then the parent process must specify the __WALL or __WCLONE options when waiting for the child with wait(2). If no signal (i.e., zero) is specified, then the parent process is not signaled when the child terminates.
So, answer is clear. You did not specify a signal, so you are not notified when your child terminates, so wait has nothing to wait.
pid_t child_pid = clone(child_container, child_stack STACK_SIZE, CLONE_NEWUTS | CLONE_NEWPID | SIGCHLD, NULL);
should solve the problem (at least, without it, you have no chance to have wait working)