I am implementing a shell.
When attempting a command other than changing directories, execvp() runs, the child terminates and a new child is created. When I change directories, the child does not terminate and a new child is created. Here is a sample of my code:
for(;;) {
printf("bash: ");
parse();
...
pid_t pid = fork()
if (pid == 0)
if (!strcmp(line[0], "cd"))
if (!line[1]) (void) chdir(getenv("HOME"));
else (void) chdir(line[1]);
else execvp(line[0], line);
...
if (pid > 0) {
while (pid == wait(NULL));
printf("%d terminated.\n", pid);
}
}
cd ../; ls; runs correctly, except I have to Ctrl+D twice to end the program.
Though, if I pipe the same information (ie. mybash < chdirtest), it runs correctly once, terminates the child, runs again except in the original directly, then terminates the final child.
cd should not be invoked through a child process, the shell itself should change its current directory (that's the property of an internal command: modify the process of the shell itself).
A (primitve) shell should looks like:
for(;;) {
printf("bash: ");
parse();
// realize internal commands (here "cd")
if (!strcmp(line[0], "cd")) {
if (!line[1]) (void) chdir(getenv("HOME"));
else (void) chdir(line[1]);
continue; // jump back to read another command
}
// realize external commands
pid_t pid = fork()
if (pid == 0) {
execvp(line[0], line);
exit(EXIT_FAILURE); // wrong exec
}
// synchro on child
if (pid > 0) {
while (pid == wait(NULL));
printf("%d terminated.\n", pid);
}
}
Related
So I've been trying to fork a process two times, first fork is to let the main process continue working and the second is to let the child capture the output of the execution of the grand child process. The code is as follow:
void execute_run(char **parameters) {
task_t* task = create_task();
tasks[task_number] = *task;
int pipe_stdout[2];
ASSERT_SYS_OK(pipe(pipe_stdout)); // creating a pipe for stdout
pid_t pid = fork();
ASSERT_SYS_OK(pid);
if (pid == 0) {
// child process
char buffer[MAXLENGTH_OUTPUT];
pid = fork();
ASSERT_SYS_OK(pid);
if (pid == 0) {
// this process will redirect stdout to buffer
const char* program_name = parameters[0];
char** program_args = ¶meters[1];
ASSERT_SYS_OK(close(pipe_stdout[0]));
ASSERT_SYS_OK(dup2(pipe_stdout[1], STDOUT_FILENO));
// redirecting stdout to pipe
ASSERT_SYS_OK(close(pipe_stdout[1]));
ASSERT_SYS_OK(execvp(program_name, program_args));
}
else {
ASSERT_SYS_OK(close(pipe_stdout[1]));
int status;
do
{
// this loop runs forever
} while (waitpid(pid, &status, WNOHANG) == 0);
ASSERT_SYS_OK(close(pipe_stdout[0]));
// this is never printed
fprintf(stderr, "Child process finished with status %d\n", status);
}
} else {
// parent process won't use any of the pipe ends
ASSERT_SYS_OK(close(pipe_stdout[0]));
ASSERT_SYS_OK(close(pipe_stdout[1]));
}
}
char** parameters are ['cat', 'in.txt'] in the example i'm trying to run (and file exists).
Thanks in advance!
I've tried debugging it and running with different commands like 'ls' for which it works fine, nevertheless I can't figure out why with 'cat' it doesn't work
The problem is that either the parent or child process is not exiting. The child process prints its message and then the program hangs. Could be stuck at system() because this doesn't happen when the system() call is removed. The unhide_string() filters out non-letters and args is defined elsewhere. The command "cmd" that's passed to get_page() is valid.
int get_page(char* cmd) {
system(cmd);
return 0;
}
int main() {
char* args_s = unhide_string(args);
if (args_s == NULL) {
printf("Please run me again :)\n");
}
if (fork() == 0) {
get_page(args_s);
printf("Guess what I did :)\n");
}
free(args_s);
return 0;
}
Fixed by having the parent wait for the child process to finish. This allowed the program to properly terminate.
Revised fork code:
if (fork() == 0) {
get_page(args_s);
printf("Guess what I did :)\n");
} else {
waitpid(-1, NULL, 0);
}
For some unknown reason, when I'm executing piped commands in my shell program, they're only outputting once I exit the program, anyone see why?
Code:
int execCmdsPiped(char **cmds, char **pipedCmds){
// 0 is read end, 1 is write end
int pipefd[2];
pid_t pid1, pid2;
if (pipe(pipefd) == -1) {
fprintf(stderr,"Pipe failed");
return 1;
}
pid1 = fork();
if (pid1 < 0) {
fprintf(stderr, "Fork Failure");
}
if (pid1 == 0) {
// Child 1 executing..
// It only needs to write at the write end
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
if (execvp(pipedCmds[0], pipedCmds) < 0) {
printf("\nCouldn't execute command 1: %s\n", *pipedCmds);
exit(0);
}
} else {
// Parent executing
pid2 = fork();
if (pid2 < 0) {
fprintf(stderr, "Fork Failure");
exit(0);
}
// Child 2 executing..
// It only needs to read at the read end
if (pid2 == 0) {
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
if (execvp(cmds[0], cmds) < 0) {
//printf("\nCouldn't execute command 2...");
printf("\nCouldn't execute command 2: %s\n", *cmds);
exit(0);
}
} else {
// parent executing, waiting for two children
wait(NULL);
}
}
}
Output:
In this example of the output, I have used "ls | sort -r" as the example, another important note is that my program is designed to only handle one pipe, I'm not supporting multi-piped commands. But with all that in mind, where am I going wrong, and what should I do to fix it so that it's outputting within the shell, not outside it. Many thanks in advance for any and all advice and help given.
The reason would be your parent process file descriptors are not closed yet. When you wait for the second command to terminate, it hangs because the writing end is not closed so it wait until either the writing end is closed, or new data is available to read.
Try closing both pipefd[0] and pipefd[1] before waiting for process to terminate.
Also note that wait(NULL); will immediately return when one process has terminated, you would need a second one as to not generate zombies if your process still runs after that.
"ulimit -c unlimited" has been done. Here is the code:
main()
{
do
{
pid_t pid = fork();
int stat_loc;
if(pid < 0)
exit(1);
else if(pid > 0)
{
waitpid(pid, &stat_loc, 0);
sleep(5);
}
else
break;
}
while(1);
assert(0);
}
If I replace sleep(5) with assert(0) the parent process dumps core.
Calling assert(0) on a debug build of your application should cause an abort:
If the argument expression of this macro with functional form compares equal to zero (i.e., the expression is false), a message is written to the standard error device and abort is called, terminating the program execution.
What are you actually looking to do here? It looks like there may be a problem with your forking logic. Typically you test to see if pid == 0 to see if you're in the child process and pid > 0 to see if you're in the parent process, like this:
pid_t pid = fork();
if (pid == 0) {
// child process because return value zero
printf("Hello from Child!\n");
} else if (pid > 0) {
// parent process because return value non-zero.
printf("Hello from Parent!\n");
} else {
printf("Error occurred.\n");
}
In your question you're checking for > 0 and < 0.
Edit: Added error checking branch.
Learning to use the fork() command and how to pipe data between a parent and it's children. I am currently trying to write a simple program to test how the fork and pipe functions work. My problem seems to be the correct use/placement of the wait function. I want the parent to wait for both of its children to finish processing. Here is the code I have so far:
int main(void)
{
int n, fd1[2], fd2[2];
pid_t pid;
char line[100];
if (pipe(fd1) < 0 || pipe(fd2) < 0)
{
printf("Pipe error\n");
return 1;
}
// create the first child
pid = fork();
if (pid < 0)
printf("Fork Error\n");
else if (pid == 0) // child segment
{
close(fd1[1]); // close write end
read(fd1[0], line, 17); // read from pipe
printf("Child reads the message: %s", line);
return 0;
}
else // parent segment
{
close(fd1[0]); // close read end
write(fd1[1], "\nHello 1st World\n", 17); // write to pipe
// fork a second child
pid = fork();
if (pid < 0 )
printf("Fork Error\n");
else if (pid == 0) // child gets return value 0 and executes this block
// this code is processed by the child process only
{
close(fd2[1]); // close write end
read(fd2[0], line, 17); // read from pipe
printf("\nChild reads the message: %s", line);
}
else
{
close(fd2[0]); // close read end
write(fd2[1], "\nHello 2nd World\n", 17); // write to pipe
if (wait(0) != pid)
printf("Wait error\n");
}
if (wait(0) != pid)
printf("Wait error\n");
}
// code executed by both parent and child
return 0;
} // end main
Currently my output looks something along the lines of:
./fork2
Child reads the message: Hello 1st World
Wait error
Child reads the message: Hello 2nd World
Wait error
Where is the appropriate place to make the parent wait?
Thanks,
Tomek
That seems mostly ok (I didn't run it, mind you). Your logic error is in assuming that the children will end in some particular order; don't check the results of wait(0) against a particular pid unless you're sure you know which one you're going to get back!
Edit:
I ran your program; you do have at least one bug, your second child process calls wait(), which you probably didn't want to do. I recommend breaking some of your code out into functions, so you can more clearly see the order of operations you're performing without all the clutter.
i think its better to use something like this, in order to wait for all the childrens.
int stat;
while (wait(&stat) > 0)
{}