#ifndef _BLURB_
#define _BLURB_
/*

            Coda: an Experimental Distributed File System
                             Release 3.1

          Copyright (c) 1987-1995 Carnegie Mellon University
                         All Rights Reserved

Permission  to  use, copy, modify and distribute this software and its
documentation is hereby granted,  provided  that  both  the  copyright
notice  and  this  permission  notice  appear  in  all  copies  of the
software, derivative works or  modified  versions,  and  any  portions
thereof, and that both notices appear in supporting documentation, and
that credit is given to Carnegie Mellon University  in  all  documents
and publicity pertaining to direct or indirect use of this code or its
derivatives.

CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
ANY DERIVATIVE WORK.

Carnegie  Mellon  encourages  users  of  this  software  to return any
improvements or extensions that  they  make,  and  to  grant  Carnegie
Mellon the rights to redistribute these changes without encumbrance.
*/

static char *rcsid = "$Header: repair.c,v 3.2.2.1 95/10/11 10:11:02 raiff Exp $";
#endif /*_BLURB_*/



#ifdef __cplusplus
extern "C" {
#endif __cplusplus

#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <ci.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/file.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <strings.h>
#include <sys/stat.h>
#include <setjmp.h>
#include <libc.h>
#include <rpc2.h>

extern void ci(char *, int, int, char *, char *, int);
extern int getbool(char *, int);

#ifdef __cplusplus
}
#endif __cplusplus

#include <vice.h>
#include <venusioctl.h>
#include <auth2.h>
#include <repio.h>
#include <resolve.h>
#include <inconsist.h>
#include "repair.h"



#define HELPDIR "/coda/project/coda/doc/cihelp/repair"
#define       ISDIR(vnode) ((vnode) & 1)  /* directory vnodesare odd */
#define DOREPAIRDATADIR "/coda/usr/luqi/data/repair/"

int localsession;
int  repair_DebugFlag;
char beginRepDefault[MAXPATHLEN];
char doRepDefault[MAXPATHLEN];
char compDirDefault[MAXPATHLEN];
char compOutputFile[MAXPATHLEN];// filename for output of last docompare
struct stat compOutputStatBuf;	// file information for the repair
				// commands file
struct stat doInputStatBuf;

PRIVATE jmp_buf NormalJmpBuf;  /* to exit from ci loops gracefully */

PRIVATE void	SetDefaultPaths();
PRIVATE int	compareVV(int, char **, struct repvol *);
PRIVATE void	GetArgs(int argc, char *argv[]);
PRIVATE	int	getcompareargs(char *, char *, char *);
PRIVATE	int	getlistargs(char *, char *);
PRIVATE int	getremoveargs(char *, char *);
PRIVATE void	getremovelists(int, resreplica *, struct listhdr **);
PRIVATE int	getrepairargs(char *, char *, char *, char *);
PRIVATE int	makedff(char *extfile, char *intfile);
PRIVATE int	doCompare(int, struct repvol *, char **, char *, char *, ViceFid *);
PRIVATE int	compareStatus(int, resreplica *);
PRIVATE int	compareQuotas(int , char **);
PRIVATE int 	compareOwner(int, resreplica *);
PRIVATE void	printAcl(struct Acl *);
PRIVATE int	compareAcl(int, resreplica *);
PRIVATE int 	GetReplicaNames(char **, int , char *);
PRIVATE int 	GetTokens();

#define INITHELPMSG 	\
"The repair tool can be used to manually repair files and directories \n\
that have diverging replicas.  You will first need to do a \"beginRepair\" \n\
which will expose the replicas of the inconsistent object as its children.\n\n\n\
If you are repairing a directory, you will probably use the \"compareDir\" and \"doRepair\" commands.\
\n\nFor inconsistent files you will only need to use the \"doRepair\" command.\
\n\nIf you want to REMOVE an inconsistent object, use the \"removeInc\" command.\
\n\nHelp on individual commands can also be obtained using the \"help\" facility.\n"


/* Relax, ci allows abbreviations of command names */
CIENTRY cil1[] = 
    {/* normal mode */
    CICMD("beginrepair",  beginRepair),  	/* <reppathname> */
    CICMD("dorepair",     doRepair),     	/* <reppathname> <fixfilename> */
    CICMD("comparedirs",  compareDirs),	 	/* <reppathname>, <fixfile> */
    CICMD("clearinc",   clearInc),	 	/* <reppathname> */
    CICMD("removeinc", removeInc), 	 	/* <reppathname> */
    CICMD("quit",         quit),         	/* no args */
    CICMD("checklocal",   checkLocal),	 	/* no args */
    CICMD("listlocal",   listLocal),	 	/* no args */
    CICMD("preservelocal", preserveLocal),      /* no args */
    CICMD("preservealllocal", preserveAllLocal),/* no args */
    CICMD("discardlocal", discardLocal),        /* no args */
    CICMD("discardalllocal", discardAllLocal),  /* no args */
    CICMD("setglobalview", setGlobalView),      /* no args */
    CICMD("setmixedview", setMixedView),        /* no args */
    CICMD("setlocalview", setLocalView),        /* no args */
    CIEND
    };


main(int argc, char *argv[])
    {
    int rc;

    /* parse args */
    GetArgs(argc, argv);

    SetDefaultPaths();

    /* initialize normal mode setjmp */
    rc = setjmp(NormalJmpBuf);
    if (rc){exit(0);}


    /* check if help is available and accessible */
    if (access(HELPDIR, R_OK|X_OK) < 0)
	{
	printf("The help directory \"%s\" is not accessible\n", HELPDIR);
	printf("Was HELPDIR correctly defined when repair.c was compiled?\n");
	}

    /* print a message indicating basic repair methodology */
    printf(INITHELPMSG);
    /* warn user if no tokens are found */
    if (GetTokens()) {
	printf("\n\n\nWARNING: YOU DON'T HAVE TOKENS.  "
	       "YOU MIGHT WANT TO AUTHENTICATE FIRST\n\n");
    }
    /* Sit in command loop */
    ci("*", 0, 0, (char *)cil1, HELPDIR, 0); 
    }

