[comp.hypercube] Remote C Compiler

grunwald@M.CS.UIUC.EDU (07/16/87)

Tired of developing your iPSC applications on the 310? Want to use EMACS, RCS
and your other normal tools?

Announcing, 'rcc', the remote C compiler server. Unpack the following, read
the Readme & have at it. Note: you must have excelan NET software installed
and you need to have the 'ndir' library under Xenix.

#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	Readme
#	fileio.c
#	rcc.c
#	rcc.h
#	rcccomm.h
#	remote.c
#	server.c
#	short.c
# This archive created: Wed Jul 15 12:11:44 1987
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: over-writing existing file "'Makefile'"
fi
cat << \SHAR_EOF > 'Makefile'
PARALLEL = 4

COFLAGS	=-rversion1
CIFLAGS	=-Nversion1

#
#	These defines are for the machine which used RCC.
#	RCC_BIN	is where the binaries wind up
#	RCC_DIST is where source gets distributed to
#
RCC_BIN		=/mntm/1/reed/picasso/bin
RCC_DIST	=/mntm/1/reed/picasso/rcc
#
#	These defines are for the RCC deamon machine.
#	If it's a UNIX machine, you need the NDIR utilities.
#
RCCD_HOST	=tess.cs.uiuc.edu
RCCD		=/usr/local/rccd
RCCD_SRC	=/usr/src/local/rcc
RCCD_BIN	=/usr/local
LIB		=-lndir -lx

CFLAGS	= $(MODEL) -DHOST=\"$(RCCD_HOST)\" -DRCCD=\"$(RCCD)\"

default: 
	@echo Use either install_rcc or install_rccd

SOBJ	= server.o fileio.o short.o
SSRC	= server.c fileio.c short.c rcc.h rcccomm.h  Makefile

dist_rccd: $(SSRC)
	echo "($(SSRC)) -> ($(RCCD_HOST))" > /tmp/rcc.rdist
	echo "	install -y $(RCCD_SRC) ;" >> /tmp/rcc.rdist
	rdist -f /tmp/rcc.rdist

remote: dist
	rsh $(HOST) "cd $(DIST_SRC) ; make install_rccd"

rccd: $(SOBJ)
	cc -o rccd $(CFLAGS) $(SOBJ) $(LIB)

install_rccd: rccd
	chmod ugo+x rccd
	cp rccd $(RCCD_BIN)

ROBJ	= rcc.o short.o remote.o fileio.o
RSRC	= rcc.c short.c remote.c fileio.c Makefile rcc.h rcccomm.h

rcc:	$(ROBJ)
	cc -o rcc $(ROBJ)

dist_rcc: $(RSRC)
	echo "($(RSRC)) -> ($(RCC_HOST))" > /tmp/rcc.rdist
	echo "	install -y $(RCC_DIST) ;" >> /tmp/rcc.rdist
	rdist -f /tmp/rcc.rdist

install_rcc: rcc
	chmod ugo+x rcc
	(cd $(RCC_BIN) ; \
	 rm -f rcc rld ras rnm rlint rranlib rar rdosld)
	cp rcc $(RCC_BIN)/rcc
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rld
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/ras
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rnm
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rlint
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rranlib
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rar
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rdosld
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rasm286
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/ras286
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rbld286
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rbnd286
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/romc286
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rcmo286
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rsize
	-ln -s $(RCC_BIN)/rcc $(RCC_BIN)/rmasm

#
#
#	House cleaning
#
#
ci:
	-ci $(CIFLAGS) $(SSRC) $(RSRC)

clean:	ci
	rm *.o rcc LOG

shar: Readme $(SSRC) $(RSRC)
	rm -f /tmp/files
	for file in Readme $(SSRC) $(RSRC) ; do \
	    echo $$file >> /tmp/files; \
	done
	shar `cat /tmp/files | sort | uniq` > rcc.shar
SHAR_EOF
if test -f 'Readme'
then
	echo shar: over-writing existing file "'Readme'"
fi
cat << \SHAR_EOF > 'Readme'
Remote Compilation Facility
---------------------------

This commands allow you to remotely compile programs. They're currently
designed to work with an Intel 310 running Microsoft XENIX, but they're
fairly general.

Requirements:
	+ To Execute:
	    A Unix-like system which support 'rsh'.
	+ To Compile:
	    Make, a version of the 'ndir' library for Xenix (check your
	    excelan source directory for this)
	+ To Distribute automatically:
	    rdist. You can do this by hand if you've not ported this.

Use:
	In a normal environment, just use the following commands, with
	the same arguments you'd use on the remote machine itself.

		rcc	-- remote cc
		rnm	-- remote nm
		.. etc .. 

	There are a great many "remote utilities" which are linked to the
	rcc binary. Each executes the appropriate program.

	These will run the compilations on a ``default host''. You can
	also set the environment variable RCC_HOST to the desired host
	and RCC_SERVER to the path name of the server program.

