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

gts.c

/* DCTC - a Direct Connect text clone for Linux
 * Copyright (C) 2001 Eric Prevoteau
 *
 * gts.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: gts.c,v 1.2 2003/12/28 08:12:38 uid68112 Exp $
*/

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

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <string.h>
#include <glib.h>

#include "var.h"
#include "gts.h"
#include "display.h"
#include "action.h"
#include "user_manage.h"
#include "misc.h"
#include "uaddr.h"

/**************************/
/* Global Transfer System */
/*********************************************************************/
/* the following functions provide the capability to move a transfer */
/* from one client to another. Only queued transfers can be moved.   */
/* thus, a transfer can go from one hub to another if the user from  */
/* where we download has gone to another hub. If the .dctc dir is on */
/* a shared disc (NFS or something like that), a transfer can even   */
/* migrate from one computer to another.                             */
/* only /DL are taken into account.                                  */
/*********************************************************************/
#ifdef HAVE_MMAP

static int gts_fd=-1;
G_LOCK_DEFINE_STATIC(gts_fd);             /* must be locked before any GTS operation */

static GString *gts_filename=NULL;

static GTS_ENTRY *mapped_gts=NULL;
static int mapped_size;                         /* size of GTS file in memory (in bytes) */
static int nb_mapped_entry;               /* size of GTS file in memory (in GTS_ENTRY) */

static unsigned long next_id;


/******************************/
/* perform GTS initialisation */
/********************************************************************/
/* this function is the only one which doesn't need to lock the GTS */
/********************************************************************/
void init_gts(void)
{
      next_id=getpid()<<16;                     /* uniq ID */

      /* create gts filename */
      gts_filename=g_string_new(dctc_dir->str);
      g_string_sprintfa(gts_filename,"/gts.%d",sizeof(GTS_ENTRY));

      gts_fd=open(gts_filename->str,O_CREAT|O_RDWR,0666);
      if(gts_fd==-1)
      {
            perror("init_gts - open fails");
            exit(0);
      }

      if(flock(gts_fd,LOCK_EX|LOCK_NB)!=-1)
      {
            /* the file is locked. 2 cases: */
            /* the file is empty and we must create its header */
            /* the file is not empty and is already ready to be used */
            struct stat st;
            
            if(fstat(gts_fd,&st)==-1)
            {
                  perror("init_gts - fstat fails");
                  exit(0);
            }

            if(st.st_size==0)
            {
                  /* we have created the file, we must build its header */
                  GTS_ENTRY dummy;

                  dummy.slot_status=0;
                  dummy.id=0;
                  memset(dummy.nick,'=',GTS_NICK_SIZE);
                  memset(dummy.cmd,'+',GTS_CMD);
                  dummy.next_try=0;
                  
                  while(write(gts_fd,&dummy,sizeof(dummy))!=sizeof(dummy))
                  {
                        disp_msg(ERR_MSG,"init_gts","disk full, unable to initialize GTS file. Free some space",NULL);
                        sleep(1);
                        lseek(gts_fd,0,SEEK_SET);           /* restart from the beginning */
                  }
            }
            else
            {
                  /* the file is already ready to be used */
            }

            flock(gts_fd,LOCK_UN);        /* unlock the file */
      }
      else
      {
            /* unable to lock the file. Either someone already using it or initializing it */
            /* in both case, the file is ready */
      }
}

/***********************/
/* unlock the GTS file */
/***********************/
static inline void unlock_gts(void)
{
      flock(gts_fd,LOCK_UN);
      G_UNLOCK(gts_fd);
}

/****************************************/
/* lock GTS file and map it into memory */
/****************************************/
/* output: 1=error, 0=ok */
/*************************/
static int lock_and_map_gts(void)
{
      struct stat st;

      if(gts_fd==-1)
            return 1;

      G_LOCK(gts_fd);
      flock(gts_fd,LOCK_EX);

      if(fstat(gts_fd,&st)==-1)
      {
            unlock_gts();
            return 1;
      }

      mapped_size=st.st_size;
      mapped_gts=mmap(NULL,mapped_size,PROT_READ|PROT_WRITE,MAP_SHARED, gts_fd, 0);
      if(mapped_gts==MAP_FAILED)
      {
            unlock_gts();
            return 1;
      }

      nb_mapped_entry=mapped_size/sizeof(GTS_ENTRY);

      return 0;
}

/**********************************/
/* unmap the GTS file from memory */
/**********************************/
static inline void unmap_gts(void)
{
      munmap(mapped_gts,mapped_size);
      mapped_gts=NULL;
}

/********************************************************************/
/* the following function removes an entry to the GTS file using ID */
/********************************************************************/
void delete_gts_entry_by_id(unsigned long id)
{
      int i;
      if(lock_and_map_gts())
            return;

      for(i=1;i<nb_mapped_entry;i++)
      {
            if(mapped_gts[i].slot_status==0)          /* empty slot */
                  continue;

            if(mapped_gts[i].id==id)
            {
                  mapped_gts[i].slot_status=0;        /* the slot is now empty */
                  break;
            }
      }

      unmap_gts();            /* unmap file from memory */
      unlock_gts();
}