int allowfilerepair = 0;
int doRepair(char *args)
    /* args: <reppathname> <fixfilename> */
    {
    enum {FIXDIR, FIXFILE} fixtype;
    VolumeId vid;
    struct repvol *repv;
    struct rwvol *rwv;
    struct repvol *saverepv;
    struct stat statbuf;
    struct ViceIoctl vioc;
    char space[2048];
    int i, rc;
    char uconflictpath[MAXPATHLEN], ufixpath[MAXPATHLEN];
    char realfixpath[MAXPATHLEN];
    char reppath[MAXPATHLEN], tmppath[MAXPATHLEN];
    char prefix[MAXPATHLEN], suffix[MAXPATHLEN];
    long vids[MAXHOSTS];
    long rcodes[MAXHOSTS];
    
    /* Obtain parameters and confirmation from user */
    rc = getrepairargs(args, uconflictpath, ufixpath, realfixpath);
    if (rc < 0) return(-1);

    /* Is this the leftmost element in conflict? */
    rc = repair_isleftmost(uconflictpath, reppath); 
    if (rc < 0) return(-1);

    /* Is the volume locked for repair? */
    rc = repair_getmnt(reppath, prefix, suffix, &vid);
    if (rc < 0) return(-1);
    if (repair_findrep(vid, &repv) < 0)
	{printf("You must do \"beginRepair\" first\n"); return(-1);}
    saverepv = repv;
    /* Is it a directory?
       Assumption: rw replicas are all of the same type.
       This must be true if this is a leftmost conflict
    */
    assert(repv->rwhead);          /* better have one rw replica at least! */
    rwv = repv->rwhead;
    while (rwv != 0) {
	sprintf(tmppath, "%s/%s", reppath, rwv->compname);
	rc = lstat(tmppath, &statbuf);

	/* If it's a symlink, that rep must not be available, skip over it. */
	if ((statbuf.st_mode & S_IFMT) == S_IFLNK) rc = -1;

	if (rc >= 0) break;
	else 
	    rwv = rwv->next;
    }
    if (rc < 0) {
	myperror(" lstat", tmppath, errno);  
	printf("NO replicas accessible\n");
	return(-1);
    }
    if ((statbuf.st_mode & S_IFMT) == S_IFDIR) fixtype = FIXDIR;
    else {
	fixtype = FIXFILE;
	/* check that the allowfilerepair flag is set */
	if (!allowfilerepair) {
	    printf("Use xfrepair from the shell to repair files...\n");
	    printf("or run repair -allowfilerepair from the shell\n");
	    return(-1);
	}
    }
    DEBUG(("fixtype = %d\n", fixtype));
    if (fixtype == FIXDIR) rc = makedff(realfixpath, tmppath);     /* Create internal form of fix file */
    if (rc < 0) return(-1);
    DEBUG(("ufixpath = \"%s\"  tmppath = \"%s\"\n", ufixpath,
	   tmppath));

    // collect statistics about the repair
    int collectstats = 1;
    int repfilemodified = 0;
    int changesdifflevel = -1;
    if (fixtype == FIXDIR) {
	if (compOutputFile[0]) {
	    if (stat(realfixpath, &doInputStatBuf)) {
		printf("Couldn't stat file %s\n", realfixpath);
		collectstats = 0;
	    }
	    else {
		if ((compOutputStatBuf.st_ino != doInputStatBuf.st_ino) ||
		    (compOutputStatBuf.st_size != doInputStatBuf.st_size) ||
		    (compOutputStatBuf.st_mtime != doInputStatBuf.st_mtime))
		    repfilemodified = 1;
		if (repfilemodified) {
		    printf("The repair file was modified.\nHow difficult was it to change?");
		    while ((changesdifflevel < 0) || (changesdifflevel > 5)) {
			printf("(0 Easy, 5 Difficult)");
			scanf("%d", &changesdifflevel);
		    }
		}
	    }
	}
	else 
	    collectstats = 0;
    }

    /* Do the repair */
    if (fixtype == FIXDIR)
	{
	vioc.in_size = (short)(1+strlen(tmppath));
	vioc.in = tmppath;
	}
    else
	{
	vioc.in_size = (short)(1 + strlen(ufixpath));
	vioc.in  = ufixpath;
	}
    vioc.out_size = (short)sizeof(space);
    vioc.out = space;
    bzero(space, (short)sizeof(space));
    rc = pioctl(reppath, VIOC_REPAIR, &vioc, 0);
    if (rc < 0 && errno != ETOOMANYREFS) 
	myperror(" REPAIR", reppath, errno);
    {	
	long *l;
	int failure = 0;
	l = (long *) space;
	for (i = 0; i < MAXHOSTS; i++)
	    vids[i] = l[i];
	for (i = 0; i < MAXHOSTS; i++)
	    rcodes[i] = l[i+MAXHOSTS];
	for (i = 0; i < MAXHOSTS; i++)
	    if (vids[i]) {
		/* find server name */
		rwv = saverepv->rwhead;
		while(rwv && vids[i] != rwv->vid)
		    rwv = rwv->next;
		assert(vids[i] == rwv->vid);
		printf("%s %s\n", 
		       rwv->srvname, rcodes[i] ? "failed" : "succeeded");
		if (rcodes[i]) {
		    failure++;
		    myperror("     REPAIR: ", rwv->srvname, (int)rcodes[i]);
		}
	    }
	if (failure)
	    printf("Repair not successful\n");
    }
    if (collectstats ) {
	// open the statistics file 
	char statfilename[MAXPATHLEN];
	uid_t uid = getuid();
	sprintf(statfilename, "%s%u.XXXXXX", DOREPAIRDATADIR, uid);
	int statfd = mkstemp(statfilename);
	if (statfd>0) {
	    FILE *fp = fdopen(statfd, "w");
	    assert(fp);
	    if (fixtype == FIXDIR) {
		fprintf(fp, "Kind of Repair: Directory\n");
		fprintf(fp, "Repair file changed: %d\n", repfilemodified);
		fprintf(fp, "Level of difficulty for changes %d\n", changesdifflevel);
		fprintf(fp, "The repair file was as follows:\n");
		FILE *repfp = fopen(realfixpath, "r");
		if (repfp) {
		    char s[MAXPATHLEN];
		    while (fgets(s, MAXPATHLEN, repfp))
			fprintf(fp, "%s", s);
		    fclose(repfp);
		}
		else fprintf(fp, "Couldn't open the repair file\n");
	    }
	    else {
		fprintf(fp, "Kind of Repair: File\n");
		fprintf(fp, "Path of repair file: %s\n", ufixpath);
	    }

	    fprintf(fp, "Return codes from the repair were:\n");
	    // print the return codes
	    for (i = 0; i < MAXHOSTS; i++)
		if (vids[i]) {
		    /* find server name */
		    rwv = saverepv->rwhead;
		    while(rwv && vids[i] != rwv->vid)
			rwv = rwv->next;
		    assert(vids[i] == rwv->vid);
		    fprintf(fp, "%s %s\n", 
			    rwv->srvname, rcodes[i] ? "failed" : "succeeded");
		}
	    fclose(fp);
	    close(statfd);
	}
    }
    /* Clean up */
    if (fixtype == FIXDIR) unlink(tmppath); /* ignore rc */
    return(rc);
    }

