Shell Redirection causes infinite printing to terminal - c

So I am writing a simple shell in C that can do STDOUT redirects. I am reading a string from the user, split it in arguments and then feeding it to exec pretty much. But when I am redirecting the output it 1st: Creates the file I asked for and populates it with the correct data, and then prints the command results in the terminal infinitely. Am I doing something wrong with dup2?
Here is my redirecting code. All the variables used are global, except for the file descriptor.
int execRedirectCommand(){
int fd;
pid_t pid = fork();
// fork failed
if(pid == -1){
char* error = strerror(errno);
printf("fork: %s\n", error);
return -1;
}
//Child process
else if(pid == 0){
fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0755);
if(fd == -1){
char* error = strerror(errno);
printf("open:%s\n", error);
return -1;
}
dup2(fd, STDOUT_FILENO);
close(fd);
execvp(payloadArgv[0], payloadArgv);
// execvp failed
char* error = strerror(errno);
printf("pdsh:%s:%s\n", payloadArgv[0], error);
return -1;
}
// Parent process
else{
close(fd);
fileName = NULL;
// Wait for child process to finish
int childStatus;
waitpid(pid, &childStatus, 0);
return 0;
}
}
EDIT Fixed a typo in the code nothing major.
EDIT 2 Including my main:
int main(){
setSigHandler();
char* user = getlogin();
while(1){
printf("[%s]-->$", user);
getNextCommand(payload);
if(!strcmp(payload, "\n")) continue;
if(!strcmp(payload, "close")) break;
parseCommandString();
if(fileName != NULL){
execRedirectCommand();
}else{
execSimpleCommand();
}
}
return 0; }
Note: commands without STDOUT redirection work just fine.

Thanks to mata and nsilent22 for pointing that out. The close(fd) in the parent process is not needed.

Related

Bad file descriptor error in Output redirection in c

I'm making a simple shell using c and when trying to redirect the output to a file, I cannot open that file or view its content to make sure my redirection actually worked, that is after exiting the simple shell and returning to the "actual" shell. I get Output.txt: Permission denied
Does anyone know what is happening? Here is part of my code
Note that redir is 1 when in args[] we have '>'.
while(1) {
bg = 0;
int cnt = getcmd("\n>> ", args, &bg);
int pid = fork();
if(pid == 0) {
if (redir == 1) {
int stdout = dup(1);
close(1);
int fd = open(args[2], O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU);
dup2(fd,1);
redir = 0;
}
if (execvp(args[0], args) < 0) {
printf("exec failed\n");
exit(1);
}
exit(0);
}
else if (pid == -1){
printf("ERROR: fork failed\n");
exit(1);
}
}

Redirection operator in C not working

I'm just doing this C program, which acts like a terminal and I've implemented the "dir" command, but I also want to implement the redirection ">" operator, so I can redirect everything to a file. I made a C program that waits for my input, and I want that input to be sent to the .txt file.
This is the output.c, which waits for an user input.
#include <stdio.h>
#define SIZE 500
int main(int argc, char *argv[]){
char text[SIZE];
scanf("%s", text);
return 0;
}
And this is the code that should redirect my input to my file.
UPDATE:
int rv;
pid_t pid;
int commpipe[2];
if(argc > 2){
char filename[] = "output.txt";
int old_prn_han, new_prn_han;
old_prn_han = dup(fileno(stdout));
strcpy(filename, argv[2]);
if((new_prn_han = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)) == -1){
fprintf(stderr, "Unable to create/open file '%s'\n", filename);
return 1;
}
dup2(new_prn_han, fileno(stdout));
if((pid=fork()) == -1){
fprintf(stderr, "Error forking new process!\n");
exit(2);
}
if(pid){
//Parent process
close(commpipe[0]);
setvbuf(stdout,(char*)NULL,_IONBF,0);
sleep(2);
wait(&rv);
fprintf(stderr, "Child exited with a %d value\n", rv);
}else{
//Child process
dup2(commpipe[0],0);
close(commpipe[1]);
if(execl("output", "output", NULL) == -1){
fprintf(stderr, "Error executing new process!\n");
exit(3);
}
}
close(fileno(stdout));
dup2(old_prn_han, fileno(stdout));
}
The problem is, that it seems my output program, which awaits user input does not execute. Can you guys help me out ?
UPDATE
I've updated my code. Now the new process is executing but when i type something in like 'name' at stdin, my file contains some weird symbols and they do not appear until I exit my main program. Why is that ?

Pipes and Forks

