Launch execlp as background process - c

I'm trying to launch a program using execlp, continue with the program. and get control of the shell back.
This is what I did after some research from SO.
pid_t child;
pid_t sid;
child = fork();
if (!child) {
sid = setsid();
if(!sid) {
exit(1);
}
execlp(RUN_EXE, RUN_EXE, SPEC_RUN.run_args[j], (char *)0);
}
But I'm unable to print anything after execlp. The execlp works correctly for me.
What do I need to do for the shell to return back?

When successful, the execve(2) syscall does not return (it can return only on failure). So does the execlp(3) wrapper.
You usually want to execve inside the child process. And fork(2) could fail. You usually should call fflush(3) before fork-ing . So code:
fflush(NULL);
pid_t p = fork();
if (p < 0) { perror("fork"); exit (EXIT_FAILURE); };
if (p == 0) { /* child process */
execlp(RUN_EXE, RUN_EXE, SPEC_RUN.run_args[j], (char *)0);
perror("execlp");
fflush(NULL);
_exit(EXIT_FAILURE);
}
/// continue in the parent process
printf("child process is %d\n", (int)p);
Don't forget to wait the child, e.g. using waitpid(2)
See also system(3), popen(3), daemon(3), posix_spawn and read Advanced Linux Programming (which has a nice chapter explaining these things).
Also, use strace(1) to understand how things work.

If I understand you correctly, you want to create a subprocess, run a program in that process, and then wait for it to finish. Each of those three steps is its own operation on Unix, when working with system primitives directly. You know about fork() and execlp() already; the third step, waiting for a subprocess to finish, is done with waitpid() and its relatives.
Building on what Basile wrote, here is the missing piece:
#define _POSIX_C_SOURCE 200809L /* strsignal */
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
void run_program(void)
{
int status;
pid_t pid;
fflush(0);
/* create the subprocess */
pid = fork();
if (p < 0) { /* fork failed */
perror("fork");
exit(1);
}
/* in the child only, execute a program */
if (p == 0) {
execlp(RUN_EXE, RUN_EXE, SPEC_RUN.run_args[j], (char *)0);
/* If execlp returns, it failed. It is unsafe to call `exit` if this
happens; you must instead use `_exit`. This means you have to flush
output manually. */
fprintf(stderr, "execlp: %s: %s\n", RUN_EXE, strerror(errno));
fflush(stderr);
_exit(1);
}
/* in the parent, wait for the child to finish */
if (waitpid(pid, &status, 0) != pid) {
perror("waitpid");
exit(1);
}
/* decode and report any failure of the child */
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 0)
return; /* success! */
fprintf(stderr, "%s: unsuccessful exit %d\n",
RUN_EXE, WEXITSTATUS(status));
exit(1);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "%s: %s%s\n",
RUN_EXE,
strsignal(WTERMSIG(status)),
WCOREDUMP(status) ? " (core dumped)" : "");
exit(1);
}
fprintf(stderr, "%s: impossible exit status %04x\n",
RUN_EXE, status);
exit(1);
}
... If that looks like a giant ball of hair that you don't want to deal with, you should consider using the higher-level library functions system() and/or popen() instead. They have their own flaws -- most importantly, they go through /bin/sh, which is often Not What You Want -- but they are easier to work with in simple cases.

Related

Redirection of stdin and stdout via pipes in C works for external programmes but not for recursive call

