
/* 
 * internal.c
 *
 * $Author: dm3e $
 * $Date: 1993/03/04 00:51:50 $
 * $Revision: 1.6 $
 * $State: Exp $
 * $Locker:  $
 */

/******************************************************************
 *        Copyright 1991 by Carnegie Mellon University
 *
 *                      All Rights Reserved
 *
 * 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 and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of CMU not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 ******************************************************************/

#include "emtd.h"

/***************************************************************************
 * [exported] emt_GetVolumeInfo
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Tue Sep 24 20:43:44 1991 - created by David Allen Markley
 *      Tue Nov 17 16:44:18 1992 - David Allen Markley (dm3e) -
 *           Moved to internal.c
 *
 ***************************************************************************/
long
emt_GetVolumeInfo (volume, server, partition, id, sites)
     char               *volume,
                       **server,
                       **partition;
     int                *id,
                        *sites;
{
  admint_objectPtr_t  obj,
                      value;
  long                admcode;
  char               *admerr;

  IF_RTN (admlib_obj_ConvertToObj
	  (&obj, admlib_obj_FromList,
	   admlib_obj_FromIdentifier, "emt-get-entry",
	   admlib_obj_FromString, volume,
	   ADMLIB_OBJ_END));
  IF_RTN (adm_Evaluate (obj, &value, &admcode, &admerr, NULL));
  IF_RTN (admlib_obj_ConvertFromObj
	  (value, admlib_obj_ToList,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToNumber, sites,
	   admlib_obj_ToListPartial,	/* BEGIN SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBSUBLIST */
	   admlib_obj_ToString, server,
	   admlib_obj_ToString, partition,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBSUBLIST */
	   ADMLIB_OBJ_END,
	   admlib_obj_ToIgnore,	/* END SUBLIST */
	   admlib_obj_ToListPartial,	/* BEGIN SUBLIST */
	   admlib_obj_ToNumber, id,
	   ADMLIB_OBJ_END,
	   admlib_obj_ToIgnore,	/* END SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END));
  return OK;
}

/***************************************************************************
 * [exported] emt_GetMoreVolumeInfo
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Fri Oct 18 13:47:23 1991 - created by David Allen Markley
 *      Tue Nov 17 16:46:27 1992 - David Allen Markley (dm3e) -
 *           Moved to internal.c
 *
 ***************************************************************************/
long
emt_GetMoreVolumeInfo (server, partition, volid, used, quota)
     char               *server,
                        *partition;
     long                volid;
     int                *used,
                        *quota;
{
  admint_objectPtr_t  obj,
                      value;
  long                admcode;
  char               *admerr;

  IF_RTN (admlib_obj_ConvertToObj
	  (&obj, admlib_obj_FromList,
	   admlib_obj_FromIdentifier, "emt-list-one-volume",
	   admlib_obj_FromString, server,
	   admlib_obj_FromString, partition,
	   admlib_obj_FromNumber, volid,
	   ADMLIB_OBJ_END));
  IF_RTN (adm_Evaluate (obj, &value, &admcode, &admerr, NULL));
  IF_RTN (admlib_obj_ConvertFromObj
	  (value, admlib_obj_ToList,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToNumber, quota,
	   admlib_obj_ToNumber, used,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   admlib_obj_ToList,	/* BEGIN SUBLIST */
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   admlib_obj_ToIgnore,
	   ADMLIB_OBJ_END,	/* END SUBLIST */
	   ADMLIB_OBJ_END));
  return OK;
}

/***************************************************************************
 * [exported] emtd_MakeDir
 *
 * Description:
 *      Creates the directory denoted by the path supplied, and sets its
 *      mode accordingly.
 *
 * Arguments:
 *      path - The complete path for the directory, including the directory.
 *      mode - The mode that the directory will be given (as in chmod).
 *
 * History:
 *      Thu Oct  1 13:42:56 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_MakeDir (path, mode)
char   *path;
int     mode;
{
    CheckAuth ();
    if (!mkdir (path, mode)) {
	Log("mkdir: %s\n", path);
	return OK;
    }
    switch (errno) {
    case EISDIR: 
    case EEXIST: 
	return OK;
    default: 
	Log("Error making directory %s (%ld).\n", path, errno);
	return errno;
    }
}

/***************************************************************************
 * [exported] emtd_ListMount
 *
 * Description:
 *      Returns the volume mounted at the location given by path.
 *
 * Arguments:
 *      path - The path to the mountpoint.
 *      mount - When the procedure returns, this points to the volume name.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 13:50:16 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_ListMount (path, mount)
char   *path;
char  **mount;
{
    char   *mount_found;
    int     error;

    if (NULL == (mount_found = statMount (path, &error))) {
	mount_found = "";
	*mount = copy_string (mount_found);
	return error;
    }
    *mount = copy_string (mount_found);
    return OK;
}

/***************************************************************************
 * [exported] emtd_Depot
 *
 * Description:
 *      Runs depot on an environment. The way it is run depends on the
 *      flags that are present, and information in the request structure.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      system - The system for which depot will be run under the environment.
 *      flags - A compilation of flags that indicate how depot should be run.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 13:50:35 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_Depot (request, system, flags, errstr)
        comreq_t request;
char *system;
long flags;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
            command[MAXBUFSIZE],
            depot_dir[MAXPATHLEN],
            db_dir[MAXPATHLEN],
            script_path[MAXPATHLEN],
            arguments[MAXBUFSIZE],
            cline[MAXBUFSIZE],
           *host;
    FILE *fd;
    register long   code;
    edb_sys_t c_sys;
    edb_depot_t c_dh;
    register char  *c;
    int     i;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
    if (NULL == (c_sys = get_system (request->env, system))) {
	Log ("%s system not found.\n", system);
	return EMTD_BAD_SYSTEM;
    }
    emtd_Broadcast(request, EMTD_LOGBASE, 
		   "|==== Running Depot on %s in %s %s for %s ====|\n", 
		   REQ_COLLECTION, REQ_ENVIRONMENT, REQ_TREE, c_sys->name);
				/* We must obtain a lock to ensure that other
				 processes are not running depot on this
				 same area */
    while (-1 == CheckLock ((&(c_sys -> depot_lock)))) {
	emtd_Broadcast(request, EMTD_LOGXTRA, "Lock found on %s %s %s.\n",
		       REQ_TREE, REQ_ENVIRONMENT, system);
	emtd_Broadcast(request, EMTD_LOGXTRA,
		       "Waiting for lock to be released by another process\n");
	LWP_WaitProcess ((&(c_sys -> depot_event)));
	emtd_Broadcast(request, EMTD_LOGXTRA,
		       "Lock released. Continuing depot run.\n");
    }
    ObtainWriteLock ((&(c_sys -> depot_lock)));
				/* Now we set up the arguments for depot */
    bzero (command, MAXBUFSIZE);
    bzero (arguments, MAXBUFSIZE);
    strcpy (arguments, "-");
    if (!c_sys -> depot) {
	sprintf (command, "/usr/local/etc/wallydepot");
	if (flags & DEPOT_VERBOSE)
	    strcat (arguments, "v");
	if (flags & DEPOT_FORCE)
	    strcat (arguments, "F");
	if (flags & DEPOT_DELETE)
	    strcat (arguments, "u");
    }
    else {
	sprintf (command, "/usr/local/bin/depot");
	if (flags & DEPOT_COLLECTIONS)
	    strcat (arguments, "c");
	if (flags & DEPOT_ALL)
	    strcat (arguments, "a");
	if (flags & DEPOT_DELETE)
	    strcat (arguments, "d");
	if (flags & DEPOT_QUICK)
	    strcat (arguments, "q");
	if (flags & DEPOT_UNLOCK)
	    strcat (arguments, "u");
	if (flags & DEPOT_VERBOSE)
	  strcat (arguments, "v");
    }
    if ('\0' == arguments[1])
	arguments[0] = '\0';
#ifdef USE_RSH
    sprintf (script_path, "%s/rshdepot", EMTD_SCRIPTS_DIRECTORY);
#else
    sprintf (script_path, "%s/localdepot", EMTD_SCRIPTS_DIRECTORY);
#endif				/* USE_RSH */
				/* We have to log the depot output to a file
				 on afs, and then read it back, since the
				 popen only returns the error code from the
				 rsh call. Hey...it works. */
    sprintf (db_dir, "%s/%s/depot-%s-%s-%s", EMTD_DB_DIRECTORY, REQ_TREE,
	    REQ_TREE, REQ_ENVIRONMENT, c_sys -> name);
    sprintf (depot_dir, "%s/depot", c_sys -> path);
    emtd_Broadcast(request, EMTD_LOGXTRA, "%s %s %s\n", command, 
		   arguments, REQ_COLLECTION);
    code = NULL;
#ifdef USE_RSH
    if (NULL != (c_dh = c_sys -> depot_hosts)) {
	do {
	    CheckAuth ();
	    host = copy_string (c_dh -> hostname);
	    code = emtd_LWPopen (request, EMTD_RSH_PATH, EMTD_RSH_PATH,
				 host, script_path, db_dir, depot_dir,
				 arguments, REQ_COLLECTION, NULL);
	    c_dh = c_dh -> next;
	} while ((0 != code) && (NULL != c_dh));
    }
    else {
	sprintf (script_path, "%s/localdepot", EMTD_SCRIPTS_DIRECTORY);
	CheckAuth ();
	code = emtd_LWPopen (request, script_path, script_path, db_dir,
			     depot_dir, arguments, REQ_COLLECTION, NULL);
    }
#else
    CheckAuth ();
    code = emtd_LWPopen (request, script_path, script_path, db_dir, depot_dir,
			 arguments, REQ_COLLECTION, NULL);
#endif				/* USE_RSH */
    if (0 != code) {
#ifdef USE_RSH
	emtd_Broadcast(request, EMTD_LOGERR, "rsh exited %ld\n", code);
#else
	emtd_Broadcast(request, EMTD_LOGERR, "localdepot exited %ld\n", code);
#endif				/* USE_RSH */
	ReleaseWriteLock ((&(c_sys -> depot_lock)));
	LWP_NoYieldSignal ((&(c_sys -> depot_event)));
	return code;
    }
    code = 0;
				/* Now we get the error code from our depot
				 run from the file on afs */
    sprintf (buffer, "%s-depot.status", db_dir);
    if (NULL != (fd = fopen (buffer, "r"))) {
	if (NULL != fgets (buffer, MAXBUFSIZE, fd)) {
	    i = 0;
	    code = next_int (&i, buffer, ',');
	}
	fclose (fd);
    }
				/* Log the output of the depot run */
    sprintf (buffer, "%s-depot.errors", db_dir);
    if (NULL != (fd = fopen (buffer, "r"))) {
	long nlines = 0;

	while (NULL != fgets (buffer, MAXBUFSIZE, fd)) {
	    buffer[strlen (buffer) - 1] = '\0';
	    emtd_Broadcast(request, EMTD_LOGXTRA, "%s\n", buffer);
	    if (++nlines > 100) {
		Log("Sleeping.\n");
		IOMGR_Sleep(1);
		nlines = 0;
	    }
	}
	fclose (fd);
    }
    emtd_Broadcast(request, code ? EMTD_LOGERR : EMTD_LOGXTRA,
		   "Depot code %ld\n", code);
    sprintf (buffer, "%s-depot.status", db_dir);
    unlink (buffer);
    sprintf (buffer, "%s-depot.errors", db_dir);
    unlink (buffer);
    emtd_Broadcast(request, EMTD_LOGBASE, "|==== End of Depot ====|\n");
				/* Release the lock */
    ReleaseWriteLock ((&(c_sys -> depot_lock)));
				/* Signal other procs that might be
				 waiting to run depot on the same area */
    LWP_NoYieldSignal ((&(c_sys -> depot_event)));
    return code;
}

/***************************************************************************
 * [exported] emtd_MakeGroups
 *
 * Description:
 *      Creates the groups that are associated with the collections.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:05:02 1992 - created by David Allen Markley
 *      Tue Nov 17 14:07:51 1992 - David Allen Markley (dm3e) -
 *           Added creation of visitors group.
 *
 ***************************************************************************/
long    emtd_MakeGroups(request, errstr)
        comreq_t request;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
            owner[MAXBUFSIZE];
    FILE * pfile;
    register long   code;
    int i;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));

    sprintf (buffer, "sysmaint:%s.commanders", REQ_COLLECTION);
    for (i = 0; i < MAXBUFSIZE && '\0' != buffer[i]; i++)
      buffer[i] = tolower(buffer[i]);
    strcpy(owner, request->tree->owner);
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
		admlib_obj_FromIdentifier, "emt-create-group",
		admlib_obj_FromString, buffer,
		admlib_obj_FromEmpty,
		admlib_obj_FromString, owner,
		ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode, &admerr, errstr);
    switch (code) {
    case PREXIST:
	break;
    case OK:
	emtd_Broadcast(request, EMTD_LOGXTRA, "Created group: %s.\n", buffer);
	break;
    default:
	return code;
    }
    if (REQ_VISITORS || (REQ_FLAGS & PROTECTED_COLLECTION)) {
	sprintf (buffer, "sysmaint:%s.visitors", REQ_COLLECTION);
	for (i = 0; i < MAXBUFSIZE && '\0' != buffer[i]; i++)
	  buffer[i] = tolower(buffer[i]);
	strcpy(owner, request->tree->owner);
	IF_RTN (admlib_obj_ConvertToObj
		(&obj, admlib_obj_FromList,
		 admlib_obj_FromIdentifier, "emt-create-group",
		 admlib_obj_FromString, buffer,
		 admlib_obj_FromEmpty,
		 admlib_obj_FromString, owner,
		 ADMLIB_OBJ_END));
	code = emtd_admEvaluate(request, obj, &value, &admcode, &admerr, errstr);
	switch (code) {
	case PREXIST:
	    break;
	case OK:
	    emtd_Broadcast(request, EMTD_LOGXTRA, "Created group: %s.\n", buffer);
	    break;
	default:
	    return code;
	}
    }
    return OK;
}