/***********************************************************/
/* the following function removes an entry to the GTS file */
/***********************************************************/
void delete_gts_entry(const char *nickname, const char *cmd)
{
      int i;

      if(lock_and_map_gts())
            return;

      for(i=1;i<nb_mapped_entry;i++)
      {
            if(mapped_gts[i].slot_status==0)          /* empty slot */
                  continue;

            if((!strcmp(nickname,mapped_gts[i].nick)) &&
                  (!strcmp(cmd,mapped_gts[i].cmd)) )
            {
                  mapped_gts[i].slot_status=0;        /* the slot is now empty */
                  break;
            }
      }

      unmap_gts();            /* unmap file from memory */
      unlock_gts();
}

/********************************************************/
/* the following function adds an entry to the GTS file */
/**********************************************************************************************************************/
/* the following cases can appear:                                                                                    */
/* no entry having the same (nick/filename/dlpath) exists and it is created.                                          */
/* an entry having the same (nick/filename/dlpath) exists, nothing is done (we don't add anything else).              */
/**********************************************************************************************************************/
/* output: 0= ok    */
/*         1= error */
/********************/
int add_gts_entry(const char *nickname, const char *cmd, int delay)
{
      int i;
      int fnd=0;
      int ret;
      int empty_entry=-1;

      if(lock_and_map_gts())
            return 1;         /* error */

      /* GTS file is in memory */
      /* we just have to scan it from the 2nd entry (the first is the header) */
      for(i=1;i<nb_mapped_entry;i++)
      {
            if(mapped_gts[i].slot_status==0)          /* empty slot */
            {
                  if(empty_entry==-1)
                        empty_entry=i;
                  continue;
            }

            if((!strcmp(nickname,mapped_gts[i].nick)) &&
                  (!strcmp(cmd,mapped_gts[i].cmd)) )
            {
                  fnd=i;
                  break;
            }
      }

      if(fnd)
      {
            /* still here, nothing to do */
            unmap_gts();
            ret=0;            /* ok */
      }
      else
      {
            /* no entry exists, we must create one */
            GTS_ENTRY nw;

            /* the entry to add */
            nw.slot_status=1;                                                             /* slot is busy */
            nw.id=next_id;                                                    /* this id is used by /KILLKBN */
            next_id++;
            strncpy_max(nw.nick,nickname,GTS_NICK_SIZE);
            strncpy_max(nw.cmd,cmd,GTS_CMD);
            nw.next_try=time(NULL)+delay;
            
            if(empty_entry==-1)
            {
                  /* no empty entry inside GTS file, must increase file size */

                  unmap_gts();            /* unmap file from memory */
      
                  if(lseek(gts_fd,nb_mapped_entry*sizeof(GTS_ENTRY),SEEK_SET)==(nb_mapped_entry*sizeof(GTS_ENTRY)))
                  {
                        /* after moving to the right place */
                        /* write the entry */
                        if(write(gts_fd,&nw,sizeof(GTS_ENTRY))!=sizeof(GTS_ENTRY))
                              ret=1;            /* error */
                        else
                              ret=0;            /* ok */
                  }
                  else
                  {
                        ret=1;      /* error */
                  }
            }
            else
            {
                  /* an empty entry exists, use it */
                  memcpy(&mapped_gts[empty_entry],&nw,sizeof(GTS_ENTRY));
                  unmap_gts();            /* unmap file from memory */
                  ret=0;            /* ok */
            }
      }

      unlock_gts();
      return ret;
}

/***************************************************************************/
/* this function scans the GTS files and looks if there is something to do */
/*******************************************************************************/
/* this function must be called regularly when the connection to the hub works */
/*******************************************************************************/
/* input: user_list containing the list of all users of the hub */
/****************************************************************/
void gts_action(GPtrArray *user_list)
{
      int i;
      time_t cur;
      static time_t last_time=0;

      if((user_list==NULL)||(user_list->len==0))
            return;

      /* limit the number of call to reduce CPU work */
      cur=time(NULL);
      if((cur-last_time)<DELAY_BETWEEN_GTS)
            return;

      last_time=cur;

      if(lock_and_map_gts())
            return;           /* error */


      /* GTS file is in memory */
      /* we just have to scan it from the 2nd entry (the first is the header) */
      for(i=1;i<nb_mapped_entry;i++)
      {
            if((mapped_gts[i].slot_status!=0) &&                                    /* slot is not empty */
                  (mapped_gts[i].next_try<=cur) &&                                        /* time reached */
                  (   (user_in_list(user_list, mapped_gts[i].nick))     /* user is in here */
                        || ( (with_ddl==1)&&(check_uaddr_entry_by_name(mapped_gts[i].nick)) )         /* or DDL enabled and user ip is known */
               )
              )
            {     /* if the entry is not locked and it is the same user */
                  /* ... start download using sim */
                  add_new_sim_input(0,mapped_gts[i].cmd);
                  /* ... and lock the entry */
                  mapped_gts[i].slot_status=0;        /* slot is free */
            }
      }

      unmap_gts();
      unlock_gts();
}

