[comp.sources.unix] v21i085: File distribution alternative to rdist, Part02/03

rsalz@uunet.uu.net (Rich Salz) (04/10/90)

Submitted-by: Rich Salz <rsalz@bbn.com>
Posting-number: Volume 21, Issue 85
Archive-name: coda/part02

#! /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 2 (of 3)."
# Contents:  adt.c codaserver.8 file.c libvms.c server.c server.h
# Wrapped by rsalz@litchi.bbn.com on Mon Apr  9 16:49:04 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'adt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'adt.c'\"
else
echo shar: Extracting \"'adt.c'\" \(6748 characters\)
sed "s/^X//" >'adt.c' <<'END_OF_FILE'
X/*
X**  Copyright 1989 BBN Systems and Technologies Corporation.
X**  All Rights Reserved.
X**  This is free software, and may be distributed under the terms of the
X**  GNU Public License; see the file COPYING for more details.
X**
X**  Data abstractions for CODA server.
X*/
X#include "server.h"
X#include <sys/stat.h>
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: adt.c,v 2.0 90/03/23 14:40:58 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  A class maps a name to a list of strings.  It's like a recursive
X**  definition of a collection.  We keep a linked list of all our maps.
X*/
Xtypedef struct _CLASS {
X    struct _CLASS	*Next;		/* Next one			*/
X    char		*Name;		/* The name			*/
X    int			NumHosts;	/* Number of hosts in the class	*/
X    int			MaxHosts;	/* Amount of space allocated	*/
X    char		**Hosts;	/* List of hosts		*/
X    int			NumClasses;	/* Number of classes		*/
X    int			MaxClasses;	/* Amount of space allocated	*/
X    char		**Classes;	/* List of classes in the class	*/
X} CLASS;
X
X
XSTATIC CLASS	BaseClass;		/* Our linked list of classes	*/
XSTATIC STRLIST	BaseHost;		/* List of hosts in Codafile	*/
XSTATIC int	MaxItem;		/* Size of "Item" array		*/
X
X
X
X/*
X**  Remove all defined blocks, classes, and hosts.  This is not likely to
X**  be called too often, so just flush memory -- the Add...() routines
X**  share pointers so a free'ing would be a bit of work.  We use shared
X**  pointers not to save space, but to make string compares fast.
X*/
Xvoid
XResetStorage()
X{
X    BaseBlock.Next = NULL;
X    BaseClass.Next = NULL;
X    BaseHost.Next = NULL;
X    ResetItem();
X}
X
X
Xvoid
XResetItem()
X{
X    register ITEM	*I;
X    register ITEM	*Iend;
X
X    NumItem = 0;
X    if (BaseItem == NULL) {
X	/* Since we can't trust realloc(NULL, ...) yet... */
X	MaxItem = ITEM_DELTA;
X	BaseItem = NEW(ITEM, MaxItem);
X    }
X    else
X	/* Free old strings. */
X	for (I = BaseItem, Iend = &BaseItem[NumItem]; I < Iend; I++)
X	    free(I->Name);
X}
X
X
X
X/*
X**  Guarantee an instantiation of a class.
X*/
XSTATIC CLASS *
XFindOrCreateClass(Name)
X    register char	*Name;
X{
X    register CLASS	*C;
X
X    /* Rummage through the list. */
X    for (C = BaseClass.Next; C; C = C->Next)
X	if (EQ(C->Name, Name))
X	    return C;
X
X    /* Create a new class, add it to the list. */
X    C = NEW(CLASS, 1);
X    C->Next = BaseClass.Next;
X    C->Name = Name;
X    C->NumHosts = 0;
X    C->MaxHosts = HOST_DELTA;
X    C->Hosts = NEW(char*, C->MaxHosts);
X    C->NumClasses = 0;
X    C->MaxClasses = CLASS_DELTA;
X    C->Classes = NEW(char*, C->MaxClasses);
X    BaseClass.Next = C;
X    return C;
X}
X
X
X/*
X**  Add a host to a class.
X*/
Xvoid
XAddHostToClass(Name, Host)
X    register char	*Name;
X    register char	*Host;
X{
X    register char	**str;
X    register CLASS	*C;
X    register int	i;
X
X    C = FindOrCreateClass(Name);
X
X    /* Look through list of hosts, see if it's already there. */
X    for (i = C->NumHosts, str = C->Hosts; --i >= 0; str++)
X	if (EQ(Host, *str))
X	    return;
X
X    /* Have to add it; do we need more room? */
X    if (C->NumHosts == C->MaxHosts - 1) {
X	C->MaxHosts += HOST_DELTA;
X	C->Hosts = GROW(C->Hosts, char*, C->MaxHosts);
X    }
X
X    /* Add it. */
X    C->Hosts[C->NumHosts++] = Host;
X}
X
X
X/*
X**  Add a class to a class.
X*/
XSTATIC void
XAddClassToClass(C, Name)
X    register CLASS	*C;
X    register char	*Name;
X{
X    register char	**str;
X    register int	i;
X
X    /* Look through list of classes, see if it's already there. */
X    for (i = C->NumClasses, str = C->Classes; --i >= 0; str++)
X	if (EQ(Name, *str))
X	    return;
X
X    /* Have to add it; do we need more room? */
X    if (C->NumClasses == C->MaxClasses - 1) {
X	C->MaxClasses += CLASS_DELTA;
X	C->Classes = GROW(C->Classes, char*, C->MaxClasses);
X    }
X
X    /* Add it. */
X    C->Classes[C->NumClasses++] = Name;
X}
X
X
X
X/*
X**  Add a list of classes to a class.
X*/
Xvoid
XAddClassesToClass(Name, L)
X    register char	*Name;
X    register STRLIST	*L;
X{
X    register CLASS	*C;
X
X    for (C = FindOrCreateClass(Name); L; L = L->Next)
X	AddClassToClass(C, L->Value);
X}
X
X
X
X/*
X**  Return TRUE if the host is a member of the specified class.
X*/
Xint
XHostIsInClass(Host, Class)
X    register char	*Host;
X    register char	*Class;
X{
X    static char		Everyone[] = ALL;
X    register char	**str;
X    register CLASS	*C;
X    register int	i;
X
X    /* Almost everyone is in the built-in class. */
X    if (EQ(Class, Everyone))
X	return !EQ(Host, UnknownHost) && HostWasDeclared(Host);
X
X    /* Nobody is in undefined classes. */
X    for (C = BaseClass.Next; C; C = C->Next)
X	if (EQ(C->Name, Class))
X	    break;
X    if (C == NULL)
X	return FALSE;
X
X    /* Look through list of hosts, see if it's already there. */
X    for (i = C->NumHosts, str = C->Hosts; --i >= 0; str++)
X	if (EQ(Host, *str))
X	    return TRUE;
X
X    /* Recursively look through list of classes. */
X    for (i = C->NumClasses, str = C->Classes; --i >= 0; str++)
X	if (HostIsInClass(Host, *str))
X	    return TRUE;
X
X    /* Not there. */
X    return FALSE;
X}
X
X
X/*
X**  Look through the list of hosts, see if we've got that one.
X*/
Xint
XHostWasDeclared(Name)
X    register char	*Name;
X{
X    register STRLIST	*S;
X
X    /* Rummage through the list. */
X    for (S = BaseHost.Next; S; S = S->Next)
X	if (EQ(S->Value, Name))
X	    return TRUE;
X
X    return FALSE;
X}
X
X
X/*
X**  Add a host to our list of known hosts.
X*/
Xvoid
XDefineHost(Host, Class)
X    char	*Host;
X    char	*Class;
X{
X    STRLIST	*S;
X
X    S = NEW(STRLIST, 1);
X    S->Value = Host;
X    S->Next = BaseHost.Next;
X    BaseHost.Next = S;
X    AddHostToClass(Class, Host);
X}
X
X
X
X/*
X**  Look through the list of blocks, see if we've got that one.
X*/
XBLOCK *
XFindBlock(Name)
X    register char	*Name;
X{
X    register BLOCK	*B;
X
X    /* Rummage through the list. */
X    for (B = BaseBlock.Next; B; B = B->Next)
X	if (EQ(B->Name, Name))
X	    return B;
X    return NULL;
X}
X
X
X
X/*
X**  Add to the list of things sent.
X*/
Xvoid
XAddItemToList(Name, Sb)
X    char	*Name;
X    struct stat	*Sb;
X{
X    ITEM	*I;
X
X    /* Need more room? */
X    if (NumItem == MaxItem - 1) {
X	MaxItem += ITEM_DELTA;
X	BaseItem = GROW(BaseItem, ITEM, MaxItem);
X    }
X
X    I = &BaseItem[NumItem++];
X    I->Name	= COPY(Name);
X    I->Uid	= Sb->st_uid;
X    I->Gid	= Sb->st_gid;
X    I->Directory= (Sb->st_mode & S_IFMT) == S_IFDIR;
X    I->Size	= Sb->st_size;
X    I->Time	= Sb->st_mtime;
X    I->Mode	= Sb->st_mode & 0777;
X}
X
X
XSTATIC int
XItemCompare(I1, I2)
X    ITEM	*I1;
X    ITEM	*I2;
X{
X    return strcmp(I1->Name, I2->Name);
X}
X
X
X/*
X**  Sort our array.
X*/
Xvoid
XSortItem()
X{
X    qsort((char *)BaseItem, NumItem, sizeof *BaseItem, ItemCompare);
X}
X
X
X/*
X**  Use the bsearch() routine to find the item.
X*/
XITEM *
XFindItem(Name)
X    char	*Name;
X{
X    ITEM	*I;
X    ITEM	Key;
X
X    Key.Name = Name;
X    I = (ITEM *)bsearch((char *)&Key, (char *)BaseItem,
X			(unsigned int)NumItem, sizeof *BaseItem, ItemCompare);
X    return I;
X}
END_OF_FILE
if test 6748 -ne `wc -c <'adt.c'`; then
    echo shar: \"'adt.c'\" unpacked with wrong size!
