Can't ignore signal CHLD, forcing to default - sigchld

#include <sys/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <iostream>
#include <unistd.h>
int main(){
signal(SIGCHLD,SIG_IGN );
pid_t pid = vfork();
if(pid <0){
std::cout<< "Error: Fork failed at main!!!";
exit(1);
}else if (0 == pid){
char * cmd[] ={(char*)"../work/C++11/Prj_LargeFile/script/Logger.pl",NULL};
//char * cmd[] = {(char*)"pwd",NULL};
//char * cmd[] = {(char*)"/usr/bin/perl",(char*)"../work/C++11/Prj_LargeFile/script/Logger.pl",NULL};
execvp(cmd[0],cmd);
_exit(0);
}
}
I want to have orphan child process which will run a perl script in the backgroud and I want the parent process to be completed without waiting on its child. Hence I have used signal(SIGCHLD,SIG_IGN ) at the beginning to ignore child signal on completion and to avoid zombies. But when i am running the code its giving me error as Can't ignore signal CHLD, forcing to default. . On the other when i am running those commented lines instead its running with the desired signal. what is the reason of that?

Perl is issuing this warning. It looks like recent versions of glibc (or possibly particular options in the Linux kernel) have removed the ability to ignore SIGCHLD, as far as Perl is concerned. I don't know why this is the case, but I just ran into it on a system that previously was happy to ignore SIGCHLD.
According to perldoc perlipc, which you should definitely look at for more information, the best thing to address this is:
use POSIX ":sys_wait_h"; # for nonblocking read
$SIG{CHLD} = sub {
while ((my $child = waitpid(-1, WNOHANG)) > 0) {
# $Kid_Status{$child} = $?;
}
};
# do something that forks...
The line with $Kid_Status is from the original perldoc, but I commented it out because the intent is to ignore the child processes, and I wanted to make clear that the inside of the while doesn't need to contain any code. There are a variety of ways to address handling child processes in the perldoc, such as if your Perl script is intended to daemonize or if you intend to exec or die within the handler.
If you can't edit the Perl script you're calling, then I recommend printing a line to STDERR right before the execvp telling the user to ignore the spurious warning. For example,
fputs("The next line is a harmless warning.\n", stderr);

Related

execl in child process works for only in specific cases

I have been bussy for the last five hours with this problem so I hope someone can help me out. In my C++ program (which I develop in QTcreator on lubuntu) I want to run airodump-ng in the child process of my program. The output of airodump-ng should be directed to the STDOUT of the parent proces. This works with many other programs but strangly enough not with airodump-ng. There is simply no output in the console. This, or my Linux crashes, I get logged out and when I log back in all my programs are closed. Does anybody know why?
#include <QCoreApplication>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <sys/wait.h>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//execl("/usr/sbin/airodump-ng", "airodump-ng", (char*)0 );
//dup2(1, 2); //pipe stderr to stdout
pid_t pidAirodump;
pid_t pidAircrack;
int pip[2];
if (pipe(pip) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
pidAirodump = fork();
if(pidAirodump > 0)//parent
{
pidAircrack = fork();
if(pidAircrack == 0)//pidAircrack
{
close(pip[0]);
dup2(pip[1], 2);
cout << "test" << endl;
//execl("/usr/sbin/arp", "arp", (char*)0 );
execl("/usr/sbin/airodump-ng", "airodump-ng ", "mon0", (char*)0 );
exit(0);
}
}
else//pidAirodump
{
exit(0);
}
wait(NULL);
return a.exec();
}
There's a few oddities in your program. But let's start at the question - you should distinguish between execl not working and the program you're trying to execute is misbehaving. You should not be able to crash linux from a user space program without special privilieges. The code snippet you've posted should not do that (what airpodump-ng does is another question).
If execl fails it will return and set errno, I suggest that you examine that after execl instead of just exiting.
Now for the oddities:
The first fork? Why do you do that? You basically forks and exits the child right away. You fork again and let the parent wait - this should trigger on the fact that the first child has terminated rather immediately.
The pipe, why do you do that if you wan't to keep the standard out? Instead you dup standard err to the write end of the pipe, but you doesn't seem to do anything with the read end.