/***************************************************************************
 * [exported] emtd_DeleteGroups
 *
 * Description:
 *
 * Arguments:
 *
 * Environment:
 *
 * History:
 *      Fri Jan  8 11:17:23 1993 - created by David Allen Markley
 *
 ***************************************************************************/
long emtd_DeleteGroups(request, errstr)
comreq_t request;
char  **errstr;
{
    char    buffer[MAXBUFSIZE];
    FILE * pfile;
    register long   code;
    int i;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));

    sprintf (buffer, "sysmaint:%s.commanders", REQ_COLLECTION);
    for (i = 0; i < MAXBUFSIZE && '\0' != buffer[i]; i++)
      buffer[i] = tolower(buffer[i]);
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "emt-delete-group",
	     admlib_obj_FromString, buffer,
	     ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode, &admerr, errstr);
    switch (code) {
    case PRNOENT:
    case PRPERM:
	break;
    case OK:
	emtd_Broadcast(request, EMTD_LOGXTRA, "Deleted group: %s.\n", buffer);
	break;
    default:
	emtd_Broadcast(request, EMTD_LOGERR, "Error deleting group: %s (%ld).\n",
		       buffer, code);
	return code;
    }
    sprintf (buffer, "sysmaint:%s.visitors", REQ_COLLECTION);
    for (i = 0; i < MAXBUFSIZE && '\0' != buffer[i]; i++)
      buffer[i] = tolower(buffer[i]);
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "emt-delete-group",
	     admlib_obj_FromString, buffer,
	     ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode, &admerr, errstr);
    switch (code) {
    case PRNOENT:
    case PRPERM:
	break;
    case OK:
	emtd_Broadcast(request, EMTD_LOGXTRA, "Deleted group: %s.\n", buffer);
	break;
    default:
	emtd_Broadcast(request, EMTD_LOGERR, "Error deleting group: %s (%ld).\n",
		       buffer, code);
	return code;
    }
    return OK;
}

