Use opened file descriptor - c

I've got 2 programs, and in one i'm opening a file to read and from the other one i'm trying to read from file :
first program
fd = open("test.txt",O_RDONLY);
printf("%d\n",fd);
while(1);
second program :
char sir[100];
int fd, result;
scanf("%d",&fd);
rez = read(fd,((void*)sir), 2);
In the second program i read what i printed in first program. Why this code doesn't work and how can i read from that file descriptor from program nr 2?

File descriptors are unique to the process. Also you need to write to the file descriptor.

There are several problems:
fd = open("test.txt", O_RDONLY) opens the file for reading. If I understand what you are trying to do, you want to create the file and open it for writing. That would be fd = open("test.txt", O_CREAT | O_WRONLY).
printf("%d\n",fd) displays the value of the file handle. While that might be useful for debugging, I think you want something which writes to the file handle. write (fd, "hello", 5) is closer to that.
while(1); is an infinite CPU busy loop. This is not very useful.
Similarly the second program has issues:
fd = scanf("%d",&fd) is peculiar. I think you want to open the file just written, no? Instead, fd = open("test.txt", O_RDONLY).
With that corrected, the program can then read the content into the variable read (fd, sir, sizeof sir).
See if those help you.
If you are not primarily working with binary data in the files, the fopen() and fprintf() library calls are more convenient.

Related

Piping the stdout of a command to the stdin of another other using shared file and dup2()

I am writing a porgram with takes two arguments - the name of commands. The program should redirect the output of the first to a file 'tmp' than execute it, than redirect the stdin of the second command to 'tmp' and execute the second command.
#include<unistd.h>
#include<fcntl.h>
#include<wait.h>
#include<stdio.h>
int main(int argc, char** argv){
int fd = open("tmp", O_RDWR |O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
int cpid = fork();
if(cpid == 0){
dup2(fd, 1);
execlp(argv[1], "", NULL);
}
wait(NULL);
//If we uncoment this line the program gives correct output
//fd = open("tmp", O_RDWR, S_IRUSR | S_IWUSR);
dup2(fd, 0);
execlp(argv[2], "", NULL);
}
However when i run the program like ./main ls wc instead of
5 5 50
i get output 0 0 0 which means the wc command reads 0 bytes from stdin.
But if I instead create the file descriptor anew on the same file 'tmp' the program gives the correct output. How can this behaviour be explained?
This question is basically a duplicate of Can anyone explain a simple description regarding 'file descriptor' after fork()? but since this is a bit subtle I'll explain this specific case.
The process opens a file. This creates a file description. File descriptions are an intermediate concept between files and file descriptors. They are not directly exposed in the Unix API, but they have an important property in addition to the file that they point to, which we'll see in a minute.
The child writes to this file description. The parent waits.
The parent reads from the file description.
At the end of step 2, the file position on this file description is the end of the file. So at step 3, the parent starts reading at the end of the file.
If you add a call to rewind(fd) after wait(NULL), the child will read from the beginning of the file.
If you open the same file with a new open call, this creates a new file description. open puts the position on the new file description at the beginning of the file unless you set append mode.
The same file descriptions can be accessed through any number of file descriptors, potentially in different processes. The file position is a property of the file description, so anything that moves it (reading, writing, seeking) through one file descriptor also moves it for the other file descriptors, even in different processes.

redirect pipe's read end to a file descriptor

I have two child processes they share their parent's common pipe descriptors. There is no problem for closing ends etc. The problem is I wish to redirect pipe's read end to a file descriptor instead of holding a buffer and writing the buffer's content to a file. Is it possible? My code snippet as follow
// we're sure we can read from fd[0], I did it sucessfully
// I mean there is no problem about the communication
int open_fd = open(filename, O_WRONLY|O_CREAT, 0666);
if (dup2(open_fd,fd[0]) == -1) {
perror("error ");
return 1;
}
if (close(open_fd) == -1) {
perror("close error");
return 1;
}
When I did the above code, I doesn't write into the file called as filename. By the way, is there a need to close open_fd by calling close(open_fd)? Since dup2 closes it already.
You've probably misunderstood the purpose of dup2. It just changes "the meaning" of the file descriptor, so that it now "points" to the same stream as the other descriptor. But it doesn't in any way transfer data from one file descriptor to another. To actually achieve what you want you can try splice:
int open_fd = open(filename, O_WRONLY|O_CREAT, 0666);
splice(fd[0], NULL, open_fd, NULL, size, 0);
Note that you'll have to specify how much data you want to transfer (size variable in above example).
When I did the above code, I doesn't write into the file called as filename.
Of course not. When you call dup2(open_fd,fd[0]), you make the integer value stored in fd[0] refer to the same file that open_fd does, but that has to has nothing directly to do with what happens to bytes fed into the write end of the pipe. It affects them only indirectly, by causing the file descriptor number whose value was initially stored in fd[0] to first be closed if it is open.
A file descriptor is basically a key to a table mapping integers to open file descriptions in the kernel. dup2() changes what open file description the target FD is mapped to; it does not modify the open file description itself or affect its semantics, and it's at that level where the pipe lives.
Bytes written to the write end of a pipe are obtained from the read end of the pipe by reading it. However you do that, they initially reside in memory and / or in CPU registers. To make them go from there to a file, you need to send them there. For example, you might set up a thread whose purpose is to read whatever bytes are available from the pipe, and then writes them to your file.

Why doesn't dup2 occur in sequential order?

Here is a code snippet.
int saved_stdout = dup(1);
int fd = open("file.txt", O_WRONLY | O_CREAT, 0640);
close(1);
dup(fd);
close(fd);
printf("This text should go into the file\n");
//restore stdout
dup2(saved_stdout, 1);
printf("stdout restore");
I am trying to learn about dup and dup2. So I initially connected my file.txt to stdout. So whenever I use printf, I should be writing to file.txt instead of stdout. But I want to restore it back as well once I am done with this usage, so I use dup2 at the end as well.
The problem is that the text "This text should go into the file\n" never actually goes into the file, but gets printed on stdout. Why so? I straced for it, only to find that dup2 call occurs before that printf("This text..."); statement, why so?
The problem may be due to output buffering. stdout is fully buffered if it's not writing to a terminal, so when you redirect it to the file with dup() it will be buffered. Try flushing the output after the printf().
printf("This text should go into the file\n");
fflush(stdout);
I removed my prior answer as it's wrong.... But when you use printf() you're using the FILE * for stdout, inside of which there's a descriptor or something pointing to the terminal. Changing fd 1 is apparently not changing that.
I'm getting inconsistent results with testing, so giving up here. I just want to add that putting this line just after the open() makes it work for me, which highlights the obscure behaviour:
printf("fileno is %i\n", fileno(stdout));
Don't mix FILE * operations such as printf() with file descriptor manipulation. You should use write() with the descriptor.

Read a file in a while loop line-by-line while another function updates it

I am reading a file in a while loop from start to end:
FILE *file;
file = fopen(path_to_file), "r");
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, file) > 0) {
delete_line_from_file(line);
}
fclose(file);
The function delete_line_from_file() removes the line passed to it from the file. It reads in the whole file via open(fd, O_RDONLY | O_CLOEXEC) + read() + close(), then removes the line from the buffer and writes the whole buffer to the same file via open(fd, O_WRONLY | O_TRUNC | O_CLOEXEC) + write() + close(). The read() is locked in an advisory read-lock via struct flock lk and the write() is locked in an advisory write-lock.
When I read the file there are lines that get missed which has something to do with me reading the file from start to finish in one loop while writing to it. If I read in the whole file and go through the buffer line-by-line no lines get missed. (This is my preferred solution so far.) There are also no mistakes made when truncating and writing the file. The missed lines are still in the file after the loop finishes.
Can I make sure that my while-loop does not miss a line and cleanly empties the file? The file needs to be emptied line-by-line. It cannot be just truncated.
Here is one possible solution I had in mind. Mirror the file via fstat(file &fbuf) and check it's size with if (fbuf.st_size !=0) fseek(file, 0, SEEK_SET); but that seems inefficient.
So is the goal to empty the file completely?
Why don't you open the file as such:
open("file", O_TRUNC | O_WRONLY);
This will open the file with truncation. Alternatively, and perhaps a better solution, you can do this:
fopen("file", "w");
fopen with the "w" option delete the original file and replaces it with the new file of name "file".
Use fseek and ftell inside your loop.
Two processes modifying the same file is a recipe for problems. May be you need to use a pipe(2).

