Ctrl+Z Signal handling in C - c

I am writing a simple shell in C.
However, I found that my program cannot properly handle the Ctrl+Z signal. My program looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
void interpreter() {
char input[256];
int i;
char dir[PATH_MAX+1];
char *argv[256];
int argc = 0;
char *token;
if (getcwd(dir, PATH_MAX+1) == NULL) {
//error occured
exit(0);
}
printf("[shell:%s]$ ", dir);
fgets(input,256,stdin);
if (strlen(input) == 0) {
exit(0);
}
input[strlen(input)-1] = 0;
if (strcmp(input,"") == 0) {
return;
}
token = strtok(input, " ");
while(token && argc < 255) {
argv[argc++] = token;
token = strtok(NULL, " ");
}
argv[argc] = 0;
pid_t forknum = fork();
if (forknum != 0) {
int status;
waitpid(forknum, &status, WUNTRACED);
} else {
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
setenv("PATH","/bin:/usr/bin:.",1);
execvp(argv[0], argv);
if (errno == ENOENT) {
printf("%s: command not found\n", argv[0]);
} else {
printf("%s: unknown error\n", argv[0]);
}
exit(0);
}
}
int main() {
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
while(1) {
interpreter();
}
}
I have ignored above signals in the main process.
When I start cat(1) and then hit Ctrl+Z, the next line of input will still be captured by the cat(1) program rather than my main process. It means that my main process will do nothing but if I wake up the cat(1) program, it will output what I typed immediately. All things go back to normal after this.
I can't figure out how to resolve this. I am still not sure if I have stated it clearly.