/***************************************************************************
 * [exported] emtd_AddToGroups
 *
 * Description:
 *      Adds users found in the request structure to the maintainer group
 *      for the collection associated with the request.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:07:17 1992 - created by David Allen Markley
 *      Tue Nov 17 14:07:29 1992 - David Allen Markley (dm3e) -
 *           Changed to handle visitor group additions.
 *
 ***************************************************************************/
long    emtd_AddToGroups (request, errstr)
        comreq_t request;
char **errstr;
{
    char    group[MAXBUFSIZE];
    register long   code;
    llist_t c_user;
    int i;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));

    if (NULL != (c_user = REQ_USERS)) {
	emtd_Broadcast(request, EMTD_LOGBASE, "Adding users.\n");
	sprintf (group, "sysmaint:%s.commanders", REQ_COLLECTION);
	for (i = 0; i < MAXBUFSIZE && '\0' != group[i]; i++)
	  group[i] = tolower(group[i]);
	while (NULL != c_user) {
	    IF_RTN (admlib_obj_ConvertToObj
		(&obj, admlib_obj_FromList,
		 admlib_obj_FromIdentifier, "emt-add-to-group",
		 admlib_obj_FromString, c_user -> buffA,
		 admlib_obj_FromString, group,
		 ADMLIB_OBJ_END));
	    code = emtd_admEvaluate(request, obj, &value, &admcode,&admerr,errstr);
	    switch (code) {
	    case PRIDEXIST:
		emtd_Broadcast(request, EMTD_LOGXTRA, "%s already in %s.\n",
			       c_user->buffA, group);
		break;
	    case OK:
		emtd_Broadcast(request, EMTD_LOGXTRA, "Added %s to %s.\n", 
			       c_user->buffA, group);
		break;
	    default:
		emtd_Broadcast(request, EMTD_LOGERR, "Error adding %s to %s.\n",
			       c_user->buffA, group);
	    }
	    c_user = c_user -> next;
	}
    }
    if (NULL == (c_user = REQ_VISITORS)) return OK;
    emtd_Broadcast(request, EMTD_LOGBASE, "Adding visitors.\n");
    sprintf (group, "sysmaint:%s.visitors", REQ_COLLECTION);
    for (i = 0; i < MAXBUFSIZE && '\0' != group[i]; i++)
      group[i] = tolower(group[i]);
    while (NULL != c_user) {
	IF_RTN (admlib_obj_ConvertToObj
		(&obj, admlib_obj_FromList,
		    admlib_obj_FromIdentifier, "emt-add-to-group",
		    admlib_obj_FromString, c_user -> buffA,
		    admlib_obj_FromString, group,
		    ADMLIB_OBJ_END));
	code = emtd_admEvaluate(request, obj, &value, &admcode,&admerr,errstr);
	switch (code) {
	case PRIDEXIST:
	    emtd_Broadcast(request, EMTD_LOGXTRA, "%s already in %s.\n",
			   c_user->buffA, group);
	    break;
	case OK:
	    emtd_Broadcast(request, EMTD_LOGXTRA, "Added %s to %s.\n", 
			   c_user->buffA, group);
	    break;
	default:
	    emtd_Broadcast(request, EMTD_LOGERR, "Error adding %s to %s.\n",
			   c_user->buffA, group);
	}
	c_user = c_user -> next;
    }
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumePurge
 *
 * Description:
 *      Purges the volume from afs, through a call to ADM. Before doing so,
 *      it checks to make sure that the volume is not in use in any environment
 *      under the tree assocated with the request.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume to be purged.
 *      system - The system type of the volume to be purged.
 *      flags - Flags indicating the type of volume being purged.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:14:27 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long emtd_VolumePurge (request, volume, system, flags, errstr)
