Reputation: 92966
I'm writing a curses-based program. In order to make it simpler for me to find errors in this program, I would like to produce debug output. Due to the program already displaying a user interface on the terminal, I cannot put debugging output there.
Instead, I plan to write debugging output to file descriptor 3 unconditionally. You can invoke the program as program 3>/dev/ttyX
with /dev/ttyX
being a different teletype to see the debugging output. When file descriptor 3 is not opened, write calls fail with EBADF
, which I ignore like all errors when writing debugging output.
A problem occurs when I open another file and no debugging output has been requested (i.e. file descriptor 3 has not been opened). In this case, the newly opened file might receive file descriptor 3, causing debugging output to randomly corrupt a file I just opened. This is a bad thing. How can I avoid this? Is there a portable way to mark a file descriptor as “reserved” or such?
Here are a couple of ideas I had and their problems:
/dev/null
or a temporary file to file descriptor 3 (e.g. by means of dup2()
) before opening any other file. This works but I'm not sure if I can assume this to always succeed as opening /dev/null
may not succeed.exec
as a different file descriptor might have been opened (and not closed) prior to the exec
call. I could intentionally close file descriptor 3 before calling exec
when it has not been opened for debugging, but this feels really uggly.Upvotes: 2
Views: 2048
Reputation: 21248
Why use fd 3? Why not use fd 2 (stderr)? It already has a well-defined "I am logging of some sorts" meaning, is always (not true, but sufficiently true...) and you can redirect it before starting your binary, to get the logs where you want.
Another option would be to log messages to syslog, using the LOG_DEBUG level. This entails calling syslog()
instead of a normal write function, but that's simply making the logging more explicit.
A simple way of checking if stderr has been redirected or is still pointing at the terminal is by using the isatty
function (example code below):
#include <stdio.h>
#include <unistd.h>
int main(void) {
if (isatty(2)) {
printf("stderr is not redirected.\n");
} else {
printf("stderr seems to be redirected.\n");
}
}
Upvotes: 4
Reputation: 25119
You claim you can't guarantee you can open /dev/null
successfully, which is a little strange, but let's run with it. You should be able to use socketpair()
to get a pair of FDs. You can then set the write end of the pair non-blocking, and dup2
it. You claim you are already ignoring errors on writes to this FD, so the data going in the bit-bucket won't bother you. You can of course close the other end of the socketpair.
Upvotes: 2
Reputation: 1
Don't focus on a specific file descriptor value - you can't control it in a portable manner anyway. If you can control it at all. But you can use an environment variable to control debug output to a file:
int debugFD = getDebugFD();
...
int getDebugFD()
{
const char *debugFile = getenv( "DEBUG_FILE" );
if ( NULL == debugFile )
{
return( -1 );
}
int fd = open( debugFile, O_CREAT | O_APPEND | O_WRONLY, 0644 );
// error checking can be here
return( fd );
}
Now you can write your debug output to debugFD
. I assume you know enough to make sure debugFD
is visible where you need it, and also how to make sure it's initialized before trying to use it.
If you don't pass a DEBUG_FILE
envval, you get an invalid file descriptor and your debug calls fail - presumably silently.
Upvotes: 1
Reputation: 10947
In the very beginning of your program, open /dev/null
and then assign it to file descriptor 3:
int fd = open ("/dev/null", O_WRONLY);
dup2(fd, 3);
This way, file descriptor 3 won't be taken.
Then, if needed, reuse dup2()
to assign file descriptor 3 to your debugging output.
Upvotes: 2