killing a child process if execvp fails - c

I have a C program similar in structure to: http://www.csl.mtu.edu/cs4411/www/NOTES/process/fork/exec.html (that is, it's a shell that runs one command with execvp when entered and loops indefinitely until "exit" is entered).
What is the best way to kill a child process immediately if an unrecognized command is passed to execvp? For example, if I typed "ehco" instead of "echo" how could I quickly kill this child process? I've noticed if I enter a command not in my PATH and then immediately type exit it doesn't exit until I type exit again later.

The linked example already does the right thing: the child should unconditionally call _exit() after execvp(). The execvp() will only return if it fails.
(In other words, you don't kill the child process from the parent; you wait for the child process to exit, and write the child process so that it kills itself if the exec fails).

Related

How to get the return value of a child process in C after parent process ends?

I'm new so bear with me. I compiled an exe from a c file which uses fork(). I have the parent process return a value, let's say "A," and the child process returns "B." When the program ends and I "echo $?" the last return, I get "A."
I do have waitpid() implemented for the child to finish. But is there a way to output "B"(whatever that value is) from the shell after the process has finished?

Use C to exit ssh session to linux

I"m running a program that runs over ssh session. Meaning I connect to a linux using putty, the program starts, using the /etc/bash.bashrc file. At some point the program suppose to end and with it the ssh session via putty should disconnect.
What I"ve done untill now with a code below, and it doesn't work: the program exits, and stays in linux shell, meaning the putty is connected. What I expected is the command "exit" to end the putty ssh session.
char file_name[20];
pid_t pid = fork();
if (pid == 0)
{
printf("Starting vi\r\n");
char external_cmd[200] = "vi ";
strcat(external_cmd, file_name);
system(external_cmd);
} else
{
waitpid(pid,0,0);
printf("Exit..\r\n");
system("exit");
}
thanks for the help.
The easiest solution is to start your C program with exec so that it replaces the shell. The SSH session will naturally end when the program exits.
$ exec your_program
This code doesn't accomplish anything:
system("exit");
system() runs a child process under sh:
The system() function shall behave as if a child process were
created using fork(), and the child process invoked the sh utility
using execl() as follows:
execl(<shell path>, "sh", "-c", command, (char *)0);
So, your call to system() starts a sh process, which then executes the exit command you passed to it - and the child process exits, doing nothing to the parent processes.
You can kill the parent shell, though, by obtaining the PID of the parent process with getppid() and then calling kill() to kill it:
#include <unistd.h>
#include <signal.h>
...
pid_t parent = getppid();
kill( parent, SIGKILL );
SIGKILL is probably a bit extreme - it kills a process immediately, with no chance for the process to clean up after itself, but it will work (unless you're running a non-root setuid child process in this case - if you don't know what that is, don't worry about it.). SIGTERM is a less-extreme signal, but since it can be caught or blocked it isn't guaranteed to end a process. SIGINT is the equivalent of CTRL-C and may also work, but again the process can catch or ignore the signal. SIGQUIT can also be used, but it's purpose is to cause the process to quit and dump a core file, which you probably don't want.

Separate I/O for child process after fork()

I am trying to implement an application on Linux using C, and I have a requirement that I need to do I/O separately on my child & parent process. Here is what I am looking for
User runs the application, the parent process spawns 3 child processes.
Each of the child process will spawn a thread that waits for the user input.
There should be an intuitive method by which the user can specify which of the child process he is interacting with.
Ideally I would like if each of the child processes is executed on different terminal, that way it is very clear to the user with whom he is interacting.
I saw a similar question in Executing child process in new terminal, but the answer is not very clear regarding the steps involved. It seems to suggest that it can be done by execing the xterm like this xterm -e sh -c, but it is not confirmed. I would also want to setup some IPC between the parent <--> child & child <--> child process as well, so if I launch the child process in a new terminal by execing xterm, who is the child of my parent process? Is it xterm? If so, the code that I actually want to execute in my child process, will it get executed as a child of xterm?
Assume that you have already spawned the three child processes and that you run your parent on tty1.
tty1: Now contains all the diagnostics information
tty2: Child process 1
tty3: Child process 2
tty4: Child process 3
tty5: User input
So each child process will read from its tty as if it were a file (note: requires root permissions). To give input to, say, child process 2, go to tty5 and type in this command:
cat - >/dev/tty3
Then type in the input to your program, and then press Ctrl-D. Your child process 2 should now recieve that input.
EDIT You do not need to actually run the child processes on different ttys. It is only required to run them with root permissions and then read and write from those tty numbers, just as you would read from stdin and write to stdout. Sorry for the confusion.

execvp/fork -- how to catch unsuccessful executions?

Right now I'm writing a C program that must execute a child process. I'm not doing multiple child processes simultaneously or anything, so this is fairly straightforward. I am definitely executing the built-in shell programs (i.e. things like cat and echo) successfully, but I also need to be able to tell when one of these programs fails to execute successfully. I'm trying this with the following simplified code:
int returnStatus; // The return status of the child process.
pid_t pid = fork();
if (pid == -1) // error with forking.
{
// Not really important for this question.
}
else if (pid == 0) // We're in the child process.
{
execvp(programName, programNameAndCommandsArray); // vars declared above fork().
// If this code executes the execution has failed.
exit(127); // This exit code was taken from a exec tutorial -- why 127?
}
else // We're in the parent process.
{
wait(&returnStatus); // Wait for the child process to exit.
if (returnStatus == -1) // The child process execution failed.
{
// Log an error of execution.
}
}
So for example, if I try to execute rm fileThatDoesntExist.txt, I would like to consider that a failure since the file didn't exist. How can I accomplish this? Also, while that execvp() call successfully executes built-in shell programs, it doesn't execute programs in the current directory of the executable (i.e. the program that this code is running inside of); Is there something else that I have to do in order to get it to run programs in the current directory?
Thanks!
This is a classic problem with a very elegant solution. Before forking, create a pipe in the parent. After fork, the parent should close the writing end of the pipe, and block attempting to read from the reading end. The child should close the reading end and set the close-on-exec flag, using fcntl, for the writing end.
Now, if the child calls execvp successfully, the writing end of the pipe will be closed with no data, and read in the parent will return 0. If execvp fails in the child, write the error code to the pipe, and read in the parent will return nonzero, having read the error code for the parent to handle.
wait(2) gives you more than just the exit status of the child process. In order to get the real exit status, you need to use the WIFEXITED() macro to test if the child exited normally (as opposed to abnormally via a signal etc.), and then use the WEXITSTATUS() macro to get the real exit status:
wait(&status);
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
// Program succeeded
}
else
{
// Program failed but exited normally
}
}
else
{
// Program exited abnormally
}
In order for execvp(3) to run a program in the current directory, you either need to add the current directory to your $PATH environment (generally not a good idea), or pass it the full path, e.g. use ./myprogram instead of just myprogram.
In terms of failure detection, if an exec() function replaces the current process with a new one, then the current process is gone; it doesn't matter if the executed program decides that what it has been asked to do is a success or failure. However, the parent process from before the fork can discover the child's exit code which would likely have the command success/failure information.
In terms of finding executables, execvp() duplicates the action of the shell, searching the current path. If it is not finding executables in the working directory, it is likely that directory is not in the search path (or the files are not actually executable). You can try specifying them by a full path name.
If you simply want to run a command and wait for the result, you might want to use the system() function which handles this for you, instead of building it yourself with fork/exec.