comreq_t request;
char   *volume;
char   *system;
long    flags;
char  **errstr;
{
    register long   code;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
				/* Check to make sure it isn't in use */
    if (VolumeInUse(system, REQ_COLLECTION, REQ_VERSION, flags)) {
	emtd_Broadcast(request, EMTD_LOGERR,
		       "%s not purged. Still released to an environment.\n",
		       volume);
	return OK;
    }
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "emt-purge-volume",
	     admlib_obj_FromString, volume,
	     ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode, &admerr, errstr);
    switch (code) {
    case VL_NOENT:
	break;
    case OK:
	emtd_Broadcast(request, EMTD_LOGXTRA, "%s purged.\n", volume);
	break;
    default:
	emtd_Broadcast(request, EMTD_LOGERR, "Error purging %s (%ld).\n",
		       volume, admcode);
    }
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumeCreate
 *
 * Description:
 *      Creates the desired volume through a call to ADM.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume to be created.
 *      system - The system type of the volume being created.
 *      quota - The desired quota for the volume being created.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:18:56 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeCreate (request, volume, system, quota, errstr)
        comreq_t request;
char   *volume;
char   *system;
long quota;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
           *server = NULL,
           *partition = NULL;
    FILE * pfile;
    long    code,
            i;
    llist_t c_site;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
				/* Check to see if a site for creation was
				 specified by the caller */
    for (c_site = REQ_SITES; c_site; c_site = c_site->next) {
	if (!strcmp(system, "src") || !strcmp(system, "obj")) {
	    if (strcmp(c_site->buffA, system)) continue;
	} else if (strcmp(c_site->buffA, "dest") &&
		   strcmp(c_site->buffA, system)) continue;
	if (NULL == (c_site = c_site->next)) break;
	server = copy_string(c_site->buffA);
	if (NULL == (c_site = c_site->next)) break;
	partition = copy_string(c_site->buffA);
    }
				/* If no site was specified, use the site
				 program to determine a site */
    if (NULL == server || NULL == partition) {
	sprintf (buffer, "%s -t %s -s %s -a create -q %ld", EMTD_SITE_PATH,
		 REQ_TREE, system, quota);
	if (NULL != (pfile = popen (buffer, "r"))) {
	    if (NULL == fgets (buffer, MAXBUFSIZE, pfile)) {
		pclose (pfile);
		free(*errstr);
		*errstr = copy_string ("site call failed");
		return EMTD_SITE_CALL_ERR;
	    }
	    else {
		LogN (buffer);
		i = 0;
		server = next_string (&i, buffer);
		partition = next_string (&i, buffer);
	    }
	    pclose (pfile);
	} else {
	    return EMTD_SITE_CALL_ERR;
	}
    }
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "emt-create-volume",
	     admlib_obj_FromString, server,
	     admlib_obj_FromString, partition,
	     admlib_obj_FromString, volume,
	     admlib_obj_FromEmpty,
	     ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode,&admerr,errstr);
    switch (code) {
    case VL_NAMEEXIST:
	break;
    case OK:
	emtd_Broadcast(request, EMTD_LOGXTRA, "%s created on %s %s.\n",
		       volume, server, partition);
	break;
    default:
	emtd_Broadcast(request, EMTD_LOGERR,
		       "Error creating %s on %s %s (code = %ld).\n",
		       volume, server, partition, code);
	free(server);
	free(partition);
	return code;
    }
    free(server);
    free(partition);
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumeCopy
 *
 * Description:
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume to be created.
 *      system - The system type of the volume being created.
 *      quota - The desired quota for the volume being created.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:22:56 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long emtd_VolumeCopy (request, fvolume, tvolume, system, quota, errstr)
        comreq_t request;
