Redirecting input/output of execvp - c

I want to redirect the output to a specific file when the program encounters a ">" and for it to get the input from a file if it finds a "<".
This works for the input, but when I try the output to file it doesn't write anything in the actual file nor does it show it in the terminal.
I can't seem to figure out why, can anyone help?
switch(fork()){
case 0:
for(i=0; i<num; i++){
if(strcmp(argv[i], ">") == 0){
if(fd1 = open(argv[i+1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR) < 0){
perror("cant open file");
exit(0);
}
dup2(fd1, 1);
close(fd1);
argv[i] = NULL;
}
if(strcmp(argv[i], "<") == 0){
if(fd0 = open(argv[i+1], O_RDONLY) < 0){
perror("cant open file");
exit(0);
}
dup2(fd0, 0);
close(fd0);
}
}
execvp(argv[0], argv);

The primary problem is that the = operator has lower precedence than the < operator. Thus, in your expression
fd1 = open(argv[i+1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR) < 0
, the return value of open() is compared to 0, and then the result of that comparison -- either 1 or 0 -- is assigned to fd1. The newly-opened file descriptor is lost, and goes unused. The actual result of the comparison is most likely 0 (the file was successfully opened), so your dup2() call a few lines later attempts to dupe file descriptor 0 onto file descriptor 1. In all likelihood that works, but any attempt by the program you exec to write to that file descriptor very likely fails.
You ought to write it as
(fd1 = open(argv[i+1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0
Alternatively, be a little less clever and separate that into two statements, so that the precedence question does not arise in the first place.
You have the same problem when you redirect input, so I'm doubtful of your claim that "This works for the input", but because in that case you'll end up just duping file descriptor 0 onto itself, the problem will manifest simply as the redirection not working as expected -- input will still come from the original program's standard input, not the standard input you designate.
Even that might go unnoticed, however, if you allow the shell to perform the input redirection instead of quoting the redirection operator so as to get it through to the program as an argument.

Related

Redirect output back to terminal in C

if (out) {
out = 0;
int fd = open(output_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) perror("open for writing");
if (dup2(fd, STDOUT_FILENO) < 0) { perror("dup2"); abort();}
close(fd);
}
if (-1 == execvp(argv[0], argv)) {
perror("execvp");
printf(" (couldn't find command)\n");
}
exit(1);
I am trying to redirect output back into terminal after redirecting it to a separate file. At the moment, this works when I call:
$ ls > file.txt
However, when I call
$ ls -l
right afterwards, it continues to direct its output into file.txt, rather than the terminal.
You cannot get the original standard output back after closing all copies of it.
Normally, if you are going to exec another program with redirected output, you first fork() a child, and apply the redirection only in the child, then have the child terminate immediately if the exec fails. The parent's streams are unaffected that way, so there is no need to restore them. This is what shells do when they run external commands.
But to answer the question posed, if you want to be able to redirect the standard output and then later restore it, you need to make a copy of it first ...
int stdout_save = dup(STDOUT_FILENO);
Then perform your redirection. When ready to restore, just copy the saved file descriptor back, and close the dupe.
dup2(stdout_save, STDOUT_FILENO);
close(stdout_save);
Do not neglect to perform proper checks on the return values of these function calls; I have omitted that from the example code for clarity and brevity.

Using execvp with dup2 raises EFAULT error

I'm building a small linux shell and am trying to implement the > operator to redirect the output of the commands to a file.
The issue I have is that when I try to run something like ls > test.txt, I get a Bad Address (EFAULT) error.
However, if I try it without the redirection, everything works as expected.
I've trimmed the code to a minimum to test only for ls, but i still get the same error, here's the code.
int saved_stdout;
__pid_t id = fork();
if (id == 0) {
saved_stdout = dup(1);
int fd = open("test.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
char* args[] = {"\0"};
execvp("ls", args);
fprintf(stderr, "Value of errno: %d\n", errno);
perror("Error printed by perror");
} else {
int status;
waitpid(id, &status, 0);
if (saved_stdout) {
dup2(saved_stdout, 1);
close(saved_stdout);
}
}
Does anyone as an idea on what I'm doing wrong here?
Thanks a lot
The execvp function expects the argument array to be terminated by a null pointer, not an empty string.
You should also remember that the argument array includes argv[0].
So the array should be like
char* args[] = { "ls", NULL };
Does anyone as an idea on what I'm doing wrong here?
The main problem is likely that your arguments to execvp() are incorrect:
char* args[] = {"\0"};
execvp("ls", args);
There are two things definitely wrong with that:
The argument array needs to be terminated with a null pointer. "\0" is not a null pointer; rather it is an array containing two null characters, and it decays to a valid, therefore non-null, pointer.
Even if "\0" were a null pointer, you would be short one argument. The first element of the argument vector, at index 0, should be a pointer to a string representing the program name.
In other words:
char* args[] = { "ls", NULL };
execvp("ls", args);
Additionally, the redirection you are performing is not consistent with the POSIX shell's treatment of the > redirection operator. In that form, that operator redirects only the standard output, not the standard error. Furthermore, it should open the designated file write-only, not read/write, because writing to it is all the program needs to do. Opening it read/write could cause it to fail to redirect to an existing file on which the user has write permissions but not read permissions.
Furthermore, The file mode you specify for the event that a new file is created also produces behavior inconsistent with the POSIX shell's. You should specify read/write permissions for user, group, and other, and let that be modified according to the umask in effect:
int fd = open("test.txt", O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

Using open(), read() and write() system calls to copy a file

I'm having trouble using read(), write(), and open() to copy a file into a new directory. I'm able to create a new file with open() and I'm able to write to the terminal with write() but I was under the assumption that if I passed my int file descriptor from open to write, write would write to the specified file.
In addition to this, once I open a file the permissions are all funky and I can only open it as root. Each time I create a file, it's completely empty but I can see the entire file I just read be printed out in the terminal.
I've read through the man files for each system call but I'm still not grasping what the issue is here.
Here's my code:
void *scanFile( s_request *request )
{
//TODO Print out all struct variables.
pthread_mutex_lock(&mutex);
int readFileReference;
int writeFileReference;
int bufferSize = request->bufferSize;
int numberOfBytesRead = bufferSize;
int *buffer[bufferSize];
if ((readFileReference = open(request->file, O_RDONLY)) == -1)
{
printf("Failed to open file. Aborting.\n");
exit(EXIT_FAILURE);
}
if ((writeFileReference = open("newfile.txt", O_CREAT | O_APPEND | O_RDWR) == -1))
{
printf("Failed to open write file. Aborting.\n");
exit(EXIT_FAILURE);
}
while ((read(readFileReference, buffer, numberOfBytesRead)) != 0)
{
write(writeFileReference, buffer, bufferSize);
bzero(buffer, bufferSize);
}
pthread_mutex_unlock(&mutex);
}
Fixed the issue:
Updated code:
void *scanFile( s_request *request )
{
//TODO Print out all struct variables.
pthread_mutex_lock(&mutex);
int readFileReference;
int writeFileReference;
int bufferSize = request->bufferSize;
int numberOfBytesRead = bufferSize;
int *buffer[bufferSize];
if ((readFileReference = open(request->file, O_RDONLY)) == -1)
{
printf("Failed to open file. Aborting.\n");
exit(EXIT_FAILURE);
}
if ((writeFileReference = open("newfile.txt", O_CREAT | O_WRONLY, 0777)) == -1)
{
printf("Failed to open write file. Aborting.\n");
exit(EXIT_FAILURE);
}
while ((read(readFileReference, buffer, numberOfBytesRead)) != 0)
{
write(writeFileReference, buffer, bufferSize);
bzero(buffer, bufferSize);
}
close(writeFileReference);
close(readFileReference);
pthread_mutex_unlock(&mutex);
}
if ((writeFileReference = open("newfile.txt", O_CREAT | O_APPEND | O_RDWR) == -1))
You've got the parentheses wrong on this line. (It's correct on the readFileReference line.) What you want is:
if ((writeFileReference = open("newfile.txt", O_CREAT | O_APPEND | O_RDWR)) == -1)
^^^ ^^^
Your original version of that line was calling open(), performing a comparison to -1, then assigning the result of that comparison to writeFileReference.
Additionally:
As noted by Zan Lynx in a comment, you need to pass a permissions value to open() when creating a file. 0666 is typically correct -- it'll create the file as readable/writable. (It's modified by the process umask, so it'll end up creating the file as 0644 under a typical configuration.)
You need to save the return value from read somewhere and pass that as the third argument to write (instead of bufferSize). Otherwise, your program will write more bytes than were read, e.g. when copying small files.
Technically, you should be checking the return value from write(). Just like read(), it's not guaranteed to complete a full write every time it's called. (In practice, when working with regular files, it will always either complete the write or return an error, but there are some esoteric situations where this may not be the case.)
You don't need to bzero() the buffer after writing from it.

How to redirect stdin (specific file name) to stdout (specific file name)

I am creating a shell code. Basically, I want to redirect stdin file to stdout file. For instance when I enter a command like sort < hello.c > t.txt, then the hello.c file should be copied in new mentioned file called t.txt.
Here is my code, I am able to redirect output of other commands, when I type ls > t.txt. However, I don't have any idea about redirecting one file's input to other file using dup2.
Here is my code, I am only posting the loop, as this is where I have to create the logic.
int in, out;
for (i = 0; i < arridx; i++) {
if(strcmp( array[i],"<")==0)
{
in = open(array[i+1], O_RDONLY);
array[i]=0;
// array[i+1]=0;
}
if(strcmp( array[i],">")==0)
{
out = open(array[i+1], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
array[i]=0;
// array[i+1]=0;
}
}
dup2(in, 0);
dup2(out, 1);
// close unused file descriptors
close(in);
close(out);
Input array would be like
array[0]="sort"
array[1]="<"
array[2]="hello.c"
array[3]=">"
array[4]="t.txt"
In fact, whenever you run something like :
command < hello.c > t.txt
The redirection will take place presuming command is your argv[0] with no of arguments as 1, and redirection taking place by the shell.
However, on another point, going through your program, if redirection is used
not from command prompt but by array contents only,
int dup2(int oldfd, int newfd); - creates a copy of the file descriptor oldfd.
In your case,
dup2(in, 0);
dup2(out, 1);
0 and 1 stands for stdin and stdout file descriptors respectively. So, if you would like to redirect your input to be taken from the stdin instead of hello.c (file opened as in) and output to be taken from the stdout instead of t.txt (file opened as out), then shouldn't be other way round i.e.
dup2(0, in);
dup2(1, out);

C: `write error: Bad file descriptor` after fork, dup2, and execv

Continuing on this problem, but I'll reiterate:
For a homework assignment I have to write a basic shell including redirection. The program uses readline to prompt for input, parses the input string, and breaks it down into the executable name, the arguments, and the input/output file(s), if applicable. After parsing the string, it forks and the child execv()'s to the executable that was passed in. I'm using dup2() to change the file descriptors after the fork and before the execv, but am having a problem once the program has execv'd to the new executable. If in my shell I run ls > foo.out, I get: ls: write error: Bad file descriptor
Here is the code for my child process (this is after fork()):
int _child(struct command *c){
int ret;
/* When given `ls > foo.out`, these next two lines output:
** c->infile is (null)
** c->outfile is foo.out
*/
printf("c->infile is %s\n",c->infile);
printf("c->outfile is %s\n",c->outfile);
if(c->infile){
int fd = open( c->infile, O_RDONLY);
int _fd_dup = dup2(fd,0);
close(0);
if(_fd_dup != 0){
fprintf(stderr, "Failed to redirect command input.\n");
return 0;
}
}
if(c->outfile){
int fd = open( c->outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
int _fd_dup = dup2(fd,1);
close(1);
if(_fd_dup != 1){
fprintf(stderr, "Failed to redirect command output.\n");
return 0;
}
}
I do not get the "Failed to redirect command output." error. As I mentioned, this is a homework assignment so I'm not looking for anyone to fix this, but rather point me in the right direction.
The problem is in this bit of code:
int _fd_dup = dup2(fd,1);
close(1);
You should be closing fd, not 1. You have the same problem in the fd 0 case, too.

Resources