/* -*- c -*- */

/* server */

/*
 * Each command class contains 
 *   process id of current process (or -1 if none)
 *   Q of commands
 */

/*
 * Each Q contains entries that consist of the command
 * and the client object.
 */

/*
 * A client object contains the following:
 *     qrpc object of accepted connection
 *     number of pending requests;
 *     time of last request;
 * Client objects are stored in the file descriptor table.
 */


server_main(cmd_class_descriptors, params) {
    /*
     * Params: service name, port number, timeout, kerberos key name,
     * application version information
     */

    /*
     * Each command class descriptor contains a list of 
     * commands (just argv[0]).  Exactly one command class can be
     * non-forking.
     */

    Set up array of command classes;
    /* 
     * Each command class contains a list of commands, the process
     * id of its current instance (0 if no current instance, -1 if 
     * non-forking), and Q of pending requests.
     * Each pending request contains the command and client object 
     * of the client issuing the command.
     */
    Set up lookup table matching commands to indexes into the 
	command class table;

    set up completion pipe;
    set up chld signal handler;
    /*
     * When a server instance completes, the sigchld handler writes
     * a byte to the completion pipe.  The main loop does the rest.
     */

    set up table mapping file descriptors to objects;
    /*
     * Server has table with FD_SETSIZE elements that maps 
     * file descriptors to their jobs.  An entry in a union:
     *
     *
     *  client:
     *     client object
     *  listener:
     *     qrpc object of listener
     *  completion:
     *     null
     *
     */ 



    while (1) {
	select (listener, each client, completion, timeout) {
	  timeout:
	    foreach client object {
		if client has pending request(s) and is not active
		    notify client of status;
		if client has no pending requests and is not active and
		    has not been active for a certain amount of time,
		    close connection;
	    }
	  newconnection:
	    accept;
	    read and validate authenticator;
	    register_client;
	    
	  request:
	    get client object;
	    increment number of pending requests;
	    try_dispatch;
	    determine command class;
	    Enqueue request;
	    try_dispatch(command class);
	  completion:
	    wait3 to reap child and determine pid / status;
	    determine command class from pid;
	    deQ top request;
	    reset active pid;
	    report completion and status to the client;
	    decrement number of pending requests for client;
	    try_dispatch(command class)
	}
    }
}

chld {
    send a byte over completion pipe;
}


try_dispatch(command class) {
    if no active job and Q not empty {
	dispatch (command class);
}

register_client(qrpc) {
    create and initialize client object;
    insert in fd table;
}


dispatch(command class) {
    fork {
      parent:
	register pid in command class object;
	return;

      child:
	reset chld signal handler;
	call dispatch routine;
	_exit(0);
    }
}



/* client */

/*
 * Every time the client tries to contact the server, it should
 * verify the that connection is still there and reinitiate if not.
 * The server shuts down idle connections.
 */

connect via qrpc to server;
send authenticator;

make requests and send commands;

Each

/* afs-cmd */
    fork {
      parent:
	if appropriate and possible, check status and report
	    progress to client periodically;
      child:
	run command reporting output to client;
    }
