Re: GIOChannel & Intercepting stdout



On Thu, Aug 26, 2010 at 11:01 AM, Hrvoje Niksic <hrvoje niksic avl com> wrote:

The good news is that libraries shouldn't require a terminal for stdout because then they wouldn't work with stdout being redirected to a file (highly unlikely).

The bad news is that this is harder to do correctly than it sounds.  If you connect stdout to a pipe, then a thread (or a subprocess) should exist to collect output from the pipe, or the program deadlocks whenever a library prints a chunk of output larger than the pipe buffer.

The other bad news is that the dup2/pipe solution is very Unix-specific.  It is not clear if this is a problem to Richard.

Hi guys,

Thanks for the feedback, I'm now sorted.

You are correct, the libraries don't expect a terminal, no problem there.  

The actual program I intend to use this in is a background process where the actual processing executes in a separate thread while the main thread sits in the main loop.  With the watch on the read-end of the pipe, the callback is fired and executes within the main loop, while all writing occurs on the execution thread; i.e, backwards from what you suggest, but both processing entities (write to, read from) are separate and so can't collide with or block each other (he says, crossing fingers furiously).

I re-post the modified code, very pared down, using GIOChannel watching that now works as expected/required.

thanks again,

richard

==== BEGIN CODE =====
// save to: io.c
// compile as: gcc io.c `pkg-config --cflags --libs glib-2.0`

#include <glib.h>
#include <stdio.h>
#include <string.h>

enum {READFD, WRITEFD, TTLPIPEFDS};
int fd[TTLPIPEFDS];

gboolean my_callback(GIOChannel *source, GIOCondition condition, gpointer data)
{
  GMainLoop *loop = (GMainLoop *) data;

  switch (condition)
  {
    case G_IO_IN:
    {
      gchar buf2[100];
      memset(buf2, 0, sizeof(buf2));
      read(fd[READFD], buf2, sizeof(buf2));    // read the data from the read-end of the pipe
      if (buf2[0])
      {  // output to text file and to stderr
        FILE *fp = fopen("/tmp/test.out", "w+");
        fprintf(fp, "%s", buf2);
        fclose(fp);
        fprintf(stderr, "written to file: '%s'\n", buf2);
      }
      g_main_loop_quit(loop);
  g_io_channel_shutdown(source,TRUE,NULL);
    }
    break;
  }
  return FALSE;
}

gboolean idle_function(gpointer nil)
{
  printf("printf() test" );
  fflush(stdout);
  return FALSE;  // remove
}

int main()
{
  GMainLoop *loop = g_main_loop_new(NULL,FALSE);
  GIOChannel *channel;

  if (pipe(fd) < 0)                             return(-1);    // make a new pipe
  if (dup2(fd[WRITEFD], fileno(stdout)) < 0)    return(-1);    // copy stdout to write-end
  close(fd[WRITEFD]);
  
  g_idle_add((GSourceFunc) idle_function, NULL);   // write message to stdout

  channel = g_io_channel_unix_new(fd[READFD]);     // watch read-end of pipe
  g_io_add_watch(channel, G_IO_IN,(GIOFunc) my_callback, loop);

  g_main_loop_run(loop);
  
  return 0;
}

==== END CODE =====


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]