3

I want to print identical messages to stdout and stderr ONLY if both are redirected to separate files or one of them goes to terminal and another - to a file. A redirection can be done with a shell or inside of a program (dup2). To that end, I can use isatty to distinguish between a character device and file. A problem is - if both streams go to files, how can I tell if those files are distinct?

def
  • 521
  • 4
  • 16
  • Of course, if I do in-program redirection, I can distinguish between 2 separate files. – def Nov 05 '22 at 21:10
  • 7
    May I suggest that you *not* do this? Generally speaking, your decisions about what messages to write to the standard output and what to write to the standard error ought not to be conditioned on what's on the other end of those. – John Bollinger Nov 05 '22 at 21:28
  • 3
    I agree with John Bollinger, above. Personally, I use stdout for data output, and stderr for error messages and progress information, because that best matches how Unix/POSIX/Linux command-line utilities are used. I never print identical messages to both. Perhaps you could consider a similar approach instead? Besides, while one could use `fstat(fileno(stdout), &outstat)` and `fstat(fileno(stderr), &errstat)` (see [man 2 fstat](https://www.man7.org/linux/man-pages/man2/fstat.2.html)), the calls can/will fail for non-file streams, so errors would need to be handled very precisely. – Blabbo the Verbose Nov 05 '22 at 21:36
  • I would happy to NOT do this, but in fact - it's a legacy product in which some dude have done this in some sadistic way many years ago, and of course, after close examination - it proves to not properly work. I am assigned to fix this. But if there is a solid reason against such a thing, I will have to convince my boss to get rid of it and be done. – def Nov 05 '22 at 21:47
  • fstat sounds like a good approach for ordinary files. For terminals, you can check ```ptsname```. Not sure there is a proper way for pipes, but you can inspect the symlinks in ```/proc//fd```. If both are redirected to the same pipe, the content of the symlink should show up as something like ```pipe:[13145240]``` – Homer512 Nov 05 '22 at 22:47
  • @BlabbotheVerbose: `fstat` does not fail for "non-file streams". On a 64-bit system, it shouldn't fail at all as long as its argument is a valid fd. A lot of the fields won't have useful information but many will. – rici Nov 05 '22 at 22:48
  • 1
    @Homer512: in the case of your pipe, you'll find 13145240 in the `st_ino` (inode) field in the `struct stat` returned by `fstat`. `st_ino` works fine for sockets, too. And ptys. – rici Nov 05 '22 at 22:53
  • @rici: It *can* fail for example for security reasons (see [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html)), and in Linux, whenever the underlying fs implements getattr() but returns an error for whatever reasons. Trusting that the call does not fail (or can only fail in a real error condition) will lead to a fragile implementation. This is why it would be very important to handle the returned errors carefully and correctly; definitely not just assume the calls shan't fail. – Blabbo the Verbose Nov 06 '22 at 00:50
  • 3
    In general, if you do happen to have two `struct stat` returned by successful calls to `fstat()` or `fstatat()`, the two refer to the same thing if and only if their `st_dev` and `st_ino` fields match. Timestamps, size, mode, owner, group, and link count are not reliable, as the thing might be accessed or linked between the two calls. – Blabbo the Verbose Nov 06 '22 at 00:53
  • @blabbo: I agree with all that. You're right, I overstated a bit. My main point was that it doesn't fail just because the target was not a regular file. – rici Nov 06 '22 at 00:55
  • @blabbo: However, the [Posix](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html) reference you provide does not seem to support your assertion that it can fail for security reasons. The failure causes listed are EBADF, indicating that the integer you provided is not the index of an open file descriptor (perhaps its garbage or perhaps you already closed the fd), or EIO, indicating an I/O error, or EOVERFLOW, indicating that fields in struct stat aren't wide enough, something which shouldn't happen on 64-bit Linux (but certainly happened on 32-bit). – rici Nov 06 '22 at 01:36
  • EACCES is not in the list (as it is for stat and lstat). – rici Nov 06 '22 at 01:39
  • @rici: POSIX does say *"An implementation that provides additional or alternative file access control mechanisms may, under implementation-defined conditions, cause fstat() to fail."* and indeed, this can happen in Linux with a security module that hooks `inode_getattr`, for example SELinux: [security/selinux/hooks.c:selinux_inode_getattr()](https://elixir.bootlin.com/linux/latest/source/security/selinux/hooks.c#L3259). – Blabbo the Verbose Nov 06 '22 at 06:55
  • @Homer512 you just need to read `"/proc/self/fd/0"`, no need to get the pid and read `/proc//fd` – phuclv Nov 07 '22 at 16:17

0 Answers0