char   *fvolume;
char   *tvolume;
char   *system;
long quota;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
           *server = NULL,
           *partition = NULL,
           *fserver,
           *fpartition;
    FILE * pfile;
    long    i,
            code,
            fvolid,
            fsites;
    llist_t c_site;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));

				/* Check to see if a site for creation was
				 specified by the caller */
    for (c_site = REQ_SITES; c_site; c_site = c_site->next) {
	if (!strcmp(system, "src") || !strcmp(system, "obj")) {
	    if (strcmp(c_site->buffA, system)) continue;
	} else if (strcmp(c_site->buffA, "dest") &&
		   strcmp(c_site->buffA, system)) continue;
	if (NULL == (c_site = c_site->next)) break;
	server = copy_string(c_site->buffA);
	if (NULL == (c_site = c_site->next)) break;
	partition = copy_string(c_site->buffA);
    }
				/* If no site was specified, use the site
				 program to determine a site */
    if (NULL == server || NULL == partition) {
	sprintf (buffer, "%s -t %s -s %s -a create -q %ld", EMTD_SITE_PATH,
		 REQ_TREE, system, quota);
	LogN (buffer);
	if (NULL != (pfile = popen (buffer, "r"))) {
	    if (NULL == fgets (buffer, MAXBUFSIZE, pfile)) {
		pclose (pfile);
		free(*errstr);
		*errstr = copy_string ("site call failed");
		return EMTD_SITE_CALL_ERR;
	    }
	    else {
		LogN (buffer);
		i = 0;
		server = next_string (&i, buffer);
		partition = next_string (&i, buffer);
	    }
	    pclose (pfile);
	} else {
	    return EMTD_SITE_CALL_ERR;
	}
    }	    
    IF_RTN (emt_GetVolumeInfo
	    (fvolume, &fserver, &fpartition, &fvolid, &fsites));
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "emt-copy-volume",
	     admlib_obj_FromNumber, fvolid,
	     admlib_obj_FromString, fserver,
	     admlib_obj_FromString, fpartition,
	     admlib_obj_FromString, tvolume,
	     admlib_obj_FromString, server,
	     admlib_obj_FromString, partition,
	     ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode,&admerr,errstr);
    switch (admcode) {
    case VL_NAMEEXIST:
	break;
    case OK:
	emtd_Broadcast(request, EMTD_LOGXTRA, "%s created on %s %s.\n",
		       tvolume, server, partition);
	break;
    default:
	emtd_Broadcast(request, EMTD_LOGERR, "Error creating %s (%ld).\n",
		       tvolume, admcode);
	free(server);
	free(partition);
	return admcode;
    }
    free(server);
    free(partition);
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumeRelease
 *
 * Description:
 *      Releases the specified volume through a call to ADM.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume to be released.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:33:04 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeRelease (request, volume, errstr)
        comreq_t request;
char   *volume;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
           *server,
           *partition;
    FILE * pfile;
    long    volid,
            sites,
            code;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
				/* Check to see if the volume is replicated */
    IF_RTN (emt_GetVolumeInfo (volume, &server, &partition, &volid, &sites));
    if (sites == 1) {
	Log ("%s not replicated.\n", volume);
	return OK;		/* If it isn't, return without releasing it */
    }
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
		admlib_obj_FromIdentifier, "emt-release-volume",
		admlib_obj_FromString, server,
		admlib_obj_FromString, partition,
		admlib_obj_FromNumber, volid,
		admlib_obj_FromBoolean, TRUE,
		ADMLIB_OBJ_END));
    ADM_INTEVAL;
    emtd_Broadcast(request, EMTD_LOGXTRA, "%s released.\n", volume);
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumeMove
 *
 * Description:
 *      Moves a volume from one site to another, through an ADM call.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volid - The volume id of the volume being moved.
 *      srvr - The server the volume is initially on.
 *      part - The partition the volume is initially on.
 *      rep_srvr - The server the volume is being moved to.
 *      rep_part - The partition the volume is being moved to.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Tue Oct 27 12:43:28 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long emtd_VolumeMove(request, volid, srvr, part, rep_srvr, rep_part, errstr)
comreq_t request;
long volid;
char *srvr;
char *part;
char *rep_srvr;
char *rep_part;
char **errstr;
{
    long code;
    ADMVARS;
		
    IF_RTN(admlib_obj_ConvertToObj
	   (&obj, admlib_obj_FromList,
	    admlib_obj_FromIdentifier, "emt-move-volume",
	    admlib_obj_FromNumber, volid,
	    admlib_obj_FromString, srvr,
	    admlib_obj_FromString, part,
	    admlib_obj_FromString, rep_srvr,
	    admlib_obj_FromString, rep_part,
	    ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode,
			    &admerr, errstr);
    switch (code) {
    case OK:
	return OK;
    default:
	emtd_Broadcast(request, EMTD_LOGERR, "Moving volid = %ld to %s %s.\n",
		       volid, srvr, part);
	return code;
    }
}

/***************************************************************************
 * [exported] emtd_VolumeAddSite
 *
 * Description:
 *      Adds a replication site to a volume, through an ADM call.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volid - The volume id of the volume that is having the site added.
 *      server - The server portion of the site being added.
 *      partition - The partition portion of the site being added.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Tue Oct 27 12:50:15 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long emtd_VolumeAddSite(request, volid, server, partition, errstr)
comreq_t request;
long volid;
char *server;
char *partition;
char **errstr;
{
    long code;
    ADMVARS;

    IF_RTN(admlib_obj_ConvertToObj
	   (&obj, admlib_obj_FromList,
	    admlib_obj_FromIdentifier, "emt-add-site",
	    admlib_obj_FromString, server,
	    admlib_obj_FromString, partition,
	    admlib_obj_FromNumber, volid,
	    ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, obj, &value, &admcode,
			    &admerr, errstr);
    switch (code) {
    case OK:
	return OK;
    default:
	emtd_Broadcast(request, EMTD_LOGERR,
		       "Adding site %s %s to site to volid = %ld.\n", server,
		       partition, volid);
	return code;
    }
}


/***************************************************************************
 * [exported] emtd_VolumeReplicate
 *
 * Description:
 *      Replicates the volume specified.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume to be replicated.
 *      system - The system type of the volume being replicated.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:34:44 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeReplicate (request, volume, system, errstr)
        comreq_t request;
char   *volume;
char   *system;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
           *server,
           *partition,
           *rep_server,
           *rep_partition;
    FILE   *pfile;
    long    volid,
            sites,
            moved = 0,
            i,
            code;
    llist_t c_site;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
    IF_RTN (emt_GetVolumeInfo (volume, &server, &partition, &volid, &sites));
    if (sites > 1) {
	Log ("%s already replicated.\n", volume);
	return emtd_VolumeRelease (request, volume, errstr);
    }
				/* See if there were any sites specified in
				 the request for replication */
    for (c_site = REQ_SITES; c_site; c_site = c_site->next) {
	if (strcmp(c_site->buffA, "dest") && strcmp(c_site->buffA, system)) {
	    continue;
	}
	if (NULL == (c_site = c_site->next)) break;
	rep_server = copy_string(c_site->buffA);
	if (NULL == (c_site = c_site->next)) break;
	rep_partition = copy_string(c_site->buffA);
	Log ("server: %s, partition: %s\n", rep_server, rep_partition);
	if (!moved) {
	    code = emtd_VolumeMove(volid, server, partition, rep_server,
				   rep_partition, errstr);
	    if (code) break;
	    moved = 1;
	}
	IF_RTN(emtd_VolumeAddSite(request, volid, rep_server,
				  rep_partition, errstr));
    }
				/* If no sites were specified, use the site
				 program to determine replication sites */
    if (! moved) {
	sprintf (buffer, "%s -a replicate -q 5000 -s %s -t %s", EMTD_SITE_PATH,
		 system, REQ_TREE);
	LogN (buffer);
	if (NULL != (pfile = popen (buffer, "r"))) {
	    while (fgets (buffer, MAXBUFSIZE, pfile) != NULL) {
		LogN (buffer);
		i = 0;
		rep_server = next_string (&i, buffer);
		rep_partition = next_string (&i, buffer);
		Log ("server: %s, partition: %s\n", rep_server, rep_partition);
		if (!moved) {
		    code = emtd_VolumeMove(request, volid, server, partition,
					   rep_server, rep_partition, errstr);
		    if (code) {
			pclose(pfile);
			return code;
		    }
		    moved = 1;
		}
		code = emtd_VolumeAddSite(request, volid, rep_server,
					  rep_partition, errstr);
		if (code) {
		    pclose(pfile);
		    return code;
		}
		free (rep_server);
		free (rep_partition);
	    }
	    pclose (pfile);
	}
    }
    emtd_Broadcast(request, EMTD_LOGXTRA, "%s replicated.\n", volume);
    code = emtd_VolumeRelease (request, volume, errstr);
    return code;
}

