/*
 *  procMisc.c --
 *
 *	Misc. routines to get and set process state.
 *
 * Copyright 1986 Regents of the University of California
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/proc/procMisc.c,v 9.26 92/09/27 15:50:49 shirriff Exp $ SPRITE (Berkeley)";
#endif /* not lint */

#include <sprite.h>
#include <proc.h>
#include <status.h>
#include <sync.h>
#include <sched.h>
#include <sig.h>
#include <stdlib.h>
#include <list.h>
#include <string.h>
#include <procInt.h>
#include <rpc.h>
#include <dbg.h>
#include <vm.h>
#include <ctype.h>
#include <fscache.h>
#include <fsutil.h>
#include <rpcClient.h>
#include <rpcServer.h>
#include <procServer.h>
#include <fsrmt.h>
#include <lfsTypes.h>
#include <fsconsist.h>
#include <bstring.h>
#include <stdio.h>

#define min(a,b) ((a) < (b) ? (a) : (b))
/*
 * Procedures internal to this file
 */

static ReturnStatus 	GetRemotePCB _ARGS_((int hostID, Proc_PID pid,
				Proc_PCBInfo *pcbPtr, char *argString));
static void		FillPCBInfo _ARGS_((Proc_ControlBlock *pcbPtr,
				Proc_PCBInfo *statusInfoPtr));	

/*
 *----------------------------------------------------------------------
 *
 * Proc_Init --
 *
 *	Called during startup to initialize data structures.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Process table initialized, debug list initialized, locks initialized.
 *
 *----------------------------------------------------------------------
 */