Subprocess icon bounces in dock

My application starts a subprocess program to read video using the QuickTime framework via fork() and pipes. The subprocess goes into a wait loop when it is not busy, i.e. it does usleep until there is input. The subprocess is not a GUI application and it is written in C++.
When opening AVI video coded using the MSVC codec, a second copy of the application icon shows in the dock and bounces. After about 30 seconds in the Activity Monitor I can see that the subprocess changes to "not responding" even though CPU appears to be ~0%. The subprocess is still running and responding; it's just that Activity Monitor says otherwise.
If I look at the state of the subprocess, via gdb attach or check its output; everything looks fine. I can tell the subprocess to close the file and open another one and continue using it at which point the bouncing dock icon disappears and the process is not marked as not responding.
It's as if OSX thinks my subprocess has crashed (?) but I cannot detect an exception.
How can I stop the subprocess showing an icon in the dock, bouncing and being marked as not responding ?
This is how I set up communication with the subprocess:
#include <unistd.h>
#define READ 0
#define WRITE 1
// Start process
pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
// Set up pipes
if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return(-1);
pid = fork();
if(pid < 0)
return(pid);
else if(pid == 0)
{
// Set up communication via stdin/out
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
execvp(command, argv); // run subprocess
perror("execvp");
exit(1);
}
// Provide pointers to the file descriptors to the caller
if(infp == NULL)
close(p_stdin[WRITE]);
else
*infp = p_stdin[WRITE];
if(outfp == NULL)
close(p_stdout[READ]);
else
*outfp = p_stdout[READ];
return(pid);
}
See this SO question for more discussion of popen2().
Note: this code may or may not be the cause of my problem. As a first step, I would really like to prove what is the cause.
The “not responding” part is simple: Your subprocess is not running a runloop of any type, so from the POV of the system, it’s not handling events.
I’m a bit hazier on why your new process is getting a dock icon, but it basically boils down to fork() creating a process that inherits the attributes of the parent process (in this case, of it being a foreground application). OS X has a number of mechanisms to launch subprocesses in more sensible ways than fork(). If your app is in Cocoa, use NSTask, otherwise, take a look at posix_spawn(2).
This should be a drop in replacement for your routine:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <spawn.h>
#include <signal.h>
#include <crt_externs.h>
#define READ 0
#define WRITE 1
#define environ (*_NSGetEnviron())
pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return(-1);
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_init(&file_actions);
posix_spawn_file_actions_adddup2(&file_actions, p_stdin[READ], 0);
posix_spawn_file_actions_adddup2(&file_actions, p_stdout[WRITE], 1);
posix_spawn_file_actions_adddup2(&file_actions, 2, 2);
posix_spawnattr_t spawnAttributes;
posix_spawnattr_init(&spawnAttributes);
sigset_t no_signals;
sigset_t all_signals;
sigemptyset (&no_signals);
sigfillset (&all_signals);
posix_spawnattr_setsigmask(&spawnAttributes, &no_signals);
posix_spawnattr_setsigdefault(&spawnAttributes, &all_signals);
short flags = POSIX_SPAWN_CLOEXEC_DEFAULT | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
posix_spawnattr_setflags(&spawnAttributes, flags);
if (posix_spawn(&pid, command, &file_actions, &spawnAttributes, argv, environ)) {
perror("posix_spawn");
exit(1);
}
close(p_stdin[READ]);
if(infp == NULL)
close(p_stdin[WRITE]);
else
*infp = p_stdin[WRITE];
close(p_stdout[WRITE]);
if(outfp == NULL)
close(p_stdout[READ]);
else
*outfp = p_stdout[READ];
return(pid);
}
If you're using a compiler that supports c++11, I'd recommend checking out packaged_tasks.
Alternatively, you could also use condition_variables, but I'd try to get it working with packaged tasks first. Condition variables are more of the primitive than the higher-level packaged tasks. Either way, it's a lot easier (and standards compliant) to use these mechanisms than tradition IPC techniques.
That is, of course, if you don't have to have a separate process.
For further information, I'd highly recommend checking out The C++ Programming Language, 4th Edition. It has simple example of a producer/consumer mechanism with a simple vector that may suit you well. If you don't spring for the book, I'm sure you can find similar examples online for using condition_variables, futures or promises.
HTH
I ended up rewriting the subprocess as a MacOS XPC service. In the XPC service's property list I added LSBackgroundOnly to get Launch Services to ignore system event handling.

