C - Notify When A Background Process Is Finished - c

I am creating a small shell program, one functionality of my shell is to be able to run commands in the background.
If a job is run in the background, then a small message is shown notifying the user of the pid.
Furthermore, I want to also be able to notify the user of when the background job is done
This is my code so far:
int execArgs(char *argv[], int bg)
{
int pid ;
int child_info = -1;
if(argv[1] == "exit"){
printf("%s\n", argv[0]);
}
if ( argv[0] == NULL ) /* nothing succeeds */
return 0;
if ( (pid = fork()) == -1 )
perror("fork");
else if ( pid == 0 ){
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
execvp(argv[0], argv);
perror("cannot execute command");
exit(1);
}
else {
if(bg == 0){
if ( wait(&child_info) == -1 )
perror("wait");
}
else
printf("The Process PID:%d has started\n", pid);
}
return child_info;
}
I know the answer lies in something to do with the
pid_t return_pid = waitpid(process_id, &status, WNOHANG);
but I don't understand how to use it completely, what does 'status' mean?
I tried implementing it like this:
int execArgs(char *argv[], int bg)
{
int pid ;
int child_info = -1;
if(argv[1] == "exit"){
printf("%s\n", argv[0]);
}
if ( argv[0] == NULL ) /* nothing succeeds */
return 0;
if ( (pid = fork()) == -1 )
perror("fork");
else if ( pid == 0 ){
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
execvp(argv[0], argv);
perror("cannot execute command");
exit(1);
}
else {
if(bg == 0){
if ( wait(&child_info) == -1 )
perror("wait");
}
else
printf("The Process PID:%d has started\n", pid);
int status;
pid_t return_pid = waitpid(pid, &status, WNOHANG); /* WNOHANG def'd in wait.h */
if (return_pid == -1) {
/* error */
} else if (return_pid == 0) {
/* child is still running */
} else if (return_pid == pid) {
printf("The process PID:%d has finished\n", pid);
}
}
return child_info;
}
but it doesn't work. I have read the man pages but they are a bit cumbersome and I don't understand them.
Apologies if the answer is elsewhere on StackOverFlow, I tried searching as thoroughly as I could.

Related

How to pipe different processes? (C)

I'm trying to pipe two different processes to implement the terminal's functionality "|".
I need the new child to execute a command itself, and fork another process to execute a different command using the first process's output, then output the final process's results on the terminal.
Here's my code, I keep the original parent untouched because I need to continue executing the other parts of my program afterwards.
int exec_pipe(char **args, int index, int *i, char**** jobs){
int fd1[2];
pid_t pid, wpid;
int status;
int savedStdOut = dup(1);
int savedStdIn = dup(fileno(stdin));
if (pipe(fd1)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork Failed" );
return 1;
}else if (pid == 0)
{
// Child process
close(fd1[0]);
dup2(fd1[1], 1);
printf("%s\n", args[index - 1]);
if (execvp(args[0], args) == -1)
{
printf("command not found\n");
}
int childId = fork();
if(childId < 0){
fprintf(stderr, "fork Failed" );
return 1;
}else if(childId == 0){
// child of child of parent
fdopen(fd1[1], "r");
dup2(fd1[1], savedStdOut);
if (execvp(args[index + 1], args) == -1)
{
printf("command not found\n");
}
exit(EXIT_FAILURE);
}else {
// parent of child of child of parent
do
{
wpid = waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status)); // wait for child until it's exited or been killed
fdopen(savedStdIn, "r");
close(fd1[0]);
close(fd1[1]);
}
}else{
// Parent process
do
{
wpid = waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status)); // wait for child until it's exited or been killed
}
return 1;
}
I'm getting a "No such file or directory" error with the program ending with exit code 9.
Solved it based on some comments, thanks all!
int fd1[2];
pid_t pid;
// allocate two pipe sets
if (pipe(fd1) < 0)
{
perror("Failed to create pipe.");
return EXIT_FAILURE;
}
// launch first child process.
if ((pid = fork()) < 0)
{
perror("Failed to fork child(1)");
return EXIT_FAILURE;
}
if (pid == 0)
{
// child
// stdout = fd1(write)
if(strcmp(args[0], args[index - 1]) == 0)
{
dup2(fd1[WRITE], STDOUT_FILENO);
close(fd1[READ]);
close(fd1[WRITE]);
execlp(args[0], args[0], (char*) NULL);
fprintf(stderr, "failed to execute '%s'\n", args[0]);
exit(1);
}else{
dup2(fd1[WRITE], STDOUT_FILENO);
close(fd1[READ]);
close(fd1[WRITE]);
execlp(args[0], args[0], args[index - 1], (char*) NULL);
fprintf(stderr, "failed to execute '%s'\n", args[0]);
exit(1);
}
}else
{
// parent
// fork once more.
if ((pid = fork()) < 0)
{
perror("Failed to fork child(2)");
return EXIT_FAILURE;
}
if (pid == 0)
{
// child of child
// stdin = fd1(read)
dup2(fd1[READ], STDIN_FILENO);
close(fd1[WRITE]);
close(fd1[READ]);
execlp(args[index + 1], args[index + 1], (char*) NULL);
fprintf(stderr, "failed to execute '%s'\n", args[index + 1]);
exit(1);
}else{
int status;
close(fd1[READ]);
close(fd1[WRITE]);
waitpid(pid, &status, 0);
}
}

