| /* |
| * 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; |
| } |