Implementation:
	All the commands are symbolic links to 'rcc'. Rcc establishes a
	connection to a server running on the remote machine using 'rsh'.
	Rcc parses the command string, determining which arguments are files.
	Files ending in .c and .s are sent through the local version of
	/lib/cpp (meaning that all include files must be on *your* machine,
	not the remote machine).

	Any translation of the /lib/cpp information is done by the server
	(e.g. translating ``# 10 "foo.s'' to ``#line 10 "foo.s"'' for Xenix).

	All .o or .a files are sent straight through.

	The remote command is executed, and a selected set of the files which
	were sent over is deleted on the remote system (i.e. we don't delete
	the .a file in a ranlib, since we want it back).

	All other files in the remote directory are returned to the current
	local directory. Files will have the same name and chmod() information
	as on the remote system.

Bugs:
	You'll notice the following problems:

	+ You must have your .rhosts set up. I.e. if my remote host is A
	  and I'm on B, make certain that you can RCP a file from A to
	  and B and from B to A without error.

	+ ranlib time-stamps the final library. You'll get many messages
	  concerning out of date libraries. Ignore them.

	+ It's not exceedingly robust -- the server program and the
	  rcc program maintain a transmission protocol, and it's possible
	  for some weird error on the remote side to cause errors on
	  the local side. However, I haven't noticed any such problems

	+ If you get the message "end-of-file in middle of protocol", this
	  means that the server has died.

Local Information:
	Localaly (uiucdcsg, uiucdcsm) these files are in the directory
	/mntm/1/reed/picasso/bin, with source in /mntm/1/reed/picasso/rcc.

	  
SHAR_EOF
if test -f 'fileio.c'
then
	echo shar: over-writing existing file "'fileio.c'"
fi
cat << \SHAR_EOF > 'fileio.c'
#include <stdio.h>
#include <sys/types.h>

#ifdef	M_XENIX
#include "ndir.h"
#else
#include <sys/dir.h>
#endif
#include <sys/stat.h>
#include "rcc.h"

extern int Debug;
extern char *me;

/*
 *	IMPORTANT: both getFile and sendFile use this buffer, but they are
 *	never executed at the same time on the same machine, so it's o.k.
 *
 *	We define this here so that XENIX versions don't barf on excessive
 *	stack growth
 */

#define	BIG_BUFFER	16384
char	BigBuffer[BIG_BUFFER];

sendFile(rfd, fileName, remoteName)
FILE *rfd;
char *fileName;
char *remoteName;
{
    FILE *f;
    long fileSize;
    int bytes;
    struct stat fStat;
    
    if ((f = fopen(fileName, "r")) == 0) {
	fprintf(stderr,"Unable to open %s\n", fileName);
	return(0);
    }
    
    fseek(f, 0L, 2);	/* determine the file size */
    fileSize = ftell(f);
    fseek(f, 0L, 0);	/* rewind */

    fstat(fileno(f), &fStat);
    
    if (Debug) {
	fprintf(stderr, "%s: Send %s as %s (size is %ld bytes) at mode %o\n",
		me, fileName, remoteName, fileSize, fStat.st_mode & 0777);
    }
    
    fprintf(rfd,"%c\n%s\n%u\n%ld\n",
	    RCC_IN, remoteName, fStat.st_mode & 0777, fileSize);
    
    while (fileSize > 0) {
	bytes = fread( BigBuffer, sizeof(char), BIG_BUFFER, f);
	fwrite( BigBuffer, sizeof(char), bytes, rfd);
	fileSize -= bytes;
    }
    fflush(rfd);
    fclose(f);
    
    if (Debug) {
	fprintf(stderr,"%s: finished sending file\n", me);
    }
}

getFile(rfd)
FILE *rfd;
{
    char cmdName[BUFSIZ];
    char fileName[BUFSIZ];

    FILE *f;
    long bytes;
    int toRead;
    int l;
    int newMode;
    long atol();

    if (feof(rfd)) {
	fprintf(stderr,"%s: end-of-file in getFile\n", me);
	cleanExit(1);
    }

    fgets(cmdName, BUFSIZ, rfd);
    l = strlen(cmdName);
    if (cmdName[l-1] == '\n') {
	cmdName[l-1] = 0;
    }
    sprintf(fileName, "./%s", cmdName);
    if ( ! (f = fopen(fileName, "w"))) {
	perror("fopen");
/*
 *	To maintain better reliability, whenever we botch a file open, we
 *	dump the contents into /dev/null to keep the protocol happy.
 */

	f = fopen("/dev/null", "w");
    }

    if (Debug) {
	fprintf(stderr,"%s: get chmod information\n", me);
	fflush(stderr);
    }

    fgets(cmdName, BUFSIZ, rfd);
    newMode = atoi(cmdName);
    if (Debug) {
	fprintf(stderr,"%s: file mode is %o\n", me, newMode);
    }
    chmod(fileName, newMode);

    fgets(cmdName, BUFSIZ, rfd);
    bytes = atol(cmdName);
    
    if (Debug) {
	fprintf(stderr,"%s: read %s (%ld bytes)\n",
		me, fileName, bytes);
    }
    
    while (bytes > 0L) {
	toRead = ( bytes > BIG_BUFFER) ? BIG_BUFFER : bytes;
	l = fread(BigBuffer, sizeof(char), toRead, rfd);
	if (l <= 0) {
	    perror("fread");
	    cleanExit(1);
	}
	fwrite(BigBuffer, sizeof(char), l, f);
	bytes -= l;
    }
    
    fclose(f);
    if (Debug) {
	fprintf(stderr,"%s: Finished loading file\n", me);
    }
}

sendDirectory(dirName,rfd)
char *dirName;
FILE *rfd;
{
    DIR *dir;
    struct direct *entry;
    
    if (Debug) {
	fprintf(stderr,"%s: open directory \"%s\"\n",
		me, dirName);
    }
    
    dir = opendir(dirName);
    
    if (dir == 0) {
	fprintf(stderr,"%s: Unable to open directory %s\n",
		me, dirName);
    } else {
	
	if (Debug) {
	    fprintf(stderr,"Reading directory\n");
	}
	
	while( (entry = readdir(dir)) != 0) {
	    if ( *(entry -> d_name) != '.') {	/* don't send . and .. */
		if (Debug) {
		    fprintf(stderr,"%s: directory send  %s\n",
			    me, entry -> d_name);
		}
		sendFile(rfd, entry -> d_name, entry -> d_name);
	    }
	}
    }
    if (Debug) {
	fprintf(stderr,"%s: send ACK\n", me);
    }
    fprintf(rfd, "ACK\n");
    fflush(rfd);
}
    
getDirectory(rfd)
FILE *rfd;
{
    char cmdBuffer[BUFSIZ];
    char proper[10];

    sprintf(proper,"%c\n", RCC_IN);

    for(;;) {
	if (Debug) {
	    fprintf(stderr,"%s: waiting for directory command\n", me);
	}
	if (fgets(cmdBuffer, BUFSIZ, rfd) == 0) {
	    if (feof(rfd)) {
		fprintf(stderr,"%s: end-of-file in middle of protocol\n", me);
	    } else {
		perror("fgets");
	    }
	    return(0);
	}
	if (Debug) {
	    fprintf(stderr,"%s: got directory command \"%s\"\n",
		    me, cmdBuffer);
	}
	if (strcmp(cmdBuffer, "ACK\n") == 0) break;
	if (strcmp(cmdBuffer, proper) != 0) {
	    fprintf(stderr,"%s: protocol snafu, got \"%s\"\n",
		    me, cmdBuffer);
	    cleanExit(1);
	}
	getFile(rfd);
    }
}
SHAR_EOF
if test -f 'rcc.c'
then
	echo shar: over-writing existing file "'rcc.c'"
fi
cat << \SHAR_EOF > 'rcc.c'
/*
 *	The remote CC command
 */

#include <stdio.h>
#include <signal.h>
#include "rcc.h"
#include "rcccomm.h"

extern FILE *remote();
extern char *shortFileName();
extern char *getsuffix();
extern char *getenv();

int Debug = 0;
char *me;
int iAm;
int exitStatus;

extern FILE *rfdWrite;
extern FILE *rfdRead;

#define	MAX_ARGS	128

char	*cppArgs[MAX_ARGS];
int	cppArgCnt;
char	*rccArgs[MAX_ARGS];
int	rccArgCnt;
char	*killArgs[MAX_ARGS];
int	killArgCnt;

main(argc, argv)
int argc;
char **argv;
{
    int argvNum;
    int i;
    int noKill;
    char inputStr[BUFSIZ];

    char *rccHost = getenv("RCC_HOST");
    char *rccServer = getenv("RCC_SERVER");

    me = argv[0];
    while (*me != 0) me++;
    while (me != argv[0] && *me != '/') me--;
    if (*me == '/') me++;

    noKill = 0;

    for (i = 0;
	 cmdList[i].name != 0 && strcmp(me, cmdList[i].name) != 0;
	 i++);

    if (cmdList[i].name == 0) {
	fprintf(stderr,"Remote \"%s\" not implemented yet\n", me);
	cleanExit(1);
    }

    iAm = cmdList[i].key;

    if (iAm == RAR) {
	noKill = 2;    /* the archive */
    } else if (iAm == RRANLIB) {
	noKill = 1;    /* the archive */
    }

    if (rccHost == 0) {
	rccHost = HOST;
    }
    if (rccServer == 0) {
	rccServer = RCCD;
    }

    if ( ! remote(rccHost, rccServer, 1) ) {
	fprintf(stderr,"Can't establish remote connection\n");
    }
    
    rccArgCnt = 0;
    cppArgCnt = 0;
    killArgCnt = 0;

    addArg(rccArgs,me);	/* command name */
    addArg(rccArgs,me);	/* arg0 */

    addArg(cppArgs,"/lib/cpp");
    
    for (argvNum = 1; argvNum < argc; argvNum++) {
	
	if (argv[argvNum][0] == '-') {
	    switch(argv[argvNum][1]) {
	    case 'd' :
		if (argv[argvNum][2] == 0) {
		    Debug = 1;
		}
		break;

	    case 'o' :  /* object file */
		argvNum++;
		addArg(rccArgs, "-o");
		addArg(rccArgs, argv[argvNum]);
		break;
		
	    case 'D':
	    case 'U':
	    case 'I':
		addArg(cppArgs, argv[argvNum]);
		break;
		
	    default:
		addArg(rccArgs, argv[argvNum]);
		break;
	    }
	    /*
	     *	File name
	     */
	} else {
	    char *suffix = getsuffix(argv[argvNum]);
	    char *shortName = shortFileName(argv[argvNum]);
	    FILE *f;
	    
	    if (access(argv[argvNum], 4) == -1) {
		addArg(rccArgs, argv[argvNum]);
	    } else if (strcmp(suffix, "c") == 0 || strcmp(suffix, "s") == 0) {
		addArg(killArgs, shortName);
		addArg(rccArgs, shortName);
		sendCppFile(rfdWrite, argv[argvNum], shortName);
	    }
	    else if (strcmp(suffix, "o") == 0 ) {
		addArg(killArgs, shortName);
		addArg(rccArgs, shortName);
		sendFile(rfdWrite, argv[argvNum], shortName);
	    }
	    else if (strcmp(suffix,"a") == 0) {
		if (argvNum != noKill) {
		    addArg(killArgs, shortName);
		}
		addArg(rccArgs, shortName);
		sendFile(rfdWrite, argv[argvNum], shortName);
/*
 *	rar argument doesn't start with a dash
 */

	    } else if (iAm == RAR && argvNum == 1) {
		addArg(rccArgs, argv[argvNum]);
	    } else {
/*
 *	We can read the file, but don't know what it means
 */
		fprintf(stderr,"[%s: sending %s as %s]\n",
			me, argv[argvNum], shortName);
		sendFile(rfdWrite, argv[argvNum], shortName);
		addArg(rccArgs, shortName);
	    }
	}
    }

/*
 *	Run the command in the remote directory
 */
    if (Debug) {
	fprintf(stderr,"%s: send exec of %s\n",me, rccArgs[0]);
    }

    fprintf(rfdWrite,"%c\n", RCC_EXEC);
    for (i = 0; rccArgs[i] != 0 ; i++) {
	if (Debug) {
	    fprintf(stderr,"%s: Arg[%2d] = %s\n",
		    me, i, rccArgs[i]);
	}
	fprintf(rfdWrite,"%s\n", rccArgs[i]);
    }
    fputs("\n", rfdWrite);	/* blank line -- end of args */
    fflush(rfdWrite);

    fgets(inputStr, BUFSIZ, rfdRead);	/* wait for exit status */

    if (Debug) {
	printf("%s: got exit status message \"%s\"\n", me, inputStr);
    }
    exitStatus = (atoi(inputStr) >> 8) & 0xff;

/*
 *	Kill remote files we don't want
 */

    for (i = 0; i < killArgCnt; i++) {
	fprintf(rfdWrite, "%c\n%s\n", RCC_KILL, killArgs[i]);
    }
    fflush(rfdWrite);
/*
 *	Now, get back all the files which havn't been deleted
 */
    fprintf(rfdWrite,"%c\n", RCC_SENDALL);
    fflush(rfdWrite);
    if (Debug) {
	fprintf(stderr,"About to read directory\n");
    }

    getDirectory(rfdRead);

    cleanExit(exitStatus);
}

cleanExit(i)
int i;
{
    char cmdName[BUFSIZ];

    if (rfdWrite != 0) {
	fprintf(rfdWrite,"Q\n");
	fflush(rfdWrite);
    }
    remoteKill();
    exit(i);
}

addArg(argList, arg)
char **argList;
char *arg;
{
    if (argList == cppArgs) {
	if (Debug) {
	    fprintf(stderr,"Add CPP: %s\n", arg);
	}
	cppArgs[cppArgCnt++] = arg;
	cppArgs[cppArgCnt] = 0;
    }
    else if (argList == rccArgs) {
	if (Debug) {
	    fprintf(stderr,"Add RCC: %s\n", arg);
	}
	rccArgs[rccArgCnt++] = arg;
	rccArgs[rccArgCnt] = 0;
    }
    else if (argList == killArgs) {
	if (Debug) {
	    fprintf(stderr,"Add KILL: %s\n", arg);
	}
	killArgs[killArgCnt++] = arg;
	killArgs[killArgCnt] = 0;
    }
    else {
	fprintf(stderr,"bad logic in addArg\n");
	cleanExit(1);
    }
}

sendCppFile(rfd, fileName, remoteName)
FILE *rfd;
char *fileName;
char *remoteName;
{
    FILE *f = fopen(fileName, "r");
    char buffer[BUFSIZ];
    int status;
    int i;
    
    if (f == 0) {
	fprintf(stderr,"Unable to open %s\n", fileName);
	return(0);
    }
    fclose(f);
    
    if (Debug) {
	fprintf(stderr, "%s: CPP %s as %s \n",
		me, fileName, remoteName);
    }
    
    fprintf(rfd,"%c\n%s\n",
	    RCC_CPP, remoteName);
    fflush(rfd);

    if (fork() == 0) {

/*
 *	By placing this after the fork(), we don't touch the parents
 *	cppArgs list, allowing us to re-use it later
 */

	addArg(cppArgs, fileName);

	if (Debug) {
	    for (i = 0; cppArgs[i] != 0; i++) {
		fprintf(stderr, "cpp[%2d] %s\n", i, cppArgs[i]);
	    }
	}

	fclose(stdin);

	dup2(open(fileName,0), 0);

	dup2(fileno(rfdWrite), 1);

	execv("/lib/cpp", cppArgs);
    } else {
	wait(&status);

	if (status != 0) cleanExit( (status >> 8) & 0xff );

	if (Debug) {
	    fprintf(stderr,"%s: cpp done, send end line\n", me);
	}
	fprintf(rfd,"##ENDFILE##\n");
    }
}
SHAR_EOF
if test -f 'rcc.h'
then
	echo shar: over-writing existing file "'rcc.h'"
fi
cat << \SHAR_EOF > 'rcc.h'
/*
 *	Remote CC commands
 */

#define	RCC_EXEC	'X'	/* execute a command */
#define	RCC_IN		'I'	/* move a file to the server machine */
#define	RCC_CPP		'C'	/* accept a file run through CPP */
#define	RCC_OUT		'O'	/* return a file	*/
#define	RCC_KILL	'K'	/* kill a named file */
#define RCC_SENDALL	'S'	/* send all remaining files */
#define	RCC_QUIT	'Q'	/* quit */
SHAR_EOF
if test -f 'rcccomm.h'
then
	echo shar: over-writing existing file "'rcccomm.h'"
fi
cat << \SHAR_EOF > 'rcccomm.h'

/*
 *	Allowed Remote Execs
 */

#define	RCC	1
#define	RLD	2
#define	RAS	3
#define	RAR	4
#define	RRANLIB	5
#define	RLINT	6
#define	RNM	7
#define	RECHO	8
#define	RDOSLD	9
#define RMASM	10
#define RSIZE	11
#define	RASM286	12
#define RAS286	13
#define	RBLD286	14
#define	RBND286 15
#define	RCMO286	16
#define	RLIB286	17
#define	RMAP286	18
#define	ROMC286	19
#define	ROVL286	20
#define	RXLC286	21

struct {
    int key;
    char *name;
    char *file;
} cmdList[] = {
    {RCC, "rcc", "/bin/cc"},
    {RAS, "ras", "/bin/masm"},
    {RLD, "rld", "/bin/ld"},
    {RAR, "rar", "/bin/ar"},
    {RRANLIB, "rranlib", "/bin/ranlib"},
    {RLINT, "rlint", "/usr/bin/lint"},
    {RNM, "rnm", "/bin/nm"},
    {RDOSLD, "rdosld","/usr/bin/dosld"},
    {RECHO, "recho", "/bin/echo"},
    {RMASM, "rmasm", "/bin/masm"},
    {RSIZE, "rsize", "/bin/size"},
    {RASM286, "rasm286", "/usr/intel/bin/asm286"},
    {RAS286, "ras286", "/usr/intel/bin/as286"},
    {RBLD286, "rbld286", "/usr/intel/bin/bld286"},
    {RBND286, "rbnd286", "/usr/intel/bin/bnd286"},
    {RCMO286, "rcmo286", "/usr/intel/bin/cmo286"},
    {RLIB286, "rlib286", "/usr/intel/bin/lib286"},
    {RMAP286, "rmap286", "/usr/intel/bin/map286"},
    {ROMC286, "romc286", "/usr/intel/bin/omc286"},
    {ROVL286, "rovl286", "/usr/intel/bin/ovl286"},
    {RXLC286, "rxlc286", "/usr/intel/bin/xlc286"},
    {0,0}
};

SHAR_EOF
if test -f 'remote.c'
then
	echo shar: over-writing existing file "'remote.c'"
fi
cat << \SHAR_EOF > 'remote.c'
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>

/*
 *    A remote exec facility for XENIX systems based on rsh
 */

FILE *rfdRead;
FILE *rfdWrite;

static int remotePid = 0;

extern int Debug;
extern char *me;

int
remote(hostName, cmdName, monitor)
char *hostName;
char *cmdName;
int monitor;
{
    int toChild[2];
    int toParent[2];
    char *arg[128];
    int args;
    char cmd[BUFSIZ];

    char *rsh = "/usr/ucb/rsh";

    fflush(stdout);
    fflush(stderr);
    pipe(toChild);
    pipe(toParent);
    if ( (remotePid = fork()) != 0) {
	rfdWrite = fdopen(toChild[1], "w+");
	rfdRead  = fdopen(toParent[0], "r+");
	close(toChild[0]);
	close(toParent[1]);
	return(1);
    } else {

/*
 *    The program executing rsh will have the pipe files as stdin and stdout
 */

	fclose(stdin);
	fclose(stdout);

	dup2(toChild[0], 0);
	dup2(toParent[1], 1);
	close(toChild[1]);
	close(toParent[0]);

	args = 0;
	arg[args++] = "/usr/ucb/rsh";
	arg[args++] = hostName;
	arg[args++] = "exec";
	if (Debug) {
	    sprintf(cmd, "%s -d", cmdName);
	    arg[args++];
	} else {
	    arg[args++] = cmdName;
	}
	arg[args++] = 0;

	execv(rsh, arg);
    }
}

remoteKill2()
{
    static int killAttempts = 0;

    if (remotePid != 0) {
	if (Debug) {
	    fprintf(stderr,"%s: kill process %d\n", me, remotePid);
	}
	switch( killAttempts ) {
	case 0 :  kill(SIGINT, remotePid);
	    break;

	case 1 : kill(SIGHUP, remotePid);
	    break;

	case 2 : kill(SIGKILL, remotePid);
	    break;
	}
	killAttempts++;
	alarm(2);
    }
}

	
remoteKill()
{
    int status;

    while(remotePid != 0) {
	signal(SIGALRM, remoteKill2);
	alarm(5);
	if (Debug) {
	    fprintf(stderr,"%s: prepare to wait for %d to die\n",
		    me, remotePid);
	}
	if (wait(&status) == -1) return;
	if (Debug) {
	    fprintf(stderr,"%s: waiting for %d to die\n", me);
	}
    }
}
SHAR_EOF
if test -f 'server.c'
then
	echo shar: over-writing existing file "'server.c'"
fi
cat << \SHAR_EOF > 'server.c'
/*
 *	Compiler Server
 *
 *	This server is started by the remote execution daemon.
 *	When a session is started, a directory is created, files
 *	are  copied (from the remote host) into that directory,
 *	
 */
#include <stdio.h>
#include <signal.h>
#include "rcc.h"
#include "rcccomm.h"

#define	PATH_LEN 512

char	remoteDir[PATH_LEN];

char	*cmdArgs[64];
int	cmdArgCnt;

char	cmdBuffer[1024];
char	cmdName[512];

int	Debug = 0;
char	*me;
int	exitStatus;

extern	char	*malloc();
extern	long	atol();

cleanExit(i)
int i;
{
    char sysCmd[512];

    signal(SIGHUP, SIG_DFL);
    signal(SIGINT, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);

    if (Debug) {
	fprintf(stderr,"%s: normally, I would delete %s now\n",
		me, remoteDir);
    } else {
	sprintf(sysCmd,"cd /usr/tmp ; rm -rf %s", remoteDir);
	system(sysCmd);
    }
    exit(i);
}

main(argc,argv)
int argc;
char **argv;
{
    int flag;
    int cmds;
    int i,l;

    me = argv[0];
    if (argv[1] != 0 && argv[1][0] == '-' ) {
	Debug = 1;
    }

    sprintf(remoteDir,"/usr/tmp/rcc.%d", getpid());

    cmdArgCnt = 0;
    addArg(cmdArgs, "/bin/mkdir");
    addArg(cmdArgs, remoteDir);

    if ((i = doExec("/bin/mkdir")) != 0) {
	perror("mkdir");
	fprintf(stderr,"%s: Unable to create directory: %s (%d)\n",
		me, remoteDir, i);
	cleanExit(1);
    }

    signal(SIGHUP, cleanExit);
    signal(SIGINT, cleanExit);
    signal(SIGPIPE, cleanExit);
    signal(SIGALRM, cleanExit);
    alarm(60 * 10);			/* kill strays after 10 minutes */

    if (chdir(remoteDir) == -1) {
	perror("chdir");
	cleanExit(1);
    }

/*
 *	Enter the command pooling loop
 */

    for(;;) {

	if (feof(stdin)) cleanExit(0);
	gets(cmdName);
	flag = cmdName[0];

	if (Debug) {
	    fprintf(stderr,"%s: Got command %c\n",
		    me, flag);
	}

	switch( flag ) {
	case RCC_EXEC:
	    cmds = 0;
	    gets(cmdName);

	    if (Debug) {
		fprintf(stderr,"%s : Command name is %s\n",
			me, cmdName);
	    }

	    cmdArgCnt = 0;

	    do {
		gets(cmdBuffer);
		if (Debug) {
 		    fprintf(stderr,"%s: Got: %s\n",
			    me, cmdBuffer);
		}
		l = strlen(cmdBuffer);
		if (l != 0) {
		    char *p;	/* local var */

		    p = malloc(l+20);

		    if (p == (char *) 0) {
			fprintf(stderr,"%s: malloc fails!\n",
				me);
			cleanExit(1);
		    }

		    strncpy(p, cmdBuffer, l+1);

		    if (Debug) {
			fprintf(stderr,"%s: Arg[%2d]: %s\n",
			    me, cmds++, p);
			fflush(stderr);
		    }

		    addArg(cmdArgs, p);
		}
	    } while (l != 0);

	    /*
	     * Given the command name, find the real command
	     */
	    fflush(stdout);
	    if (Debug) {
		fprintf(stderr,"%s: Look for \"%s\"  in the command list\n",
		    me, cmdName);
	    }
	    for (i = 0; cmdList[i].name != 0 ; i++) {
		if (strcmp(cmdName, cmdList[i].name) == 0) {
		    if (Debug) {
			fprintf(stderr,"%s: found %s\n",
				me, cmdList[i].name);
		    }
		    exitStatus = doExec(cmdList[i].file);
		    break;
		}
	    }

	    fprintf(stdout,"%d\n", exitStatus);
	    fflush(stdout);
	    break;

	case RCC_IN:
	    getFile(stdin);
	    break;

	case RCC_CPP:
	    getCppFile(stdin);
	    break;

	case RCC_KILL:
	    {
		char *fileName[PATH_LEN];
		int i;

		gets(cmdName);
		sprintf(fileName,"./%s", cmdName);

		i = unlink(fileName);
		if (Debug) {
		    if (i == -1) {
			perror("unlink");
			fprintf(stderr,"%s: unable to delete %s\n",
				me, fileName);
		    }
		    else {
			fprintf(stderr,"%s: deleted %s\n",
				me, fileName);
		    }
		}
	    }
	    break;

	case RCC_SENDALL:
	    sendDirectory(remoteDir, stdout);
	    break;

	case RCC_QUIT:
	    if (Debug) {
		fprintf(stderr,"%s: RCC quits\n", me);
	    }
	    fprintf(stdout,"ACK\n");
	    fflush(stdout);
	    cleanExit(0);
	    break;

	default:
	    fprintf(stderr,"Invalid RCC command: %c\n", flag);
	    break;
	}
    }
    cleanExit(0);
}

int
doExec(cmd)
char *cmd;
{
    int status;
    int i;
    
    cmdArgs[0] = cmd;

    if (Debug) {
	char **p;
	int i;
	fprintf(stderr,"%s: EXEC %s\n", me, cmd);
	for (p = cmdArgs, i = 0; *p != 0; p++, i++) {
	    fprintf(stderr,"%s: Arg[%2d] = %s\n",
		    me, i, *p);
	}
	fflush(stderr);
    }
    
    fflush(stdout);
    fflush(stderr);
    
    if (fork() == 0) {
	/*
	 *	Make the stdout of the new process be the stderr of the current
	 *	process
	 */
	fclose(stdout);
	dup2(2,1);

	freopen("/dev/null", "r", stdin);
	fflush(stderr);
	
	i = execv(cmd, cmdArgs);
/*
 *	If the execv failes, it might be a shell script which wasn't
 *	detected (it happens on our 310 -- I assume that Xenix is brain-dead
 *	about this.
 *
 *	So, shift over the args, and execut /bin/sh
 */
	if (access(cmd,1) == 0) {
	    for (i = 0; cmdArgs[i] != 0; i++);
	    for (; i >= 0; i--) {
		cmdArgs[i+1] = cmdArgs[i];
	    }
	    cmdArgs[0] = "/bin/sh" ;
	    execv("/bin/sh", cmdArgs);
	}
	perror("execv");
	fprintf(stderr,"%s: execve failed with %d\n", me, i);
	fprintf(stderr,"%s: command was %s\n", me, cmd);
	for (i = 0; cmdArgs[i] != 0; i++) {
	    fprintf(stderr,"%s: Arg[%2d] = %s\n",
		    me, i, cmdArgs[i]);
	}
	cleanExit(1);
    }
    
    i = wait(&status);
    
    if (Debug) {
	fprintf(stderr,"%s: Child returns with status %d (%d)\n",
		me, status, i);
    }
    
    if (i != -1) {
	return(status);
    } else {
	return(0);
    }
}


/*
 *	CPP files come across as a file name followed by the file, line
 *	by line. It ends with a line ##ENDFILE##.
 */

getCppFile(rfd)
FILE *rfd;
{
    char cmdName[BUFSIZ];
    char fileName[BUFSIZ];
    FILE *f;
    int done;
    int cppLines;
    char *getsuffix();
    
    gets(cmdName);
    sprintf(fileName, "./%s", cmdName);
    if ( ! (f = fopen(fileName, "w"))) {
	perror("fopen");
	return(0);
    }

/*
 *	Check if this is an assembler file (.s), since masm can't deal
 *	with #line directives
 */

    cppLines = 1;
    if (strcmp(getsuffix(fileName),"s") == 0) {
	cppLines = 0;
    }

    if (Debug) {
	fprintf(stderr,"%s: starting loading %s\n", me, fileName);
    }

    done = 0;
    while(!done) {
	if (fgets(cmdName, BUFSIZ, rfd) == 0) {
	    perror("fgets");
	    fprintf(stderr,"%s: end of file in middle of CPP file\n");
	    cleanExit(1);
	} else if (cmdName[0] == '#') {
	    if (strcmp(cmdName,"##ENDFILE##\n") == 0) {
		done = 1;
	    }
#ifdef	M_XENIX
/*
 *	Convert ^# to ^#line
 */
	    else if (cppLines) {
		fputs("#line", f);
		fputs(&cmdName[1], f);
	    }
#else	M_XENIX
	    else if (cppLines) {
		fputs(cmdName,f);
	    }
#endif

	} else {
	    fputs(cmdName, f);
	}
    }
    fclose(f);
    if (Debug) {
	fprintf(stderr,"%s: done loading file\n", me);
    }
    return(1);
}

addArg(argList, arg)
char **argList;
char *arg;
{
    if (argList == cmdArgs) {
	if (Debug) {
	    fprintf(stderr,"Add CMD: %s\n", arg);
	}
	cmdArgs[cmdArgCnt++] = arg;
	cmdArgs[cmdArgCnt] = 0;
    } else {
	fprintf(stderr,"bad logic in addArg\n");
	cleanExit(1);
    }
}
SHAR_EOF
if test -f 'short.c'
then
	echo shar: over-writing existing file "'short.c'"
fi
cat << \SHAR_EOF > 'short.c'
/*
 *	shortFileName removes all leading slashes from the file name
 *	and gives the extension "ext". You must use the value of
 *	the function before calling it again.
 */

#include <stdio.h>
extern char *me;

char *
shortFileName(name,ext)
char *name;
{
    char *newName;
    char *malloc();
    char   *p;
    int len;

    for (p = name; *p != 0; p++);/* move to end */

    for (; (p != name) && (*p != '/'); p--);/* backup to slash */
    if (p != name)
	p++;			/* skip slash		 */

    len = strlen(p) + 1;
    newName = malloc( len + 20 );
    if (newName == (char *) 0) {
	fprintf(stderr,"%s: Ran out of malloc!\n", me);
	return(p);	/* try to cover our ass */
    }
    strncpy(newName, p, len+1);
    return (newName);
}

char *
getsuffix(name)
char *name;
{
    char   *p;

    for (p = name; *p != 0; p++);/* move to end */

    for (; (p != name) && (*p != '.'); p--);/* backup to period */
    if (p != name)
	p++;			/* skip period		 */
    return (p);
}

SHAR_EOF
#	End of shell archive
exit 0

wen-king@VLSI.CALTECH.EDU (Wen-King Su) (07/17/87)

In article <309@hubcap.UUCP> grunwald@M.CS.UIUC.EDU writes:
>Tired of developing your iPSC applications on the 310? Want to use EMACS, RCS
>and your other normal tools?
>
>Announcing, 'rcc', the remote C compiler server.

Here in Caltech, we have been running a locally developed environment
that reduces the 310 to merely an advanced device driver and compiler
engine.  We are not only able to compile cube applications from any
hosts connected to our network, we are also able to allocate subcubes,
run host programs, spawn cube programs, etc all without ever having to
touch the 310 directly.  Since the host programs run right on the Unix
machine you are using, without any special arrangements, you can have
your SUN3/160 display the Mandlebrot set generated by your cube; you
write a host program that opens a SUN window and communicates with
other cube processes as if itself is one of the cube processes.  This
environment, called the Cosmic Environment, brings together the cube
and any network connected resources in a transparent way (upto the
level of byte order compatibility).  We have users working, compiling
program, and running jobs from other departments, from across L.A., and
all the way north to the state of Washington.  Long distance computing
is a bit slow however.  This cube/software combo is very popular here
and users sometimes resort to verbal abuse to get a share of the cube.

For some time, Intel has worked to port this environment.  The result
is included in Intel's software distribution as part of the unsupported
protocode package.  We have not talked to them about this for a while
and cooperation has wained on this.  The main reason for the difficulty
in porting Cosmic Environment to the current iPSC is that, due to
historical reasons, our set of utilities and message functions are
different from Intel's and the patch work gets a little messy.  We
shaped our software after what is available to us and how we use the
machine.  For example, since we do not have an extended memory machine
and we do not have vector options, we could not add facilities in our
environment to deal with of them.  Part of what Intel did is try to
modify the Cosmic Environment so that it handles these other options.
Intel gives considerations to those who intend to run these cubes stand
alone and write programs in fortran.  We did not design in the support
for either because our computing resources consist a network of Unix
boxes and programmers who speak C as their second language.

There is no right side on this, but I think there should have been
more cooperation between us and Intel in the begining when the iPSC is
still in development and we were trying to put together a generalized
host environment.  For the next generation cube, Intel gets to start
over and we intend to get a better cooperation with them.  We hope this
environment or a later revision of it (after working it out with all
interested parties), will set a standard that most vendors can agree
on, easing the portability problem.  I can't say on this, but I can
tell you that we have a very good start on this.

Right now, if you have a plain iPSC sitting on a network of 4.2BSD
machines the latest Cosmic Environment is available for you on Unix
tar tape or via ARPANET.  Requests should be sent to:

Dr. Charles L. Seitz
Computer Science 256-80
California Institute of Technology
California, 91125

Or by electronic mail to "chuck@vlsi.caltech.edu".

I do not think I can post the software on the net because it approaches
1 megabyte in source form.  Beside, the big brothers are watching.

+--------------------------------------------------------------------------+
| Wen-King Su  wen-king@vlsi.caltech.edu  Caltech Corp of Cosmic Engineers |
+--------------------------------------------------------------------------+