fi
# end of 'adt.c'
fi
if test -f 'codaserver.8' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'codaserver.8'\"
else
echo shar: Extracting \"'codaserver.8'\" \(5278 characters\)
sed "s/^X//" >'codaserver.8' <<'END_OF_FILE'
X.TH CODASERVER 8 LOCAL
X.SH NAME
Xcodaserver \- Code distribution aid daemon
X.SH SYNOPSIS
X.B codaserver
X[
X.B \-c
X] [
X.BI \-l file
X] [
X.BI \-r file
X] [
X.B \-t
X]
X.SH DESCRIPTION
X.I Codaserver
Xis the server for the code distribution system.
XIt should be installed on any system where you want clients to be able
Xto pick up files.
X.PP
X.I Codaserver
Xis normally started as necessary by the inet daemon.
XTo run it in an interactive test mode, use the ``\-t'' flag; this will
Xread commands from standard input and echo results to standard output.
XTo test the syntax of a codafile, use the ``\-r'' flag; this will read
Xand parse the file, and report any errors to standard output.
X.PP
X.I Codaserver
Xreports all transactions to a log file, normally
X.IR /usr/spool/log/codalog .
XTo specify a different log file, use the ``\-l'' flag; the name ``\-'' is
Xtaken to mean standard output.
XFor each in decoding the logs, every message is stamped with the process-id
Xand a sequence number.
X.PP
XSee the
X.I coda
Xmanual page for a description of the
X.I Codafile
Xlanguage.
X.PP
XThe ``\-c'' flag prints out copyright and version information.
X.SH PROTOCOL
XThe coda server uses a simple protocol running over a TCP stream.
XCommands and their responses are exchanged in ASCII, and lines are
Xterminated by a carriage-return/news line pair.
XIt is easy to debug the system by connecting to the server with a
X.IR telnet (1)
Xto the right port.
X.SS "Message Types"
X.PP
XUpon first connecting, the server will send an acknowledgement; after
Xthis the server will only reply to messages sent from the client.
XEvery non-blank line sent to the server will result in one or more
Xlines of reply that ultimately end with an
X.I ACK
Xor
X.I NAK
Xmessage.
X.PP
XAll messages from the server are preceded by a three-character code.
XIf the fourth character is a space, the server has more data to
Xsend (as in the
X.I help
Xcommand); if the fourth character is a dash, this line has the
Xserver's final reply to the client's request.
XThe following message types are sent by the server:
X.TP
X.I ACK
X.br
XThe last command successfully completed;
Xthe rest of the line may contain some information.
X.TP
X.I NAK
X.br
Xthe rest of the line should give an explanation for the failure.
X.TP
X.I INF
X.br
XInformation line intended for the user (e.g., help output).
X.TP
X.I DAT
X.br
XData lines intended for client programs (e.g., file lists).
XThe rest of the line is interpreted by the client program.
XThere is currently one type of data line, it describes a file or
Xdirectory to be loaded by the client.
XAfter the four-character identifier are the four letters
X.I ITEM
Xand a space.
XThe remainder of the line is a set of space-separated key-value
Xpairs, where the key is a single letter followed by an equal
Xsign.
XThe values are all interpreted in a Unix context; numbers are sent
Xin decimal.
XThe current keys are:
X.RS
X.nf
X	W	Type; \fId\fP for directory, \fIf\fP for file
X	N	Name of the file as text
X	U	The numeric id of the item's owner
X	G	The numeric id of the item's group
X	M	The decimal value of the item's permission bits
X	S	The size of the item in bytes
X	T	The modification date of the file
X.fi
X.RE
X.SS "Server Commands"
X.PP
XCommands consist of a word, possibly followed by an argument.
XThe command word may be in either case.
XThe coda server understands the following commands:
X.TP
X.I "GOTO dir"
X.br
XThe argument is a directory for the server to
X.IR chdir (2)
Xto.
XThis command is not as useful as it used to be.
X.TP
X.I "HELP"
X.br
XDescribe the available commands.
X.TP
X.I "HOST name"
X.br
XSet the name of the apparent destination host.
XThis command is only available in test mode.
XThe host name is converted to uppercase.
X.TP
X.I "LIST [block]"
X.br
XAfter a
X.I read
Xcommand, this will list walk through the directories listed in the
XCodafile and send a set of data line listing all the files and
Xdirectories that are applicable for the client system.
XIf no block name is given, all applicable blocks in the file are walked.
X.TP
X.I "MESG [text]"
X.br
XThe text is written to the server's log file.
X.TP
X.I "QUIT"
X.br
XThe server exits.
X.TP
X.I "READ [file]"
X.br
XRead the indicated Codafile; the default is
X.IR Codafile .
XThis resets any root value that might have been previously set,
Xand clears the item list.
X.TP
X.I "ROOT [path]"
X.br
XSet the root directory for relative pathnames.
XThis can be used to override the root after a
X.I read
Xcommand has been given.
XThe default path is 
X.IR / .
X.TP
X.I "SEND filename"
X.br
XAfter a
X.I list
Xcommand, this sends back an
X.I ACK
Xmessage, the uninterpreted bytes in the file, and a second
X.I ACK
Xmessage.
XThe client must remember (from the
X.I list
Xcommand)
Xhow big the file is so it knows how much data to expect.
XOnly items mentioned in the most recent
X.I list
Xcommand can be sent.
X.TP
X.I "USER name pass"
XLog in with the given name and password; only the
X.I help
Xand
X.I mesg
Xcommands can be used without logging in.
X.SH AUTHOR
XThis program was written by Rich $alz <rsalz@bbn.com>.
XIt has the following copyright:
X.RS
X.nf
XCopyright 1989 BBN Systems and Technologies Corporation.
XAll Rights Reserved.
XThis is free software, and may be distributed under the terms of the
XGNU Public License; see the file COPYING for more details.
X
X$Header: codaserver.8,v 2.0 90/03/23 15:02:01 rsalz Exp $
X.fi
X.RE
X.SH "SEE ALSO"
Xcoda(1).
END_OF_FILE
if test 5278 -ne `wc -c <'codaserver.8'`; then
    echo shar: \"'codaserver.8'\" unpacked with wrong size!
