[comp.os.minix] V1.3 posting #37 - amoeba/examples/*

ast@cs.vu.nl (Andy Tanenbaum) (07/18/88)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'READ_ME'
sed 's/^X//' > 'READ_ME' << '+ END-OF-FILE ''READ_ME'
XThis directory contains several examples of networking code.  These examples
Xalso serve as programs to test the correctness and performance of the 
Xnetworking and to provide some useful utility functions.  In all the tests,
Xthe client and server may run on the same machine, or if an Ethernet is
Xinstalled, on different machines.  The  ports 'xyz' used in tests 1 and 2
Xare examples.  It is possible to run multiple tests at the same time (e.g.,
Xn1 sets of (client1, server1) and n2 sets of (client2, server2), provided
Xthat each pair of (clientX, serverX) uses a port not used by any other process.
X
X1. Test 1 consists of two programs, server1.c and client1.c.  This test 
X   transfers 1 megabyte from the client to the server in 1K chunks, has the
X   server transform the data and send it back.  The client then verifies if
X   the returned data is correct.  To run the test, type the following lines:
X   
X	make client1 server1
X   	server1 xyz &
X   	client1 xyz
X   
X   If an Ethernet is installed and the kernel has been compiled with 
X   -DAM_KERNEL, the server and client may either be on the same machine or
X   one different ones.  If there is no Ethernet and the kernel has been
X   compiled with _DAM_KERNEL -DNONET, both processes must be on the same CPU.
X
X   At the end of the test, "ok" is printed if no errors occurred.  Otherwise
X   the number of bad transactions is printed. Both the client and server exit
X   by themselves after the test has been completed.
X
X2. Test 2 consists of two programs, server2.c and client2.c.  This test
X   measures the performance of the transactions.  Like test 1, it can be run
X   either locally or remotely.  The test consists of having the client request
X   n bytes of data from the server, where n grows from 1 to 30,000 bytes per
X   transaction.  For each value of n, the test is run 1000 times to increase
X   the statistical accuracy.  For each value of n, the value of n, the delay
X   (transaction time in milliseconds), and the throughput (bytes/sec moved)
X   is reported.  To run the test, type:
X
X	make client2 server2
X   	server2 xyz &
X   	client2 xyz
X
X   
X3. Test 3 is an example of how one might go about making a remote file server
X   for MINIX to service diskless PCs.  In this approach, one writes a server,
X   server3.c in this example, and a set of library routines, contained in
X   client3.c.  These library routines should have the same names as the MINIX
X   system calls, such as open(), read(), and write().  When programs are linked
X   with these routines instead of the usual ones, the routines make calls to
X   the remote file server instead of FS.  The file client3.c contains not only
X   a few of these library routines, but also a short main program to test them.
X   The main program fetches a file from the remote file server and copies it to
X   stdout.  To test the program, type:
X
X	make client3 server3
X	server3 &
X	client3 <filename>
X
X   where <filename> is the name of a file on the same machine as server3.  The
X   result of this command will be that <filename> is written to client3's 
X   standard output.
+ END-OF-FILE READ_ME
chmod 'u=rw,g=r,o=r' 'READ_ME'
set `wc -c 'READ_ME'`
count=$1
case $count in
3080)	:;;
*)	echo 'Bad character count in ''READ_ME' >&2
		echo 'Count should be 3080' >&2
