/* 
 * misc.c --
 *
 *	Miscellaneous utility procedures for fsattach.
 *
 * Copyright 1989 Regents of the University of California
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /sprite/src/admin/fsattach/RCS/misc.c,v 1.10 91/01/12 16:55:15 jhh Exp $ SPRITE (Berkeley)";
#endif /* not lint */

#include "fsattach.h"


/*
 *----------------------------------------------------------------------
 *
 * ParseMount --
 *
 *	Parses the mount information in the file and fills in the mount
 *	table. 
 *
 * Results:
 *	SUCCESS if the information was parsed ok, FAILURE otherwise.
 *
 * Side effects:
 *	*countPtr contains the size of the mount table.
 *
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
ParseMount(mountFile, countPtr)
    char	*mountFile;		/* file containing mount info */
    int		*countPtr;		/* Ptr to size of mount table */
{
    static char 	source[MAX_FIELD_LENGTH];
    static char 	string[MAX_LINE_LENGTH];
    static char 	dest[MAX_FIELD_LENGTH];
    static char 	command[MAX_FIELD_LENGTH];
    static char 	readFlag[MAX_FIELD_LENGTH];
    static char 	arg[MAX_FIELD_LENGTH];
    static char 	group[MAX_FIELD_LENGTH];
    char 		exportString[MAX_FIELD_LENGTH];
    int 		n;
    Boolean		export;
    char 		*eof;
    int 		line;
    int			index;
    Boolean		readonly;
    FILE		*stream;
    ArgInfo		*argTable = NULL;
    int			argTableSize;
    int			argTableSizeIncrement = 5;
    int			argCount;
    ArgInfo		*argInfoPtr = NULL;
    int			argIndex;
    int			i;
    int			j;
    ReturnStatus	status;
    ArgHeader		*argHeader;

    status = SUCCESS;
    stream = fopen(mountFile, "r");
    if (stream == (FILE *)NULL) {
	(void) fprintf(stderr, "%s: can't open \"%s\", ", progName, mountFile);
	perror("");
	return FAILURE;
    }
    line = 0;
    index = 0;
    argCount = 0;
    argTableSize = 0;
    if (verbose) {
	fprintf(stderr,"Parsing mount file %s.\n", mountFile);
    }
    for (eof = fgets(string, MAX_LINE_LENGTH, stream);
	 eof != NULL;
	 eof = fgets(string, MAX_LINE_LENGTH, stream)) {

	line++;
	n = sscanf(string, " %256s", command);
	if (n < 1 || *command == '#') {
	    continue;
	}
	if (index >= mountTableSize) {
	    mountTableSize += mountTableSizeIncrement;
	    mountTable = (MountInfo *) realloc(mountTable, mountTableSize);
	    if (mountTable == NULL) {
		(void) fprintf(stderr,"%s: Out of memory.\n");
		(void) exit(HARDERROR);
	    }
	}
	if (!strcasecmp("Attach", command)) {
	    n = sscanf(string, " %*s %256s %256s %256s %256s %256s", dest, 
		       source, group, exportString, readFlag);
	    if (n != 5) {
		(void) fprintf(stderr, 
			       "Garbled input at line %d of %s: \"%s\"\n", 
			       line, mountFile, string);
		continue;
	    }
	    export = TRUE;
	    if (!strcasecmp("export", exportString)) {
		export = TRUE;
	    } else if (!strcasecmp("local", exportString)) {
		export = FALSE;
	    } else {
		(void) fprintf(stderr, "Bad export value at line %d: \"%s\"\n", 
			       line, exportString);
		continue;
	    }
	    for (i = 0; i < numGroups; i++) {
		if (!strcmp(groupInfo[i].name, group)) {
		    break;
		}
	    }
	    if (i == numGroups) {
		if (i >= groupInfoSize) {
		    groupInfoSize += groupInfoSizeIncrement;
		    groupInfo = (GroupInfo *) realloc(groupInfo, groupInfoSize);
		    if (groupInfoSize == NULL) {
			(void) fprintf(stderr,"%s: Out of memory.\n");
			(void) exit(HARDERROR);
		    }
		}
		numGroups++;
		strcpy(groupInfo[i].name, group);
	    }
	    mountTable[index].group = i;
	    if (!strcasecmp("r", readFlag)) {
		readonly = TRUE;
	    } else  if (!strcasecmp("rw", readFlag)) {
		readonly = FALSE;
	    } else {
		(void) fprintf(stderr, 
			       "Bad read/write value at line %d: \"%s\"\n",
			       line, readFlag);
		continue;
	    }
	    if (verbose) {
		(void) printf("%-20s %-10s %-10s %-10s %-2s\n", dest, source, 
		              group, exportString, readFlag);
	    }
	    mountTable[index].export = export;
	    mountTable[index].readonly = readonly;
	    mountTable[index].device = TRUE;
	    mountTable[index].doCheck = TRUE;
	    mountTable[index].checked = FALSE;
	    (void) strcpy(mountTable[index].source, source);
	    (void) strcpy(mountTable[index].dest, dest);
	    List_Init(&mountTable[index].argInfo.argList);
	    index++;
	} else if (!strcasecmp("Export", command)) {
	    n = sscanf(string, " %*s %256s %256s", dest, source);
	    if (n != 2) {
		(void) fprintf(stderr, 
			       "Garbled input at line %d of %s: \"%s\"\n", 
			       line, mountFile, string);
		continue;
	    }
	    if (verbose) {
		printf("%-20s %-10s\n", dest, source);
	    }
	    mountTable[index].export = TRUE;
	    mountTable[index].readonly = FALSE;
	    mountTable[index].device = FALSE;
	    mountTable[index].doCheck = FALSE;
	    mountTable[index].checked = FALSE;
	    (void) strcpy(mountTable[index].source, source);
	    (void) strcpy(mountTable[index].dest, dest);
	    List_Init(&mountTable[index].argInfo.argList);
	    index++;
	} else {
	    n = sscanf(string, " %256s", source);
	    if (n != 1) {
		(void) fprintf(stderr, 
			       "Garbled input at line %d of %s: \"%s\"\n", 
			       line, mountFile, string);
		continue;
	    }
	    if (argCount >= argTableSize) {
		argTableSize += argTableSizeIncrement;
		if (argCount == 0) {
		    Alloc(argTable, ArgInfo, argTableSize, "argTable");
		} else {
		    argTable = (ArgInfo *) realloc(argTable, argTableSize);
		}
		if (argTable == NULL) {
		    (void) fprintf(stderr,"%s: Out of memory.\n");
		    (void) exit(HARDERROR);
		}
	    }
	    i = 0;
	    Alloc(argInfoPtr, ArgInfo, 1, "argInfoPtr");
	    List_Init(&argInfoPtr->argList);
	    argInfoPtr->line = line;
	    strcpy(argInfoPtr->source, source);
	    for (argIndex = strspn(string, " \t"); 
		 string[argIndex] != '\0';
		 argIndex += strcspn(&string[argIndex], " \t\n"),
		 argIndex += strspn(&string[argIndex], " \t\n"), 
		 i++) {

		 if (i < 1) {
		     continue;
		 }
		 if (sscanf(&string[argIndex], " %s", arg) == 0) {
		     break;
		 }
		 Alloc(argHeader, ArgHeader, 1, "argHeader");
		 List_InitElement((List_Links *) argHeader);
		 Alloc(argHeader->arg, char, strlen(arg) + 1, "arg");
		 strcpy(argHeader->arg, arg);
		 List_Insert((List_Links *) argHeader, 
		     LIST_BEFORE((List_Links *) &argInfoPtr->argList));
	     }
	    if (strcasecmp(source, "all")) {
		for (i = 0; i < index; i++) {
		     if (!strcmp(mountTable[i].source, source)) {
			 MergeList(&argInfoPtr->argList, 
			     &mountTable[i].argInfo.argList);
			 break;
		     }
		 }
		 if (i == index) {
		     argTable[argCount] = *argInfoPtr;
		     List_Init(&argTable[argCount].argList);
		     AddList(&argInfoPtr->argList, 
			 &argTable[argCount].argList);
		     free(argInfoPtr);
		     argCount++;
		 }
	     } else {
		 argTable[argCount] = *argInfoPtr;
		 List_Init(&argTable[argCount].argList);
		 AddList(&argInfoPtr->argList, 
		     &argTable[argCount].argList);
		 free(argInfoPtr);
		 argCount++;
	     }
	}   
    }
    for (i = 0; i < argCount; i++) {
	if (!strcasecmp(argTable[i].source, "all")) {
	    for (j = 0; j < index; j++) {
		if (mountTable[j].device == TRUE) {
		    MergeList(&argTable[i].argList, 
			&mountTable[j].argInfo.argList);
		    strcpy(mountTable[j].argInfo.source, argTable[i].source);
		    mountTable[j].argInfo.line = argTable[i].line;
		}
	    }
	    DeleteList(&argTable[i].argList);
	} else {
	    for (j = 0; j < index; j++) {
		 if (!strcmp(argTable[i].source, mountTable[j].source)) {
		     MergeList(&argTable[i].argList, 
			 &mountTable[j].argInfo.argList);
			strcpy(mountTable[i].argInfo.source, 
			    argTable[i].source);
			mountTable[i].argInfo.line = argTable[i].line;
		     break;
		 }
	     }
	     if (j == index) {
		 fprintf(stderr, "Device %s not found at line %d\n", 
		     argTable[i].source, argTable[i].line);
		 status = FAILURE;
	     }
	 }
     }
    if (argTable != NULL) {
	free(argTable);
    }
    fclose(stream);
    *countPtr = index;
    return status;
}

