How to prevent background jobs from printing next to prompt? - c

I am trying to implement my own shell and am experimenting with background jobs with using WNOHANG option in waitpid. But whenever I run a command in shell it just prints the output next to my prompt like this:
user#hostmachine:/.../$: [output]
Where am I going wrong?
`
while (1) {
int childPid, status;
// Display prompt and read input
char *buffer = print_prompt();
char *input = readline(buffer);
check_and_free(buffer)
// add input to readline history.
add_history(input);
time_t t;
time(&t);
add_history_time(ctime(&t));
// Check for EOF.
if (!input)
break;
parseInfo *result = parse(input);
if (result == NULL) {
goto free;
}
print_info(result);
commandType *input_command = &result->CommArray[0];
// execute builtin command in parent process
int commType = isBuiltInCommand(input_command->command);
if (commType == EXIT) {
free_info(result);
check_and_free(input)
exit(0);
}
else if (commType != NO_SUCH_BUILTIN) {
executeBuiltInCommand(input_command, commType, history_get_history_state());
} else {
// create a child process to execute command
childPid = fork();
if (childPid == 0) {
// calls execvp
printf("Executing child process...\n\n");
executeCommand(input_command, result);
} else {
waitpid(childPid, &status, WNOHANG);
if (status != 0) {
printf("Error! Child exited with error code %d\n", WEXITSTATUS(status));
}
}
}
// Free buffer that was allocated by readline
free:
free_info(result);
check_and_free(input)
}
return 0;
}
`
I tried to execute a job in background and for quick ones like "ls" it just prints the output next to my prompt!

Related

waitpid hangs even though child process is dead

i have the following function:
int run_func(command history[MAX_INPUT_SIZE], char** args, int capacity) {
int need_to_wait = 1;
int i = 0;
char* arg = args[0];
int status;
while (arg != NULL) {
if (strcmp(arg, "&") == 0) {
need_to_wait = 0;
break;
}
arg = args[i++];
}
pid_t wait_pid;
pid_t pid = fork();
int res;
if (pid == 0) {
res = execvp(args[0], args);
if (res == -1) {
printf("exec failed\n");
fflush(stdout);
return 0;
}
} else if (pid < 0) {
printf("fork failed\n");
fflush(stdout);
return 0;
} else {
if (need_to_wait){
do {
wait_pid = waitpid(pid, &status, 0);
} while(!WIFEXITED(status) && !WIFSIGNALED(status));
}
history[capacity - 1].pid = pid;
}
return 1;
}
the issue I have is that the bottom while loop, hangs and doesn't stop whenever I get an invalid command such as 'hello' from the user from the terminal until I press enter again.
this function is being called from another function that receives input from the user.
Copying comment into an answer.
Side issues:
Error messages should be printed to stderr, not stdout.
There's no need to save or test the return value from execvp() — if it returns, it failed; if it succeeds, it does not return.
Main observation:
You should almost certainly have an exit() or _exit() instead of return 0; in the error handling code after execvp(). When the command fails (hello?), then you end up with two processes running — one from the failed execvp() and one is the parent process. This is apt to confuse everything as you have two processes trying to read the terminal at the same time.

command hangs on read end of pipe

I've doing custom shell for an assignment, and I wanted to implement the pseudo code from emphemient here for multiple piping. However, my code is still hanging on the read end of the pipe (in my test command, I do echo hello | wc and it's hanging on wc).
Is my implementation incorrect based on the pseudocode? I think I close the pipes properly
void piped()
{
// test command
char args[] = "echo hello | wc";
int status;
pid_t pid;
int cmdsRun = 0;
int newfds[2];
int oldfds[2];
char *nextCmd;
char *prevCmd = NULL;
char *cmdToken;
char *cmdSavePter;
cmdToken = strtok_r(args, "|", &cmdSavePter);
while (cmdToken)
{
nextCmd = strtok_r(NULL, "|", &cmdSavePter);
fprintf(stderr, "cmdToken: %s, nextCmd: %s, prev: %s\n", cmdToken, nextCmd, prevCmd);
struct command *commands = parseW(cmdToken);
// if next command make new pipes
if (nextCmd)
pipe(newfds);
// fork
pid = fork();
if (pid == 0) // child
{
// if there was prev cmd
if (prevCmd)
{
dup2(oldfds[0], STDIN_FILENO);
close(oldfds[0]);
close(oldfds[1]);
}
// if next cmd
if (nextCmd)
{
close(newfds[0]);
dup2(newfds[1], STDOUT_FILENO);
close(newfds[1]);
}
// this function simply execvps the commands
runSimple(commands);
}
else if (pid < 0)
{
perror("error");
exit(EXIT_FAILURE);
}
else // parent
{
// if there was a prev command
if (prevCmd)
{
close(oldfds[0]);
close(oldfds[1]);
}
// if next command
if (nextCmd)
{
memcpy(oldfds, newfds, sizeof(oldfds));
}
}
waitpid(pid, &status, 0);
prevCmd = cmdToken;
cmdToken = nextCmd;
cmdsRun++;
}
// parent: if multiple cmds, close
if (cmdsRun > 1)
{
close(oldfds[0]);
close(oldfds[1]);
}
}