esac
echo Extracting 'client1.c'
sed 's/^X//' > 'client1.c' << '+ END-OF-FILE ''client1.c'
X/* This program tests the basic RPC to see if it works. */
X
X#include <amoeba.h>
X#include "header.h"
X
X#define MAX_TRIALS 1000
X
Xchar buffer1[BUF_SIZE], buffer2[BUF_SIZE];
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X
X  header hdr1, hdr2;
X  int cnt, i, base = 0, size, errors, blocks, iterations = 0;
X  long bad_trans = 0;
X
X  if (argc != 2) {
X	printf("Usage: client1 portname\n");
X	exit(1);
X  }
X
X  /* Copy the filename into the start of the buffer. */
X  strncpy(&hdr1.h_port, argv[1], PORTSIZE);
X  hdr1.h_command = WORK;
X
X  printf("Number of tests performed = %5d ", 0);
X
X  while (iterations < MAX_TRIALS) {
X 	/* Initialize the buffer. */
X	for (i = 0; i < BUF_SIZE; i++) buffer1[i] = base + i;
X
X	size = trans(&hdr1, buffer1, BUF_SIZE, &hdr2, buffer2, BUF_SIZE);
X	if (size < 0) {
X	    printf("\nTransaction failed. Error = %d.   ", size);
X	    printf("Hit F1 to see if AMTASK running.\n");
X		exit(1);
X	}
X
X	/* Check reply. */
X	errors = 0;
X	for (i = 0; i < BUF_SIZE; i++) 
X		if ( (buffer2[i]&0377) != ( (buffer1[i]+1)&0377)) errors++;
X	if (errors > 0) bad_trans++;
X	blocks++;
X	base++;
X	iterations++;
X	if (iterations % 10 == 0) printf("\b\b\b\b\b\b%5d ", iterations);
X  }
X
X  /* Test done. Send null trans to tell server. Report on errors. */
X  hdr1.h_command = QUIT;
X  trans(&hdr1, buffer1, 0, &hdr2, buffer2, BUF_SIZE);
X  printf("\b.   Number of errors = %d.\n", bad_trans);
X}
+ END-OF-FILE client1.c
chmod 'u=rw,g=r,o=r' 'client1.c'
set `wc -c 'client1.c'`
count=$1
case $count in
1370)	:;;
*)	echo 'Bad character count in ''client1.c' >&2
		echo 'Count should be 1370' >&2
esac
echo Extracting 'client2.c'
sed 's/^X//' > 'client2.c' << '+ END-OF-FILE ''client2.c'
X/* This file is the client of a test program for measuring RPC speed. */
X
X#include <amoeba.h>
X#include <minix/callnr.h>
X#include "header.h"
X
X#define TRIALS 1000
X
Xchar buffer[MAX_TRANS];
Xint bytes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 
X		8192, 16384, 30000, 0};
X
Xheader hdr1, hdr2;
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X  int i, cnt;
X
X  if (argc != 2) {
X	printf("Usage: client2 portname\n");
X	exit(1);
X  }
X
X  /* Copy the filename into the start of the buffer. */
X  strncpy(&hdr1.h_port, argv[1], PORTSIZE);
X  hdr1.h_command = READ;
X
X  printf("Buf size        Delay       Throughput      (Each test repeated %d times)\n", TRIALS);
X  printf("            (msec/trans)    (bytes/sec)\n");
X  printf("--------    ------------    -----------\n");
X
X  i = 0;
X  while (bytes[i] != 0) {
X	run_test(bytes[i], TRIALS);
X	i++;
X  }
X  hdr1.h_command = QUIT;
X  trans(&hdr1, buffer, 0, &hdr2, buffer, 0);
X  exit(0);
X}
X
X
Xrun_test(count, trials)
Xint count, trials;
X{
X/* Run a performance test. */
X
X  int i, n;
X  long start, finish, traffic, sec, msec, delay, throughput;
X  
X  time(&start);				/* record starting time */
X  for (i = 0; i < trials; i++) {
X	hdr1.h_command = READ;
X	hdr1.h_size = (unshort) count;
X	n = trans(&hdr1, buffer, 0, &hdr2, buffer, MAX_TRANS);
X  	if (n < 0) {
X		printf("Transaction failed. Error = %d.   ", n);
X		printf("Hit F1 to see if AMTASK running.\n");
X		exit(1);
X	}
X  }
X  time(&finish);
X  sec = finish - start;			/* time for this trial in seconds */
X  msec = 1000L * sec;
X  traffic = (long) trials * (long) count;
X  delay = msec/trials;			/* msec per transaction */
X  if (sec != 0L)
X	throughput = traffic/sec;
X  else
X	throughput = 0L;
X  printf("  %5d         %4D          %7D\n", count, delay, throughput);
X}
X
X
+ END-OF-FILE client2.c
chmod 'u=rw,g=r,o=r' 'client2.c'
set `wc -c 'client2.c'`
count=$1
case $count in
1746)	:;;
*)	echo 'Bad character count in ''client2.c' >&2
		echo 'Count should be 1746' >&2