/*
 *----------------------------------------------------------------------
 *
 * MergeList --
 *
 *	Merge the src list into the dest list without modifying the source
 *	list.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory is allocated and the dest list is modified.
 *
 *----------------------------------------------------------------------
 */

void
MergeList(srcListPtr, destListPtr)
    ArgHeader *srcListPtr;		/* source list */
    ArgHeader *destListPtr;		/* destination list */

{
    List_Links	*itemPtr;
    List_Links	*newPtr;

    LIST_FORALL((List_Links *) srcListPtr, itemPtr) {
	Alloc((ArgHeader *) newPtr, ArgHeader, 1, "newPtr");
	*((ArgHeader *) newPtr) = *((ArgHeader *) itemPtr);
	List_Insert(newPtr, LIST_ATREAR((List_Links *) destListPtr));
    }
}

/*
 *----------------------------------------------------------------------
 *
 * AddList --
 *
 *	Adds the src list to the dest list. 
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The dest list is modified. 
 *
 *----------------------------------------------------------------------
 */

void
AddList(srcListPtr, destListPtr)
    ArgHeader *srcListPtr;		/* source list */
    ArgHeader *destListPtr;		/* destination list */

{
    List_Links	*itemPtr;
    List_Links	*tempPtr;

    itemPtr = List_First((List_Links *) srcListPtr);
    while (!List_IsAtEnd((List_Links *) srcListPtr, itemPtr)) {
	tempPtr = itemPtr;
	itemPtr = List_Next(itemPtr);
	List_Remove(tempPtr);
	List_Insert(tempPtr, LIST_ATREAR((List_Links *) destListPtr));
    }
}

