I saw following function in setup.c of git source code.
Code:
/* if any standard file descriptor is missing open it to /dev/null */
void sanitize_stdfds(void)
{
int fd = open("/dev/null", O_RDWR, 0);
while (fd != -1 && fd < 2)
fd = dup(fd);
if (fd == -1)
die_errno("open /dev/null or dup failed");
if (fd > 2)
close(fd);
}
It try to open stdio file descriptors (0/1/2) to /dev/null, if they are missing.
My question is:
In while (fd != -1 && fd < 2) , why use 2, but not 3.
Because if fd == 2 you have opened file descriptors 0,1,2 and there is nothing more to do. You only needed to open those 3 descriptors for stdin, stdout and stderr.
If you put there 3, the loop would open file descriptors 0,1,2,3. Then the line if (fd > 2) close(fd); would close the descriptor 3. So it will work in both cases, but the original solution is better.
The reason of fd < 2 is because you have already called the method int fd = open("/dev/null", O_RDWR, 0); .
"The file descriptor returned by a successful call(open) will be the lowest-numbered file descriptor not currently open for the process." So if the stdio file descriptors (0/1/2) are missing, the return of open has already occupied a missed file descriptors. Then you should judge by 2 rather than 3.
Related
I have read this post Determine between socket and fd, and the answer recommends creating a structure with two fields, is there another way to test if a descriptor is a socket or a regular file on windows ?
You could try something that should only work on a socket, and see if it fails with ENOTSOCK. getsockname() for example.
this could help:
replace stdin stdout stderr with a socket…
http://www6.uniovi.es/cscene/CS5/CS5-05.html
int exec_comm_handler( int sck )
{
close(0); /* close standard input */
close(1); /* close standard output */
close(2); /* close standard error */
if( dup(sck) != 0 || dup(sck) != 1 || dup(sck) != 2 ) {
perror("error duplicating socket for stdin/stdout/stderr");
exit(1);
}
printf("this should now go across the socket...\n");
execl( "/bin/sh", "/bin/sh", "-c", "/path/to/redirected_program" );
perror("the execl(3) call failed.");
exit(1);
}
Our 'comm handler' first closes the file descriptors for standard input,
standard output, and standard error. It then uses dup(2) to duplicate
the socket file handle. dup(2) will duplicate the given file descriptor
and return the duplicate as the next available descriptor. Since 0, 1,
and 2 are the next available descriptors, they should be the returned
duplicates. Now operations on stdin/stdout/stderr [0/1/2] will act
upon the socket instead of the original stdin/stdout/stderr.
I was unaware that this technique (calling dup(2) on a socket file
descriptor) was possible untill seeing code written by Martin Mares.
i am writing a driver code, to read some register values from x86., when i ran my user space application i got the below error.
ioctl:Inappropriate ioctl for device
here is the code sniff..
fd = open_drvfile();
if(ioctl(fd, IOCTL_MSR_CMDS, (long long)msr_start) == -1 ) {
perror("ioctl:");
exit (0);
}
and open_drvfile() just open(create and open) the char file as below
fd = open("/dev/" DEV_NAME, O_RDWR|O_CREAT);
if (fd == -1) {
perror("Failed to open /dev/" DEV_NAME);
}
return fd;
can some one point where i made mistake on this?
A char device implies that it shall be created with mknod(), and not with O_CREAT under open() flags (which will create a regular file, not a char device).
(see question comments).
close(fileno(stdout));
int fd = dup(fileno(stdin));
//printf("Hello World\n");
write(fd, "Hello", 7);
Here for both printf and write is writing Hello to the screen..But I think they should not because I am duplicating the stdin to 1 or stdout closed perviously. And printf is connected to stdout but not stdin but still they are printing...Please explain if I have anything wrong in understanding the dup
The printf and write in your code happen to work, due to historical custom. Here's what's happening:
When a user logs into a terminal on a Unix-like system, or opens a terminal window under X11, file descriptors 0, 1, and 2 are connected to a terminal device, and each of them is opened for both reading and writing. This is the case despite the fact that one normally only reads from fd 0 and writes to fd 1 and 2.
Yet here is the code from 7th edition init.c:
open(tty, 2);
dup(0);
dup(0);
...
execl(getty, minus, tty, (char *)0);
And here is how ssh does it:
ioctl(*ttyfd, TCSETCTTY, NULL);
fd = open("/dev/tty", O_RDWR);
if (fd < 0)
error("%.100s: %.100s", tty, strerror(errno));
close(*ttyfd);
*ttyfd = fd;
...
/* Redirect stdin/stdout/stderr from the pseudo tty. */
if (dup2(ttyfd, 0) < 0)
error("dup2 stdin: %s", strerror(errno));
if (dup2(ttyfd, 1) < 0)
error("dup2 stdout: %s", strerror(errno));
if (dup2(ttyfd, 2) < 0)
error("dup2 stderr: %s", strerror(errno));
(The dup2 function dups arg1 into arg2, closing arg2 first if necessary.)
And here is how xterm does it:
if ((ttyfd = open(ttydev, O_RDWR)) >= 0) {
/* make /dev/tty work */
ioctl(ttyfd, TCSETCTTY, 0);
...
/* this is the time to go and set up stdin, out, and err
*/
{
/* dup the tty */
for (i = 0; i <= 2; i++)
if (i != ttyfd) {
IGNORE_RC(close(i));
IGNORE_RC(dup(ttyfd));
}
/* and close the tty */
if (ttyfd > 2)
close_fd(ttyfd);
Back to your code.
close(fileno(stdout));
This closes fd 1.
int fd = dup(fileno(stdin));
This duplicates fd 0 into the lowest available fd, which is 1, and assigns 1 to fd. (This assumes that fd 0 is open, of course.) Both fd's 0 and 1 are now open for reading and writing (assuming they're connected to a terminal device). On a Linux system you can verify this:
$ cat /proc/self/fdinfo/0
pos: 0
flags: 0100002
mnt_id: 20
$ cat /proc/self/fdinfo/1
pos: 0
flags: 0100002
mnt_id: 20
The 2 in flags, which is the constant O_RDWR, means open for reading and writing.
printf("Hello World\n");
This writes to fd 1, which is, once again, open for reading and writing to your terminal.
write(fd, "Hello", 7);
This writes to fd 1 again. (Note that "Hello" is only 6 bytes, though.)
If you are running your program on a terminal, then stdin and stdout are opens of the same file - your terminal device. So it should not be surprising that writing to stdin - or a dup() of fileno(stdin) - writes to your terminal device, and you see it.
If you run your program with stdin connected somewhere else, like a disk file, you'll see a different result:
./program < file
I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it. Is there any robust way to find out which process actually did create the file and which did open already create file, for instance, if I want to accurately count how many times that file was opened in such scenario.
I guess I could put a global mutex on file open operation, and do a sequence of open() calls using O_CREAT and O_EXCL flags, but that doesn't fit my definition of "robust".
Use O_EXCL flag with O_CREAT. This will fail if the file exists and errno will be set to EEXIST. If it does fail
then attempt open again without O_CREAT and without O_EXCL modes.
e.g.
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if ((fd == -1) && (EEXIST == errno))
{
/* open the existing file with write flag */
fd = open(path, O_WRONLY);
}
Based roughly on your comments, you want something along the lines of this function:
/* return the fd or negative on error (check errno);
how is 1 if created, or 0 if opened */
int create_or_open (const char *path, int create_flags, int open_flags,
int *how) {
int fd;
create_flags |= (O_CREAT|O_EXCL);
open_flags &= ~(O_CREAT|O_EXCL);
for (;;) {
*how = 1;
fd = open(path, create_flags);
if (fd >= 0) break;
if (errno != EEXIST) break;
*how = 0;
fd = open(path, open_flags);
if (fd >= 0) break;
if (errno != ENOENT) break;
}
return fd;
}
This solution is not bullet proof. There may be cases (symbolic links maybe?) that would cause it to loop forever. Also, it may live-lock in certain concurrency scenarios. I'll leave resolving such issues as an exercise. :-)
In your edited question, you pose:
I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it.
A hack-ish, but more bullet proof, solution would be to give each process a different user ID. Then, just use the regular open(path, O_CREAT|...) call. You can then query the file with fstat() on the file descriptor, and check the st_uid field of the stat structure. If the field equals the processes' user ID, then it was the creator. Otherwise, it was an opener. This works since each process deletes the file after opening.
From what I have been reading on The Open Group website on fcntl, open, read, and write, I get the impression that whether O_NONBLOCK is set on a file descriptor, and hence whether non-blocking I/O is used with the descriptor, should be a property of that file descriptor rather than the underlying file. Being a property of the file descriptor means, for example, that if I duplicate a file descriptor or open another descriptor to the same file, then I can use blocking I/O with one and non-blocking I/O with the other.
Experimenting with a FIFO, however, it appears that it is not possible to have a blocking I/O descriptor and non-blocking I/O descriptor to the FIFO simultaneously (so whether O_NONBLOCK is set is a property of the underlying file [the FIFO]):
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fds[2];
if (pipe(fds) == -1) {
fprintf(stderr, "`pipe` failed.\n");
return EXIT_FAILURE;
}
int fd0_dup = dup(fds[0]);
if (fd0_dup <= STDERR_FILENO) {
fprintf(stderr, "Failed to duplicate the read end\n");
return EXIT_FAILURE;
}
if (fds[0] == fd0_dup) {
fprintf(stderr, "`fds[0]` should not equal `fd0_dup`.\n");
return EXIT_FAILURE;
}
if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) {
fprintf(stderr, "`fds[0]` should not have `O_NONBLOCK` set.\n");
return EXIT_FAILURE;
}
if (fcntl(fd0_dup, F_SETFL, fcntl(fd0_dup, F_GETFL) | O_NONBLOCK) == -1) {
fprintf(stderr, "Failed to set `O_NONBLOCK` on `fd0_dup`\n");
return EXIT_FAILURE;
}
if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) {
fprintf(stderr, "`fds[0]` should still have `O_NONBLOCK` unset.\n");
return EXIT_FAILURE; // RETURNS HERE
}
char buf[1];
if (read(fd0_dup, buf, 1) != -1) {
fprintf(stderr, "Expected `read` on `fd0_dup` to fail immediately\n");
return EXIT_FAILURE;
}
else if (errno != EAGAIN) {
fprintf(stderr, "Expected `errno` to be `EAGAIN`\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
This leaves me thinking: is it ever possible to have a non-blocking I/O descriptor and blocking I/O descriptor to the same file and if so, does it depend on the type of file (regular file, FIFO, block special file, character special file, socket, etc.)?
O_NONBLOCK is a property of the open file description, not of the file descriptor, nor of the underlying file.
Yes, you could have separate file descriptors open for the same file, one of which is blocking and the other of which is non-blocking.
You need to distinguish between a FIFO (created using mkfifo()) and a pipe (created using pipe()).
Note that the blocking status is a property of the 'open file description', but in the simplest cases, there is a one-to-one mapping between file descriptors and open file descriptions. The open() function call creates a new open file description and a new file descriptor that refers to the open file description.
When you use dup(), you have two file descriptors sharing one open file description, and the properties belong to the open file description. The description of fcntl() says that F_SETFL affects the open file description associated with the file descriptor. Note that lseek() adjusts the file position of the open file description associated with the file descriptor - so it affects other file descriptors duplicated from the original one.
Removing the error handling from your code to reduce it, you have:
int fds[2];
pipe(fds);
int fd0_dup = dup(fds[0]);
fcntl(fd0_dup, F_SETFL, fcntl(fd0_dup, F_GETFL) | O_NONBLOCK);
Now both fd0_dup and fds[0] refer to the same open file description (because of the dup()), so the fcntl() operation affected both file descriptors.
if ((fcntl(fds[0], F_GETFL) & O_NONBLOCK)) { ... }
Hence the observed behaviour here is required by POSIX.