next up previous contents index
Next: Client: pclient.h Up: Program Previous: Program   Contents   Index

Server: pserver.c


/*******************************************************************************/
/* pserver.c - server process 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 "ppserver.h" /* include file for PeStO server  */
/* END include *****************************************************************/

/* BEGIN globals ***************************************************************/
int sd1,sd2;                /* socket descriptors */
struct sockaddr_in sa1,sa2; /* socket addresses (on the Internet) */
long t;
struct linger l;
/* END globals *****************************************************************/

/* BEGIN PeStO file server *****************************************************/
void p_server()
{
  char host[HOSTNAME_MAX];          /* name of host */
  struct hostent *hp;               /* result of hostname lookup */
  pmessage buf;                     /* message buffer */
  struct stat stbuf;                /* file status buffer */
  int locked,readlocked,writelocked;
  long ret,wet,rlet,wlet;
  int fd;
  int flag;
  char lockname[LOCKNAME_MAX];
  char logstr[1000];

  char *inet_ntoa();

  close_sock(sd1);

  if((hp=gethostbyaddr((char *)&sa2.sin_addr,sizeof(struct in_addr),sa2.sin_family))==NULL)
    strcpy(host,inet_ntoa(sa2.sin_addr));
  else
    strcpy(host,hp->h_name);

#ifdef LOG
  time(&t);
  sprintf(logstr,"Startup from %s port %u at %s",host,ntohs(sa2.sin_port),ctime(&t));
  plog(logstr);
#endif

  l.l_onoff=1;
  l.l_linger=1;
  if(setsockopt(sd2,SOL_SOCKET,SO_LINGER,&l,sizeof(struct linger))==-1) {
    pso_errno=PSO_EERROR;
    close_sock(sd2);
    exit(NOT_OK);
  }

  if(recv(sd2,&buf,sizeof(buf),0)!=sizeof(buf)) {
    pso_errno=PSO_EERROR;
    close_sock(sd2);
    exit(NOT_OK);
  }

#ifdef LOG
  sprintf(logstr,"Request from client initiated %s",ctime(&(buf.t)));
  plog(logstr);
#endif

  /* handle the request and give a reply */

  switch(buf.request){
    case READ_OPEN:
    case WRITE_OPEN:
      /* BEGIN open for READING or WRITING *************************************/

      /*
         Possible returns are: buf.status is set to NOT_OK or OK, and if OK then
         buf.request is set to CONSISTENT, INCONSISTENT, NOTFOUND, ISLOCKED or
         WASLOCKED.

         If CONSISTENT then buf.ct and buf.cct are set. If INCONSISTENT then
         buf.mt and buf.cct are set. If ISLOCKED or WASLOCKED then also
         CONSISTENT if buf.ct is equal to buf.ct else INCONSISTENT.

         If buf.request is READ_OPEN then the content of buf.wet is ignored,
         and similarly the content of buf.ret is ignored if buf.request is
         WRITE_OPEN.
      */

#ifdef LOG
      if(buf.request==READ_OPEN)
        sprintf(logstr,"READ_OPEN %s\n",buf.name);
      else
        sprintf(logstr,"WRITE_OPEN %s\n",buf.name);
      plog(logstr);
#endif

      buf.status=OK;

      if(stat(buf.name,&stbuf)==-1) /* check existense and status of file */ {
        if(errno==ENOENT) /* No such file or directory */
          buf.request=NOTFOUND;
        else
          buf.status=NOT_OK;
      }
      else {
        if((locked=read_lock(buf.name,host,&ret,&wet,&fd))==NOT_OK)
          buf.status=NOT_OK;
        else {
          time(&t);
          readlocked=(locked&&ret>=t);
          writelocked=(locked&&wet>=t);

          buf.cct=t; /* set consistency check time for file */
          if(stbuf.st_mtime==buf.mt) /* check modification time of file */ {
            flag=CONSISTENT;
            buf.ct=t;
          }
          else {
            flag=INCONSISTENT;
            buf.mt=stbuf.st_mtime;
          }

          if(writelocked||(readlocked&&buf.request==WRITE_OPEN))
            buf.request=ISLOCKED;
          else {
            if((buf.request==READ_OPEN&&buf.ret<t)||
               (buf.request==WRITE_OPEN&&buf.wet<t)) /* no locking required */ {
              if((buf.request==READ_OPEN&&buf.ret>0L)||
                 (buf.request==WRITE_OPEN&&buf.wet>0L))
                buf.request=NOTLOCKED;
              else
                buf.request=flag;
            }
            else /* locking required */ {
              /*
                 At this point we know that the client wishes to put a readlock
                 on the file. But that will only be the case if neither a read-
                 nor a writelock was previously obtained. Still we might have an
                 out-dated (timeout'ed) entry in the lock(file).
              */
              if((locked=lookup_lock(fd,host,&ret,&wet))==NOT_OK)
                  buf.status=NOT_OK;
              else {
                if(locked&&(wet>=t||ret>=t))
                  /*
                     Note, that this should not be! Here we have a client asking for
                     a lock although the client already has one. This could be the
                     result of unsynchronized clocks.
                  */
                  buf.status=NOT_OK;
                else {
                  if(buf.request==READ_OPEN) {
                    rlet=buf.ret;
                    wlet=0L;
                  }
                  else {
                    wlet=buf.wet;
                    rlet=0L;
                  }
                  if(locked) {
                    if(update_lock(fd,host,rlet,wlet)==NOT_OK)
                      buf.status=NOT_OK;
                    else
                      buf.request=WASLOCKED;
                  }
                  else {
                    if(write_lock(fd,host,rlet,wlet)==NOT_OK)
                      buf.status=NOT_OK;
                    else
                      buf.request=WASLOCKED;
                  }
                }
              }
            }
          }

          if(unlock_file(fd)==NOT_OK)
            buf.status=NOT_OK;
        }
      }

      psend(sd2,&buf);
      break;
      /* END open for READING or WRITING ***************************************/

    /* ... */

    default:
      /* BEGIN request UNKNOWN *************************************************/

      /*
         Returns buf.status set to NOT_OK.
      */

#ifdef LOG
      sprintf(logstr,"UNKNOWN\n");
      plog(logstr);
#endif

      buf.status=NOT_OK;
      psend(sd2,&buf);
      break;
      /* END request UNKNOWN ***************************************************/
  }

#ifdef LOG
  time(&t);
  sprintf(logstr,"Completed %s port %u at %s",host,ntohs(sa2.sin_port),ctime(&t));
  plog(logstr);
#endif

  close_sock(sd2);
}
/* END PeStO file server *******************************************************/

