views:

61

answers:

2

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?

+1  A: 

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.

Kedar Soparkar
Sorry I forgot to include a rather crucial exec() call in the child branch. My bad :( The problem at the moment is that the parent's 2nd call to readline() doesn't return.
jameszhao00
Have you tried adding the exit()? It does work on my system.
Kedar Soparkar
Updated w/ the execl call. Tried the exit(0) and it didn't work. According to the specs (I think) exec(...) never returns.
jameszhao00
Maybe, the called process is causing the problem, which executable are you calling?
Kedar Soparkar
Any executable. ls, ps, echo, ...
jameszhao00
+1  A: 

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");
caf