readline clash with child printf? - c

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.

Related

fork / pipe / close in a recursive function

In order to realize a shell command interpretor, I try to execute pipes.
To do it, I use a recursive function in wich I use the pipe function and some redirections with dup2.
Here is my code :
void test_recurs(pid_t pid, char **ae)
{
char *const arg[2] = {"/bin/ls", NULL};
char *const arg2[3] = {"/bin/wc", NULL};
static int limit = 0;
int check;
int fd[2];
if (limit > 5)
return ;
if (pipe(fd) == -1)
{
printf("pipe failed\n");
return ;
}
pid = fork();
if(pid != 0)
{
printf("père %d\n",getpid());
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
if ((execve("/bin/ls", arg, ae)) == -1)
exit(125);
dprintf(2, "execution ls\n");
wait(&check);
}
else
{
printf("fils %d\n", getpid());
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
if ((execve("/bin/wc", arg2, ae)) == -1)
printf("echec execve\n");;
dprintf(2, "limit[%d]\n", limit);
limit++;
test_recurs(pid, ae);
}
}
The problem is it only execute "ls | wc" one time and then wait on the standard input. I know that the problem may come from the pipes (and the redirections).
It's a bit unclear how you are trying to use the function you present, but here are some notable points about it:
It's poor form to rely on a static variable to limit recursion depth because it's not thread-safe and because you need to do extra work to manage it (for example, to ensure that any changes are backed out when the function returns). Use a function parameter instead.
As has been observed in comments, the exec-family functions return only on failure. Although you acknowledge that, I'm not sure you appreciate the consequences, for both branches of your fork contain code that will never be executed as a result. The recursive call in particular is dead and will never be executed.
Moreover, the process in which the function is called performs an execve() call itself. The reason that function does not return is that it replaces the process image with that of the new process. That means that function test_recurs() also does not return.
Just as shell ordinarily must fork / exec to launch a single external command, it ordinarily must fork / exec for each command in a pipeline. If it fails to do so then afterward it is no longer running -- whatever it exec'ed without forking runs instead.
The problem is it only execute "ls | wc" one time and then wait on the standard input.
Certainly it does not recurse, because the recursive call is in a section of dead code. I suspect you are mistaken in your claim that it afterward waits on standard input, because the process that calls that function execs /bin/ls, which does not read from standard input. When the ls exits, however, leaving you with neither shell nor ls, what you then see might seem to be a wait on stdin.

Fork() new process and write to files for child and parent processes

I'm new to fork(), parent and child processes and have some difficulty understanding the logic behind the code that I wrote, but did not perform what I expected. Here is what I have:
int main (int argc, char** argv)
{
FILE *fp_parent;
FILE *fp_child;
fp_parent = fopen ("parent.out","w");
fp_child = fopen ("child.out","w");
int test_pid;
printf ("GET HERE\n");
fprintf (fp_parent,"Begin\n"); // MY CONCERN
for (int i = 0; i < 1; i++) //for simplicity, just fork 1 process.
{ // but i want to fork more processes later
test_pid = fork();
if(test_pid < 0)
{
printf ("ERROR fork\n");
exit (0);
}
else if(test_pid == 0) // CHILD
{
fprintf(fp_child,"child\n");
break;
}
else //PARENT
{
fprintf(fp_parent,"parent\n");
}
}
fclose(fp_parent);
fclose(fp_child);
}
So the output of above code is:
to stdout: GET HERE
in parent.out:
Begin
parent
Begin
in child.out:
child
My main concern is that I don't quite understand why "Begin" get written to parent.out twice. If I remove the for loop completely, then only one "Begin" is written which is expected.
So I think that it's because of the fork() and definitely I miss or don't understand some logic behind it. Could you guys please help me explain?
My plan to be able to write something before the for loop in parent.out and write something during the for loop in parent.out. Child process will write to child.out.
In C, the input/output operations using FILE structure are buffered at the level of user process. In your case, the output you have written to fp_parent was not actually written onto the disk and was kept in a local buffer at the moment of fork. The fork creates a copy of the whole process including the buffer containing Begin and this is why it appears twice in your file. Try to put fflush(fp_parent); before the fork. This will flush the buffer and the dirty line will disappear from the file.

fork() and write commands

