/*
 * compServerUtils.c - utilities for the mimosa and cinder stubs
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <signal.h>
#ifdef sun
#include <mntent.h>
#elif ←IBMR2
/*
 * for compatability with SunOS
 */
#include <fstab.h>
#define MOUNTED     ""     /* bogus value for compatability purposes only */
struct mntent {
	char *mnt←dir;
};

static FILE *setmntent();
static struct mntent *getmntent(); 

#endif

#include "compServer.h"
#include "compServerDefs.h"
/* maximum number of machines of each architecture type for use
 * as cross-compile servers
 */
#define MAXNUMMACH   5
/*
 * the automount map prefix for referring to machines on the net
 */
#define NET←AUTOMOUNT←PREFIX   "/net"
#define AUTOMOUNT←PREFIX       "/tmp←mnt"
#define MIMOSA←SERVER          "MIMOSA←SERVER"

static char *getRandomMach();
static char *getMachInner();
extern char *getwd();

extern char **environ;

char *archNames[] = { "unknown", "sun4", "rs6000", "dec" };

extern JobStatus JobStatusFromStatus(sstatus)
int sstatus;
{
    return (JobStatus)sstatus;
}

extern void ExitSuccessfully()
{
    exit(0);
    /*NOTREACHED*/
}	

/* command line switches/args routines */

extern void setSwitch(h, sw)
Handle          h;
Switches sw;
{
    h->job.switches[(int)sw - 'a'] = TRUE;
}

extern void resetSwitch(h, sw)
Handle          h;
Switches sw;
{
    h->job.switches[(int)sw - 'a'] = FALSE;
}

extern int isSwitchSet(h, sw)
Handle          h;
Switches sw;
{
    return h->job.switches[(int)sw - 'a'];
}

/* process/command execution routines */

extern int CallSys(h, fileArg, argVector)
Handle h;
char *fileArg, **argVector;
{
#ifdef ←POSIX←SOURCE
    int status;
#elif sun 
    union wait status;
#else
    --->>  status declaration <<---
#endif
	int pid, i;
    
    for (i = 0; argVector[i]; i++) {
	Verbose(h, argVector[i]);
	Verbose(h, " ");
    }
    Verbose(h, "\n");
    
    /* if dryrun, don't actually do the op */
    if (isSwitchSet(h, (Switches)'d'))
	return 0;  
    
    switch (pid = fork()) {
    case -1:
	Fatal(h, "No more processes\n");
	break;
    case 0:
	(void)execv(fileArg, argVector);
	Fatal2(h, "Couldn't exec \n", fileArg);
	break;
    }
    while (pid != wait(&status))
	;
#ifdef ←POSIX←SOURCE
    if ((pid = (status & 0377)) != 0)
#elif sun
    if ((pid = (status.w←status & 0377)) != 0) 
#else
	    ---->> status use <<-----
#endif
	Fatal2(h, "Fatal error in ", fileArg);
    
#ifdef ←POSIX←SOURCE
    return (status >> 8) & 0377;
#elif sun
    return (status.w←status>>8) & 0377;
#else
    ---->> status return <<-----
#endif
}

extern int csh←system(s)
char *s;
{
    int status;
    pid←t   pid, w;
    register void (*istat)(), (*qstat)();

    if ((pid = fork() ) == 0) {
	(void)execle("/bin/csh", "csh", "-f -c", s, (char *)0, environ);
	←exit(127);
    }
    istat = signal(SIGINT, SIG←IGN);
    qstat = signal(SIGQUIT, SIG←IGN);

    w = waitpid(pid, &status, 0);
    (void) signal(SIGINT, istat);
    (void) signal(SIGQUIT, qstat);
    return ((w == -1) ? -1: status);
}