Unable to exit the background properly after using fork in C. (Simple Shell Implementation)

I am implementing a simple shell in C. The runCommand works fine for all the other processes but when I run a program using & (Background process). The command-line prompt gets stuck and does not prompt again until I press enter again or some other input from the keyboard.
I am not sure what exactly is causing this issue. I have implemented flush to clear the stdout but still the same error.
Main Driver Function. Runs in a loop to keep prompting the user after each command execution.
/* Simple Shell implementation. Prompts until exit received from user or any other errors */
int eggShell()
{
char *lineRead;
char **command;
char *commandsPathFull = getenv("PATH"); //Get the $PATH in the environment
while (1)
{
fflush(stdout);
signal(SIGCHLD, sigchld_handler);
lineRead = readline("esh> ");
command = parseCommand(lineRead, " ");
int backgroundTask = 0;
if (command[0] != NULL)
{
if (strcmp(command[0], "exit") == 0)
{
free(command);
free(lineRead);
return 1;
}
if (command[1] != NULL)
{
for (size_t i = 1; command[i] != (char *)NULL; i++)
{
if (strcmp(command[i], "&") == 0)
{
backgroundTask = 1;
}
}
}
if ((validCommand(command[0], commandsPathFull)) == 1)
{
printf("Running Command\n");
runCommand(command, backgroundTask);
free(command);
free(lineRead);
}
}
fflush(stdout);
}
free(lineRead);
free(command);
return 1;
}
The Runcommand forks a new process for each command. If the command is not background command parent does not wait for the child to finish.
/* Receives a command and runs it using fork
returns 1 on successfull execution.*/
int runCommand(char **command, int bckTask)
{
pid_t pid, wpid;
int status;
pid = fork();
// Child process
if (pid == 0)
{
//Execute the command
if (execvp(command[0], command) == -1)
{
perror("esh Exec:");
}
exit(EXIT_FAILURE);
}
// Error forking
else if (pid < 0)
{
perror("esh fork error:");
}
// Parent process
else
{
// wait until either the processes are exited or killed
// If not a background task
if (bckTask != 1)
{
// {
do
{
wpid = waitpid(pid, &status, WUNTRACED);
if (wpid == -1)
{
perror("Esh Wait PID:");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
//printf("Foreground Task\n");
//waitpid(pid, &status, 0);
}
else if (bckTask == 1)
{
printf("Command %s | Background PID: %d\n", command[0], pid);
return 1;
// do
// {
// wpid = waitpid(-1, &status, WNOHANG);
// printf("Background pid: %d\n", pid);
// if (wpid > 0)
// {
// printf("Completed Background pid: %d\n", wpid);
// }
// } while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
}
return 1;
}
Current Output:
esh> ls
Running Command
esh esh.c Makefile README.md
esh> echo "Hello World"
Running Command
"Hello World"
esh> echo "Hello 2" &
Running Command
Command echo | Background PID: 16618
esh> "Hello 2" &
(At this point the cursor is stuck. I need to press enter again to get the prompt)
esh>

Can't kill PID started with spawn

Some days ago, I asked here how to start a program using C without using fork(). This solution is working fine.....except that I can't kill this child process!
My program (in this case, 'rbfeeder') has some threads....and when I send kill signal, only threads are killed (I think). What am I doing wrong?
This is the code used to 'start' rbfeeder:
/*
* Start dump1090, if not running
*/
void startDump(void) {
if (p_dump != 0) {
log_level(8, "Looks like dump is already running.\n");
return;
}
pid_t ret = run_cmd("/home/jmaurin/dev/client/rbfeeder");
if (ret != 0) {
log_level(8, "Ok, started! Pid is: %i\n", ret);
p_dump = ret;
sendStats();
} else {
log_level(8, "Error starting dump1090\n");
p_dump = 0;
sendStats();
}
return;
}
And this is the code to 'stop':
void stopDump(void) {
if (checkDumpRunning()) {
log_level(3, "Dump is running, let's try to kill.\n");
if (kill(p_dump, SIGKILL) == 0) {
log_level(3,"Succesfull kill dump!\n");
sendStats();
return;
} else {
log_level(3,"Error killing dump.\n");
return;
}
} else {
log_level(3, "Dump is not running.\n");
}
return;
}
and run_cmd function:
pid_t run_cmd(char *cmd) {
pid_t pid, ret;
char *argv[] = {"sh", "-c", cmd, NULL};
int status, s;
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_t *file_actionsp;
s = posix_spawn_file_actions_init(&file_actions);
if (s != 0)
return 0;
//STDERR_FILENO
s = posix_spawn_file_actions_addclose(&file_actions,STDERR_FILENO);
if (s != 0)
return 0;
file_actionsp = &file_actions;
//printf("Run command: %s\n", cmd);
status = posix_spawn(&pid, "/bin/sh", file_actionsp, NULL, argv, environ);
if (status == 0) {
log_level(8, "Child pid: %i\n", pid);
ret = pid;
/*
if (waitpid(pid, &status, 0) != -1) {
printf("Child exited with status %i\n", status);
} else {
perror("waitpid");
}
*/
} else {
// printf("posix_spawn: %s\n", strerror(status));
ret = 0;
}
return ret;
//printf("End of run\n");
}
p_dump is a global variable to hold PID.
This image is when my 'client' receive the command (over ethernet) to start external program:
Then, an HTOP on the same machine....see that PID are the same, which means that my variable is correct:
Then, I've sent an 'stop' command and my client executed 'stopDump', but one process still running (the other threads from same program are 'killed'):
The external program doesn't 'spawn/fork' itself, but it does have threads.
Your kill is completing successfully, because the process is becoming a zombie, (The Z in the status column in HTOP). A zombie is a process that still has metadata in the kernel, but is not actually running. To get rid of the zombie, the parent has to wait on the process. As your process is the parent, adding a call to waitpid(p_dump) after the kill succeeds should handle this.
The answer by user1937198 worked fine, but I found another way that doesn't need to call any function, like 'waitpid'.
struct sigaction sigchld_action = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDWAIT
};
sigaction(SIGCHLD, &sigchld_action, NULL);
At least in my case, it's preventing zombie proccess after kill. It's works fine.

Unix C Shell - Job Control Issue!

I've been working on creating my own Unix Shell in C to get practice with its interworkings...I'm having some issues getting my process to run in the background while allowing my shell to continue taking user input. If you could take the time to dissect what I've got below it would be much appreciated!
My variables are below, just incase that helps understand things more...
#define TRUE 1
static char user_input = '\0';
static char *cmd_argv[5]; // array of strings of command
static int cmd_argc = 0; // # words of command
static char buffer[50]; // input line buffer
static int buffer_characters = 0;
int jobs_list_size = 0;
/* int pid; */
int status;
int jobs_list[50];
Here is my main function.
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
create_process();
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
Parse Input...I know, I can't get readline to work on this box :(
If provided the & operator, create the job in the background... (see below)
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
printf("Started job %d\n", getpid());
make_background_job();
}
cmd_argc++;
}
}
Make background job. Closes child process STDIN, opens new STDIN, and executes.
void make_background_job()
{
int pid;
pid = fork();
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
}
The meat of my job control. Fork spawns child, returns 0 for child and PID for parent.
void create_process()
{
pid_t pid;
pid = fork();
status = 0;
switch(pid){
case -1:
perror("[MYSHELL ] $ (fork)");
exit(EXIT_FAILURE);
case 0:
make_background_job();
printf("\n\n----Just made background job in case 0 of create_process----\n\n");
break;
default:
printf("\n\n----Default case of create_process----\n\n");
// parent process, waiting on child...
waitpid(pid, &status, 0);
if (status != 0)
fprintf (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
else
break;
}
}
My problem is when I execute a job in the background, its executing the command twice, and exiting out of the shell. (It functions correctly otherwise if no background process is enabled). Where am I getting confused? I think it may have to do with issues regarding my PID's, as I'm not populating the list correctly either in 'make_background_job'
Here is my output, the example.sh just throws out helloWorld:
[MYSHELL] $ ./example.sh &
Started job 15479
Child pid = 15479
Child pid = 15481
Hello World
Hello World
What seems to happen is
in main() the prompt is displayed, expecting a command
when a command is input, parse_input() is called
it builds the commands array until it finds & where it calls make_background_jobs()
that function forks quickly, and executes in parallel, in two processes, execvp()
execvp() replaces each of the two processes to execute the command
thus two "Hello world" appear.
The problem is in make_background_jobs() where, I think, the expected behavior was that only one of the two processes should execute the command, and the other one (father) returns, to keep the program active.
This can be solved by modifying that function, making the father process return:
void make_background_job()
{
int pid;
pid = fork();
if (pid) return; // The father process returns to keep program active
...
edit
I gave it a try, removing the unnecessary
void make_background_job()
{
int pid;
pid = fork();
if ( ! pid)
{
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child Job pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
exit(1);
}
waitpid(pid, &status, 0);
}
The background job is created in another process. The father waits for the job to complete.
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
int ok = 0;
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
ok = 1;
break;
}
cmd_argc++;
}
if (!ok) cmd_argv[cmd_argc = 0] = NULL; // If no & found, reset commands
}
Only parses input.
Below a new handle_commands() that return 0 if there is a command to play, and the main follows.
int handle_commands() { return cmd_argc > 0 ? 0:1; }
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
make_background_job(); // Call directly the bg job
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
The main() calls directly make_background_job().
There is only one fork() in make_background_job. create_process() has been removed.

Resources