void
Proc_Init()
{
    ProcInitTable();
    ProcDebugInit();
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_GetPCBInfo --
 *
 *	Returns the process control blocks for the specified processes
 *	on the specified host.  If firstPid is equal to PROC_MY_PID
 *	and the hostID is PROC_MY_HOSTID, then the PCB for the current
 *	process is returned.  Otherwise PCBs for all processes in the
 *	range firstPid to lastPid on host hostID are returned.  Only
 *	the index portions of the processIDs for firstPid and lastPid
 *	are relevant. 
 *
 * Results:
 *	SYS_INVALID_ARG - 	firstPid was < 0, firstPid > lastPid
 *	SYS_ARG_NOACCESS - 	The buffers to store the pcbs in were not
 *				accessible.
 *      *trueNumBuffers is set to be the actual number of
 *	PCBs returned which can be less than the number requested if
 *	lastPid - firstPid is greater than the maximum PCBs available.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

/*
 * Macro to fix up ticks for a process control block.
 */
#define TICKS_TO_TIME(pcbEntry) \
	Timer_TicksToTime(pcbEntry.kernelCpuUsage.ticks,  \
			  &pcbEntry.kernelCpuUsage.time); \
	Timer_TicksToTime(pcbEntry.userCpuUsage.ticks,  \
			  &pcbEntry.userCpuUsage.time); \
	Timer_TicksToTime(pcbEntry.childKernelCpuUsage.ticks,  \
			  &pcbEntry.childKernelCpuUsage.time); \
	Timer_TicksToTime(pcbEntry.childUserCpuUsage.ticks,  \
			  &pcbEntry.childUserCpuUsage.time);


ReturnStatus
Proc_GetPCBInfo(firstPid, lastPid, hostID, infoSize, bufferPtr, 
		argsPtr, trueNumBuffersPtr)
    Proc_PID 		firstPid;	     /* First pid to get info for. */
    Proc_PID		lastPid;	     /* Last pid to get info for. */
    int			hostID;		     /* Host ID to get info for. */
    int			infoSize;   	     /* Size of structure */
    Address	 	bufferPtr;	     /* Pointer to buffers. */
    Proc_PCBArgString	*argsPtr;	     /* Pointer to argument strings. */
    int 		*trueNumBuffersPtr;  /* The actual number of buffers 
						used.*/
{
    register Proc_ControlBlock 	*procPtr = (Proc_ControlBlock *) NIL;
    int				i, j;
    char 			argString[PROC_PCB_ARG_LENGTH];
    Proc_ControlBlock		pcbEntry;
    Boolean			remote = FALSE;
    Proc_PID			processID = firstPid;
    ReturnStatus		status = SUCCESS;
    Proc_PCBInfo		statusInfo;
    int				bytesToCopy;


    if (firstPid != PROC_MY_PID) {
	firstPid &= PROC_INDEX_MASK;
	lastPid &= PROC_INDEX_MASK;
	if ((firstPid > lastPid) ||
	    ((firstPid == PROC_MY_PID) && hostID != PROC_MY_HOSTID)) {
	    return(GEN_INVALID_ARG);
	}
    }
    if (bufferPtr == USER_NIL) {
	return (SYS_ARG_NOACCESS);
    }
    if (hostID != PROC_MY_HOSTID &&
	(hostID <= 0 || hostID > NET_NUM_SPRITE_HOSTS)) {
	return(GEN_INVALID_ARG);
    }

    bytesToCopy = min(sizeof(Proc_PCBInfo), infoSize);
    /*
     * Determine whether to get process table entries for this machine.
     * Currently, the information for this machine is returned unless
     * another machine is explicitly specified; i.e., migrated processes
     * get information for their current machine rather than their home.
     */

    if (hostID == PROC_MY_HOSTID) {
#ifdef FORWARD_MIGRATED_GET_PCBS
	procPtr = Proc_GetCurrentProc();
	Proc_Lock(procPtr);
	if (procPtr->genFlags & PROC_FOREIGN) {
	    hostID = procPtr->peerHostID;
	    processID = procPtr->peerHostID;
	    remote = TRUE;
	} 
	Proc_Unlock(procPtr);
#endif /* FORWARD_MIGRATED_GET_PCBS */
    } else if (hostID != rpc_SpriteID) {
	remote = TRUE;
    }

    if (firstPid == PROC_MY_PID) {
	/*
	 *  Return PCB for the current process.
	 */
	procPtr = Proc_GetCurrentProc();
	if (!remote) {
	    bcopy((Address)procPtr, (Address)&pcbEntry,
		    sizeof (Proc_ControlBlock));
	    TICKS_TO_TIME(pcbEntry);
	    FillPCBInfo(&pcbEntry, &statusInfo);
	} else {
	    status = GetRemotePCB(hostID, processID, &statusInfo,
				       argString);
	    if (status != SUCCESS) {
		return(status);
	    }
	}
	if (Proc_ByteCopy(FALSE, bytesToCopy,
		(Address)&statusInfo, (Address) bufferPtr) != SUCCESS) {
	    return(SYS_ARG_NOACCESS);
	}
	if (argsPtr != (Proc_PCBArgString *) USER_NIL) {
	    if (!remote) {
		if (procPtr->argString != (Address) NIL) {
		    (void) strncpy(argString, procPtr->argString,
			    PROC_PCB_ARG_LENGTH - 1);
		    argString[PROC_PCB_ARG_LENGTH - 1] = '\0';
		} else {
		    argString[0] = '\0';
		}
	    }
	    if (Proc_ByteCopy(FALSE, PROC_PCB_ARG_LENGTH, argString,
			      (Address) argsPtr) != SUCCESS) {
		return(SYS_ARG_NOACCESS);
	    }
	}
    } else {
	
	/*
	 * Return PCB for all processes or enough to fill all of
	 * the buffers, whichever comes first.
	 */

	
	for (i = firstPid, j = 0; 
	     i <= lastPid; 
	     i++, j++, (Address) bufferPtr += infoSize) {
	    if (!remote) {
		if (i >= proc_MaxNumProcesses) {
		    break;
		}
		procPtr = Proc_GetPCB(i);
		if (procPtr == (Proc_ControlBlock *) NIL) {
		    panic("Proc_GetInfo: procPtr == NIL!\n");
		    status = FAILURE;
		    break;
		}
		bcopy((Address)procPtr, (Address)&pcbEntry,
			sizeof (Proc_ControlBlock));

		TICKS_TO_TIME(pcbEntry);
		FillPCBInfo(&pcbEntry, &statusInfo);
	    } else {
		status = GetRemotePCB(hostID, (Proc_PID) i, &statusInfo,
					   argString);
		if (status != SUCCESS) {
		    /*
		     * Break if we hit an error.  The typical error condition
		     * is to hit an invalid process ID, which happens since
		     * we don't know proc_MaxNumProcesses on the other
		     * machine.  Instead, we convert GEN_INVALID_ARG to
		     * SUCCESS and return what we found so far.
		     */
		    if (status == GEN_INVALID_ARG) {
			status = SUCCESS;
		    }
		    break;
		}
	    }
	    if (Proc_ByteCopy(FALSE, bytesToCopy,
		    (Address)&statusInfo, (Address) bufferPtr) != SUCCESS) {
		return(SYS_ARG_NOACCESS);
	    }
	    if (argsPtr != (Proc_PCBArgString *) USER_NIL) {
		if (!remote) {
		    if (procPtr->argString != (Address) NIL) {
			(void) strncpy(argString, procPtr->argString,
				       PROC_PCB_ARG_LENGTH - 1);
			argString[PROC_PCB_ARG_LENGTH - 1] = '\0';
		    } else {
			argString[0] = '\0';
		    }
		}
		if (Proc_ByteCopy(FALSE, PROC_PCB_ARG_LENGTH, argString,
				  (Address) &(argsPtr[j])) != SUCCESS) {
		    return(SYS_ARG_NOACCESS);
		}
	    }
	}

	if (trueNumBuffersPtr != USER_NIL) {
	    if (Proc_ByteCopy(FALSE, sizeof(j), (Address) &j, 
			    (Address) trueNumBuffersPtr) != SUCCESS) {
		return(SYS_ARG_NOACCESS);
	    }
	}
    }

    return(status);
}



/*
 * Define some constants used to distinguish RPC sub-commands.
 */
#define GET_PCB 1
#define GET_SEG_INFO 2

/*
 *----------------------------------------------------------------------
 *
 * GetRemotePCB --
 *
 *	Perform an RPC to get a process control block from another host.
 *
 * Results:
 *	The return status from the RPC is returned.  
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static ReturnStatus	
GetRemotePCB(hostID, pid, pcbPtr, argString)
    int		hostID;		/* Host to send RPC to. */
    Proc_PID	pid;		/* index of PCB to obtain. */
    Proc_PCBInfo *pcbPtr;	/* Place to return PCB data. */
    char	*argString;	/* Place to return argument string. */
{
    Rpc_Storage		storage;
    ReturnStatus 	status;
    int			request;

    request = GET_PCB;
    storage.requestParamPtr = (Address)&request;
    storage.requestParamSize = sizeof(request);
    storage.requestDataPtr = (Address)&pid;
    storage.requestDataSize = sizeof(Proc_PID);
    storage.replyParamPtr = (Address)pcbPtr;
    storage.replyParamSize = sizeof(Proc_PCBInfo);
    storage.replyDataPtr = (Address)argString;
    storage.replyDataSize = PROC_PCB_ARG_LENGTH;

    status = Rpc_Call(hostID, RPC_PROC_GETPCB, &storage);
    if (status == SUCCESS && storage.replyDataSize == 0) {
	argString[0] = '\0';
    }
    return(status);

}
/*
 *----------------------------------------------------------------------
 *
 * Proc_GetRemoteSegInfo --
 *
 *	Perform an RPC to get info for a VM segment control from another host.
 *
 * Results:
 *	The return status from the RPC is returned.  
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus	
Proc_GetRemoteSegInfo(hostID, segNum, segInfoPtr)
    int		hostID;		/* Host to send RPC to. */
    int		segNum;		/* index of segment to obtain. */
    Vm_SegmentInfo *segInfoPtr;	/* Place to return segment data. */
{
    Rpc_Storage		storage;
    ReturnStatus 	status;
    int			request;

    request = GET_SEG_INFO;
    storage.requestParamPtr = (Address)&request;
    storage.requestParamSize = sizeof(request);
    storage.requestDataPtr = (Address)&segNum;
    storage.requestDataSize = sizeof(int);
    storage.replyParamPtr = (Address)segInfoPtr;
    storage.replyParamSize = sizeof(Vm_SegmentInfo);
    storage.replyDataPtr = (Address)NIL;
    storage.replyDataSize = 0;

    status = Rpc_Call(hostID, RPC_PROC_GETPCB, &storage);
    return(status);

}

/*
 *----------------------------------------------------------------------
 *
 * FillPCBInfo --
 *
 *	Fills in a Proc_PCBInfo structure from the contents of a
 *	control block.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
FillPCBInfo(pcbPtr, statusInfoPtr)
    Proc_ControlBlock		*pcbPtr; 	/* Ptr to pcb to convert */
    Proc_PCBInfo		*statusInfoPtr; /* Structure to fill in */
{
    int 	i;

    statusInfoPtr->processor = pcbPtr->processor;
    statusInfoPtr->state = pcbPtr->state;
    statusInfoPtr->genFlags = pcbPtr->genFlags;
    statusInfoPtr->processID = pcbPtr->processID;
    statusInfoPtr->parentID = pcbPtr->parentID;
    statusInfoPtr->familyID = pcbPtr->familyID;
    statusInfoPtr->userID = pcbPtr->userID;
    statusInfoPtr->effectiveUserID = pcbPtr->effectiveUserID;
    statusInfoPtr->event = pcbPtr->event;
    statusInfoPtr->billingRate = pcbPtr->billingRate;
    statusInfoPtr->recentUsage = pcbPtr->recentUsage;
    statusInfoPtr->weightedUsage = pcbPtr->weightedUsage;
    statusInfoPtr->unweightedUsage = pcbPtr->unweightedUsage;
    statusInfoPtr->kernelCpuUsage = pcbPtr->kernelCpuUsage.time;
    statusInfoPtr->userCpuUsage = pcbPtr->userCpuUsage.time;
    statusInfoPtr->childKernelCpuUsage = pcbPtr->childKernelCpuUsage.time;
    statusInfoPtr->childUserCpuUsage = pcbPtr->childUserCpuUsage.time;
    statusInfoPtr->numQuantumEnds = pcbPtr->numQuantumEnds;
    statusInfoPtr->numWaitEvents = pcbPtr->numWaitEvents;
    statusInfoPtr->schedQuantumTicks = pcbPtr->schedQuantumTicks;
    for(i = 0; i < VM_NUM_SEGMENTS; i++) {
	if (pcbPtr->vmPtr != (Vm_ProcInfo *) NIL && 
	    pcbPtr->vmPtr->segPtrArray[i] != (Vm_Segment *) NIL) {
	    statusInfoPtr->vmSegments[i] = 
		(Vm_SegmentID) pcbPtr->vmPtr->segPtrArray[i]->segNum;
	} else {
	    statusInfoPtr->vmSegments[i] = (Vm_SegmentID) -1;
	}
    }
    statusInfoPtr->sigHoldMask = pcbPtr->sigHoldMask;
    statusInfoPtr->sigPendingMask = pcbPtr->sigPendingMask;
    for(i = 0; i < SIG_NUM_SIGNALS; i++) {
	statusInfoPtr->sigActions[i] = pcbPtr->sigActions[i];
    }
    statusInfoPtr->peerHostID = pcbPtr->peerHostID;
    statusInfoPtr->peerProcessID = pcbPtr->peerProcessID;
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_RpcGetPCB --
 *
 *	Stub to handle a remote request for a PCB or Vm_Segment.
 *
 * Results:
 *	Status of reply:
 *	GEN_INVALID_ARG - index into table of process control blocks is
 *			  invalid, or segment is invalid.
 *	SUCCESS 	- information is returned.
 *
 *	SUCCESS is passed to the caller on this machine.
 *
 * Side effects:
 *	Reply is sent.
 *
 *----------------------------------------------------------------------
 */
/*ARGSUSED*/
ReturnStatus	
Proc_RpcGetPCB(srvToken, clientID, command, storagePtr)
    ClientData 		 srvToken;	/* Handle on server process passed to
				 	 * Rpc_Reply */
    int 		 clientID;	/* Sprite ID of client host */
    int 		 command;	/* Command identifier */
    register Rpc_Storage *storagePtr;	/* The request fields refer to the 
					 * request buffers and also indicate 
					 * the exact amount of data in the 
					 * request buffers.  The reply fields 
					 * are initialized to NIL for the
				 	 * pointers and 0 for the lengths.  
					 * This can be passed to Rpc_Reply */
{
    ReturnStatus	status = SUCCESS;
    Proc_PID		*pidPtr;
    Rpc_ReplyMem	*replyMemPtr;
    Proc_PCBInfo	*pcbPtr;
    Proc_ControlBlock   *procPtr = (Proc_ControlBlock *) NIL;
    Proc_ControlBlock   pcb;
    int			*segNumPtr;
    Vm_SegmentInfo	*segInfoPtr;
    int			*requestPtr;

    requestPtr = (int *) storagePtr->requestParamPtr;
    if (*requestPtr == GET_SEG_INFO) {
	segNumPtr = (int *) storagePtr->requestDataPtr;
	segInfoPtr = (Vm_SegmentInfo *) malloc(sizeof (Vm_SegmentInfo));
	status = Vm_EncapSegInfo(*segNumPtr, segInfoPtr);
	storagePtr->replyParamPtr = (Address) segInfoPtr;
	storagePtr->replyParamSize = sizeof(Vm_SegmentInfo);
	goto done;
    } else if (*requestPtr == GET_PCB) {
	pidPtr = (Proc_PID *) storagePtr->requestDataPtr;
	if (*pidPtr >= proc_MaxNumProcesses) {
	    status = GEN_INVALID_ARG;
	} else {
	    procPtr = Proc_GetPCB(*pidPtr);
	    if (procPtr == (Proc_ControlBlock *) NIL) {
		panic("Proc_RpcGetPCB: found nil PCB!");
		status = FAILURE;
	    }
	}
	if (status != SUCCESS) {
	    Rpc_Reply(srvToken, status, storagePtr,
		      (int(*)())NIL, (ClientData)NIL);
	    return(SUCCESS);
	}

	bcopy((Address) procPtr, (Address) &pcb, sizeof (Proc_ControlBlock));
	TICKS_TO_TIME(pcb);
	pcbPtr = (Proc_PCBInfo *) malloc(sizeof (Proc_PCBInfo));
	storagePtr->replyParamPtr = (Address) pcbPtr;
	storagePtr->replyParamSize = sizeof(Proc_PCBInfo);
	FillPCBInfo(&pcb, pcbPtr);

	if (procPtr->argString != (Address) NIL) {
	    storagePtr->replyDataSize = strlen(procPtr->argString) + 1;
	    storagePtr->replyDataPtr = (Address) malloc(storagePtr->replyDataSize);
	    (void) strcpy(storagePtr->replyDataPtr, procPtr->argString);
	} else {
	    storagePtr->replyDataSize = 0;
	    storagePtr->replyDataPtr = (Address) NIL;
	}
    }
    done:
    replyMemPtr = (Rpc_ReplyMem *) malloc(sizeof(Rpc_ReplyMem));
    replyMemPtr->paramPtr = storagePtr->replyParamPtr;
    replyMemPtr->dataPtr = storagePtr->replyDataPtr;

    Rpc_Reply(srvToken, status, storagePtr, Rpc_FreeMem,
	    (ClientData) replyMemPtr);
    return(SUCCESS);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_GetResUsage --
 *
 *	Returns the resource usage for a process.
 *
 * Results:
 *	SYS_INVALID_ARG - 	buffer address was invalid.
 *	PROC_INVALID_PID - 	The pid was out-of-range or specified a
 *				non-existent process.
 *	SYS_ARG_NOACCESS - 	The buffers to store the pcbs in were not
 *				accessible.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Proc_GetResUsage(pid, bufferPtr)
    Proc_PID 		pid;
    Proc_ResUsage 	*bufferPtr;	     
{
    register Proc_ControlBlock 	*procPtr;
    Proc_ResUsage 		resUsage;	     
    ReturnStatus		status = SUCCESS;

    if (pid == PROC_MY_PID) {
	procPtr = Proc_GetEffectiveProc();
	if (procPtr == (Proc_ControlBlock *) NIL) {
	    panic("Proc_GetResUsage: procPtr == NIL\n");
	} 
	Proc_Lock(procPtr);
    } else {
	procPtr = Proc_LockPID(pid);
        if (procPtr == (Proc_ControlBlock *) NIL) {
            return (PROC_INVALID_PID);
        }
    }

    /*
     *  Copy the information to the out parameters.
     */

    if (bufferPtr == USER_NIL) {
	status = SYS_INVALID_ARG;
    } else {
	Timer_TicksToTime(procPtr->kernelCpuUsage.ticks,
			  &resUsage.kernelCpuUsage);
	Timer_TicksToTime(procPtr->userCpuUsage.ticks, &resUsage.userCpuUsage);
	Timer_TicksToTime(procPtr->childKernelCpuUsage.ticks, 
				&resUsage.childKernelCpuUsage);
	Timer_TicksToTime(procPtr->childUserCpuUsage.ticks,
				&resUsage.childUserCpuUsage);
	resUsage.numQuantumEnds = procPtr->numQuantumEnds;
	resUsage.numWaitEvents 	= procPtr->numWaitEvents;

	if (Proc_ByteCopy(FALSE, sizeof(Proc_ResUsage), 
	    (Address) &resUsage, (Address) bufferPtr) != SUCCESS){
	    status = SYS_ARG_NOACCESS;
	}
    }
    Proc_Unlock(procPtr);
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_GetPriority --
 *
 *	Returns the priority of a process.
 *
 * Results:
 *	SYS_INVALID_ARG - 	priorityPtr address was invalid.
 *	PROC_INVALID_PID - 	The pid was out-of-range or specified a
 *				non-existent process.
 *	SYS_ARG_NOACCESS - 	The buffer to store the priority was not
 *				accessible.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Proc_GetPriority(pid, priorityPtr)
    Proc_PID pid;	/* ID of process whose priority is to be returned. */
    int *priorityPtr;	/* Priority returned by Proc_GetPriority. */
{
    register Proc_ControlBlock 	*procPtr;
    ReturnStatus		status = SUCCESS;

    if (pid == PROC_MY_PID) {
	procPtr = Proc_GetEffectiveProc();
	if (procPtr == (Proc_ControlBlock *) NIL) {
	    panic("Proc_GetPriority: procPtr == NIL\n");
	} 
	Proc_Lock(procPtr);
    } else {
	procPtr = Proc_LockPID(pid);
        if (procPtr == (Proc_ControlBlock *) NIL) {
            return (PROC_INVALID_PID);
        }
    }

    /*
     *  Copy the information to the out parameter.
     */

    if (priorityPtr == USER_NIL) {
	status = SYS_INVALID_ARG;
    } else {
	if (Proc_ByteCopy(FALSE, sizeof(int),
 	        (Address) &(procPtr->billingRate),
	        (Address) priorityPtr) != SUCCESS) {
	    status = SYS_ARG_NOACCESS;
	}
    }
    Proc_Unlock(procPtr);
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_SetPriority --
 *
 *	Sets the priority for a process.
 *
 * Results:
 *	PROC_INVALID_PID - 	The pid was out-of-range or specified a
 *				non-existent process.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Proc_SetPriority(pid, priority, useFamily)
    Proc_PID 	pid;		/* ID of process whose priority is to be set. */
    int 	priority;	/* New scheduling priority for pid. */
    Boolean 	useFamily;	/* If TRUE, use pid as the head of a process 
				 * family, and set the priority of every 
				 * process in the family. */
{
    register Proc_ControlBlock 	*procPtr;
    register Proc_PCBLink	*procLinkPtr;
    List_Links			*familyList;
    int				userID;
    ReturnStatus		status;

    if (priority > PROC_MAX_PRIORITY) {
	priority = PROC_MAX_PRIORITY;
    } else if (priority < PROC_MIN_PRIORITY) {
	priority = PROC_MIN_PRIORITY;
    }

    if (useFamily) {
	/*
	 * Set priorities of processes in family.
	 */
	status = Proc_LockFamily((int) pid, &familyList, &userID);
	if (status != SUCCESS) {
	    return(status);
	}
	if (!Proc_HasPermission(userID)) {
	    Proc_UnlockFamily((int) pid);
	    return(PROC_UID_MISMATCH);
	}
	LIST_FORALL(familyList, (List_Links *) procLinkPtr) {
	    procPtr = procLinkPtr->procPtr;
	    Proc_Lock(procPtr);
	    procPtr->billingRate = priority;
	    Proc_Unlock(procPtr);
	}
	Proc_UnlockFamily((int) pid);
    } else {
	/*
	 * Set the individual process's priority.
	 */
	if (pid == PROC_MY_PID) {
	    procPtr = Proc_GetEffectiveProc();
	    Proc_Lock(procPtr);
	} else {
	    procPtr = Proc_LockPID(pid);
	    if (procPtr == (Proc_ControlBlock *) NIL) {
		return (PROC_INVALID_PID);
	    }
	    if (!Proc_HasPermission(procPtr->effectiveUserID)) {
		Proc_Unlock(procPtr);
		return(PROC_UID_MISMATCH);
	    }
	}
	procPtr->billingRate = priority;
	Proc_Unlock(procPtr);
    }

    return(SUCCESS);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_Profile --
 *
 *	Starts profiling the memory accesses of the current process.
 *
 * Results:
 *	SUCCESS		-	always returned for now.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

/*ARGSUSED*/
ReturnStatus
Proc_Profile(shiftSize, lowPC, highPC, interval, counterArray)
    int shiftSize;	/* # of bits to shift the PC to the right. */
    int lowPC;		/* The lowest PC to profile. */
    int highPC;		/* The highest PC to profile. */
    Time interval;	/* The time interval at which the PC is sampled. */
    int counterArray[];	/* Counters used to count instruction executions. */
{
    return(SUCCESS);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_Dump --
 *
 *	Prints out an abbreviated proc table for debugging purposes.
 *
 * Results:
 *	SUCCESS.
 *
 * Side effects:
 *	Prints stuff to screen.
 *
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Proc_Dump()
{
    int i;
    Proc_ControlBlock *pcbPtr;

    printf("\n%8s %5s %10s %10s %8s %8s   %s\n",
	"ID", "wtd", "user", "kernel", "event", "state", "name");
    
    for (i = 0; i < proc_MaxNumProcesses; i++) {
	pcbPtr = proc_PCBTable[i];
	if (pcbPtr->state != PROC_UNUSED) {
	    Proc_DumpPCB(pcbPtr);
	}
    }
    return(SUCCESS);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_DumpPCB --
 *
 *	Prints out the contents of a PCB for debugging purposes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Prints stuff to the screen.
 *
 *----------------------------------------------------------------------
 */

void
Proc_DumpPCB(procPtr)
    Proc_ControlBlock *procPtr;
{

    Time	kernelTime, userTime;

#define DEBUG_INDEX	0x9

    static char *states[] = {
	"unused",
	"running",
	"ready",
	"waiting",
	"exiting",
	"dead",
	"migrated",
	"new",
	"suspended",
	"debug",
    };
    Proc_State state;

    state = procPtr->state;
    switch (state) {
	case PROC_UNUSED:
	case PROC_RUNNING:
	case PROC_READY:
	case PROC_WAITING:
	case PROC_EXITING:
	case PROC_DEAD:
	case PROC_MIGRATED:
	case PROC_NEW:
	    break;
	case PROC_SUSPENDED:
	    /* If process is suspended for debugging print "debug" for its
	     * state.
	     */
	    if (procPtr->genFlags & (PROC_DEBUGGED | PROC_ON_DEBUG_LIST)) {
		state = (Proc_State)DEBUG_INDEX;
	    }
	    break;
	default:
	    printf("Warning: Proc_DumpPCB: process %x has invalid process state: %x.\n",
		   procPtr->processID, state);
	    return;
    }
    /*
     * A header describing the fields has already been printed.
     */
    Timer_TicksToTime(procPtr->userCpuUsage.ticks, &userTime);
    Timer_TicksToTime(procPtr->kernelCpuUsage.ticks, &kernelTime);
    printf("%8x %5d [%1d,%6d] [%1d,%6d] %8x %8s",
	       procPtr->processID, 
	       procPtr->weightedUsage, 
	       userTime.seconds,
	       userTime.microseconds,
	       kernelTime.seconds, 
	       kernelTime.microseconds,
	       procPtr->event,
	       states[(int) state]);
    if (procPtr->argString != (Address) NIL) {
	char cmd[30];
	char *space;

	(void) strncpy(cmd, procPtr->argString, 30);
	space = strchr(cmd, ' ');
	if (space != (char *) NULL) {
	    *space = '\0';
	}
	printf(" %s\n", cmd);
    } else {
	printf("\n");
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Proc_KillAllProcesses --
 *
 *	Send the kill signal to all processes in the proc table except for
 *	the caller.  If userProcsOnly is TRUE only send signals to user 
 *	processes.
 *
 * Results:
 *	The number of runnable and waiting processes.
 *
 * Side effects:
 *	The kill signal bit is set for all processes.
 *
 *----------------------------------------------------------------------
 */
int
Proc_KillAllProcesses(userProcsOnly)
    Boolean	userProcsOnly;	/* TRUE if only kill user processes. */
{
    register int i;
    register Proc_ControlBlock	*pcbPtr;
    Proc_ControlBlock		*curProcPtr;
    int				alive = 0;

    curProcPtr = Proc_GetActualProc();

    for (i = 0; i < proc_MaxNumProcesses; i++) {
	pcbPtr = proc_PCBTable[i];
	if (pcbPtr == curProcPtr || pcbPtr->state == PROC_UNUSED ||
	    (userProcsOnly && !(pcbPtr->genFlags & PROC_USER))) {
	    continue;
	}
	Proc_Lock(pcbPtr);
	if (pcbPtr->state == PROC_RUNNING ||
		pcbPtr->state == PROC_READY ||
		pcbPtr->state == PROC_WAITING ||
		pcbPtr->state == PROC_MIGRATED) {
	    alive++;
	    (void) Sig_SendProc(pcbPtr, SIG_KILL, 0, (Address)0);
	}
	Proc_Unlock(pcbPtr);
    }

    return(alive);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_WakeupAllProcesses --
 *
 *	Wakup all waiting processes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All waiting processes are awakened.
 *
 *----------------------------------------------------------------------
 */
void
Proc_WakeupAllProcesses()
{
    register int		i;
    register Proc_ControlBlock	*pcbPtr;
    Proc_ControlBlock		*curProcPtr;

    curProcPtr = Proc_GetActualProc();

    for (i = 0; i < proc_MaxNumProcesses; i++) {
	pcbPtr = proc_PCBTable[i];
	if (pcbPtr == curProcPtr) {
	    continue;
	}
	Proc_Lock(pcbPtr);
	if (pcbPtr->state != PROC_UNUSED) {
	    Sync_WakeWaitingProcess(pcbPtr);
	}
	Proc_Unlock(pcbPtr);
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_HasPermission --
 *
 *      See if the current process has permission to perform an operation on
 *      a process with the given user id.
 *
 * Results:
 *      TRUE if the current process has the same effective user id
 *      as the given user id or the current process is super user.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Boolean
Proc_HasPermission(userID)
    int         userID;
{
    Proc_ControlBlock   *procPtr;

    procPtr = Proc_GetEffectiveProc();
    return(procPtr->effectiveUserID == userID ||
           procPtr->effectiveUserID == PROC_SUPER_USER_ID);
}



/*
 *----------------------------------------------------------------------
 *
 * Proc_DoForEveryProc --
 *
 *	For every process in the process table, apply *booleanFuncPtr to it.
 *	If that returns TRUE, apply *actionFuncPtr to it.  This is done by
 *	passing booleanFuncPtr to a monitored routine and having it
 *	return an array of qualifying processes.  There is a bit of a race
 *	condition if something happens to any of those processes after the
 *	list is returned, but in that case the process is ignored and the next
 *	one is processed.
 *
 *	IgnoreStatus indicates whether the routine should abort if
 *	a non-SUCCESS status is returned by *actionFuncPtr.
 *
 * Results:
 *	If anything "unexpected" happens, FAILURE will be returned, but in
 *	general SUCCESS is returned.  If numMatchedPtr is non-NIL, then
 *	the number of processes matched is returned in *numMatchedPtr.
 *
 * Side effects:
 *	The process table is locked temporarily.  Otherwise, dependent on the
 *	call-back procedures.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Proc_DoForEveryProc(booleanFuncPtr, actionFuncPtr, ignoreStatus, numMatchedPtr)
    Boolean (*booleanFuncPtr) _ARGS_((Proc_ControlBlock *pcbPtr));
					/* function to match */
    ReturnStatus (*actionFuncPtr)_ARGS_((Proc_PID pid));	
					/* function to invoke on matches */
    Boolean ignoreStatus;		/* do not abort if bad ReturnStatus  */
    int *numMatchedPtr;			/* number of matches in table, or NIL */
{
    ReturnStatus status = SUCCESS;
    Proc_PID *pidArray;
    int max;
    int i;
    int numMatched;

    max = proc_MaxNumProcesses;

    pidArray = (Proc_PID *) malloc(sizeof(Proc_PID) * max);
    numMatched = ProcTableMatch(max, booleanFuncPtr, pidArray);
    for (i = 0; i < numMatched; i++) {
	status = (*actionFuncPtr)(pidArray[i]);
	if ((!ignoreStatus) && (status != SUCCESS)) {
	    break;
	}
    }
    free((Address) pidArray);
    if (numMatchedPtr != (int *) NIL) {
	*numMatchedPtr = numMatched;
    }
    if (ignoreStatus) {
	return(SUCCESS);
    }
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Proc_SetServerPriority --
 *
 *	Changes the priority of a server process to the non-interruptable
 *	value. The pid is assumed to be valid.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The process's priority is changed.
 *
 *----------------------------------------------------------------------
 */

void
Proc_SetServerPriority(pid)
    Proc_PID	pid;
{
    Proc_GetPCB(pid)->billingRate = PROC_NO_INTR_PRIORITY;
}



/*
 *----------------------------------------------------------------------
 *
 * Proc_GetHostIDs --
 *
 *	Returns the sprite IDs corresponding to the machines on which
 *	the current process is effectively executing and on which
 *	it is physically executing.  These hosts are called the virtualHost
 *	and physicalHost, respectively.  For an unmigrated process, these
 *	two are identical.
 *
 * Results:
 *	SUCCESS 		The call was successful.
 *	SYS_ARG_NOACCESS	The user arguments were not accessible.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Proc_GetHostIDs(virtualHostPtr, physicalHostPtr)
    int	*virtualHostPtr;   	/* Buffer to hold virtual host ID. */
    int	*physicalHostPtr;   	/* Buffer to hold physical host ID. */
{

    Proc_ControlBlock *procPtr;
    int host;

    if (physicalHostPtr != (int *) USER_NIL) {

	if (Vm_CopyOut(sizeof(int), (Address) &rpc_SpriteID, 
				(Address) physicalHostPtr) != SUCCESS) {
	    return(SYS_ARG_NOACCESS);
	}
    }


    if (virtualHostPtr != (int *) USER_NIL) {
	procPtr = Proc_GetCurrentProc();
	Proc_Lock(procPtr);
	host = procPtr->peerHostID;
	Proc_Unlock(procPtr);
	if (host == NIL) {
	    host = rpc_SpriteID;
	}
	if (Vm_CopyOut(sizeof(int), (Address) &host, 
				(Address) virtualHostPtr) != SUCCESS) {
	    return(SYS_ARG_NOACCESS);
	}
    }

    return(SUCCESS);
}

/*
 *----------------------------------------------------------------------
 *
 * Proc_PushLockStack --
 *
 *	Pushes the given lock type on the lock stack for the process.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Stuff is printed if the stack overflows.
 *
 *----------------------------------------------------------------------
 */

/*ARGSUSED*/
void
Proc_PushLockStack(pcbPtr, type, lockPtr)
    Proc_ControlBlock		*pcbPtr;	/* ptr to pcb to modify */
    int				type;		/* type of lock */
    Address			lockPtr;	/* ptr to lock */

{
#ifdef LOCKDEP
    static Boolean	firstOverflow = TRUE;

    /*
     *  Modifying the lock stack of a process has to be an atomic operation,
     *  but we don't want to use a lock to do this, since this is part of
     *  the code used when locking or unlocking. Using a lock would lead
     *  to a circularity and probably a deadlock. All we really need to
     *  prevent is an interrupt handler from grabbing a lock while we're
     *  modifying the lock stack. A process only modifies its own
     *  lock stack, so turning off interrupts should be good enough.
     */
    DISABLE_INTR();
    if (pcbPtr->lockStackSize >= PROC_LOCKSTACK_SIZE) {
	if (firstOverflow) {
	    printf("Proc_PushLockStack: stack overflow in pcb 0x%x.\n",pcbPtr);
	    firstOverflow = FALSE;
	}
	goto exit;
    }
    if (pcbPtr->lockStackSize < 0 ) {
	printf("Proc_PushLockStack: stack underflow (%d) in pcb 0x%x.\n",
	       pcbPtr->lockStackSize, pcbPtr);
        goto exit;
    }
    pcbPtr->lockStack[pcbPtr->lockStackSize].type = type;
    pcbPtr->lockStack[pcbPtr->lockStackSize].lockPtr = lockPtr;
    pcbPtr->lockStackSize++;
exit:
    ENABLE_INTR();
#endif
}

/*
 *----------------------------------------------------------------------
 *
 * Proc_RemoveFromLockStack --
 *
 *	Removes the given lock from the stack if it is there.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

/*ARGSUSED*/
void
Proc_RemoveFromLockStack(pcbPtr, lockPtr)
    Proc_ControlBlock		*pcbPtr;	/* ptr to pcb to modify */
    Address			lockPtr;	/* ptr to lock */
{
#ifdef LOCKDEP
    int 	i;
    int		stackTop;
    Boolean 	found = FALSE;

    DISABLE_INTR();
    if (pcbPtr->lockStackSize < 0) {
	ENABLE_INTR();
	panic("Lock stack underflow (1).\n");
	goto exit;
    }
    if (pcbPtr->lockStackSize == 0) {
	goto exit;
    }
    stackTop = pcbPtr->lockStackSize - 1;
    for (i = pcbPtr->lockStackSize - 1; i >= 0; i--) {
	if (pcbPtr->lockStack[i].lockPtr == lockPtr) {
	    pcbPtr->lockStack[i].lockPtr = (Address) NIL;
	    pcbPtr->lockStack[i].type = -1;
	    found = TRUE;
	    break;
	}
    }
    if (!found) {
	goto exit;
    }
    for (i = stackTop; i >= 0; i--) {
	if (pcbPtr->lockStack[i].lockPtr != (Address) NIL) {
	    break;
	}
    }
    pcbPtr->lockStackSize = i + 1;
    if (pcbPtr->lockStackSize < 0) {
	printf("lockStackSize %d\n",pcbPtr->lockStackSize);
    }
exit:
    ENABLE_INTR();
#endif
}

#ifndef LOCKREG
#ifndef CLEAN_LOCK
#ifndef CLEAN
/*
 *----------------------------------------------------------------------
 *
 * Proc_KDump --
 *
 *	Prints out an (kluged) proc table with state information.
 *
 *	This routine uses several macros to analyse the event data structure:
 *	ISADDR(x) tests if x is a valid address.
 *	ISSTR(x) tests if x is a pointer to a valid string.
 *	ISALIGN(x) tests if x is an aligned address.
 *	ISBOOL(x) tests if x is a boolean.
 *	ISSMALL(x) tests is x is a small integer.
 *	ISLIST(x) tests if x points to a List_Links structure.
 *	ISPCB(x) tests if x points to a Proc_ControlBlock structure.
 *	FIELD(x,type,field) is x->type.field
 *
 * Results:
 *	SUCCESS.
 *
 * Side effects:
 *	Prints stuff to screen.
 *
 *
 *----------------------------------------------------------------------
 */
#define INT(x)		((int)(x))
#define INTP(x)		((int *)(x))
#define	ISSTRZ(x)	(INT(x)==0 || ISSTR(x))
#define ISALIGN(x)	(ISADDR(x) && (INT(x)&3)==0)
#define ISALIGNZ(x)	(INT(x)==0 || INT(x)==NIL || ISALIGN(x))
/* sun3 test-and-set sets to 0x80000000, sun4 to 0xff000000 */
#define ISBOOL(x)	(INT(x)==0||INT(x)==1||INT(x)==0x80000000||\
			INT(x)==0xff000000)
#define ISSMALL(x)	(INT(x)>=0&&INT(x)<20)
#define ISPCBZ(x)	(INT(x)==0||INT(x)==NIL||ISPCB(x))
#define OFF(type,field) (INTP(&(((type *)0)->field))-INTP(0))
#define READIN(type,src,dst) (READIN_INT(sizeof(type),(Address)(src),\
		(Address)(dst)))

static int ISADDR _ARGS_((Address x));
static int ISSTR _ARGS_((char *x));
static int PRINTHANDLE _ARGS_ ((char *str, Fs_HandleHeader *handlePtr));
static int ISLIST _ARGS_((List_Links *x));
static int ISPCB _ARGS_((Proc_ControlBlock *x));
static int FINDPID _ARGS_((Proc_ControlBlock *x));
static int PRINTRPCCLIENT _ARGS_((RpcClientChannel *x));
static int PRINTRPCSERVER _ARGS_((RpcServerState *x));
static int PRINTSERVERPROC _ARGS_((ServerInfo *x));
static int PRINTLOCK _ARGS_((Sync_Lock *x, int print));
static int PRINTSEM _ARGS_((Sync_Semaphore *x));
static int PRINTLFS _ARGS_((Lfs *x, char *text));

/*
 * Read in some memory.
 * The macro looks after the types.
 */
static int READIN_INT(len,src,dst)
int len;
Address src,dst;
{
    ReturnStatus status;
    int i;
#if defined(ds3100) || defined(ds5000)
    /* Mach_Probe doesn't work right for some reason */
    char buf[2];
    for (i=0;i<len;i++) {
	if (! Dbg_InRange(((unsigned)src)&~1,2,FALSE)) {
	    return FALSE;
	}
    }
    bcopy(src,dst,len);
    return TRUE;
#else
    for (i=0;i<len;i++) {
	status = Mach_Probe(1,src+i,dst+i);
	if (status != SUCCESS) return FALSE;
    }
    return TRUE;
#endif
}

/*
 * Test if x is a valid address.
 */
static int ISADDR(x)
Address x;
{
    int buf;
    if (READIN(int,x,(Address)&buf)) {
	return TRUE;
    } else {
	return FALSE;
    }
}

/*
 * Test if x is a string.  Leave the string in strbuf if so.
 */
char strbuf[40];
static int ISSTR(x)
char *x;
{
    int i;
    for (i=0;i<sizeof(strbuf);i++) {
	if (!READIN(char,x+i,strbuf+i)) {
	    return FALSE;
	}
	if (strbuf[i]=='\0') {
	    return TRUE;
	}
	if (!isprint(strbuf[i])) {
	    return FALSE;
	}
    }
    strbuf[39]='\0';
    return TRUE;
}


/*
 * Print out the name associated with a handle.
 */
static int PRINTHANDLE(str,handlePtr)
char *str;
Fs_HandleHeader *handlePtr;
{
    Fs_HandleHeader handle;
    if (!READIN(Fs_HandleHeader,handlePtr,&handle) ||
	    !ISLIST(&handle.lruLinks) || !ISBOOL(handle.unlocked.waiting) ||
	    !ISSMALL(handle.refCount) ||
		!(handle.lockProcPtr==(Proc_ControlBlock *)NIL ||
		ISPCB(handle.lockProcPtr))) {
	return FALSE;
    }
    if (handle.name==(char *)NIL) {
	printf("%s: \"%s\" (handle locked by %x)", str, "(no name)",
		handle.lockProcPtr);
    } else if (ISSTR(handle.name)) {
	printf("%s: \"%s\" (handle locked by %x)", str, strbuf,
		handle.lockProcPtr);
    } else {
	printf("%s: \"%s\" (handle locked by %x)", str, "(bad name)",
		handle.lockProcPtr);
    }
    return TRUE;
}

/*
 * Test if x is a list.
 */
static int ISLIST(x)
List_Links *x;
{
    List_Links thisList, prevList, nextList;
    int retVal;
    retVal =  READIN(List_Links,x,&thisList) &&
	    READIN(List_Links,thisList.prevPtr,&prevList) &&
	    READIN(List_Links,thisList.nextPtr,&nextList) &&
	    prevList.nextPtr == x && nextList.prevPtr == x;
    return retVal;
}

/*
 * Test if x is a pcb
 */
static int ISPCB(x)
Proc_ControlBlock *x;
{
    Proc_ControlBlock pcb;
    int retVal;
    retVal = READIN(Proc_ControlBlock,x,&pcb) && ISSMALL(pcb.processor) &&
            ISSTR(pcb.argString) && ISADDR((Address)pcb.links.prevPtr) &&
	    ISADDR((Address)pcb.links.nextPtr) && ISSMALL(pcb.state) &&
	    ISADDR((Address)pcb.childList) && pcb.processID <=0xfffff;
#if 0
    if (!retVal) {
        if (!READIN(Proc_ControlBlock,x,&pcb)) {
	    printf("PCB: READIN failed ");
	} else if (!ISLIST(&pcb.links)) {
	    printf("PCB: LIST failed ");
	} else if (!ISSMALL(pcb.processor)) {
	    printf("PCB: SMALL failed ");
	} else if (!ISLIST(&pcb.childListHdr)) {
	    printf("PCB: LIST2 failed ");
	} else if (pcb.processID >0xfffff) {
	} else {
	    printf("PCB: mystery ");
	}
    }
#endif
    return retVal;
}

/*
 * Return PID or 0
 */
static int FINDPID(x)
Proc_ControlBlock *x;
{
    Proc_ControlBlock pcb;
    if (READIN(Proc_ControlBlock,x,&pcb) && ISPCB(&pcb)) {
	return pcb.processID;
    } else {
	return FALSE;
    }
}

/*
 * Print if x is a rpc client channel
 */
static int PRINTRPCCLIENT(x)
RpcClientChannel *x;
{
    int i;
    RpcClientChannel chan;
    if (!READIN(RpcClientChannel,x,&chan)) {
	return FALSE;
    }
    for (i=0;i<rpcNumChannels;i++) {
	if (rpcChannelPtrPtr[i] == x) {
	    printf("RPC client: \"waitCondition\", server %d ",
		    chan.serverID);
	    if (chan.state & CHAN_FREE) {
		printf("FREE ");
	    }
	    if (chan.state & CHAN_BUSY) {
		printf("BUSY ");
	    }
	    if (chan.state & CHAN_WAITING) {
		printf("WAIT ");
	    }
	    if (chan.state & CHAN_TIMEOUT) {
		printf("TIME ");
	    }
	    if (chan.state & CHAN_FRAGMENTING) {
		printf("FRAG ");
	    }
	    return TRUE;
	}
    }
    return FALSE;
}

/*
 * Test if x is a rpc server.
 */
static int PRINTRPCSERVER(x)
RpcServerState *x;
{
    int i;
    RpcServerState state;
    if (!READIN(RpcServerState,x,&state)) {
	return FALSE;
    }
    for (i=0;i<rpcMaxServers;i++) {
	if (rpcServerPtrPtr[i] == x) {
	    printf("RPC server:\"waitCondition\", client %d ",
		    state.clientID);
	    if (state.state & SRV_NOTREADY) {
		printf("NOTREADY ");
	    }
	    if (state.state & SRV_FREE) {
		printf("FREE ");
	    }
	    if (state.state & SRV_BUSY) {
		printf("BUSY ");
	    }
	    if (state.state & SRV_WAITING) {
		printf("WAIT ");
	    }
	    if (state.state & SRV_AGING) {
		printf("AGING ");
	    }
	    if (state.state & SRV_FRAGMENT) {
		printf("FRAG ");
	    }
	    if (state.state & SRV_NO_REPLY) {
		printf("NO_REPLY ");
	    }
	    if (state.state & SRV_STUCK) {
		printf("STUCK ");
	    }
	    return TRUE;
	}
    }
    return FALSE;
}

/* 
 * Print if x is a rpc server proc
 */
static int PRINTSERVERPROC(x)
ServerInfo *x;
{
    int i;
    ServerInfo info;
    if (!READIN(ServerInfo,x,&info)) {
	return FALSE;
    }
    for (i=0;i<proc_NumServers;i++) {
	if (serverInfoTable+i == x) {
	    printf("ServerProc: \"condition\" (waiting for task)");
	    if (info.flags & SERVER_BUSY) {
		printf("BUSY ");
	    }
	    if (info.flags & FUNC_PENDING) {
		printf("PENDING ");
	    }
	    return TRUE;
	}
    }
    return FALSE;
}

/*
 * Print if x is a Lfs structure
 */
static int PRINTLFS(x, text)
Lfs *x;
char *text;
{
    Lfs lfs;
    if (!READIN(Lfs,x,&lfs)) {
	return FALSE;
    }
    if (ISSTR(lfs.name) && ISBOOL(lfs.writeBackActive) &&
	    PRINTLOCK(&lfs.cacheBackendLock,0) &&
	    ISBOOL(lfs.writeBackMoreWork) && ISBOOL(lfs.shutDownActive) &&
	    PRINTLOCK(&lfs.lock,0)) {
	(void) ISSTR(lfs.name);
	printf("Lfs: %s on %s", text, strbuf);
	return TRUE;
    }
    return FALSE;
}

/*
 * Print if x is a lock.
 * Or just return true/false if print=0;
 */
static int PRINTLOCK(x,print)
Sync_Lock *x;
int print;
{
    Sync_Lock lock;
    if (READIN(Sync_Lock,x,&lock) && ISBOOL(lock.inUse) &&
	    ISBOOL(lock.waiting) &&
	    ISALIGNZ(lock.holderPC) && ISPCBZ(lock.holderPCBPtr) &&
	    ISSTR(lock.name)) {
	if (print) {
	    printf("lock \"%s\" at %x", strbuf, lock.holderPC);
	}
	if (print && FINDPID(lock.holderPCBPtr)) {
	    printf(" held by process %x", FINDPID(lock.holderPCBPtr));
	}
	return TRUE;
    } else {
	return FALSE;
    }
}

/*
 * Test if x is a semaphore.
 */
static int PRINTSEM(x)
Sync_Semaphore *x;
{
    Sync_Semaphore sem;
    if (READIN(Sync_Semaphore,x,&sem) && ISSMALL(sem.value) &&
	    ISALIGNZ(sem.holderPC) &&
	    ISPCBZ(sem.holderPCBPtr) && ISSTR(sem.name)) {
	printf("semaphore \"%s\" at %x", strbuf, sem.holderPC);
	if (FINDPID(sem.holderPCBPtr)) {
	    printf(" held by process %x",
		    FINDPID(sem.holderPCBPtr));
	}
	return TRUE;
    } else {
	return FALSE;
    }
}

typedef struct LockEntry {
    int	addr;		/* Address (event) associated with the lock. */
    char *name;		/* Name of the lock event. */
} LockEntry;

extern Sync_Condition cleanBlockCondition, writeBackComplete,
    closeCondition, lruDone, debugListCondition, familyCondition,
    migrateCondition, evictCondition, recovCondition, recovPingCondition,
    rpcDaemon, freeChannels, signalCondition, codeSegCondition,
    cleanCondition, swapDownCondition, mappingCondition, swapFileCondition;

LockEntry locks[] = {
    (int)&cleanBlockCondition, "cleanBlockCondition",
    (int)&writeBackComplete, "writeBackComplete",
    (int)&closeCondition, "closeCondition",
    (int)&lruDone, "lruDone",
    (int)&debugListCondition, "debugListCondition",
    (int)&familyCondition, "familyCondition",
    (int)&migrateCondition, "migrateCondition",
    (int)&evictCondition, "evictCondition",
    (int)&recovCondition, "recovCondition",
    (int)&rpcDaemon, "rpcDaemon",
    (int)&freeChannels, "freeChannels",
    (int)&signalCondition, "signalCondition (pause syscall)",
    (int)&codeSegCondition, "codeSegCondition",
    (int)&cleanCondition, "cleanCondition",
    (int)&swapDownCondition, "swapDownCondition",
    (int)&mappingCondition, "mappingCondition",
    (int)&swapFileCondition, "swapFileCondition",
    (int)&recovPingCondition, "recovPingCondition",
    0, 0
};

static void Proc_KDumpInt _ARGS_((ClientData data, Proc_CallInfo *callInfoPtr));

void
Proc_KDump(data)
    ClientData data;
{
    Proc_CallFunc(Proc_KDumpInt, data, 0);
}

/*ARGSUSED*/
static void
Proc_KDumpInt(data, callInfoPtr)
    ClientData	data;
    Proc_CallInfo *callInfoPtr;
{
    int i;
    Proc_ControlBlock *procPtr, *tmpProcPtr;
    int *event;
    int atEvent;
    LockEntry *lockPtr;
    Fscache_FileInfo cacheInfo, *cacheInfoPtr;
    Fs_HandleHeader *handlePtr;
    RpcClientChannel *rpcClientPtr;
    RpcServerState *rpcServerPtr;
    ServerInfo *serverProcPtr;
    Sync_Lock *syncLockPtr;
    Fsconsist_Info consistInfo;
    Lfs *lfsPtr;
    int match;

    for (i = 0; i < proc_MaxNumProcesses; i++) {
	procPtr = proc_PCBTable[i];
	match = 0;
	if (procPtr->state == PROC_WAITING) {
	    printf("%6x", procPtr->processID);
	    if (procPtr->argString != (Address) NIL) {
		char cmd[30];
		char *space;

		(void) strncpy(cmd, procPtr->argString, 30);
		space = strchr(cmd, ' ');
		if (space != (char *) NULL) {
		    *space = '\0';
		} else {
		    cmd[29] = '\0';
		}
		printf("(%s)", cmd);
	    }
	    printf(": ");
	    event = (int *)procPtr->event;
	    if (ISADDR((Address)event)) {
		for (lockPtr = locks ; lockPtr->addr != 0; lockPtr++) {
		    if ((int)event == lockPtr->addr) {
			printf("condition \"%s\"\n", lockPtr->name);
			goto found;
		    }
		}
		if (PRINTLOCK((Sync_Lock *)event,1)) {
			/* Sync_Lock / Sync_KernelLock */
		} else if ((Proc_ControlBlock *)event==procPtr) {
		    /* Proc_ControlBlock */
		    printf("timer");
		} else if (FINDPID((Proc_ControlBlock *)event)) {
		    /* Proc_ControlBlock */
		    printf("timer %x", FINDPID((Proc_ControlBlock *)event));
		} else if (PRINTSEM((Sync_Semaphore *)event)) {
		   /* Sync_Semaphore */
		} else if (READIN(int,event,&atEvent) && ISBOOL(atEvent)) {
		    /* Sync_Condition */
		    handlePtr = (Fs_HandleHeader *)
			    (event-OFF(Fs_HandleHeader,unlocked));
		    if (PRINTHANDLE("handle: \"unlocked\"", handlePtr)) {
			match++;
		    }
		    /*
		     * We might be blocked on the noDirtyBlocks field of
		     * a Fscache_FileInfo structure.  In that case, print
		     * the associated handle.
		     */
		    if (READIN(Fs_HandleHeader *,((int *)event)-
			    OFF(Fscache_FileInfo,noDirtyBlocks)+
			    OFF(Fscache_FileInfo,hdrPtr),&handlePtr)) {
			if (PRINTHANDLE("cache block: \"noDirtyBlocks\"",
				    handlePtr)) {
			    match++;
			}
		    }
		    /*
		     * We might be blocked on the ioDone field of a
		     * Fscache_Block structure.  In that case, access the
		     * cacheInfoPtr and print the handle.
		     */
		    if (READIN(Fscache_FileInfo *,((int *)event)-
			    OFF(Fscache_Block,ioDone)+
			    OFF(Fscache_Block,cacheInfoPtr),&cacheInfoPtr)) {
			if (READIN(Fscache_FileInfo,cacheInfoPtr,&cacheInfo) &&
				PRINTHANDLE("cache block: \"ioDone\"",
				    cacheInfo.hdrPtr)) {
			    match++;
			}
		    }
		    /*
		     * We might be blocked on the waitCondition in the PCB.
		     */
		    tmpProcPtr = (Proc_ControlBlock *)
			    (event-OFF(Proc_ControlBlock,waitCondition));
		    if (tmpProcPtr == procPtr) {
			printf("PCB: \"waitCondition\" (wait syscall) ");
			match++;
		    } else if (FINDPID(tmpProcPtr)) {
			printf("PCB: \"waitCondition\" %x (wait syscall) ",
				FINDPID(tmpProcPtr));
			match++;
		    }
		    /* Or we might be blocked on the lockedCondition */
		    tmpProcPtr = (Proc_ControlBlock *)
			    (event-OFF(Proc_ControlBlock,lockedCondition));
		    if (tmpProcPtr==procPtr) {
			printf("PCB: \"lockedCondition\" (locked entry) ");
			match++;
		    } else if (FINDPID(tmpProcPtr)) {
			printf("PCB: \"lockedCondition\" %x (locked entry) ",
				FINDPID(tmpProcPtr));
			match++;
		    }
		    /* Or we might be blocked on Fsconsist_Info */
		    syncLockPtr = (Sync_Lock *)(event-
			    OFF(Fsconsist_Info,consistDone));
		    if (PRINTLOCK(syncLockPtr,1)) {
			printf(" (consistDone)\n");
			match++;
			if (READIN(Fsconsist_Info,syncLockPtr,&consistInfo)) {
			    (void) PRINTHANDLE("handle:", consistInfo.hdrPtr);
			}
		    }
		    syncLockPtr = (Sync_Lock *)(event-
			    OFF(Fsconsist_Info,repliesIn));
		    if (PRINTLOCK(syncLockPtr,1)) {
			printf(" (repliesIn)\n");
			match++;
			if (READIN(Fsconsist_Info,syncLockPtr,&consistInfo)) {
			    (void) PRINTHANDLE("handle:", consistInfo.hdrPtr);
			}
		    }

		    /* Maybe it's a LFS structure */
		    lfsPtr = (Lfs *)(event-OFF(Lfs, writeWait));
		    if (PRINTLFS(lfsPtr, "writeWait")) {
			match++;
		    }
		    lfsPtr = (Lfs *)(event-OFF(Lfs, cleanSegmentsWait));
		    if (PRINTLFS(lfsPtr, "cleanSegmentsWait")) {
			match++;
		    }
		    lfsPtr = (Lfs *)(event-OFF(Lfs, cacheBackendLock));
		    if (PRINTLFS(lfsPtr, "cacheBackendLock")) {
			match++;
		    }
		    lfsPtr = (Lfs *)(event-OFF(Lfs, lock));
		    if (PRINTLFS(lfsPtr, "Lfs master lock")) {
			match++;
		    }
		    lfsPtr = (Lfs *)(event-OFF(Lfs, checkPointWait));
		    if (PRINTLFS(lfsPtr, "checkPointWait")) {
			match++;
		    }

		    /* Or maybe something else. */
		    serverProcPtr = (ServerInfo *)(event-
			    OFF(ServerInfo, condition));
		    if (PRINTSERVERPROC(serverProcPtr)) {
			match++;
		    }
		    rpcClientPtr = (RpcClientChannel *)(event-
			    OFF(RpcClientChannel, waitCondition));
		    if (PRINTRPCCLIENT(rpcClientPtr)) {
			match++;
		    }
		    rpcServerPtr = (RpcServerState *)(event-
			    OFF(RpcServerState, waitCondition));
		    if (PRINTRPCSERVER(rpcServerPtr)) {
			match++;
		    }
		    handlePtr = (Fs_HandleHeader *)(event-OFF(Fsrmt_IOHandle,
			recovery.reopenComplete));
		    if (PRINTHANDLE("\"recovery.reopenComplete\"", handlePtr)) {
			match++;
		    }
		    if (!match) {
			printf("condition %x", (int)event);
		    } else if (match>1) {
			printf("(Ambiguous)");
		    }
		} else {
		    printf("event %x", (int)event);
		}
	    } else if ((int)event == -1) {
		printf("wakeup signal (prob. select syscall)");
	    } else {
		printf("event? %x", (int)event);
	    }
	    printf("\n");
found:;
	}
    }
}
#define KDUMP
#endif
#endif
#endif

#ifndef KDUMP
/* ARGSUSED */
ReturnStatus
Proc_KDump(dummy) 
    ClientData dummy;
{ 
    return SUCCESS;
}
#endif