/* BEGIN main ******************************************************************/
void main()
{
  struct hostent *hp;           /* result of hostname lookup */
  int len;                      /* address length */
  char localhost[HOSTNAME_MAX];

#ifndef SYSV5
#ifndef LINUX
  int fd;
  int sig_child();
#endif
#endif

  memset((char *)&sa1,0,sizeof(struct sockaddr_in));
  memset((char *)&sa2,0,sizeof(struct sockaddr_in));

  if(gethostname(localhost,HOSTNAME_MAX)==-1) {
    pso_errno=PSO_EERROR;
    exit(NOT_OK);
  }

  if((hp=gethostbyname(localhost))==NULL) {
    pso_errno=PSO_EERROR;
    exit(NOT_OK);
  }

  if((sa1.sin_family=hp->h_addrtype)!=AF_INET) {
    pso_errno=PSO_EBADADDRESS;
    exit(NOT_OK);
  }

  sa1.sin_port=ntohs(PORT);

  if((sd1=socket(AF_INET,SOCK_STREAM,0))==-1) {
    pso_errno=PSO_EERROR;
    exit(NOT_OK);
  }

  if(bind(sd1,(void *)&sa1,sizeof(struct sockaddr_in))==-1) {
    pso_errno=PSO_EERROR;
    close_sock(sd1);
    exit(NOT_OK);
  }

  if(listen(sd1,10)==-1) {
    pso_errno=PSO_EERROR;
    close_sock(sd1);
    exit(NOT_OK);
  }

#ifdef SYSV5
  setpgrp();
#else
#ifndef LINUX
  setpgrp(0,getpid());
  if((fd=open("/dev/tty",O_RDWR))>=0) {
    ioctl(fd,TIOCNOTTY,(char *)NULL); /* loose controlling tty */
    close(fd);
  }
#else
  setpgrp();
#endif
#endif

  switch(fork()) {
    case -1:
      pso_errno=PSO_EERROR;
      close_sock(sd1);
      exit(NOT_OK);
    case 0: /* child process */
      fclose(stdin);
      fclose(stderr);
#ifdef SYSV5
      signal(SIGCLD,SIG_IGN);
#else
#ifndef LINUX
      signal(SIGCLD,sig_child);
#else
      signal(SIGCLD,SIG_IGN);
#endif
#endif
      for(;;) {
        len=sizeof(struct sockaddr_in);
        if((sd2=accept(sd1,(void *)&sa2,&len))==-1) {
          pso_errno=PSO_EERROR;
          close_sock(sd1);
          exit(NOT_OK);
        }

        switch(fork()) {
          case -1:
            pso_errno=PSO_EERROR;
            close_sock(sd1);
            close_sock(sd2);
            exit(NOT_OK);
          case 0: /* child process */
            p_server();
            exit(OK);
          default: /* parent process */
            close_sock(sd2);
        }
      }

    default: /* parent process */
      close_sock(sd1);
      exit(OK);
  }
}
/* END main ********************************************************************/



michael@garfield.dk
2000-10-13