next up previous contents index
Next: Examples Up: Program Previous: Server: pserver.c   Contents   Index

Client: pclient.h


/******************************************************************************/
/* pclient.h - client process(es) for PeStO file server                       */
/*                                                                            */
/* Written by: Michael G. S\o rensen, September-November 1996, DIKU             */
/******************************************************************************/

/* BEGIN include **************************************************************/
#include "pesto.h"    /* include file for PeStO client and PeStO server */
#include "ppclient.h" /* include file PeStO client */
/* END include ****************************************************************/

/* BEGIN library subroutines **************************************************/
FILE *p_open(name,mode,tb)
  char *name; /* pathname */
  char *mode; /* filemode */
  int tb;     /* time bound (in minutes) */
{
  int sd;                     /* socket descriptor */
  pmessage buf;               /* message buffer */
  long mt,ct,cct,ret,wet,crt;
  long ctb,mtb,t,rlet,wlet;
  int fd;
  int cached,cf,commstat,rewrite,within;

  pso_errno=PSO_ENOERROR;

  if(strchr(mode,'w')!=NULL||strchr(mode,'a')!=NULL||
    (strchr(mode,'r')!=NULL&&strchr(mode,'+')!=NULL)) {

    /* BEGIN open for WRITING *************************************************/

    /* ... */

    /* END open for WRITING ****************************************************/
  }
  else {
    if(strchr(mode,'r')!=NULL) {

    /* BEGIN open for READING **************************************************/

      if((cached=read_info(name,&mt,&ct,&cct,&ret,&wet,&crt,&fd))==NOT_OK)
        return NULL;

      time(&t);
      ctb=60*(long)tb;  /* consistency time bound (in seconds) */

      if(!cached) {

        /* BEGIN open for READING and NOT CACHED *******************************/

        /* ... */

        /* END open for READING and NOT CACHED *********************************/
      }
      else {

       /* BEGIN open for READING and CACHED ************************************/

        cf=(ct==ctb);

        if(ctb<0) {

          /* BEGIN open for READING and CACHED and OPTIMISTIC ******************/

          if(wet>=t||ret>=t) /* file is read- or writelocked */
            return pfopen(name,mode,fd,0L,0L);

          if((commstat=ptaco())==NOT_OK) {
            unlock_file(fd);
            return NULL;
          }

          within=(t+ctb<=ct||(wet>0&&t+ctb<=wet)||(ret>0&&t+ctb<=ret));

          if(commstat!=CONNECTED) /* DISCONNECTED or WEAKLY CONNECTED */ {
            if(within)
              /*
                 At this point we know that the cached file is within the
                 specified (consistency) time bound.

                 We do not care whether we know the cached file is consistent
                 (cf) or not (!cf).
              */
              return pfopen(name,mode,fd,0L,0L);
            else {
              pso_errno=PSO_ENOTWITHIN;
              unlock_file(fd);
              return NULL;
            }
          }

          /* send request to server and receive reply */

          pbuf(&buf,READ_OPEN,0L,0L,ct,cct,mt,name);
          if(prequest(&sa,&sd,&buf)==NOT_OK||buf.status==NOT_OK) {
            /*
               We could not request the file from the server, so we use the
               cached version (if its within the specified time bound).
            */
            if(within) {
              close_sock(sd);
              return pfopen(name,mode,fd,0L,0L);
            }
            else {
              pso_errno=PSO_ENOTWITHIN;
              unlock_file(fd);
              close_sock(sd);
              return NULL;
            }
          }

          if(buf.request==ISLOCKED)
            /*
               Note, that even if the file is writelocked on the server we will
               read it - being optimistic.
            */
            if(mt==buf.mt)
              buf.request=CONSISTENT;
            else
              buf.request=INCONSISTENT;

          switch(buf.request) {
            case NOTFOUND:
              if(within) {
                pso_errno=PSO_ENOTWITHIN;
                break;
              }
              else {
                close_sock(sd);
                return pfopen(name,mode,fd,0L,0L);
              }

            case INCONSISTENT:
              pbuf(&buf,SEND_FILE,0L,0L,buf.ct,buf.cct,buf.mt,name);
              if(preceive(&sa,&sd,&buf)==NOT_OK) {
                if(within) {
                  close_sock(sd);
                  return pfopen(name,mode,fd,0L,0L);
                }
                else {
                  pso_errno=PSO_ENOTWITHIN;
                  break;
                }
              }
              else
                crt=buf.crt;

            case CONSISTENT:
              if(update_info(fd,buf.mt,buf.ct,buf.cct,ret,wet,crt)==NOT_OK)
                break;
              else {
                close_sock(sd);
                return pfopen(name,mode,fd,0L,0L);
              }

            case NOTLOCKED:
            case WASLOCKED:
              /*
                 Note, that the file should not have been attempted locked or
                 locked,  because we did not ask for a lock (buf.ret=0L)!
              */
              pso_errno=PSO_ESERVERERROR;
              break;

            default:
              pso_errno=PSO_EUNKNOWNREPLY;
          }

          unlock_file(fd);
          close_sock(sd);
          return NULL;

          /* END open for READING and CACHED and OPTIMISTIC ********************/
        }
        else
          if(ctb==0) {

            /* BEGIN open for READING and CACHED and STRICT ********************/

            /* ... */

            /* END open for READING and CACHED and STRICT **********************/
          }
          else {

            /* BEGIN open for READING and CACHED and PESSIMISTIC ***************/

            /* ... */

            /* END open for READING and CACHED and PESSIMISTIC *****************/
          }
        /* END open for READING and CACHED *************************************/
      }
      /* END open for READING **************************************************/
    }
    else {
      pso_errno=PSO_EBADMODE;
      return NULL;
    }
  }
}