extern int rsh←system(h, shell)
Handle h;
char	*shell;
{
    char *shellStr;
    int w;
    
    shellStr = malloc((unsigned) MAXCOMMANDSTRINGSIZE);
    
    (void)strcpy(shellStr, " "); /* so we can use strcat from here on out */
    if (getArch() != h->archtype) {
	char *mach = getMach(h, h->archtype);
	(void)strcpy(shellStr, "rsh ");
	(void)strcat(shellStr, mach);
	(void)strcat(shellStr, " \"cd ");
	(void)strcat(shellStr, getwd←wo←automount("."));
	(void)strcat(shellStr, "; ");
    }
    (void)strcat(shellStr, shell);
    
    w = csh←system(shellStr) >> 8;

    free(shellStr);

    return w;
}
    
    
extern int systeme(h, shell, commander, fileToWorkOn, archType, defSuccess)
Handle h;
char	*shell;
char    *commander;
char    *fileToWorkOn;
arch 	archType;
int defSuccess;
{
    int w;
    char *commanderStr, *shellStr;
    char *shmtype;
    char statusFile[25];
    
    commanderStr = malloc((unsigned) MAXCOMMANDSTRINGSIZE);
    shellStr = malloc((unsigned) MAXCOMMANDSTRINGSIZE);

    (void)strcpy(commanderStr, "COMMANDER←INITIAL←COMMAND=");
    (void)strcat(commanderStr, commander);
    (void)strcat(commanderStr, " ");
    (void)strcat(commanderStr, fileToWorkOn);
    Verbose(h, commanderStr);
    Verbose(h, "\n");
    
    (void)strcpy(shellStr, " "); /* so we can use strcat from here on out */
  /*
    if (getArch() != archType) {
	char *mach = getMach(h, archType);
	(void)strcpy(shellStr, "rsh ");
	(void)strcat(shellStr, mach);
	(void)strcat(shellStr, " 'cd ");
	(void)strcat(shellStr, getwd←wo←automount(fileToWorkOn));
	(void)strcat(shellStr, "; setenv COMMANDER←INITIAL←COMMAND \"");
        (void)strcat(shellStr, commander);
        (void)strcat(shellStr, " ");
        (void)strcat(shellStr, fileToWorkOn);
        (void)strcat(shellStr, "\"; ");
    }
    
    */
    if (getArch() != archType) {
	char *mach = getMach(h, archType);
	(void)strcpy(shellStr, "rsh ");
	(void)strcat(shellStr, mach);
	(void)strcat(shellStr, " 'cd ");
	(void)strcat(shellStr, getwd←wo←automount(fileToWorkOn));
        (void)strcat(shellStr, "; setenv COMMANDER←INITIAL←COMMAND \"");
	(void)strcat(shellStr, "cdf ");
	(void)strcat(shellStr, getwd←wo←automount(fileToWorkOn));
	(void)strcat(shellStr, "; ");
        (void)strcat(shellStr, commander);
        (void)strcat(shellStr, " ");
        (void)strcat(shellStr, fileToWorkOn);
        (void)strcat(shellStr, "\"; ");
    } else if (getArch() == RS6000) {
        (void)strcat(shellStr, "setenv COMMANDER←INITIAL←COMMAND \"");
	(void)strcat(shellStr, "cdf ");
	(void)strcat(shellStr, getwd←wo←automount(fileToWorkOn));
	(void)strcat(shellStr, "; ");
        (void)strcat(shellStr, commander);
        (void)strcat(shellStr, " ");
        (void)strcat(shellStr, fileToWorkOn);
        (void)strcat(shellStr, "\"; ");
      }
      
    (void)strcat(shellStr, shell);
    (void)strcat(shellStr, "-");
    (void)strcat(shellStr, stringFromArch(archType));

    if (getArch() != archType) {
        (void)strcat(shellStr, "; echo $status > ");
        (void)sprintf(statusFile, "%s%d", ".mimstat", getpid());
	(void)strcat(shellStr, statusFile);
	/* (void)unlink(statusFile); */
    }
    
    if (getArch() == archType) { /* only if you are compiling locally can
				    you hope to check for /pcrswap */
	shmtype = getenv("TOOLS←SHMTYPE");
	if (shmtype && !strcmp("mmap", shmtype)) {
	    if (access("/pcrswap", F←OK|W←OK))
		Fatal(h, "Error: TOOLS←SHMTYPE mmap requires writable /pcrswap directory\n");
	    (void)strcat(shellStr, " -shmtype mmap ");
	}
    }
    
    if (getArch() != archType) {
	(void)strcat(shellStr, "'");
    }
    if (!getenv("MIMOSA←NO←NULL")) {
	(void)strcat(shellStr, " > /dev/null ");
    }
    Verbose(h, shellStr);
    Verbose(h, "\n");
    
    if (isSwitchSet(h, (Switches)'d')) return defSuccess;
    
    if (putenv(commanderStr) != 0) {  /* set env variable */
	(void)free(commanderStr);
	(void)free(shellStr);
	Fatal(h, "Unable to set environment variable \"COMMANDER←INITIAL←COMMAND\"\n");
    }
    
    w = csh←system(shellStr) >> 8;
    
    if (getArch() != archType) {
    	FILE *f = fopen(statusFile, "r");
    	if (f == 0) {
    	    Fatal(h, "Couldn't read status file\n");
    	}
    	(void)fscanf(f, "%d", &w);
    	(void)fclose(f);
    	(void)unlink(statusFile);
    }
    	
    
    if (putenv("COMMANDER←INITIAL←COMMAND=") != 0) { /* unset env. variable */
	(void)free(commanderStr);
	(void)free(shellStr);
	Fatal(h, "Unable to unset environment variable \"COMMANDER←INITIAL←COMMAND\"\n");
    }
    
    (void)free(commanderStr);
    (void)free(shellStr);
    return w;
}

