Search code examples
cposix

POSIX flags - how to set all of them / individual ones


I have this peice of code that should read from a pseudo terminal and print to another pseudo terminal (stdin):

// Declare & define a file descriptor.
int fd;

int status;
char string[] = "XXXX";
char buffer[8];

// Establish connection between a file and the file descriptor.
fd = open("/dev/pts/4", O_RDWR);

// Check if connection was established.
if(fd == -1){
    printf("Assigning failed: %s\n", strerror(errno));
}
else{
    fputs("Assigning successful!\n", stdout);
    // Set some flags for the connection.
    fcntl(fd, F_SETFL, 0);
}
   
// Read from the connection.
status = read(fd, buffer, 8);
if (status == -1){
    printf("Read failed: %s\n", strerror(errno));
}
else{
    printf("I read this: %s\n", buffer);
}

return 0;

I don't know exactly what fcntl(fd, F_SETFL, 0); does, but untill I used it I wasn't able to read from the /dev/pts/4. After I used it for the first time I could read normaly, even if I commented out this line of code.

I tried to explain it to myself reading POSIX...

F_SETFL flag is defined in the POSIX as:

Set the file status flags, defined in <fcntl.h>, for the file description associated with fildes from the corresponding bits in the third argument, arg, taken as type int. Bits corresponding to the file access mode and the file creation flags, as defined in <fcntl.h>, that are set in arg shall be ignored. If any bits in arg other than those mentioned here are changed by the application, the result is unspecified. If fildes does not support non-blocking operations, it is unspecified whether the O_NONBLOCK flag will be ignored.

So it sets file status flags asociated with the 1st argument (file descriptor) of fnctl() to 0? I found an explanation about the status flags in the POSIX:

The <fcntl.h> header shall define the following symbolic constants for use as file status flags for open(), openat(), and fcntl(). The values shall be suitable for use in #if preprocessing directives.

O_APPEND Set append mode. O_DSYNC Write according to synchronized I/O data integrity completion. O_NONBLOCK Non-blocking mode. O_RSYNC Synchronized read I/O operations. O_SYNC Write according to synchronized I/O file integrity completion.

So does fcntl(fd, F_SETFL, 0); set all those to 0? Are all of those flags a single bit? How do I only set one of them to 1 or 0? And how do I know which bit is assigned to which flag?


Solution

  • It's a bitmask. Each flag is a bit (or several bits) in the integer you pass to fcntl. Each symbolic constant expands to a number in which only the corresponding bit(s) is set, and so you can set several flags by ORing them together. For instance, if bit 2 is the append flag, then O_APPEND would be 0b100 == 4; if bit 3 is the nonblock flag, then O_NONBLOCK would be 0b1000 == 8, and so on. But by using the symbolic constants, you don't need to know which bits they actually are, which is good since they may vary between OSes.

    In fcntl(fd, F_SETFL, 0), the number 0 has no bits set, so all the flags are cleared. To set append and clear the rest, you could do fcntl(fd, F_SETFL, O_APPEND);. To set both append and nonblock, OR their constants together: fcntl(fd, F_SETFL, O_APPEND | O_NONBLOCK);

    With similar bit operations, you can operate on the flags returned by fl = fcntl(fd, F_GETFL):

    int fl = fcntl(fd, F_GETFL);
    if (fl & O_APPEND) { ... } // test if in append mode
    fcntl(fd, F_SETFL, fl | O_NONBLOCK); // turn on nonblock and keep everything else the same
    fcntl(fd, F_SETFL, fl & ~O_DSYNC); // turn off dsync and keep everything else the same
    

    There's some more examples here.