Echo problems with execv() after fork() - c

AFAICS, the child process inherits stdout/stdin from the parent process on fork(). This leaves me wondering why the following code does NOT work:
int main(int argc, char *argv[])
{
char *earg[] = {"echo", "Hello", NULL};
if(fork() == 0) {
printf("running echo...\n");
execv("echo", earg);
printf("done!\n");
exit(0);
} else {
sleep(2);
}
return 0;
}
When running this little program, the two printf() calls appear just fine on the console. But the call to echo somehow gets lost! The output on the console is just:
running echo...
done!
Can somebody explain to me why the echo output doesn't appear on the console? And how I can fix this?

Since your printf("done") gets invoked your execv() clearly failed. All exec() functions do only return if an error occurred. Evaluating errno should help you to find out why it failed.
try using the whole path to echo:
execv("/bin/echo", earg);
EDIT: if you want to print done as soon as the child exited you should add a wait(NULL) call to your parent. see the manpage of wait() for more information and an example how to use it.

execv will not search for echo command in the PATH, so it fails, and it prints out "done" (which should not happen if execv is successful). You must supply the full path for execv to work
You may want to use execvp instead. It will search for the echo command in the PATH variable.

Related

Why is sleep in a child process blocking my program?

So I have this simple program that sleeps for 4 second if the value returned by fork is '0' meaning that the child process is executing, I've tried using sleep in child process but the program is blocked, and flushing standard output isn't working...
code:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[]) {
pid_t value = fork();
if (value == 0) {
sleep(4);
}
printf("Value returned by fork: %d\n", value);
printf("I'm the process N°%d\n", getpid());
return 0;
}
I'm running on Ubuntu 20.04.3 LTS.
Output:
Value returned by fork: 12618
I'm the process N°12617\
farouk#farouk-HP-Pavilion-Desktop-TP01-1xxx:~/sysexp$ Value returned by fork: 0
I'm the process N°12618
To allow this question to have an accepted answer.
The child process is not blocking the shell. The shell gave its prompt and the child wrote some output after the prompt, leaving the cursor at the start of a line without a shell prompt visible — because the shell prompt already appeared earlier.
There are a variety of ways around this.
The simplest is just to type a command such as ps and hit return, noting that the shell executes it, and that the ps output does not list the child process. If you type the ps command quick enough, you might see the child listed in the output before its output appears.
Another is to modify the program so that it waits for all child processes to exit before it exits — using wait() or waitpid(). The same code can be used in the child and the parent since the child will have no children of its own. The call to the wait function will return immediately with a 'no more children' status (error).
You can find extensive discussion of all this in the comments — I've chosen to make this a Community Wiki answer since there was a lot of activity in the comments that identified the gist of this answer.

Pipe chaining in my own shell implementation

I am currently writing my own shell implementation in C. I understood the principle behind piping and redirecting the fds. However, some specific behavior with pipes has attracted my attention:
cat | ls (or any command that does not read from stdin as final element of the pipe).
In that case, what happens in the shell is that ls executes and cat asks for a single line before exiting (resulting from a SIGPIPE I guess). I have tried to follow this tutorial to better understand the principle behind multiple pipes: http://web.cse.ohio-state.edu/~mamrak.1/CIS762/pipes_lab_notes.html
Below is some code I have written to try to replicate the behavior I am looking for:
char *cmd1[] = {"/bin/cat", NULL};
char *cmd2[] = {"/bin/ls", NULL};
int pdes[2];
pid_t child;
if (!(child = fork()))
{
pipe(pdes);
if (!fork())
{
close(pdes[0]);
dup2(pdes[1], STDOUT_FILENO);
/* cat command gets executed here */
execvp(cmd1[0], cmd1);
}
else
{
close(pdes[1]);
dup2(pdes[0], STDIN_FILENO);
/* ls command gets executed here */
execvp(cmd2[0], cmd2);
}
}
wait(NULL);
I am aware of the security flaws of that implementation but this is just for testing. The problem with that code as I understand it is that whenever ls gets executed, it just exits and then cat runs in the background somehow (and in my case fail because it tries to read during the prompt of zsh as my program exits). I cannot find a solution to make it work like it should be. Because if I wait for the commands one by one, such commands as cat /dev/random | head -c 10 would run forever...
If anyone has a solution for this issue or at least some guidance it would be greatly appreciated.
After consideration of comments from #thatotherguy here is the solution I found as implemented in my code. Please bear in mind that pipe and fork calls should be checked for errors but this version is meant to be as simple as possible. Extra exit calls are also necessary for some of my built-in commands.
void exec_pipe(t_ast *tree, t_sh *sh)
{
int pdes[2];
int status;
pid_t child_right;
pid_t child_left;
pipe(pdes);
if (!(child_left = fork()))
{
close(pdes[READ_END]);
dup2(pdes[WRITE_END], STDOUT_FILENO);
/* Execute command to the left of the tree */
exit(execute_cmd(tree->left, sh));
}
if (!(child_right = fork()))
{
close(pdes[WRITE_END]);
dup2(pdes[READ_END], STDIN_FILENO);
/* Recursive call or execution of last command */
if (tree->right->type == PIPE_NODE)
exec_pipe(tree->right, sh);
else
exit(execute_cmd(tree->right, sh));
}
/* Should not forget to close both ends of the pipe */
close(pdes[WRITE_END]);
close(pdes[READ_END]);
wait(NULL);
waitpid(child_right, &status, 0);
exit(get_status(status));
}
I was confused with the original link I posted and the different ways to handle chained pipes. From the link to the POSIX documented posted below my original question (http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_02) it appears that:
If the pipeline is not in the background (see Asynchronous Lists), the shell shall wait for the last command specified in the pipeline to complete, and may also wait for all commands to complete.
Both behavior are therefore accepted: waiting for last command, or waiting for all of them. I chose to implement the second behavior to stick to what bash/zsh would do.