/* IO routines */

extern void PutLong(arg)
long arg;
{
    (void)printf("%ld", arg);
}

extern void PutC(ch)
char ch;
{
    (void)fputc(ch, stdout);
}

extern void PutS(arg)
char *arg;
{
    (void)fputs(arg, stdout);
    (void)fflush(stdout);
}

extern void echoArgs(h)
Handle h;
{
    Switches s;
    for (s = a; s <= Q; s++) {
	if (isSwitchSet(h, s)) {
	    PutC('-');
	    PutC((char)s);
	    PutC(' ');
	}
    }
}

extern void Verbose(h, s)
Handle h;
char *s;
{
    if (isSwitchSet(h, (Switches)'v') || isSwitchSet(h, (Switches)'d')) {
	PutS(s);
    }
}

/*ARGSUSED*/
extern void echoCmd(h, cmd, filename)
Handle h;
char *cmd;
char *filename;
{
    PutS(cmd);
    PutC(' ');
    PutS(filename);
    PutC('\n');
}


extern void Fatal(h, string1)
Handle h;
char *string1;
{
    
    if (string1)
	(void)fputs(string1, stderr);
    (void)fputc('\n', stderr);
    if (h)
	FreeHandle(h);
    exit(88);
    /*NOTREACHED*/
}

extern void Fatal2(h, string1, string2)
Handle h;
char *string1, *string2;
{
    if (string1) (void)fputs(string1, stderr);
    Fatal(h, string2);
}

extern void Warning(h, arg)
Handle h;
char *arg;
{
    if (!h || !isSwitchSet(h, (Switches)'w')) {
	(void)fputs(arg, stderr);
	(void)fflush(stderr);
    }
}

/* C compiling */

extern void DoCCompile(h, suffix, cswitch)
Handle h;
char *suffix;
int cswitch;
{
    char *fileNameBase = h->job.shortFileName;
    char stringBody[MAXNAMLEN+3];
    char shellStr[MAXCOMMANDSTRINGSIZE];
    char *cFileName = stringBody;
    char outBody[MAXNAMLEN+3];
    char *outFileName = outBody;
    char *outFileExtension = ".o";
    
    (void)strcpy(cFileName, fileNameBase);
    (void)strcat(cFileName, suffix);
    (void)strcpy(outFileName, fileNameBase);
    
    if ((h->job.jobKind==compile || h->job.jobKind == ms) &&
	isSwitchSet(h,(Switches)'l')) {
	outFileExtension = ".c2c.o";
    }
    if (h->job.jobKind == cind && isSwitchSet(h, (Switches)'m')) {
	outFileExtension = ".c2c.o";
    }
    (void)strcat(outFileName, outFileExtension);
    
    (void)strcpy(shellStr, "");
    if (getArch() != h->archtype) {
	(void)strcpy(shellStr, "rsh ");
	(void)strcat(shellStr, getMach(h, h->archtype));
	(void)strcat(shellStr, " \"cd ");
	(void)strcat(shellStr, getwd←wo←automount(cFileName));
	(void)strcat(shellStr, "; ");
    }
    
    (void)strcat(shellStr, "/bin/cc ");
    if (cswitch)    (void)strcat(shellStr, "-c ");
    if (isSwitchSet(h, (Switches)'a')) {
	(void)strcat(shellStr,"-a ");    /* coverage testing code */
    }
    if (!isSwitchSet(h, (Switches)'g')) (void)strcat(shellStr, "-g ");
    if (isSwitchSet(h, (Switches)'o')) {
	if (isSun4Target(h)) {
	    char s[3];
	    (void)strcat(shellStr, "-O");
	    s[0] = h->optLevel + '0';
	    s[1] = ' ';
	    s[2] = '\0';
	    (void)strcat(shellStr, s);
	}
	else /* !isSun4Target() */ {
	    (void)strcat(shellStr, "-O ");
	}
    }
    if (isSwitchSet(h, (Switches)'p'))   (void)strcat(shellStr,"-p ");
    if (isSwitchSet(h, (Switches)'q'))   (void)strcat(shellStr,"-pic ");
    if (isSwitchSet(h, Q))   (void)strcat(shellStr,"-PIC ");
    if (isSwitchSet(h, (Switches)'s')) {
	if (isSun4Target(h) || isDecTarget(h)) {
	    (void)strcat(shellStr,"-S ");
	} else /* isRS6000Target() */ {
	    (void)strcat(shellStr,"-qlst ");
	}	
    }
    if (isSwitchSet(h, (Switches)'w'))    (void)strcat(shellStr, "-w ");
    (void)strcat(shellStr,cFileName);
    (void)strcat(shellStr," ");
    
    if (!isSwitchSet(h, (Switches)'s') && !isRS6000Target(h)) {
	/*
	 * -o doesn't work on AIX except for non-executable files
	 */
	(void)strcat(shellStr, "-o ");
	(void)strcat(shellStr, outFileName);
    }
    if (getArch() != h->archtype) {
	(void)strcat(shellStr, "\"");
    }
    (void)strcat(shellStr,"\n");
    
    PutC('C');
    Verbose(h, shellStr);
    if (!isSwitchSet(h, (Switches)'d')) {
	if (system(shellStr))
	Fatal2(h, "cc failed for: ", fileNameBase);
    }
}

