Logo Search packages:      
Sourcecode: dcgui version File versions  Download package

proc_serv.c

/* dc_gui2 - a GTK+2 GUI for DCTC
 * Copyright (C) 2002 Eric Prevoteau
 *
 * proc_serv.c: Copyright (C) Eric Prevoteau <www@a2pb.gotdns.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
/*
$Id: proc_serv.c,v 1.8 2004/01/14 15:48:39 ericprev Exp $
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <glib.h>
#include <gtk/gtk.h>

#include "proc_serv.h"
#include "proc_serv_cmd.h"
#include "mem_access.h"
#include "misc_gtk.h"
#include "main.h"
#include "do_connect.h"
#include "dctc_process.h"

static GPtrArray *proc_serv=NULL;

/*****************************************************************/
/* delete a PSE from the registered service array and discard it */
/*****************************************************************/
static void unregister_entry(PROC_SERV_ENTRY *pse)
{
      /* unregister the entry */
      g_ptr_array_remove_fast(proc_serv,pse);

      /* and delete it */
      g_free(pse->service_name);
      g_byte_array_free(pse->incoming_data,TRUE);
      g_byte_array_free(pse->outgoing_data,TRUE);
      
      /* remove the watch */
      g_source_remove(pse->g_io_watch_id);
      if(pse->g_io_watch_id_w)
            g_source_remove(pse->g_io_watch_id_w);

      g_io_channel_unref(pse->channel);
      free(pse);
}

static inline void dump_packet(guint8 *data, int len)
{
#if 0
      printf("incoming packet:\n");
   while(len-->0)
   {
      printf("%02x ",(unsigned int)*data++);
   }
      printf("\n");
#endif
}

/******************************************************/
/* check if the given buffer contains a whole message */
/******************************************************/
static gboolean message_fully_received(GByteArray *input_array)
{
      gint16 packet_size;

      dump_packet(input_array->data,input_array->len);
      if(input_array->len<2)
            return FALSE;

      packet_size=GET_UAA_GUINT16(input_array->data);
      //printf("want: %d have: %d output: %d\n",packet_size,input_array->len,packet_size>input_array->len);
      if(packet_size>input_array->len)
            return FALSE;
      return TRUE;
}

/*******************************************/
/* write output buffer into the GIOChannel */
/*******************************************/
static gboolean post_reply(GIOChannel *dest, GIOCondition condition, gpointer data)
{
      PROC_SERV_ENTRY *pse=data;

      if(condition&(G_IO_HUP|G_IO_ERR))
      {
            fprintf(stderr,"Connection lost with '%s'\n",pse->service_name);
            unregister_entry(data);
      }
      else
      {
            GError *ge=NULL;
            gsize written;

            switch(g_io_channel_write_chars(pse->channel, pse->outgoing_data->data, pse->outgoing_data->len, &written, &ge))
            {
                  case G_IO_STATUS_NORMAL:
                  case G_IO_STATUS_AGAIN:
                                    if(written)
                                    {
                                          /* discard written data */
                                          if(pse->outgoing_data->len==written)
                                          {
                                                g_byte_array_set_size(pse->outgoing_data,0);
                                                /* no remaining data => unregister the write handler */
                                                g_source_remove(pse->g_io_watch_id_w);
                                                pse->g_io_watch_id_w=0;
                                          }
                                          else
                                          {
                                                memcpy(pse->outgoing_data->data, pse->outgoing_data->data+written, pse->outgoing_data->len-written);
                                                g_byte_array_set_size(pse->outgoing_data,pse->outgoing_data->len-written);
                                          }
                                    }
                                    if(ge)
                                    {
                                          fprintf(stderr,"Warning on posting reply for '%s': %s\n",pse->service_name,ge->message);
                                    }
                                    break;

                  default:
                                    fprintf(stderr,"Error on posting reply for '%s': %s\n",pse->service_name,ge->message);
                                    g_error_free(ge);
                                    unregister_entry(data);
                                    break;
            }
      }
      return TRUE;
}