int p_close(fp,etb)
  FILE *fp;
  int etb;  /* expiration time bound (in minutes) */
{
  char name[PATHNAME_MAX];
  int cached,commstat,request,status,updated;
  long t,et,mt,ct,cct,ret,wet,crt,rlet,wlet;
  int fd,sd;
  struct stat stbuf;
  pmessage buf;

  pso_errno=PSO_ENOERROR;

  time(&t);
  if(etb<0)
    etb=0;
  if(etb>0)
    et=t+60*(long)etb; /* expiration time */

#ifdef SYSV5
  if((fp->_flag&01)==0)
    request=WRITE_CLOSE;
  else
    request=READ_CLOSE;
#else
  if((fp->_flags&04)==0)
    request=READ_CLOSE;
  else
    request=WRITE_CLOSE;
#endif

  if(pgetname(fp,name,&rlet,&wlet)==NOT_OK) {
    pso_errno=PSO_ENOTFOUND;
    fclose(fp);
    return NOT_OK;
  }

  if((cached=read_info(name,&mt,&ct,&cct,&ret,&wet,&crt,&fd))==NOT_OK) {
    pdelname(fp);
    fclose(fp);
    return NOT_OK;
  }

  if(!cached) {
    pso_errno=PSO_ENOTCACHED;
    delete_info(name);
    pdelname(fp);
    fclose(fp);
    unlock_file(fd);
    return NOT_OK;
  }

  if(ct!=cct) {
    pso_errno=PSO_ENOTCONSISTENT;
    pdelname(fp);
    fclose(fp);
    unlock_file(fd);
    return NOT_OK;
  }

  if(stat(name,&stbuf)==-1) {
    if(errno==ENOENT)
      pso_errno=PSO_ENOTFOUND;
    else
      pso_errno=PSO_EERROR;
    pdelname(fp);
    fclose(fp);
    unlock_file(fd);
    return NOT_OK;
  }

  if(stbuf.st_mtime>=crt&&request==WRITE_CLOSE)
    updated=UPDATED;
  else
    updated=NOT_UPDATED;

  if((commstat=ptaco())==NOT_OK) {
    unlock_file(fd);
    return NOT_OK;
  }

  if(commstat==DISCONNECTED) {
    if(etb==0) {
      if(pdelname(fp)==NOT_OK) {
        fclose(fp);
        unlock_file(fd);
        return NOT_OK;
      }
      if(fclose(fp)==EOF) {
        pso_errno=PSO_ENOTCLOSED;
        unlock_file(fd);
        return NOT_OK;
      }
      if(unlock_file(fd)==NOT_OK)
        return NOT_OK;
    }
    else {
      /* wait for better communication status or timeout on et */
      while((commstat=ptaco())!=DISCONNECTED&&et<t) {
        sleep(SLEEPTIME);
        time(&t);
      }
    }
  }

  if(commstat==DISCONNECTED) {
    if(updated) {
      switch(fork()) {
        case -1:
          pso_errno=PSO_EERROR;
          return NOT_OK;
        case 0: /* child process - wait for better communication */
          fclose(stdin);
          fclose(stderr);
#ifdef SYSV5
          signal(SIGCLD,SIG_IGN);
#else
#ifndef LINUX
          signal(SIGCLD,sig_child);
#else
          signal(SIGCLD,SIG_IGN);
#endif
#endif
          while((commstat=ptaco())!=DISCONNECTED)
            sleep(SLEEPTIME);
          pbuf(&buf,request,rlet,wlet,ct,cct,mt,name);
          pclosesend(&sa,&sd,&buf,fd,updated,crt);
          exit(0);
        default: /* parent */
          return TIMEOUT;
      }
    }
    else
      return TIMEOUT;
  }

tryagain:

  /* send request to server and receive reply */

  pbuf(&buf,request,rlet,wlet,ct,cct,mt,name);
  if(prequest(&sa,&sd,&buf)==NOT_OK||buf.status==NOT_OK) {
    if(etb==0) {
      if(pdelname(fp)==NOT_OK) {
        fclose(fp);
        unlock_file(fd);
        close_sock(sd);
        return NOT_OK;
      }
      if(fclose(fp)==EOF) {
        pso_errno=PSO_ENOTCLOSED;
        unlock_file(fd);
        close_sock(sd);
        return NOT_OK;
      }
      if(unlock_file(fd)==NOT_OK) {
        close_sock(sd);
        return NOT_OK;
      }
      commstat=DISCONNECTED;
    }
    else {
      while((commstat=ptaco())!=DISCONNECTED&&et<t) {
        sleep(SLEEPTIME);
        time(&t);
      }

      if(commstat!=DISCONNECTED&&et>t) {
        close_sock(sd);
        goto tryagain;
      }
    }
  }

  if(commstat==DISCONNECTED) {
    if(updated) {
      switch(fork()) {
        case -1:
          pso_errno=PSO_EERROR;
          return NOT_OK;
        case 0: /* child process - wait for better communication */
          fclose(stdin);
          fclose(stderr);
#ifdef SYSV5
          signal(SIGCLD,SIG_IGN);
#else
#ifndef LINUX
          signal(SIGCLD,sig_child);
#else
          signal(SIGCLD,SIG_IGN);
#endif
#endif
          while((commstat=ptaco())!=DISCONNECTED)
            sleep(SLEEPTIME);
          pbuf(&buf,request,rlet,wlet,ct,cct,mt,name);
          pclosesend(&sa,&sd,&buf,fd,updated,crt);
          exit(0);
        default: /* parent */
          close_sock(sd);
          return TIMEOUT;
      }
    }
    else {
      close_sock(sd);
      return TIMEOUT;
    }
  }

  switch(buf.request) {
    case INCONSISTENT: /* has been updated on the server in the mean time */
    case ISLOCKED:     /* has been  locked on the server in the mean time */
      status=FAILURE;
      break;

    case CONSISTENT:
      if(updated) {
        if(pdelname(fp)==NOT_OK) {
          pso_errno=PSO_EERROR;
          fclose(fp);
          break;
        }
        if(fclose(fp)==-1) {
          pso_errno=PSO_EERROR;
          break;
        }
        else {
          pbuf(&buf,RECV_FILE,0L,0L,buf.ct,buf.cct,buf.mt,name);
          if(prequest(&sa,&sd,&buf)==NOT_OK||buf.status==NOT_OK) {
            if(buf.status==NOT_OK)
              pso_errno=PSO_ESERVERERROR;
            break;
          }
          else {
            if(send_file(sd,name)==NOT_OK)
              break;
            else {
              pbuf(&buf,SEND_STAT,0L,0L,buf.ct,buf.cct,buf.mt,name);
              if(prequest(&sa,&sd,&buf)==NOT_OK||buf.status==NOT_OK) {
                if(buf.status==NOT_OK)
                  pso_errno=PSO_ESERVERERROR;
                break;
              }
              else {
                if(wlet>t)
                  wlet=t;
                if(update_info(fd,buf.mt,buf.ct,buf.cct,ret,wlet,crt)==NOT_OK)
                  break;
                else {
                  status=SUCCESS;
                  break;
                }
              }
            }
          }
        }
      }
      else {
        if(request==READ_CLOSE)
          if(rlet>t)
            rlet=t;
          else
            rlet=ret;
        if(request==WRITE_CLOSE)
          if(wlet>t)
            wlet=t;
          else
            wlet=wet;
        if(update_info(fd,buf.mt,buf.ct,buf.cct,rlet,wlet,crt)==NOT_OK)
          break;
        else {
          status=SUCCESS;
          break;
        }
      }

    default:
      pso_errno=PSO_EUNKNOWNREPLY;
  }

  if(pso_errno==PSO_ENOERROR) {
    if(!updated) {
      if(pdelname(fp)==NOT_OK) {
        fclose(fp);
        unlock_file(fd);
        close_sock(sd);
        return NOT_OK;
      }
      if(fclose(fp)==EOF) {
        pso_errno=PSO_ENOTCLOSED;
        unlock_file(fd);
        close_sock(sd);
        return NOT_OK;
      }
    }
    if(unlock_file(fd)==NOT_OK) {
      close_sock(sd);
      return NOT_OK;
    }
    close_sock(sd);
    return status;
  }
  else {
    if(!updated) {
      pdelname(fp);
      fclose(fp);
    }
    unlock_file(fd);
    close_sock(sd);
    return NOT_OK;
  }
}

int p_lock(name,mode,letb)
  char *name,*mode;
  int letb;         /* lock expiration time bound (in minutes) */
{
  /* ... */
}

int p_unlock(name,mode)
  char *name,*mode;
{
  /* ... */
}

int p_remove(name)
  char *name;
{
  /* ... */
}

int p_stat(name,mt,ct,cct,ret,wet)
  char *name;
  long *mt,*ct,*cct,*ret,*wet;
{
  int cached;
  long crt;
  int fd;

  pso_errno=PSO_ENOERROR;

  if((cached=read_info(name,mt,ct,cct,ret,wet,&crt,&fd))==NOT_OK)
    return NOT_OK;
  else {
    if(!cached)
      if(delete_info(name)==NOT_OK)
        return NOT_OK;
    if(unlock_file(fd)==NOT_OK)
      return NOT_OK;
    else
      return cached;
  }
}

int p_comm()
{
  pso_errno=PSO_ENOERROR;

  return ptaco();
}

long p_time(flag)
  int flag;
{
  /* ... */
}
/* END library subroutines ****************************************************/



michael@garfield.dk
2000-10-13