/***************************************************************************
 * [exported] emtd_VolumeUnreplicate
 *
 * Description:
 *      Unreplicates the volume specified through a call to ADM.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume to be unreplicated.
 *      system - The system type of the volume being unreplicated.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:41:35 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeUnreplicate (request, volume, system, errstr)
        comreq_t request;
char   *volume;
char   *system;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
           *server,
           *partition;
    tree_t tree;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
		admlib_obj_FromIdentifier, "emt-unreplicate-volume",
		admlib_obj_FromString, volume,
		ADMLIB_OBJ_END));
    ADM_INTEVAL;
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumeMount
 *
 * Description:
 *      Mounts a volume at a location specified by the path, and the flags
 *      which indicate the type of volume being mounted.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume to be mounted.
 *      system - The system type of the volume being mounted.
 *      path - The base path to the location of where the mount will be made.
 *      flags - The flags that indicate the type of volume and if access is
 *              to be set.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 14:43:03 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeMount (request, volume, system, path, flags, errstr)
        comreq_t request;
char   *volume;
char   *system;
char   *path;
long    flags;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
            message[MAXBUFSIZE],
            fpath[MAXPATHLEN],
            spath[MAXPATHLEN],
            *period;
    FILE * pfile;
    int     i,
            code;
    struct timeval  tp;
    long    timestamp,
            oldtime,
            newtime,
            same_version,
            collength;
    char *mount = NULL;
    int mnterr;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
    sprintf (fpath, "%s/%s", path, REQ_COLLECTION);
    if (flags & (SRC_VOL | OBJ_VOL | DEST_VOL)) {
	if (!mkdir (fpath, 666)) {
	    emtd_Broadcast(request, EMTD_LOGXTRA, "mkdir: %s.\n", fpath);
	} else {
	    switch (errno) {
	    case EISDIR: 
	    case EEXIST: 
		sprintf (buffer, "emtd: Directory already existed:\n\t%s.",
			 fpath);
		LogN (buffer);
		break;
	    default: 
		sprintf (buffer,
			 "emtd: Error making directory\(%d\):\n\t%s.",
			 errno, fpath);
		LogN (buffer);
		return errno;
	    }
	}
    }
    if (flags & (SRC_VOL | OBJ_VOL | DEST_VOL)) {
	strcat (fpath, "/");
	strcat (fpath, REQ_VERSION);
	if (flags & COMMON_VOL) {
	    strcat (fpath, "/");
	    strcat (fpath, "common");
	}
    } else {
#ifdef USE_TIMESTAMPS
	DIR *sdir;
	struct direct *sdirent;

	gettimeofday (&tp, NULL);
	timestamp = tp.tv_sec;
	sprintf (spath, "%s/depot", path);
	if (NULL == (sdir = opendir(spath))) {
	    emtd_Broadcast(request, EMTD_LOGERR,
			   "Could not open directory:\n\t%s\n", spath);
	    return EMTD_OPENING_DEPOTDIR;
	}
	same_version = 0;
	oldtime = -1;
	collength = strlen(REQ_COLLECTION);
	while (NULL != (sdirent = readdir(sdir))) {
	    if ('.' == (char)(sdirent->d_name)[0]) continue;
	    if (NULL == (period = index(sdirent->d_name, '.'))) {
		if (collength != strlen(sdirent->d_name)) continue;
	    } else {
		if (collength != (period - sdirent->d_name)) continue;
	    }
	    if (!strncmp(REQ_COLLECTION, sdirent->d_name, collength)) {
		if ((newtime = period ? atoi(period+1) : 0) > oldtime) {
		    same_version = 0;
		    oldtime = newtime;
		    sprintf(fpath, "%s/%s", spath, sdirent->d_name);
		    if (NULL != (mount = statMount(fpath, &mnterr))) {
			if (!strcmp(mount, volume)) {
			    same_version = 1;
			}
		    } else {
			emtd_Broadcast(request, EMTD_LOGERR,
				       "Could not stat mount:\n\t%s\n", fpath);
			closedir(sdir);
			return EMTD_STATING_MOUNT;
		    }
		}
	    }
	}
	closedir(sdir);
	if (same_version) {
	    emtd_Broadcast(request, EMTD_LOGXTRA,
			   "%s already mounted with latest timestamp.\n",
			   volume);
	    return OK;
	} else {
	    FILE *db;

	    sprintf(fpath, "%s/%s", EMTD_DB_DIRECTORY, REQ_TREE);
	    IF_RTN(emtd_MakeDir(fpath, 666));
	    sprintf(fpath, "%s/%s/tsdb-%s-%s-%s", EMTD_DB_DIRECTORY,
		    REQ_TREE, REQ_TREE, REQ_ENVIRONMENT, system);
	    if (NULL == (db = fopen(fpath, "a"))) {
		fprintf(stderr, "emtd: error opening %s\n", fpath);
		return EMTD_OPEN_FILE_ERR;
	    }
	    fprintf(db, "%s.%ld %s\n", REQ_COLLECTION, timestamp, volume);
	    fclose(db);
	    sprintf (fpath, "%s/depot/%s.%ld", path, REQ_COLLECTION, timestamp);
	}
#else
	sprintf (fpath, "%s/depot/%s", path, REQ_COLLECTION);
#endif				/* USE_TIMESTAMPS */
    }