fi
# end of 'codaserver.8'
fi
if test -f 'file.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'file.c'\"
else
echo shar: Extracting \"'file.c'\" \(8889 characters\)
sed "s/^X//" >'file.c' <<'END_OF_FILE'
X/*
X**  Copyright 1989 BBN Systems and Technologies Corporation.
X**  All Rights Reserved.
X**  This is free software, and may be distributed under the terms of the
X**  GNU Public License; see the file COPYING for more details.
X**
X**  File routines for the CODA server.
X*/
X#include "server.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <a.out.h>
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: file.c,v 2.0 90/03/23 14:41:23 rsalz Exp $";
X#endif	/* RCSID */
X
XSTATIC int	RootLen;		/* Length of the root string	*/
XSTATIC int	ScanCount;		/* How much have we looked at?	*/
X
X/*
X**  Yet another routine to walk through a directory tree.  It is similar
X**  to ftw(3), but different.  It is not based on licensed code.
X*/
XSTATIC int
XFileWalk(Path, Predicate, Depth, UserData)
X    char		*Path;
X    int			(*Predicate)();
X    int			Depth;
X    char		*UserData;
X{
X    register DIR	*Dp;
X    register char	*p;
X    register int	i;
X    struct direct	*E;
X    struct stat		Sb;
X    long		cookie;
X    char		fullpath[MAXPATH];
X
X    /* If we can't stat, pass it to the user but go no further. */
X    if (stat(Path, &Sb) < 0) {
X	(void)(*Predicate)(Path, (struct stat *)NULL, UserData);
X	return FW_NOFURTHER;
X    }
X
X    /* Call the user's function. */
X    switch ((*Predicate)(Path, &Sb, UserData)) {
X    default:
X    case FW_NOFURTHER:
X	return FW_NOFURTHER;
X    case FW_EXIT:
X	return FW_EXIT;
X    case FW_PROCEED:
X	if ((Sb.st_mode & S_IFMT) != S_IFDIR)
X	    return FW_PROCEED;
X	break;
X    }
X
X    /* Open directory; and if we can't tell the user so. */
X    if ((Dp = opendir(Path)) == NULL)
X	return FW_NOFURTHER;
X
X    /* For speed, remember where the last component starts. */
X    i = strlen(Path);
X    (void)strcpy(fullpath, Path);
X    p = &fullpath[i];
X    if (i && p[-1] != '/')
X	*p++ = '/';
X
X    /* Read all entries in the directory. */
X    while (E = readdir(Dp)) {
X	if (EQ(E->d_name, ".") || EQ(E->d_name, ".."))
X	    continue;
X
X	/* If going too deep, remember our state and recycle. */
X	if (Depth <= 1) {
X	    cookie = telldir(Dp);
X	    (void)closedir(Dp);
X	    Dp = NULL;
X	}
X
X	/* Process the entry. */
X	(void)strcpy(p, E->d_name);
X	if (FileWalk(fullpath, Predicate, Depth - 1, UserData) == FW_EXIT) {
X	    /* User's finished; clean up. */
X	    if (Dp)
X		(void)closedir(Dp);
X	    return FW_EXIT;
X	}
X
X	/* Reopen the directory if necessary. */
X	if (Dp == NULL) {
X	    if ((Dp = opendir(Path)) == NULL)
X		/* WTF? */
X		return FW_NOFURTHER;
X	    seekdir(Dp, cookie);
X	}
X    }
X
X    /* Clean up. */
X    (void)closedir(Dp);
X    return FW_PROCEED;
X}
X
X
X/*
X**  Do shell-style pattern matching for ?, \, [], and * characters.
X**  Might not be robust in face of malformed patterns; e.g., "foo[a-"
X**  could cause a segmentation violation.
X**
X**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
X*/
XSTATIC int
XStar(s, p)
X    register char	*s;
X    register char	*p;
X{
X    STATIC int		Match();
X
X    while (Match(s, p) == FALSE)
X	if (*++s == '\0')
X	    return FALSE;
X    return TRUE;
X}
X
XSTATIC int
XMatch(s, p)
X    register char	*s;
X    register char	*p;
X{
X    register int	last;
X    register int	matched;
X    register int	reverse;
X
X    for ( ; *p; s++, p++)
X	switch (*p) {
X	case '\\':
X	    /* Literal match with following character. */
X	    p++;
X	    /* FALLTHROUGH */
X	default:
X	    if (*s != *p)
X		return FALSE;
X	    continue;
X	case '?':
X	    /* Match anything. */
X	    if (*s == '\0')
X		return FALSE;
X	    continue;
X	case '*':
X	    /* Trailing star matches everything. */
X	    return *++p ? Star(s, p) : TRUE;
X	case '[':
X	    /* [^....] means inverse character class. */
X	    if (reverse = p[1] == '^')
X		p++;
X	    for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p)
X		/* This next line requires a good C compiler. */
X		if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
X		    matched = TRUE;
X	    if (matched == reverse)
X		return FALSE;
X	    continue;
X	}
X
X    return *s == '\0';
X}
X
X
X/*
X**  Return TRUE if a file is an "a.out" file.
X*/
XSTATIC int
XIsBinaryFile(Path)
X    char	*Path;
X{
X    FILE	*F;
X    struct exec	Head;
X
X    if ((F = fopen(Path, "r")) == NULL
X     || fread((char *)&Head, sizeof Head, 1, F) != 1)
X	return FALSE;
X    (void)fclose(F);
X    switch (Head.a_magic) {
X    default:
X	return FALSE;
X#ifdef	OMAGIC
X    case OMAGIC:
X	return TRUE;
X#endif	/* OMAGIC */
X#ifdef	NMAGIC
X    case NMAGIC:
X	return TRUE;
X#endif	/* NMAGIC */
X#ifdef	ZMAGIC
X    case ZMAGIC:
X	return TRUE;
X#endif	/* ZMAGIC */
X    }
X}
X
X
X/*
X**  This is the predicate for the file walker.  It gets passed the
X**  current EXCEPTION block, to see if the Path is something we want
X**  to add to our list or not.
X*/
XSTATIC int
XAdder(Path, Sb, UserData)
X    char		*Path;
X    struct stat		*Sb;
X    char		*UserData;
X{
X    register EXCEPTION	*E;
X    register STRLIST	*S;
X    register char	*tail;
X
X    /* Silently skip bogus entries. */
X    if (Sb == NULL)
X	return FW_NOFURTHER;
X
X    if (++ScanCount == SCAN_PING) {
X	Message("Still scanning...");
X	ScanCount = 0;
X    }
X
X    /* Get last component. */
X    if (tail = strrchr(Path, '/'))
X	tail++;
X    else
X	tail = Path;
X
X    switch (Sb->st_mode & S_IFMT) {
X    default:
X	/* Something we don't handle. */
X	return FW_PROCEED;
X    case S_IFDIR:
X	/* Look through all "directory" exceptions; if we find one for
X	 * the client's host, go no further. */
X	for (E = (EXCEPTION *)UserData; E; E = E->Next)
X	    if (E->Directory && HostIsInClass(TheHost, E->Class))
X		for (S = E->Value; S; S = S->Next) {
X		    if (*S->Value == '^' && Match(Path, &S->Value[1]))
X			return FW_NOFURTHER;
X		    if (*S->Value != '^' && Match(tail, S->Value))
X			return FW_NOFURTHER;
X		}
X	/* Make sure the user can hack on it. */
X	Sb->st_mode |= S_IREAD | S_IWRITE | S_IEXEC;
X        break;
X    case S_IFREG:
X	/* Normal file; if we don't want it, we still keep going. */
X	for (E = (EXCEPTION *)UserData; E; E = E->Next)
X	    if (!E->Directory && HostIsInClass(TheHost, E->Class))
X		for (S = E->Value; S; S = S->Next) {
X		    if (*S->Value == '^' && Match(Path, &S->Value[1]))
X			return FW_PROCEED;
X		    if (*S->Value != '^' && Match(tail, S->Value))
X			return FW_PROCEED;
X		}
X	if (!AllowBinaries && IsBinaryFile(Path))
X	    return FW_PROCEED;
X	break;
X    }
X
X    /* Add the item, tell the Walker to keep going. */
X    if (strlen(Path) > RootLen
X     && Path[RootLen] == '/'
X     && strncmp(Path, TheRoot, RootLen) == 0)
X	Path += RootLen + 1;
X    AddItemToList(Path, Sb);
X    return FW_PROCEED;
X}
X
X
X/*
X**  Give status information on all the files in a block.
X*/
XSTATIC int
XListFilesInBlock(B)
X    BLOCK		*B;
X{
X    register DIRLIST	*D;
X    register char	*p;
X    struct stat		Sb;
X    char		fullpath[MAXPATH];
X
X    /* Does the host get this block? */
X    if (!HostIsInClass(TheHost, B->Class))
X	return FALSE;
X
X    /* Loop over all directories in this block. */
X    for (ScanCount = 0, D = B->Directories; D; D = D->Next) {
X	/* Build the pathname. */
X	if (*D->Value == '/')
X	    p = D->Value;
X	else {
X	    (void)sprintf(fullpath, "%s/%s", TheRoot, D->Value);
X	    p = fullpath;
X	}
X	if (!D->Directory)
X	    (void)FileWalk(p, Adder, FASTDEPTH, (char *)D->Exceptions);
X	else if (stat(p, &Sb) >= 0)
X	    /* Ignore exceptions? */
X	    AddItemToList(D->Value, &Sb);
X	else
X	    /* Silently skip bogus files. */
X	    ;
X    }
X
X    /* Done. */
X    return TRUE;
X}
X
X
X/*
X**  List all the files in the block.
X*/
Xvoid
XListFiles(p)
X    char		*p;
X{
X    register BLOCK	*B;
X    register ITEM	*I;
X    register ITEM	*Iend;
X    char		buff[SIZE];
X
X    ResetItem();
X    RootLen = strlen(TheRoot);
X
X    if (*p) {
X	if ((B = FindBlock(p)) == NULL) {
X	    Nack("No such block");
X	    return;
X	}
X	if (!ListFilesInBlock(B)) {
X	    Nack("Host doesn't get that block");
X	    return;
X	}
X    }
X    else
X	/* List all blocks. */
X	for (B = BaseBlock.Next; B; B = B->Next)
X	    if (!B->Excluded)
X		(void)ListFilesInBlock(B);
X
X    SortItem();
X    for (I = BaseItem, Iend = &BaseItem[NumItem]; I < Iend; I++) {
X	(void)sprintf(buff, "ITEM W=%c N=%s U=%d G=%d M=%d S=%ld T=%ld",
X			I->Directory ? 'd' : 'f', I->Name, I->Uid,
X			I->Gid, I->Mode, (long)I->Size, (long)I->Time);
X	Data(buff);
X    }
X
X    (void)sprintf(buff, "Found %d files", NumItem);
X    Ack(buff);
X}
X
X
X/*
X**  Send one file down the pipe.
X*/
Xvoid
XSendFile(Name)
X    char		*Name;
X{
X    register FILE	*F;
X    register int	C;
X    ITEM		*I;
X    char		fullpath[MAXPATH];
X
X    /* Can client get it? */
X    if ((I = FindItem(Name)) == NULL) {
X	Nack("File not found");
X	return;
X    }
X
X    /* Open it, dealing with relative pathnames. */
X    if (Name[0] == '/')
X	F = fopen(Name, "r");
X    else {
X	(void)sprintf(fullpath, "%s/%s", TheRoot, Name);
X	F = fopen(fullpath, "r");
X    }
X    if (F == NULL) {
X	Nack((char *)NULL);
X	return;
X    }
X
X    /* Send it. */
X    Ack(Name);
X    while ((C = getc(F)) != EOF)
X	(void)putchar(C);
X    (void)fclose(F);
X#ifdef	VERBOSE_LOG
X    LogSentItem(I);
X#else
X# ifdef	lint
X    I = I;
X# endif	/* lint */
X#endif	/* VERBOSE_LOG */
X    Ack((char *)NULL);
X}
END_OF_FILE
if test 8889 -ne `wc -c <'file.c'`; then
    echo shar: \"'file.c'\" unpacked with wrong size!