Interesting. Even though this is tagged Linux, I'll go out on a limb and say that you are running this on OS X.
When compiled on Linux, the problem is not there, but on Mac it happens exactly as you described. It looks like a bug in OS X: because both the shell process and cat(1) are on the same process group (since you don't explicitly change group membership), it seems like OS X makes the mistake of feeding the next input line to the fgets(3) call that is asleep in the cat(1) process, so you end up losing that line of input from the shell process (because it is consumed by the sleeping cat(1)).
The reason this doesn't happen with bash is because bash supports job control, and as such processes are put in separate process groups (in particular, bash chooses the first process of a process pipeline as the process group leader). So when you do the same thing on bash, each invocation of cat(1) ends up putting it in a separate process group (and then the shell controls which process group is in the foreground with tcsetpgrp(3)). So, at any time, it is clear which process group has control over terminal input; the moment you suspend cat(1) in bash, the foreground process group is changed to bash again and input is read successfully.
If you do the same as bash in your shell, it will work in Linux, OS/X, and basically any other UNIX variant (and it is how other shells do it too).
In fact, if you want your shell to have job support, you'll have to do this sooner or later (learn about process groups, sessions, tcsetpgrp(3), setpgid(2), etc.).
So, in short, do the right thing if you want job support and wrap the forked process in a new process group:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
void interpreter() {
char input[256];
char dir[PATH_MAX+1];
char *argv[256];
int argc = 0;
char *token;
if (getcwd(dir, PATH_MAX+1) == NULL) {
//error occured
exit(0);
}
printf("[shell:%s]$ ", dir);
fgets(input,256,stdin);
if (strlen(input) == 0) {
exit(0);
}
input[strlen(input)-1] = 0;
if (strcmp(input,"") == 0) {
return;
}
token = strtok(input, " ");
while(token && argc < 255) {
argv[argc++] = token;
token = strtok(NULL, " ");
}
argv[argc] = 0;
pid_t forknum = fork();
if (forknum != 0) {
setpgid(forknum, forknum);
signal(SIGTTOU, SIG_IGN);
tcsetpgrp(STDIN_FILENO, forknum);
tcsetpgrp(STDOUT_FILENO, forknum);
int status;
waitpid(forknum, &status, WUNTRACED);
tcsetpgrp(STDOUT_FILENO, getpid());
tcsetpgrp(STDIN_FILENO, getpid());
} else {
setpgid(0, getpid());
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
setenv("PATH","/bin:/usr/bin:.",1);
execvp(argv[0], argv);
if (errno == ENOENT) {
printf("%s: command not found\n", argv[0]);
} else {
printf("%s: unknown error\n", argv[0]);
}
exit(0);
}
}
int main() {
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
while(1) {
interpreter();
}
}
(Although, admittedly, it is unfortunate that OS X does such a poor job in this situation - you really shouldn't have to do this).
The changes are just inside the process-specific code: both the child and the parent call setpgid(2) to make sure that the newborn process is indeed in a single process group before either the parent of the process itself assumes that this is already true (this pattern is recommended in Advanced Programming in the UNIX Environment); the tcsetpgrp(3) call must be invoked by the parent.
Of course, this is far from complete, you then need to code the necessary functions to bring a job back to the foreground, list jobs, etc. But the code above works with your test scenario nonetheless.
Nitpick: you should be using sigaction(2) instead of the deprecated, unreliable and platform-dependent signal(3), but it's a minor issue here.

Related

My program does not stop running after finishing child process

I am now learning about folk, exec etc and I have this piece of code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t childpid;
int status;
childpid=fork();
if (childpid== -1){
perror("Failed to fork\n");
exit(1);
}
if (childpid==0) {
printf("I am in child process with id = %lu\n", (long)getpid());
execvp(argv[1], &argv[1]);
perror("exec failure ");
exit(1);
}
else {
printf("I am in parent process with id = %lu\n", (long)getpid());
exit(1);
}
}
The child process works fine but after that for some reason the program continues running without doing anything. It never prints "I am in child process with id = ...." or "I am in parent process with id =... ". It's like it never goes to parent process. Do you have any ideas why? Thanks in advance
From my top comment ...
You are creating a zombie process. This is because the parent process is not waiting for the child to complete.
The parent process will terminate [relatively] quickly. Thus, the child loses its parent and becomes a zombie. A zombie will be reparented by the kernel as a child of process 1 (e.g. systemd or initd).
To fix, add: wait(NULL); after the final printf
UPDATE:
Therefore do I need to always put wait(NULL) in these types of situations?
The TL;DR is ... Yes!
This is what you normally want to do for most programs.
One of the few times you would want to create a zombie is (e.g.) if you're a server program (e.g. inetd).
Servers want to run "detached". That is, as a child of the init process (e.g. systemd, initd, etc.). There is one and only one init process on the system.
All other processes are children of init, even if indirectly. For example, your program's process hierarchy was something like:
init -> window_manager -> xterm -> bash -> your_program
Anyway, most server programs these days are fired up by systemd directly. It examines some config files and starts things based on these config options. So, now, most server programs don't have to do anything special.
But, if you were testing a server of your own, invoked it from the command line, and wanted it to run [detached] in the background, you might do:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int opt_d;
int
main(int argc, char **argv)
{
char *cp;
pid_t childpid;
int status;
// skip over program name
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'd':
opt_d = 1;
break;
}
}
// detach into background
if (opt_d) {
childpid = fork();
if (childpid == -1) {
perror("Failed to detach\n");
exit(1);
}
// exit the parent -- child is now detached [and a zombie] and a child
// of the init process
if (childpid != 0)
exit(0);
}
childpid = fork();
if (childpid == -1) {
perror("Failed to fork\n");
exit(1);
}
if (childpid == 0) {
printf("I am in child process with id = %lu\n", (long) getpid());
execvp(*argv, argv);
perror("exec failure ");
exit(1);
}
printf("I am in parent process with id = %lu\n", (long) getpid());
wait(&status);
return 0;
}

How a child process kill other child process and then terminate?

