2

I have code, written in C++, that needs to daemonize itself. As per usual, it iterates over all open file descriptors, closing them all. There a few exceptions, one of which is the file descriptor used for debug logging.

I am currently in the process of transitioning the code into the Boost::log infrastructure, when I came upon a problem. I have no way to know what the file descriptor for the stream is.

I've already accepted that I cannot straight out get the file descriptor out of the stream. I have some really ugly workarounds, and I can always re-implement the output stream, but these all seem so much work for very little gain.

Are these really my options? Is there something I might be missing?

Thanks, Shachar

Community
  • 1
  • 1
Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57
  • Do you really have to close all open file descriptors? It strikes me odd as many daemons operate on files.. Why not only close the std file descriptors? they have standardized numbers (0, 1, 2, or, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, respectively). – ypnos Mar 15 '14 at 18:13
  • It is advisable. For example, many shells keep an extra fd pointing at the tty, which means I'd be holding the tty open for no reason. – Shachar Shemesh Mar 15 '14 at 18:39
  • Don't know if you can leverage this, but there's also `O_CLOEXEC` that is able to automatically close fds in some situations. – sehe Mar 15 '14 at 22:42
  • Why don't you just deamonize yourself, and *then* open the streams you need? – cmaster - reinstate monica Mar 21 '14 at 21:46
  • Because I need to log stuff before daemonization. Because if the log file given on command line is unopenable, I need to be able to report it before closing stderr. Because it is a legitimate request that shouldn't be too hard to perform. – Shachar Shemesh Mar 22 '14 at 03:56

1 Answers1

1

I said "it all seems like much work for little gain".

Once again, boost to the rescue. Re-implementing a streambuf turned out to be almost a one liner:

static ios::file_descriptor_sink log_file;
static ios::stream_buffer<decltype(log_file)> log_streambuf;

void init_log()
{
    int fd = open(file_name, O_WRONLY|O_CREAT|O_APPEND, 0666);
    log_file = std::move( ios::file_descriptor_sink(fd, ios::close_handle) );

    // Allocate a log sink
    typedef logging::sinks::synchronous_sink< logging::sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    // Add a stream to write log to
    log_streambuf.open(log_file);
    sink->locked_backend()->add_stream( boost::make_shared<std::ostream>( &log_streambuf ) );
    sink->locked_backend()->auto_flush(flush);

    // Register the sink in the logging core
    logging::core::get()->add_sink(sink);
}

The main parts of it:

  • Use boost::iostreams::file_descriptor_sink to keep track of the file descriptor
  • Use boost::iostreams::stream_buffer to turn this into a stream buffer
  • Use the stream_buffer constructor of std::ostream to create the ostream

So long as you keep the origin file_descriptor instance, you will know what is the code file descriptor of the log.

Shachar

Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57