Minix1.5/amoeba/examples/client3.c

Compare this file to the similar file:
Show the results in this format:

/* This file shows how one could build a remote file server for MINIX.
 * In this file there are several library routines for the basic system
 * calls.  Unlike the "real" ones, these call a remote file server instead
 * of the local kernel.  On client machines, one would replace the library
 * routines with these routines, and then recompile programs.  In this way,
 * clients will then call the remote file server.  It should be obvious that
 * this file is just an example, and that a productio version would have to
 * be much more complete.
 *
 * The file server3.c contains the start of a stateless file server.  Because
 * MINIX is not stateless, the conversion must be done in this library.  For
 * example, when an open() is done, the library records the name, but no
 * operation is performed on the file server.
 *
 * An alternative approach to making a remote file systems is to replace FS, 
 * the local file server, with one that makes the calls to the remote file 
 * server itself.  This approach is less efficient, because a call then 
 * consists of a local FS call plus a remote one, but it is more transparent 
 * because no programs need to recompiled.
 */

#include <amoeba.h>
#include <errno.h>
#include <minix/callnr.h>
#include "header.h"

#define MAX_FD 20
#define LOCAL 100
#define HEAPSIZE 512			/* space for file names */
#define WRITING 2
#define ER -1
#define FS 1
#define NIL_PTR (char*) 0

/* The local array is indexed by file descriptor.  Those entries containing
 * LOCAL are local (e.g., stdin), those containing REMOTE are remote, and
 * those containing 0 are unassigned.
 */

char where[MAX_FD] = {LOCAL, LOCAL, LOCAL};
long pos[MAX_FD];			/* current offset */
char *server_name = "filsrv";
char *file_name[MAX_FD];

char heap[HEAPSIZE];
char *heap_ptr = heap;
header hdr1, hdr2;
char buffer[BUF_SIZE+NAME_SIZE];

extern int errno;


/*============================= Remote Library ==============================*/
int open(name, how)
char *name;
int how;
{
/* Open is entirely local. */

  int i, len;

  if (how < 0 || how > 2) { errno = EINVAL; return(ER);}

  /* Find a free file descriptor. */
  for (i = 0; i < MAX_FD; i++) {
	if (where[i] == 0) {
		len = strlen(name);
		file_name[i] = heap_ptr;
		bcopy(name, heap_ptr, len);
		heap_ptr += len;
		*heap_ptr++ = 0;
		where[i] = how+1;
		return(i);
	}
  }
  errno = EMFILE;
  return(ER);
}
  
int creat(name, mode)
char *name;
int mode;
{
/* Create a file. */

  int i, len, n;

  /* Find a free file descriptor. */
  for (i = 0; i < MAX_FD; i++) {
	if (where[i] == 0) {
		len = strlen(name);
		file_name[i] = heap_ptr;
		bcopy(name, heap_ptr, len);
		heap_ptr += len;
		*heap_ptr++ = 0;
		where[i] = WRITING;

		strncpy(&hdr1.h_port, server_name, PORTSIZE);
		hdr1.h_command = CREAT;
		hdr1.h_size = mode;
		n = trans(&hdr1, file_name[i], len+1, &hdr2, buffer, 0);
		if (n < 0) {errno = EIO; return(ER);}
		return(hdr2.h_status);
	}
  }
  errno = EMFILE;
  return(ER);
}
  

/* int close(fd)
int fd;
{

  if (where[fd] == LOCAL) return(Xclose(fd));
  if (where[fd] == 0) {errno = EBADF; return(ER);}
  where[fd] = 0;
  return(OK);
}
*/

int read(fd, buf, bytes)
int fd, bytes;
char buf[];
{
/* Primitive read() routine for reads up to 1K. */

  int n;

  if (where[fd] == LOCAL) return (Xread(fd, buf, bytes));
  if ((where[fd]&1) == 0) {errno = EBADF; return(ER);}
  if (bytes > BUF_SIZE) return(EINVAL);	/* in a real version, fix this */
  strncpy(&hdr1.h_port, server_name, PORTSIZE);

  hdr1.h_command = READ;
  hdr1.h_size = bytes;
  hdr1.h_offset = pos[fd];
  n = trans(&hdr1, file_name[fd], strlen(file_name[fd])+1, &hdr2, buf, bytes);
  if (n < 0) {errno = EIO; return(ER);}
  if (hdr2.h_extra != 0) errno = hdr2.h_extra;
  pos[fd] += hdr2.h_status;		/* advance file position */
  return(hdr2.h_status);
}


int write(fd, buf, bytes)
int fd, bytes;
char buf[];
{
/* Primitive write() routine for writes up to 1K.  This is a very simple
 * routine.  Because the server is stateless, for a write we must send both
 * the data and the file name.  In this example, the first 1K of the buffer
 * is reserved for the data, with the file name starting at position 1024.
 */

  int n, len;

  if (where[fd] == LOCAL) return (Xwrite(fd, buf, bytes));
  if ((where[fd]&02) == 0) {errno = EBADF; return(ER);}
  if (bytes > BUF_SIZE) return(EINVAL);	/* in a real version, fix this */
  strncpy(&hdr1.h_port, server_name, PORTSIZE);

  len = strlen(file_name[fd]);
  hdr1.h_command = WRITE;
  hdr1.h_size = bytes;
  hdr1.h_offset = pos[fd];
  bcopy(buf, buffer, bytes);		/* copy data to message */
  bcopy(file_name[fd], &buffer[BUF_SIZE], len+1);
  n = trans(&hdr1, buffer, BUF_SIZE+len+1, &hdr2, buf, 0);
  if(n < 0) {errno = EIO; return(ER);}
  if (hdr2.h_extra != 0) errno = hdr2.h_extra;
  pos[fd] += hdr2.h_status;
  return(hdr2.h_status);
}




/* Below are the real calls, which are sometimes needed. */

int Xread(fd, buffer, nbytes)
int fd;
char *buffer;
int nbytes;
{
  int n;
  n = callm1(FS, READ, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR);
  return(n);
}

int Xwrite(fd, buffer, nbytes)
char *buffer;
int nbytes;
{
  return callm1(FS, WRITE, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR);
}


int Xclose(fd)
int fd;
{
  return callm1(FS, CLOSE, fd, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR);

}


/* ========================= test program =============================*/
main(argc, argv)
int argc;
char *argv[];
{
  int fd1, n;
  char b[1024];

  if (argc != 2) {
	printf("Usage: client3 file\n");
	exit(1);
  }

  fd1 = open(argv[1], 0);
  if (fd1 < 0) {
	printf("Open of %s failed\n", argv[1]);
	exit(1);
  }

  do {
	if ((n=read(fd1, b, 1024) < 0)) {
		printf("Cannot read %s\n", argv[1]);
		exit(1);
	}
	if (write(1, b, n) < 0) {
		printf("Cannot write stdout\n");
		exit(1);
	}
  } while (n > 0);
}