extern void DeleteFile(h, fileName)
Handle h;
char *fileName;
{
    /* just do your best to delete the file - don't worry if you can't do it */
    
    Verbose(h, "/bin/rm -f ");
    Verbose(h, fileName);
    Verbose(h, "\n");
    if (!isSwitchSet(h, (Switches)'d'))    (void)unlink(fileName);
}

extern void AppendFileName(base, s1, s2)
char *base, *s1, *s2;
{
    (void)strcpy(base, s1);
    (void)strcat(base, s2);
}

extern int validArch(archtype)
char *archtype;
{
    int i;
    for (i = 0; i <= MAXARCH; i++) {
	if (!strcmp(archtype, archNames[i])) return 1;
    }
    return 0;
}

/*ARGSUSED*/
extern int checkHostType(s, t)
char *s;
arch t;
{
    return 1;
}

extern int isSun4Target(h) Handle h; { return h->archtype == SUN4; }

extern int isDecTarget(h) Handle h; { return h->archtype == DEC; }

extern int isRS6000Target(h) Handle h; { return h->archtype == RS6000; }

extern arch getArch() {
#ifdef sun
    return SUN4;
#elif AIX
    return RS6000;
#elif ←←mips←←
    return DEC;
#else
    return UNKNOWN;
#endif
}

extern char *stringFromArch(archtype)
arch archtype;
{
    return archNames[(int)archtype];
}

extern arch archFromString(s)
char *s;
{
    arch i;
    for (i = UNKNOWN; i <= MAXARCH; i++) {
	if (!strcmp(s, archNames[(int)i])) {
	    return i;
	}
    }
    return UNKNOWN;
}

extern char *getwd←wo←automount(s)
char *s;
{
    char cwd[MAXPATHLEN], *wd;

    wd = getwd(cwd);
    if (wd == NULL)
        Fatal((Handle)0, "Couldn't determine working directory\n");
    if (strstr(wd, AUTOMOUNT←PREFIX) != NULL) {  /* must be automounted dir. */
	wd += strlen(AUTOMOUNT←PREFIX);
	return wd;
    } else {   /* local filesytem -- convert its name to net based view */
	char hostname[MAXHOSTNAMELEN];
	char rcwd[MAXPATHLEN];
	char *common←prefix;
	struct stat filestat, dirstat;
	struct mntent *mnt;
	FILE *mnttbl;
	
	if (stat(s, &filestat) == -1 && stat(".", &filestat)) {
	    Fatal((Handle)0, "Couldn't stat file");
	}
	if ((mnttbl = setmntent(MOUNTED, "r")) == 0) {
	    Fatal((Handle)0, "setmntent failed");
	}
	while ((mnt = getmntent(mnttbl)) != 0) {
	    if (stat(mnt->mnt←dir, &dirstat) == -1) {
		Fatal((Handle)0, "stat on dir failed");
	    }
	    if (dirstat.st←dev == filestat.st←dev) {
		break;
	    }
	}
	if (mnt == 0) {
	    Fatal((Handle)0, "mount point not found");
	}
	(void)endmntent(mnttbl);
	(void)gethostname(hostname, MAXHOSTNAMELEN);
	(void)sprintf(rcwd, "%s/%s%s", NET←AUTOMOUNT←PREFIX, hostname, mnt->mnt←dir);
	if (common←prefix = strstr(cwd, mnt->mnt←dir)) {
	    (void)strcat(rcwd, cwd + strlen(mnt->mnt←dir));
	} else {
	    (void)strcat(rcwd, s);
	}
	return rcwd;
    }
}
	