fi
# end of 'file.c'
fi
if test -f 'libvms.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'libvms.c'\"
else
echo shar: Extracting \"'libvms.c'\" \(7419 characters\)
sed "s/^X//" >'libvms.c' <<'END_OF_FILE'
X/*
X**  Copyright 1989 BBN Systems and Technologies Corporation.
X**  All Rights Reserved.
X**  This is free software, and may be distributed under the terms of the
X**  GNU Public License; see the file COPYING for more details.
X**
X**  Client library routines for VMS with Wollongong 3.0 TCP.
X**  Check the "/INCLUDE" qualifier in descrip.mms.
X**
X**  Our VMS documentation claims some functions exist that really don't,
X**  so we've had to implement them on our own.
X**
X**  Also, Wollongong's header files try to re-specify some of the same
X**  same typedef's that the VMS C RTL specifies.  Sigh.
X*/
X#include "client.h"
X#include <types.h>
X#include <string.h>
X#include <ssdef.h>
X#include <iodef.h>
X#include <descrip.h>
X#define time_t	TWG_time_t
X#define size_t	TWG_size_t
X#include "[sys]types2.h"
X#include "[sys]socket.h"
X#include "[netinet]in.h"
X#undef time_t
X#undef size_t
X#ifdef	RCSID
Xstatic char	 RCS[] =
X	"$Header: libvms.c,v 2.0 90/04/09 16:29:34 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  Depending on what version of the C RTL you have, you will need to
X**  edit these.
X*/
X#define NEED_UTIME
X#undef NEED_RENAME
X#undef NEED_MEMSET
X
XSTATIC int	 Channel;		/* Something to talk with	*/
X
X
X#ifdef	NEED_UTIME
X#include "vmsutime.inc"
X#endif	/* NEED_UTIME */
X
X
X/*
X**  This comes from the AT&T public domain getopt handed out at 1985
X**  UNIFORUM and subsequently posted to Usenet.
X*/
Xint		opterr = 1;
Xint		optind = 1;
Xint		optopt;
Xchar		*optarg;
Xextern char	*strchr();
X
Xint
Xgetopt(ac, av, options)
X    int			ac;
X    char		**av;
X    char		*options;
X{
X    static int		sp = 1;
X    REGISTER char	*p;
X
X    if (sp == 1) {
X	if (optind >= ac || av[optind][0] != '-' || av[optind][1] == '\0')
X	    return EOF;
X	if (strcmp(av[optind], "--") == 0) {
X	    optind++;
X	    return EOF;
X	}
X    }
X
X    optopt = av[optind][sp];
X
X    /* Bogus flag? */
X    if (optopt == ':' || (p = strchr(options, optopt)) == NULL) {
X	if (opterr)
X	    (void)fprintf(stderr, "%s: illegal option -- %c\n", av[0], optopt);
X	if (av[optind][++sp] == '\0') {
X	    optind++;
X	    sp = 1;
X	}
X	return '?';
X    }
X
X    /* Flag needs an option? */
X    if (*++p == ':') {
X	if (av[optind][sp + 1])
X	    optarg = &av[optind++][sp + 1];
X	else if (++optind < ac)
X	    optarg = av[optind++];
X	else {
X	    if (opterr)
X		(void)fprintf(stderr,
X			"%s: option requires an argument -- %c\n",
X			av[0], optopt);
X	    sp = 1;
X	    return '?';
X	}
X	sp = 1;
X    }
X    else {
X	if (av[optind][++sp] == '\0') {
X	    sp = 1;
X	    optind++;
X	}
X	optarg = NULL;
X    }
X    return optopt;
X}
X
X
X#ifdef	NEED_MEMSET
X/*
X**  Set memory to a value.
X*/
Xvoid *
Xmemset(save, c, count)
X    void		*save;
X    int			c;
X    unsigned int	count;
X{
X    char		*p;
X    register int	 i;
X
X    if ((i = count) != 0)
X	for (p = save; --i >= 0; )
X	    *p++ = c;
X
X    return save;
X}
X#endif	/* NEED_MEMSET */
X
X
X#ifdef	NEED_RENAME
X/*
X**  Turn a nice pretty /unix/path/name into an ugly [.vms.path]name
X*/
XSTATIC void
XVMSname(path, buffer)
X    char		*path;
X    char		*buffer;
X{
X    REGISTER char	*p;
X    REGISTER char	*tail;
X    char		temp[SIZE];
X
X    /* Simple case "foo" ==> "foo" */
X    (void)strcpy(temp, path);
X    if ((tail = strrchr(temp, '/')) == NULL) {
X	strcpy(buffer, path);
X	return;
X    }
X
X    /* Split off the last component. */
X    *tail++ = '\0';
X
X    /* Turn all slashes into periods. */
X    for (p = temp; p = strchr(p, '/'); )
X	*p++ = '.';
X    if (temp[0] == '.')
X	(void)sprintf(buffer, "[%s]%s", &temp[1], tail);
X    else
X	(void)sprintf(buffer, "[.%s]%s", temp, tail);
X}
X
X
X/*
X**  Rename a file.
X*/
Xint
Xrename(from, to)
X    char			*from;
X    char			*to;
X{
X    struct dsc$descriptor_s	Fdesc;
X    struct dsc$descriptor_s	Tdesc;
X    char			Fname[SIZE];
X    char			Tname[SIZE];
X
X    VMSname(from, Fname);
X    Fdesc.dsc$a_pointer = Fname;
X    Fdesc.dsc$w_length  = strlen(Fname);
X    Fdesc.dsc$b_dtype   = DSC$K_DTYPE_T;
X    Fdesc.dsc$b_class   = DSC$K_CLASS_S;
X
X    VMSname(to, Tname);
X    Tdesc.dsc$a_pointer = Tname;
X    Tdesc.dsc$w_length  = strlen(Tname);
X    Tdesc.dsc$b_dtype   = DSC$K_DTYPE_T;
X    Tdesc.dsc$b_class   = DSC$K_CLASS_S;
X
X    return lib$rename_file(&Fdesc, &Tdesc) == SS$_NORMAL ? 0 : -1;
X}
X#endif	/* NEED_RENAME */
X
X
X/*
X**  Remove a file.
X*/
Xint
Xunlink(Name)
X    char	*Name;
X{
X    return delete(Name);
X
X}
X
X
X/*
X**  Do the grundge work of getting us a socket.
X*/
XSTATIC int
XGetSocket(machine, port)
X    char		*machine;
X    int			port;
X{
X    unsigned long	rhost();
X    REGISTER int	s;
X    char		*p;
X    struct sockaddr_in	sin;
X
X    /* Set up the socket. */
X    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X	perror("Socket creation failed");
X	return -1;
X    }
X
X    /* Set up the socket address. */
X    (void)memset((void *)&sin, '\0', sizeof sin);
X    p = machine;
X    if ((sin.sin_addr.s_addr = rhost(&p)) == -1) {
X	perror("No such machine");
X	return -1;
X    }
X    sin.sin_family = AF_INET;
X    sin.sin_port = htons(port);
X
X    /* Connect to the server. */
X    if (connect(s, &sin, sizeof sin) < 0) {
X	perror("Connect failed");
X	netclose(s);
X	return -1;
X    }
X
X    return s;
X}
X
X
X/*
X**  Open connection to server, return FALSE on error.
X*/
Xint
XSRVopen(machine, port)
X    char	*machine;
X    int		port;
X{
X    return (Channel = GetSocket(machine, port)) >= 0;
X}
X
X
X/*
X**  Send a QUIT and shut down.
X*/
Xvoid
XSRVclose()
X{
X    SRVput("QUIT");
X    (void)netclose(Channel);
X}
X
X
X/*
X**  Send a line to the server.
X*/
Xvoid
XSRVput(p)
X    char	*p;
X{
X    if (SRVtrace)
X	(void)printf(">>>%s\n", p);
X    netwrite(Channel, p, strlen(p));
X    netwrite(Channel, "\r\n", 2);
X}
X
X
X/*
X**  Get a line of text from the server.  Strip end-of-line characters.
X*/
Xint
XSRVget(buff, size)
X    char		*buff;
X    int			size;
X{
X    REGISTER char	*p;
X    REGISTER char	*bend;
X    REGISTER int	c;
X
X    for (bend = &buff[size - 1]; ; ) {
X	for (p = buff; ((c = SRVcget()) != '\n'); ) {
X	    if (c == EOF)
X		return FALSE;
X	    if (c != '\r' && p < bend)
X		*p++ = c;
X	}
X	*p = '\0';
X	if (SRVtrace)
X	    (void)printf("<<<%s\n", buff);
X	if (strncmp(buff, "INF ", 4))
X	    return TRUE;
X	(void)printf("Server message:\n\t%s\n", &buff[4]);
X	(void)fflush(stdout);
X    }
X}
X
X
X/*
X**  Get a character from the server.
X*/
Xint
XSRVcget()
X{
X    static char	buff[1024];
X    static int	count;
X    static int	max;
X
X    if (count == max) {
X	while ((max = netread(Channel, buff, sizeof buff)) == 0)
X	    ;
X	if (max < 0)
X	    return EOF;
X	if (max > sizeof buff)
X	    (void)abort();
X	count = 0;
X    }
X    return buff[count++];
X}
X
X
X/*
X**  Get a password without echoing.
X*/
Xvoid
XGetPassword(buff, size)
X     char			*buff;
X     int			size;
X{
X    struct dsc$descriptor_s	Desc;
X    int				i;
X    int				kb;
X    int				mask;
X    int				timeout;
X    int				length;
X
X    /* Create a keyboard to read from. */
X    if (smg$create_virtual_keyboard(&kb) != SS$_NORMAL) {
X	perror("Error creating virtual_keyboard");
X	exit(i);
X    }
X
X    /* Set up the parameters. */
X    Desc.dsc$w_length = size;
X    Desc.dsc$b_dtype = DSC$K_DTYPE_T;
X    Desc.dsc$b_class = DSC$K_CLASS_S;
X    Desc.dsc$a_pointer = buff;
X    mask = IO$M_NOECHO;
X    timeout = 60;
X    length = 0;
X
X    /* Read it. */
X    i = smg$read_string(&kb, &Desc, 0, &size, &mask, &timeout, 0, &length);
X    if (i != SS$_NORMAL) {
X	perror("Error reading password");
X	exit(i);
X    }
X
X    /* Delete the keyboard. */
X    if (smg$delete_virtual_keyboard(&kb) != SS$_NORMAL) {
X	perror("Error deleting virtual keyboard");
X	exit(i);
X    }
X
X    /* Clean up and return. */
X    (void)printf("\n");
X    buff[length] = '\0';
X}
END_OF_FILE
if test 7419 -ne `wc -c <'libvms.c'`; then
    echo shar: \"'libvms.c'\" unpacked with wrong size!