int compareDirs(char *args)
   /* args: <reppathname> <fixfilename> */
{
    int	    nreplicas;
    char    **names;
    int rc, i;
    int	sizeOfPath;
    VolumeId vid;
    struct  repvol *repv;
    char    reppath[MAXPATHLEN], filepath[MAXPATHLEN];
    char    prefix[MAXPATHLEN], suffix[MAXPATHLEN];
    char    tmppath[MAXPATHLEN];
    char    uconflictpath[MAXPATHLEN];
    ViceFid confFid;
    vv_t    confvv;
    struct stat buf;

    /* Obtain parameters from user */
    rc = getcompareargs(args, uconflictpath, filepath);
    if (rc < 0) return(-1);

    /* Is this the leftmost element in conflict? */
    rc = repair_isleftmost(uconflictpath, reppath); 
    if (rc < 0) return(-1);

    /* Is the volume locked for repair */
    rc = repair_getmnt(reppath, prefix, suffix, &vid);

    if (rc < 0) return(-1);
    if (repair_findrep(vid, &repv) < 0){
	printf("You must do \"beginRepair\" first\n"); 
	return(-1);
    }
    
    assert(repv->rwhead);   /* better have atleast one rw replica */

    if (repair_getfid(uconflictpath, &confFid, &confvv)) {
	printf("%s isn't in /coda... Are you sure this object is in conflict\n", 
	       uconflictpath);
	return(-1);
    }
    if (!ISDIR(confFid.Vnode)) {
	printf("You can only compare directory replicas\n");
	return(-1);
    }
    /* initialize the array of ropaths */
    nreplicas = repair_countRWReplicas(repv);
    sizeOfPath = (int) (strlen(repv->rodir) + 15 + strlen(suffix));
    names = (char **)malloc(nreplicas * sizeof(char *));
    nreplicas = GetReplicaNames(names, nreplicas, reppath);
    if (nreplicas > 0) {
	rc = lstat(names[0], &buf);
	if (rc < 0)  goto Exit;
	if ((buf.st_mode & S_IFMT) != S_IFDIR) {
	    printf("Compare allowed only on directories\n");
	    return(-1);
	}
    }
    else {
	printf("Couldn't get replicas to do compare\n");
	return(-1);
    }
    
  redocompare:
    /* do the compare */
    rc = doCompare(nreplicas, repv, names, filepath, prefix, &confFid);
    if (!rc)
	printf("No Conflicts detected \n");
    else if (rc > 0)
	printf("Operations to resolve conflicts are in %s\n", 
	       filepath);	
    
    else if (rc == NNCONFLICTS) 
	if (getbool("Do you want to repair the name/name conflicts", 1)) {
	    /* repair the name/name conflicts */
	    struct ViceIoctl vioc;
	    char space[2048];
	    rc = makedff(filepath, tmppath);
	    vioc.in_size = (short) (1+strlen(tmppath));
	    vioc.in = tmppath;
	    vioc.out_size = (short) sizeof(space);
	    vioc.out = space;
	    bzero(space, (int)sizeof(space));
	    rc = pioctl(reppath, VIOC_REPAIR, &vioc, 0);
	    if (!rc) {
		/* name/name conflicts were repaired 
		   if the only thing wrong was the n/n conflict, then the object isn\'t inconsistent anymore.
		   try to stat the individual replica - if it fails then just exit */
		struct stat buf;
		if (stat(names[0], &buf)) {
		    /* object doesn't exist anymore */
		    printf("The directory was repaired and is no longer inconsistent\n");
		    printf("You can quit now :-)\n");
		    goto Exit;
		}
		else /* the object is still there so the dir is still inconsistent */
		    goto redocompare;
	    }
	    else  {
		printf("Couldn't remove name/name conflicts successfully");
		printf(" - Try another compare\n");
	    }
	}
    
  Exit:
    /* free the malloced space */
    for ( i = 0; i < nreplicas; i++)
	free(names[i]);
    free(names);
}

int allowclear = 0;
int clearInc(char *args)
{
    int	    rc, i;
    char    *p;
    char    reppath[MAXPATHLEN], uconflictpath[MAXPATHLEN];
    char    prefix[MAXPATHLEN], suffix[MAXPATHLEN];
    VolumeId	vid;
    struct repvol *repv;
    int	    sizeOfPath;
    char    **names;
    int	    nreplicas;
    struct stat buf;

    if (!allowclear) {
	printf("Clear Inconsistency: This command is obsolete.");
	printf("You don't need to use this anymore\n");
	return 0;
    }

    p =args;
    *uconflictpath = 0;
    while (*uconflictpath == 0)
	strarg(&p, " ", "Pathname of Object that should be made consistent?", doRepDefault, uconflictpath);

    /* Is this the leftmost element in conflict? */
    rc = repair_isleftmost(uconflictpath, reppath); 
    if (rc < 0) return(-1);
    
    /* Is the volume locked for repair */
    rc = repair_getmnt(reppath, prefix, suffix, &vid);
    if (rc < 0) return(-1);
    if (repair_findrep(vid, &repv) < 0){
	printf("You must do \"beginRepair\" first\n"); 
	return(-1);
    }

    assert(repv->rwhead);   /* better have atleast one rw replica */

    /* initialize the array of ropaths */
    nreplicas = repair_countRWReplicas(repv);
    sizeOfPath = (int) (strlen(repv->rodir) + 15 + strlen(suffix));
    names = (char **)malloc(nreplicas * sizeof(char *));
    nreplicas = GetReplicaNames(names, nreplicas, reppath);
    if (nreplicas <= 0) {
	printf("Clear Inconsistency: Couldn't get enough replicas to compare\n");
	return(-1);
    }
    ViceFid confFid;
    confFid.Volume = vid;
    confFid.Vnode = 0;	// set the fid to 0.0 so docompare will not check quotas
    confFid.Unique = 0; 
    /* do the compare */
    if (!doCompare(nreplicas, repv, names, "/dev/null", prefix, &confFid)){
	ViceFid Fid[MAXHOSTS];
	vv_t vv[MAXHOSTS];
	struct ViceIoctl vioc;

	/* now for each replica get the vv */
	/* XXX - if a get fid is done between two setvv's resolve might 
	   get called - therefore get all the vv's before doing the setvv */
	for (i = 0; i < nreplicas; i++){
	    if ((rc = repair_getfid(names[i], &Fid[i], &vv[i])) < 0){
		printf("Error in repair_getfid(%s)\n", names[i]);
		return(-1);
	    }
	}
	if ((Fid[0].Vnode == 1) && (Fid[0].Unique == 1)) {
	    printf("Comparing Volume Quotas...\n");
	    if (compareQuotas(nreplicas, names)) 
		printf("Warning: Volume Quotas are different\n");
	    else 
		printf("No Conflicts detected \n");		
	}
	else 
	    printf("No Conflicts detected \n");
	
	printf("Clearing inconsistency flags ... \n");
	for (i = 0; i < nreplicas; i++) {
	    ClearIncon(vv[i]);
	    vioc.in_size = (short)sizeof(vv_t);
	    vioc.in = (char *)&vv[i];
	    vioc.out_size = 0;
	    vioc.out = 0;
	    rc = pioctl(names[i], VIOC_SETVV, &vioc, 0);
	    if (rc){
		myperror("SETVV", names[i], errno);
		return(-1);
	    }
	}
    }
    else
	printf("Since replicas are not yet identical, inconsistency still remains\n");

    /* clean up */
    for ( i = 0; i < nreplicas; i++)
	free(names[i]);
    free(names);
    
    return(0);
	
}

