-/*
- * GDB operations to send and recieve RPC requests and replies.
+/* $Id$
+ *
+ * Pass an mr_params off to the Moira server and get a reply
+ *
+ * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
*/
-sms_start_call(op, hcon, arg)
- OPERATION op;
- HALF_CONNECTION hcon;
- register struct param *arg;
+#include <mit-copyright.h>
+#include <moira.h>
+#include "mr_private.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <netinet/in.h>
+#endif /* _WIN32 */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+RCSID("$Header$");
+
+/* Moira RPC format:
+
+ 4-byte total length (including these 4 bytes)
+ 4-byte version number (MR_VERSION_2 == 2)
+ 4-byte opcode (from client) or status (from server)
+ 4-byte argc
+
+ 4-byte len, followed by null-terminated string, padded to 4-byte boundary
+ (the len doesn't include the padding)
+ ...
+
+ (followed by more packets if status was MR_MORE_DATA)
+
+ All numbers are in network byte order.
+*/
+
+int mr_do_call(struct mr_params *params, struct mr_params *reply)
{
- /*
- * This should probably be split into several routines.
- * It could also probably be made more efficient (punting most
- * of the argument marshalling stuff by doing I/O directly
- * from the strings. Anyone for a scatter/gather sms_send_data?
- *
- * that would look a lot like the uio stuff in the kernel.. hmm.
- */
-
- /*
- * Marshall the entire data right now..
- * We are sending the version number,
- * total request size, request number,
- * argument count, and then each argument.
- * At least for now, each argument is a string, which is
- * sent as a count of bytes followed by the bytes
- * (including the trailing '\0'), padded
- * to a longword boundary.
- */
-
- sms_size = 4 * sizeof(long);
-
- argl = (int *)malloc(sizeof(int) * arg->argc);
-
- /*
- * For each argument, figure out how much space is needed.
- */
-
- for (i = arg->argc; i; --i) {
- argl[i] = len = strlen(arg->argv[i]) + 1;
- sms_size += sizeof(long) + len;
- sms_size = sizeof(long) * howmany(sms_size, sizeof(long));
- }
-
- arg->flattened = buf = db_alloc(sms_size);
-
- bzero(arg->flattened, sms_size);
-
- arg->size = sms_size;
-
- /*
- * This is gross. Any better suggestions, anyone?
- * It should work on the RT's, since malloc is guaranteed to
- * return a pointer which is aligned correctly for any data.
- */
-
- ((long *)buf)[0] = htonl(SMS_VERSION_1);
- ((long *)buf)[1] = htonl(sms_size);
- ((long *)buf)[2] = htonl(arg->procno);
- ((long *)buf)[3] = htonl(arg->argc);
-
- /*
- * bp is a pointer into the point in the buffer to put
- * the next argument.
- */
-
- bp = (char *)(&(long *)buf[4])
- for (i = arg->argc; i; --i) {
- len = argl[i];
- *((long *)bp) = htonl(len);
- bp += sizeof(long);
- bcopy(arg->argv[i], bp, len);
- bp += sizeof(long) * howmany(len, sizeof(long));
+ int status;
+
+ CHECK_CONNECTED;
+
+ status = mr_send(_mr_conn, params);
+ if (status == MR_SUCCESS)
+ status = mr_receive(_mr_conn, reply);
+
+ if (status)
+ mr_disconnect();
+
+ return status;
+}
+
+int mr_send(int fd, struct mr_params *params)
+{
+ u_long length;
+ int written;
+ int i, *argl;
+ char *buf, *p;
+
+ length = 16; /* length + version + opcode/status + argc */
+
+ if (params->mr_argl)
+ {
+ argl = params->mr_argl;
+ for (i = 0; i < params->mr_argc; i++)
+ length += 8 + argl[i];
+ }
+ else
+ {
+ argl = malloc(params->mr_argc * sizeof(int));
+ if (params->mr_argc && !argl)
+ return ENOMEM;
+ for (i = 0; i < params->mr_argc; i++)
+ {
+ argl[i] = strlen(params->mr_argv[i]) + 1;
+ length += 8 + argl[i];
}
- op->fcn.cont = sms_cont_call;
- arg->size = sms_size;
-
- if (gdb_send_data(hcon, arg->flattened, sms_size) == OP_COMPLETE)
- return sms_cont_call(op, hcon, arg);
- else return OP_RUNNING;
-}
-
-/*
- * This doesn't get called until after the actual buffered write completes.
- * In a non-preflattening version of this, this would then queue the
- * write of the next bunch of data.
- */
+ }
+
+ buf = malloc(length);
+ if (!buf)
+ {
+ if (!params->mr_argl)
+ free(argl);
+ return ENOMEM;
+ }
+ memset(buf, 0, length);
+
+ putlong(buf + 4, MR_VERSION_2);
+ putlong(buf + 8, params->u.mr_procno);
+ putlong(buf + 12, params->mr_argc);
-sms_cont_call(op, hcon, arg)
- OPERATION op;
- HALF_CONNECTION hcon;
- struct params *arg;
+ for (i = 0, p = buf + 16; i < params->mr_argc; i++)
+ {
+ putlong(p, argl[i]);
+ memcpy(p += 4, params->mr_argv[i], argl[i]);
+ p += argl[i] + (4 - argl[i] % 4) % 4;
+ }
+ length = p - buf;
+ putlong(buf, length);
+
+ written = send(fd, buf, length, 0);
+ free(buf);
+ if (!params->mr_argl)
+ free(argl);
+
+ if (written != (int)length)
+ return MR_ABORTED;
+ else
+ return MR_SUCCESS;
+}
+
+int mr_receive(int fd, struct mr_params *reply)
{
- op->result = OP_SUCCESS;
- db_free(arg->flattened, arg->size);
- db_free(arg, sizeof(struct params));
- return OP_COMPLETE;
+ int status;
+
+ memset(reply, 0, sizeof(struct mr_params));
+ do
+ status = mr_cont_receive(fd, reply);
+ while (status == -1);
+
+ return status;
}
+
+/* Read some or all of a client response, without losing if it turns
+ * out to be malformed. Returns MR_SUCCESS on success, an error code
+ * on failure, or -1 if the packet hasn't been completely received
+ * yet.
+ */
+
+int mr_cont_receive(int fd, struct mr_params *reply)
+{
+ u_long length, data;
+ int size, more;
+ char *p, *end;
+ int i;
+
+ if (!reply->mr_flattened)
+ {
+ char lbuf[4];
+
+ size = recv(fd, lbuf, 4, 0);
+ if (size != 4)
+ return size ? MR_ABORTED : MR_NOT_CONNECTED;
+ getlong(lbuf, length);
+ if (length > 8192)
+ return MR_INTERNAL;
+ reply->mr_flattened = malloc(length);
+ if (!reply->mr_flattened)
+ return ENOMEM;
+ memcpy(reply->mr_flattened, lbuf, 4);
+ reply->mr_filled = 4;
+ return -1;
+ }
+ else
+ getlong(reply->mr_flattened, length);
-/* Need routines to decode all that... */
+ more = recv(fd, reply->mr_flattened + reply->mr_filled,
+ length - reply->mr_filled, 0);
+ if (more == -1)
+ {
+ mr_destroy_reply(*reply);
+ return MR_ABORTED;
+ }
+
+ reply->mr_filled += more;
+
+ if (reply->mr_filled != length)
+ return -1;
+
+ getlong(reply->mr_flattened + 4, data);
+ if (data != MR_VERSION_2)
+ {
+ mr_destroy_reply(*reply);
+ return MR_VERSION_MISMATCH;
+ }
+
+ getlong(reply->mr_flattened + 8, reply->u.mr_status);
+ getlong(reply->mr_flattened + 12, reply->mr_argc);
+ if (reply->mr_argc > ((int)length - 16) / 8)
+ {
+ mr_destroy_reply(*reply);
+ return MR_INTERNAL;
+ }
+ reply->mr_argv = malloc(reply->mr_argc * sizeof(char *));
+ reply->mr_argl = malloc(reply->mr_argc * sizeof(int));
+ if (reply->mr_argc && (!reply->mr_argv || !reply->mr_argl))
+ {
+ mr_destroy_reply(*reply);
+ return ENOMEM;
+ }
+
+ p = (char *)reply->mr_flattened + 16;
+ end = (char *)reply->mr_flattened + length;
+ for (i = 0; i < reply->mr_argc && p + 4 <= end; i++)
+ {
+ getlong(p, reply->mr_argl[i]);
+ if (p + 4 + reply->mr_argl[i] > end)
+ break;
+ reply->mr_argv[i] = p + 4;
+ p += 4 + reply->mr_argl[i] + (4 - reply->mr_argl[i] % 4) % 4;
+ }
+
+ if (i != reply->mr_argc)
+ {
+ mr_destroy_reply(*reply);
+ return MR_INTERNAL;
+ }
+
+ return MR_SUCCESS;
+}
+
+void mr_destroy_reply(mr_params reply)
+{
+ free(reply.mr_argl);
+ free(reply.mr_argv);
+ free(reply.mr_flattened);
+}