views:

277

answers:

4

I'm writing an assignment which involves adding some functionality to PostgreSQL on a Solaris box. As part of the assignment, we need to print some information on the client side (i.e.: using elog.)

PostgreSQL already has lots of helper methods which print out the required information, however, the helper methods are packed with hundreds of printf calls, and the elog method only works with c-style strings.

Is there I way that I could temporarily redirect printf calls to a buffer so I could easily send it over elog to the client?

If that's not possible, what would be the simplest way to modify the helper methods to end up with a buffer as output?

+2  A: 

The simplest way is to modify the helper methods to call sprintf(). Whether or not you can hack that in easily, I don't know. Maybe

#define printf(...) sprintf(buffer, __VA_ARGS__)

Will do it for you. You'll still need to define buffer for each helper function, and get its contents returned to whoever cares about them.

Carl Norum
Yeah, there's about a dozen helper methods across two files so there's still quite a bit of work going this route. Also, wouldn't multiple calls then overwrite the previous sprintf? Or does sprintf append?
Ben S
@Ben S, maybe there are already functions that do you what you want? You can append with `sprintf()` since it returns the number of characters printed. If you had a buffer for each function, that probably wouldn't be *too* hard to get working.
Carl Norum
+6  A: 

If you define your own version of printf and link to it prior to the libc version, your version will supersede the standard version.

You should also be able to supersede the standard version by using LD_PRELOAD to load a library that has printf defined.

To write your own printf, you will want to use stdarg functionality:

int printf(const char *fmt, ...)
{
    int rv;
    va_list ap;
    va_start(ap, fmt);

    if (redirect_printf)
    {
#ifdef HAVE_VLOG
        // If you have a vlog function that takes a va_list
        vlog(fmt, ap);
        rv = ...;
#else
        char buffer[LARGESIZE];
        rv = vsnprintf(buffer, sizeof(buffer), fmt, ap);

        log(buffer);
#endif;
    }
    else
    {
        rv = vprintf(fmt, ap);
    }

    return rv;
}

This simple version will truncate data when the final formatted output is greater than LARGESIZE. If you don't want that, you can also call vsnprintf first with a NULL buffer to get the final size, do a dynamic allocation and then a second call to vsprintf to format the buffer.

R Samuel Klatchko
Since this is an assignment and I'm only handing in the requires sources files, I can't control when anything gets linked.
Ben S
@BenS - if you put printf in one of your source file, it should just work.
R Samuel Klatchko
I think @R Samuel is correct, libc gets linked last, right?
Carl Norum
Once I'm done redirecting, I can't really undo this though...
Ben S
@BenS - updated to show you how can can turn redirection on/off via a global variable.
R Samuel Klatchko
+1  A: 

If you can tolerate the use of a temporary file you could redirect standard out with the freopen() call:-

newstdout = freopen("/tmp/log", "w", stdout);

This will force all the printf's to be written to /tmp/log instead of the console output. At some convenient point later in your program you could then open the same file for reading:-

readfd = fopen("/tmp/log", "r");

and forward the contents that have been added using something like this:-

void forward_to_elog(void)
{
   int bytesread;
   char buf[100];

   memset(buf,0,100);
   do {
      memset(buf,0,100);
      bytesread = fread(buf, sizeof(buf)-1, 1, readfd);
      /* call elog(buf) */ ;
   } while(bytesread);

}

If you keep the file open you can call forward_to_elog() multiple times to incrementally forward the contents that have been added.

The tmpnam() function can be used to get a name for the temporary file if you don't want to have to statically code one.

Andrew O'Reilly
+3  A: 
Tometzky