int removeInc(char *args) 
     /* removes inconsistent objects;
	first does  a repair, clears the inc 
	and then removes the object */
{
    int	    nreplicas;
    char    **names;
    int rc, i, j;
    int	sizeOfPath;
    char    reppath[MAXPATHLEN], uconflictpath[MAXPATHLEN];
    char    prefix[MAXPATHLEN], suffix[MAXPATHLEN];
    char    tmppath[MAXPATHLEN];
    VolumeId vid;
    struct  repvol *repv;
    struct  rwvol  *rwv;
    struct listhdr *repairlist;
    resreplica *dirs;
    struct stat buf;
    FILE *file;
    enum {FIXDIR, FIXFILE, UNKNOWN} fixtype;

    rc = 0;
    fixtype = UNKNOWN;

    /* Obtain parameters */
    rc = getremoveargs(args, uconflictpath);
    if (rc < 0) return(-1);

    /* do a begin repair first */
    if (beginRepair(uconflictpath) != 0) {
	printf("Couldn\'t do begin repair on %s\n",
	       uconflictpath);
	return(-1);
    }
    /* Is this the leftmost element in conflict? */
    rc = repair_isleftmost(uconflictpath, reppath); 
    if (rc < 0) return(-1);

    /* Is the volume locked for repair */
    rc = repair_getmnt(reppath, prefix, suffix, &vid);

    if (rc < 0) return(-1);
    if (repair_findrep(vid, &repv) < 0){
	printf("You must do \"beginRepair\" first\n"); 
	return(-1);
    }
    
    assert(repv->rwhead);   /* better have atleast one rw replica */

    /* get repair type */
    {
	rwv = repv->rwhead;
	while (rwv != 0) {
	    sprintf(tmppath, "%s/%s", repv->rodir, rwv->compname);
	    rc = lstat(tmppath, &buf);  
	    if (rc >= 0) break;
	    else 
		rwv = rwv->next;
	}
	if (rc < 0) {
	    myperror(" lstat", tmppath, errno);  
	    printf("NO replicas accessible\n");
	    return(-1);
	}
	if ((buf.st_mode & S_IFMT) == S_IFDIR) fixtype = FIXDIR;
	else fixtype = FIXFILE;
    }
    
    /* initialize the array of ropaths */
    nreplicas = repair_countRWReplicas(repv);
    sizeOfPath = (int) (strlen(repv->rodir) + 15 + strlen(suffix));
    names = (char **)malloc(nreplicas * sizeof(char *));
    nreplicas = GetReplicaNames(names, nreplicas, reppath);
    
    if (nreplicas <= 0) {
	printf("No replica is accessible!\n");
	rc = -1;
	goto Error;
    }

    if (fixtype == FIXFILE) {
	ViceFid fixfid;
	vv_t fixvv;
	struct ViceIoctl vioc;
	char space[2048];
	
	if (rc = repair_getfid(names[0], &fixfid, &fixvv)) {
	    printf("Could not get fid for %s\n", names[0]);
	    goto Error;
	}

	sprintf(tmppath, "@%x.%x.%x", fixfid.Volume, 
		fixfid.Vnode, fixfid.Unique);

	/* do the repair */
	vioc.in_size = (short) (1+strlen(tmppath));
	vioc.in = tmppath;
	vioc.out_size = (int)sizeof(space);
	vioc.out = space;
	bzero(space, (int)sizeof(space));
	rc = pioctl(reppath, VIOC_REPAIR, &vioc, 0);
	if (rc < 0 && errno != ETOOMANYREFS) 
	    myperror(" REPAIR", reppath, errno);
    }
    else {
	/* get the directory entries and create list of 
	    children to be removed */
	getunixdirreps(nreplicas, names, &dirs);
	getremovelists(nreplicas, dirs, &repairlist);
	
	if (repair_DebugFlag) {
	    file = stdout;
	    for (i = 0; i < nreplicas; i++){
		/* find the server name */
		{
		    rwv = repv->rwhead;
		    while (rwv && repairlist[i].replicaId != rwv->vid)
			rwv = rwv->next;
		    assert(repairlist[i].replicaId == rwv->vid);
		}
		fprintf(file,"\nreplica %s %x \n", rwv->srvname, 
			repairlist[i].replicaId);
		for (j = 0; j < repairlist[i].repairCount; j++)
		    repair_printline(&(repairlist[i].repairList[j]), file);
	    }
	}
	
	/* convert list to internal format */
	strcpy(tmppath, "/tmp/REPAIR.XXXXXX");
	mktemp(tmppath);
	
	/* write out internal rep */
	rc = repair_putdfile(tmppath, nreplicas, repairlist);
	if (rc) {
	    printf("Coudn't put repair list into file %s\n",
		   tmppath);
	    goto Error;
	}
	
	/* Do the repair */
	{
	    struct ViceIoctl vioc;
	    char space[2048];
	    
	    vioc.in_size = (short)(1+strlen(tmppath));
	    vioc.in = tmppath;
	    vioc.out_size = (short)sizeof(space);
	    vioc.out = space;
	    bzero(space, (int)sizeof(space));
	    rc = pioctl(reppath, VIOC_REPAIR, &vioc, 0);
	    if (rc < 0 && errno != ETOOMANYREFS) 
		myperror(" REPAIR", reppath, errno);
	    /* Clean up */
	    unlink(tmppath); 
	}
	
	/* clear the inconsistency if needed and possible */
	{
	    ViceFid confFid; 
	    ViceVersionVector confvv;
	    if ((!repair_getfid(uconflictpath, &confFid, &confvv)) && 
		((confvv.StoreId.Host == -1) && (confvv.StoreId.Uniquifier == -1))) {
		    // object is still inconsistent ... try to clear inconsistency
		    // confFid is 0.0 so that doCompare will not check quotas
		    confFid.Volume = confFid.Vnode = confFid.Unique = 0;
		    if (!doCompare(nreplicas, repv, names, "/dev/null", prefix, &confFid)){
			ViceFid Fid[MAXHOSTS];
			vv_t vv[MAXHOSTS];
			struct ViceIoctl vioc;
			printf("No Conflicts exist in directory\n");
			printf("Clearing Inconsistency flags.....\n");
			
			/* now for each replica get the vv and  clear the inconsistency  */
			for (i = 0; i < nreplicas; i++){
			    if ((rc = repair_getfid(names[i], &Fid[i], &vv[i])) < 0){
				printf("Error in repair_getfid(%s)\n", names[i]);
				goto Error;
			    }
			}
			for (i = 0; i < nreplicas; i++) {
			    ClearIncon(vv[i]);
			    vioc.in_size = (short) sizeof(vv_t);
			    vioc.in = (char *)&vv[i];
			    vioc.out_size = 0;
			    vioc.out = 0;
			    rc = pioctl(names[i], VIOC_SETVV, &vioc, 0);
			    if (rc){
				myperror("SETVV", names[i], errno);
				goto Error;
			    }
			}
		    }
		    else {
			printf("Replicas are still not equal - cannot remove\n");
			goto Error;
		    }
		}
	}
    }
    
  Error:
    /* clean up malloced memory */
    if (fixtype == FIXDIR) 
	resClean(nreplicas, dirs, repairlist);

    /* free the malloced space */
    for ( i = 0; i < nreplicas; i++)
	free(names[i]);
    free(names);
    
    /* disable repair after we are done */
    {
	struct ViceIoctl vioc;
	int retcode;
	vioc.out_size = 0;
	vioc.in_size = 0;
	vioc.out = 0;
	repair_unlinkrep(repv);
	retcode = pioctl(repv->mnt, VIOC_DISABLEREPAIR, &vioc, 0);
	if (retcode < 0) myperror(" DISABLEREPAIR", repv->mnt, errno);
	repair_finish(repv);
    }
    if (!rc) {
	/* no error - try to remove the object */
	if (fixtype == FIXDIR) {
	    if (rmdir(uconflictpath)) {
		printf("Could not remove %s\n", 
		       uconflictpath);
		return(-1);
	    }
	}
	else {
	    if (unlink(uconflictpath)) {
		printf("Could not remove %s\n", 
		       uconflictpath);
		return(-1);
	    }
	}
    }
    return(rc);
}

