Obtaining the status of execv without stopping the execution of the parent - c

Is there a way to obtain the status of a command run with execv without waiting for it with waitpid or wait?
The program I need to do reads other executable program as argument and need to print the status of this program when executed with execv exactly when the command is finished executing, but needs to keep executing after the fork instead of waiting for the program passed as argument to finish executing.
Here is what I have right now which doesn't seem to capture any status:
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio_ext.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/shm.h>
int status;
void child_exit_handler(int signal)
{
int pid = wait(&status);
}
int main(int argc, char *argv[])
{
pid_t pid;
signal(SIGCHLD, child_exit_handler);
for (int i = 1; i < argc; i++)
{
pid = fork();
if (pid == 0)
{
char *cmd[4];
cmd[0] = "sh";
cmd[1] = "-c";
cmd[2] = argv[i];
cmd[3] = NULL;
execv("/bin/bash", cmd);
}
}
if (pid > 0)
{
// do something;
// later do something with status
}
}

Related

Execv syscall program not getting arguements

I've written this program as a more precise version of GNU time.
The idea is you call it with your program and optionally some args. The program part is working but the args aren't being passed properly.
> gcc -o time time.c
> ./time sleep 3
exits right away instead of waiting 3 seconds, sleep is called but doesn't seem to see the args.
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("./time PROGRAM arg1 arg2 ...\n");
exit(-1);
}
struct rusage result;
pid_t cpid;
int id = fork();
if (id == 0) {
fclose(stdout);
fclose(stderr);
execv(argv[1], argv + 1);
} else {
cpid = wait(NULL);
}
getrusage(RUSAGE_CHILDREN, &result);
unsigned long micro_seconds = result.ru_utime.tv_usec + result.ru_stime.tv_usec;
micro_seconds += (result.ru_utime.tv_sec + result.ru_stime.tv_sec) * 1000000;
printf("%lu\n", micro_seconds);
exit(0);
}
The first arguement of execv must be the full path not just 'sleep'. Calling ./time /usr/bin/sleep 3 will work.

I'm getting a final link error when I compile a c program

I'm writing this program:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main(){
int fd[2];
pid_t pid;
pipe(fd);
pid = fork();
if(pid>0)
{
while(1){
char * command = (char*)malloc(55);
printf("Command: ");
fgets(command,55,stdin);
write(fd[1],&command,55);
wait(NULL);
}
}
else if(pid)
{
char * command = (char*)malloc(55);
read(fd[0],&command,55);
printf("Child command: %s",command);
}
return 0;
}
and when I compile I'll get this error:
I can't figure out what could be. I know that I'm not using all libraries and checking for errors but i think that's not the issue here. Thanks! Have a great day!

Linux syscalls: PTRACE_O_TRACECLONE causes indefinite hanging

I have a binary from which I need to intercept a certain syscall--in this case unlinkat--and make it do nothing. I have the following code which works fine for a single process; however, with PTRACE_O_TRACECLONE added to the ptrace opts, after the tracee makes a call to clone, the waitpid call hangs forever. I've been pulling my hair out for days on different parts of the internet, to the point where I was going through the source of strace, and had in fact straced strace to see what the strace I had straced was ptracing.
Here's the source--I removed some stuff to make it as minimal as possible for readability.
#define _POSIX_C_SOURCE 200112L
// std (i think)
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// linux
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
#define OPTS PTRACE_O_TRACESYSGOOD // | PTRACE_O_TRACECLONE | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEFORK
#define WOPTS 0
/* The TRACEE. Executes the process we want to target with PTRACE_TRACEME */
int do_child(int argc, char **argv) {
char *args[argc + 1];
memcpy(args, argv, argc * sizeof(char *));
args[argc] = NULL;
ptrace(PTRACE_TRACEME);
kill(getpid(), SIGSTOP);
return execvp(args[0], args);
}
/* Waits for the next syscall and checks to see if the process has been exited */
int wait_for_syscall(pid_t child) {
int status;
while (1) {
ptrace(PTRACE_SYSCALL, child, 0, 0);
waitpid(child, &status, WOPTS); // <--- THIS CALL HANGS FOREVER AFTER CLONE
if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)
return 0;
if (WIFEXITED(status))
return 1;
}
return -1; // unreachable
}
/* The TRACER. Takes the pid of the child process that we just started and actually does the
PTRACE stuff by passing signals back and forth to that process. */
int do_trace(pid_t child) {
int status, syscall;
waitpid(child, &status, WOPTS);
ptrace(PTRACE_SETOPTIONS, child, 0, (unsigned long)OPTS);
while (1) {
// ptrace(PTRACE_SYSCALL) really needs to be called twice, first is before entry second is after exit, but idgaf
if (wait_for_syscall(child) != 0) {
break;
}
syscall = ptrace(PTRACE_PEEKUSER, child, sizeof(long) * ORIG_RAX);
switch (syscall) {
case SYS_clone:
fprintf(stderr, "DEBUG: clone detected\n");
break;
case SYS_unlinkat:
fprintf(stderr, "DEBUG: unlinkat detected\n");
ptrace(PTRACE_POKEUSER, child, sizeof(long) * RAX, 0);
break;
}
}
return 0;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s prog args\n", argv[0]);
exit(1);
}
pid_t child = fork();
if (child == 0) {
return do_child(argc - 1, argv + 1);
} else {
return do_trace(child);
}
return 0;
}
Just as a disclaimer, I am NOT a C developer, these days I mainly write Python, so a lot of this was just copied and pasted from different tutorials I found and I basically added/removed random shit until gcc didn't give me that many warnings.
Based on what I've read, I suspect the issue is something about raising signals to the processes involved and waiting for a SIGTRAP, I just have no real intuition on what to do at that level.
The solution was using libseccomp instead.
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <seccomp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int do_child(int argc, char **argv)
{
char *args[argc + 1];
memcpy(args, argv, argc * sizeof(char *));
args[argc] = NULL;
return execvp(args[0], args);
}
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s prog args\n", argv[0]);
exit(1);
}
// Init the filter
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_ALLOW); // default allow
// setup basic whitelist
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(0), SCMP_SYS(unlinkat), 0);
// build and load the filter
seccomp_load(ctx);
pid_t child = fork();
if (child == 0)
{
return do_child(argc - 1, argv + 1);
}
return 0;
}

