X-Git-Url: http://andersk.mit.edu/gitweb/moira.git/blobdiff_plain/e2a67c78ae291e815fba7a2a859b8a1205ec757d..refs/heads/origin:/lib/mr_call.c diff --git a/lib/mr_call.c b/lib/mr_call.c index 8bd9fd50..f226e682 100644 --- a/lib/mr_call.c +++ b/lib/mr_call.c @@ -1,100 +1,225 @@ -/* - * 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 + * . */ -sms_start_call(op, hcon, arg) - OPERATION op; - HALF_CONNECTION hcon; - register struct param *arg; +#include +#include +#include "mr_private.h" + +#include +#include +#include +#ifndef _WIN32 +#include +#endif /* _WIN32 */ + +#ifdef HAVE_UNISTD_H +#include +#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); +}