Here is the code, where parent process writes a string input in pipe and children processes read this from pipe. If child process reads from pipe the word "end", then i want to terminate all the processes and then terminate itself, and if reads the word "finish" i want to raise a signal to father for killing all the processes and then exit. I run the code and i had segmentation fault. Why it is wrong?
#define _POSIX_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
void measure_time(int sig)
{
printf("child [%d] received signal %d\n", getpid(), sig);
}
int main(int argc, char *argv[])
{
int n_task = 4;
pid_t pid;
pid_t pid_array[n_task];
int fd[2];
for (int i = 0; i < n_task; i++)
{
pid = fork();
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
if (pid < 0)
{
perror("fork");
exit(1);
}
if (pid == 0) //child
{
char *buf;
close(fd[1]);
read(fd[0], buf, 10);
printf("I read: %s", buf);
if (strcmp(buf, "end") == 0)
{
for (int i = 0; i < n_task; i++)
kill(pid_array[i], SIGUSR1);
}else if(strcmp(buf,"finish") == 0){
/*Here i want father to kill all children and then exit.*/
}
exit(0);
}
close(fd[0]);
char *buf;
printf("Give the input string: \n");
scanf("%s", buf);
write(fd[1], buf, strlen(buf));
close(fd[1]);
pid_array[i] = pid;
}
sleep(1);
for (int i = 0; i < n_task; i++)
wait(NULL);
return (0);
}
Besides the issue of uninitialized buf identified by #G. Sliepen, the pipe() need be called before fork() as file descriptors are kept open when forking child process(s). This is also how pipe works.
You can try to change your code snippet to put pipe() before fork().
...
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork");
exit(1);
}
...
Please read the manual page of pipe(2) in which an example presented.
SO has this post fork() and pipes() in c explained this as well.
Update for terminating process(s)
This child process has no knowledge about existence of its siblings, but its parent process has. If not explicitly required, you can let the parent to do so, i.e. to "end" all child processes.
BTW, instead of sending signal SIGUSR1 it is better to send SIGTERM signal. Although SIGUSSR1 can cause the target process be terminated by default (see signal(7)).
To "finish", i.e. to kill (or terminate) all the child processes as well as parent process, you can simplly kill the parent. All its descendants got killed as well. Or, you can send signal to the same process group. See kill(2).
You are declaring a pointer buf, but did not initialize it. Subsequent calls to read() and scanf() will fail because the pointer is invalid.
You need to make sure buf is initialized and pointing to valid memory. A simple way to fix your code is to do:
char buf[10];
read(fd[0], buf, 10);
If you enable compiler warnings with -Wall, then the compiler will warn you about initialized variables.
Be aware of potential buffer overflows: if you declare char buf[10], make sure you will never write more than ten bytes into it. Also, check the return value of functions like read(), write(), scanf() to ensure no errors were encountered, otherwise the contents of the buffers or output files might not be as expected.

How to set status termination of a process C?