I am trying to create a interactive shell

I am trying to create a interactive shell program that prompts the user for a command, parses the command, and then executes it with a child process. Here is the code that I have but im not sure where to go after this PLEAE HELP !!!!
Int shell(char *cmd_str ){
int commandLength=0;
cmd_t command;
commandLength=make_cmd(cmd_str, command);
cout<< commandLength<<endl;
cout << command.argv[0]<< endl;
if( execvp( command.argv[0], command.argv)==-1)
//if the command it executed nothing runs after this line
{
commandLength=-1;
}else
{
cout<<"work"<<endl;
}
cout<< commandLength<<endl;
return commandLength;
}
Assuming shell() is being run within a child process called with fork(), you'll need to ensure that the parent process properly waits for the child process to terminate. See the wait(2) family of functions.
Additionally, you'll want to retrieve the exit status of said child process (again, see wait(2)).
You can also try to implement stream redirection. Assuming this is an exercise, I'll leave the additional research on how to implement these things up to the user :) -- look into dup(2).

Trouble calling the Unix command kill(int PID) through c program

I am writing a program in C in one of my systems classes. We write c code and run it in a unix environment. I have looked all over the internet, but can't seem to find any way to make the kill(int PID) command work. The code will compile and run fine, but if I use the
ps -u username
command in a unix command prompt (after execution has completed of course,) it says that the all of the processes I tried to kill in my c code are still running. I can kill them from the unix command prompt by manually entering their PIDs, but for the life of me, I cannot figure out how to do it inside of my program.
In this particular program, I am trying to kill process CC, which is a process that just infinitely calls usleep(100); until terminated.
I tried using kill(C3, -9); and variations of execlp("kill", "kill", C3, (char *)0); but still no luck. Does anyone have any idea what I am doing wrong here? My only guess is that the kill command is being passed the wrong PID parameter, but if that's the case, I have no idea how I would get the correct one.
EDIT: Also, the kill command returns a value of zero, which I believe means that it "succeeded" in executing the command.
EDIT: Just noticed that the solution to my problem was in the instructions for the assignment all along. Yup. I'm stupid.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int args, char* argv[])
{
//
//Step 7
//
//Create process C3
int C3=fork();
if (C3==0)
{
execlp("CC", "CC", (char *)0);
}
else
{
usleep(500000);
//
//Step 8
//
int ps=fork();
if (ps==0)
{
execlp("ps", "ps", "-u", "cooley", (char *)0);
}
else
{
wait(NULL);
kill(C3);
}
}
exit(0);
}
You're calling the kill system call with only one argument, when it takes two. This leads to undefined behavior since then the second argument can by anything. You should get a warning about this when compiling.
The second argument should be a value from <signal.h> (see the signal(7) manual page).

readline clash with child printf?

In Linux, readline() in an infinite loop repeatdly reads text\n. However, as soon as child processes start printing to the screen, readline no longer reads new lines. Even if I repeatdly press enter, readline() doesn't return.
Anyone know what's wrong?
Code sample as requested:
char* input;
int cpid;
while(1)
{
input = readline(">>>");
strcpy(tempInput, input); //...does some stuff w/ tempInput
//....
cpid = fork();
if(cpid == 0){
printf("..."); printf("...");
execl("ls", "ls", "-l", (char*) NULL); //sample execl parameters
}
else{
//do some stuff...
printf("...");
}
free(input);
}
//readline(">>>") works the first time and doesn't return on subsequent calls
The stacktrace of where the code hangs (forever):
Thread [1] (Suspended : Signal : SIGINT:Interrupt)
__read_nocancel() at ../sysdeps/unix/syscall-template.S:82 0x7ffff78f0490
rl_getc() at 0x7ffff7bc3727
rl_read_key() at 0x7ffff7bc3c90
readline_internal_char() at 0x7ffff7baf25f
readline() at 0x7ffff7baf795
main() at /home/.../.../xxx.c:95 0x4010a1
Edit: This probably sounds like total technobabble to experienced unix developers, but could the child process have somehow 'captured' stdin and fail to release it somehow?
Your child processes, even after exec, still has its standard input connected to your terminal. Either use wait() to wait for the child process to finish, or reopen the child's standard input as /dev/null:
close(STDIN_FILENO) && open("/dev/null", "O_WRONLY");
After you fork(), your child process executes the 2 printf calls, then goes on to execute the while(1) loop, so what you are doing is creating 1 extra process after every newline.
What you need to do, is kill the child immediately after it prints the 2 ellipses (...)
Change your code to this:
if(cpid == 0){
printf("..."); printf("..."); exec(...); exit(0);
}
Bear in mind, exit() is called only if the exec() fails.
Edit:
If you intend to accept input for some kind of command line interpreter, readline isn't a very good option. Its official man page says:
BUGS:
It’s too big and too slow.
I could suggest an alternative way of getting the input string:
char c;
inputString = calloc(0,sizeof(char));
inputLength = 0;
c = getchar();
while(c!='\n')
{
inputString = realloc(inputString,(inputLength+1)*sizeof(char));
inputString[inputLength++] = c;
c = getchar();
}
inputString[inputLength] = '\0';
Now you have the string in inputString & its length in inputLength. You could very well do:
execlp(inputString,inputString,0);
to execute the required functionality.
This piece of code is not working because your call to execl() is failing, therefore the child process will resume at the loop code, which will also be using readline().
You must pass the full path to the executable when using execl(), or use the execlp() variant (which search into PATH environment variable) for it to work.
Even for the simplest syscalls, it's a good practice to always check for their return codes.

Resources