esac
echo Extracting 'client3.c'
sed 's/^X//' > 'client3.c' << '+ END-OF-FILE ''client3.c'
X/* This file shows how one could build a remote file server for MINIX.
X * In this file there are several library routines for the basic system
X * calls.  Unlike the "real" ones, these call a remote file server instead
X * of the local kernel.  On client machines, one would replace the library
X * routines with these routines, and then recompile programs.  In this way,
X * clients will then call the remote file server.  It should be obvious that
X * this file is just an example, and that a productio version would have to
X * be much more complete.
X *
X * The file server3.c contains the start of a stateless file server.  Because
X * MINIX is not stateless, the conversion must be done in this library.  For
X * example, when an open() is done, the library records the name, but no
X * operation is performed on the file server.
X *
X * An alternative approach to making a remote file systems is to replace FS, 
X * the local file server, with one that makes the calls to the remote file 
X * server itself.  This approach is less efficient, because a call then 
X * consists of a local FS call plus a remote one, but it is more transparent 
X * because no programs need to recompiled.
X */
X
X#include <amoeba.h>
X#include <errno.h>
X#include <minix/callnr.h>
X#include "header.h"
X
X#define MAX_FD 20
X#define LOCAL 100
X#define HEAPSIZE 512			/* space for file names */
X#define WRITING 2
X#define ER -1
X#define FS 1
X#define NIL_PTR (char*) 0
X
X/* The local array is indexed by file descriptor.  Those entries containing
X * LOCAL are local (e.g., stdin), those containing REMOTE are remote, and
X * those containing 0 are unassigned.
X */
X
Xchar where[MAX_FD] = {LOCAL, LOCAL, LOCAL};
Xlong pos[MAX_FD];			/* current offset */
Xchar *server_name = "filsrv";
Xchar *file_name[MAX_FD];
X
Xchar heap[HEAPSIZE];
Xchar *heap_ptr = heap;
Xheader hdr1, hdr2;
Xchar buffer[BUF_SIZE+NAME_SIZE];
X
Xextern int errno;
X
X
X/*============================= Remote Library ==============================*/
Xint open(name, how)
Xchar *name;
Xint how;
X{
X/* Open is entirely local. */
X
X  int i, len;
X
X  if (how < 0 || how > 2) { errno = EINVAL; return(ER);}
X
X  /* Find a free file descriptor. */
X  for (i = 0; i < MAX_FD; i++) {
X	if (where[i] == 0) {
X		len = strlen(name);
X		file_name[i] = heap_ptr;
X		bcopy(name, heap_ptr, len);
X		heap_ptr += len;
X		*heap_ptr++ = 0;
X		where[i] = how+1;
X		return(i);
X	}
X  }
X  errno = EMFILE;
X  return(ER);
X}
X  
Xint creat(name, mode)
Xchar *name;
Xint mode;
X{
X/* Create a file. */
X
X  int i, len, n;
X
X  /* Find a free file descriptor. */
X  for (i = 0; i < MAX_FD; i++) {
X	if (where[i] == 0) {
X		len = strlen(name);
X		file_name[i] = heap_ptr;
X		bcopy(name, heap_ptr, len);
X		heap_ptr += len;
X		*heap_ptr++ = 0;
X		where[i] = WRITING;
X
X		strncpy(&hdr1.h_port, server_name, PORTSIZE);
X		hdr1.h_command = CREAT;
X		hdr1.h_size = mode;
X		n = trans(&hdr1, file_name[i], len+1, &hdr2, buffer, 0);
X		if (n < 0) {errno = EIO; return(ER);}
X		return(hdr2.h_status);
X	}
X  }
X  errno = EMFILE;
X  return(ER);
X}
X  
X
Xint close(fd)
Xint fd;
X{
X/* Close a file. */
X
X  if (where[fd] == LOCAL) return(Xclose(fd));
X  if (where[fd] == 0) {errno = EBADF; return(ER);}
X  where[fd] = 0;
X  return(OK);
X}
X
X
Xint read(fd, buf, bytes)
Xint fd, bytes;
Xchar buf[];
X{
X/* Primitive read() routine for reads up to 1K. */
X
X  int n;
X
X  if (where[fd] == LOCAL) return (Xread(fd, buf, bytes));
X  if ((where[fd]&1) == 0) {errno = EBADF; return(ER);}
X  if (bytes > BUF_SIZE) return(EINVAL);	/* in a real version, fix this */
X  strncpy(&hdr1.h_port, server_name, PORTSIZE);
X
X  hdr1.h_command = READ;
X  hdr1.h_size = bytes;
X  hdr1.h_offset = pos[fd];
X  n = trans(&hdr1, file_name[fd], strlen(file_name[fd])+1, &hdr2, buf, bytes);
X  if (n < 0) {errno = EIO; return(ER);}
X  if (hdr2.h_extra != 0) errno = hdr2.h_extra;
X  pos[fd] += hdr2.h_status;		/* advance file position */
X  return(hdr2.h_status);
X}
X
X
Xint write(fd, buf, bytes)
Xint fd, bytes;
Xchar buf[];
X{
X/* Primitive write() routine for writes up to 1K.  This is a very simple
X * routine.  Because the server is stateless, for a write we must send both
X * the data and the file name.  In this example, the first 1K of the buffer
X * is reserved for the data, with the file name starting at position 1024.
X */
X
X  int n, len;
X
X  if (where[fd] == LOCAL) return (Xwrite(fd, buf, bytes));
X  if ((where[fd]&02) == 0) {errno = EBADF; return(ER);}
X  if (bytes > BUF_SIZE) return(EINVAL);	/* in a real version, fix this */
X  strncpy(&hdr1.h_port, server_name, PORTSIZE);
X
X  len = strlen(file_name[fd]);
X  hdr1.h_command = WRITE;
X  hdr1.h_size = bytes;
X  hdr1.h_offset = pos[fd];
X  bcopy(buf, buffer, bytes);		/* copy data to message */
X  bcopy(file_name[fd], &buffer[BUF_SIZE], len+1);
X  n = trans(&hdr1, buffer, BUF_SIZE+len+1, &hdr2, buf, 0);
X  if(n < 0) {errno = EIO; return(ER);}
X  if (hdr2.h_extra != 0) errno = hdr2.h_extra;
X  pos[fd] += hdr2.h_status;
X  return(hdr2.h_status);
X}
X
X
X
X
X/* Below are the real calls, which are sometimes needed. */
X
Xint Xread(fd, buffer, nbytes)
Xint fd;
Xchar *buffer;
Xint nbytes;
X{
X  int n;
X  n = callm1(FS, READ, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR);
X  return(n);
X}
X
Xint Xwrite(fd, buffer, nbytes)
Xchar *buffer;
Xint nbytes;
X{
X  return callm1(FS, WRITE, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR);
X}
X
X
Xint Xclose(fd)
Xint fd;
X{
X  return callm1(FS, CLOSE, fd, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR);
X
X}
X
X
X/* ========================= test program =============================*/
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X  int fd1, n;
X  char b[1024];
X
X  if (argc != 2) {
X	printf("Usage: client3 file\n");
X	exit(1);
X  }
X
X  fd1 = open(argv[1], 0);
X  if (fd1 < 0) {
X	printf("Open of %s failed\n", argv[1]);
X	exit(1);
X  }
X
X  do {
X	if ((n=read(fd1, b, 1024) < 0)) {
X		printf("Cannot read %s\n", argv[1]);
X		exit(1);
X	}
X	if (write(1, b, n) < 0) {
X		printf("Cannot write stdout\n");
X		exit(1);
X	}
X  } while (n > 0);
X}
+ END-OF-FILE client3.c
chmod 'u=rw,g=r,o=r' 'client3.c'
set `wc -c 'client3.c'`
count=$1
case $count in
5810)	:;;
*)	echo 'Bad character count in ''client3.c' >&2
		echo 'Count should be 5810' >&2