fi
# end of 'libvms.c'
fi
if test -f 'server.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'server.c'\"
else
echo shar: Extracting \"'server.c'\" \(9605 characters\)
sed "s/^X//" >'server.c' <<'END_OF_FILE'
X/*
X**  Copyright 1989 BBN Systems and Technologies Corporation.
X**  All Rights Reserved.
X**  This is free software, and may be distributed under the terms of the
X**  GNU Public License; see the file COPYING for more details.
X**
X**  Main driver for CODA server.
X*/
X#define MAINLINE
X#include "server.h"
X#include <pwd.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <sys/stat.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#ifdef	RCSID
Xstatic char RCS[] =
X	"$Header: server.c,v 2.0 90/03/23 14:41:55 rsalz Exp $";
X#endif	/* RCSID */
X
X
X/*
X**  A command has a text name, an internal value, and a help message.
X*/
Xtypedef struct _TABLE {
X    char	*Name;
X    COMMAND	Value;
X    char	*Help;
X} TABLE;
X
Xchar		*LogFile = LOGFILE;	/* Name of the log file		*/
Xchar		UnknownHost[] = GUESTHOST; /* For unknown hosts		*/
X
XSTATIC jmp_buf	Context;		/* When the bell rings		*/
XSTATIC BOOL	LoggedIn;		/* Did we get a USER command?	*/
XSTATIC BOOL	Testing;		/* Are we in test mode?		*/
X
XSTATIC TABLE	Commands[] = {		/* List of commands		*/
X    {	"GOTO",		CMDgoto,	"Change to specified directory" },
X    {	"EXIT",		CMDquit,	"Shut down server" },
X    {	"HELP",		CMDhelp,	"Print this status report" },
X    {	"HOST",		CMDhost,	"Specify name of destination host" },
X    {	"MESG",		CMDmesg,	"Send message to log file" },
X    {	"LIST",		CMDlist,	"List status of files [in block]" },
X    {	"QUIT",		CMDquit,	"Shut down server" },
X    {	"READ",		CMDread,	"Read control file, name optional" },
X    {	"ROOT",		CMDroot,	"Set root for relative pathnames" },
X    {	"SEND",		CMDsend,	"Start file-sending protocol" },
X    {	"USER",		CMDuser,	"Log in a specified user" },
X    {	"",		CMD_time,	NULL },
X    { NULL }
X};
X
X
X
X/*
X**  Return a perror-style string.
X*/
Xchar *
Xstrerror(e)
X    int		e;
X{
X    extern int	sys_nerr;
X    extern char	*sys_errlist[];
X    char	buff[20];
X
X    if (e < 0 || e > sys_nerr) {
X	(void)sprintf(buff, "Error code %d\n", e);
X	return buff;
X    }
X    return sys_errlist[e];
X}
X
X
X
X/*
X**  Send a message saying a command was successful.
X*/
Xvoid
XAck(p)
X    char	*p;
X{
X    (void)printf("ACK-%s\r\n", p ? p : "Done");
X    (void)fflush(stdout);
X}
X
X
X/*
X**  Send a message saying that a command failed.
X*/
Xvoid
XNack(p)
X    char	*p;
X{
X    (void)printf("NAK-%s\r\n", p ? p : strerror(errno));
X    (void)fflush(stdout);
X}
X
X
X/*
X**  Send an information message.  The client shoujld pass these on to
X**  the user.
X*/
Xvoid
XMessage(p)
X    char	*p;
X{
X    (void)printf("INF %s\r\n", p ? p : "Never mind");
X}
X
X
X/*
X**  Send a data message.  This is for the client program.
X*/
Xvoid
XData(p)
X    char	*p;
X{
X    if (p)
X	(void)printf("DAT %s\r\n", p);
X}
X
X
X
X/*
X**  When that bell rings, get out of here!
X*/
XSTATIC CATCHER
XAlarmCatch()
X{
X    longjmp(Context, 1);
X    /* NOTREACHED */
X}
X
X
X/*
X**  Read a line from the client.  Quit if it times out.  Otherwise
X**  parse the first word as a command, stuff the rest of the line as
X**  a possible argument to the command, and return the command's value.
X*/
XSTATIC COMMAND
XReadLine(arg, size)
X    char		*arg;
X    int			size;
X{
X    register char	*p;
X    register char	*q;
X    register TABLE	*T;
X    char		buff[SIZE];
X
X    if (setjmp(Context) == 1)
X	return CMD_time;
X
X    for ( ; ; ) {
X	/* Timed-out read. */
X	(void)alarm(TIMEOUT);
X	p = fgets(buff, sizeof buff, stdin);
X	(void)alarm(0);
X	if (p == NULL)
X	    return CMDquit;
X
X	/* Kill the terminator. */
X	if ((p = strchr(buff, '\r')) || (p = strchr(buff, '\n')))
X	    *p = '\0';
X
X	/* Skip whitespace, ignore totally blank lines. */
X	for (p = buff; *p && WHITE(*p); p++)
X	    ;
X	if (*p == '\0')
X	    continue;
X
X	/* Find first word. */
X	for (q = p; *q && !WHITE(*q); q++)
X	    ;
X
X	/* Snip off first word, copy rest of line to argument. */
X	if (*q) {
X	    for (*q = '\0'; *++q && WHITE(*q); )
X		;
X	    (void)strncpy(arg, q, size);
X	    arg[size - 1] = '\0';
X	}
X	else
X	    arg[0] = '\0';
X	Uppercase(p);
X
X	/* Find first word in the command table. */
X	for (T = Commands; T->Name; T++)
X	    if (EQ(T->Name, p))
X		return T->Value;
X
X	Nack("Unknown command");
X    }
X}
X
X
X
X
X/*
X**  Get the name of the host where the client it.
X*/
XSTATIC void
XGetClientHostname()
X{
X    struct hostent	*hp;
X    struct sockaddr_in	venial;
X    char		buff[SIZE];
X    int			size;
X
X    if (isatty(0))
X	/* Obviously a local call... */
X	TheHost = gethostname(buff, sizeof buff) < 0 ? NULL : buff;
X    else {
X	size = sizeof venial;
X	if (getpeername(0, (struct sockaddr *)&venial, &size) < 0)
X	    TheHost = NULL;
X	else {
X	    hp = gethostbyaddr((char *)&venial.sin_addr,
X				sizeof venial.sin_addr, AF_INET);
X	    TheHost = hp ? hp->h_name : NULL;
X	}
X    }
X
X    TheHost = TheHost ? COPY(TheHost) : COPY(UnknownHost);
X    Uppercase(TheHost);
X}
X
X
X/*
X**  Read a CODA control file to see if its valid.
X*/
XSTATIC void
XCheckCodafile(p)
X    char	*p;
X{
X    ResetStorage();
X    if (!yyopen(TRUE, p)) {
X	Nack((char *)NULL);
X	exit(1);
X    }
X    if (yyparse() != BADPARSE && !HaveErrors)
X	exit(0);
X    Nack("Syntax errors found");
X    exit(1);
X}
X
X
X/*
X**  Verify a user's name and password.
X*/
XSTATIC void
XLogin(p)
X    char		*p;
X{
X    static char		BAD[] = "Bad username or password";
X    struct passwd	*pwd;
X    char		*pass;
X    char		buff[SIZE];
X
X    /* Split at the space into a name:password pair. */
X    if ((pass = strchr(p, ' ')) == NULL) {
X	Nack(BAD);
X	return;
X    }
X    *pass++ = '\0';
X
X    /* Valid name with the right password? */
X    if ((pwd = getpwnam(p)) == NULL) {
X	Nack(BAD);
X	return;
X    }
X    if (!EQ(pwd->pw_passwd, crypt(pass, pwd->pw_passwd))) {
X	Nack(BAD);
X	return;
X    }
X
X    /* Change identity. */
X    if (initgroups(p, pwd->pw_gid) < 0 || setuid(pwd->pw_uid) < 0) {
X	Nack((char *)NULL);
X	return;
X    }
X
X    /* Done; log and acknowledge it. */
X    LoggedIn = TRUE;
X    (void)sprintf(buff, "Logged in %s", p);
X    LogText(buff);
X    Ack(buff);
X}
X
X
X/*
X**  Print a usage message and exit.
X*/
XSTATIC void
XUsage()
X{
X    (void)fprintf(stderr, "Usage: codaserver %s\n",
X	    "[-c] [-lLogfile] [-rCodafile] [-t]");
X    exit(1);
X}
X
X
X/* ARGSUSED1 */
Xmain(ac, av)
X    int			ac;
X    char		*av[];
X{
X    static char		LOGIN[] = "Login first";
X    register BOOL	DidRead;
X    register char	*p;
X    register COMMAND	C;
X    register int	i;
X    register TABLE	*T;
X    char		arg[SIZE];
X
X    /* Parse JCL. */
X    while ((i = getopt(ac, av, "cl:r:t")) != EOF)
X	switch (i) {
X	default:
X	    Usage();
X	    /* NOTREACHED */
X	case 'c':
X	    Copyright();
X	    exit(0);
X	    /* NOTREACHED */
X	case 'l':
X	    LogFile = optarg;
X	    break;
X	case 'r':
X	    CheckCodafile(optarg);
X	    /* NOTREACHED */
X	case 't':
X	    /* Play it safe... */
X	    if (isatty(0)) {
X		Testing = TRUE;
X		LoggedIn = TRUE;
X		(void)printf("Testing...\n");
X	    }
X	    break;
X	}
X
X    /* Check for other arguments. */
X    ac -= optind;
X    av += optind;
X    if (ac)
X	Usage();
X
X    if (!Testing) {
X	/* Set up IO descriptors, be robust in the face of varying inetd's. */
X	(void)close(1);
X	(void)close(2);
X	(void)dup(0);
X	(void)dup(0);
X    }
X
X    /* Do various initializations. */
X    GetClientHostname();
X    (void)signal(SIGALRM, AlarmCatch);
X    ResetStorage();
X    LogOpen();
X
X    /* Tell client we're here. */
X    (void)sprintf(arg, "Hello %s; how are you today?", TheHost);
X    Ack(arg);
X
X    /* Read and dispatch loop. */
X    DidRead = FALSE;
X    while ((C = ReadLine(arg, sizeof arg)) != CMDquit)
X	switch (C) {
X	default:
X	    Nack("Unimplemented command");
X	    break;
X
X	case CMDgoto:
X	    if (!LoggedIn)
X		Nack(LOGIN);
X	    else if (arg[0] == '\0')
X		Nack("Directory missing");
X	    else if (chdir(arg) < 0)
X		Nack((char *)NULL);
X	    else
X		Ack((char *)NULL);
X	    break;
X
X	case CMDhelp:
X	    for (T = Commands; T->Name; T++)
X		if (T->Help) {
X		    (void)sprintf(arg, "%s\t%s", T->Name, T->Help);
X		    Message(arg);
X		}
X	    Ack((char *)NULL);
X	    break;
X
X	case CMDhost:
X	    if (!Testing)
X		Nack("I already know who you are");
X	    else {
X		if (TheHost) {
X		    free(TheHost);
X		    TheHost = NULL;
X		}
X		Uppercase(arg);
X		if (HostWasDeclared(arg)) {
X		    TheHost = COPY(arg);
X		    Ack((char *)NULL);
X		}
X		else
X		    Nack("Host undefined");
X	    }
X	    break;
X
X	case CMDlist:
X	    if (!LoggedIn)
X		Nack(LOGIN);
X	    else if (TheHost == NULL)
X		Nack("No host specified");
X	    else if (!DidRead)
X		Nack("Use READ first");
X	    else
X		/* ListFiles() does its own Ack or Nack. */
X		ListFiles(arg);
X	    break;
X
X	case CMDmesg:
X	    LogText(arg);
X	    Ack((char *)NULL);
X	    break;
X
X	case CMDread:
X	    if (!LoggedIn)
X		Nack(LOGIN);
X	    else {
X		Rooted = FALSE;
X		AllowBinaries = TRUE;
X		ResetStorage();
X		DidRead = FALSE;
X		p = arg[0] ? arg : CONTROLFILE;
X		if (!yyopen(FALSE, p))
X		    Nack((char *)NULL);
X		else {
X		    if (yyparse() == BADPARSE || HaveErrors)
X			Nack("Can't parse file");
X		    else {
X			LogReadfile(p);
X			if (!HostWasDeclared(TheHost)) {
X			    (void)sprintf(arg, "Host \"%s\" not in the file.",
X					TheHost);
X			    Message(arg);
X			}
X			Ack((char *)NULL);
X			DidRead = TRUE;
X		    }
X		    yyclose();
X		}
X	    }
X	    break;
X
X	case CMDroot:
X	    if (Rooted)
X		Nack("Permission denied");
X	    else {
X		if (TheRoot)
X		    free(TheRoot);
X		TheRoot = arg[0] ? COPY(arg) : NULL;
X		Ack((char *)NULL);
X	    }
X	    break;
X
X	case CMDsend:
X	    if (!LoggedIn)
X		Nack(LOGIN);
X	    else if (!DidRead)
X		Nack("Use READ first");
X	    else if (arg[0])
X		/* SendFile() does its own Ack or Nack. */
X		SendFile(arg);
X	    else
X		Nack("Filename missing");
X	    break;
X
X	case CMDuser:
X	    /* Login() does its own Ack or Nack. */
X	    Login(arg);
X	    break;
X
X	case CMD_time:
X	    LogClose("timeout");
X	    exit(0);
X	    /* NOTREACHED */
X
X    }
X
X    LogClose("quit");
X    exit(0);
X    /* NOTREACHED */
X}
END_OF_FILE
if test 9605 -ne `wc -c <'server.c'`; then
    echo shar: \"'server.c'\" unpacked with wrong size!