#include<stdio.h>
int main()
{
if (fork())
{
wait();
printf("Now I am showing you ls -l"); // why can't we see this ?
execlp("ls","ls", "-l", 0); //gets printed second
}
else
{
printf("We are in the child process."); // gets printed first
}
}
I have this simple peace of code. My question is why don't we see on the console the first print, in the parent process?
The printf function provides buffered IO. When printing to stdout, as you are here, the buffer will normally get flushed when you print a newline \n, which you have not done, so your string is sitting in a buffer waiting to be printed.
The next thing you are doing is calling execlp. The exec family of functions replace the current process with the new one you have specified. The buffer is lost without ever being flushed, because the whole process is replaced.
If you add \n to the strings you are printing, you will probably see the output.
you have to flush stdout before the execlp.
if you put a \n at the end of the printf (or you call fflush(stdout)) you will get the correct result
In the interactive case, standard output is line buffered by default. And that execlp() will replace its memory image, therefore that output line may be dropped before it is written to console.
Change that print() statement to
printf("Now I am showing you ls -l\n");
to fix this problem.
There is 2 problems with your code:
first you need to flush your outputs, either by adding \n in the strings or by calling fflush(stdout)
But it should be the job of the son to exec(), of course you can always exec in the parent, but this is probably not what you want.
A more common fork/exec sample looks like :
int main() {
if (fork()) { // parent
wait(NULL); // wait his child
printf("Ok, my children finished the job\n");
exit(0);
}
else { // child
printf("We are in the child process.\n"); // gets printed first
printf("Now I am showing you ls -l\n");
execlp("ls","ls", "-l", 0); // never return from this
}
}

printf() in background program

I have a front-end X program, in this program , a background program a.out is called. In a.out, there are some printf sentences. I found the standard outputs are saved in ~/.xsession-errors. Is it normal? I don't want to save these printf contents. Is there any method to avoid saving them except deleting printf?
Yes, you can use the freopen(3) function to redirect stdout to some other file, or to the null device if you'd rather not have any output:
// Discard all further output to standard output for the duration of the
// program (or until the next call to freopen()):
stdout = freopen("/dev/null", "w", stdout);
Depending on how the child program is launched, you can also just redirect its stdout stream. If you're launching it with system(3), you can just use shell redirection:
system("./a.out args >/dev/null");
If you're launching it with a fork() and exec() pair, then you can redirect the stdout file descriptor in between the fork() and exec() to avoid changing anything in the parent process:
// Error checking omitted for expository purposes
pid_t pid = fork();
if(pid == 0)
{
// Child process
int fd = open("/dev/null", O_WRONLY);
dup2(fd, STDOUT_FILENO);
close(fd);
execve("./a.out", argv, envp);
}
There are a few possibilities, by no means an exhaustive list:
When you run your child program, do so in such a way that standard output/error is sent to the bitbucket, such as system ("myprog >/dev/nul 2>&1");.
Include in that child program your own printf varargs-type function which basically does nothing. Provided that function is included before any attempt is made to link in the C runtime libraries, it will use your dummy one in preference.
Use freopen to redirect standard output and error to the same bitbucket.
Options 2 and 3 require changes to the child program which may or may not be desirable. The first option can be effected by only changing the parent program.
In a similar situation I had done this:
#define printf(...) ;

communicate with an execv()'ed program via pipe doesn't work

i try to write a socket which loads programs and redirects socket io to these. sounds much like inetd but as far as i know, inetd loads the program when its port is requested. i want to have it loaded permanently.
so far so good. writing a socket server is not that tricky but i didn't get the rest working.
I basically want to open a pipe(), dup2() it to stdin and stdout and execv() my program.
the problem is, that my called program doesn't get any input.I'll try to show it with a test program. can someone tell me, what's wrong?
int create_program_fork(int *ios, char const *program) {
// create pipes to program
if (pipe(ios) != 0) {
return -1;
}
// fork to new process
int f = fork();
if (f < 0) {
// fork didn't work
close(ios[0]);
close(ios[1]);
return(-1);
}
if (f > 0) {
// master hasn't much to do here
return f;
}
// *** Child Process
// close std** file descriptors
printf ("executing program");
close(STDIN_FILENO);
close(STDOUT_FILENO);
// duplicate pipes as std**
dup2(ios[0], STDIN_FILENO);
dup2(ios[1], STDOUT_FILENO);
// close pipes
close(ios[0]);
close(ios[1]);
// call program
return execvp(program, NULL );
}
int main(int argc, char *argv[]) {
int ios[2];
// call program
int pid = create_program_fork(ios, "/bin/bash");
if (0 != pid){
exit(EXIT_FAILURE);
}
char const exit_order[] = "exit\0";
char const order[] = ">/tmp/test.txt\0";
// do something
write(ios[1], order, strlen(order));
// bash should stop then..
write(ios[1], exit_order, strlen(exit_order));
return 0;
}
I see two possible source of trouble:
1) the write part of the pipe is redirected to the child's stdout, so the new process' output
is sent back to the input. I suggest to dup only the pipe's read part at the child side. If you want to intercept the child's output, you need another channel (i.e. a new pipe, or simply let both parent and child share the same stdout).
2) the strings you send seem to contain line-oriented commands. It's possible that the child process expects newlines at the end of the strings. This is a very common source of problems. I suggest to check the way the child reads its input. A "\n" at the end of the strings could help (by the way, it's not necessary to explicitly add a "\0" at the end of C strings, since the compiler do it for you. Anyway, strlen won't count the "\0").

Resources