/***************************/
/* build and queue a reply */
/*****************************************************************************/
/* optionnal paramete: nb_param * (1 unsigned int(=B), a pointer on B bytes) */
/*****************************************************************************/
static void send_reply(PROC_SERV_ENTRY *pse, guint16 cmd, guint8 nb_param, ...)
{
      guint packet_size;
      GByteArray *gba;
      va_list ap;
      guint8 i;

      packet_size=2;
      gba=g_byte_array_new();
      g_byte_array_append(gba,(guint8*)&cmd,2);
      g_byte_array_append(gba,&nb_param,1);

      va_start(ap,nb_param);
      for(i=0;i<nb_param;i++)
      {
            guint16 psize;
            guint8 *pptr;

            psize=va_arg(ap,unsigned int);
            pptr=va_arg(ap,void *);

            g_byte_array_append(gba,(guint8*)&psize,2);
            if(psize)
                  g_byte_array_append(gba,pptr,psize);
      }
      va_end(ap);

      packet_size+=gba->len;
      /* add the newly created packet to the array */
      g_byte_array_append(pse->outgoing_data,(guint8*)&packet_size,2);
      g_byte_array_append(pse->outgoing_data,gba->data,gba->len);

      g_byte_array_free(gba,TRUE);

      /* check if a write_handler is enabled */
      if(pse->g_io_watch_id_w==0)
      {
            pse->g_io_watch_id_w=g_io_add_watch(pse->channel,G_IO_OUT|G_IO_ERR|G_IO_HUP,post_reply,pse);
            //printf("adding write handler\n");
      }
}

/******************************************/
/* process PS_GET_TEXT_WIDGET_VALUE query */
/******************************************/
static void ps_get_text_widget_value (PROC_SERV_ENTRY *pse, guint8 nb_param, guint8 *param_data, void *extra_param)
{
      if(nb_param!=1)
      {
            send_reply(pse,PS_INVALID_QUERY,0);
      }
      else
      {
            GString *str;
            guint16 param_size;
            GtkWidget *w;

            str=g_string_new("");
            
            /* extract the widget name */
            param_size=GET_UAA_GUINT16(param_data);
            g_string_append_len(str,param_data+2,param_size);

            w=get_widget_by_widget_name(main_window,str->str);
            if(w==NULL)
            {
                  send_reply(pse,PS_TEXT_WIDGET_VALUE,0);         /* no value on reply because the widget does not exist */
            }
            else
            {
                  gchar *wc;

                  wc=gtk_editable_get_chars(GTK_EDITABLE(w),0,-1);
                  if(wc==NULL)
                        send_reply(pse,PS_TEXT_WIDGET_VALUE,1,0,NULL);        /* invalid value => return an empty string */
                  else
                  {
                        send_reply(pse,PS_TEXT_WIDGET_VALUE,1,strlen(wc),wc);       /* returned value comes from the interface and is UTF8 encoded */
                        g_free(wc);
                  }
            }
            g_string_free(str,TRUE);
      }
}

/*************************************************/
/* process PS_GET_CHECKBUTTON_WIDGET_VALUE query */
/*************************************************/
static void ps_get_checkbutton_widget_value (PROC_SERV_ENTRY *pse, guint8 nb_param, guint8 *param_data, void *extra_param)
{
      if(nb_param!=1)
      {
            send_reply(pse,PS_INVALID_QUERY,0);
      }
      else
      {
            GString *str;
            guint16 param_size;
            GtkWidget *w;

            str=g_string_new("");
            
            /* extract the widget name */
            param_size=GET_UAA_GUINT16(param_data);
            g_string_append_len(str,param_data+2,param_size);

            w=get_widget_by_widget_name(main_window,str->str);
            if(w==NULL)
            {
                  send_reply(pse,PS_TEXT_WIDGET_VALUE,0);         /* no value on reply because the widget does not exist */
            }
            else
            {
                  guint8 val=0;

                  if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))==TRUE)
                        val=1;
                  else
                        val=0;

                  send_reply(pse,PS_CHECKBUTTON_WIDGET_VALUE,1,sizeof(val),&val);         /* returned value as a 1-byte value */
            }
            g_string_free(str,TRUE);
      }
}

