Quote:
Originally Posted by
howdini
I had initially planned on using Qt4 for my interface, but after reading on SDL I think i'll go that way. Allow me to ask the most pertinent question anybody undertaking such a project would like to ask. Exactly how does one execute a program from within the TE application, get the stdin, stdout and stderr of this newly executed program (my best guess for now is using some fork()ed processes) and spit this back out on my for now string variables for parsing and other processing?
Good guess.
- Create two pipes with the pipe() call. One will be shell input, one will be shell output. Two pipes, not three -- stdout and stderr usually just go to the same output unless the shell's told otherwise. It's fine to have the same pipe opened in two or more places, the kernel will do the smart thing and merge their output and only remove the pipe when all copies are closed.
- fork(). This creates a clone of the process differering only in the return value they see from the fork() call. The pipes will join them: write() to one end in the parent, and you can read() from the other end in the child.
You might want to do an immediate SDL_Quit() in the child after the fork. Or even just create a process before you call SDL_Init() that does the actual shell work while the original one handles all the graphics. (At this stage though, you might not need graphics at all, just dump to shell and avoid these complications)
- In the child process, use dup2() to replace stdin, stdout, and stderr with the appropriate ends of the input and output pipes. i.e. the writing end of the output pipe gets duplicated over STDOUT_FILENO and STDERR_FILENO, and the reading end of the input pipe gets duplicated over STDIN_FILENO. (I think STDIN_FILENO etc is available in stdio.h, and pipe() is in unistd.h)
- In both parent and child, close() the ends of the pipe you aren't using. You don't need the copies, and they'll jam it open later if you don't close them.
- In the child, run execv or execvp to replace the child process with the command you want to run. Something like execvp("/bin/echo", "/bin/echo", "asdf", NULL); The exec family of functions is in unistd.h
- You now have a child process of your choosing whose input you can write to by write()ing to the input pipe and whose output you can read() by reading from the output pipe.
- Keep read()ing output from the program until it dies, then close() both remaining pipes, and wait() for the child so it can enter the digital afterlife instead of lingering as a zombie. (see man 2 wait).
...oh, and when you initialize SDL, initialize it with SDL_INIT_NOPARACHUTE to prevent it from catching signals like SIGPIPE and SIGCHLD for you. Normally SDL catching rogue signals is a good thing, helps games not crash, but you definitely want to handle these signals in your own code for a shell.