void quit(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;

    if (localsession) {
	int commit;
	if (getbool("commit repair changes?", 1)) {
	    commit = 1;
	} else {
	    commit = 0;
	}

	char space[2048];
	char choice[10];
	vioc.out = space;
	vioc.out_size = 2048;
	sprintf(choice, "%d %d", REP_CMD_END, commit);
	vioc.in = choice;
	vioc.in_size = (short) (strlen(choice) + 1);
	
	errno = 0;
	rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
	if (rc < 0) {
	    perror("VIOC_REP_CMD(REP_CMD_END)");
	}
	if (strcmp(vioc.out, "repair session completed")) {
	    printf("%s\n", vioc.out);
	    return;
	}
    } else {
        /* Release volume-level locks, for server/server session */
	vioc.out = 0;
	vioc.out_size = 0;
	vioc.in_size = 0;

	/* Release all volume-level locks, and clean up */
	while (RepVolHead)
	  {/* new element at head on each iteration */
	      repv = RepVolHead;
	      repair_unlinkrep(repv);
	      errno = 0;
	      rc = pioctl(repv->mnt, VIOC_DISABLEREPAIR, &vioc, 0);
	      if (rc < 0) myperror(" DISABLEREPAIR", repv->mnt, errno);
	      repair_finish(repv);
	  }
    }
    /* And quit */
    exit(0);
}


/* 
  user passed path (realpath) is what the user gives as the fixfile.
  if the object is in coda then fixpath contains the @fid representation of the object
 */
PRIVATE int getrepairargs(char *args, char *conflictpath, char *fixpath /* OUT */,
			  char *realpath)
    {
    char *p;
    char msg[2*MAXPATHLEN+100];
    ViceFid fixfid;
    vv_t fixvv;
    
    p = args;
    *conflictpath = 0;
    while (*conflictpath == 0)
	strarg(&p, " ", "Pathname of object in conflict?", 
	       (*doRepDefault == '\0')?beginRepDefault:doRepDefault,
	       conflictpath);
    strcpy(doRepDefault, conflictpath);
	
    *fixpath = 0;
    while (*fixpath == 0)
	strarg(&p, " ", "Pathname of fix file?", 
	       (*compDirDefault == '\0')?"":compDirDefault, fixpath);
    strcpy(realpath, fixpath);
    strcpy(compDirDefault, fixpath);
    if (!repair_getfid(fixpath, &fixfid, &fixvv))
	{
/* 	printf("%s is in Coda and cannot be used as the fix file\n", fixpath);
	return(-1);
*/  
	sprintf(fixpath, "@%x.%x.%x", fixfid.Volume, fixfid.Vnode, fixfid.Unique);
	}
	
    sprintf(msg, "OK to repair \"%s\" by fixfile \"%s\"?", conflictpath, fixpath);
    if (!getbool(msg, 0)) return(-1);
    else return(0);    
    }


PRIVATE	int getcompareargs(char *args, char *reppath, char *filepath)
{
    char *p;
    
    p = args;
    *reppath = 0;
    while (*reppath == 0)
	strarg(&p, " ", "Pathname of Object in conflict?", 
	       doRepDefault, reppath);
    
    *filepath = 0;
    while (*filepath == 0) {
	strarg(&p, " ", "Pathname of repair file produced?", 
	       (*compDirDefault == '\0')?"":compDirDefault, 
	       filepath);
	if (filepath && IsInCoda(filepath)) {
		printf("Please use a fixfile not in /coda \n");
		*filepath = 0;
		continue;
	}
    }
    strcpy(compDirDefault, filepath);
    return 0;
}

PRIVATE int getremoveargs(char *args, char *uconfpath)
{
    char *p;
    
    p = args;
    *uconfpath = 0;
    while (*uconfpath == 0)
	strarg(&p, " ", "Pathname of Object in conflict?", 
	       doRepDefault, uconfpath);
    
    return(0);
}

PRIVATE	int getlistargs(char *args, char *reppath)
{
    char *p;
    
    p = args;
    *reppath = 0;
    while (*reppath == 0)
	strarg(&p, " ", "Pathname of Object in conflict?", 
	       doRepDefault, reppath);

    return 0;
}