fi
# end of 'server.c'
fi
if test -f 'server.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'server.h'\"
else
echo shar: Extracting \"'server.h'\" \(5418 characters\)
sed "s/^X//" >'server.h' <<'END_OF_FILE'
X/*
X**  Copyright 1989 BBN Systems and Technologies Corporation.
X**  All Rights Reserved.
X**  This is free software, and may be distributed under the terms of the
X**  GNU Public License; see the file COPYING for more details.
X**
X**  Header file for CODA server.
X**  $Header: server.h,v 2.0 90/03/23 14:42:01 rsalz Exp $
X*/
X/* SUPPRESS 223 *//* Nested comment */
X#include <stdio.h>
X#include <sys/types.h>
X
X
X/*
X**  Constants, compilation control, et cetera.
X*/
X
X/* Assorted constants. */
X#define TRUE		1		/* Any non-zero value		*/
X#define FALSE		0		/* Must be zero			*/
X#define BADPARSE	1		/* Returned by YACC; must be 1	*/
X#define SIZE		256		/* String buffer size		*/
X#define MAXPATH		1024		/* Maximum pathname length	*/
X#define HOST_DELTA	10		/* Clump for growing host array	*/
X#define CLASS_DELTA	10		/* Clump for class arrays	*/
X#define ITEM_DELTA	50		/* Clump for those other things	*/
X#define SCAN_PING	200		/* How often to say we're alive	*/
X#define FASTDEPTH	6		/* How far before closedir()?	*/
X#define FW_NOFURTHER	1		/* FileWalker, go no further	*/
X#define FW_PROCEED	2		/* FileWalker, keep going	*/
X#define FW_EXIT		3		/* FileWalker, exit and pop up	*/
X
X/* Compile in RCS id strings? */
X#ifndef	SABER
X#define RCSID
X#endif	/* SABER */
X
X/* Give up after this many seconds of inactivity. */
X#define TIMEOUT		(30 * 60)
X
X/* Default name of the log file. */
X#define LOGFILE		"/usr/spool/log/codalog"
X
X/* Default name of the file to read. */
X#define CONTROLFILE	"Codafile"
X
X/* Name of the built-in class that contains all hosts except UnknownHost. */
X#define ALL		"_ALL"
X
X/* Default name if host name isn't found. */
X#define GUESTHOST	"_ANYHOST"
X
X/* Return type of a signal-handling function. */
Xtypedef int		CATCHER;	/* .. */
X/* typedef void		CATCHER;	/* .. */
X
X/* Hide routines that can be hidden? */
X#define STATIC		static		/* .. */
X/*efine STATIC		/* NULL */	/* .. */
X
X#define WHITE(c)	((c) == ' ' || (c) == '\t')
X
X/* Shut up, okay? */
X#ifdef	lint
X#undef putc
X#undef putchar
X#undef ungetc
X#undef RCSID
X#endif	/* lint */
X
X/* Memory allocation. */
X#define NEW(T, c)		\
X	((T *)malloc((unsigned int)(sizeof (T) * (c))))
X#define GROW(p, T, c)		\
X	((T *)realloc((char *)p, (unsigned int)(sizeof (T) * (c))))
X#define COPY(p)			\
X	strcpy(NEW(char, strlen(p) + 1), p)
X
X/* Fast front-end for string comparison. */
X#define EQ(p, q)		\
X	((p) == (q) || ((p)[0] == (q)[0] && strcmp((p), (q)) == 0))
X
X
X
X/*
X**  List of server commands.
X*/
Xtypedef enum _COMMAND {
X    CMDgoto,
X    CMDhelp,
X    CMDhost,
X    CMDlist,
X    CMDmesg,
X    CMDquit,
X    CMDread,
X    CMDroot,
X    CMDsend,
X    CMDuser,
X    CMD_time
X} COMMAND;
X
X
X/*
X**  Is it, or is it not?
X*/
Xtypedef int	BOOL;
X
X
X/*
X**  A linked list of strings.
X*/
Xtypedef struct _STRLIST {
X    struct _STRLIST	*Next;
X    char		*Value;
X} STRLIST;
X
X
X/*
X**  An exception is a pattern for either files or a directory that is
X**  valid for a class of hosts.
X*/
Xtypedef struct _EXCEPTION {
X    struct _EXCEPTION	*Next;
X    STRLIST		*Value;
X    BOOL		Directory;
X    char		*Class;
X} EXCEPTION;
X
X
X/*
X**  A directory list is a linked list of pathnames and their exceptions.
X*/
Xtypedef struct _DIRLIST {
X    struct _DIRLIST	*Next;
X    char		*Value;
X    BOOL		Directory;
X    EXCEPTION		*Exceptions;
X} DIRLIST;
X
X
X/*
X**  A block has a name, whether it is part of the default list, a list of
X**  files, and a class that it is valid for.
X*/
Xtypedef struct _BLOCK {
X    struct _BLOCK	*Next;
X    char		*Name;
X    BOOL		Excluded;
X    DIRLIST		*Directories;
X    char		*Class;
X} BLOCK;
X
X
X/*
X**  An item is something that can be sent to a client.  It has permissions,
X**  a modification time, and might be a directory.
X*/
Xtypedef struct _ITEM {
X    char		*Name;
X    int			Uid;
X    int			Gid;
X    BOOL		Directory;
X    long		Size;
X    time_t		Time;
X    int			Mode;
X} ITEM;
X
X
X/*
X**  Data is declared everywhere, lives oncewhere.
X*/
X#ifdef	MAINLINE
X#define EXTERN		/* NULL */
X#else
X#define EXTERN		extern
X#endif	/* MAINLINE */
X
XEXTERN BLOCK	BaseBlock;		/* Chain of known blocks	*/
XEXTERN BOOL	AllowBinaries;		/* Don't check for a.out files?	*/
XEXTERN BOOL	Rooted;			/* Can client set the root?	*/
Xextern char	UnknownHost[];		/* Name of the unknown host	*/
Xextern char	*LogFile;		/* Name of the log file		*/
XEXTERN char	*TheHost;		/* Host that called us		*/
XEXTERN char	*TheRoot;		/* Root for relative pathnames	*/
XEXTERN int	HaveErrors;		/* Failed to parse Codafile?	*/
XEXTERN ITEM	*BaseItem;		/* Array of things to send	*/
XEXTERN int	NumItem;		/* Size of said array		*/
X
X/* Our routines. */
XBLOCK		*FindBlock();
XITEM		*FindItem();
Xchar		*strerror();
Xint		HostIsInClass();
Xint		yyopen();
Xint		yyparse();
Xvoid		Ack();
Xvoid		AddClassesToClass();
Xvoid		AddHostToClass();
Xvoid		AddItemToList();
Xvoid		Data();
Xvoid		DefineBlock();
Xvoid		DefineHost();
Xvoid		ListFiles();
Xvoid		LogClose();
Xvoid		LogOpen();
Xvoid		LogReadfile();
Xvoid		LogSentItem();
Xvoid		LogText();
Xvoid		Message();
Xvoid		Nack();
Xvoid		ResetItem();
Xvoid		ResetStorage();
Xvoid		SendFile();
Xvoid		SortItem();
Xvoid		Uppercase();
Xvoid		yyclose();
Xvoid		yyerror();
X
X/* From the C library. */
Xextern int	errno;
Xextern int	optind;
Xextern time_t	time();
Xextern char	*optarg;
Xextern char	*bsearch();
Xextern char	*crypt();
Xextern char	*getwd();
Xextern char	*malloc();
Xextern char	*realloc();
Xextern char	*sprintf();		/* Too painful, my ass		*/
Xextern char	*strcat();
Xextern char	*strchr();
Xextern char	*strcpy();
Xextern char	*strncpy();
Xextern char	*strrchr();
END_OF_FILE
if test 5418 -ne `wc -c <'server.h'`; then
    echo shar: \"'server.h'\" unpacked with wrong size!
fi
# end of 'server.h'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 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
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.