/* 
 * $Id: s_dispatch.c,v 1.2 91/05/03 03:05:17 qjb Exp Locker: qjb $
 * $Source: /afs/athena.mit.edu/astaff/project/qcs/src/RCS/s_dispatch.c,v $
 * $Author: qjb $
 *
 */

#if !defined(lint) && !defined(SABER) || defined(RCS_HDRS)
static char *rcsid = "$Id: s_dispatch.c,v 1.2 91/05/03 03:05:17 qjb Exp Locker: qjb $";
#endif /* !lint && !SABER || RCS_HDRS */

#include <stdio.h>
#include <syslog.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <krb.h>
#include <des.h>
#include <qrpc.h>

#include "qcs.h"
#include "qcs_private.h"


char tmpbuf[BUFSIZ];


static qcs_error_t qcsi_dispatch(qcs_cmd_class_id cc_index)
{
    qcs_error_t status = QCS_SUCCESS;
    qcs_cmd_class *cmd_class;
    int pid;
    qcs_request *req;
    qcs_client client;
    qcs_dispatcher dispatcher;

    /*
     * We don't do sanity checking here since this routine can only be 
     * called from try_dispatch which does the checking.
     */

    /* Invalid cmd class */
    if ((cmd_class = qcsi_get_cmd_class(cc_index)) == NULL)
	return (QCS_FAILURE);
    
    req = &(cmd_class->pending[cmd_class->head]);
    client = qcsi_get_client_from_req(req);

    dispatcher = qcsi_get_dispatcher(req->cmd);

    sprintf(tmpbuf, "Now starting command (%s) for client %s",
	    qcsi_unparse_cmd(req->cmd), qcsi_unparse_client(client));
    qcsi_log(tmpbuf);

    sprintf(tmpbuf, "Command starting is job %d", req->jobno);
    qcsi_debug(tmpbuf);

    sprintf(tmpbuf, "Your command (%s) is now being started.", 
	    qcsi_unparse_cmd(req->cmd));
    
    if (qcsi_client_notify(client, tmpbuf)) {
	qcsi_flush_client(&client);
	return (QCS_FAILURE);
    }

    if (cmd_class->pid == QCS_NOFORK) {
	if (dispatcher)
	    dispatcher(client, req);
	qcsi_complete(QCS_JT_NOFORK);
    }
    else {
	switch (pid = fork()) {
	    
	  case -1:
	    sprintf(tmpbuf, "Unable to run your job: %s", sys_errlist[errno]);
	    if (qcsi_client_error(client, tmpbuf))
		qcsi_flush_client(&client);
	    status = QCS_FAILURE;
	    break;
	    
	  case 0:
	    /* Child */
	    
	    /* 
	     * We don't want our children's signals to effect the main 
	     * instance of the server
	     */
	    (void) signal(SIGCHLD, SIG_DFL);
	    (void) signal(SIGQUIT, SIG_DFL);
	    
	    /* 
	     * Set process group so that we won't inherit signals from
	     * parent.
	     */
	    setpgrp(0, getpid());

	    if (dispatcher)
		dispatcher(client, req);
	    _exit(0);
	    
	    break;
	    
	  default:
	    /* Parent */
	    cmd_class->pid = pid;
	    break;
	}
    }
    
    return (status);
}


void qcsi_try_dispatch(qcs_cmd_class_id cc_index)
{
    qcs_cmd_class *cmd_class;
    qcs_bool done = FALSE;
    qcs_client client;
    qcs_bool shutting_down;
    
    shutting_down = qcsi_is_server_shutdown();

    /* Invalid cmd class */
    if ((cmd_class = qcsi_get_cmd_class(cc_index)) == NULL)
	return;
    
    /* Command already running from this cmd class */
    if ((cmd_class->pid != QCS_NOFORK) && (cmd_class->pid != QCS_READY))
	return;

    /* 
     * Dispatch if there is something waiting to be run.  Stop
     * as soon as we dispatch succesfully.  Ignore all jobs whose
     * clients have gone away and refuse to dispatch jobs if the
     * server is shutting down.
     */
    while ((! done) && (cmd_class->npending > 0)) {
	client = 
	    qcsi_get_client_from_req(&(cmd_class->pending[cmd_class->head]));
	if (client == NULL) {
	    /* Ignore jobs with dead clients */
	    qcs_request trash_req;
	    qcsi_dequeue_request(cc_index, &trash_req);
	}
	else {
	    if ((! shutting_down) && qcsi_dispatch(cc_index) == QCS_SUCCESS) {
		/* We're not shutting down and we've dispatched a job */
		done = TRUE;
	    }
	    else {
		/* We're shutting down or the dispatch failed. Dequeue. */
		qcs_request trash_req;
		qcsi_dequeue_request(cc_index, &trash_req);
		/* If we're shutting down, tell client */
		if (shutting_down) {
		    client->nreq--;
		    sprintf(tmpbuf, "Can't dispatch command (%s) because %s",
			    qcsi_unparse_cmd(trash_req.cmd),
			    "the server is shutting down.");
		    qcsi_client_error(client, tmpbuf);
		}
	    }
	}
    }
}