extern char *getMach(h, archType)
Handle h;
arch archType;
{
    /*
     * algorithm: 1. check MIMOSA←SERVER←arch environment variable
     *            2. check MIMOSA←SERVER environment variable
     *            3. generate random machine
     */
    char env[21];      /* strlen("MIMOSA←SERVER←") + max arch string + 1 */
    char *archString = stringFromArch(archType);
    char *mimServ;     /* machine to rsh to */

    sprintf(env, "%s←%s", MIMOSA←SERVER, archString); /* form environ var */
    mimServ = getenv(env); /* step 1 */
    if (mimServ) {        
	return getMachInner(mimServ, archType);
    } else {             
	mimServ = getenv(MIMOSA←SERVER);
	if (mimServ) {    /* step 2 */
	    return getMachInner(mimServ, archType);
	}
	return getRandomMach(h);   /* step 3 */
    }
}

static char *getMachInner(host, archType)
char *host;
arch archType;
{
    if (!checkHostType(host, archType)) {
	Warning((Handle)0, host);
	Warning((Handle)0, "not a ");
	Warning((Handle)0, stringFromArch(archType));
	Warning((Handle)0, " architecture machine\n");
    }
    return host;
}

static char *getRandomMach(h)
Handle h;
{
    extern long random();
    extern int srandom();
    
    char *s = stringFromArch(h->archtype);
    char *randomMachName;
    randomMachName = (char *)malloc(strlen(s) + 8); /* 8 == "host" + XXX + '\0' */
    srandom(time((time←t *)0));
    (void)sprintf(randomMachName, "%shost%03d", s, random() % MAXNUMMACH);
    return randomMachName;
}

/* Handle routines */

extern void FreeHandle(h)
Handle          h;
{
    int temp = 0;
    
    (void)free(h->job.shortFileName);
    /* 
      (void)free(h->job.workingDirectory); 
      (void)free(h->job.userName);
      (void)free(h->job.userPassword);
      (void)free(h->job.nfsHostName);
      (void)free(h->serverName);
      */
    /* for each source file, free the jobList entry */
    for (temp=0; temp < h->nJobs; temp++) {
	(void)free(h->jobList[temp]);
    } 
    (void)free((char *)h->jobList);
    (void)free((char *)h);
}

extern Handle NewHandle(argc, argv)
int argc;
char **argv;
{
    Handle          h;
    /*char *defaultServer = "hindenburg";*/
    /*char *envServer;*/
    int temp;
    
    /* allocate object */
    h = (Handle) malloc(sizeof(Object));
    
    h->job.userPassword = NULL; /* calloc(MAXNAMLEN, sizeof(char)); */
    h->serverName = NULL; /* calloc(MAXNAMLEN, sizeof(char)); */
    
    /* initialize object */
    /* most default false */
    for (temp = 0; temp < 26; temp++) h->job.switches[temp] = FALSE; 
    /* envServer = getenv("MIMOSASERVER");
       if (envServer) (void)strcpy(h->serverName, envServer);
       else (void)strcpy(h->serverName, defaultServer);
       */
    h->optLevel = 3;
    h->jobID = 0;
    h->argv = argv;
    h->argc = argc;
    h->jobList = (char **)calloc((unsigned)argc, sizeof(char **));
    h->nJobs = 0;
    /*	h->client = NULL; */
    h->inlineFile = NULL;
    h->archtype = UNKNOWN;
    return (h);
}


#ifdef ←IBMR2

/* 
 * get/setmntent compatibility stuff -- AIX doesn't have these
 * routines so, emulate them with setfsent and getfsent.
 */

static FILE *setmntent(filep, type)
char *filep;
char *type;
{
    setfsent();
    return (FILE *)1;
/* return bogus value -- only valid in call to getmntent() */
}

static struct mntent *getmntent(f)
FILE *f;
{
    static struct mntent m;
    struct fstab *fs;
    if ((fs = getfsent()) == 0)
        return 0;
    m.mnt←dir = fs->fs←file;
    return &m;
}
     
static int endmntent(f)
FILE *f;
{
	endfsent();
	return 1;
} 

#endif
	
/* Revision History
 *  [lah 03-Nov-88]    Add -x option.
 *  [mna 24-Mar-91]
 *    Massive changes made.  Re-wrote large portions and deleted mimosa Server
 *    stuff for now.
 */