PRIVATE int doCompare(int nreplicas, struct repvol *repv, char **names, 
		      char *filepath, char *volmtpt, ViceFid *incfid)
{
    resreplica *dirs;
    struct  listhdr *k;
    int	i, j;
    FILE *file;
    struct rwvol *rwv;

    if (nreplicas == 1) {
	printf("DoCompare: Since there is only one replica, nothing to compare\n");
	return(0);
    }
    compOutputFile[0] = '\0';
    if (!strcmp(filepath, "stdout"))
	file = stdout;
    else {
	file = fopen(filepath, "w");
	if (!file) {
	    printf("Couldn't open file %s for writing\n", filepath);
	    return(-2);
	}
	if (strcmp(filepath, "/dev/null"))
	    sprintf(compOutputFile, "%s", filepath);
    }
    /* set the global RepVolume to the volume we are repairing */
    RepVolume = repv->vid;
    
    getunixdirreps(nreplicas, names, &dirs);
    int rc = dirresolve(nreplicas, dirs, NULL, &k, volmtpt);

    for (i = 0; i < nreplicas; i++){
	/* find the server name */
	{
	    rwv = repv->rwhead;
	    while (rwv && k[i].replicaId != rwv->vid)
		rwv = rwv->next;
	    assert(k[i].replicaId == rwv->vid);
	}
	fprintf(file,"\nreplica %s %x \n", rwv->srvname, k[i].replicaId);
	for (j = 0; j < k[i].repairCount; j++)
	    repair_printline(&(k[i].repairList[j]), file);
    }
    if (file != stdout) {
	fclose(file);
	if (compOutputFile[0]) {
	    // save the information about the compare file produced
	    if (stat(compOutputFile, &compOutputStatBuf)) {
		printf("Stat of %s failed\n", compOutputFile);
		compOutputFile[0] = '\0';
	    }
	}
    }
    if (compareAcl(nreplicas, dirs)){
	nConflicts ++;
	printf("Acls differ: Please repair manually using setacl <user> <rights>\n");
    }
    if (compareStatus(nreplicas, dirs)){
	nConflicts++;
	printf("Modebits differ - a repair should set the bits\n");
    }
    if (compareOwner(nreplicas, dirs)) {
	nConflicts++;
	printf("Owner differs: Please repair manually using setowner <uid>\n");
    }
    if (compareVV(nreplicas, names, repv)) {
	if (!nConflicts) {
	    // warn the user if no conflicts were detected otherwise
	    printf("The fix file may be empty but .... \n"
		   "You still need a dorepair because the "
		   "Version state is different\n");
	}
	nConflicts++;
    }
    if ((incfid->Vnode == 1) && (incfid->Unique == 1) &&
	(compareQuotas(nreplicas, names))) {
	nConflicts++;
	printf("Your volume quota is different on the various replicas\n");
	printf("You SHOULD talk to one of the system administrators soon\n");
    }
    /* clean up the malloced memory */
    resClean(nreplicas, dirs, k);
    if (rc == NNCONFLICTS) return(NNCONFLICTS);
    return(nConflicts);
}

PRIVATE int compareStatus(int nreplicas, resreplica *dirs)
{
    int	i;
    for (i = 1; i < nreplicas; i++)
	if (dirs[i].modebits != dirs[0].modebits)
	    return -1;
    return 0;
}
PRIVATE int compareOwner(int nreplicas, resreplica *dirs) {
    int i;
    for (i = 1; i < nreplicas; i++)
	if (dirs[i].owner != dirs[0].owner)
	    return -1;
    return 0;
}
PRIVATE int compareQuotas(int nreplicas, char **names)
{
    if (nreplicas <= 1) {
	printf("Comparing Quotas: Not enough replicas to compare\n");
	return 0;
    }
    char piobuf[2048];    
    struct ViceIoctl vio;
    vio.in = 0;
    vio.in_size = 0;
    vio.out_size = 2048;
    vio.out = piobuf;

    /* Do the pioctl */
    int rc = pioctl(names[0], VIOCGETVOLSTAT, &vio, 1);
    if (rc <0) {fflush(stdout); perror(names[0]); return(1);}
    /* Get pointers to output fields */
    VolumeStatus *vs = (VolumeStatus *)piobuf;
    int minquota0 = (int) vs->MinQuota;
    int maxquota0 = (int) vs->MaxQuota;
    for (int i = 1; i < nreplicas; i++) {
	vio.in = 0;
	vio.in_size = 0;
	vio.out_size = 2048;
	vio.out = piobuf;
	
	/* Do the pioctl */
	rc = pioctl(names[i], VIOCGETVOLSTAT, &vio, 1);
	if (rc <0) {fflush(stdout); perror(names[i]); return(1);}
	/* Get pointers to output fields */
	vs = (VolumeStatus *)piobuf;
	if ((vs->MinQuota !=  minquota0) || (vs->MaxQuota != maxquota0)) 
	    return 1;
    }
    return 0;
}

PRIVATE void printAcl(struct Acl *acl)
{
    int i;
    
    printf("There are %d plus entries\n", acl->nplus);
    for (i = 0; i < acl->nplus; i++)
	printf("%s \t %d\n", ((acl->pluslist)[i]).name, ((acl->pluslist)[i]).rights);
    printf("There are %d negative entries\n", acl->nminus);
    for (i = 0; i < acl->nminus; i++)
	printf("%s \t %d\n", ((acl->minuslist)[i]).name, ((acl->minuslist)[i]).rights);
    printf("End of Access List\n");
}

PRIVATE int compareAcl(int nreplicas, resreplica *dirs)
{
    int	i, j;
    struct Acl *al0, *ali;
    al0 = dirs[0].al;

    for (i = 1; i < nreplicas; i++){
	ali = dirs[i].al;

	if (ali->nplus != al0->nplus || ali->nminus != al0->nminus)
	    return -1;
	for (j = 0; j < al0->nplus; j++){
	    if (strcmp((al0->pluslist)[j].name, (ali->pluslist)[j].name))
		return -1;
	    if ((al0->pluslist)[j].rights != (ali->pluslist)[j].rights)
		return -1;
	}
	for (j = 0; j < al0->nminus; j++){
	    if (strcmp((al0->minuslist)[j].name, (ali->minuslist)[j].name))
		return -1;
	    if ((al0->minuslist)[j].rights != (ali->minuslist)[j].rights)
		return -1;
	}
    }
    return 0;
}
PRIVATE int compareVV(int nreplicas, char **names, struct repvol *repv) 
{
    vv_t vv[MAXHOSTS];
    vv_t *vvp[MAXHOSTS];
    ViceFid fid;
    int nhosts = 0;
    int i;
    int HowMany = 0;

    for (i = 0; i < MAXHOSTS; i++) 
	vvp[i] = NULL;

    for (i = 0; i < nreplicas; i++) {
	if (repair_getfid(names[i], &fid, &vv[nhosts]))
	    printf("Couldn't get vv for %s\n", names[i]);
	else
	    nhosts++;
    }
    for (i = 0; i < nhosts; i++)
	vvp[i] = &vv[i];
    if (VV_Check_IgnoreInc(&HowMany, vvp, 1) != 1)
	return(1);
    return(0);
}