#ifndef USE_TIMESTAMPS
    if (unlink (fpath)) {
	sprintf (message, "emtd: old mount %s could not be removed.", fpath);
	LogN (message);
	return EMTD_COULD_NOT_MAKE_MOUNT;
    }
#endif				/* USE_TIMESTAMPS */
    sprintf (buffer, "#%s#", volume);
    if (symlink (buffer, fpath)) {
	if (EEXIST == errno) {
	    Log("%s already exists.\n", fpath);
	} else {
	    emtd_Broadcast(request, EMTD_LOGERR,
			   "Couldn't mount %s (%ld) at:\n\t%s.\n",
			   volume, errno, fpath);
	    return EMTD_COULD_NOT_MAKE_MOUNT;
	}
    } else {
	emtd_Broadcast(request, EMTD_LOGXTRA, "%s mounted at:\n\t%s\n",
		       volume, fpath);
    }
    if (flags & SET_VOLUME_ACL) {
	return emtd_VolumeSetACL(request, path, flags, errstr);
    }
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumeSetACL
 *
 * Description:
 *      Sets the ACL (Access Control List) for the volume found at the
 *      location specified by the path and type of volume.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      path - The base path where the ACL will start to be set.
 *      flags - The flags indicating the type of volume at the end of the path.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 15:14:43 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeSetACL (request, path, flags, errstr)
        comreq_t request;
char   *path;
long    flags;
char  **errstr;
{
    char    fpath[MAXPATHLEN],
            maintainers[MAXBUFSIZE],
            visitors[MAXBUFSIZE];
    admint_objectPtr_t aclobj, tmpobj1, tmpobj2;
    int restricted = 0;
    long    code;
    long    timestamp;
    struct timeval  tp;
    int i;
    llist_t c_acl;
    ADMVARS;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
    sprintf (fpath, "%s/%s", path, REQ_COLLECTION);
    if (flags & (SRC_VOL | OBJ_VOL | DEST_VOL)) {
	strcat (fpath, "/");
	strcat (fpath, REQ_VERSION);
	if (flags & COMMON_VOL) {
	    strcat (fpath, "/");
	    strcat (fpath, "common");
	}
    }
    sprintf (maintainers, "sysmaint:%s.commanders", REQ_COLLECTION);
    for (i = 0; i < MAXBUFSIZE && '\0' != maintainers[i]; i++)
      maintainers[i] = tolower(maintainers[i]);
    sprintf (visitors, "sysmaint:%s.visitors", REQ_COLLECTION);
    for (i = 0; i < MAXBUFSIZE && '\0' != visitors[i]; i++)
      visitors[i] = tolower(visitors[i]);

    IF_RTN (admlib_obj_ConvertToObj
	    (&aclobj, admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "emt-group-exists",
	     admlib_obj_FromString, visitors,
	     ADMLIB_OBJ_END));
    code = emtd_admEvaluate(request, aclobj, &value, &admcode, &admerr, errstr);

    switch (code) {
    case PREXIST:
	break;
    case OK:
	break;
    default:
	return code;
    }
    IF_RTN (admlib_obj_ConvertFromObj
	    (value, admlib_obj_ToBoolean, &restricted));
    
    if (flags & LOCK_VOLUME) {
	IF_RTN(admlib_obj_ConvertToObj
	       (&tmpobj1, admlib_obj_FromList,
		admlib_obj_FromList,
		admlib_obj_FromString, request->tree->owner,
		admlib_obj_FromString, "rla",
		ADMLIB_OBJ_END,
		admlib_obj_FromList,
		admlib_obj_FromString, maintainers,
		admlib_obj_FromString, "rl",
		ADMLIB_OBJ_END,	ADMLIB_OBJ_END));
    } else {
	IF_RTN(admlib_obj_ConvertToObj
	       (&tmpobj1, admlib_obj_FromList,
		admlib_obj_FromList,
		admlib_obj_FromString, request->tree->owner,
		admlib_obj_FromString, "all",
		ADMLIB_OBJ_END,
		admlib_obj_FromList,
		admlib_obj_FromString, maintainers,
		admlib_obj_FromString, "all",
		ADMLIB_OBJ_END,	ADMLIB_OBJ_END));
    }
    if (restricted && (flags & (SRC_VOL | OBJ_VOL))) {
	IF_RTN(admlib_obj_ConvertToObj
	       (&tmpobj2, admlib_obj_FromList,
		admlib_obj_FromString, visitors,
		admlib_obj_FromString, "rl",
		ADMLIB_OBJ_END));
	IF_RTN(admlib_obj_ConvertToObj
	       (&aclobj, admlib_obj_FromPair,
		admlib_obj_FromObj, tmpobj2,
		admlib_obj_FromObj, tmpobj1,
		ADMLIB_OBJ_END));
	tmpobj1 = aclobj;
	aclobj = NULL;
	for (c_acl = request->tree->proprietary; NULL != c_acl; c_acl = c_acl->next) {
	    IF_RTN(admlib_obj_ConvertToObj
		   (&tmpobj2, admlib_obj_FromList,
		    admlib_obj_FromString, c_acl->buffA,
		    admlib_obj_FromString, c_acl->buffB,
		    ADMLIB_OBJ_END));
	    IF_RTN(admlib_obj_ConvertToObj
		   (&aclobj, admlib_obj_FromPair,
		    admlib_obj_FromObj, tmpobj2,
		    admlib_obj_FromObj, tmpobj1,
		    ADMLIB_OBJ_END));
	}
    } else {
	for (c_acl = request->tree->general; NULL != c_acl; c_acl = c_acl->next) {
	    if (aclobj == tmpobj1) aclobj = NULL;
	    IF_RTN(admlib_obj_ConvertToObj
		   (&tmpobj2, admlib_obj_FromList,
		    admlib_obj_FromString, c_acl->buffA,
		    admlib_obj_FromString, c_acl->buffB,
		    ADMLIB_OBJ_END));
	    IF_RTN(admlib_obj_ConvertToObj
		   (&aclobj, admlib_obj_FromPair,
		    admlib_obj_FromObj, tmpobj2,
		    admlib_obj_FromObj, tmpobj1,
		    ADMLIB_OBJ_END));
	    tmpobj1 = aclobj;
	}
    }
    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "emt-set-protection",
	     admlib_obj_FromString, fpath,
	     admlib_obj_FromList,
	     admlib_obj_FromIdentifier, "quote",
	     admlib_obj_FromList,
	     admlib_obj_FromObj, aclobj,
	     admlib_obj_FromEmpty,
	     ADMLIB_OBJ_END,
	     ADMLIB_OBJ_END,
	     admlib_obj_FromNumber, EMTD_WORK_ID,
	     ADMLIB_OBJ_END));
    ADM_INTEVAL;
    emtd_Broadcast(request, EMTD_LOGXTRA, "ACL set on: %s\n", fpath);
    return OK;
}


