1

I'm having some troubles with fork() and that kind of things.

I'm developing a shell, where the user can write commands that whill be executed as in a normal and common shell.

I have a main function like this:

void Shell::init() {
    string command;
    while (1) {
        cout << getPrompt() << " ";
        command = readCommand();
        if (command.length() > 0) handleCommand(command);
    }
}

handleCommand() is the function that does pretty much everything. Somewhere in it, I have the following:

...
else {
    pid_t pid;
    pid = fork();

    char* arg[tokens.size() + 1];
    for (int i = 0; i < tokens.size(); ++i) {
        arg[i] = (char*) tokens[i].c_str();
    }
    arg[tokens.size()] = NULL;

    if (pid == 0) {
        if (execvp(tokens[0].c_str(), arg) == -1) {
            cout << "Command not known. " << endl;
        };
    } else {
        wait();
    }
}

What I want is that when I reach that point, the command will be treated as a program invocation, so I create a child to run it. It's working almost perfect, but I get the prompt again before the program output. Example:

tronfi@orion:~/NetBeansProjects/Shell2$ whoami
tronfi@orion:~/NetBeansProjects/Shell2$ tronfi

tronfi@orion:~/NetBeansProjects/Shell2$ 

The child should die after the execvp, so it shouldn't be calling the prompt, and the parent is waiting until the child die.

So... what I'm doing wrong?

Thanks!!

Pablo Reyes
  • 293
  • 1
  • 5
  • 14
  • 1
    I don't think this is precisely your problem, but consider what happens if `execvp()` fails. How many instances of your shell would you have at that point? – Greg Hewgill Dec 09 '10 at 19:13
  • 2
    I think the title of this question is going to trigger my mid-life crisis... – David Dec 09 '10 at 19:16
  • Ok Greg, I suppouse that I should have to kill the child from the parent. Am I wrong? – Pablo Reyes Dec 09 '10 at 19:25
  • You're right David, I just changed it, hehe – Pablo Reyes Dec 09 '10 at 19:25
  • +1 to Greg: For debugging, I'd add the shell pid to the prompt; and also print the pid of newly created child processes. – Lars Dec 09 '10 at 19:30
  • I've expanded on my comment in an answer with code. – Greg Hewgill Dec 09 '10 at 19:34
  • What version of wait are you using that does not look correct. http://linux.die.net/man/2/wait Also I would wait for a particular child to die rather than waiting for a generic child (or signal). – Martin York Dec 09 '10 at 20:08
  • move your if ( pid == 0 ) up to include your argument string setup code. There's no point doing that code in both the parent and the child. – Rob K Dec 09 '10 at 21:38

3 Answers3

3

I'm not sure that this is exactly the problem, but you must ensure that the child exits even if execvp() fails:

if (pid == 0) {
    if (execvp(tokens[0].c_str(), arg) == -1) {
        cout << "Command not known. " << endl;
    };
    exit(1); // or some other error code to indicate execvp() fails
} else {
    wait();
}

If you don't do this, then if excecvp() fails then you will end up with two instances of your shell, which is probably not what you want.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
3

You are calling wait() incorrectly. It expects to be passed a pointer-to-int, in which the child's exit status will be stored:

int status;
wait(&status);

Really, though, you should be using waitpid() to check for the specific child that you're after. You also need to loop around if waitpid() is interrupted by a signal:

int r;
do {
    r = waitpid(pid, &status, 0);
} while (r < 0 && errno == EINTR);
caf
  • 233,326
  • 40
  • 323
  • 462
  • Thank you VERY much. This is working perfect right now. What I dont understand is how the parent know wich is the PID of the child, because that "pid" should be the pid of the child, right? And I enter in "child mode" when pid==0... I hope you understand my question! – Pablo Reyes Dec 11 '10 at 16:18
  • @Tronfi: Yes, that's the PID of the child - the parent knows it because it's the value returned by `fork()`, which is stored in the variable `pid` in your example. – caf Dec 12 '10 at 04:34
0

The child must be terminated using the call

exit(0)
(only on success), as this helps in clening of memory and flushes the buffer. This status returned by the child must be checked by the parent and then only it should give the prompt.

Let me know if you need more details.

Arunmu
  • 6,837
  • 1
  • 24
  • 46