/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Carbonite Inc., 756 N Pastoria Ave
* Sunnyvale, CA 94085, or: http://www.zmanda.com
*/
#ifndef IPC_BINARY_H
#define IPC_BINARY_H
#include "amanda.h"
/* This module implements bidirectional message-oriented protocols which use
* binary framing, allowing it to transmit significant quantities of binary
* data efficiently, at the cost of not being easily human-readable.
*
* A protocol is a set of messages (identified by distinct small integers),
* each of which has a variable number of arguments, identified by other small,
* nonzero integers which are unique within a particular message. Protocols
* are assumed to be known completely by both sides of a conversation -- no
* allowance is made for communication between different "versions" of a
* protocol. Arguments are limited to 2^32-1 bytes (just under 4 MB), and each
* message is similarly limited to a total of 2^32-1 bytes, including all
* protocol framing. Users are advised to choose smaller sizes (e.g., 2MB) for
* data blocks transmitted within arguments.
*
* On the wire, each message consists of a 16-bit magic number, followed by a
* 16-bit command id, a 32-bit message length, and a 32-bit argument count:
*
* +--------|--------|--------|--------+
* | magic | command |
* +--------|--------|--------|--------+
* | length |
* +-----------------|-----------------+
* | arg count | ...
* +-----------------+
*
* All integers are in network byte order, and the message length includes the
* length of the header. This header is followed by a sequence of argument
* records, each of which consists of a 32-bit length followed by a 16-bit
* argument id and the corresponding data. String arguments do not include the
* NUL terminator byte. Note that the argument length does not include the
* argument header.
*
* +--------|--------|--------|--------+
* | length |
* +--------|--------|--------|--------+
* | argid | data |
* +-----------------------------------+
* | data... |
* +-----------------------------------+
*/
/* To define a protocol, begin by enumerating the relevant message identifiers
* and argument identifiers. Then write an initialization function on the
* following format: */
#if 0
enum {
MY_PROTO_BACKUP = 1,
MY_PROTO_RESTORE = 2,
};
enum {
MY_PROTO_HOSTNAME = 1,
MY_PROTO_DISK = 2,
MY_PROTO_LEVEL = 3,
MY_PROTO_FILENAMES = 4,
};
ipc_binary_proto_t *
my_proto(void)
{
static ipc_binary_proto_t *proto = NULL;
if (!proto) {
ipc_binary_cmd_t *cmd;
proto = ipc_binary_proto_new(0xFACE);
cmd = ipc_binary_proto_add_cmd(proto, MY_PROTO_BACKUP);
ipc_binary_cmd_add_arg(cmd, MY_PROTO_HOSTNAME, IPC_BINARY_STRING);
ipc_binary_cmd_add_arg(cmd, MY_PROTO_DISK, IPC_BINARY_STRING);
ipc_binary_cmd_add_arg(cmd, MY_PROTO_LEVEL, IPC_BINARY_STRING | IPC_BINARY_OPTIONAL);
cmd = ipc_binary_proto_add_cmd(proto, MY_PROTO_RESTORE);
ipc_binary_cmd_add_arg(cmd, MY_PROTO_HOSTNAME, IPC_BINARY_STRING);
ipc_binary_cmd_add_arg(cmd, MY_PROTO_DISK, IPC_BINARY_STRING);
ipc_binary_cmd_add_arg(cmd, MY_PROTO_FILENAMES, 0);
}
return proto;
}
#endif
/* Invoke my_proto in a thread-safe manner if necessary.
*
* Note that all of the constants are one-based. Internally, the module uses these values as
* array indices, so the constants should be assigned sequentially. Although C specifies that
* enumerations will auto-increment, it is best to add explicit values to avoid accidentally
* changing protocol values between revisions.
*/
/*
* Creating a new protocol
*/
/* opaque types */
typedef struct ipc_binary_proto_t ipc_binary_proto_t;
typedef struct ipc_binary_cmd_t ipc_binary_cmd_t;
/* Create a new, empty protocol object
*
* @param magic: magic number used to identify this protocol
* @returns: proto object
*/
ipc_binary_proto_t *ipc_binary_proto_new(
guint16 magic);
/* Create a new command in a protocol. The resulting object is only
* valid until the next call to this function for this proto.
*
* @param proto: the ipc_proto_t object to which to add this command
* @param id: the nonzero identifier for this command
* @returns: a new command object, already linked to PROTO
*/
ipc_binary_cmd_t *ipc_binary_proto_add_cmd(
ipc_binary_proto_t *proto,
guint16 id);
/* Flags for arguments */
/* This argument contains a string of non-null, printable characters and should
* be displayed in debugging messages. Arguments of this type will have a
* terminating NUL byte in the ipc_binary_message_t args array, for
* convenience, but will not count that byte in the length. */
#define IPC_BINARY_STRING (1 << 0)
/* This argument may be omitted */
#define IPC_BINARY_OPTIONAL (1 << 1)
/* Add an argument to a command
*
* @param cmd: the command object
* @param id: the argument identifier
* @param flags: bit flags for the command (see above)
*/
void ipc_binary_cmd_add_arg(
ipc_binary_cmd_t *cmd,
guint16 id,
guint8 flags);
/*
* Using a protocol
*/
typedef struct ipc_binary_buf_t {
gchar *buf;
gsize size;
gsize offset;
gsize length;
} ipc_binary_buf_t;
/* A channel represents a running protocol conversation, and encapsulates
* buffers for incoming and outgoing data, as well as a pointer to the protocol
* in use. */
typedef struct ipc_binary_channel_t {
/* protocol for this channel */
ipc_binary_proto_t *proto;
/* buffers for incoming and outgoing data */
ipc_binary_buf_t in, out;
} ipc_binary_channel_t;
/* Create a new channel, ready to send and receive messages.
*
* @param proto: protocol to use on this channel
* @returns: a new channel object
*/
ipc_binary_channel_t *ipc_binary_new_channel(
ipc_binary_proto_t *proto);
/* Free a channel completely.
*
* @param channel: the channel to free
*/
void ipc_binary_free_channel(
ipc_binary_channel_t *channel);
/* message format; use the argument id as an index into the args array. If
* DATA is NULL, then the argument wasn't present. */
typedef struct ipc_binary_message_t {
ipc_binary_channel_t *chan;
guint16 cmd_id;
ipc_binary_cmd_t *cmd;
guint16 n_args;
struct {
gsize len;
gpointer data;
} *args;
} ipc_binary_message_t;
/* Create a new, blank message which will later be sent.
*
* @param chan: the channel the message will be sent on
* @param cmd: the command id for this message
* @returns: new message struct
*/
ipc_binary_message_t *ipc_binary_new_message(
ipc_binary_channel_t *chan,
guint16 cmd_id);
/* Add an argument to a message. If the argument was defined with
* IPC_BINARY_STRING, then the size will be calculated using strlen. If
* TAKE_MEMORY is true, then this module takes ownership of the memory and will
* free it (with g_free) when the message is freed; otherwise, it will copy the
* data.
*
* @param msg: the message to change
* @param arg: the argument ID
* @param size: the argument size
* @param data: the argument data
* @param take_memory: take ownership of memory if TRUE
*/
void ipc_binary_add_arg(
ipc_binary_message_t *msg,
guint16 arg,
gsize size,
gpointer data,
gboolean take_memory);
/* Free a message structure (including all associated memory)
*
* @param msg: message to free
*/
void ipc_binary_free_message(
ipc_binary_message_t *msg);
/* Synchronous interface
*
* This interface assumes that communication takes place over file
* descriptors, and that blocking on network I/O is OK. It is much
* simpler than the asynchronous interface (below).
*/
/* Get the next message from the channel, optionally blocking until such a
* message is received. Returns NULL on EOF or on a protcol error. Errno is
* set to 0 for EOF, and contains an appropriate code for any other error.
*
* @param chan: channel on which to wait for a message
* @param fd: file descriptor to read from
* @returns: the message or NULL
*/
ipc_binary_message_t *ipc_binary_read_message(
ipc_binary_channel_t *chan,
int fd);
/* Send the given message, blocking until it is completely transmitted.
* This function automatically frees the message. Returns -1 on error,
* with errno set appropriately, or 0 on success.
*
* @param chan: channel on which to send the message
* @param fd: file descriptor to write to
* @param msg: message to send
*/
int ipc_binary_write_message(
ipc_binary_channel_t *chan,
int fd,
ipc_binary_message_t *msg);
/* Asynchronous interface
*
* This interface places data into and extracts data out of the buffers
* in a channel, but leaves it to the caller to handle sending and receiving
* data. */
/* Feed the given data into the channel. Call this when new data is
* available, and then check ipc_binary_poll_message(..) for any completed
* messages.
*
* @param chan: channel into which to feed data
* @param size: size of DATA
* @param data: the new data
*/
void ipc_binary_feed_data(
ipc_binary_channel_t *chan,
gsize size,
gpointer data);
/* Signal that some bytes have been transmitted and need not be kept in the
* outgoing buffer any longer
*
* @param chan: channel from which bytes were sent
* @param size: number of bytes transmitted
*/
void ipc_binary_data_transmitted(
ipc_binary_channel_t *chan,
gsize size);
/* Return the next complete incoming message in the channel, or NULL if there
* are no complete messages available. This also returns NULL on an invalid
* message, with errno set appropriately.
*
* @param chan: channel to poll
* @returns: message or NULL
*/
ipc_binary_message_t *ipc_binary_poll_message(
ipc_binary_channel_t *chan);
/* Queue the given message for later transmission. This function will free the
* message once it is in the outgoing data buffer. It is up to the caller to
* ensure that the
*
* @param chan: the channel to feed
* @param msg: the message to send
*/
void ipc_binary_queue_message(
ipc_binary_channel_t *chan,
ipc_binary_message_t *msg);
#endif /* IPC_BINARY_H */