minishell created in c not working as expected, pipes related

im making an UNIX minishell in c, in my OS signature. I only have to improve the shell itself, we have a premade parser for implement yacc and a scanner for implement lex among other files.
The file to modify is msh.c, the main executable file.
Well, the problem begins when I try to implement the pipe secuences. This is what I made:
int executePipeLine (char*** argvv, int bg, char** filev, int n){
int i;
int in = 0;
pid_t pid;
int fd[2];
for (i = 0 ; i < n-1 ; i++){
pipe(fd);
pid_t pid = fork();
if (pid == 0){ //child
if (in != STDIN_FILENO){
dup2(in, STDIN_FILENO);
close(in);
}
if (fd[1] != STDOUT_FILENO){
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
}
execvp(argvv[i][0], argvv[0]);
}
else { //parent
close(fd[1]);
in = fd[0];
}
}
if(in != STDIN_FILENO){
dup2(in, STDIN_FILENO);
close(in);
}
pid_t lastpid = fork();
if(lastpid == 0){ //child
execvp(argvv[i][0], argvv[0]);
}
if(lastpid == -1){
perror("no se pudo crear el hijo\n");
exit(-1);
}
else { //parent
/* not bg*/
if(!bg ) {
int status;
while (wait(&status) != lastpid); /* wait the child. */
}
else {
/*bg mode*/
printf("pid del proceso last: %d\n", lastpid);
}
return 0;
}
}//end executePipeLine
This seems to work fine. In the main , where we have an extern function called obtain_order(); that returns the number of commands + 1 and save it to ret. If ret is 1 we continue with the prompt, if is 0 means EOF (Control + D keybinding) to end the shell, and if is >1 execute the commands.
Here you have it:
int main(void)
{
char ***argvv;
int command_counter;
int num_commands;
int args_counter;
char *filev[3];
int bg;
int ret;
int reset = 0;
setbuf(stdout, NULL); /* Unbuffered */
setbuf(stdin, NULL);
while (1)
{
fprintf(stderr, "%s", "msh> "); /* Prompt */
ret = obtain_order(&argvv, filev, &bg);
printf("ret: %d\n", ret);
if (ret == 0) break; /* EOF */
if (ret == -1) continue; /* Syntax error */
num_commands = ret - 1; /* Line */
if (num_commands == 0) continue; /* Empty line */
if(num_commands > 1){
executePipeLine(argvv, bg, filev, num_commands);
}
else if (num_commands == 1){
executeCommand(argvv, bg, filev);
}
} //fin while
return 0;
} //end main
All works fine with a simple command. The problem is when I try to execute a pipe line. It show a good result, but i dont know we, after that ret always is 0 in the next iteration, so every time I try to execute a pipe line, it works but close the shell process, and have to execute it again instead of continue with the promt.
You know what is the problem here?
I hope you understand me, my english is not perfec. Thanks
In the parent process (i.e. your mini-shell) you are duping the standard input:
...
else { //parent
close(fd[1]);
in = fd[0];
}
...
if(in != STDIN_FILENO){
dup2(in, STDIN_FILENO);
close(in);
}
When your pipeline terminates, children are dead, pipes are no longer readable => stdin is considered as closed. When you later call obtain_order in your main loop, it returns 0 (EOF) and your program exits.

Create background process in C using sigset()

I am making my own shell in C. This is my basic code for running a process:
pid_t childpid;
int status;
int ret = 0;
if (strcmp(line[0], "exit") == 0) {
return;
}
childpid = fork();
if (childpid >= 0) {
if (childpid == 0) {
ret = execvp(line[0], &line[0]);
if (ret == -1) {
printf("ERROR\n");
exit(0);
}
exit(ret);
} else {
waitpid(childpid, &status, 0);
ret = WEXITSTATUS(status);
}
} else {
perror("fork");
exit(-1);
}
Where line is a "string array" holding the commands to run.
Now say I want to run a background process - I have to use sigset. A parent doesn't wait for a backgrounded child process, right? How do I handle this without the child becoming a background process?