esac
echo Extracting 'header.h'
sed 's/^X//' > 'header.h' << '+ END-OF-FILE ''header.h'
X/* Some declarations need by the example clients and servers. */
X
Xchar *file_server = "filsrv";
X
X#define BUF_SIZE 1024
X#define NAME_SIZE 512
X#define WORK 1
X#define QUIT 999
X#define MAX_TRANS 30000
X
X
+ END-OF-FILE header.h
chmod 'u=rw,g=r,o=r' 'header.h'
set `wc -c 'header.h'`
count=$1
case $count in
199)	:;;
*)	echo 'Bad character count in ''header.h' >&2
		echo 'Count should be 199' >&2
esac
echo Extracting 'makefile'
sed 's/^X//' > 'makefile' << '+ END-OF-FILE ''makefile'
XCFLAGS=
Xall: client1 server1 client2 server2 client3 server3
X	
Xclient1: header.h client1.s
X	 cc -o client1 client1.s
X
Xclient2: header.h client2.s
X	 cc -o client2 client2.s
X
Xclient3: header.h client3.s
X	 cc -o client3 client3.s
X
Xserver1: header.h server1.s
X	 cc -o server1 server1.s
X	 chmem =1000 server1
X
Xserver2: header.h server2.s
X	 cc -o server2 server2.s
X	 chmem =1000 server2
X
Xserver3: header.h server3.s
X	 cc -o server3 server3.s
X	 chmem =1000 server3
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' 'makefile'
set `wc -c 'makefile'`
count=$1
case $count in
458)	:;;
*)	echo 'Bad character count in ''makefile' >&2
		echo 'Count should be 458' >&2