/*
 *----------------------------------------------------------------------
 *
 * DeleteList --
 *
 *	Frees a list.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The list is freed.
 *
 *----------------------------------------------------------------------
 */

void
DeleteList(listPtr)
    ArgHeader	*listPtr;	/* list to be freed */
{
    List_Links	*itemPtr;
    List_Links	*tempPtr;

    itemPtr = List_First((List_Links *) listPtr);
    while (!List_IsAtEnd((List_Links *) listPtr, itemPtr)) {
	tempPtr = itemPtr;
	itemPtr = List_Next(itemPtr);
	List_Remove(tempPtr);
	free(tempPtr);
    }
}


/*
 *----------------------------------------------------------------------
 *
 * MoveOutput --
 *
 *	Fscheck stores its output in a special
 *	preallocated file. We want to copy the information out of that
 *	file into the standard fscheck output file, and then zero out
 *	the special file.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The root output is appended to the output file and the special
 *	output file is filled with null characters.
 *
 *----------------------------------------------------------------------
 */

void
MoveOutput(mountCount)
    int		mountCount;
{
    FILE 	*outputStream;
    FILE	*tempStream;
    char	outputFile[MAX_LINE_LENGTH];
    char	inputFile[MAX_LINE_LENGTH];
    int		bytesRead;
    int		bytesWritten;
    int		bytesToWrite;
    char	buffer[1024];
    int		i, mountIndex;
    Boolean	done;
    char	*hostName;

    if (verbose) {
	printf("Moving output from fscheck.\n");
    }
    hostName = getenv("HOST");
    for(mountIndex = 0; mountIndex < mountCount; mountIndex++) {
	if (debug) {
	    printf("%d (%s): device = %s, status = %s\n", mountIndex, 
		mountTable[mountIndex].source,
		(mountTable[mountIndex].device == TRUE ? "true" : "false"),
		(mountTable[mountIndex].status == CHILD_OK) ? "ok" : "not ok");
	}
	if (mountTable[mountIndex].checked == FALSE ||
	    mountTable[mountIndex].status != CHILD_OK ||
	    mountTable[mountIndex].device == FALSE) {
	    continue;
	}
	(void) sprintf(outputFile, "/hosts/%s/%s.fsc", hostName,
	    mountTable[mountIndex].source);
	if (verbose) {
	    printf("Copying output from checking %s to %s.\n", 
		mountTable[mountIndex].source, outputFile);
	}
	outputStream = fopen(outputFile, "a+");
	if (outputStream == (FILE *)NULL) {
	    (void) fprintf(stderr, "%s: can't open \"%s\", ", progName, 
			outputFile);
	    perror("");
	    return;
	}
	(void) sprintf(inputFile, "%s/%s", mountTable[mountIndex].dest,
	    tempOutputFile);
	tempStream = fopen(inputFile,"r+");
	if (tempStream == (FILE *)NULL) {
	    (void) fprintf(stderr, "%s: can't open \"%s\", ", progName,
			   inputFile);
	    perror("");
	    fclose(outputStream);
	    return;
	}
	bytesRead = fread(buffer, sizeof(char), 1024, tempStream);
	done = FALSE;
	while(bytesRead > 0 && !done) {
	    for (i = 0; i < bytesRead; i++) {
		if ( buffer[i] == '\0') {
		    done = TRUE;
		    break;
		}
	    }
	    bytesToWrite = i;
	    bytesWritten = fwrite(buffer, sizeof(char), bytesToWrite, 
	    outputStream);
	    if (bytesWritten < bytesToWrite) {
		(void) fprintf(stderr, "%s: Unable to copy output to %s.\n",
			progName, outputFile);
		goto cleanup;
	    }
	    bytesRead = fread(buffer, sizeof(char), 1024, tempStream);
	}
	rewind(tempStream);
	(void) bzero(buffer, 1024);
	for (i = 0; i < tempOutputFileSize; i += bytesToWrite) {
	    bytesToWrite = min(1024, tempOutputFileSize - i);
	    bytesWritten = fwrite(buffer, sizeof(char), bytesToWrite, 
				    tempStream);
	    if (bytesWritten != bytesToWrite) {
		fprintf(stderr, "Only wrote %d bytes: ", bytesWritten);
		perror("");
		break;
	    }
	}
cleanup:
	(void) fclose(outputStream);
	(void) fclose(tempStream);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * PrintFscheckError --
 *
 *	Prints a meaningful description of fscheck's error codes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
PrintFscheckError(code, mountPtr)
    char	code;		/* error code from fscheck */
    MountInfo	*mountPtr;	/* mount info for partition checked */
{
    static char *softerrors[] = {
	"No errors",
	"Correctable error",
	"Exceeded heap limit",
	"No reboot",
	"Reboot",
    };
    static char *harderrors[] = {
	"",
	"Read of device failed",
	"Write to device failed",
	"Bad argument",
	"Heap limit too small",
	"Disk is full",
    };
    char *error;
    int	 index;

    index = (int) code;
    if (index < 0) {
	error = harderrors[-index];
    } else { 
	error = softerrors[index];
    }
    (void) fprintf(stderr, "Fscheck of %s returned (%d) : %s.\n", 
		   mountPtr->source, index, error);
}

/*
 *----------------------------------------------------------------------
 *
 * PreloadPrefixTable --
 *
 *	Load the prefix table with ourself as the server of all the
 * 	prefixes we export. That way we don't broadcast for them
 *	while we check our disks.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Exported prefixes are loaded into the prefix table.
 *
 *----------------------------------------------------------------------
 */

void
PreloadPrefixTable(spriteID, mountCount)
    int	spriteID;		/* Our Sprite ID. */
    int	mountCount;		/* Size of mount table. */
{
    int			i;
    Fs_PrefixLoadInfo	loadInfo;
    ReturnStatus	status;

    for (i = 0; i < mountCount; i++) {
	if ((mountTable[i].export == TRUE) && 
	    (strcmp("/", mountTable[i].dest) != 0)) {
	    (void) strncpy(loadInfo.prefix, mountTable[i].dest,
			FS_MAX_PATH_NAME_LENGTH);
	    loadInfo.serverID = spriteID;
	    if (printOnly || verbose) {
		printf("Preloading prefix \"%s\"\n", loadInfo.prefix);
	    }
	    if (!printOnly) {
		status = Fs_Command(FS_PREFIX_LOAD, sizeof(Fs_PrefixLoadInfo), 
			 &loadInfo);
		if (status != SUCCESS) {
		    fprintf(stderr, "Couldn't load prefix \"%s\": %s.\n",
			    mountTable[i].dest, Stat_GetMsg(status));
		}
	    }
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * GetAttachName --
 *
 *	Get the name of the prefix under which the filesystem was
 *	attached.
 *
 * Results:
 *	Name of the prefix.
 *
 * Side effects:
 *	The disk is read.
 *
 *----------------------------------------------------------------------
 */

char *
GetAttachName(device)
    char	*device;
{
    int			fd;
    Disk_Label		*labelPtr;
    Ofs_SummaryInfo	*summaryPtr;
    char		*prefix;
    ReturnStatus	status;

    fd = open(device, O_RDWR);
    if (fd < 0) {
	fprintf(stderr, "Could not open %s\n", device);
	return NULL;
    }
    labelPtr = Disk_ReadLabel(fd);
    if (labelPtr == NULL) {
	fprintf(stderr, "Could not read label from %s\n", device);
	return NULL;
    }
    summaryPtr = Disk_ReadSummaryInfo(fd, labelPtr);
    if ( summaryPtr == NULL) {
	fprintf(stderr, "Could not read summary info from %s\n", device);
	return NULL;
    }
    prefix = summaryPtr->domainPrefix;
    /*
     * FIX THIS:  The following code to clear a bit in the summary sector
     * is only needed for kernels prior to 1.070.  It can be removed
     * once those kernels are gone.  JHH.
     */
    summaryPtr->flags &= ~OFS_DOMAIN_JUST_CHECKED;
    status = Disk_WriteSummaryInfo(fd, labelPtr, summaryPtr);
    if (status != SUCCESS) {
	fprintf(stderr, "Could not write summary info to %s\n", device);
	return NULL;
    }
    close(fd);
    return prefix;
}