/**********************************/
/* process PS_GET_VAR_VALUE query */
/**********************************/
static void ps_get_var_value (PROC_SERV_ENTRY *pse, guint8 nb_param, guint8 *param_data, void *extra_param)
{
      if(nb_param!=1)
      {
            send_reply(pse,PS_INVALID_QUERY,0);
      }
      else
      {
            GString *str;
            guint16 param_size;
            const char *wc;

            str=g_string_new("");
            
            /* extract the widget name */
            param_size=GET_UAA_GUINT16(param_data);
            g_string_append_len(str,param_data+2,param_size);

            wc=get_var(str->str);
            if(wc==NULL)
                  send_reply(pse,PS_VAR_VALUE,1,0,NULL);          /* invalid value => return an empty string */
            else
                  send_reply(pse,PS_VAR_VALUE,1,strlen(wc),wc);         /* returned value comes from the interface and is UTF8 encoded */
            g_string_free(str,TRUE);
      }
}

/**********************************/
/* process PS_GET_VAR_VALUE query */
/**********************************/
static void ps_send_cmd_to_dctc(PROC_SERV_ENTRY *pse, guint8 nb_param, guint8 *param_data, void *extra_param)
{
      void (*send_data)(const char *cmd_dctc)=extra_param;

      if(nb_param==1)
      {
            GString *str;
            guint16 param_size;

            str=g_string_new("");
            
            /* extract the widget name */
            param_size=GET_UAA_GUINT16(param_data);
            g_string_append_len(str,param_data+2,param_size);

            if(str->len)
            {
                  if(str->str[str->len-1]!='\n')
                        g_string_append_c(str,'\n');

                  (*send_data)(str->str);
            }
            g_string_free(str,TRUE);
      }
}

typedef struct
{
      guint16 command_id;
      void (*fnc)(PROC_SERV_ENTRY *pse, guint8 nb_param, guint8 *param_data, void *extra_param);
      void *extra_param;
} QUERY_STRUCT;

static QUERY_STRUCT cmd_list[]={
                                                                  {PS_GET_TEXT_WIDGET_VALUE, ps_get_text_widget_value,NULL},
                                                                  {PS_GET_VAR_VALUE, ps_get_var_value,NULL},
                                                                  {PS_GET_CHECKBUTTON_WIDGET_VALUE, ps_get_checkbutton_widget_value,NULL},
                                                                  {PS_SEND_CMD_TO_DCTC, ps_send_cmd_to_dctc, send_data_to_dctc},
                                                                  {PS_SEND_CMD_TO_GDL_DCTC, ps_send_cmd_to_dctc, send_data_to_gdl_dctc},
                                                                  {0,NULL}
                                                             };

/********************************************************/
/* extract a query from the input buffer and process it */
/********************************************************/
static void process_query(PROC_SERV_ENTRY *pse)
{
      guint16 packet_size;
      guint16 proc_command;
      guint8 nb_param;
      int i;
      gboolean fnd=FALSE;
      
      //printf("process_query\n");
      /* decode the packet */
      packet_size=GET_UAA_GUINT16(pse->incoming_data->data);
      proc_command=GET_UAA_GUINT16(pse->incoming_data->data+2);
      nb_param=*(pse->incoming_data->data+4);

      for(i=0;cmd_list[i].fnc!=NULL;i++)
      {
            if(cmd_list[i].command_id==proc_command)
            {
                  (cmd_list[i].fnc)(pse,nb_param,pse->incoming_data->data+5,cmd_list[i].extra_param);
                  fnd=TRUE;
                  break;
            }
      }

      if(!fnd)
      {
            send_reply(pse,PS_INVALID_QUERY,0);
      }

      /* at end, remove the processed query from the buffer */
      if(packet_size==pse->incoming_data->len)
            g_byte_array_set_size(pse->incoming_data,0);          /* only the packet is in the buffer */
      else
      {
            memcpy(pse->incoming_data->data, pse->incoming_data->data+packet_size, pse->incoming_data->len-packet_size);
            g_byte_array_set_size(pse->incoming_data,pse->incoming_data->len-packet_size);
      }
}