/***************************************************************************
 * [exported] emtd_VolumeUnmount
 *
 * Description:
 *      Removes the mountpoint at the end of the path, only if the volume
 *      mountes there no longer exists in the VLDB.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume at the mountpoint.
 *      path - The base path for the mountpoint.
 *      flags - Flags describing what type of volume is at the mountpoint.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 15:18:56 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeUnmount (request, volume, system, path, flags, errstr)
        comreq_t request;
char   *volume;
char   *system;
char   *path;
long    flags;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
            fpath[MAXBUFSIZE],
           *server,
           *partition;
    long    volid,
            sites;
    int     i;
    register long   code;

    CheckAuth ();
    IF_RTN(emtd_Yield(request));
    if (flags & COMMON_VOL) {
	sprintf (fpath, "%s/%s/%s/common", path, REQ_COLLECTION, REQ_VERSION);
    } else {
	sprintf (fpath, "%s/%s/%s", path, REQ_COLLECTION, REQ_VERSION);
    }
    code = emt_GetVolumeInfo (volume, &server, &partition, &volid, &sites);
    if (VL_NOENT != code) {
	emtd_Broadcast(request, EMTD_LOGERR,
		       "%s still exists, so it was not unmounted:\n", volume);
	emtd_Broadcast(request, EMTD_LOGERR, "\t%s\n", fpath);
	return OK;
    }
    code = emtd_RemoveMount(fpath);
    switch (code) {
    case EINVAL:
	emtd_Broadcast(request, EMTD_LOGERR, "Not a mountpoint: %s\n", fpath);
	break;
    case ENOENT:
	break;
    case 0:
	emtd_Broadcast(request, EMTD_LOGXTRA, "Mount removed: %s\n", fpath);
	break;
    defatult:
	emtd_Broadcast(request, EMTD_LOGERR,
		       "Could not remove mount (%ld):\n\t%s\n", code, fpath);
	break;
    }
    return OK;
}

/***************************************************************************
 * [exported] emtd_VolumeSetQuota
 *
 * Description:
 *      Sets the quota on a given volume.
 *
 * Arguments:
 *      request - The request structure associated with caller of this proc.
 *      volume - The name of the volume that is having its quota set.
 *      quota - The value that the volume's quota will be set to.
 *      errstr - Where an error string could be returned.
 *
 * Environment:
 *
 * History:
 *      Thu Oct  1 15:19:31 1992 - created by David Allen Markley
 *
 ***************************************************************************/
long    emtd_VolumeSetQuota (request, volume, quota, errstr)
        comreq_t request;
char   *volume;
long    quota;
char  **errstr;
{
    char    buffer[MAXBUFSIZE],
           *server,
           *partition;
    long    code,
            volid,
            sites;
    int     used,
            old_quota;
    ADMVARS;

    if (!(REQ_AUTH & ~EMTAUTH_MAINTAINER)) {
	code = emtd_QuotaRestricted(request);
	if (code < 0) {
	    emtd_Broadcast(request, EMTD_LOGERR,
			   "Not authorized to set quota.\n");
	    return OK;
	} else if (code > 0 && quota > code) {
	    emtd_Broadcast(request, EMTD_LOGERR,
			   "Only authorized to set quota up to %ldk bytes.\n",
			   code);
	    emtd_Broadcast
	      (request, EMTD_LOGERR,
	       "Setting quota to limit, instead of the requested %ld.\n",
	       quota);
	    quota = code;
	}
    }
    CheckAuth ();
    IF_RTN(emtd_Yield(request));
    IF_RTN (emt_GetVolumeInfo (volume, &server, &partition, &volid, &sites));
    IF_RTN (emt_GetMoreVolumeInfo
	    (server, partition, volid, &used, &old_quota));
    if ((quota < used) && (0 != quota)) {
	emtd_Broadcast(request, EMTD_LOGERR,
		       "Not allowed to set quota below the quota in use.\n");
	emtd_Broadcast
	  (request, EMTD_LOGERR,
	   "Setting quota to the used amount of %ld, instead of %ld.\n",
	   used, quota);
	quota = used;
    }

    IF_RTN (admlib_obj_ConvertToObj
	    (&obj, admlib_obj_FromList,
		admlib_obj_FromIdentifier, "emt-set-volume-status",
		admlib_obj_FromString, server,
		admlib_obj_FromNumber, volid,
		admlib_obj_FromEmpty,
		admlib_obj_FromNumber, quota,
		admlib_obj_FromEmpty,
		admlib_obj_FromEmpty,
		admlib_obj_FromEmpty,
		ADMLIB_OBJ_END));
    ADM_INTEVAL;
    emtd_Broadcast(request, EMTD_LOGXTRA, "Quota for %s set to %ld.\n",
		   volume, quota);
    return OK;
}