esac
echo Extracting 'server1.c'
sed 's/^X//' > 'server1.c' << '+ END-OF-FILE ''server1.c'
X#include <amoeba.h>
X#include "header.h"
X
Xchar buffer[BUF_SIZE];
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X  unshort getreq(), putrep();
X
X  header hdr;
X  int cnt, i, iterations = 0;
X
X  if (argc != 2) {
X	printf("Usage: server1 portname\n");
X	exit(1);
X  }
X
X  strncpy( (char *) &hdr.h_port, argv[1], PORTSIZE);  /* init port */
X
X  while (1) {
X
X	/* Wait for a request to arrive. */
X	if ((cnt = (short) getreq(&hdr, buffer, BUF_SIZE)) < 0) {
X	    printf("Server's getreq failed. Error = %d.   ", cnt);
X	    printf("Hit F1 to see if AMTASK running.\n");
X	    exit(1);
X	}
X	
X	/* Opcode QUIT indicates that we are done. */
X	if (hdr.h_command == QUIT) {
X		putrep(&hdr, buffer, 0);
X		exit(0);
X	}
X
X	/* We have a request. Increment each byte. */
X	for (i = 0; i < cnt; i++) buffer[i]++;
X
X	/* Send back reply. */
X	hdr.h_status = 0;
X	putrep(&hdr, buffer, cnt);
X  }
X}
+ END-OF-FILE server1.c
chmod 'u=rw,g=r,o=r' 'server1.c'
set `wc -c 'server1.c'`
count=$1
case $count in
852)	:;;
*)	echo 'Bad character count in ''server1.c' >&2
		echo 'Count should be 852' >&2
esac
echo Extracting 'server2.c'
sed 's/^X//' > 'server2.c' << '+ END-OF-FILE ''server2.c'
X#include <amoeba.h>
X#include "header.h"
X
Xchar buffer[MAX_TRANS];
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X
X  header hdr;
X  int count;
X
X  if (argc != 2) {
X	printf("Usage: server2 portname\n");
X	exit(1);
X  }
X
X  strncpy( (char *) &hdr.h_port, argv[1], PORTSIZE);  /* init port */
X
X  while (1) {
X	/* Wait for a request to arrive. */
X	count = (short) getreq(&hdr, buffer, MAX_TRANS);
X	if (count < 0) {
X	    printf("Server's getreq failed. Error = %d.   ", count);
X	    printf("Hit F1 to see if AMTASK running.\n");
X	    exit(1);
X	}
X	
X	/* Opcode QUIT indicates that we are done. */
X	if (hdr.h_command == QUIT) {
X		putrep(&hdr, buffer, 0);
X		exit(0);
X	}
X
X	/* We have a request. Reply. */
X	count = hdr.h_size;
X	putrep(&hdr, buffer, count);
X  }
X}
+ END-OF-FILE server2.c
chmod 'u=rw,g=r,o=r' 'server2.c'
set `wc -c 'server2.c'`
count=$1
case $count in
741)	:;;
*)	echo 'Bad character count in ''server2.c' >&2
		echo 'Count should be 741' >&2
