/* 
 * $Id: c_rpc.c,v 1.1 91/05/03 03:02:08 qjb Exp Locker: qjb $
 * $Source: /afs/athena.mit.edu/astaff/project/qcs/src/RCS/c_rpc.c,v $
 * $Author: qjb $
 *
 * This file handles the client side qcs protocol.
 */

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

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <errno.h>
#include <qrpc.h>

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

char tmpbuf[BUFSIZ];

void qcsi_be_flushed(qcs_t *qcs)
{
    int i;

    if (strlen(qcs->errmsg))
	bzero(qcs->errmsg, strlen(qcs->errmsg));
    qrpc_destroy(&qcs->qrpc);
    for (i = 0; i < MAXJOBS; i++)
	if (qcs->user_qrpc[i] == QCS_NO_USERQRPC)
	    qcs->user_qrpc[i] = NULL;
	else if (qcs->user_qrpc[i])
	    qrpc_destroy(&(qcs->user_qrpc[i]));
}

qcs_error_t qcsi_check_connection(qcs_t *qcs, void *userdata)
{
    if (qcs->qrpc == NULL) {
	/* Attempt to re-establish connection */
	if (qcs_connect(qcs, qcs->params)) {
	    sprintf(tmpbuf, 
		    "Server seems to be down.  Please try again later.");
	    qcs->params->handle_error(qcs, tmpbuf, userdata);
	    return (QCS_FAILURE);
	}
    }

    return (QCS_SUCCESS);
}


void qcs_disconnect(qcs_t *qcs)
{
    if (qcs->qrpc) {
	(void) qrpc_send(qcs->qrpc, QCS_OP_QUIT, (qrpc_data_t) NULL, 
			 (qrpc_length_t) 0);
	qrpc_destroy(&qcs->qrpc);
    }
}


qcs_error_t qcs_send_command(qcs_t *qcs, char *command, void *userdata)
{
    qrpc_error_t status = QRPC_SUCCESS;
    qcs_jobno jobno;
    long argc = 0;
    int datalen = 0;
    qrpc_length_t plen;
    char *cooked;
    char *packet;
    char *p1;
    int i;
    
    if (qcsi_check_connection(qcs, userdata))
	return(QCS_SUCCESS);

    /* 
     * Compute the job number.  For now, the job number is the
     * index into user_qrpc of the first NULL.  If there are no 
     * NULLS, then we report an error.
     */

    jobno = MAXJOBS;
    for (i = 0; i < MAXJOBS; i++) {
	if (qcs->user_qrpc[i] == NULL) {
	    jobno = i;
	    break;
	}
    }

    if (jobno == MAXJOBS) {
	sprintf(qcs->errmsg, 
		"unable to send command: too many jobs are active.");
	return (QCS_FAILURE);
    }

    /*
     * First, convert this raw command into a cooked command which
     * is simply all the arguments including the first one
     * separated by nulls.  This routine gets the length of the 
     * data and the number of command arguments as well.
     */
    
    if (qcsi_command_to_argv(command, &argc, &cooked, &datalen, 
			     qcs->errmsg)) 
	return(QCS_FAILURE);
    
    /*
     * Now we can allocate memory for our packet.  We need enough 
     * memory for jobno, argc, and all the arguments.
     */

    plen = (qrpc_length_t) datalen + sizeof(jobno) + sizeof(argc);
    if ((packet = (char *)malloc(plen)) == NULL) {
	sprintf(qcs->errmsg, 
		"not enough memory to allocate %d bytes for command packet",
		plen);
	return(QCS_FAILURE);
    }
    
    /* 
     * Assemble command packet.  The command packet contains
     * a long (network byte order) for jobno followed by 
     * a long (network byte order) for argc followed by the command 
     * and its arguments each null-terminated.
     */

    p1 = packet;
    jobno = htonl(jobno);
    bcopy((char *)&jobno, p1, sizeof(qcs_jobno));
    p1 += sizeof(qcs_jobno);
    argc = htonl(argc);
    bcopy((char *)&argc, p1, sizeof(long));
    p1 += sizeof(long);

    bcopy(cooked, p1, datalen);
    free(cooked);

    /* Send the packet. */

    status = qrpc_send(qcs->qrpc, QCS_OP_CMD, (qrpc_data_t) packet, plen);
    if (status == QRPC_ERR_NOPEER) {
	qcsi_be_flushed(qcs);
	return (QCS_SUCCESS);
    }
    else if (status) {
	sprintf(qcs->errmsg, "while sending command: %s",
		qrpc_error_string(qcs->qrpc, status));
	return (QCS_FAILURE);
    }

    /* Free the memory we allocated for the command */
    free(packet);

    /* Store the job number for this command */
    qcs->user_qrpc[ntohl(jobno)] = QCS_NO_USERQRPC;

    return (QCS_SUCCESS);
}
