[comp.sources.unix] v14i096: Shared memory emulation for 4.2BSD, Part03/04

rsalz@bbn.com (Rich Salz) (05/18/88)

Submitted-by: libes@cme-durer.ARPA (Don Libes)
Posting-number: Volume 14, Issue 96
Archive-name: sharedmem/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 4)."
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'doc/usenix/abstract.trf' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'doc/usenix/abstract.trf'\"
else
echo shar: Extracting \"'doc/usenix/abstract.trf'\" \(6265 characters\)
sed "s/^X//" >'doc/usenix/abstract.trf' <<'END_OF_FILE'
X.RP
X.TL
XUser-Level Shared Variables (in a Hierarchical Control Environment)
X.AU
XDon Libes
X.AI
XFactory Automation Systems Division
XNational Bureau of Standards
XGaithersburg, MD  20899
X.PP
XWe have implemented a shared variable system for UNIX 4.2 that emulates a
Xshared or common memory.
X.IP \(bu
XShared variables provide an alternate style of communications
Xto message passing or pipes.  
X.IP \(bu
XThis system is all user level code and requires no kernel
Xmodifications.  It is accessible from a variety of languages.
X.IP \(bu
XWe are using this system in a real application - an automated
Xfactory.
X.PP
X.SH
XI   User View
X.PP
XA server, the Common Memory Manager (CMM), acts on requests to access shared
Xvariables.  Typical requests are read, write, and declare.  Variables are
Xstructured and have attributes such as type, protection, lifetime, and
Xownership.  Variable type checking is performed at runtime startup, since
Xprocesses are loosely connected.
X.PP
XSome of the more interesting attributes of these shared variables are:
X.PP
X.I
Xlifetime and "shelf life".
X.R
XIn a real time environment, the system must
Xsurvive a process dying or getting bogged down temporarily.  When the useful
Xlifetime of a variable's value has expired, other processes are free to
Xmanipulate the variable, for instance, by redeclaring or writing it.  This
Xattribute allows a "god" process to notice unexpected process deaths and
Xrestart processes or transfer control to other processes.   For less
Xcritical variables (such as an infrequently updated sensor value), processes
Xmight note a "stale" value, and make a projection that will carry them over
Xuntil the variable writer catches up to its duties.
X.PP
X.I
Xwrite count, timestamp, authorstamp.
X.R
XBy marking each variable with how many
Xtimes or when it has been written, handshaking can be performed for the
Xusers.  The archetypal example from the automation environment is the
Xcommand "hit nail with hammer".  If the supervisor runs more quickly than
Xthe subordinate, without handshaking, the subordinate may never see the
Xcommand (i.e. nail is not hit).  If the subordinate runs more quickly than
Xthe supervisor, without handshaking, the subordinate may execute the same
Xcommand twice (i.e. nail is hit more than once)
X.PP
X.I
Xwakeup.
X.R
XVariables can be marked with a list of processes to be notified upon
Xvalue change.  This was originally added for efficiency, so that servers,
Xfor example, would not have to loop just waiting for new values.  It has
Xturned out to be extremely useful for debugging.  Rather than inserting
Xprintf statements in existing code and recompiling, it is possible to create
Xa debug module that simply prints out variables when they change.  We have
Xalso used it for a graphics monitor that continuously displays the state of
Xthe entire system.  This wakeup attribute forces a queuing discipline on a
Xvariable.
X.PP
X.I
Xnon-exclusive write.
X.R
XVariables can be declared read or write, exclusively or
Xnonexclusively.  By default, writes are exclusive, meaning that only one
Xprocess can write a variable during its lifetime.  Non-exclusive write has
Xno such restriction, allowing multiple processes to write the same variable.
XOne use of this might be a server listening on a socket.  Any client can
Xrequest service by writing the variable associated with the socket.  Another
Xuse is to allow a debug process to insert a new value without modifying the
Xvariable's normal writer.
X.PP
X.SH
XII   4.2BSD Implementation
X.PP
XThe Shared Variable System consists of three layers.  The lowest level
Xsimulates a common memory facility such as is available in System V.  On top
Xof this are the procedures that transform common memory into common
Xvariables.  Above this is another level that provides handshaking, if
Xnecessary.  All levels run as user-level code and require no kernel
Xmodifications.
X.PP
X4.2BSD has no common memory facility.  Our system provides one via a
Xuser-level server that stores variables in its own memory space which is
Xprivate to it.  Access to variables is therefore strictly through the valid
Xrequests to the CMM server.
X.PP
XClient processes keep their own local version of the common variables until
Xit is convenient for them to synchronize with the CMM.  By calling
X.I cm_sync() ,
Xthe user and the common memory server become synchronized.  This
Xcall is noninterruptible for the CMM since the server completely processes
Xrequests in the order that they are received.
X.PP
XThe Shared Variables System is written in C and uses the 4.2 interprocess
Xcommunication system.  Thus, the common memory as well as the clients can be
Xdistributed.  An interface exists for Franz Lisp.
X.PP
X.SH
XIII   An application - manufacturing automation
X.PP
XWhile the CMM is a general tool, it is currently being used at the National
XBureau of Standards in the Automated Manufacturing Research Facility, a
Xproject in manufacturing automation.  This project requires communication
Xbetween stationary and mobile robots, machine tools, their controllers, and
Xhigher-level cell and factory management computers.  The processes are
Xorganized in a control hierarchy, much like the organization of a
Xconventional factory.
X.PP
XFor the purposes of hierarchical control, one can imagine that processes in
Xthe system communicate along hierarchical channels.  Each process runs
Xasynchronously, communicating only with its superior and subordinate
Xprocesses.
X.PP
XLogically however, communication is performed using the common memory model.
XFor example, a process sending commands to a subordinate writes the
Xinformation in a well-known common memory variable.  The subordinate reads
Xthe information out of the common memory variable, noting whether or not it
Xcontains a new value.
X.PP
XIn reality, there are small groups of processes and processors that directly
Xcommunicate with each other, some using physical common memory and some
Xsimulating common memory.  Each cluster representing a piece of common
Xmemory can choose to replicate any other portion of another cluster's common
Xmemory.  An underlying network invisibly supplies this service of keeping
Xall the common memories synchronized.  The result is effectively one of a
Xconsolidated common memory, with a very consistent and easy means of
Xcommunication between processes.
END_OF_FILE
if test 6265 -ne `wc -c <'doc/usenix/abstract.trf'`; then
    echo shar: \"'doc/usenix/abstract.trf'\" unpacked with wrong size!
fi
# end of 'doc/usenix/abstract.trf'
fi
if test -f 'src/cm_usr1.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/cm_usr1.c'\"
else
echo shar: Extracting \"'src/cm_usr1.c'\" \(8111 characters\)
sed "s/^X//" >'src/cm_usr1.c' <<'END_OF_FILE'
X/*
X
XThese are the user functions uses in manipulating common memory variables.
XThe primary functions are:
X
Xcm_init(process_name,host,debug_level)
Xcm_declare(name,type,role,period)
Xcm_undeclare(variable)
Xcm_sync(wait)
Xcm_exit()
X*/
X
X/* real code begins here */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <strings.h>
X#include <sys/file.h>	/* for open(/dev/null) hack */
X#include "inet.h"
X
X#include "cm_constants.h"
X#include "cm_sd.h"
X#include "cm_var.h"
X#include "cm_interface.h"
X#include "cm_sync.h"
X#include "cm_slot.h"
X#include "cm_msg.h"
X#include "cm_time.h"
X
Xextern int errno;
X
Xstatic struct msg *omsg, *imsg;
X
Xint server_socket;
Xint maxfds;
X
Xint cm_init();
Xcm_variable *cm_declare();
Xcm_variable *firstuservar(), *nextuservar();
Xcm_write();
Xstruct user_variable *cm_read();
Xcm_sync();
Xstruct slot *nextslot();
Xcm_variable *get_variable();
X
Xchar *malloc();
Xstatic composemsg();
X
X/* real definitions begin here */
X
Xint
Xcm_init(name,host,debug_level)
Xchar *name;
Xchar *host;	/* host running cmm.  0 or "" means local host */
Xint debug_level;
X{
X	set_cm_debug_level(debug_level);
X	set_cm_process_name(name);
X	if (0 > (server_socket = initport(PORT_NUMBER(CM_PORT),CLIENT,
X			SOCK_STREAM,host))) return(E_CM_INIT_FAILED);
X
X	maxfds = getdtablesize();
X	cm_time_init();
X
X	if (!(imsg = (struct msg *)malloc(CM_MSGSIZE))) {
X		fprintf(stderr,"init: failed malloc(imsg)\n");
X		exit(-1);
X	}
X	if (!(omsg = (struct msg *)malloc(CM_MSGSIZE))) {
X		fprintf(stderr,"init: failed malloc(omsg)\n");
X		exit(-1);
X	}
X	return(0);
X}
X
X/* the following call is important if you plan on restarting cm connections
Xserveral times in one process.  It runs around deallocating memory that has
Xbeen allocated by cm_sd_copy.
X*/
Xcm_exit()
X{
X	cm_variable *v;
X
X	close(server_socket);
X
X	for (v=next_user_variable((struct user_variable *)0);v;
X					v=next_user_variable(v)) {
X		if (!v->count) continue;	/* never set */
X		cm_sd_free(&v->data);
X	}
X}
X
Xcm_variable *
Xcm_declare(name,role)
Xchar *name;
Xunsigned int role;		/* READER, WRITER, XWRITER, WAKEUP */
X{
X	cm_variable *v;
X
X	if (!(v = get_variable(name))) {
X	    fprintf(stderr,"cannot get_variable(%s)\n",name);
X	    return(NULL);
X	}
X	if (role) {
X	    /* you gave no way to "or out" these bits, you twit! */
X	    if (role & CM_ROLE_READER) v->role.reader = TRUE;
X	    if (role & CM_ROLE_XWRITER) v->role.xwriter = TRUE;
X	    if (role & CM_ROLE_NONXWRITER) v->role.nonxwriter = TRUE;
X	    if (role & CM_ROLE_WAKEUP) v->role.wakeup = TRUE;
X	}
X/*	if (cmd_assoc != 0) v->command_association = cmd_assoc;*/
X
X	v->status.declared = TRUE;
X	return(v);
X}
X
Xcm_undeclare(v)
Xcm_variable *v;
X{
X	v->status.undeclared = TRUE;
X}
X
X/* returns negative for failure or 0 for success */
Xcm_sync(wait_opts)
Xint wait_opts;
X{
X	int slots;	/* number of slots in incoming message */
X	int query;	/* TRUE, if we are requesting an ack from the cmm */
X	int rc = 0;
X	int cc;
X	int selector;
X	struct slot *s;
X	int processed_msg = FALSE;	/* true if we have received and
X					   processed at least one msg in
X					   this call to cm_sync */
X
X	if ((0 == message_waiting(server_socket))
X	    && (wait_opts & CM_WAIT_READ)) {
X		/* no messages waiting, so provoke the cmm to give us some */
X		query = TRUE;
X	} else query = FALSE;
X
X	composemsg(omsg);
X        if (query) omsg->read_wait = TRUE;
X	if (omsg->slots || query) {
X	    print_msg(omsg);
X	    cc = sized_write(server_socket,omsg,omsg->size);
X	    if (cc != omsg->size) {
X		fprintf(stderr,"%s%s\n","failed to send msg to cmm.  ",
X			"cmm disappeared?");
X		exit(0);
X	    }
X	    eprintf(2,"sent msg to server (%d)\n",cc);
X	} /* no slots in msg, so no msg not sent to server */
X	while (TRUE) {
X	    /* poll first */
X	    selector = 1<<server_socket;
X	    if (-1 == (cc = message_waiting(server_socket))) {
X		rc = E_CMM_DIED;
X		break;
X	    }
X	    /* wait rules are as follows:
XWAIT:		WAIT_AT_MOST_ONCE		WAIT_FOR_ALL
Xno msg waiting	select/return			select/return
X   msg waiting	select/return			select/select
X
XNO_WAIT:	WAIT_AT_MOST_ONCE		WAIT_FOR_ALL
Xno msg waiting	return/return			return/return
X   msg waiting	select/return			select/select
X
XWhere x/y means:
XDo x if no messages have been processed in this call to cm_sync, and 
XDo y if at least one message has been processed in this call to cm_sync.
X
XFor example:
XIf user specified NO_WAIT|WAIT_AT_MOST_ONCE, no message is waiting and
Xat least one message has been processed, we return.
X*/
X	    /* catagorize by the conditions that return, which are
X               implemented by "break".
X	     */
X	    if (wait_opts & CM_NO_WAIT) {
X		if (cc == 0) break;	/* no msg waiting */
X	    } else if (wait_opts & CM_WAIT_AT_MOST_ONCE) {
X		if (processed_msg) break;
X	    } else if (processed_msg && (cc == 0)) break;
X
X	    /* That was amazingly simpler to code then to say!!! */
X		
X	    /* wait until someone responds */
X	    selector = 1<<server_socket;
X	    if (-1 == select(maxfds,&selector,(int *)0,(int *)0,
X	    				(struct timeval *)NULL)) {
X		rc = E_CMM_DIED;
X		break;
X	    } else {
X		if (0 >= (cc = sized_read(server_socket,imsg,CM_MSGSIZE))) {
X		    rc = E_CMM_DIED;
X		    break;
X		}
X		processed_msg = TRUE;
X		s = imsg->data;
X		print_msg(imsg);
X		if (imsg->version != CMM_VERSION) {
X			if (imsg->version > CMM_VERSION) {
X				fprintf(stderr,"cm library (v%d) is %s than cmm (v%d)",
X					CMM_VERSION,
X					((imsg->version > CMM_VERSION)?"older":"newer"),
X					imsg->version);
X			}
X			return(E_CM_WRONG_VERSION);
X		}
X
X		slots = imsg->slots;
X		s = imsg->data;
X		for (;;) {
X			if (s == NULL || slots == 0) break;
X			if (0 > user_decode_slot(s)) {
X			   printf("bad slot encountered...aborting msg \n");
X			   break;
X			}
X			eprintf(6,"slot = %x  ",s);
X			eprintf(6,"slot->name %s  ",s->s_name);
X			eprintf(6,"slot->size %x\n",s->s_size);
X
X			s = nextslot(imsg,s);
X			slots--;
X		}
X	    }
X	}
X	return(rc);
X}
X
X/* returns -1 if cmm died, 0 if no message waiting, >0 if message waiting */
Xstatic int
Xmessage_waiting(fd)
Xint fd;
X{
X	int selector = 1<<fd;
X
X	return(select(maxfds,&selector,(int *)0,(int *)0,&cm_period_zero));
X}
X
X/*
Xgo through our variables looking for ones that we have written or declared.
XStuff them all into a buffer and send this message to the cmm.
XBy looking at all variables only once, we get rid of duplicates.  I.e. the
Xuser may have written a value more than once before syncing.
X*/
Xstatic composemsg(m)
Xstruct msg *m;	/* outgoing msg */
X{
X	int size;
X	cm_variable *v;
X
X	init_msg(m);
X
X	for (v=next_user_variable((struct user_variable *)0);v;
X					v=next_user_variable(v)) {
X		if (v->status.declared) {
X			eprintf(6,"adding declare slot for %s\n",v->name);
X			size = put_slot_declare(m,v->name,
X/*				&v->role,v->command_association); */
X				&v->role,0);
X			if (size > 0) v->status.declared = FALSE;
X		}
X		if (v->status.written) {
X			eprintf(6,"adding write slot for %s\n",v->name);
X			size = put_slot_write(m,v->name,&v->data,
X				v->command_association);
X			if (size > 0) v->status.written = FALSE;
X		}
X		if (v->status.undeclared) {
X			eprintf(6,"adding undeclare slot for %s\n",v->name);
X			size = put_slot_undeclare(m,v->name);
X			if (size > 0) {
X				cm_sd_free(&v->data);
X				v->status.inuse = FALSE;
X			}
X		}
X	}
X#if 0
X	/* this should be last, because when the manager receives it, */
X	/* any current values going into the message queue are zapped. */
X	/* This way, the user will not get any duplicate values. */
X	if (query) put_slot_read(m);
X#endif 
X}
X
Xcm_print_variable(name)
Xchar *name;
X{
X	cm_variable *v;
X
X        if (!(v = get_variable(name))) {
X            fprintf(stderr,"cannot get_variable(%s)\n",name);
X	}
X
X	printf("name = %s\n",v->name);
X	printf("role.reader = %d\n",v->role.reader);
X	printf("role.nonxwriter = %d\n",v->role.nonxwriter);
X	printf("role.wakeup = %d\n",v->role.wakeup);
X	printf("role.xwriter = %d\n",v->role.xwriter);
X	printf("count = %d\n",v->count);
X	printf("cmd assoc = %d\n",v->command_association);
X	printf("status.inuse = %d\n",v->status.inuse);
X	printf("status.written = %d\n",v->status.written);
X	printf("status.declared = %d\n",v->status.declared);
X	printf("status.undeclared = %d\n",v->status.undeclared);
X}
END_OF_FILE
if test 8111 -ne `wc -c <'src/cm_usr1.c'`; then
    echo shar: \"'src/cm_usr1.c'\" unpacked with wrong size!
fi
# end of 'src/cm_usr1.c'
fi
if test -f 'src/franz/config.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/franz/config.h'\"
else
echo shar: Extracting \"'src/franz/config.h'\" \(16909 characters\)
sed "s/^X//" >'src/franz/config.h' <<'END_OF_FILE'
X/*                                      -[Wed Jun 12 08:00:31 1985 by jkf]-
X *      config.h                        $Locker:  $
X * configuration dependent info
X *
X * $Header: config.h,v 40.37 85/07/21 10:49:29 layer Exp $
X *
X * (c) copyright 1982, Regents of the University of California
X * Enhancements (c) copyright 1984, Franz Inc., Oakland California
X */
X 
X/* 
X * this file contains three types of parameters
X *  1) those parameters which each site may wish to modify to
X *     personalize lisp.
X *  2) those flags which are machine or operating system dependent
X *     and thus which should not be modified.
X *  3) those which should be of type (1) but by the time the customer
X *     has received lisp, the setting of the flag has been hardwired
X *     in.  We are trying to reduce flags of this type, but a few still
X *     exist.
X *
X * In the description of each flag we will note its class from the
X * list above (class 1,2 or 3).
X *
X */
X
X/*
X** The type of machine and os this is to run on will come from
X** the file lconf.h.  
X*/
X# ifndef NoLconf
X#  include "lconf.h"
X# endif
X
X
X/* GCSTRINGS - (class 3)
X *  define this if you want the garbage collector to reclaim
X *  strings.  It is not normally set because in typical applications the
X *  expense of collecting strings is not worth the amount of space
X *  retrieved
X */
X 
X/* #define GCSTRINGS */
X
X/*
X * NAMESIZE (class 1) - size of the internal stack for holding
X * local (non-special) variables.  The size allocated is
X * NAMESIZE + 4*NAMEINC, the stack growing by NAMINC each
X * time it overflows (so that overflows can be handled in lisp).
X * NAMINC is a class 1 constant.
X */
X#define NAMESIZE 3072
X#define NAMINC 50
X
X/*
X * MVR (class 3)  handle multiple value returns
X */
X#define MVR
X#define MMVR 20
X
X/*
X** HASHT (class 3) include the hash table package
X*/
X#define HASHT
X
X/* DOMAIN - (class 3) this is put on the (status features) list and
X *      is the value of (status domain)
X */
X#define DOMAIN  "franz-inc"
X
X/*
X * Machine/os dependent flags:          (class 2)
X * All flags are of the ifdef/ifndef variety.
X * Flags whose names are followed by * must be specified with a value.
X * Some flags have default values, when noted.
X * Only those flags so documented may be changed by the customer
X * The meaning of the flags are as follows
X * machine (choose one):
X *   m_vax      a vax of any type (780,750, etc)
X *   m_68k      a 68000 or 68010
X * os (choose one if in the list, or else omit)
X *   os_vms     dec's vms os
X *   os_masscomp  masscomp's version of 4.2
X *   os_sun4    sun 4bsd or newer
X *   os_unisoft unisoft variant
X * OFFSET*      lowest address (where nil will be stored)
X * TTSIZE*      max number of 512 bytes pages for text and data
X *              !This may be altered!
X * XstackSize   size of alternate stack if SPISFP is defined
X * unisys3botch something is wrong with initialization of data space
X * SPISFP       the stack pointer is also the frame pointer, so we can't
X *              push tempories on the C stack (we have to use an alternate
X *              stack)
X * NPINREG      np and lbot are stored in registers rather than global
X *              variables
X * DefDmpmode*  default dump mode (expressed in octal-like decimal)
X * NILIS0       for any UNIX implementation in which the users
X *              address space starts at 0 (and liszt takes advantage
X *              of the fact)
X * StringTable  when the a.out format uses a string table for symbol
X *              names
X * Vadvise      if system has vadvise system call, this is the location
X *              of the vadvise include file.
X * OS*          name of the operating system for the (status features)
X *              list
X * Pagesize*    the size in bytes of a page of memory. Can be specified
X *              as a function to return the value.
X *
X * Machine*     name of the processor
X * LongFilenames  if this machine has very long filenames
X * Feature      something to be put on the status features list
X * vms_cfasl    for vms, do a cfasl of a native vms object file (i believe)
X * NewLineBuf   if it uses the setlinebuf call to set buffering for stdio
X * RIndexFcn*   function to call to get the location of a given char
X *		  from the end of the string.
X * IndexFcn*    function to call to get the location of a given char
X * machine name (choose one):
X *   GetHostname  has the 4.2 gethostname function
X *   os_masscomp  [this was mentioned above] use the masscomp version
X *               of gethostname
X *   SiteName    a string which is the site name, if it must be hardwired
X *               !this may be altered!
X * CallBrkdirect if must do a brk with a system call rather than the
X *               C function.
X * UseSetmask    signals are reenabled with sigsetmask rather than
X *               calling 'signal' again (a 4.2 feature)
X * unisys3botch  has a problem in clearing low memory (mysterious flag
X *               which should be better named)
X * SunCore       has suncore
X * Subxlbotch    operands of subxl exchanged [I'm guessing at this, it
X *               was underdocumented in the code]
X * NoSysTime    <sys/time.h> is <time.h>
X * NoSyscall    doesn't have a syscall function
X * NoAsm	doesn't have the function "asm" to insert assembler into the
X *		output of the C compiler (like the cadmus).
X * AsmOnly	This macro is defined in files which are really
X		assembler files and the C pre-processor is being used.
X		(The purpose is to keep typedef's from being passed
X		to the assembler.)
X * NoGarbColl	Turns off garbage collection permanently.
X		(This is done by making sure that Initflag does not
X		get changed in sysat.c
X *
X * unchar  [default 'unsigned char'] the data type to use for unsigned
X *              character.  This is necessary since some C compilers can't
X *              handle 'unsigned char'.
X * word		An integer type (long, int, or short) whose size is the
X		same as that of a pointer so that casts back and forth
X		to (int *) or (char *) will not lose information.
X * int32	An integer type (long, int, or short) whose size is 
X		32 bits.
X *
X * xor          [ default uses ^] a macro to xor two quantities.  This is
X *              necessary on ibm systems which use the caret for something
X *              else
X * Incl_ctype   [default: defined] if defined then explicitly include
X *              <ctype.h>
X *              
X * IobName      Indicates that a special machine and/or O.S. routine must be
X *              called to determine the locained] has floating point
X * HasTermcap   [default: defined] has interface to termcap
X *   ...        stdio flag conversions:  this should not be here when
X *              we are done moving stdio
X * DefLibDir    default lisp library directory
X * NoLowInput	Indicates that no attempt should be made to interfere with
X *		low level input routine (read). Otherwise, read is
X *		rewritten in 68k.c
X * NoLowOutput  Indicates that no attempt should be made to interfere with
X *		low level output routine (write). Otherwise, write is
X *		rewritten in 68k.c
X * Savelisp	needs the save/restorelisp feature [apollo]
X * UseOnlySdotsForBignums
X *		if defined, use only sdots for constructing bignums.
X *		This is for the apollo, and if Savelisp is defined, then
X *		this also MUST be defined. [apollo]
X * NoDumpLisp	this machine cannot support (dumplisp) [apollo]
X * NoNlist	this machine does not have the UNIX function nlist() [apollo]
X * ReadDir	If defined, uses unix system call to read a directory
X *		listing.
X */
X
X/* defaults */
X
X#define unchar unsigned char
X#define xor(a,b)   (a) ^ (b)
X#define Incl_ctype 
X#define GcTime
X#define HasFloat
X#define HasTermcap
X#define XstackSize 16384
X#define DefLibDir "/usr/lib/lisp"
X
X/*
X** If more machines have weird integer sizes,
X** these typedefs will have to be moved.
X*/
X#ifndef AsmOnly
X#ifdef Elxsi
X	typedef int int32;
X	typedef int word;
X#else
X#ifdef lint
X	typedef int int32;
X	typedef int word;
X#else
X	typedef long int32;
X	typedef long word;
X#endif lint
X#endif Elxsi
X#endif AsmOnly
X
X#ifdef vax_4_1
X# define m_vax
X# define OFFSET 0x0
X# define TTSIZE 6120
X# define NPINREG
X# define DefDmpmode 413
X# define NILIS0
X# define StringTable
X# define Vadvise <vadvise.h>
X# define OS "unix"
X# define Pagesize 1024
X# define Machine "vax"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "unknown-site"
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef vax_4_1a
X# define m_vax
X# define OFFSET 0x0
X# define TTSIZE 6120
X# define NPINREG
X# define DefDmpmode 413
X# define NILIS0
X# define StringTable
X# define Vadvise <vadvise.h>
X# define OS "unix"
X# define Pagesize 1024
X# define Machine "vax"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define GetHostname
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef vax_4_1c
X# define m_vax
X# define OFFSET 0x0
X# define TTSIZE 6120
X# define NPINREG
X# define DefDmpmode 413
X# define NILIS0
X# define StringTable
X# define Vadvise <vadvise.h>
X# define OS "unix"
X# define Pagesize getpagesize()
X# define Machine "vax"
X# define LongFilenames
X# define IndexFcn index
X# define RIndexFcn rindex
X# define GetHostname
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef vax_4_2
X# define m_vax
X# define OFFSET 0x0
X# ifdef HOLE
X#   define TTSIZE 10216
X# else
X#   define TTSIZE 6120
X# endif
X# define NPINREG
X# define DefDmpmode 413
X# define NILIS0
X# define StringTable
X# define Vadvise <sys/vadvise.h>
X# define OS "unix"
X# define Pagesize getpagesize()
X# define Machine "vax"
X# define LongFilenames
X# define IndexFcn index
X# define RIndexFcn rindex
X# define GetHostname
X# define CallBrkdirect
X# define UseSetmask
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef vax_unix_ts
X# define m_vax
X# define OFFSET 0x0
X# define TTSIZE 6120
X# define NPINREG
X# define DefDmpmode 410
X# define NILIS0
X# define OS "unix"
X# define Pagesize 1024
X# define Machine "vax"
X# define IndexFcn strchr
X# define RIndexFcn strrchr
X# define SiteName "unknown-site"
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef vax_eunice_vms
X# define m_vax
X# define os_vms
X# define OFFSET 0x0
X# define TTSIZE 10216
X# define NPINREG
X# define DefDmpmode 413
X# define NILIS0
X# define StringTable
X# define OS "vms"
X# define Pagesize 512
X# define Machine "vax"
X# ifndef EUNICE_UNIX_OBJECT_FILE_CFASL
X#   define vms_cfasl
X# endif
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "unknown-site"
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef sun_unisoft
X# define m_68k
X# define os_unisoft
X# define OFFSET 0x40000
X# define TTSIZE 6120
X# define DefDmpmode 410
X# define OS "unix"
X# define Pagesize 512
X# define Machine "68k"
X# define Feature "unisoft"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "unknown-site"
X# define NoSyscall
X#define nargsdefined
X#endif
X
X#ifdef dual_unisoft
X# define m_68k
X# define os_unisoft
X# define OFFSET 0x800000
X# define TTSIZE 6120
X# define DefDmpmode 410
X# define OS "unix"
X# define Pagesize 512
X# define Machine "68k"
X# define Feature "unisoft"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "unknown-site"
X# define NoSyscall
X#define nargsdefined
X#endif
X
X#ifdef pixel_unisoft
X# define m_68k
X# define os_unisoft
X# define OFFSET 0x20000
X# define TTSIZE 6120
X# define DefDmpmode 410
X# define OS "unix"
X# define Pagesize 512
X# define Machine "68k"
X# define Feature "unisoft"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "unknown-site"
X# define NoSyscall
X#endif
X
X#ifdef lisa_unisys3
X# define m_68k
X# define os_unisoft
X# define OFFSET 0x20000
X# define TTSIZE 6120
X# define unisys3botch   1
X# define DefDmpmode 410
X# define OS "unix"
X# define Pagesize 1024
X# define Machine "68k"
X# define IndexFcn strchr
X# define RIndexFcn strrchr
X# define SiteName "unknown-site"
X# define NoSyscall
X#endif
X
X#ifdef sun_4_1c
X# define m_68k
X# define os_sun4
X# define OFFSET 0x8000
X# define TTSIZE 6120
X# define DefDmpmode 413
X# define StringTable
X# define Vadvise <vadvise.h>
X# define OS "unix"
X# define Pagesize getpagesize()
X# define Machine "68k"
X# define LongFilenames
X# define Feature "sun"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define GetHostname
X# define SunCore
X# define Subxlbotch
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef sun_4_2beta      /* sun release 1.0 */
X# define m_68k
X# define os_sun4
X# define OFFSET 0x8000
X# define TTSIZE 10216
X# define SPISFP
X# define DefDmpmode 413
X# define StringTable
X# define Vadvise <sys/vadvise.h>
X# define OS "unix"
X# define Pagesize getpagesize()
X# define Machine "68k"
X# define LongFilenames
X# define Feature "sun"
X# define NewLineBuf
X# define IndexFcn index
X# define RIndexFcn rindex
X# define GetHostname
X# define UseSetmask
X# define SunCore
X# define Subxlbotch
X#define nargsdefined
X#define ReadDir
X#endif
X
X#ifdef sun_4_2          /* sun release 1.1 */
X# define m_68k
X# define os_sun4
X# define OFFSET 0x8000
X# ifdef BIG
X#  define TTSIZE 10216
X# else
X#  define TTSIZE 6120
X# endif BIG
X# define DefDmpmode 413
X# define StringTable
X# define Vadvise <sys/vadvise.h>
X# define OS "unix"
X# define Pagesize getpagesize()
X# define Machine "68k"
X# define LongFilenames
X# define Feature "sun"
X# define NewLineBuf
X# define IndexFcn index
X# define RIndexFcn rindex
X# define GetHostname
X# define UseSetmask
X# define SunCore
X# define nargsdefined
X# define ReadDir
X#endif
X
X#ifdef mc500_2_2
X# define m_68k
X# define os_masscomp
X# define growstackbotch
X# define OFFSET 0x0
X# define TTSIZE 6120
X# define DefDmpmode 413
X# define StringTable
X/* seems to have disappeared # define Vadvise <sys/vadvise.h> */
X# define OS "unix"
X# define Machine "68k"
X/* # define LongFilenames */
X# define Feature "masscomp"
X# define IndexFcn strchr
X# define RIndexFcn strrchr
X# define Pagesize 4096
X# define NoSysTime
X# define ReadDir
X#endif
X
X#ifdef pegasus
X# define m_68k
X# define OFFSET 0
X# define TTSIZE 6120
X/* SPISFP isn't quite true, but due to the way in which there is a phantom
X * spot on the stack, it is probably better that we use it
X */
X# define growstackbotch
X# define SPISFP
X# undef  XstackSize
X# define XstackSize 22000
X# define DefDmpmode 413
X# define OS "uniflex"
X# define Machine "68k"
X# define Feature "tek4404"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "fi test machine"
X# define NoSysTime
X# define NoSyscall
X# define NoAsm
X# undef HasTermcap
X# undef DefLibDir
X# define DefLibDir "/lisp/lib"
X/* stdio conversions */
X# define _IOREAD _READ
X# define _IOWRT  _WRITE
X# define _IONBF  _UNBUF
X# define _IOSTRG 0
X# define _file _fd
X/* end stdio conversions */
X# define ReadDir
X# define NoLowInput
X#endif
X
X#ifdef ibm_cms
X# define m_ibm
X# define os_cms
X# define OFFSET ((int)&nilatom)
X# define TTSIZE 6120
X# define SPISFP
X# define DefDmpmode 0407
X# define OS "cms"
X# define Pagesize 512
X# define Machine "ibm"
X# define IndexFcn "index"
X# define RIndexFcn "rindex"
X# define SiteName "ucb cms"
X# define NoSyscall
X/* it is a bug that unsigned char doesn't exist */
X# undef unchar
X# define unchar char
X/* must define xor on ibm system to be the cent sign */
X# include "ibmxor.h"
X/* defined in stdio.h on ibm */
X# undef Incl_ctype
X# define IobName
X# undef HasTermcap
X#endif
X
X#ifdef cadmus
X# define m_68k
X# define OFFSET 0x600000
X# define CadmusOsBugs
X# ifdef CadmusOsBugs
X# define TTSIZE 8196
X# else
X# define TTSIZE 6120
X# endif CadmusOsBugs
X# define DefDmpmode 411
X# define OS "unix"
X# define Pagesize 1024
X# define Machine "68k"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "unknown-site"
X# define NoSyscall
X# define NoSysTime
X# define NoAsm
X# define Subxlbotch
X# define Feature "cadmus"
X#endif
X
X#ifdef apollo
X# define m_68k
X# define Savelisp
X# define NoDumpLisp
X# define NoNlist
X# define UseOnlySdotsForBignums
X# define SPISFP
X# define OFFSET ((int) &nilatom)
X# define TTSIZE 6120
X# define OS "aegis"
X# define Pagesize 1024
X# define Machine "68k"
X# define IndexFcn index
X# define RIndexFcn rindex
X# define SiteName "unknown-site"
X# define Feature "apollo"
X# define NoSyscall
X# define NoSysTime
X# define NoAsm
X# define NoLowInput	/* to avoid _read, _write */
X# define NoLowOuput	/* to avoid _read, _write */
X# define NewLineBuf
X/* stdio conversions */
X# define _IOREAD _SIRD
X# define _IOWRT  _SIWR
X# define _IONBF  _SIUNB
X# define _IOSTRG 0
X# define _file _fd
X/* things written in asm can't have upper case in routine names */
X# define Fixzero fixzero
X# define Ifuncal ifuncal
X# define Ipushf ipushf
X# define Lastfix lastfix
X# define Undeff undeff
X#endif
X
X#ifdef Elxsi
X/* do not replace "Elxsi" with "elxsi" because elxsi is defined
X   in the C preprosessor */
X# define SPISFP
X# define OFFSET 0x800
X# define Machine "elxsi"
X# define OS "enix"
X# define _IOSTRG _IOMYBUF
X# define NoLowInput	/* to avoid _read, _write */
X# define NoLowOutput
X# define NoSysTime
X# define NoGarbColl
X# define gstart() OFFSET  /* ??? */
X# define Pagesize 1024
X# define PAGSIZ 1024
X# define TTSIZE 6120
X# define DefDmpmode 410
X# define IndexFcn strchr
X# define RIndexFcn strrchr
X# define SiteName "unknown-site"
X#endif
X
X#ifdef m_68k
X#define SortSymbols
X#endif
X
END_OF_FILE
if test 16909 -ne `wc -c <'src/franz/config.h'`; then
    echo shar: \"'src/franz/config.h'\" unpacked with wrong size!
fi
# end of 'src/franz/config.h'
fi
if test -f 'src/man.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/man.c'\"
else
echo shar: Extracting \"'src/man.c'\" \(11451 characters\)
sed "s/^X//" >'src/man.c' <<'END_OF_FILE'
X/* top level common memory manager */
X
X/*
X
Xthis is a server process that manages common memory - or at least what
Xbehaves like common memory.  It is actually quite simple.  For each object
Xthat we are keeping track of we allocate a piece of memory via declare().
XWe write the object with writevalue() and read it with readvalue()
X
XInitially, we sit idle waiting for messages.  Each message is processed
Xas follows:
X
Xforever {
X	for each message in queue
X		for each component in message
X			declares are created or confirmed
X			reads cause a new message to be built
X			writes cause processes to be tagged for wakeup.
X	acknowledge every message, by sending back a list of variable
X	values that the process is listed as a reader which have been written
X	
X	for each process scheduled for wakeup {
X	    and not having an outstanding read in the queue {
X		create a message with all its "read" variables
X		if (message built) send it, delete it
X	    }
X	}
X}
X
X*/
X
X#include <stdio.h>
X#include <sys/types.h>
X
X#include <sys/time.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include "inet.h"
X
X#include "cm_constants.h"
X#include "cm_var.h"
X#include "cm_sd.h"
X#include "cm_slot.h"
X#include "cm_msg.h"
X#include "cm_man.h"
X#include "cm_time.h"
X
X#include <setjmp.h>
X#include <signal.h>
X
Xstruct process processes[CM_MAXPROCESSES];
Xstruct variable variables[CM_MAXVARIABLES];
Xcm_value cm_variable_names = {0,0,0,1};
X
Xint connection_socket;
X
Xstatic struct msg *cm_imsg;
Xstruct msg *cm_omsg;	/* shared with man_get_slot.c .. ugh! */
X
Xstruct slot *nextslot();
X
X#define CM_WRITE_TIMEOUT	5	/* after this many seconds, we give */
X					/* up writing to a process, and go */
X					/* on.  We will eventually retry. */
Xstatic int timeout = CM_WRITE_TIMEOUT;
Xjmp_buf write_context;	/* context when we are about to call write() */
Xvoid sigpipe_handler();
Xvoid sigalrm_handler();
X
Xchar *malloc();
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X	init(argc,argv);
X	while (TRUE) {
X	    process_msg();
X	}
X}
X
Xprocess_msg()
X{
X	struct slot *s;
X	int clientfd;
X	int slots;	/* number of slots in message */
X
X	int rc;
X
X	clientfd = man_get_msg(cm_imsg);
X	init_msg(cm_omsg);
X	if (cm_imsg->version == CMM_VERSION) {
X		slots = cm_imsg->slots;
X		s = cm_imsg->data;
X		while (TRUE) {
X			if (s == NULL || slots == 0) break;
X			if (0 > (rc = man_decode_slot(s,clientfd))) {
X				printf("process_msg: error trying to decode slot <%d>...aborting msg\n",rc);
X				break;
X			}
X			s = nextslot(cm_imsg,s);
X			slots--;
X		}
X	} else {
X		char error_message[80];
X
X		if (cm_imsg->version > CMM_VERSION)
X		sprintf(error_message,"cm library (v%d) is %s than cmm (v%d)",
X			cm_imsg->version,
X			((cm_imsg->version > CMM_VERSION)?"newer":"older"),
X			CMM_VERSION);
X		put_slot_error(cm_omsg,"version",0,error_message);
X	}
X
X
X	/* if a slot produced an explicit message (like an error), send it. */
X	/* otherwise assume he just wants normal variable updates */
X	/* Also, respond immediately to anyone waiting on an immediate
X	/* response. */
X	if ((cm_omsg->slots > 0) || cm_imsg->read_wait) {
X		add_read_vars_to_msg(cm_omsg,clientfd);
X		send_msg_to(cm_omsg,clientfd);
X		processes[clientfd].wakeup = FALSE;
X	}
X/* why is this necessary, if you figure it out, comment this! */
X/*	else processes[clientfd].wakeup = TRUE;*/
X	wakeup();	/* any processes necessary */
X}
X
Xwakeup()
X{
X    int i=0;
X
X    eprintf(3,"processes to wake:\n");
X    for (i=0;i<CM_MAXPROCESSES;i++) {	/* step through all processes */
X	if (processes[i].wakeup) {
X		eprintf(3," %s",processes[i].name);
X		init_msg(cm_omsg);
X		add_read_vars_to_msg(cm_omsg,i);
X		if (cm_omsg->slots > 0) send_msg_to(cm_omsg,i);
X		processes[i].wakeup = FALSE;
X	}
X    }
X    eprintf(3,"\n");
X}
X
Xextern char *optarg;	/* pointer to start of option argument */
Xextern int optind;	/* index of argv of next argument to be processed */
X
X/* initialize the process and variable structures */
Xinit(argc,argv)
Xint argc;
Xchar **argv;
X{
X	int i;
X	int debug_level = 0;
X	int c;
X	int port = CM_PORT;
X	extern int optind;
X
X	while (EOF != (c = getopt(argc,argv,"d:p:t:"))) {
X		switch (c) {
X		case 'd':
X			debug_level = atoi(optarg);
X			break;
X		case 'p':
X			port = atoi(optarg);
X			break;
X		case 't':	/* timeout */
X			timeout = atoi(optarg);
X			break;
X		}
X	}
X
X	set_cm_debug_level(debug_level);
X	set_cm_process_name(CM_MANAGER_NAME);
X
X	eprintf(1,"cmm version %d\n",CMM_VERSION);
X	eprintf(1,"timeout on writes = %d\n",timeout);
X
X	if (-1 == (connection_socket = initport(PORT_NUMBER(port),SERVER,SOCK_STREAM,
X		(char *)0))) {
X		fprintf(stderr,"failed to initialize connection socket\n");
X		exit(-1);
X	}
X	cm_time_init();
X
X	if (!(cm_imsg = (struct msg *)malloc(CM_MSGSIZE))) {
X		fprintf(stderr,"init: failed malloc(imsg)\n");
X		exit(-1);
X	}
X	if (!(cm_omsg = (struct msg *)malloc(CM_MSGSIZE))) {
X		fprintf(stderr,"init: failed malloc(omsg)\n");
X		exit(-1);
X	}
X
X	for (i=0;i<CM_MAXPROCESSES;i++) processes[i].inuse = FALSE;
X
X	for (i=0;i<CM_MAXVARIABLES;i++) {
X		cm_sd_clear(&variables[i].data);
X		variables[i].readers = 0;
X		variables[i].writers = 0;
X		variables[i].xwriter = 0;
X		variables[i].count = 0;
X	}
X
X#if 0
X	/* grab a variable for maintaining the names of all other variables */
X	variables[0].writers = 1;
X	variables[0].xwriter = connection_socket;	/* reserved to us */
X
X	/* stick in the first value */
X	variables[0].count = 1;
X	cm_variable_names.size = cm_variable_name.msize =
X		strlen("cm_variable_names"+1);
X	cm_variable_names.data = new_string("cm_variable_names");
X#endif
X
X	signal(SIGPIPE,sigpipe_handler);
X	signal(SIGALRM,sigalrm_handler);
X}
X
X/* if process died while we were writing to it, abort the write() */
Xvoid sigpipe_handler()
X{
X	longjmp(write_context,SIGPIPE);	/* abort the write() */
X}
X
X/* if process hangs and we write to it enough, eventually we will block */
X/* abort the write(), when the timer goes off */
Xvoid sigalrm_handler()
X{
X	longjmp(write_context,SIGALRM);
X}
X
Xstruct variable *
Xget_variable(name)
Xchar *name;
X{
X	int i;
X	int j;
X	int free_var = -1;
X
X	for (i=0;i<CM_MAXVARIABLES;i++) {
X		if (!var_inuse(&variables[i])) {
X			free_var = i;
X		} else if (!strcmp(variables[i].name,name)) {
X			return(&variables[i]);
X		}
X	}
X
X	if (free_var == -1) {
X		/* no old definition and no space for a new one! */
X		/* this is really an exceptional condition, so let's */
X		/* print out some helpful info */
X
X		fprintf(stderr,"get_variable(%s) failed\n",name);
X		for (i=0;i<CM_MAXVARIABLES;i++) {
X			printf("%d: ",i);
X			if (var_inuse(&variables[i])) {
X				printf("<%s>",variables[i].name);
X				printf(" r=%d  w=%d\n  inuse=%d",
X					variables[i].readers,
X					variables[i].writers,
X					var_inuse(&variables[i]));
X			} else printf("<not in use>\n");
X		}
X		return(NULL);
X	}
X
X
X	strcpy(variables[free_var].name,name);
X	variables[free_var].count = 0;
X	time_now(&variables[free_var].timestamp);
X	variables[free_var].command_association = 0;
X	variables[free_var].xwriter = CM_NULL_PROCESS;
X	for (j=0;j<CM_MAXPROCESSES;j++) {
X		variables[free_var].role[j].reader = FALSE;
X		variables[free_var].role[j].writer = FALSE;
X		variables[free_var].role[j].wakeup = FALSE;
X		variables[free_var].role[j].new = FALSE;
X	}
X	return(&variables[free_var]);
X}
X
Xsend_msg_to(m,to)
Xstruct msg *m;
Xint to;
X{
X	int rc;
X
X	switch (rc = setjmp(write_context)) {
X	case 0:
X		/* assume write() succeeds */
X		alarm(timeout);
X		sized_write(to,(char *)m,m->size);
X		alarm(0);
X		print_msg(m);
X		break;
X	case SIGPIPE:
X		alarm(0);
X		/* if write() interrupted due to SIGPIPE */
X		eprintf(3,"broken pipe\n");
X		eprintf(3,"detected death of %s on fd %d\n",
X			(processes[to].inuse?
X				processes[to].name:"unknown"),
X				to);
X		kill_client(to);
X		break;
X	case SIGALRM:
X		fprintf(stderr,
X			"process %s is being antisocial on fd %d\n",
X			(processes[to].inuse?
X				processes[to].name:"unknown"),
X				to);
X		break;
X	default:
X		alarm(0);
X		printf("setjmp(write_context) = %d?!\n",rc);
X		abort();	/* should never happen */
X	}
X}
X
X/* The philosophy coded here is that the cmm always responds to a msg
Xwith the data values that are marked for read and have changed since the
Xlast time it was read */
Xadd_read_vars_to_msg(m,pin)
Xstruct msg *m;
Xint pin;
X{
X	struct variable *v;
X
X	for (v=variables;v<&variables[CM_MAXVARIABLES];v++) {
X		if (!var_inuse(v)) continue;
X		eprintf(5,"is_reader(%s,%s)=%d  is_new(%s,%s)=%d\n",
X			processes[pin].name,v->name,is_reader(pin,v),
X			processes[pin].name,v->name,is_new(pin,v));
X		if (is_reader(pin,v) && is_new(pin,v)) {
X                    put_slot_read_response(m,v->name,
X			v->count,&v->timestamp,v->command_association,&v->data);
X		    unset_new(pin,v);
X		}
X	}
X}
X
Xstatic int clientfds = 0;	/* bitmap of sockets to clients */
X
Xint	/* returns file descriptor of client */
Xman_get_msg(msg)
Xstruct msg *msg;
X{
X    int clientfd;
X    int msg_length = 0;
X
X
X    while (msg_length <= 0) {
X	eprintf(3,"known fds = %x\n",clientfds);
X	clientfd = select_server_stream(connection_socket,&clientfds);
X	msg_length = sized_read(clientfd,(char *)msg,CM_MSGSIZE);
X	eprintf(3,"received message from %s.  fd = %d length = %d\n",
X				(processes[clientfd].inuse?
X					processes[clientfd].name:"new user"),
X			clientfd,
X			msg_length);
X	print_msg(msg);
X	if (msg_length < 0) {
X		eprintf(3,"detected death of %s on fd %d\n",
X				(processes[clientfd].inuse?
X					processes[clientfd].name:"unknown"),
X					clientfd);
X		kill_client(clientfd);
X	}
X    }
X
X    if (!processes[clientfd].inuse) {
X	processes[clientfd].inuse = TRUE;
X	processes[clientfd].wakeup = FALSE;
X	strcpy(processes[clientfd].name,msg->name);
X    }
X    eprintf(2,"received msg from %s on fd %d\n",msg->name,clientfd);
X
X    return(clientfd);
X}
X
X/* delete any info we had about this client */
Xkill_client(fd)
Xint fd;
X{
X	struct variable *v;
X
X	processes[fd].inuse = FALSE;
X	close(fd);
X	eprintf(3,"known fds = %x (before kill_client)\n",clientfds);
X/*new*/	clientfds &= ~(1<<fd);
X	eprintf(3,"known fds = %x (after kill_client)\n",clientfds);
X
X	for (v=variables;v<&variables[CM_MAXVARIABLES];v++) {
X		if (!var_inuse(v)) continue;
X		unset_reader(fd,v);
X		unset_writer(fd,v);
X		unset_wakeup(fd,v);
X		/* note that the above calls change the value of var_inuse */
X		if (var_inuse(v)) continue;
X		/* var had been set, free up any space taken by it */
X		if (v->count) {
X			cm_sd_free(&v->data);
X			v->count = 0;
X		}
X	}
X}
X
X/* flag this variable as having a new value with respect to this process */
Xset_new(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	var->role[proc].new = TRUE;
X}
X
Xset_reader(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	if (!var->role[proc].reader) {
X		var->role[proc].reader = TRUE;
X		var->readers++;
X	}
X}
X
Xset_nonxwriter(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	if (!var->role[proc].writer) {
X		var->role[proc].writer = TRUE;
X		var->writers++;
X	}
X}
X
Xset_xwriter(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	var->xwriter = proc;
X	set_nonxwriter(proc,var);
X}
X
Xset_wakeup(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	var->role[proc].wakeup = TRUE;
X}
X
Xunset_wakeup(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	var->role[proc].wakeup = FALSE;
X}
X
Xunset_new(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	var->role[proc].new = FALSE;
X}
X
Xunset_reader(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	if (var->role[proc].reader) {
X		var->role[proc].reader = FALSE;
X		var->readers--;
X	}
X}
X
Xunset_writer(proc,var)
Xint proc;
Xstruct variable *var;
X{
X	if (var->role[proc].writer) {
X		if (var->xwriter == proc) var->xwriter = CM_NULL_PROCESS;
X		var->role[proc].writer = FALSE;
X		var->writers--;
X	}
X}
X
Xvar_inuse(var)
Xstruct variable *var;
X{
X	return(var->readers || var->writers);
X}
END_OF_FILE
if test 11451 -ne `wc -c <'src/man.c'`; then
    echo shar: \"'src/man.c'\" unpacked with wrong size!
fi
# end of 'src/man.c'
fi
echo shar: End of archive 3 \(of 4\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.