Fork and wait - how to wait for all grandchildren to finish

I am working on an assignment to build a simple shell, and I'm trying to add a few features that aren't required yet, but I'm running into an issue with pipes.
Once my command is parsed, I fork a process to execute them. This process is a subroutine that will execute the command, if there is only one left, otherwise it will fork. The parent will execute the first command, the child will process the rest. Pipes are set up and work correctly.
My main process then calls wait(), and then outputs the prompt. When I execute a command like ls -la | cat, the prompt is printed before the output from cat.
I tried calling wait() once for each command that should be executed, but the first call works and all successive calls return ECHILD.
How can I force my main thread to wait until all children, including children of children, exit?
You can't. Either make your child process wait for its children and don't exit until they've all been waited for or fork all the children from the same process.
See this answer how to wait() for child processes: How to wait until all child processes called by fork() complete?
There is no way to wait for a grandchild; you need to implement the wait logic in each process. That way, each child will only exit after all it's children have exited (and that will then include all grandchildren recusively).
Since you are talking about grandchilds, you are obviously spawning the childs in a cascading manner. Thats a possible way to implement a pipe.
But keep in mind that the returned value from your pipe (the one you get when doing echo $? in your terminal) is the one returned from the right-most command.
This means that you need to spawn childs from right to left in this cascading implementation. You dont want to lose that returned value.
Now assuming we are only talking about builtin commands for the sake of simplicity (no extra calls to fork() and execve() are made), an intersting fact is that in some shells like "zsh", the right-most command is not even forked. We can see that with a simple piped command like:
export stack=OVERFLOW | export overflow=STACK
Using then the command env, we can appreciate the persistance of the overflow=STACK in the environment variables. It shows that the right-most command was not executed in a subshell, whereas export stack=OVERFLOW was.
Note: This is not the case in a shell like "sh".
Now lets use a basic piped command to give a possible logic for this cascading implementation.
cat /dev/random | head
Note: Even though cat /dev/random is supposedly a never ending command, it will stop as soon as the command head is done reading the first line outputed by cat /dev/random. This is because stdin is closed when head is done, and the command cat /dev/random aborts because its writing in a broken pipe.
LOGIC:
The parent process (your shell) sees that there is a pipe to execute. It will then fork two processes. The parent stays your shell, it will wait for the child to return, and store the returned value.
In the context of the first generation child: (trying to execute the right-most command of the pipe)
It sees that the command is not the last command, it will fork() again (What i call "cascading implementation").
Now that the fork is done, the parent process is going to execute first of all its task (head -1), it will then close its stdin and stdout, then wait() for its child. This is really important to close firstly stdin and stdout, then call wait(). Closing stdout sends EOF to the parent, if reading on stdin. Closing stdin make sure the grand-children trying to write in the pipe aborts, with a "broken pipe" error.
In the context of the grand-children:
It sees that it is the last command of a pipe, it will just execute the command and return its value (it closes stdin and stdout).

Resources