open() what happens if I open twice the same file?

If I open the same file twice, will it give an error, or will it create two different file descriptors? For example
a = open("teste.txt", O_RDONLY);
b = open("teste.txt", O_RDONLY);
To complement what #Drew McGowen has said,
In fact, in this case, when you call open() twice on the same file, you get two different file descriptors pointing to the same file (same physical file). BUT, the two file descriptors are indepedent in that they point to two different open file descriptions(an open file description is an entry in the system-wide table of open files).
So read operations performed later on the two file descriptors are independent, you call read() to read one byte from the first descriptor, then you call again read()on the second file descriptor, since thier offsets are not shared, both read the same thing.
#include <fcntl.h>
int main()
{
// have kernel open two connection to file alphabet.txt which contains letters from a to z
int fd1 = open("alphabet.txt",O_RDONLY);
int fd2 = open("alphabet.txt",O_RDONLY);
// read a char & write it to stdout alternately from connections fs1 & fd2
while(1)
{
char c;
if (read(fd1,&c,1) != 1) break;
write(1,&c,1);
if (read(fd2,&c,1) != 1) break;
write(1,&c,1);
}
return 0;
}
This will output aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz
See here for details, especially the examples programs at the end.
In this case, since you're opening both files as read-only, you will get two different file descriptors that refer to the same file. See the man page for open for more details.
It will create a new entry in the file descriptor table and the file table. But both the entries (old and new) in the file table will point to the same entry in the inode table.

Resources