Linux Descriptor Type - c

How do I get the descriptor type? I am using epoll to monitor lots of descriptors like sockets, timers, and signals. I saw it is possible using fstat, but the mode only says something about sockets and pipes. fstat manpage. Is there a special function to identity a descriptor?

I do not think there is any simple or uniform way to do what you are asking. The command lsof]1 manages to determine this information so you may want to take a look at that code to see what they are doing.
Off the top of my head to determine if a descriptor is a socket you can use getsockopt(2). If a call to getsockopt fails and errno == ENOTSOCK then your descriptor is not a socket. likewise you can use isatty(3) to determine if a descriptor belongs to a serial port or terminal.

Related

Why is "setsockopt()" needed if "fcntl()" also manipulates file descriptors?

When socket programming in C, I noticed that sometimes fcntl() is used to manipulate socket behavior while other times setsockopt() is used.
For example, fcntl() is used to make a socket non-blocking, but setsockopt() is used to change the timeout time when sending/receiving data from a socket.
Is there any background / intuitive reasoning as to why both functions are needed?
Sockets are not the only type of file descriptor which you can make non-blocking. In theory, you can mark any file descriptor as non-blocking (by specifying O_NONBLOCK when you open() the file) but it's possible that the setting will be ignored (for regular files or special files which don't implement nonblocking mode). So the non-blocking flag is part of the general file interface.
By contrast, the socket timeouts apply only to sockets. That's made clear by the fact that you can only set them using a socket-specific interface. (Terminal devices have two read timeout attributes -- VTIME and VMIN -- which are vaguely related, but with very different semantics. Those are set with the termios interface.)
There are very few generally-applicable attributes which can be manipulated with fcntl. There are lots of attributes which are specific to certain types of files, and which are manipulated using a variety of specific interfaces.

Is the write() function in C blocking or non-blocking?

I looked on the Linux man pages for the answer but can't seem to find it. I know that read() is blocking but I'm still not sure about write().
Can anyone point me to any documentation for clarification?
Read POSIX on read() and
write(). See also functions such as open() and pipe().
It depends on the attributes of the file descriptor you're reading from or writing to (think O_NONBLOCK, for example), and on the underlying file type (disk file vs pipe vs FIFO vs socket vs character or block special), and so on.
Succinctly, both read() and write() can be blocking or non-blocking, depending on circumstances.

Fifos in Linux in packet mode

I have read the Linux manpage of pipe2, that states that you can use the O_DIRECT flag to create a pipe that performs I/O in packet mode.
I haven't found anything similar for fifos (named pipes) although I have heard that pipes and fifos share a lot of code in Linux, and it could be useful to me in a project (we already pass messages in fifos, but we have to seek for an especial terminator, reading one byte at a time).
Is there anything equivalent to perform fifo I/O in packet mode?
The O_* flags on a file descriptor can usually be changed by fcntl(fd, F_SETFL, ...) but in this case I don't think it will work, because of this:
https://lkml.org/lkml/2015/12/15/480
It's a patch that was submitted just 2 weeks ago to support exactly this use case, and the only replies to it have been a couple of build failures from an automated tester.
So you can try fixing that patch and applying it (should be easy - looks like a typo, the f should be filp)...
or (this is the option I'd prefer) see if your needs can be met by an AF_UNIX, SOCK_SEQPACKET socket instead of a pipe with this new flag. They're more powerful and more portable.
(I assume you already tried passing the O_DIRECT to open when you open the named pipe, since that's the most obvious thing.)
Both pipe & fifo are byte stream, it open a file descriptor, then use read() and write() to do communication.
If u want a packet (I am not sure what u mean, I assume you want to read a block of data without determine the boundary by yourself), POSIX - message queue might be a good choice. It send / receive data in unit of a message instead of byte by byte.
And you can also set priority on the messages which change the order of receiving, if all messages have the same priority (e.g 0), then the send & receive order is the same as FIFO.
If that's what u want, then check man mq_overview for details.
I'm not able to switch to a socket or message queue, and am stuck with using pipe. But good news, the patch refereed to in user2404501's answer eventually got accepted and is in linux v4.5 and newer. So using fnctl() to set O_DIRECT on a named pipe is valid.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0dbf5f20652108106cb822ad7662c786baaa03ff

How to implement a timeout in open/write function

I want to use named fifo channel and I want to implement a timeout when I write in this fifo.
fd = open(pipe, O_WRONLY);
write(fd, msg, len);
Program is blocked by function open, so using the function select will not work.
Thanks.
use select() and its timeout argument.
Read pipe(7), fifo(7), poll(2)
You might setup a timer or or alarm with a signal handler (see time(7) & signal(7)) before your call to open(2) - but I won't do that - or you could use the O_NONBLOCK flag, since fifo(7) says:
A process can open a FIFO in nonblocking mode. In this case, opening
for read-only will succeed even if no-one has opened on the write
side yet, opening for write-only will fail with ENXIO (no such device
or address) unless the other end has already been opened.
However, you need something (some other process reading) on the other side of the FIFO or pipe.
Perhaps you should consider using unix(7) sockets, i.e. the AF_UNIX address family. It looks more relevant to your case: change your code above (trying to open for writing a FIFO) to a AF_UNIX socket on the client side (with a connect), and change the other process to become an AF_UNIX socket server.
As 5gon12eder commented, you might also look into inotify(7). Or even perhaps D-bus !
I'm guessing that FIFOs or pipes are not the right solution in your situation. You should explain more and give a broader picture of your concerns and goals.

Avoid send to block when not using O_NONBLOCK

I have to write a chat client-server for a class using unix sockets (without O_NONBLOCK) and select for asynchronous I/O on them. At the moment, on the server, i read 1024 bytes from the client, and directly handle it.
For example, in case of a message, i will receive a command formatted as MSG <msg> (representing a client sending a message), i will go through all the sockets of the connected clients and write the message on them.
This approach actually works but i recently found by reading the man of send that it can blocks if the socket buffer is full and is the flag O_NONBLOCK is not set on the socket.
I think this problem could happen when a client does not read for some reasons (crash, bugged etc.) and this would be critical for my server since it will basically blocks until this client read again.
So here is my question:
What is the correct approach on a potentially blocking socket to avoid send to block if the socket buffer is full?
I'm currently using select only to check if there is something to read on sockets but maybe i should use it also to see if i can write on a particular socket too? And also, can i know how many bytes i can read/write when select returns? For example, if select "tells" that i can write on this socket, how can i know how many bytes i can write at most before writing on this socket actually becomes blocking?
Thanks.
You could use setsockopt() together with SO_SNDTIMEO to set up a maximum amount of time send() will try to do its work.
See man setsockoptand man 7 socket for details.
It might be horrible. If you don't go NONBLOCK-ing mode and calling select(), which internally puts the process on sleep for specific timeout value. That means, fd will be blocked for that specific time period.
This approach actually works but i recently found by reading the man of send that it can blocks if the socket buffer is full and is the flag O_NONBLOCK is not set on the socket.
This is why you use select, but it still isn't reliable, as man select states:
Under Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks. This could for example happen when data has
arrived but upon examination has wrong checksum and is discarded. There may be other circumstances in which a file descriptor is spuriously reported as ready. Thus it may
be safer to use O_NONBLOCK on sockets that should not block.

Resources