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