Execvp doesn't return an error on an unknown command

I have the following code that forks a child and executes the command "a", which is an unknown command. However, execvp does not return an error and instead, "success" is printed. The same thing happens if I do "mv a b", when the file "a" does not exist. How should I capture and handle these errors?
int main ( int argc, char **argv ){
pid_t pid;
char *execArgs[] = { "a", NULL };
pid = fork();
// if fork fails
if (pid < 0){
exit(EXIT_FAILURE);
}
else if (pid == 0){
execvp(execArgs[0], execArgs);
if (errno == ENOENT)
_exit(-1);
_exit(-2);
}
else{
int status;
wait(&status);
if(!WIFEXITED(status)){
printf("error\n");
}
else{
printf("success\n");
}
}
}
The program exited; it just exited with a non-zero status. The primary opposite of WIFEXITED is WIFSIGNALED — see the POSIX specification for wait() and WIFSTOPPED and WIFCONTINUED for the other options.
Use:
int corpse = wait(&status);
if (corpse != -1 && WIFEXITED(status))
{
int estat = WEXITSTATUS(status);
char *err = (estat == 0) ? "success" : "failure";
printf("PID %d exited with status %d (%s)\n", corpse, estat, err);
}
else
printf("PID %d didn't exit; it was signalled\n", corpse);

Killing process that has been created with popen2

I'm using the function popen2 (that has been recommended elsewhere on stackoverflow) to programatically create a process that has to be killed again after some time. popen2 returns a PID and I thought that this PID could be used to kill the process. It doesn't work this way, though. In order to kill it, I have to increment the returned PID, which I don't understand (see code below)
Another problem might arise when various threads are doing this in parallel. In that case the PIDs might differ by more than one due to race conditions, I think.
So my question is this: How can I reliably identify the PID of the process created by popen2 in a multi-threaded scenario?
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#define READ 0
#define WRITE 1
pid_t popen2(const char *command, 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;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0)
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
execl("/bin/sh", "sh", "-c", command, NULL);
perror("execl");
exit(1);
}
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;
}
main() {
pid_t pid;
// Create process
pid = popen2("crafty", &in, &out);
sleep(5);
// Why doesn't kill(pid, SIGKILL) work?
kill(pid+1, SIGKILL);
while (1);
}
I think I got it.
execl("/bin/sh", "sh", "-c", command, NULL);
runs sh and popen2 returns it's pid. When you call kill it kills sh, but does not touch it's child process command. It is actually a fluke that killing the next pid kills command. This will not always work and is just up to race conditions.
If you want to be able to kill your target process then you will have to start that directly.
Warning (untested code):
pid_t popen2(const char **command, 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;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0)
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
execvp(*command, command);
perror("execvp");
exit(1);
}
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;
}
and pass command in the form of
char *command[] = {"program", "arg1", "arg2", ..., NULL};
in your particular example:
char *command[] = {"crafty", NULL};
You can use 'exec' command of shell to avoid pending process. Also: popen2 shall close the writing end of unused pipes, otherwise the pipe remains open. If one of pointers (infp, outpf) is NULL, it is useless to create and immediately close the pipe. Here is the version of popen2 I use in my project:
pid_t popen2(char *command, int *in_fd, int *out_fd) {
int pin[2], pout[2];
pid_t pid;
char cmd[strlen(command)+10];
if (out_fd != NULL) {
if (pipe(pin) != 0) return(-1);
}
if (in_fd != NULL) {
if (pipe(pout) != 0) {
if (out_fd != NULL) {
close(pin[0]);
close(pin[1]);
}
return(-1);
}
}
pid = fork();
if (pid < 0) {
if (out_fd != NULL) {
close(pin[0]);
close(pin[1]);
}
if (in_fd != NULL) {
close(pout[0]);
close(pout[1]);
}
return pid;
}
if (pid==0) {
if (out_fd != NULL) {
close(pin[1]);
dup2(pin[0], 0);
}
if (in_fd != NULL) {
close(pout[0]);
dup2(pout[1], 1);
}
// Exec makes possible to kill this process
sprintf(cmd, "exec %s", command);
execlp("sh", "sh", "-c", cmd, NULL);
fprintf(stderr, "%s:%d: Exec failed in popen2. ", __FILE__, __LINE__);
perror("Error:");
exit(1);
}
if (in_fd != NULL) {
close(pout[1]);
*in_fd = pout[0];
}
if (out_fd != NULL) {
close(pin[0]);
*out_fd = pin[1];
}
return pid;
}

Resources