esac
echo Extracting 'server3.c'
sed 's/^X//' > 'server3.c' << '+ END-OF-FILE ''server3.c'
X#include <amoeba.h>
X#include <minix/callnr.h>
X#include <errno.h>
X#include "header.h"
X
Xheader hdr;				/* header for incoming messages */
Xchar buffer[BUF_SIZE+NAME_SIZE];	/* buffer for incoming messages */
Xchar *server_name = "filsrv";
Xint repsize;
Xextern int errno;
X
Xmain()
X{
X/* This is a primitive file server.  The client runs with a special set of
X * routines for read(), write(), etc. that call this server.  The server is
X * stateless.
X */
X
X  int s;
X  int count;
X  unshort opcode, size;
X
X  strncpy( (char *) &hdr.h_port, server_name, PORTSIZE);  /* init port */
X
X  while (1) {
X	/* Wait for a request to arrive. */
X	count = (short) getreq(&hdr, buffer, MAX_TRANS);
X	if (count < 0) {
X	    printf("Server's getreq failed. Error = %d.   ", count);
X	    printf("Hit F1 to see if AMTASK running.\n");
X	    exit(1);
X	}
X	
X	/* Dispatch on opcode. */
X	opcode = hdr.h_command;
X	repsize = 0;
X	errno = 0;
X	switch(opcode) {
X		case CREAT:	s = do_creat();		break;
X		case READ:	s = do_read();		break;
X		case WRITE:	s = do_write();		break;
X		default:	s = EINVAL;		break;
X	}
X
X	/* Work done.  Send a reply. */
X	hdr.h_status = (unshort) s;
X 	hdr.h_extra = (unshort) errno;
X	putrep(&hdr, buffer, repsize);
X  }
X}
X
Xint do_read()
X{
X  /* Stateless read. */
X
X  int fd, n;
X  long offset;
X  unshort count;
X
X  offset = hdr.h_offset;
X  count = hdr.h_size;
X  if (count > MAX_TRANS) count = MAX_TRANS;
X
X  fd = open(buffer, 0);		/* open the file for reading */
X  if (fd < 0) return(errno);
X  lseek(fd, offset, 0);
X  n = read(fd, buffer, count);
X  close(fd);
X  repsize = n;
X  return(n);
X}
X
Xint do_write()
X{
X  /* Stateless write. */
X
X  int fd, n;
X  long offset;
X  unshort count;
X
X  offset = hdr.h_offset;
X  count = hdr.h_size;
X  if (count > MAX_TRANS) count = MAX_TRANS;
X
X  fd = open(&buffer[BUF_SIZE], 2);		/* open the file for writing */
X  if (fd < 0) return(errno);
X  lseek(fd, offset, 0);
X  n = write(fd, buffer, count);
X  close(fd);
X  return(n);
X}
X
Xint do_creat()
X{
X  /* Stateless creat. */
X
X  int fd, n, mode;
X  
X
X  mode = hdr.h_size;
X  fd = creat(buffer, mode);		/* creat the file  */ 
X  close(fd);
X  return(fd);
X}
+ END-OF-FILE server3.c
chmod 'u=rw,g=r,o=r' 'server3.c'
set `wc -c 'server3.c'`
count=$1
case $count in
2090)	:;;
*)	echo 'Bad character count in ''server3.c' >&2
		echo 'Count should be 2090' >&2
esac
exit 0