The goal of this project is to use pipes and forks to execute a line-count utility already written in a multi-process manner (one process per argument). I'm currently working on getting a single process working before expanding to handle multiple args.
Given two executables, lc1 and lc2, I want lc2 to establish a pipe to the stdout file descriptor of lc1, so that when execlp("lc1", argv[1], NULL) is called, the output will be read in by
while ((c= read(pipefd[0], readin, SIZE)) > 0)
According to my Unix book, I should use the open, dup2, close method for redirecting stdout to stdin, and here's my code:
int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));
if (pipe(pipefd)== -1)
perror("Can't open a pipe\n");
for (i=1; i< argc; i++){
if ((pid= fork())==-1)
perror("Can't fork\n");
run(argv[i]);
}
//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The run function is
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
When I try to execute this code, I get a stdin redirect error saying bad file descriptor. Why is this happening, and would appreciate any hints to for fixing.
run(argv[i]) is executed by both parent and child because are not assigning the functionality based on the returned PID, so one close after the other may have closed.
See below code, can he handy, I will use the code sample for situations like this. :
int main()
{
int pipe_fd[2] = {0};
int pid = -1;
int status = -1;
int ret_value = INVALID_CMD;
int cmd_output_len = -1;
status = pipe(pipe_fd);
if(status<0)
{
perror("pipe create err");
}
else
{
pid = fork();
if(pid<0)
{
}
else if (pid == 0)
{
/*Child functionality*/
child_func(pipe_fd, cmd);
}
else
{
/*Parent functionality*/
cmd_output_len = parent_fun(pid, pipe_fd);
}
}
return ret_value;
}
int child_func(int pipe_fd[], const char * cmd)
{
int status = 5;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
int exit_status = 0;
/*close read fd*/
close(read_fd);
/*dup2 stdout to write fd*/
//status = dup2(1, write_fd);
status = dup2(write_fd, 1);
if(status<0)
{
exit(-1);
}
else
{
system(cmd);
exit(0);
}
}
int parent_fun(int child_id, int pipe_fd[])
{
int status = -1;
int len = 0;
bool_e break_loop = FALSE;
int read_fd = pipe_fd[0]; /*read file descriptor*/
int write_fd = pipe_fd[1]; /*write file descriptor*/
/*close write fd*/
close(write_fd);
while(1)
{
sleep(1);
status = waitpid(child_id, &status, WNOHANG);
switch(status)
{
case 0:
/*Child is still active*/
printf("No process waiting to exit..\n");
len = do_ur_fun(read_fd);
write(1, output, len);
break;
/*case EINTR:
case ECHILD:
case EINVAL:
perror("waitpid error");
break_loop = TRUE;
break;*/
default:
if(status<0)
{
perror("waitpid error");
break_loop = TRUE;
len = -1;
}
else if(child_id == status)
{
/*Valid staus from child*/
len = read_output(read_fd, output);
//write(1, output, len);
break_loop = TRUE;
}
else
{
}
break;
}
if(TRUE == break_loop)
{
break;
}
}
return len;
}
int do_ur_fun (int read_fd)
{
/*Do your exec*/
}
MaheshGupta024 identified a very important problem in your code; I'm assuming you will fix that.
One of the other problem areas is:
close(1);
if (dup2(pipefd[0], 0)==-1)
perror("Can't redirect stdin");
close(pipefd[1]);
for (i=1; i< argc; i++){
if ((wait(NULL))== -1)
perror("Wait error");
while ((c= read(pipefd[0], readin, SIZE)) > 0){;
//print buf count
total += atoi(readin);
}
}
The first close closes the process's standard output; this is seldom a good idea. The next line duplicates the read end of the pipe to standard input - which is fine. As noted in a comment above, perror() does not exit. You then close the write end of the pipe - that's correct; but you should presumably close the read end of the pipe too since you have set it to come from the pipe.
Your loop starts OK; you have redundant parentheses in the wait() line. You read from pipefd[0] instead of standard input - so maybe you didn't want to close pipefd[0] but neither did you need to duplicate it to standard input. You then have a nested loop that reads on the pipe while there's more data to be read from a child - you don't absolutely need the wait() code with its loop since the inner while won't terminate until all the children are dead. On the other hand, there's no great harm in it - after the first child dies, you'll read the data from all the other children, then go into the outer loop and wait for each other child, with the inner loop terminating immediately since there is no data left to read.
So:
Don't close stdout.
Don't dup the pipe read to stdin.
Decide whether you want to clean up the loop - it will work, but could be cleaner.
The run() function is:
void run(char *f){
int fp;
if ((fp= open(f, O_RDONLY)) == -1)
perror("Can't open the file");
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
}
The argument should be const char *f (or use name or file instead of f). I would also pass the pipefd array to the function rather than use a global variable
.
Do not call a file descriptor fp; that name conventionally indicates a variable of type FILE *, not int.
However, you don't need to open the file in the first place - unless you want the calling program to do the error reporting instead of the invoked program. However, if you do want the calling program to do the error reporting, you should close the file descriptor before proceeding. (I've already commented on perror() returning).
It would be a good idea to print an error message after execlp(); the only time the function returns is when it fails, so there is no need to test its return value. You might want to exit too - rather than have the failed function go through the rest of the main program after the call to run().
Good points: you did close both the pipe file descriptors.
Hence:
void run(const char *file, int *pipefd)
{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls1", f, NULL);
perror("Failed to exec ls1");
exit(EXIT_FAILURE);
}