/*******************************************************************************/
/* this function acts in the same way that gts_action except the chosen action */
/* belongs to the given nick and is a download (/DL or /LS)                    */
/*******************************************************************************/
/* output: NULL=nothing found else it is the commandline of the action to do */
/*****************************************************************************/
GString *gts_retrieve_download(char *nick)
{
      GString *str=NULL;
      int i;

      if(lock_and_map_gts())
            return str;       /* error */

      /* GTS file is in memory */
      /* search for the wanted nick and a download */
      for(i=1;i<nb_mapped_entry;i++)
      {
            if((mapped_gts[i].slot_status!=0) &&                  /* slot is not empty */
                  (!strcmp(nick, mapped_gts[i].nick)) )           /* user is in here */
            {     /* if the entry is not locked and it is the same user */
                  /* check if it is a download */
                  if( (!strncmp(mapped_gts[i].cmd,"/DL ",4)) ||
                         ( ((keyb_fd!=-1)||(local_client_socket->len!=0)) 
                                    &&(!strncmp(mapped_gts[i].cmd,"/LS ",4))) /* /LS is only started by dctc having a display (console or ui) */
                    )
                  {
                        str=g_string_new(mapped_gts[i].cmd);
                        if(str!=NULL)
                        {
                              mapped_gts[i].slot_status=0;        /* slot is free */
                              break;
                        }
                  }
            }
      }

      unmap_gts();
      unlock_gts();

      return str;
}

/***********/
/* end GTS */
/***********/
void exit_gts(void)
{
      if(gts_filename!=NULL)
      {
            g_string_free(gts_filename,TRUE);
            gts_filename=NULL;
      }

      if(gts_fd!=-1)
      {
            close(gts_fd);
            gts_fd=-1;
      }
}

/*****************************************/
/* output GTS content into CMD_KB format */
/*****************************************/
void list_gts_content(void)
{
      int i;
      char tmp[512];

      if(lock_and_map_gts())
            return;           /* error */
      
      for(i=1;i<nb_mapped_entry;i++)
      {
            if(mapped_gts[i].slot_status==0)          /* empty slot */
                  continue;

            sprintf(tmp,"%lu|%lu",mapped_gts[i].id,mapped_gts[i].next_try);
            disp_msg(CMD_KB,NULL,tmp,mapped_gts[i].cmd,NULL);
      }

      unmap_gts();
      unlock_gts();
}

#else
/* ============================================================================ */
/* ============================================================================ */
/* ============================================================================ */
/* ============================================================================ */
/* ============================================================================ */
/* ============================================================================ */
/* ============================================================================ */
/* ============================================================================ */
/* ============================================================================ */
/* for OS without mmap, dummy functions are provided */


/******************************/
/* perform GTS initialisation */
/******************************/
void init_gts(void)
{
}

/***********/
/* end GTS */
/***********/
void exit_gts(void)
{
}


/********************************************************/
/* the following function adds an entry to the GTS file */
/**********************************************************************************************************************/
/* the following cases can appear:                                                                                    */
/* no entry having the same (nick/filename/dlpath) exists and it is created.                                          */
/* an entry having the same (nick/filename/dlpath) exists, nothing is done (we don't add anything else).              */
/**********************************************************************************************************************/
/* output: 0= ok    */
/*         1= error */
/********************/
int add_gts_entry(const char *nickname, const char *cmd, int delay)
{
      return 1;
}


/***********************************************************/
/* the following function removes an entry to the GTS file */
/***********************************************************/
void delete_gts_entry(const char *nickname, const char *cmd)
{
}

/********************************************************************/
/* the following function removes an entry to the GTS file using ID */
/********************************************************************/
void delete_gts_entry_by_id(unsigned long id)
{
}

/***************************************************************************/
/* this function scans the GTS files and looks if there is something to do */
/*******************************************************************************/
/* this function must be called regularly when the connection to the hub works */
/*******************************************************************************/
/* input: user_list containing the list of all users of the hub */
/****************************************************************/
void gts_action(GPtrArray *user_list)
{
}

/*******************************************************************************/
/* this function acts in the same way that gts_action except the chosen action */
/* belongs to the given nick and is a download (/DL or /LS)                    */
/*******************************************************************************/
/* output: NULL=nothing found else it is the commandline of the action to do */
/*****************************************************************************/
GString *gts_retrieve_download(char *nick)
{
      return NULL;
}

/*****************************************/
/* output GTS content into CMD_KB format */
/*****************************************/
void list_gts_content(void)
{
}

#endif


Generated by  Doxygen 1.6.0   Back to index