/**************************/
/* process incoming query */
/************************************************************/
/* condition can be G_IO_IN or G_IO_HUP (connection closed) */
/* data is a pointer on the PROC_SERV_ENTRY of this source  */
/************************************************************/
static gboolean proc_serv_process_query(GIOChannel *source, GIOCondition condition, gpointer data)
{
      PROC_SERV_ENTRY *pse=data;

      if(condition&G_IO_HUP)
      {
            fprintf(stderr,"Connection lost with '%s'\n",pse->service_name);
            unregister_entry(data);
      }
      else
      {
            GError *ge=NULL;
            char buf[8192];
            gsize in_buf;

            GIOStatus gios;

            gios=g_io_channel_read_chars(pse->channel,buf,sizeof(buf),&in_buf,&ge);
            //printf("gios:%d ge:%p\n",gios,ge);
            if(gios!=G_IO_STATUS_NORMAL)
            {
                  fprintf(stderr,"Error on serving query for '%s': %s\n",pse->service_name,ge->message);
                  g_error_free(ge);
                  unregister_entry(data);
            }
            else
            {
                  if(in_buf)
                  {
                        g_byte_array_append(pse->incoming_data,buf,in_buf);
                        while(message_fully_received(pse->incoming_data))
                        {
                              process_query(pse);
                        }
                  }
                  else
                  {
                        fprintf(stderr,"0 byte read on serving query for '%s'\n",pse->service_name);
                  }
            }
      }
      return TRUE;
}

/****************************************/
/* register a new entry with its socket */
/****************************************/
PROC_SERV_ENTRY *register_entry(int sock_fd, const char *service_name)
{
      PROC_SERV_ENTRY *pse;
      GError *ge=NULL;

      pse=malloc(sizeof(PROC_SERV_ENTRY));
      if(pse==NULL)
      {
            close(sock_fd);
            return NULL;
      }

      pse->channel=g_io_channel_unix_new(sock_fd);
      g_io_channel_set_close_on_unref(pse->channel,TRUE);   /* last unref close everything */

      if(g_io_channel_set_encoding(pse->channel,NULL,&ge)==G_IO_STATUS_ERROR) /* raw encoding */
      {
            fprintf(stderr, "register_entry (%s): set_encoding: %s\n", service_name,ge->message);
            g_error_free(ge);
            ge=NULL;
            g_io_channel_unref(pse->channel);
            free(pse);
            return NULL;
      }

      if(g_io_channel_set_flags(pse->channel, G_IO_FLAG_NONBLOCK, &ge)==G_IO_STATUS_ERROR)      /* non blocking mode */
      {
            fprintf(stderr, "register_entry (%s): set_flags: %s\n", service_name,ge->message);
            g_error_free(ge);
            ge=NULL;
            g_io_channel_unref(pse->channel);
            free(pse);
            return NULL;
      }

      g_io_channel_set_buffered(pse->channel,FALSE);        /* unbuffered mode */

      pse->service_name=g_strdup(service_name);
      pse->incoming_data=g_byte_array_new();
      pse->outgoing_data=g_byte_array_new();

      /* add a read watch by default */
      pse->g_io_watch_id=g_io_add_watch(pse->channel,G_IO_IN|G_IO_HUP,proc_serv_process_query,pse);
      pse->g_io_watch_id_w=0;       /* no write handler */

      if(proc_serv==NULL)
            proc_serv=g_ptr_array_new();
      g_ptr_array_add(proc_serv,pse);

      return pse;
}


Generated by  Doxygen 1.6.0   Back to index