PRIVATE void getremovelists(int nreplicas, resreplica *dirs, struct listhdr **repairlist)
{
    struct repair rep;
    resdir_entry *rde;
    int i, j;

    InitListHdr(nreplicas, dirs, repairlist);
    /* for each replica create the list of entries to be removed */
    for ( i = 0 ; i < nreplicas; i++) 
	for ( j = 0; j < dirs[i].nentries; j++) {
	    rde = &(direntriesarr[dirs[i].entry1 + j]);
	    if (ISDIR(rde->vno)) 
		rep.opcode = REPAIR_REMOVED;
	    else 
		rep.opcode = REPAIR_REMOVEFSL;
	    strcpy(&(rep.name[0]), &(rde->name[0]));
	    rep.parms[0] = 0;
	    rep.parms[1] = 0;
	    rep.parms[2] = 0;
	    InsertListHdr(&rep, repairlist, i);
	}
}

PRIVATE int makedff(char *extfile, char *intfile /* OUT */)
    /* extfile: external (ASCII) rep
       intfile: internal (binary) rep
       Returns 0 on success, -1 on failures
    */
    {
    struct listhdr *hl;
    int hlc, rc;

    DEBUG(("makedff(%s,...)\n", extfile));

    /* parse input file and obtain internal rep  */
    rc = repair_parsefile(extfile, &hlc, &hl);
    if (rc < 0) return(-1);

    /* generate temp file name */
    strcpy(intfile, "/tmp/REPAIR.XXXXXX");
    mktemp(intfile);
	
    /* write out internal rep */
    rc = repair_putdfile(intfile, hlc, hl);
    if (rc) return (-1);
    // repair_printfile(intfile);


    /* done! */
    return(0);
    }








PRIVATE void GetArgs(int argc, char *argv[])
    {
    int i;

    for (i = 1; i < argc; i++)
	{
	if (strcmp(argv[i], "-d") == 0)
	    {
	    repair_DebugFlag = 1;
	    continue;
	    }
	if (strcmp(argv[i], "-nsr") == 0)
	    {
		printf("Old-style repair is no longer supported\n");
		goto BadArgs;
	    }
	if (strcmp(argv[i], "-allowclear") == 0)
	    {
		allowclear = 1;
		continue;
	    }
        if (strcmp(argv[i], "-allowfilerepair") == 0) 
	    {
		allowfilerepair = 1;
		continue;
	    }
	goto BadArgs;
	}
    return;

BadArgs:
    printf("Usage: repair [-d] [-allowclear]\n");
    exit(-1);
    }


PRIVATE void SetDefaultPaths()
{
    char buf[MAXPATHLEN];
    char *repairrc = getenv("REPAIRRC");
    char *home;
    FILE *reprc;
    int ec;
    char arg1, arg2[MAXPATHLEN];  
    char *display;

    beginRepDefault[0] = '\0';
    doRepDefault[0] = '\0';

    if (repairrc == NULL){
	home = getenv("HOME");
	assert(home != NULL);
	strcpy(buf, home);
	strcat(buf, "/.repairrc");
	repairrc = buf;
    } 
    if (reprc = fopen(repairrc, "r")){
	while((ec = fscanf(reprc, "%c\t%s\n", &arg1, arg2)) != EOF){
	    if (ec != 2){
		printf("Error in file %s \n", repairrc);
		exit(-1);
	    }
	    switch(arg1) {
	      case 'b':
		strcpy(beginRepDefault, arg2);
		break;
	      case 'c':
                strcpy(compDirDefault, arg2);
		break;
	      case 'd':
		strcpy(doRepDefault, arg2);
		break;
	      default:
		printf("Unknown option %c in %s file\n", 
		       arg1, repairrc);
		exit(-1);
	    }
	}
	fclose(reprc);
    }
    /* if using x... then xfrepair should be used for files */
    if (!(display = getenv("DISPLAY")))
	allowfilerepair = 1;
}

PRIVATE int GetReplicaNames(char **names, int maxnames, char
			     *ReplicatedName) {
    struct direct *de;
    struct stat buf;
    int i;
    DIR *d = opendir(ReplicatedName);
    if (!d) {
	printf("GetReplicaNames: coudln't open %s\n", ReplicatedName);
	return(-1);
    }
    int SizeRepName = (int) (strlen(ReplicatedName) + 1);
    i = 0; 
    de = readdir(d);
    for (; (i < maxnames) && (de != NULL); de = readdir(d)) {
	if ((!strcmp(de->d_name, ".")) || 
	    (!strcmp(de->d_name, "..")))
	    continue;

        /* 5 is for safety */
	names[i] = (char *)malloc(strlen(de->d_name) + SizeRepName + 5); 
	assert(names[i]);
	sprintf(names[i], "%s/%s" , ReplicatedName, de->d_name);
	if (stat(names[i], &buf)) {
	    perror(names[i]);
	    printf("replica %s not accessible, NOT USING FOR COMPARE\n", 
		   names[i]);
	    free(names[i]);
	    names[i] = 0;
	    continue;
	}
	else { /* append a "/" to the name only if it is a directory */
	    if ((buf.st_mode & S_IFMT) == S_IFDIR) 
		strcat(names[i], "/");
	    i++;
	}
    }
    closedir(d);
    return (i);
}

// return zero if user has valid tokens
PRIVATE int GetTokens() {
    ClearToken clear;
    EncryptedSecretToken secret;
    return (U_GetLocalTokens(&clear, secret));
}