Can't get execvp to execute file

I am trying to write a program that will fork, then open a file and execute it. The file it should execute is called child and it has been compiled. When I type ./child, it runs. However, when I run this program it does not execute the child program and I am prompted with the error message I put in "Execution failed". What I am doing wrong?
This is my parent class
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
int main (int argc, char **argv)
{
pid_t parent = getpid();
pid_t pid = fork();
if (pid == -1)
{
// error, failed to fork()
}
else if (pid > 0)
{
int status;
waitpid(pid, &status, 0);
}
else
{
int var = execvp("./child", NULL);
if(var < 0)
{
printf("Execution failed");
}
}
exit(0); // exec never returns
}
This is the child
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv)
{
printf ("Im the child");
exit (0);
}
I actually don't know what you are doing wrong. After a copy and a compilation (and several warning complains) your code runs fine (GCC 7.2).
Obviously, child must be in the same working directory in which you run your main executable (the one that forks).
But probably I would write that code in this way, but I'm not an expert in forking:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
extern int errno;
int main () {
pid_t pid = fork();
if (pid < 0) {
fprintf(stderr, "%s\n", strerror(errno));
return 1;
}
if (pid == 0) {
int ret = execl("./child", "", (char *)NULL);
if(ret < 0) {
fprintf(stderr, "%s\n", strerror(errno));
return 1;
}
} else {
wait(NULL);
}
return 0;
}
At least it tells you which error execl has encountered.

combine fork execl wait

I have to create prog1 which take one argument with number of children have to create. (example "./prog1 5" - will create 5 children) Each of children will generate random number from 1 to 20. This number will be given to execl which will start prog2 (in same folder) which take as a argument this random number. Prog2 should sleep this random number time. After that it should return this random number to parent.
I created something like this but it still don't work properly.
prog1:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n, i, pid;
int u = getppid();
int procesy = 0;
pid_t proc_id;
n = atoi(argv[1]);
for(i = 0; i < n; i++)
{
proc_id = fork();
if(proc_id==0)
{
srand(getpid());
u = 1 + rand()%20;
execl("./prog2", "prog2", u,0);
}
else
{
procesy++;
}
}
if(u == getppid())
{
for(i = 0; i < n; i++)
{
pid = wait(&u);
printf("Process %d ende\n", pid);
procesy--;
}
if(procesy == 0) printf("endc\n");
}
return 1;
}
prog2:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n;
n = atoi(argv[1]);
sleep(n);
exit(n);
}
Change your loop to look something like the following, in order to properly call execl():
if(proc_id==0)
{
char arg[16];
srand(getpid());
sprintf(arg, "%d", 1 + rand()%20);
execl("./prog2", "prog2", arg, 0);
printf("I should not be here!\n");
exit(-1);
}
Then get rid of if(u == getppid()) (but keep the contents of the conditional). It seems with that if you were trying to filter out the child from running that block. When execl() works, the child is not going to run anything in this code after the execl()... the printf and exit I added will not be run. Those lines will only run if execl() fails, and in this simple case, the only way it will fail is if you've provided improper arguments.

Resources