I am trying to communicate with forked child processes via pipe redirection of stdin and stdout in C. I already managed to get this to work for shell commands (like ls, for example) executed in child processes. However, I wasn't able to recursively execute the same program and redirect the output (printed by printf(), fprintf() to stdout, ...) via the pipes from the child process to the parent (in this test to stdout of the parent), although this works fine for ls or similar commands.
Here's how I tried to approach this:
I create a pipe, the reading end is for the parent, the child process should write to the writing end.
The Process forks, both processes close the unused end, respectively.
The writing end of the pipe is redirected to STDOUT_FILENO and closed
The child process executes the program recursively (it is called ./to2)
As mentioned, this does work if I execute ls in the child process, but not if I try to call the same program recursively. Here's my test program where I tried to get this to work:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
static void usage(void){
fprintf(stderr,"RIP");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]){
if(argc > 1){
dprintf(STDOUT_FILENO,"Please work\n");
printf("\n THIS IS A MESSAGE FROM THE CHILD \n");
fputs("Pretty Please!\n",stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if(-1 == pipe(p1)) {
fprintf(stderr,"pipe\n");
fprintf(stderr,"%s\n",strerror(errno));
usage();
}
int f = fork();
if(f == 0){
close(p1[0]);
if(dup2(p1[1],STDOUT_FILENO) < 0){
fprintf(stderr,"dup2\n");
usage();
}
close(p1[1]);
//I want this to work:
//execlp("./to2", "./to2", "-e");
//This works fine:
execlp("ls", "ls");
exit(EXIT_SUCCESS);
} else if (f == -1) {
usage();
} else {
close(p1[1]);
int w = -1;
if(-1 == wait(&w)) usage();
char b[12];
memset(b,0,12);
read(p1[0],&b,12);
char reading_buf[1];
while(read(p1[0], reading_buf, 1) > 0){
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
}
}
For testing purposes, the function is called recursively with additional arguments, while the parent program is called without additional arguments (hence the if(argc>1)).
In the final program, endless recursion is being avoided by other means.
Did I understand something wrongly? I am pretty confused by the fact that the only thing that doesn't seem to work is redirecting the output of my own
program...
Thank you very much in advance, any help or ideas are greatly appreciated.
The primary problem is precisely as outlined in the comments — you are not calling execlp() correctly (nor ls in the alternative). You must make the last argument on those function calls into an explicit null pointer, as shown in this code, which is a mostly mildly edited version of what's in the question:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static void usage(void)
{
fprintf(stderr, "RIP\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
if (argc > 1)
{
dprintf(STDOUT_FILENO, "Please work\n");
printf("THIS IS A MESSAGE FROM THE CHILD\n");
fputs("Pretty Please!\n", stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if (-1 == pipe(p1))
{
fprintf(stderr, "pipe: %s\n", strerror(errno));
usage();
}
int f = fork();
if (f == 0)
{
close(p1[0]);
if (dup2(p1[1], STDOUT_FILENO) < 0)
{
fprintf(stderr, "dup2: %s\n", strerror(errno));
usage();
}
close(p1[1]);
execlp(argv[0], argv[0], "-e", (char *)0);
fprintf(stderr, "failed to exec %s again\n", argv[0]);
exit(EXIT_FAILURE);
}
else if (f == -1)
{
usage();
}
else
{
close(p1[1]);
char b[13];
memset(b, 0, 13);
if (read(p1[0], &b, 12) < 0)
{
fprintf(stderr, "Failed to read from pipe (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
int len = strcspn(b, "\n");
printf("M1 [%.*s]\n", len, b);
char reading_buf[1];
while (read(p1[0], reading_buf, 1) > 0)
{
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
int w = -1;
if (-1 == wait(&w))
usage();
}
return 0;
}
Two important changes should be highlighted:
This code echoes the first line of data — the one written by dprintf() — whereas the original code just read it and discarded it.
The wait() call is after the input, not before. If the child had more data to write than a set of fixed messages, it could block waiting for the parent to read some of the data, while the parent is blocked waiting for the child to exit. This would be a deadlock.
The usage() function is not appropriately named — it doesn't report how to run the program. I also exit with a failure status, not success, if the child process fails the execlp().
Under peculiar circumstances, the wait() call might report on the exit status from some child other than the one that was forked. It is generally best to use a loop to reap such children. However, the circumstances required are extremely peculiar — the process which launched the parent with an exec*() function must have previously created some children for which it didn't wait, so that they are inherited by the parent process (because the PID doesn't change across an exec*() call).

prctl(PR_SET_PDEATHSIG) race condition

As I understand, the best way to achieve terminating a child process when its parent dies is via prctl(PR_SET_PDEATHSIG) (at least on Linux): How to make child process die after parent exits?
There is one caveat to this mentioned in man prctl:
This value is cleared for the child of a fork(2) and (since Linux 2.4.36 / 2.6.23) when executing a set-user-ID or set-group-ID binary, or a binary that has associated capabilities (see capabilities(7)). This value is preserved across execve(2).
So, the following code has a race condition:
parent.c:
#include <unistd.h>
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
execl("./child", "child", NULL, NULL);
}
return 0;
}
child.c:
#include <sys/prctl.h>
#include <signal.h>
int main(int argc, char **argv) {
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
// ...
return 0;
}
Namely, the parent count die before prctl() is executed in the child (and thus the child will not receive the SIGKILL). The proper way to address this is to prctl() in the parent before the exec():
parent.c:
#include <unistd.h>
#include <sys/prctl.h>
#include <signal.h>
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
execl("./child", "child", NULL, NULL);
}
return 0;
}
child.c:
int main(int argc, char **argv) {
// ...
return 0;
}
However, if ./child is a setuid/setgid binary, then this trick to avoid the race condition doesn't work (exec()ing the setuid/setgid binary causes the PDEATHSIG to be lost as per the man page quoted above), and it seems like you are forced to employ the first (racy) solution.
Is there any way if child is a setuid/setgid binary to prctl(PR_SET_PDEATH_SIG) in a non-racy way?
It is much more common to have the parent process set up a pipe. Parent process keeps the write end open (pipefd[1]), closing the read end (pipefd[0]). Child process closes the write end (pipefd[1]), and sets the read end (pipefd[1]) nonblocking.
This way, the child process can use read(pipefd[0], buffer, 1) to check if the parent process is still alive. If the parent is still running, it will return -1 with errno == EAGAIN (or errno == EINTR).
Now, in Linux, the child process can also set the read end async, in which case it will be sent a signal (SIGIO by default) when the parent process exits:
fcntl(pipefd[0], F_SETSIG, desired_signal);
fcntl(pipefd[0], F_SETOWN, getpid());
fcntl(pipefd[0], F_SETFL, O_NONBLOCK | O_ASYNC);
Use a siginfo handler for desired_signal. If info->si_code == POLL_IN && info->si_fd == pipefd[0], the parent process either exited or wrote something to the pipe. Because read() is async-signal safe, and the pipe is nonblocking, you can use read(pipefd[0], &buffer, sizeof buffer) in the signal handler whether the parent wrote something, or if parent exited (closed the pipe). In the latter case, the read() will return 0.
As far as I can see, this approach has no race conditions (if you use a realtime signal, so that the signal is not lost because an user-sent one is already pending), although it is very Linux-specific. After setting the signal handler, and at any point during the lifetime of the child process, the child can always explicitly check if the parent is still alive, without affecting the signal generation.
So, to recap, in pseudocode:
Construct pipe
Fork child process
Child process:
Close write end of pipe
Install pipe signal handler (say, SIGRTMIN+0)
Set read end of pipe to generate pipe signal (F_SETSIG)
Set own PID as read end owner (F_SETOWN)
Set read end of pipe nonblocking and async (F_SETFL, O_NONBLOCK | O_ASYNC)
If read(pipefd[0], buffer, sizeof buffer) == 0,
the parent process has already exited.
Continue with normal work.
Child process pipe signal handler:
If siginfo->si_code == POLL_IN and siginfo->si_fd == pipefd[0],
parent process has exited.
To immediately die, use e.g. raise(SIGKILL).
Parent process:
Close read end of pipe
Continue with normal work.
I do not expect you to believe my word.
Below is a crude example program you can use to check this behaviour yourself. It is long, but only because I wanted it to be easy to see what is happening at runtime. To implement this in a normal program, you only need a couple of dozen lines of code. example.c:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
if (!done)
done = signum;
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
static int deathfd = -1;
static void death(int signum, siginfo_t *info, void *context)
{
if (info->si_code == POLL_IN && info->si_fd == deathfd)
raise(SIGTERM);
}
static int install_death(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_sigaction = death;
act.sa_flags = SA_SIGINFO;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(void)
{
pid_t child, p;
int pipefd[2], status;
char buffer[8];
if (install_done(SIGINT)) {
fprintf(stderr, "Cannot set SIGINT handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (pipe(pipefd) == -1) {
fprintf(stderr, "Cannot create control pipe: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork child process: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
if (!child) {
/*
* Child process.
*/
/* Close write end of pipe. */
deathfd = pipefd[0];
close(pipefd[1]);
/* Set a SIGHUP signal handler. */
if (install_death(SIGHUP)) {
fprintf(stderr, "Child process: cannot set SIGHUP handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Set SIGTERM signal handler. */
if (install_done(SIGTERM)) {
fprintf(stderr, "Child process: cannot set SIGTERM handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* We want a SIGHUP instead of SIGIO. */
fcntl(deathfd, F_SETSIG, SIGHUP);
/* We want the SIGHUP delivered when deathfd closes. */
fcntl(deathfd, F_SETOWN, getpid());
/* Make the deathfd (read end of pipe) nonblocking and async. */
fcntl(deathfd, F_SETFL, O_NONBLOCK | O_ASYNC);
/* Check if the parent process is dead. */
if (read(deathfd, buffer, sizeof buffer) == 0) {
printf("Child process (%ld): Parent process is already dead.\n", (long)getpid());
return EXIT_FAILURE;
}
while (1) {
status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
if (status == SIGINT)
printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
else
if (status)
break;
printf("Child process (%ld): Tick.\n", (long)getpid());
fflush(stdout);
sleep(1);
status = __atomic_fetch_and(&done, 0, __ATOMIC_SEQ_CST);
if (status == SIGINT)
printf("Child process (%ld): SIGINT caught and ignored.\n", (long)getpid());
else
if (status)
break;
printf("Child process (%ld): Tock.\n", (long)getpid());
fflush(stdout);
sleep(1);
}
printf("Child process (%ld): Exited due to %s.\n", (long)getpid(),
(status == SIGINT) ? "SIGINT" :
(status == SIGHUP) ? "SIGHUP" :
(status == SIGTERM) ? "SIGTERM" : "Unknown signal.\n");
fflush(stdout);
return EXIT_SUCCESS;
}
/*
* Parent process.
*/
/* Close read end of pipe. */
close(pipefd[0]);
while (!done) {
fprintf(stderr, "Parent process (%ld): Tick.\n", (long)getpid());
fflush(stderr);
sleep(1);
fprintf(stderr, "Parent process (%ld): Tock.\n", (long)getpid());
fflush(stderr);
sleep(1);
/* Try reaping the child process. */
p = waitpid(child, &status, WNOHANG);
if (p == child || (p == (pid_t)-1 && errno == ECHILD)) {
if (p == child && WIFSIGNALED(status))
fprintf(stderr, "Child process died from %s. Parent will now exit, too.\n",
(WTERMSIG(status) == SIGINT) ? "SIGINT" :
(WTERMSIG(status) == SIGHUP) ? "SIGHUP" :
(WTERMSIG(status) == SIGTERM) ? "SIGTERM" : "an unknown signal");
else
fprintf(stderr, "Child process has exited, so the parent will too.\n");
fflush(stderr);
break;
}
}
if (done) {
fprintf(stderr, "Parent process (%ld): Exited due to %s.\n", (long)getpid(),
(done == SIGINT) ? "SIGINT" :
(done == SIGHUP) ? "SIGHUP" : "Unknown signal.\n");
fflush(stderr);
}
/* Never reached! */
return EXIT_SUCCESS;
}
Compile and run the above using e.g.
gcc -Wall -O2 example.c -o example
./example
The parent process will print to standard output, and the child process to standard error. The parent process will exit if you press Ctrl+C; the child process will ignore that signal. The child process uses SIGHUP instead of SIGIO (although a realtime signal, say SIGRTMIN+0, would be safer); if generated by the parent process exiting, the SIGHUP signal handler will raise SIGTERM in the child.
To make the termination causes easy to see, the child catches SIGTERM, and exits the next iteration (a second later). If so desired, the handler can use e.g. raise(SIGKILL) to terminate itself immediately.
Both parent and child processes show their process IDs, so you can easily send a SIGINT/SIGHUP/SIGTERM signal from another terminal window. (The child process ignores SIGINT and SIGHUP sent from outside the process.)
Your last code snippet still contains a race condition:
int main(int argc, char **argv) {
int f = fork();
if (fork() == 0) {
// <- !!!race time!!!
prctl(PR_SET_PDEATHSIG, SIGKILL); // ignore error checking for now
execl("./child", "child", NULL, NULL);
}
return 0;
}
Meaning that in the child, after the fork, until the prctl() has visible effects (think: returns), there is a time-window where the parent may exit.
To fix this race you have to save the PID of the parent before the fork and check it after the prctl() call, e.g.:
pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() != ppid_before_fork)
exit(1);
// continue child execution ...
(see also)
Regarding executing a setuid/setgid program: You can then pass the ppid_before_fork by other means (e.g. in the argument or environment vector) and execute the prctl() (including the comparison) after the exec, i.e. inside the execed binary.
I don't know this for sure, but clearing the parent death signal on execve when invoking a set-id binary looks like an intentional restriction for security reasons. I'm not sure why, considering that you can use kill to send signals to setuid programs that share your real user ID, but they wouldn't have bothered making that change in 2.6.23 if there wasn't a reason to disallow it.
Since you control the code of the set-id child, here is a kludge: make the call to prctl, then immediately afterward, call getppid and see if it returns 1. If it does, then either the process was started directly by init (which is not as rare as it used to be) or the process was reparented to init before it had a chance to call prctl, which means its original parent is dead and it should exit.
(This is a kludge because I know of no way to rule out the possibility that the process was started directly by init. init never exits, so you have one case where it should exit and one case where it shouldn't and no way to tell which. But if you know from the larger design that the process will not be started directly by init, it should be reliable.)
(You must call getppid after prctl, or you have only narrowed the race window, not eliminated it.)

Get stdout of already running process in Linux in C

Right now, I'm having to start an external process in C. I'm currently using posix_spawn to create the process. It is necessary that I can monitor whether or not the process has terminated. I need to also have a link to the standard out of the process. I've looked at using popen, however, it does not provide an "easy" way of getting the pid. I'm slowly going insane as it can't possibly be this hard to get the stdout of a running process in Linux.
Also, on a further note, I need help deciphering what the file_actions parameter is supposed to mean. man(3) for posix_spawn on this topic says:
If file_actions is not NULL, then the file descriptors open in the child process shall be those open in the calling process as modified by the spawn file actions object pointed to by file_actions and the FD_CLOEXEC flag of each remaining open file descriptor after the spawn file actions have been processed.
If that isn't the definition of a run-on sentence, I have no idea what is.
Since you have the PID (returned from posix_spawn) and you are running Linux, you will find the stdout of the process at /proc/<pid>/fd/1. Just open (or fopen) the file for reading.
The standard way is to use fork though. Use pipe and dup2 to get a file descriptor for reading the child's output, as in this question.
You can use posix_spawn for this, without having to use race-condition-prone, Linux-specific /proc/<pid>/fd/N. You can keep all the benefits of posix_spawn.
You were on the right track thinking about file_actions. Below is an example that prints out the child's stdout in Python-style triple quotes, as well as the child's exit code, from the parent process using posix_spawn and file_actions.
Here is an example of the example output.
child pid: 17468
child exit status: 0
child stdout:
"""Hello World!
"""
Here is the example.
#define _DEFAULT_SOURCE
#include <spawn.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
extern char **environ;
static void dump_child_stdout(int filedes)
{
ssize_t num_read;
char buf[1];
printf("child stdout:\n\"\"\"");
for (;;)
{
num_read = read(filedes, buf, sizeof(buf));
if (num_read > 0)
{
printf("%c", buf[0]);
}
else
{
break;
}
}
printf("\"\"\"\n");
}
int main(int argc, char *argv[])
{
int status;
pid_t pid;
int out[2];
posix_spawn_file_actions_t action;
char *args[] = {"/bin/echo", "Hello World!", NULL };
posix_spawn_file_actions_init(&action);
pipe(out);
posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);
posix_spawn_file_actions_addclose(&action, out[0]);
status = posix_spawn(&pid, args[0], &action, NULL, args, environ);
if (status == 0)
{
printf("child pid: %d\n", pid);
if (waitpid(pid, &status, 0) < 0)
{
perror("waitpid");
}
else
{
if (WIFEXITED(status))
{
printf("child exit status: %d\n", WEXITSTATUS(status));
}
else
{
printf("child died an unnatural death.\n");
}
close(out[1]);
dump_child_stdout(out[0]);
}
}
else
{
fprintf(stderr, "posix_spawn: %s\n", strerror(status));
close(out[1]);
}
posix_spawn_file_actions_destroy(&action);
return 0;
}

Unix Pipes - Pipeline between three processes

I'm creating a small program which contains three processes; a source process, a filter process and a sink process. The stdout of the source process is redirected to the stdin of the filter process, and the filter process' stdout is redirected to the sink process' stdin.
My problem is that no output is printed to stdout from the sink process. Can any of you see the problem in the following tiny snippet of code?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// Pipes
// pipe1 is from source to filter, pipe2 is from filter to sink
int pipe1[2], pipe2[2];
// Create pipes
if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
{
perror("Creating pipes failed!");
}
if (fork() == 0)
{
close(1);
dup(pipe1[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe2[1]);
execlp("ls", "ls", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe1[0]);
close(pipe1[1]);
close(1);
dup(pipe2[1]);
close(pipe2[0]);
execlp("sort", "sort", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe2[0]);
execlp("more", "more", NULL);
exit(0);
}
}
}
wait(NULL);
printf("Done.\n");
return 0;
}
BR
Jacob
I think problem may be, wait will only wait for one process. And when the parent exits after first child returns, I suspect more command also decides to terminate, because it may get SIGHUP (speculation, not sure).
But, check for errors from on all system calls! Also for wait calls which succeeded, print why the child exited (was it signal or normal exit, and if it was normal exit, what was exit code).
Also note, perror does not exit, it only prints.
It is kind of pointless trying to see why some code fails, if it does not have error handling in it...
Some easy way to do pipes for your scenario:
char cmd[MAX_LEN];
sprintf(cmd, "%s | %s | %s", app1, app2, app3); //app123 holds app name + args
system(cmd);
if you want to capture the output of the last app, use popen:
FILE pPipe = popen(cmd, "rt"); /* same access flag as fopen()*/
while (NULL != fget(buf, buf_len, pPipe)) {
// do something with the read line in 'buf'
}

How to trace a process for system calls?

I am trying to code a program that traces itself for system calls. I am having a difficult time making this work. I tried calling a fork() to create an instance of itself (the code), then monitor the resulting child process.
The goal is for the parent process to return the index of every system call made by the child process and output it to the screen. Somehow it is not working as planned.
Here is the code:
#include <unistd.h> /* for read(), write(), close(), fork() */
#include <fcntl.h> /* for open() */
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
pid_t child;
long orig_eax;
child = fork();
if (0 == child)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if (argc != 3) {
fprintf(stderr, "Usage: copy <filefrom> <fileto>\n");
return 1;
}
int c;
size_t file1_fd, file2_fd;
if ((file1_fd = open(argv[1], O_RDONLY)) < 0) {
fprintf(stderr, "copy: can't open %s\n", argv[1]);
return 1;
}
if ((file2_fd = open(argv[2], O_WRONLY | O_CREAT)) < 0) {
fprintf(stderr, "copy: can't open %s\n", argv[2]);
return 1;
}
while (read(file1_fd, &c, 1) > 0)
write(file2_fd, &c, 1);
}
else
{
wait(NULL);
orig_eax = ptrace (PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);
printf("copy made a system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
This code was based on this code:
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h> /* For constants
ORIG_EAX etc */
int main()
{
pid_t child;
long orig_eax;
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
}
else {
wait(NULL);
orig_eax = ptrace(PTRACE_PEEKUSER,
child, 4 * ORIG_EAX,
NULL);
printf("The child made a "
"system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
The output of this one is:
The child made a system call 11
which is the index for the exec system call.
According to the man pages for wait():
All of these system calls are used to wait for state changes in a child
of the calling process, and obtain information about the child whose
state has changed. A state change is considered to be: the child terminated;
the child was stopped by a signal; or the child was resumed by
a signal.
The way I understand it is that every time a system call is invoked by a user program, the kernel will first inspect if the process is being traced prior to executing the system call routine and pauses that process with a signal and returns control to the parent. Wouldn't that be a state change already?
The problem is that when the child calls ptrace(TRACEME) it sets itself up for tracing but doesn't actually stop -- it keeps going until it calls exec (in which case it stops with a SIGTRAP), or it gets some other signal. So in order for you to have the parent see what it does WITHOUT an exec call, you need to arrange for the child to receive a signal. The easiest way to do that is probably to have the child call raise(SIGCONT); (or any other signal) immediately after calling ptrace(TRACEME)
Now in the parent you just wait (once) and assume that the child is now stopped at a system call. This won't be the case if it stopped at a signal, so you instead need to call wait(&status) to get the child status and call WIFSTOPPED(status) and WSTOPSIG(status) to see WHY it has stopped. If it has stopped due to a syscall, the signal will be SIGTRAP.
If you want to see multiple system calls in the client, you'll need to do all of this in a loop; something like:
while(1) {
wait(&status);
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
// stopped before or after a system call -- query the child and print out info
}
if (WIFEXITED(status) || WIFSIGNALED(status)) {
// child has exited or terminated
break;
}
ptrace(PTRACE_SYSCALL, childpid, 0, 0); // ignore any signal and continue the child
}
Note that it will stop TWICE for each system call -- once before the system call and a second time just after the system call completes.
you are basically trying to write strace binary in linux, which traces the system calls of the process. Linux provides ptrace(2) system call for this. ptrace system call takes 4 arguement and the first arguement tells what you need to do. OS communicates with the parent process with signals and child process is stopped by sending SIGSTOP. broadly you need to follow below steps.
if(fork() == 0 )
{
//child process
ptrace(PTRACE_TRACEME, 0,0, 0);
exec(...);
}
else
{
start:
wait4(...);
if (WIFSIGNALED(status)) {
//done
}
if (WIFEXITED(status)) {
//done
}
if(flag == startup)
{
flag = startupdone;
ptrace(PTRACE_SYSCALL, pid,0, 0) ;
goto start;
}
if (if (WSTOPSIG(status) == SIGTRAP) {) {
//extract the register
ptrace(PTRACE_GETREGS,pid,(char *)&regs,0)
}
Note the register reading and interpretation will depend on your architecture. The above code is just an example to get it right you need to dig deeper. have a look at strace code for further understanding.
In your parent how many calls do you want to monitor? If you want more than one you're going to need some kind of loop.
Note the line in the example, it's important:
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
Looking at the man page the child needs to either do a PTRACE_TRACEME and an exec, or the parent needs to trace using PTRACE_ATTACH. I don't see either in your code:
The parent can initiate a trace by calling fork(2) and having the resulting child do a PTRACE_TRACEME, followed (typically) by an exec(3). Alternatively, the parent may commence trace of an existing process using PTRACE_ATTACH.
Just putting together what Chris Dodd said:
#include <unistd.h> /* for read(), write(), close(), fork() */
#include <fcntl.h> /* for open() */
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
pid_t child;
int status;
long orig_eax;
child = fork();
if (0 == child)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
raise(SIGCONT);
if (argc != 3) {
fprintf(stderr, "Usage: copy <filefrom> <fileto>\n");
return 1;
}
int c;
size_t file1_fd, file2_fd;
if ((file1_fd = open(argv[1], O_RDONLY)) < 0) {
fprintf(stderr, "copy: can't open %s\n", argv[1]);
return 1;
}
if ((file2_fd = open(argv[2], O_WRONLY | O_CREAT)) < 0) {
fprintf(stderr, "copy: can't open %s\n", argv[2]);
return 1;
}
while (read(file1_fd, &c, 1) > 0)
write(file2_fd, &c, 1);
}
else
{
while(1){
wait(&status);
if(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP){
orig_eax = ptrace(PTRACE_PEEKUSER, child, sizeof(long) * ORIG_EAX, NULL);
printf("copy made a system call %ld\n", orig_eax);
}
if(WIFEXITED(status) || WIFSIGNALED(status)){
break;
}
ptrace(PTRACE_SYSCALL, child, 0, 0);
}
}
return 0;
}

Resources