int beginRepair(char *args)
    /* args:  <reppathname> */
    {
    VolumeId vid;
    int rc;
    char *p;
    VolumeId *tv;
    struct ViceIoctl vioc;
    struct repvol *repv;
    char space[2048]; /* XXX */
    char userpath[MAXPATHLEN], reppath[MAXPATHLEN];
    char prefix[MAXPATHLEN], suffix[MAXPATHLEN];

    p = args;
    *userpath= 0;
    while (*userpath == 0)
	strarg(&p, " ", "Pathname of object in conflict?", 
	       beginRepDefault, userpath);
    strcpy(beginRepDefault, userpath);
    strcpy(doRepDefault, userpath);
    rc = repair_isleftmost(userpath, reppath);
    if (rc < 0) return(-1);

    rc = repair_getmnt(reppath, prefix, suffix, &vid);
    if (rc < 0) return(-1);
    /* See if this volume is already being repaired */
    repair_findrep(vid, &repv);
    if (repv) return(0);

    /* Create a new rep vol entry */
    repair_newrep(vid, prefix, &repv);

    /* Obtain names of rw replicas */
    vioc.out_size = (short)sizeof(space);
    vioc.in_size = 0;
    vioc.out = space;
    bzero(space, (int)sizeof(space));
    rc = pioctl(prefix, VIOC_ENABLEREPAIR, &vioc, 0);
    if (rc < 0)
	{
	if (errno == EWOULDBLOCK)
	    {
	    printf("Repair in progress on volume at \"%s\"\n", prefix);
	    /* Print out Workstation/IP addr here --- Satya */
	    }
	else myperror(" ENABLEREPAIR", prefix, errno); /* some other error */
	repair_finish(repv);
	return(-1);
	}
    sprintf(repv->rodir, "%s", reppath);

    /* Mount the rw replicas - or just insert into list of replicas */
    tv = (VolumeId *)space; /* output of ENABLEREPAIR pioctl */
    rc = repair_mountrw(repv, tv, MAXHOSTS);
    if (rc) {repair_finish(repv); return(-1);}
    /* Link in new volume */
    repair_linkrep(repv);
    
    char cmd[10];
    sprintf(cmd, "%d 1", REP_CMD_BEGIN);
    vioc.in = cmd;
    vioc.in_size = (short) (strlen(cmd) + 1);
    vioc.out_size = (short)sizeof(space);
    vioc.out = space;
    rc = pioctl(reppath, VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_BEGIN)");
    }

    sscanf(vioc.out, "%d", &rc);
    switch (rc) {
    case 0: 
	printf("a local-global-conflict repair session started\n");
	printf("the conflict are cuased by reintegration failures\n");
	printf("use the following commands to repair the conflict:\n");
	printf("\tchecklocal\n");
	printf("\tlistlocal\n");
	printf("\tpreservelocal\n");
	printf("\tpreservealllocal\n");
	printf("\tdiscardlocal\n");
	printf("\tdiscardalllocal\n");
	printf("\tsetglobalview\n");
	printf("\tsetmixedview\n");
	printf("\tsetlocalview\n");
	printf("a list of disconnected mutations is availabe in a .cml file in the coda spool directory\n");
	localsession = 1;
	repv->local = 1;
	break;
    case 1: 
	printf("a local-global-conflict repair session already in progress\n");
	localsession = 1;
	repv->local = 0;
	break;
    case 2:
	printf("a server-server-conflict repair session started\n");
	localsession = 0;
	repv->local = 0;
	break;
    default:
	printf("bogus return code from venus (%d)\n", rc);
    }
    fflush(stdout);
    return(0);
}

void checkLocal(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[2048];
    
    /* Release volume-level locks */
    vioc.out = space;
    vioc.out_size = 2048;
    vioc.in = buf;
    sprintf(buf, "%d", REP_CMD_CHECK);
    vioc.in_size = (short) strlen(vioc.in) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_CHECK)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}

void listLocal(char *args)
    /* args: <no args> */
    {
    int fd;
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[2048];
    char filename[100];
    
    /* Release volume-level locks */
    vioc.out = space;
    vioc.out_size = 2048;
    strcpy(filename, "/tmp/listlocal.XXXXXX");
    mktemp(filename);
    vioc.in = buf;
    sprintf(buf, "%d %s", REP_CMD_LIST, filename);
    vioc.in_size = (short) strlen(vioc.in) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_LIST)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
    if (rc == 0) {
	fd = open(filename, O_RDONLY, 0);
	if (fd < 0) {
	    perror(filename);
	} else {
	    while (read(fd, buf, 2048) > 0)
	      write(1, buf, strlen(buf));
	    close(fd);
	}	
    }
    unlink(filename);
}

void preserveLocal(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[BUFSIZ];
    
    /* Release volume-level locks */
    vioc.out = space;
    vioc.out_size = 2048;
    sprintf(buf, "%d", REP_CMD_PRESERVE);
    vioc.in = buf;
    vioc.in_size = (short) strlen(buf) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMU_PRESERVE)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}

void preserveAllLocal(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[BUFSIZ];
    
    /* Release volume-level locks */
    vioc.out = space;
    vioc.out_size = 2048;
    sprintf(buf, "%d", REP_CMD_PRESERVE_ALL);
    vioc.in = buf;
    vioc.in_size = (short) strlen(buf) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMU_PRESERVE_ALL)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}

void discardLocal(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[BUFSIZ];
    
    /* Release volume-level locks */
    vioc.out = space;
    vioc.out_size = 2048;
    sprintf(buf, "%d", REP_CMD_DISCARD);
    vioc.in = buf;    
    vioc.in_size = (short) strlen(buf) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_DISCARD)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}

void discardAllLocal(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[BUFSIZ];
    
    /* Release volume-level locks */
    vioc.out = space;
    vioc.out_size = 2048;
    sprintf(buf, "%d", REP_CMD_DISCARD_ALL);
    vioc.in = buf;
    vioc.in_size = (short) strlen(buf) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_DISCARD_ALL)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}

void setLocalView(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[BUFSIZ];
    
    vioc.out = space;
    vioc.out_size = 2048;
    sprintf(buf, "%d", REP_CMD_LOCAL_VIEW);
    vioc.in = buf;
    vioc.in_size = (short) strlen(buf) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_LOCAL_VIEW)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}

void setGlobalView(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[BUFSIZ];
    
    vioc.out = space;
    vioc.out_size = 2048;
    sprintf(buf, "%d", REP_CMD_GLOBAL_VIEW);
    vioc.in = buf;
    vioc.in_size = (short) strlen(buf) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_GLOBAL_VIEW)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}

void setMixedView(char *args)
    /* args: <no args> */
    {
    struct ViceIoctl vioc;
    int rc;
    struct repvol *repv;
    char space[2048];
    char buf[BUFSIZ];
    
    vioc.out = space;
    vioc.out_size = 2048;
    sprintf(buf, "%d", REP_CMD_MIXED_VIEW);
    vioc.in = buf;
    vioc.in_size = (short) strlen(buf) + 1;

    rc = pioctl("/coda", VIOC_REP_CMD, &vioc, 0);
    if (rc < 0) {
	perror("VIOC_REP_CMD(REP_CMD_MIXED_VIEW)");
    }
    printf("%s\n", vioc.out);
    fflush(stdout);
}