Redirecting stdout to file after a fork()

I'm working on a simple shell, but right now I am just trying to understand redirection. I'm just hard coding an ls command and trying to write it to a file for now. Currently, the ls runs, and the output file is created, but the output still goes to stdout and the file is blank. I'm confused as to why. Thanks in advance.
Here is my code:
int main()
{
int ls_pid; /* The new process id for ls*/
char *const ls_params[] = {"/bin/ls", NULL}; /* for ls */
int file; /* file for writing */
/* Open file check user permissions */
if (file = open("outfile", O_WRONLY|O_CREAT) == -1) /* , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP */
{
perror("Failed to open file");
_exit(EXIT_FAILURE);
}
ls_pid = fork(); /* Create new process for ls */
if (ls_pid == -1) /* error check */
{
perror("Error forking ls (pid == -1)");
_exit(EXIT_FAILURE);
}
else if (ls_pid == 0) /* Child of ls */
{
/* Redirect output to file */
if (dup2(file, STDOUT_FILENO) == -1) /* STDOUT_FILENO = 1 */
{
perror("Error duping to file");
_exit(EXIT_FAILURE);
}
close(file);
execvp("ls", ls_params); /* create the sort process */
/* execlp("ls", "ls", NULL); */
/* if this does not end the program, something is wrong */
perror("Exec failed at sort");
_exit(EXIT_FAILURE);
}
else /* ls parent (1) */
{
/* wait for child */
if (wait(NULL) == -1)
{
perror("fork failed on parent");
_exit(EXIT_FAILURE);
}
}
}
I don't see any problem. I tried your code by myself. And it works! My simplified code looks like:
int main(void) {
int fd;
char* const param[] = {"/bin/ls", NULL};
fd = open("outfile", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
perror("open failed\n");
exit(0);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork failed\n");
exit(0);
}
else if (pid == 0) {
dup2(fd, STDOUT_FILENO);
close(fd);
execvp("ls", param);
}
else {
wait(NULL);
}
}
I compile an execute it, and find the expected results in outfile.
The only difference is that I feed open with the S_IRUSER | S_IWUSER permission options since otherwise the open fails. I see similar thing in your code but somehow you commented them...

How to catch the ouput from a execl command

I'm using the execl function to run a Linux process from C. When I do, for example:
int cmd_quem() {
int result;
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
execl("/usr/bin/who", "who", NULL);
sleep(4); //checking if father is being polite
exit(1);
}
else {
// father's time
wait();
}
return 0;
}
I get on the console the result of doing "who" on the terminal. What I'd like to know is if there is any function to "catch" the output result from a command. What I mean is, if there is anyway to catch this:
feuplive tty5 2009-11-21 18:20
Which is one of the lines resulting from the who command.
To do this, you need to open a pipe. You then replace the child's stdout with the writing end of the pipe, and read from the reading end of the pipe in the parent. Like this modified version of your code:
int cmd_quem(void) {
int result;
int pipefd[2];
FILE *cmd_output;
char buf[1024];
int status;
result = pipe(pipefd);
if (result < 0) {
perror("pipe");
exit(-1);
}
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
dup2(pipefd[1], STDOUT_FILENO); /* Duplicate writing end to stdout */
close(pipefd[0]);
close(pipefd[1]);
execl("/usr/bin/who", "who", NULL);
_exit(1);
}
/* Parent process */
close(pipefd[1]); /* Close writing end of pipe */
cmd_output = fdopen(pipefd[0], "r");
if (fgets(buf, sizeof buf, cmd_output)) {
printf("Data from who command: %s\n", buf);
} else {
printf("No data received.\n");
}
wait(&status);
printf("Child exit status = %d\n", status);
return 0;
}
First, execl does not return unless there's a problem like the executable is not found. That sleep(4) is probably never executed.
As for redirecting and getting the output, check out the Unix Programming FAQ. Look for spawn_background_command.
The exec() family of functions creates a new process image from a regular, executable file. This file is either an executable object file, or an interpreter script. There is no return from a successful call to an exec() function, because the calling process is functionally replaced by the new process.
So any code after exec() is never executed unless it is failed.
If you want to capture output of a shell command you need popen.

Resources