popen simultaneous read and write [duplicate]

This question already has answers here:
Can popen() make bidirectional pipes like pipe() + fork()?
(6 answers)
Closed 3 years ago.
Is it possible to read and write to a file descriptor returned by popen. I have an interactive process I'd like to control through C. If this isn't possible with popen, is there any way around it?
As already answered, popen works in one direction. If you need to read and write, You can create a pipe with pipe(), span a new process by fork() and exec functions and then redirect its input and outputs with dup2(). Anyway I prefer exec over popen, as it gives you better control over the process (e.g. you know its pid)
EDITED:
As comments suggested, a pipe can be used in one direction only. Therefore you have to create separate pipes for reading and writing. Since the example posted before was wrong, I deleted it and created a new, correct one:
#include<unistd.h>
#include<sys/wait.h>
#include<sys/prctl.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(int argc, char** argv)
{
pid_t pid = 0;
int inpipefd[2];
int outpipefd[2];
char buf[256];
char msg[256];
int status;
pipe(inpipefd);
pipe(outpipefd);
pid = fork();
if (pid == 0)
{
// Child
dup2(outpipefd[0], STDIN_FILENO);
dup2(inpipefd[1], STDOUT_FILENO);
dup2(inpipefd[1], STDERR_FILENO);
//ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
//replace tee with your process
execl("/usr/bin/tee", "tee", (char*) NULL);
// Nothing below this line should be executed by child process. If so,
// it means that the execl function wasn't successfull, so lets exit:
exit(1);
}
// The code below will be executed only by parent. You can write and read
// from the child using pipefd descriptors, and you can send signals to
// the process using its pid by kill() function. If the child process will
// exit unexpectedly, the parent process will obtain SIGCHLD signal that
// can be handled (e.g. you can respawn the child process).
//close unused pipe ends
close(outpipefd[0]);
close(inpipefd[1]);
// Now, you can write to outpipefd[1] and read from inpipefd[0] :
while(1)
{
printf("Enter message to send\n");
scanf("%s", msg);
if(strcmp(msg, "exit") == 0) break;
write(outpipefd[1], msg, strlen(msg));
read(inpipefd[0], buf, 256);
printf("Received answer: %s\n", buf);
}
kill(pid, SIGKILL); //send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
The reason popen() and friends don't offer bidirectional communication is that it would be deadlock-prone, due to buffering in the subprocess. All the makeshift pipework and socketpair() solutions discussed in the answers suffer from the same problem.
Under UNIX, most commands cannot be trusted to read one line and immediately process it and print it, except if their standard output is a tty. The reason is that stdio buffers output in userspace by default, and defers the write() system call until either the buffer is full or the stdio stream is closed (typically because the program or script is about to exit after having seen EOF on input). If you write to such a program's stdin through a pipe, and now wait for an answer from that program's stdout (without closing the ingress pipe), the answer is stuck in the stdio buffers and will never come out - This is a deadlock.
You can trick some line-oriented programs (eg grep) into not buffering by using a pseudo-tty to talk to them; take a look at libexpect(3). But in the general case, you would have to re-run a different subprocess for each message, allowing to use EOF to signal the end of each message and cause whatever buffers in the command (or pipeline of commands) to be flushed. Obviously not a good thing performance-wise.
See more info about this problem in the perlipc man page (it's for bi-directional pipes in Perl but the buffering considerations apply regardless of the language used for the main program).
You want something often called popen2. Here's a basic implementation without error checking (found by a web search, not my code):
// http://media.unpythonic.net/emergent-files/01108826729/popen2.c
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "popen2.h"
int popen2(const char *cmdline, struct popen2 *childinfo) {
pid_t p;
int pipe_stdin[2], pipe_stdout[2];
if(pipe(pipe_stdin)) return -1;
if(pipe(pipe_stdout)) return -1;
//printf("pipe_stdin[0] = %d, pipe_stdin[1] = %d\n", pipe_stdin[0], pipe_stdin[1]);
//printf("pipe_stdout[0] = %d, pipe_stdout[1] = %d\n", pipe_stdout[0], pipe_stdout[1]);
p = fork();
if(p < 0) return p; /* Fork failed */
if(p == 0) { /* child */
close(pipe_stdin[1]);
dup2(pipe_stdin[0], 0);
close(pipe_stdout[0]);
dup2(pipe_stdout[1], 1);
execl("/bin/sh", "sh", "-c", cmdline, NULL);
perror("execl"); exit(99);
}
childinfo->child_pid = p;
childinfo->to_child = pipe_stdin[1];
childinfo->from_child = pipe_stdout[0];
close(pipe_stdin[0]);
close(pipe_stdout[1]);
return 0;
}
//#define TESTING
#ifdef TESTING
int main(void) {
char buf[1000];
struct popen2 kid;
popen2("tr a-z A-Z", &kid);
write(kid.to_child, "testing\n", 8);
close(kid.to_child);
memset(buf, 0, 1000);
read(kid.from_child, buf, 1000);
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
printf("from child: %s", buf);
printf("waitpid() -> %d\n", waitpid(kid.child_pid, NULL, 0));
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
return 0;
}
#endif
popen() can only open the pipe in read or write mode, not both. Take a look at this thread for a workaround.
In one of netresolve backends I'm talking to a script and therefore I need to write to its stdin and read from its stdout. The following function executes a command with stdin and stdout redirected to a pipe. You can use it and adapt it to your liking.
static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
int p1[2], p2[2];
if (!pid || !infd || !outfd)
return false;
if (pipe(p1) == -1)
goto err_pipe1;
if (pipe(p2) == -1)
goto err_pipe2;
if ((*pid = fork()) == -1)
goto err_fork;
if (*pid) {
/* Parent process. */
*infd = p1[1];
*outfd = p2[0];
close(p1[0]);
close(p2[1]);
return true;
} else {
/* Child process. */
dup2(p1[0], 0);
dup2(p2[1], 1);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execvp(*command, command);
/* Error occured. */
fprintf(stderr, "error running %s: %s", *command, strerror(errno));
abort();
}
err_fork:
close(p2[1]);
close(p2[0]);
err_pipe2:
close(p1[1]);
close(p1[0]);
err_pipe1:
return false;
}
https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46
(I used the same code in Can popen() make bidirectional pipes like pipe() + fork()?)
Use forkpty (it's non-standard, but the API is very nice, and you can always drop in your own implementation if you don't have it) and exec the program you want to communicate with in the child process.
Alternatively, if tty semantics aren't to your liking, you could write something like forkpty but using two pipes, one for each direction of communication, or using socketpair to communicate with the external program over a unix socket.
You can't use popen to use two-way pipes.
In fact, some OSs don't support two-way pipes, in which case a socket-pair (socketpair) is the only way to do it.
popen works for me in both directions (read and write)
I have been using a popen() pipe in both directions..
Reading and writing a child process stdin and stdout with the file descriptor returned by popen(command,"w")
It seems to work fine..
I assumed it would work before I knew better, and it does.
According posts above this shouldn't work.. which worries me a little bit.
gcc on raspbian (raspbery pi debian)

bi-directional communication using socketpair: hangs reading output from child process

I'm trying to use a socketpair to have a parent process provide input to a child process that execs a different program (e.g., grep) and then read the resulting output. The program hangs in the while loop that reads the output from the program that the child execs.. The child dupes stdin and stdout on to its end of the socketpair and the parent and the child both close their unused end of the pair.
Interestingly, if the child execs a program that I wrote (OK, I ripped it off from Stevens Advanced Programming in the Unix Environment) everything works as expected. However, if the child execs grep (or some other standard program) the parent invariably hangs in trying to read the output. I can't tell if the input is not reaching grep or if the grep cannot determine the end of the input or if the output is somehow being lost.
Here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <cstdio>
#include <cerrno>
#include <iostream>
using namespace std;
void
sigpipe_handler(int sig, siginfo_t *siginfo, void * context) {
cout << "caught SIGPIPE\n";
pid_t pid;
if (errno == EPIPE) {
throw "SIGPIPE caught";
}
}
int main(int argc, char** argv) {
struct sigaction sa;
memset(&sa, '\0', sizeof(struct sigaction));
sa.sa_sigaction = sigpipe_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPIPE, &sa, NULL);
int sp[2];
socketpair(PF_UNIX, SOCK_STREAM, AF_UNIX, sp);
pid_t childPid = fork();
if (childPid == 0) {
close(sp[0]);
if (dup2(sp[1], STDIN_FILENO) != STDIN_FILENO) throw "dup2 error to stdin";
if (dup2(sp[1], STDOUT_FILENO) != STDOUT_FILENO) throw "dup2 error to stdout";
execl("/bin/grep", "grep", "-n", "namespace", (char*)NULL);
} else {
close(sp[1]);
char line[80];
int n;
try {
while (fgets(line, 80, stdin) != NULL) {
n = strlen(line);
if (write(sp[0], line, n) != n) {
throw "write error to pipe";
}
if ((n=read(sp[0], line, 80)) < 0) { // hangs here
throw "read error from pipe";
}
if (n ==0) {
throw "child closed pipe";
break;
}
line[n] = 0;
if (fputs(line, stdout) == EOF) {
throw "puts error";
}
if (ferror(stdin)) {
throw "fgets error on stdin";
}
exit(0);
}
} catch (const char* e) {
cout << e << endl;
}
int status;
waitpid(childPid, &status, 0);
}
}
Your code hangs as grep's output may be less than 80 bytes and you are issuing a blocking read on sp[0]. The proper way of doing this is by marking both sockets as non-blocking and selecting() over both of them.
You also forgot to close(sp[0]) before you wait(), which will leave your child process waiting for input.
You cannot achieve deadlock-free bidirectional communication with a subprocess using UNIX pipes or socketpairs, because you don't have control over buffering in the subprocess.
It just so happens that cat can be trusted to read one line and immediately print it, regardless of whether its standard output is a tty, a pipe or a socket. This is not the case with grep (and actually most programs using stdio), which will buffer output in-process (in the stdio buffers) and defer the write() call until either the buffer is full or the stdio stream is closed (typically because grep is about to exit after having seen EOF on input).
You can trick line-oriented programs (including grep) into not buffering by using a pseudo-tty instead; take a look at libexpect(3). But in the general case, you would have to re-run a different subprocess for each message, which allows to use EOF to signal the end of each message and cause whatever buffers in the command (or pipeline of commands) to be flushed.
See more info about this problem in the perlipc man page (it's for bi-directional pipes in Perl but the buffering considerations apply regardless of the language used for the main program).
It works fine with cat, so the problem is with grep. May be grep output behave differently when connected to something else than a terminal. Or it is not detecting the pattern for some reason.

How to handle execvp(...) errors after fork()?

I do the regular thing:
fork()
execvp(cmd, ) in child
If execvp fails because no cmd is found, how can I notice this error in parent process?
The well-known self-pipe trick can be adapted for this purpose.
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <sysexits.h>
#include <unistd.h>
int main(int argc, char **argv) {
int pipefds[2];
int count, err;
pid_t child;
if (pipe(pipefds)) {
perror("pipe");
return EX_OSERR;
}
if (fcntl(pipefds[1], F_SETFD, fcntl(pipefds[1], F_GETFD) | FD_CLOEXEC)) {
perror("fcntl");
return EX_OSERR;
}
switch (child = fork()) {
case -1:
perror("fork");
return EX_OSERR;
case 0:
close(pipefds[0]);
execvp(argv[1], argv + 1);
write(pipefds[1], &errno, sizeof(int));
_exit(0);
default:
close(pipefds[1]);
while ((count = read(pipefds[0], &err, sizeof(errno))) == -1)
if (errno != EAGAIN && errno != EINTR) break;
if (count) {
fprintf(stderr, "child's execvp: %s\n", strerror(err));
return EX_UNAVAILABLE;
}
close(pipefds[0]);
puts("waiting for child...");
while (waitpid(child, &err, 0) == -1)
if (errno != EINTR) {
perror("waitpid");
return EX_SOFTWARE;
}
if (WIFEXITED(err))
printf("child exited with %d\n", WEXITSTATUS(err));
else if (WIFSIGNALED(err))
printf("child killed by %d\n", WTERMSIG(err));
}
return err;
}
Here's a complete program.
$ ./a.out foo
child's execvp: No such file or directory
$ (sleep 1 && killall -QUIT sleep &); ./a.out sleep 60
waiting for child...
child killed by 3
$ ./a.out true
waiting for child...
child exited with 0
How this works:
Create a pipe, and make the write endpoint CLOEXEC: it auto-closes when an exec is successfully performed.
In the child, try to exec. If it succeeds, we no longer have control, but the pipe is closed. If it fails, write the failure code to the pipe and exit.
In the parent, try to read from the other pipe endpoint. If read returns zero, then the pipe was closed and the child must have exec successfully. If read returns data, it's the failure code that our child wrote.
You terminate the child (by calling _exit()) and then the parent can notice this (through e.g. waitpid()). For instance, your child could exit with an exit status of -1 to indicate failure to exec. One caveat with this is that it is impossible to tell from your parent whether the child in its original state (i.e. before exec) returned -1 or if it was the newly executed process.
As suggested in the comments below, using an "unusual" return code would be appropriate to make it easier to distinguish between your specific error and one from the exec()'ed program. Common ones are 1, 2, 3 etc. while higher numbers 99, 100, etc. are more unusual. You should keep your numbers below 255 (unsigned) or 127 (signed) to increase portability.
Since waitpid blocks your application (or rather, the thread calling it) you will either need to put it on a background thread or use the signalling mechanism in POSIX to get information about child process termination. See the SIGCHLD signal and the sigaction function to hook up a listener.
You could also do some error checking before forking, such as making sure the executable exists.
If you use something like Glib, there are utility functions to do this, and they come with pretty good error reporting. Take a look at the "spawning processes" section of the manual.
1) Use _exit() not exit() - see http://opengroup.org/onlinepubs/007908775/xsh/vfork.html - NB: applies to fork() as well as vfork().
2) The problem with doing more complicated IPC than the exit status, is that you have a shared memory map, and it's possible to get some nasty state if you do anything too complicated - e.g. in multithreaded code, one of the killed threads (in the child) could have been holding a lock.
Not should you wonder how you can notice it in parent process, but also you should keep in mind that you must notice the error in parent process. That's especially true for multithreaded applications.
After execvp you must place a call to function that terminates the process in any case. You should not call any complex functions that interact with C library (such as stdio), since effects of them may mingle with pthreads of libc functionality of parent process. So you can't print a message with printf() in child process and have to inform parent about the error instead.
The easiest way, among the other, is passing return code. Supply nonzero argument to _exit() function (see note below) you used to terminate the child and then examine the return code in the parent. Here's the example:
int pid, stat;
pid = fork();
if (pid == 0){
// Child process
execvp(cmd);
if (errno == ENOENT)
_exit(-1);
_exit(-2);
}
wait(&stat);
if (!WIFEXITED(stat)) { // Error happened
...
}
Instead of _exit(), you might think of exit() function, but it's incorrect, since this function will do a part of the C-library cleanup that should be done only when parent process terminates. Instead, use _exit() function, that doesn't do such a cleanup.
Well, you could use the wait/waitpid functions in the parent process. You can specify a status variable that holds info about the status of the process that terminated. The downside is that the parent process is blocked until the child process finishes execution.
Anytime exec fails in a subprocess, you should use kill(getpid(),SIGKILL) and the parent should always have a signal handler for SIGCLD and tell the user of the program, in the appropriate way, that the process was not successfully started.