blob: a9696d7be43c68c24900941244e1fbef7c7a58a5 [file] [log] [blame]
/*
* Copyright (C) 2010 Red Hat, Inc. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*/
#include "logging.h"
#include "cluster.h"
#include "compat.h"
#include "xlate.h"
#include <errno.h>
/*
* Older versions of the log daemon communicate with different
* versions of the inter-machine communication structure, which
* varies in size and fields. The older versions append the
* standard upstream version of the structure to every request.
* COMPAT_OFFSET is where the upstream structure starts.
*/
#define COMPAT_OFFSET 256
static void v5_data_endian_switch(struct clog_request *rq, int to_network __attribute__((unused)))
{
int i, end;
int64_t *pi64;
uint64_t *pu64;
uint32_t rq_type = rq->u_rq.request_type & ~DM_ULOG_RESPONSE;
if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
switch (rq_type) {
case DM_ULOG_CTR:
case DM_ULOG_DTR:
LOG_ERROR("Invalid response type in endian switch");
exit(EXIT_FAILURE);
case DM_ULOG_PRESUSPEND:
case DM_ULOG_POSTSUSPEND:
case DM_ULOG_RESUME:
case DM_ULOG_FLUSH:
case DM_ULOG_MARK_REGION:
case DM_ULOG_CLEAR_REGION:
case DM_ULOG_SET_REGION_SYNC:
case DM_ULOG_CHECKPOINT_READY:
case DM_ULOG_MEMBER_JOIN:
case DM_ULOG_STATUS_INFO:
case DM_ULOG_STATUS_TABLE:
/* No outbound data */
break;
case DM_ULOG_GET_REGION_SIZE:
case DM_ULOG_GET_SYNC_COUNT:
pu64 = (uint64_t *)rq->u_rq.data;
*pu64 = xlate64(*pu64);
break;
case DM_ULOG_IS_CLEAN:
case DM_ULOG_IN_SYNC:
pi64 = (int64_t *)rq->u_rq.data;
*pi64 = xlate64(*pi64);
break;
case DM_ULOG_GET_RESYNC_WORK:
case DM_ULOG_IS_REMOTE_RECOVERING:
pi64 = (int64_t *)rq->u_rq.data;
pu64 = ((uint64_t *)rq->u_rq.data) + 1;
*pi64 = xlate64(*pi64);
*pu64 = xlate64(*pu64);
break;
default:
LOG_ERROR("Unknown request type, %u", rq_type);
return;
}
} else {
switch (rq_type) {
case DM_ULOG_CTR:
case DM_ULOG_DTR:
LOG_ERROR("Invalid request type in endian switch");
exit(EXIT_FAILURE);
case DM_ULOG_PRESUSPEND:
case DM_ULOG_POSTSUSPEND:
case DM_ULOG_RESUME:
case DM_ULOG_GET_REGION_SIZE:
case DM_ULOG_FLUSH:
case DM_ULOG_GET_RESYNC_WORK:
case DM_ULOG_GET_SYNC_COUNT:
case DM_ULOG_STATUS_INFO:
case DM_ULOG_STATUS_TABLE:
case DM_ULOG_CHECKPOINT_READY:
case DM_ULOG_MEMBER_JOIN:
/* No incoming data */
break;
case DM_ULOG_IS_CLEAN:
case DM_ULOG_IN_SYNC:
case DM_ULOG_IS_REMOTE_RECOVERING:
pu64 = (uint64_t *)rq->u_rq.data;
*pu64 = xlate64(*pu64);
break;
case DM_ULOG_MARK_REGION:
case DM_ULOG_CLEAR_REGION:
end = rq->u_rq.data_size/sizeof(uint64_t);
pu64 = (uint64_t *)rq->u_rq.data;
for (i = 0; i < end; i++)
pu64[i] = xlate64(pu64[i]);
break;
case DM_ULOG_SET_REGION_SYNC:
pu64 = (uint64_t *)rq->u_rq.data;
pi64 = ((int64_t *)rq->u_rq.data) + 1;
*pu64 = xlate64(*pu64);
*pi64 = xlate64(*pi64);
break;
default:
LOG_ERROR("Unknown request type, %u", rq_type);
exit(EXIT_FAILURE);
}
}
}
static int v5_endian_to_network(struct clog_request *rq)
{
int size;
struct dm_ulog_request *u_rq = &rq->u_rq;
size = sizeof(*rq) + u_rq->data_size;
u_rq->error = xlate32(u_rq->error);
u_rq->seq = xlate32(u_rq->seq);
rq->originator = xlate32(rq->originator);
v5_data_endian_switch(rq, 1);
u_rq->request_type = xlate32(u_rq->request_type);
u_rq->data_size = xlate32(u_rq->data_size);
return size;
}
int clog_request_to_network(struct clog_request *rq)
{
int r;
/* FIXME: Remove this safety check */
if (rq->u.version[0] != xlate64(rq->u.version[1])) {
LOG_ERROR("Programmer error: version[0] must be LE");
exit(EXIT_FAILURE);
}
/*
* Are we already running in the endian mode we send
* over the wire?
*/
if (rq->u.version[0] == rq->u.version[1])
return 0;
r = v5_endian_to_network(rq);
if (r < 0)
return r;
return 0;
}
static int v5_endian_from_network(struct clog_request *rq)
{
int size;
struct dm_ulog_request *u_rq = &rq->u_rq;
u_rq->error = xlate32(u_rq->error);
u_rq->seq = xlate32(u_rq->seq);
u_rq->request_type = xlate32(u_rq->request_type);
u_rq->data_size = xlate32(u_rq->data_size);
rq->originator = xlate32(rq->originator);
size = sizeof(*rq) + u_rq->data_size;
v5_data_endian_switch(rq, 0);
return size;
}
int clog_request_from_network(void *data, size_t data_len)
{
uint64_t *vp = data;
uint64_t version = xlate64(vp[0]);
struct clog_request *rq = data;
switch (version) {
case 5: /* Upstream */
if (version == vp[0])
return 0;
break;
case 4: /* RHEL 5.[45] */
case 3: /* RHEL 5.3 */
case 2: /* RHEL 5.2 */
/* FIXME: still need to account for payload */
if (data_len < (COMPAT_OFFSET + sizeof(*rq)))
return -ENOSPC;
rq = (struct clog_request *)((char *)data + COMPAT_OFFSET);
break;
default:
LOG_ERROR("Unable to process cluster message: "
"Incompatible version");
return -EINVAL;
}
v5_endian_from_network(rq);
return 0;
}