So, after tweaking the attached program and pulling veritable tufts of hair,
I am not one inch closer to understanding how GIOChannel should work, nor
how it is actually working, in my particular case.  Which leads me to
conclude that I suffer from a rather fundamental lack of understanding as to
how GIOChannel should behave for this particular example.

What I want to do: wrap stdout (and eventually stderr as well) with
GIOChannel within my program so that all messages going to stdout are
intercepted (via g_io_add_watch()) for more processing.  I realize there are
many other ways to solve this problem, except that my situation/requirements
disallow them, since I need to:

   1. handle stdout and stderr messages being written to by libraries
   employed by my program that are external, i.e., i can't rewrite them to make
   their messages go elsewhere; and,

   2. these messages need to go to two places, both a file and to a DB; and,

   3. I would ideally like to intercept known error conditions and handle
   them real-time (thus requiring no user intervention).

My reading of GIOChannel functionality renders my usage understanding to be:

   - create a channel using the file descriptor (in this case STDOUT_FILENO)
   - add a watch on the conditions of interest (in this case G_IO_IN)
   - start the main loop
   - read the data on the channel within my callback and process accordingly

Which, I think, would result in:

   - the callback being called with condition G_IO_IN whenever anything is
   written to stdout

Except that I don't experience anything close to this result.

Below is sample code demonstrating what I'm trying to achieve here, but
basically, it:

   - creates a GIOChannel on stdout
   - adds a watch on condition G_IO_IN
   - adds an idle routine to write a message to stdout
   - starts the main loop

As I said, what I would expect from this:

   - callback fired with condition G_IO_IN when idle call writes to stdout

However, when this executes, I get:

   - the test message output to the terminal (completely bypassing the
   callback routine)
   - the callback gets fired only after the <ENTER> key is hit on the
   keyboard, with nothing to be read from the channel.  (huh?...)

Swapping the commented call to g_io_add_watch() (adding condition G_IO_OUT)
also results in not understood results.  When this version executes, the
callback fires immediately (before the idle call).  Further, when taking
this path (G_IO_OUT) and subsequently writing to the channel (still stdout)
using g_io_write_chars(), this output also goes directly to the terminal.

I have scoured the internet for two days to no avail (including google code
search).  There are abundant examples of trapping stdout and stderr from
spawned processes from within a program, but I could find no example doing
what I would need to do here.  I have also tweaked the code below 1.1
million different ways (okay, i exaggerate somewhat, but only a little) and
come no closer to understanding:

   - why it behaves the way it does
   - how my understanding of how it should work is wrong
   - how it needs to really be written to achieve what I need it to do

What am I missing here?  Am I doing something that is "not allowed" or
simply not possible?  If solving my problem is not possible with GIOChannel,
is there another way I'm also yet unaware of?  But surely it is not true
that GIOChannel can trap stdin, stdout, and stderr streams from
spawned/child processes but not its own?

Any and all insight here is greatly appreciated.  My hope is that I'm just
stupid and missing something totally obvious, i.e., that I'll be able to
solve my problem elegantly with GIOChannel.

thanks in advance for any advice,

richard

p.s. OS = Ubuntu 9.10, glib = 2.22.3


==== BEGIN SAMPLE 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>

gboolean my_callback(GIOChannel *source, GIOCondition condition, gpointer
data)
{
  GMainLoop *loop = (GMainLoop *) data;
  gchar *buf=NULL;
  GError *error = NULL;

  switch (condition)
  {
    case G_IO_IN:
      fprintf(stderr, "callback: condition G_IO_IN (%d)\n", condition);
      g_io_channel_read_line(source, &buf, NULL, NULL, &error);
      if (buf)
      {
        FILE *fp = fopen("/tmp/test.out", "w+");
        fprintf(fp, "%s", buf);
        g_free(buf);
        fclose(fp);
      }
      g_main_loop_quit(loop);                    // kill main loop, i.e.,
force pgm exit
      g_io_channel_shutdown(source,TRUE,NULL);   // close channel
    break;
    case G_IO_OUT:
    {
      char *str = g_strdup("string OUT via g_io_channel_write_chars()\n");
      fprintf(stderr, "callback: condition G_IO_OUT (%d)\n", condition);
      g_io_channel_read_line(source, &buf, NULL, NULL, &error);
      if (buf)
      {
        fprintf(stderr, "read on channel returned data (%s)\n", buf);
        g_free(buf);
      }
      else
      {
        fprintf(stderr, "read on channel returned NO data\n");
      }
      g_io_channel_write_chars(source, str, strlen(str), NULL, NULL);
      g_free(str);
    }
    break;
    case G_IO_PRI:
      fprintf(stderr, "callback: condition G_IO_PRI (%d)\n", condition);
    break;
    case G_IO_ERR:
      fprintf(stderr, "callback: condition G_IO_ERR (%d)\n", condition);
    break;
    case G_IO_HUP:
      fprintf(stderr, "callback: condition G_IO_HUP (%d)\n", condition);
    break;
    case G_IO_NVAL:
      fprintf(stderr, "callback: condition G_IO_NVAL (%d)\n", condition);
    break;
    default:
      fprintf(stderr, "callback: unhandled condition (%d)\n", condition);
    break;
  }

  return FALSE;
}

gboolean idle_function(gpointer nil)
{  // write test message to stdout
  fprintf(stdout, "test\n" );
  return FALSE;  // remove
}

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

  g_idle_add((GSourceFunc) idle_function, NULL);

  fd = fileno(stdout);                             // fd = STDOUT_FILENO
  channel = g_io_channel_unix_new(fd);             // wrap stdout with
GIOChannel
  g_io_channel_set_encoding(channel, NULL, NULL);  // accept binary, allow
no buffering
  g_io_channel_set_buffered(channel, FALSE);       // don't buffer
  g_io_add_watch(channel, G_IO_IN,(GIOFunc) my_callback, loop);
//  g_io_add_watch(channel, G_IO_IN | G_IO_OUT,(GIOFunc) my_callback, loop);

  g_main_loop_run(loop);

  return 0;
}

==== END SAMPLE CODE =====
_______________________________________________
gtk-list mailing list
gtk-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-list

Reply via email to