My program is a rudimental little shell.
It allow you to run programs in PATH as ls, cd..also with arguments.
To run the program type from terminal "./myshell2" then it starts and you can insert how many commands you want.
It starts a child process, runs execvp,it returns and restarts so you can type a new command.
When typed "Q" or "q" all the entire program should terminates.
The problem is that I don't know how to stop it,the code is below.
My idea is, when typed "Q" or "q", to kill the child process created and send a signal to comunicate its bad termination(of child process).
So the final status(from parent) 'll be not 1 and the function returns.
I commented some parts of the code hoping that it's easier to understand.
It works the problem is that to stop it I need of ctrl C.
I would like to say to child process that he must ends with a non-zero value.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
int main(int argc, char * argv[]) {
while(1)
{
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
printf("type the command to start (and arguments if required) \n"
"Q to quit\n");
char *dest[10]; // allow you to insert
char line[4096];//commands from terminal
if (fgets(line,sizeof(line),stdin)==0) return 1;
int i;
line[strcspn(line, "\n")] = '\0';
char *st = line;
for (i=0; i< 10 && (dest[i]=strsep(&st," "))!=NULL;i++)
continue;//now you typed the command
if ( ( memcmp(dest[0],"Q",1)==0 ) // if Q or q the program
|| (memcmp(dest[0],"q",1)==0) ) //must end
{
printf("got it!\n");
if (kill(getpid(),SIGSEGV)==-1) printf("kill error\n");
//in theory the process should terminates with bad status
// and the value of the variable "status" 'll be not 0
// I think that the problem is in this part of the code
}
if( strcmp(dest[0]," ")!=0 )
{
int res = execvp(dest[0], dest);
}
else
{ int res= execvp(dest[1],dest+1);}
perror("execvp error");
exit(EXIT_FAILURE);
}
int status;
pid_t child = wait(&status);
if (child == -1) {
perror("wait error");
exit(EXIT_FAILURE);
}
if (status==1)
break; //so it can exit from the loop that creates new process
setenv("WAIT","TRUE",0); //dont' worry about
//perror("setenv error\n");
if (memcmp("TRUE",getenv("WAIT"),4) == 0 ) //these 6 lines
printf("WAIT=TRUE\n");
else if(memcmp("FALSE",getenv("WAIT"),4) == 0 )
printf("WAIT=FALSE\n");
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
return EXIT_SUCCESS;
}
You're printing out WEXITSTATUS() for all cases, but that isn't right. You need to check if the status returned by wait is an exit status or not using WIFEXITED(). If it's non-zero then the child exited normally. Otherwise, you can use WIFSIGNALED() to see if the child was terminated and you'll get the signal from WTERMSIG()
if(WIFEXITED(status))
{
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
else if(WIFSIGNALED(status))
{
printf("end current process (signal=%d, child=%d)\n", WTERMSIG(status), son);
}
You really should have the parent process handle the inputting of the command and leave the child process to run it though.

foreground signal tcsetpgrp c

I'm making my own shell cause why the hell not.
When you run a command and end it with &, the process with be run in the background, so I'd like to make an fg command that you can use to put the background process to the foreground.
I have some troubles making the fg function.
If i understand it correctly, putting signal() in the child process will let the child process receive a signal.
Signal receives two arguments, signum and the handler function.
We're gonna use tcsetpgrp() to set a given background process to foreground. So in lsh_fg I call tcsetpgrp(STDIN_FILENO, pid).
So signum should be sigttou so it can receive the signal from tcsetpgrp().
I don't know what should be put inside the handler, since tcsetpgrp() is supposed to do as the man page describes it:
"
The function tcsetpgrp() makes the process group with process group ID
pgrp the foreground process group on the terminal associated to fd
"
As I understand it, tcsetpgrp() is sending a signal to the process that has signal(sigttou,handler), which is put to the foreground when it receives it. But I clearly misunderstood this since it's not working.
My questions: How should I understand the way tcsetpgrp() and signal(sigttou,handler) work together? And what should my handler include?
I really appreciate your answers cause i really got stuck here :-)
See my code below:
Ps: I'm new to C and system programming and this is my first post ever so any constructive criticism regarding my code is warmly welcome
THANKS A LOT :D
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
pid_t pid;
int toke_c;
//function declaration for the function pointers
int lsh_cd(char **args);
int lsh_pwd(char **args);
int lsh_exit(char **args);
int lsh_fg(char **args);
//An array of functions:
int (*builtin_func[]) (char **) = {
&lsh_cd,
&lsh_pwd,
&lsh_exit,
&lsh_fg
};
//An array of the given strings:
char *builtin_str[] = {
"cd",
"pwd",
"exit",
"fg"
};
///built in functions cd and pwd
int lsh_fg(char **args){
tcsetpgrp(STDIN_FILENO, pid);
return 1;
}
void fg_handler()
{
//What to put here???
}
///built in functions cd and pwd
int lsh_cd(char **args)
{
if (args[1] == NULL) {
fprintf(stderr, "lsh: cd: no arguments given\n");
} else {
if (chdir(args[1]) != 0) {
perror("lsh");
}
}
return 1;
}
int lsh_pwd(char **args)
{
char * cwd;
cwd=getcwd (NULL,0);
printf ("%s\n ", cwd);
return 1;
}
int lsh_exit(char **args)
{
return 0;
}
/* Handlers Here*/
void killer()
{
if (pid == 0)
exit(0);
}
void handler()
{
//I DON'T KNOW WHAT TO PUT HERE
}
int lsh_launch(char **args)
{
int status=0;
pid = fork();
if (pid == 0) {
// child process
signal(SIGINT, killer);
if (execvp(args[0], args) == -1) {
fprintf(stderr,"Command not found in $PATH\n");
}
return 1;
} else if (pid < 0) {
//error
perror("lsh");
} else {
// parent
signal(SIGINT, killer);
waitpid(pid, &status, WUNTRACED);
}
return 1;
}
int lsh_background(char **args)
{
pid_t pid;
int status=0;
pid = fork();
if (pid == 0) {
// child process
setpgid(0, 0);
signal(SIGINT, killer);
signal(SIGTTOU, fg_handler);
if (execvp(args[0], args) == -1) {
fprintf(stderr,"Command not found in $PATH\n");
}
return 1;
} else if (pid < 0) {
//error
perror("lsh");
} else {
// parent
signal(SIGTTOU, fg_handler);
signal(SIGINT, killer);
}
return 1;
}
//if a command was entered that we've been using
int lsh_exec(int argc, char **args)
{
int i;
if (args[0] == NULL) {return 1;}
int tresh=4;
char **args1=malloc(toke_c*sizeof(char *));
int j;
for(j=0;j<toke_c-1;j++){
args1[j]=args[j];
}
if(strcmp(args[toke_c-1],"&")==0){
return lsh_background(args1);
}
for (i = 0; i < tresh; i++) {
if (strcmp(args[0], builtin_str[i]) == 0) {
return (*builtin_func[i])(args);
}
}
return lsh_launch(args);
}
#define MAX_STR 256
//reading the line
char *lsh_lread(void)
{
char *str = malloc (MAX_STR);
fgets (str, MAX_STR, stdin);
}
//tokenizer
char **lsh_tokenizer(char *line)
{
int bufsize = 64;
int pos_t = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char *token;
token = strtok(line, " \t\r\n\a");
while (token != NULL) {
tokens[pos_t] = token;
pos_t++;
token = strtok(NULL, " \t\r\n\a");
}
tokens[pos_t] = NULL;
toke_c=pos_t;
return tokens;
}
void lsh_loop(void)
{
int argc;
char *line;
char **args;
int status;
do {
printf(">> ");
line = lsh_lread();
args = lsh_tokenizer(line);
status = lsh_exec(argc,args);
free(line);
free(args);
} while (status);
}
int main(int argc, char **argv)
{
lsh_loop();
return 0;
}
How should I understand the way tcsetpgrp() and signal(sigttou,handler) work together?
For your purposes, they don't. You do not need to send a process a signal to make its process group the foreground pgroup (but see below). In fact, I don't see why you would ever intentionally send a SIGTTOU to a process group that you're trying to put in the foreground.
Here's the central part of POSIX's documentation for tcsetpgrp() (emphasis added):
If the process has a controlling terminal, tcsetpgrp() shall set the foreground process group ID associated with the terminal to pgid_id. The application shall ensure that the file associated with fildes is the controlling terminal of the calling process and the controlling terminal is currently associated with the session of the calling process. The application shall ensure that the value of pgid_id matches a process group ID of a process in the same session as the calling process.
Attempts to use tcsetpgrp() from a process which is a member of a background process group on a fildes associated with its controlling terminal shall cause the process group to be sent a SIGTTOU signal. [...]
You're talking about implementing an fg command. The primary usefulness of such a command is interactive execution, and if a process (i.e. your shell) is receiving that command interactively then it must be in the foreground process group, because that's the only process group that receives input from the terminal. Supposing, then, that such a process calls the function, and that the arguments satisfy their individual requirements, the effect is "tcsetpgrp() shall set the foreground process group ID associated with the terminal to pgid_id." Or fail, of course. No signaling is documented to go along with that.
SIGTTOU comes into this picture only if tcsetpgrp() is called by a process that is in a background process group. Were I implementing a shell, I'd be inclined to disable job control for shells running in the background (the command would fail with an error). The default handler for this signal stops the process (not the same thing as terminating it); this is appropriate for a background process that attempts to write to its session's controlling terminal. Similarly, SIGTTIN by default stops the process, and is delivered to background processes that attempt to read from its session's controlling terminal.
For an fg command, you do not expect or want to handle SIGTTOU or SIGTTIN, but that doesn't mean you don't need to signal. Rather, the (initially foreground) process that calls tcsetpgrp() should afterward send a SIGCONT to the new forground pgroup in case some or all of those processes are stopped, as might well be the case. The default handler for this signal resumes the process if it is stopped, which is exactly what you want.
In short, then, you probably do not need to write any custom signal handlers at all for this purpose.

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;
}

Resources