From c7e3ec7dfd450b041cf2ec4db58d694cf21c6610 Mon Sep 17 00:00:00 2001 From: Renat Akhmerov Date: Tue, 1 Oct 2013 23:27:48 +0700 Subject: [PATCH] Add C demo agent * Contains sources for simple agent implementation in C that polls messages from the input queue and puts results into the result queue Change-Id: Ib38ed3fee65fbda31f94acb9e5e8e7e597ca3260 --- c-demo-agent/Makefile | 38 + c-demo-agent/Readme | 64 + c-demo-agent/amqp.h | 2302 ++++++++++++++++++++++++ c-demo-agent/amqp_framing.h | 630 +++++++ c-demo-agent/amqp_ssl_socket.h | 161 ++ c-demo-agent/amqp_tcp_socket.h | 69 + c-demo-agent/lcfg_static.c | 1641 +++++++++++++++++ c-demo-agent/lcfg_static.h | 146 ++ c-demo-agent/librabbitmq.a | Bin 0 -> 104484 bytes c-demo-agent/murano-agent.c | 239 +++ c-demo-agent/murano-agent.conf.example | 7 + c-demo-agent/producer.c | 128 ++ c-demo-agent/utils.c | 201 +++ c-demo-agent/utils.h | 13 + 14 files changed, 5639 insertions(+) create mode 100644 c-demo-agent/Makefile create mode 100644 c-demo-agent/Readme create mode 100644 c-demo-agent/amqp.h create mode 100644 c-demo-agent/amqp_framing.h create mode 100644 c-demo-agent/amqp_ssl_socket.h create mode 100644 c-demo-agent/amqp_tcp_socket.h create mode 100644 c-demo-agent/lcfg_static.c create mode 100644 c-demo-agent/lcfg_static.h create mode 100644 c-demo-agent/librabbitmq.a create mode 100644 c-demo-agent/murano-agent.c create mode 100644 c-demo-agent/murano-agent.conf.example create mode 100644 c-demo-agent/producer.c create mode 100644 c-demo-agent/utils.c create mode 100644 c-demo-agent/utils.h diff --git a/c-demo-agent/Makefile b/c-demo-agent/Makefile new file mode 100644 index 00000000..dade85ef --- /dev/null +++ b/c-demo-agent/Makefile @@ -0,0 +1,38 @@ +# Makefile for murano agent + +CFLAGS= -Wall + +default_target: all + + +all: agent-binary producer-binary + + +# NOTE: Use this if RabbitMQ needs to be linked in as a dynamic library +#agent-binary: murano-agent.o lcfg_static.o utils.o +# gcc -o murano-agent murano-agent.o lcfg_static.o utils.o -lrabbitmq -lrt + +agent-binary: murano-agent.o lcfg_static.o utils.o librabbitmq.a + gcc $(CFLAGS) -o murano-agent murano-agent.o lcfg_static.o utils.o -L. -lrabbitmq -lrt + +# NOTE: Use this if RabbitMQ needs to be linked in as a dynamic library +#producer-binary: producer.o utils.o +# gcc -o producer producer.o utils.o -lrabbitmq -lrt + +producer-binary: producer.o utils.o librabbitmq.a + gcc $(CFLAGS) -o producer producer.o utils.o -L. -lrabbitmq -lrt + +murano-agent.o: murano-agent.c + gcc $(CFLAGS) -c -I. -fPIC murano-agent.c + +producer.o: producer.c + gcc $(CFLAGS) -c -I. -fPIC producer.c + +lcfg_static.o: lcfg_static.c + gcc $(CFLAGS) -c -fPIC lcfg_static.c + +utils.o: utils.c + gcc $(CFLAGS) -c -I. -fPIC utils.c + +clean: + rm -f *.o murano-agent producer diff --git a/c-demo-agent/Readme b/c-demo-agent/Readme new file mode 100644 index 00000000..a23f6d84 --- /dev/null +++ b/c-demo-agent/Readme @@ -0,0 +1,64 @@ +This is a demo implementation of Murano agent. + +=== The scenario === + +Effectively the agent only connects to an AMQP queue +using properties from the configuration file, consumes +all the messages from the queue, logs them and puts +result messages into a result queue. Result messages +are just stubs signaling success back to the calling +application. + +=== Building binaries === + +Build process has been tested on Ubuntu 13.04 only for +x86_64 architecture. Note that the distribution contains +RabbitMQ client pre-built as a static library. It gets +linked with demo agent object files statically. It was +done to eliminate the need to build and install RabbitMQ +client library separately in case of using Ubuntu 64 bit. + +In order to build demo agent for embedded operating systems +based on BusyBox technology (such as Cirros) refer to +Buildroot toolkit (http://buildroot.uclibc.org/). + +To build agent binary: +make agent-binary + +To build test producer binary: +make prodcer-binary + +To build both binaries: +make + +To clean up the source directory: +make clean + +=== Usage === + +To run agent: +./murano-agent + +: +Refer to murano-agent.conf.example file to find out +what configuration properties are possible. + +: +Path to log file which agent writes the messages into. + +To run test message producer: +./producer + +: +RabbitMQ server host for the producer to connect to. + +: +RabbitMQ server port for the producer to connect to. + +: +Message rate at which the producer sends messages +(delay in seconds between messages). + +: +A number of messages the producer should send. + diff --git a/c-demo-agent/amqp.h b/c-demo-agent/amqp.h new file mode 100644 index 00000000..d3d15b54 --- /dev/null +++ b/c-demo-agent/amqp.h @@ -0,0 +1,2302 @@ +/* vim:set ft=c ts=2 sw=2 sts=2 et cindent: */ +/** \file */ +/* + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifndef AMQP_H +#define AMQP_H + +/** \cond HIDE_FROM_DOXYGEN */ + +#ifdef __cplusplus +#define AMQP_BEGIN_DECLS extern "C" { +#define AMQP_END_DECLS } +#else +#define AMQP_BEGIN_DECLS +#define AMQP_END_DECLS +#endif + + + +/* + * \internal + * Important API decorators: + * AMQP_PUBLIC_FUNCTION - a public API function + * AMQP_PUBLIC_VARIABLE - a public API external variable + * AMQP_CALL - calling convension (used on Win32) + */ + +#if defined(_WIN32) && defined(_MSC_VER) +# if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +# define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +# define AMQP_PUBLIC_VARIABLE __declspec(dllexport) extern +# else +# define AMQP_PUBLIC_FUNCTION +# if !defined(AMQP_STATIC) +# define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +# else +# define AMQP_PUBLIC_VARIABLE extern +# endif +# endif +# define AMQP_CALL __cdecl + +#elif defined(_WIN32) && defined(__BORLANDC__) +# if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +# define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +# define AMQP_PUBLIC_VARIABLE __declspec(dllexport) extern +# else +# define AMQP_PUBLIC_FUNCTION +# if !defined(AMQP_STATIC) +# define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +# else +# define AMQP_PUBLIC_VARIABLE extern +# endif +# endif +# define AMQP_CALL __cdecl + +#elif defined(_WIN32) && defined(__MINGW32__) +# if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +# define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +# define AMQP_PUBLIC_VARIABLE __declspec(dllexport) extern +# else +# define AMQP_PUBLIC_FUNCTION +# if !defined(AMQP_STATIC) +# define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +# else +# define AMQP_PUBLIC_VARIABLE extern +# endif +# endif +# define AMQP_CALL __cdecl + +#elif defined(_WIN32) && defined(__CYGWIN__) +# if defined(AMQP_BUILD) && !defined(AMQP_STATIC) +# define AMQP_PUBLIC_FUNCTION __declspec(dllexport) +# define AMQP_PUBLIC_VARIABLE __declspec(dllexport) +# else +# define AMQP_PUBLIC_FUNCTION +# if !defined(AMQP_STATIC) +# define AMQP_PUBLIC_VARIABLE __declspec(dllimport) extern +# else +# define AMQP_PUBLIC_VARIABLE extern +# endif +# endif +# define AMQP_CALL __cdecl + +#elif defined(__GNUC__) && __GNUC__ >= 4 +# include +# define AMQP_PUBLIC_FUNCTION \ + __attribute__ ((visibility ("default"))) +# define AMQP_PUBLIC_VARIABLE \ + __attribute__ ((visibility ("default"))) extern +# define AMQP_CALL +#else +# define AMQP_PUBLIC_FUNCTION +# define AMQP_PUBLIC_VARIABLE extern +# define AMQP_CALL +#endif + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define AMQP_DEPRECATED(function) \ + function __attribute__ ((__deprecated__)) +#elif defined(_MSC_VER) +# define AMQP_DEPRECATED(function) \ + __declspec(deprecated) function +#else +# define AMQP_DEPRECATED(function) +#endif + +/* Define ssize_t on Win32/64 platforms + See: http://lists.cs.uiuc.edu/pipermail/llvmdev/2010-April/030649.html for details + */ +#if !defined(_W64) +#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +#define _W64 __w64 +#else +#define _W64 +#endif +#endif + +#ifdef _MSC_VER +#ifdef _WIN64 +typedef __int64 ssize_t; +#else +typedef _W64 int ssize_t; +#endif +#endif + +/** \endcond */ + +#include +#include + +struct timeval; + +AMQP_BEGIN_DECLS + +/** + * \def AMQP_VERSION_MAJOR + * + * Major library version number compile-time constant + * + * The major version is incremented when backwards incompatible API changes + * are made. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ + +/** + * \def AMQP_VERSION_MINOR + * + * Minor library version number compile-time constant + * + * The minor version is incremented when new APIs are added. Existing APIs + * are left alone. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ + +/** + * \def AMQP_VERSION_PATCH + * + * Patch library version number compile-time constant + * + * The patch version is incremented when library code changes, but the API + * is not changed. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ + +/** + * \def AMQP_VERSION_IS_RELEASE + * + * Version constant set to 1 for tagged release, 0 otherwise + * + * NOTE: versions that are not tagged releases are not guaranteed to be API/ABI + * compatible with older releases, and may change commit-to-commit. + * + * \sa AMQP_VERSION, AMQP_VERSION_STRING + * + * \since v0.4.0 + */ +/* + * Developer note: when changing these, be sure to update SOVERSION constants + * in CMakeLists.txt and configure.ac + */ + +#define AMQP_VERSION_MAJOR 0 +#define AMQP_VERSION_MINOR 4 +#define AMQP_VERSION_PATCH 1 +#define AMQP_VERSION_IS_RELEASE 1 + + +/** + * \def AMQP_VERSION + * + * Packed version number + * + * AMQP_VERSION is a 4-byte unsigned integer with the most significant byte + * set to AMQP_VERSION_MAJOR, the second most significant byte set to + * AMQP_VERSION_MINOR, third most significant byte set to AMQP_VERSION_PATCH, + * and the lowest byte set to AMQP_VERSION_IS_RELEASE. + * + * For example version 2.3.4 which is released version would be encoded as + * 0x02030401 + * + * \sa amqp_version_number() AMQP_VERSION_MAJOR, AMQP_VERSION_MINOR, + * AMQP_VERSION_PATCH, AMQP_VERSION_IS_RELEASE + * + * \since v0.4.0 + */ +#define AMQP_VERSION ((AMQP_VERSION_MAJOR << 24) | \ + (AMQP_VERSION_MINOR << 16) | \ + (AMQP_VERSION_PATCH << 8) | \ + (AMQP_VERSION_IS_RELEASE)) + +/** \cond HIDE_FROM_DOXYGEN */ +#define AMQ_STRINGIFY(s) AMQ_STRINGIFY_HELPER(s) +#define AMQ_STRINGIFY_HELPER(s) #s + +#define AMQ_VERSION_STRING AMQ_STRINGIFY(AMQP_VERSION_MAJOR) "." \ + AMQ_STRINGIFY(AMQP_VERSION_MINOR) "." \ + AMQ_STRINGIFY(AMQP_VERSION_PATCH) +/** \endcond */ + +/** + * \def AMQP_VERSION_STRING + * + * Version string compile-time constant + * + * Non-released versions of the library will have "-pre" appended to the + * version string + * + * \sa amqp_version() + * + * \since v0.4.0 + */ +#if AMQP_VERSION_IS_RELEASE +# define AMQP_VERSION_STRING AMQ_VERSION_STRING +#else +# define AMQP_VERSION_STRING AMQ_VERSION_STRING "-pre" +#endif + + +/** + * Returns the rabbitmq-c version as a packed integer. + * + * See \ref AMQP_VERSION + * + * \return packed 32-bit integer representing version of library at runtime + * + * \sa AMQP_VERSION, amqp_version() + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +uint32_t +AMQP_CALL amqp_version_number(void); + +/** + * Returns the rabbitmq-c version as a string. + * + * See \ref AMQP_VERSION_STRING + * + * \return a statically allocated string describing the version of rabbitmq-c. + * + * \sa amqp_version_number(), AMQP_VERSION_STRING, AMQP_VERSION + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +char const * +AMQP_CALL amqp_version(void); + +/** + * \def AMQP_DEFAULT_FRAME_SIZE + * + * Default frame size (128Kb) + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.4.0 + */ +#define AMQP_DEFAULT_FRAME_SIZE 131072 + +/** + * \def AMQP_DEFAULT_MAX_CHANNELS + * + * Default maximum number of channels (0, no limit) + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.4.0 + */ +#define AMQP_DEFAULT_MAX_CHANNELS 0 + +/** + * \def AMQP_DEFAULT_HEARTBEAT + * + * Default heartbeat interval (0, heartbeat disabled) + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.4.0 + */ +#define AMQP_DEFAULT_HEARTBEAT 0 + +/** + * boolean type 0 = false, true otherwise + * + * \since v0.1 + */ +typedef int amqp_boolean_t; + +/** + * Method number + * + * \since v0.1 + */ +typedef uint32_t amqp_method_number_t; + +/** + * Bitmask for flags + * + * \since v0.1 + */ +typedef uint32_t amqp_flags_t; + +/** + * Channel type + * + * \since v0.1 + */ +typedef uint16_t amqp_channel_t; + +/** + * Buffer descriptor + * + * \since v0.1 + */ +typedef struct amqp_bytes_t_ { + size_t len; /**< length of the buffer in bytes */ + void *bytes; /**< pointer to the beginning of the buffer */ +} amqp_bytes_t; + +/** + * Decimal data type + * + * \since v0.1 + */ +typedef struct amqp_decimal_t_ { + uint8_t decimals; /**< the location of the decimal point */ + uint32_t value; /**< the value before the decimal point is applied */ +} amqp_decimal_t; + +/** + * AMQP field table + * + * An AMQP field table is a set of key-value pairs. + * A key is a UTF-8 encoded string up to 128 bytes long, and are not null + * terminated. + * A value can be one of several different datatypes. \sa amqp_field_value_kind_t + * + * \sa amqp_table_entry_t + * + * \since v0.1 + */ +typedef struct amqp_table_t_ { + int num_entries; /**< length of entries array */ + struct amqp_table_entry_t_ *entries; /**< an array of table entries */ +} amqp_table_t; + +/** + * An AMQP Field Array + * + * A repeated set of field values, all must be of the same type + * + * \since v0.1 + */ +typedef struct amqp_array_t_ { + int num_entries; /**< Number of entries in the table */ + struct amqp_field_value_t_ *entries; /**< linked list of field values */ +} amqp_array_t; + +/* + 0-9 0-9-1 Qpid/Rabbit Type Remarks +--------------------------------------------------------------------------- + t t Boolean + b b Signed 8-bit + B Unsigned 8-bit + U s Signed 16-bit (A1) + u Unsigned 16-bit + I I I Signed 32-bit + i Unsigned 32-bit + L l Signed 64-bit (B) + l Unsigned 64-bit + f f 32-bit float + d d 64-bit float + D D D Decimal + s Short string (A2) + S S S Long string + A Nested Array + T T T Timestamp (u64) + F F F Nested Table + V V V Void + x Byte array + +Remarks: + + A1, A2: Notice how the types **CONFLICT** here. In Qpid and Rabbit, + 's' means a signed 16-bit integer; in 0-9-1, it means a + short string. + + B: Notice how the signednesses **CONFLICT** here. In Qpid and Rabbit, + 'l' means a signed 64-bit integer; in 0-9-1, it means an unsigned + 64-bit integer. + +I'm going with the Qpid/Rabbit types, where there's a conflict, and +the 0-9-1 types otherwise. 0-8 is a subset of 0-9, which is a subset +of the other two, so this will work for both 0-8 and 0-9-1 branches of +the code. +*/ + +/** + * A field table value + * + * \since v0.1 + */ +typedef struct amqp_field_value_t_ { + uint8_t kind; /**< the type of the entry /sa amqp_field_value_kind_t */ + union { + amqp_boolean_t boolean; /**< boolean type AMQP_FIELD_KIND_BOOLEAN */ + int8_t i8; /**< int8_t type AMQP_FIELD_KIND_I8 */ + uint8_t u8; /**< uint8_t type AMQP_FIELD_KIND_U8 */ + int16_t i16; /**< int16_t type AMQP_FIELD_KIND_I16 */ + uint16_t u16; /**< uint16_t type AMQP_FIELD_KIND_U16 */ + int32_t i32; /**< int32_t type AMQP_FIELD_KIND_I32 */ + uint32_t u32; /**< uint32_t type AMQP_FIELD_KIND_U32 */ + int64_t i64; /**< int64_t type AMQP_FIELD_KIND_I64 */ + uint64_t u64; /**< uint64_t type AMQP_FIELD_KIND_U64, AMQP_FIELD_KIND_TIMESTAMP */ + float f32; /**< float type AMQP_FIELD_KIND_F32 */ + double f64; /**< double type AMQP_FIELD_KIND_F64 */ + amqp_decimal_t decimal; /**< amqp_decimal_t AMQP_FIELD_KIND_DECIMAL */ + amqp_bytes_t bytes; /**< amqp_bytes_t type AMQP_FIELD_KIND_UTF8, AMQP_FIELD_KIND_BYTES */ + amqp_table_t table; /**< amqp_table_t type AMQP_FIELD_KIND_TABLE */ + amqp_array_t array; /**< amqp_array_t type AMQP_FIELD_KIND_ARRAY */ + } value; /**< a union of the value */ +} amqp_field_value_t; + +/** + * An entry in a field-table + * + * \sa amqp_table_encode(), amqp_table_decode(), amqp_table_clone() + * + * \since v0.1 + */ +typedef struct amqp_table_entry_t_ { + amqp_bytes_t key; /**< the table entry key. Its a null-terminated UTF-8 string, + * with a maximum size of 128 bytes */ + amqp_field_value_t value; /**< the table entry values */ +} amqp_table_entry_t; + +/** + * Field value types + * + * \since v0.1 + */ +typedef enum { + AMQP_FIELD_KIND_BOOLEAN = 't', /**< boolean type. 0 = false, 1 = true @see amqp_boolean_t */ + AMQP_FIELD_KIND_I8 = 'b', /**< 8-bit signed integer, datatype: int8_t */ + AMQP_FIELD_KIND_U8 = 'B', /**< 8-bit unsigned integer, datatype: uint8_t */ + AMQP_FIELD_KIND_I16 = 's', /**< 16-bit signed integer, datatype: int16_t */ + AMQP_FIELD_KIND_U16 = 'u', /**< 16-bit unsigned integer, datatype: uint16_t */ + AMQP_FIELD_KIND_I32 = 'I', /**< 32-bit signed integer, datatype: int32_t */ + AMQP_FIELD_KIND_U32 = 'i', /**< 32-bit unsigned integer, datatype: uint32_t */ + AMQP_FIELD_KIND_I64 = 'l', /**< 64-bit signed integer, datatype: int64_t */ + AMQP_FIELD_KIND_U64 = 'L', /**< 64-bit unsigned integer, datatype: uint64_t */ + AMQP_FIELD_KIND_F32 = 'f', /**< single-precision floating point value, datatype: float */ + AMQP_FIELD_KIND_F64 = 'd', /**< double-precision floating point value, datatype: double */ + AMQP_FIELD_KIND_DECIMAL = 'D', /**< amqp-decimal value, datatype: amqp_decimal_t */ + AMQP_FIELD_KIND_UTF8 = 'S', /**< UTF-8 null-terminated character string, datatype: amqp_bytes_t */ + AMQP_FIELD_KIND_ARRAY = 'A', /**< field array (repeated values of another datatype. datatype: amqp_array_t */ + AMQP_FIELD_KIND_TIMESTAMP = 'T',/**< 64-bit timestamp. datatype uint64_t */ + AMQP_FIELD_KIND_TABLE = 'F', /**< field table. encapsulates a table inside a table entry. datatype: amqp_table_t */ + AMQP_FIELD_KIND_VOID = 'V', /**< empty entry */ + AMQP_FIELD_KIND_BYTES = 'x' /**< unformatted byte string, datatype: amqp_bytes_t */ +} amqp_field_value_kind_t; + +/** + * A list of allocation blocks + * + * \since v0.1 + */ +typedef struct amqp_pool_blocklist_t_ { + int num_blocks; /**< Number of blocks in the block list */ + void **blocklist; /**< Array of memory blocks */ +} amqp_pool_blocklist_t; + +/** + * A memory pool + * + * \since v0.1 + */ +typedef struct amqp_pool_t_ { + size_t pagesize; /**< the size of the page in bytes. + * allocations less than or equal to this size are + * allocated in the pages block list + * allocations greater than this are allocated in their + * own block in the large_blocks block list */ + + amqp_pool_blocklist_t pages; /**< blocks that are the size of pagesize */ + amqp_pool_blocklist_t large_blocks; /**< allocations larger than the pagesize */ + + int next_page; /**< an index to the next unused page block */ + char *alloc_block; /**< pointer to the current allocation block */ + size_t alloc_used; /**< number of bytes in the current allocation block that has been used */ +} amqp_pool_t; + +/** + * An amqp method + * + * \since v0.1 + */ +typedef struct amqp_method_t_ { + amqp_method_number_t id; /**< the method id number */ + void *decoded; /**< pointer to the decoded method, + * cast to the appropriate type to use */ +} amqp_method_t; + +/** + * An AMQP frame + * + * \since v0.1 + */ +typedef struct amqp_frame_t_ { + uint8_t frame_type; /**< frame type. The types: + * - AMQP_FRAME_METHOD - use the method union member + * - AMQP_FRAME_HEADER - use the properties union member + * - AMQP_FRAME_BODY - use the body_fragment union member + */ + amqp_channel_t channel; /**< the channel the frame was received on */ + union { + amqp_method_t method; /**< a method, use if frame_type == AMQP_FRAME_METHOD */ + struct { + uint16_t class_id; /**< the class for the properties */ + uint64_t body_size; /**< size of the body in bytes */ + void *decoded; /**< the decoded properties */ + amqp_bytes_t raw; /**< amqp-encoded properties structure */ + } properties; /**< message header, a.k.a., properties, + use if frame_type == AMQP_FRAME_HEADER */ + amqp_bytes_t body_fragment; /**< a body fragment, use if frame_type == AMQP_FRAME_BODY */ + struct { + uint8_t transport_high; /**< @internal first byte of handshake */ + uint8_t transport_low; /**< @internal second byte of handshake */ + uint8_t protocol_version_major; /**< @internal third byte of handshake */ + uint8_t protocol_version_minor; /**< @internal fourth byte of handshake */ + } protocol_header; /**< Used only when doing the initial handshake with the broker, + don't use otherwise */ + } payload; /**< the payload of the frame */ +} amqp_frame_t; + +/** + * Response type + * + * \since v0.1 + */ +typedef enum amqp_response_type_enum_ { + AMQP_RESPONSE_NONE = 0, /**< the library got an EOF from the socket */ + AMQP_RESPONSE_NORMAL, /**< response normal, the RPC completed successfully */ + AMQP_RESPONSE_LIBRARY_EXCEPTION,/**< library error, an error occurred in the library, examine the library_error */ + AMQP_RESPONSE_SERVER_EXCEPTION /**< server exception, the broker returned an error, check replay */ +} amqp_response_type_enum; + +/** + * Reply from a RPC method on the broker + * + * \since v0.1 + */ +typedef struct amqp_rpc_reply_t_ { + amqp_response_type_enum reply_type; /**< the reply type: + * - AMQP_RESPONSE_NORMAL - the RPC completed successfully + * - AMQP_RESPONSE_SERVER_EXCEPTION - the broker returned + * an exception, check the reply field + * - AMQP_RESPONSE_LIBRARY_EXCEPTION - the library + * encountered an error, check the library_error field + */ + amqp_method_t reply; /**< in case of AMQP_RESPONSE_SERVER_EXCEPTION this + * field will be set to the method returned from the broker */ + int library_error; /**< in case of AMQP_RESPONSE_LIBRARY_EXCEPTION this + * field will be set to an error code. An error + * string can be retrieved using amqp_error_string */ +} amqp_rpc_reply_t; + +/** + * SASL method type + * + * \since v0.1 + */ +typedef enum amqp_sasl_method_enum_ { + AMQP_SASL_METHOD_PLAIN = 0 /**< the PLAIN SASL method for authentication to the broker */ +} amqp_sasl_method_enum; + +/** + * connection state object + * + * \since v0.1 + */ +typedef struct amqp_connection_state_t_ *amqp_connection_state_t; + +/** + * Socket object + * + * \since v0.4.0 + */ +typedef struct amqp_socket_t_ amqp_socket_t; + +/** + * Status codes + * + * \since v0.4.0 + */ +typedef enum amqp_status_enum_ +{ + AMQP_STATUS_OK = 0x0, /**< Operation successful */ + AMQP_STATUS_NO_MEMORY = -0x0001, /**< Memory allocation + failed */ + AMQP_STATUS_BAD_AMQP_DATA = -0x0002, /**< Incorrect or corrupt + data was received from + the broker. This is a + protocol error. */ + AMQP_STATUS_UNKNOWN_CLASS = -0x0003, /**< An unknown AMQP class + was received. This is + a protocol error. */ + AMQP_STATUS_UNKNOWN_METHOD = -0x0004, /**< An unknown AMQP method + was received. This is + a protocol error. */ + AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED= -0x0005, /**< Unable to resolve the + * hostname */ + AMQP_STATUS_INCOMPATIBLE_AMQP_VERSION = -0x0006, /**< The broker advertised + an incompaible AMQP + version */ + AMQP_STATUS_CONNECTION_CLOSED = -0x0007, /**< The connection to the + broker has been closed + */ + AMQP_STATUS_BAD_URL = -0x0008, /**< malformed AMQP URL */ + AMQP_STATUS_SOCKET_ERROR = -0x0009, /**< A socket error + occurred */ + AMQP_STATUS_INVALID_PARAMETER = -0x000A, /**< An invalid parameter + was passed into the + function */ + AMQP_STATUS_TABLE_TOO_BIG = -0x000B, /**< The amqp_table_t object + cannot be serialized + because the output + buffer is too small */ + AMQP_STATUS_WRONG_METHOD = -0x000C, /**< The wrong method was + received */ + AMQP_STATUS_TIMEOUT = -0x000D, /**< Operation timed out */ + AMQP_STATUS_TIMER_FAILURE = -0x000E, /**< The underlying system + timer facility failed */ + AMQP_STATUS_HEARTBEAT_TIMEOUT = -0x000F, /**< Timed out waiting for + heartbeat */ + AMQP_STATUS_UNEXPECTED_STATE = -0x0010, /**< Unexpected protocol + state */ + + AMQP_STATUS_TCP_ERROR = -0x0100, /**< A generic TCP error + occurred */ + AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR = -0x0101, /**< An error occurred trying + to initialize the + socket library*/ + + AMQP_STATUS_SSL_ERROR = -0x0200, /**< A generic SSL error + occurred. */ + AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED= -0x0201, /**< SSL validation of + hostname against + peer certificate + failed */ + AMQP_STATUS_SSL_PEER_VERIFY_FAILED = -0x0202, /**< SSL validation of peer + certificate failed. */ + AMQP_STATUS_SSL_CONNECTION_FAILED = -0x0203 /**< SSL handshake failed. */ +} amqp_status_enum; + +AMQP_END_DECLS + +#include + +AMQP_BEGIN_DECLS + +/** + * Empty bytes structure + * + * \since v0.2 + */ +AMQP_PUBLIC_VARIABLE const amqp_bytes_t amqp_empty_bytes; + +/** + * Empty table structure + * + * \since v0.2 + */ +AMQP_PUBLIC_VARIABLE const amqp_table_t amqp_empty_table; + +/** + * Empty table array structure + * + * \since v0.2 + */ +AMQP_PUBLIC_VARIABLE const amqp_array_t amqp_empty_array; + +/* Compatibility macros for the above, to avoid the need to update + code written against earlier versions of librabbitmq. */ + +/** + * \def AMQP_EMPTY_BYTES + * + * Deprecated, use \ref amqp_empty_bytes instead + * + * \deprecated use \ref amqp_empty_bytes instead + * + * \since v0.1 + */ +#define AMQP_EMPTY_BYTES amqp_empty_bytes + +/** + * \def AMQP_EMPTY_TABLE + * + * Deprecated, use \ref amqp_empty_table instead + * + * \deprecated use \ref amqp_empty_table instead + * + * \since v0.1 + */ +#define AMQP_EMPTY_TABLE amqp_empty_table + +/** + * \def AMQP_EMPTY_ARRAY + * + * Deprecated, use \ref amqp_empty_array instead + * + * \deprecated use \ref amqp_empty_array instead + * + * \since v0.1 + */ +#define AMQP_EMPTY_ARRAY amqp_empty_array + +/** + * Initializes an amqp_pool_t memory allocation pool for use + * + * Readies an allocation pool for use. An amqp_pool_t + * must be initialized before use + * + * \param [in] pool the amqp_pool_t structure to initialize. + * Calling this function on a pool a pool that has + * already been initialized will result in undefined + * behavior + * \param [in] pagesize the unit size that the pool will allocate + * memory chunks in. Anything allocated against the pool + * with a requested size will be carved out of a block + * this size. Allocations larger than this will be + * allocated individually + * + * \sa recycle_amqp_pool(), empty_amqp_pool(), amqp_pool_alloc(), + * amqp_pool_alloc_bytes(), amqp_pool_t + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL init_amqp_pool(amqp_pool_t *pool, size_t pagesize); + +/** + * Recycles an amqp_pool_t memory allocation pool + * + * Recycles the space allocate by the pool + * + * This invalidates all allocations made against the pool before this call is + * made, any use of any allocations made before recycle_amqp_pool() is called + * will result in undefined behavior. + * + * Note: this may or may not release memory, to force memory to be released + * call empty_amqp_pool(). + * + * \param [in] pool the amqp_pool_t to recycle + * + * \sa recycle_amqp_pool(), empty_amqp_pool(), amqp_pool_alloc(), + * amqp_pool_alloc_bytes() + * + * \since v0.1 + * + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL recycle_amqp_pool(amqp_pool_t *pool); + +/** + * Empties an amqp memory pool + * + * Releases all memory associated with an allocation pool + * + * \param [in] pool the amqp_pool_t to empty + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL empty_amqp_pool(amqp_pool_t *pool); + +/** + * Allocates a block of memory from an amqp_pool_t memory pool + * + * Memory will be aligned on a 8-byte boundary. If a 0-length allocation is + * requested, a NULL pointer will be returned. + * + * \param [in] pool the allocation pool to allocate the memory from + * \param [in] amount the size of the allocation in bytes. + * \return a pointer to the memory block, or NULL if the allocation cannot + * be satisfied. + * + * \sa init_amqp_pool(), recycle_amqp_pool(), empty_amqp_pool(), + * amqp_pool_alloc_bytes() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void * +AMQP_CALL amqp_pool_alloc(amqp_pool_t *pool, size_t amount); + +/** + * Allocates a block of memory from an amqp_pool_t to an amqp_bytes_t + * + * Memory will be aligned on a 8-byte boundary. If a 0-length allocation is + * requested, output.bytes = NULL. + * + * \param [in] pool the allocation pool to allocate the memory from + * \param [in] amount the size of the allocation in bytes + * \param [in] output the location to store the pointer. On success + * output.bytes will be set to the beginning of the buffer + * output.len will be set to amount + * On error output.bytes will be set to NULL and output.len + * set to 0 + * + * \sa init_amqp_pool(), recycle_amqp_pool(), empty_amqp_pool(), + * amqp_pool_alloc() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_pool_alloc_bytes(amqp_pool_t *pool, size_t amount, amqp_bytes_t *output); + +/** + * Wraps a c string in an amqp_bytes_t + * + * Takes a string, calculates its length and creates an + * amqp_bytes_t that points to it. The string is not duplicated. + * + * For a given input cstr, The amqp_bytes_t output.bytes is the + * same as cstr, output.len is the length of the string not including + * the \0 terminator + * + * This function uses strlen() internally so cstr must be properly + * terminated + * + * \param [in] cstr the c string to wrap + * \return an amqp_bytes_t that describes the string + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_bytes_t +AMQP_CALL amqp_cstring_bytes(char const *cstr); + +/** + * Duplicates an amqp_bytes_t buffer. + * + * The buffer is cloned and the contents copied. + * + * The memory associated with the output is allocated + * with amqp_bytes_malloc() and should be freed with + * amqp_bytes_free() + * + * \param [in] src + * \return a clone of the src + * + * \sa amqp_bytes_free(), amqp_bytes_malloc() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_bytes_t +AMQP_CALL amqp_bytes_malloc_dup(amqp_bytes_t src); + +/** + * Allocates a amqp_bytes_t buffer + * + * Creates an amqp_bytes_t buffer of the specified amount, the buffer should be + * freed using amqp_bytes_free() + * + * \param [in] amount the size of the buffer in bytes + * \returns an amqp_bytes_t with amount bytes allocated. + * output.bytes will be set to NULL on error + * + * \sa amqp_bytes_free(), amqp_bytes_malloc_dup() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_bytes_t +AMQP_CALL amqp_bytes_malloc(size_t amount); + +/** + * Frees an amqp_bytes_t buffer + * + * Frees a buffer allocated with amqp_bytes_malloc() or amqp_bytes_malloc_dup() + * + * Calling amqp_bytes_free on buffers not allocated with one + * of those two functions will result in undefined behavior + * + * \param [in] bytes the buffer to free + * + * \sa amqp_bytes_malloc(), amqp_bytes_malloc_dup() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_bytes_free(amqp_bytes_t bytes); + +/** + * Allocate and initialize a new amqp_connection_state_t object + * + * amqp_connection_state_t objects created with this function + * should be freed with amqp_destroy_connection() + * + * \returns an opaque pointer on success, NULL or 0 on failure. + * + * \sa amqp_destroy_connection() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_connection_state_t +AMQP_CALL amqp_new_connection(void); + +/** + * Get the underlying socket descriptor for the connection + * + * \warning Use the socket returned from this function carefully, incorrect use + * of the socket outside of the library will lead to undefined behavior. + * Additionally rabbitmq-c may use the socket differently version-to-version, + * what may work in one version, may break in the next version. Be sure to + * throughly test any applications that use the socket returned by this + * function especially when using a newer version of rabbitmq-c + * + * \param [in] state the connection object + * \returns the socket descriptor if one has been set, -1 otherwise + * + * \sa amqp_tcp_socket_new(), amqp_ssl_socket_new(), amqp_socket_open() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_get_sockfd(amqp_connection_state_t state); + + +/** + * Deprecated, use amqp_tcp_socket_new() or amqp_ssl_socket_new() + * + * \deprecated Use amqp_tcp_socket_new() or amqp_ssl_socket_new() + * + * Sets the socket descriptor associated with the connection. The socket + * should be connected to a broker, and should not be read to or written from + * before calling this function. A socket descriptor can be created and opened + * using amqp_open_socket() + * + * \param [in] state the connection object + * \param [in] sockfd the socket + * + * \sa amqp_open_socket(), amqp_tcp_socket_new(), amqp_ssl_socket_new() + * + * \since v0.1 + */ +AMQP_DEPRECATED( + AMQP_PUBLIC_FUNCTION + void + AMQP_CALL amqp_set_sockfd(amqp_connection_state_t state, int sockfd) +); + + +/** + * Tune client side parameters + * + * \warning This function may call abort() if the connection is in a certain + * state. As such it should probably not be called code outside the library. + * connection parameters should be specified when calling amqp_login() or + * amqp_login_with_properties() + * + * This function changes channel_max, frame_max, and heartbeat parameters, on + * the client side only. It does not try to renegotiate these parameters with + * the broker. Using this function will lead to unexpected results. + * + * \param [in] state the connection object + * \param [in] channel_max the maximum number of channels. + * The largest this can be is 65535 + * \param [in] frame_max the maximum size of an frame. + * The smallest this can be is 4096 + * The largest this can be is 2147483647 + * Unless you know what you're doing the recommended + * size is 131072 or 128KB + * \param [in] heartbeat the number of seconds between heartbeats + * + * \return AMQP_STATUS_OK on success, an amqp_status_enum value otherwise. + * Possible error codes include: + * - AMQP_STATUS_NO_MEMORY memory allocation failed. + * - AMQP_STATUS_TIMER_FAILURE the underlying system timer indicated it + * failed. + * + * \sa amqp_login(), amqp_login_with_properties() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_tune_connection(amqp_connection_state_t state, + int channel_max, + int frame_max, + int heartbeat); + +/** + * Get the maximum number of channels the connection can handle + * + * The maximum number of channels is set when connection negotiation takes + * place in amqp_login() or amqp_login_with_properties(). + * + * \param [in] state the connection object + * \return the maximum number of channels. 0 if there is no limit + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_get_channel_max(amqp_connection_state_t state); + +/** + * Destroys an amqp_connection_state_t object + * + * Destroys a amqp_connection_state_t object that was created with + * amqp_new_connection(). If the connection with the broker is open, it will be + * implicitly closed with a reply code of 200 (success). Any memory that + * would be freed with amqp_maybe_release_buffers() or + * amqp_maybe_release_buffers_on_channel() will be freed, and use of that + * memory will caused undefined behavior. + * + * \param [in] state the connection object + * \return AMQP_STATUS_OK on success. amqp_status_enum value failure + * + * \sa amqp_new_connection() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_destroy_connection(amqp_connection_state_t state); + +/** + * Process incoming data + * + * \warning This is a low-level function intended for those who want to + * have greater control over input and output over the socket from the + * broker. Correctly using this function requires in-depth knowledge of AMQP + * and rabbitmq-c. + * + * For a given buffer of data received from the broker, decode the first + * frame in the buffer. If more than one frame is contained in the input buffer + * the return value will be less than the received_data size, the caller should + * adjust received_data buffer descriptor to point to the beginning of the + * buffer + the return value. + * + * \param [in] state the connection object + * \param [in] received_data a buffer of data received from the broker. The + * function will return the number of bytes of the buffer it used. The + * function copies these bytes to an internal buffer: this part of the buffer + * may be reused after this function successfully completes. + * \param [in,out] decoded_frame caller should pass in a pointer to an + * amqp_frame_t struct. If there is enough data in received_data for a + * complete frame, decoded_frame->frame_type will be set to something OTHER + * than 0. decoded_frame may contain members pointing to memory owned by + * the state object. This memory can be recycled with amqp_maybe_release_buffers() + * or amqp_maybe_release_buffers_on_channel() + * \return number of bytes consumed from received_data or 0 if a 0-length + * buffer was passed. A negative return value indicates failure. Possible errors: + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely in + * an indeterminate state making recovery unlikely. Client should note the error + * and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_handle_input(amqp_connection_state_t state, + amqp_bytes_t received_data, + amqp_frame_t *decoded_frame); + +/** + * Check to see if connection memory can be released + * + * \deprecated This function is deprecated in favor of + * amqp_maybe_release_buffers() or amqp_maybe_release_buffers_on_channel() + * + * Checks the state of an amqp_connection_state_t object to see if + * amqp_release_buffers() can be called successfully. + * + * \param [in] state the connection object + * \returns TRUE if the buffers can be released FALSE otherwise + * + * \sa amqp_release_buffers() amqp_maybe_release_buffers() + * amqp_maybe_release_buffers_on_channel() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t +AMQP_CALL amqp_release_buffers_ok(amqp_connection_state_t state); + +/** + * Release amqp_connection_state_t owned memory + * + * \deprecated This function is deprecated in favor of + * amqp_maybe_release_buffers() or amqp_maybe_release_buffers_on_channel() + * + * \warning caller should ensure amqp_release_buffers_ok() returns true before + * calling this function. Failure to do so may result in abort() being called. + * + * Release memory owned by the amqp_connection_state_t for reuse by the + * library. Use of any memory returned by the library before this function is + * called will result in undefined behavior. + * + * \note internally rabbitmq-c tries to reuse memory when possible. As a result + * its possible calling this function may not have a noticeable effect on + * memory usage. + * + * \param [in] state the connection object + * + * \sa amqp_release_buffers_ok() amqp_maybe_release_buffers() + * amqp_maybe_release_buffers_on_channel() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_release_buffers(amqp_connection_state_t state); + +/** + * Release amqp_connection_state_t owned memory + * + * Release memory owned by the amqp_connection_state_t object related to any + * channel, allowing reuse by the library. Use of any memory returned by the + * library before this function is called with result in undefined behavior. + * + * \note internally rabbitmq-c tries to reuse memory when possible. As a result + * its possible calling this function may not have a noticeable effect on + * memory usage. + * + * \param [in] state the connection object + * + * \sa amqp_maybe_release_buffers_on_channel() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_maybe_release_buffers(amqp_connection_state_t state); + +/** + * Release amqp_connection_state_t owned memory related to a channel + * + * Release memory owned by the amqp_connection_state_t object related to the + * specified channel, allowing reuse by the library. Use of any memory returned + * the library for a specific channel will result in undefined behavior. + * + * \note internally rabbitmq-c tries to reuse memory when possible. As a result + * its possible calling this function may not have a noticeable effect on + * memory usage. + * + * \param [in] state the connection object + * \param [in] channel the channel specifier for which memory should be + * released. Note that the library does not care about the state of the + * channel when calling this function + * + * \sa amqp_maybe_release_buffers() + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_maybe_release_buffers_on_channel(amqp_connection_state_t state, + amqp_channel_t channel); + +/** + * Send a frame to the broker + * + * \param [in] state the connection object + * \param [in] frame the frame to send to the broker + * \return AMQP_STATUS_OK on success, an amqp_status_enum value on error. + * Possible error codes: + * - AMQP_STATUS_BAD_AMQP_DATA the serialized form of the method or + * properties was too large to fit in a single AMQP frame, or the + * method contains an invalid value. The frame was not sent. + * - AMQP_STATUS_TABLE_TOO_BIG the serialized form of an amqp_table_t is + * too large to fit in a single AMQP frame. Frame was not sent. + * - AMQP_STATUS_UNKNOWN_METHOD an invalid method type was passed in + * - AMQP_STATUS_UNKNOWN_CLASS an invalid properties type was passed in + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. The frame + * was sent + * - AMQP_STATUS_SOCKET_ERROR + * - AMQP_STATUS_SSL_ERROR + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_send_frame(amqp_connection_state_t state, amqp_frame_t const *frame); + +/** + * Compare two table entries + * + * Works just like strcmp(), comparing two the table keys, datatype, then values + * + * \param [in] entry1 the entry on the left + * \param [in] entry2 the entry on the right + * \return 0 if entries are equal, 0 < if left is greater, 0 > if right is greater + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_table_entry_cmp(void const *entry1, void const *entry2); + +/** + * Open a socket to a remote host + * + * \deprecated This function is deprecated in favor of amqp_socket_open() + * + * Looks up the hostname, then attempts to open a socket to the host using + * the specified portnumber. It also sets various options on the socket to + * improve performance and correctness. + * + * \param [in] hostname this can be a hostname or IP address. + * Both IPv4 and IPv6 are acceptable + * \param [in] portnumber the port to connect on. RabbitMQ brokers + * listen on port 5672, and 5671 for SSL + * \return a positive value indicates success and is the sockfd. A negative + * value (see amqp_status_enum)is returned on failure. Possible error codes: + * - AMQP_STATUS_TCP_SOCKETLIB_INIT_ERROR Initialization of underlying socket + * library failed. + * - AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED hostname lookup failed. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. errno or WSAGetLastError() + * may return more useful information. + * + * \note IPv6 support was added in v0.3 + * + * \sa amqp_socket_open() amqp_set_sockfd() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_open_socket(char const *hostname, int portnumber); + +/** + * Send initial AMQP header to the broker + * + * \warning this is a low level function intended for those who want to + * interact with the broker at a very low level. Use of this function without + * understanding what it does will result in AMQP protocol errors. + * + * This function sends the AMQP protocol header to the broker. + * + * \param [in] state the connection object + * \return AMQP_STATUS_OK on success, a negative value on failure. Possible + * error codes: + * - AMQP_STATUS_CONNECTION_CLOSED the connection to the broker was closed. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. It is likely the + * underlying socket has been closed. errno or WSAGetLastError() may provide + * further information. + * - AMQP_STATUS_SSL_ERROR a SSL error occurred. The connection to the broker + * was closed. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_send_header(amqp_connection_state_t state); + +/** + * Checks to see if there are any incoming frames ready to be read + * + * Checks to see if there are any amqp_frame_t objects buffered by the + * amqp_connection_state_t object. Having one or more frames buffered means + * that amqp_simple_wait_frame() or amqp_simple_wait_frame_noblock() will + * return a frame without potentially blocking on a read() call. + * + * \param [in] state the connection object + * \return TRUE if there are frames enqueued, FALSE otherwise + * + * \sa amqp_simple_wait_frame() amqp_simple_wait_frame_noblock() + * amqp_data_in_buffer() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t +AMQP_CALL amqp_frames_enqueued(amqp_connection_state_t state); + +/** + * Read a single amqp_frame_t + * + * Waits for the next amqp_frame_t frame to be read from the broker. + * This function has the potential to block for a long time in the case of + * waiting for a basic.deliver method frame from the broker. + * + * The library may buffer frames. When an amqp_connection_state_t object + * has frames buffered calling amqp_simple_wait_frame() will return an + * amqp_frame_t without entering a blocking read(). You can test to see if + * an amqp_connection_state_t object has frames buffered by calling the + * amqp_frames_enqueued() function. + * + * The library has a socket read buffer. When there is data in an + * amqp_connection_state_t read buffer, amqp_simple_wait_frame() may return an + * amqp_frame_t without entering a blocking read(). You can test to see if an + * amqp_connection_state_t object has data in its read buffer by calling the + * amqp_data_in_buffer() function. + * + * \param [in] state the connection object + * \param [out] decoded_frame the frame + * \return AMQP_STATUS_OK on success, an amqp_status_enum value + * is returned otherwise. Possible errors include: + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely in + * an indeterminate state making recovery unlikely. Client should note the error + * and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * - AMQP_STATUS_HEARTBEAT_TIMEOUT timed out while waiting for heartbeat + * from the broker. The connection has been closed. + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. The connection has + * been closed + * - AMQP_STATUS_SSL_ERROR a SSL socket error occurred. The connection has + * been closed. + * + * \sa amqp_simple_wait_frame_noblock() amqp_frames_enqueued() + * amqp_data_in_buffer() + * + * \note as of v0.4.0 this function will no longer return heartbeat frames + * when enabled by specifying a non-zero heartbeat value in amqp_login(). + * Heartbeating is handled internally by the library. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_simple_wait_frame(amqp_connection_state_t state, + amqp_frame_t *decoded_frame); + +/** + * Read a single amqp_frame_t with a timeout. + * + * Waits for the next amqp_frame_t frame to be read from the broker, up to + * a timespan specified by tv. The function will return AMQP_STATUS_TIMEOUT + * if the timeout is reached. The tv value is not modified by the function. + * + * If a 0 timeval is specified, the function behaves as if its non-blocking: it + * will test to see if a frame can be read from the broker, and return immediately. + * + * If NULL is passed in for tv, the function will behave like + * amqp_simple_wait_frame() and block until a frame is received from the broker + * + * The library may buffer frames. When an amqp_connection_state_t object + * has frames buffered calling amqp_simple_wait_frame_noblock() will return an + * amqp_frame_t without entering a blocking read(). You can test to see if an + * amqp_connection_state_t object has frames buffered by calling the + * amqp_frames_enqueued() function. + * + * The library has a socket read buffer. When there is data in an + * amqp_connection_state_t read buffer, amqp_simple_wait_frame_noblock() may return + * an amqp_frame_t without entering a blocking read(). You can test to see if an + * amqp_connection_state_t object has data in its read buffer by calling the + * amqp_data_in_buffer() function. + * + * \note This function does not return heartbeat frames. When enabled, heartbeating + * is handed internally internally by the library + * + * \param [in,out] state the connection object + * \param [out] decoded_frame the frame + * \param [in] tv the maximum time to wait for a frame to be read. Setting + * tv->tv_sec = 0 and tv->tv_usec = 0 will do a non-blocking read. Specifying + * NULL for tv will make the function block until a frame is read. + * \return AMQP_STATUS_OK on success. An amqp_status_enum value is returned + * otherwise. Possible errors include: + * - AMQP_STATUS_TIMEOUT the timeout was reached while waiting for a frame + * from the broker. + * - AMQP_STATUS_INVALID_PARAMETER the tv parameter contains an invalid value. + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely in + * an indeterminate state making recovery unlikely. Client should note the error + * and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * - AMQP_STATUS_HEARTBEAT_TIMEOUT timed out while waiting for heartbeat + * from the broker. The connection has been closed. + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. The connection has + * been closed + * - AMQP_STATUS_SSL_ERROR a SSL socket error occurred. The connection has + * been closed. + * + * \sa amqp_simple_wait_frame() amqp_frames_enqueued() amqp_data_in_buffer() + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_simple_wait_frame_noblock(amqp_connection_state_t state, + amqp_frame_t *decoded_frame, + struct timeval *tv); + +/** + * Waits for a specific method from the broker + * + * \warning You probably don't want to use this function. If this function + * doesn't receive exactly the frame requested it closes the whole connection. + * + * Waits for a single method on a channel from the broker. + * If a frame is received that does not match expected_channel + * or expected_method the program will abort + * + * \param [in] state the connection object + * \param [in] expected_channel the channel that the method should be delivered on + * \param [in] expected_method the method to wait for + * \param [out] output the method + * \returns AMQP_STATUS_OK on success. An amqp_status_enum value is returned + * otherwise. Possible errors include: + * - AMQP_STATUS_WRONG_METHOD a frame containing the wrong method, wrong frame + * type or wrong channel was received. The connection is closed. + * - AMQP_STATUS_NO_MEMORY failure in allocating memory. The library is likely in + * an indeterminate state making recovery unlikely. Client should note the error + * and terminate the application + * - AMQP_STATUS_BAD_AMQP_DATA bad AMQP data was received. The connection + * should be shutdown immediately + * - AMQP_STATUS_UNKNOWN_METHOD: an unknown method was received from the + * broker. This is likely a protocol error and the connection should be + * shutdown immediately + * - AMQP_STATUS_UNKNOWN_CLASS: a properties frame with an unknown class + * was received from the broker. This is likely a protocol error and the + * connection should be shutdown immediately + * - AMQP_STATUS_HEARTBEAT_TIMEOUT timed out while waiting for heartbeat + * from the broker. The connection has been closed. + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. + * - AMQP_STATUS_SOCKET_ERROR a socket error occurred. The connection has + * been closed + * - AMQP_STATUS_SSL_ERROR a SSL socket error occurred. The connection has + * been closed. + * + * \since v0.1 + */ + +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_simple_wait_method(amqp_connection_state_t state, + amqp_channel_t expected_channel, + amqp_method_number_t expected_method, + amqp_method_t *output); + +/** + * Sends a method to the broker + * + * This is a thin wrapper around amqp_send_frame(), providing a way to send + * a method to the broker on a specified channel. + * + * \param [in] state the connection object + * \param [in] channel the channel object + * \param [in] id the method number + * \param [in] decoded the method object + * \returns AMQP_STATUS_OK on success, an amqp_status_enum value otherwise. + * Possible errors include: + * - AMQP_STATUS_BAD_AMQP_DATA the serialized form of the method or + * properties was too large to fit in a single AMQP frame, or the + * method contains an invalid value. The frame was not sent. + * - AMQP_STATUS_TABLE_TOO_BIG the serialized form of an amqp_table_t is + * too large to fit in a single AMQP frame. Frame was not sent. + * - AMQP_STATUS_UNKNOWN_METHOD an invalid method type was passed in + * - AMQP_STATUS_UNKNOWN_CLASS an invalid properties type was passed in + * - AMQP_STATUS_TIMER_FAILURE system timer indicated failure. The frame + * was sent + * - AMQP_STATUS_SOCKET_ERROR + * - AMQP_STATUS_SSL_ERROR + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_send_method(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t id, + void *decoded); + +/** + * Sends a method to the broker and waits for a method response + * + * \param [in] state the connection object + * \param [in] channel the channel object + * \param [in] request_id the method number of the request + * \param [in] expected_reply_ids a 0 terminated array of expected response + * method numbers + * \param [in] decoded_request_method the method to be sent to the broker + * \return a amqp_rpc_reply_t: + * - r.reply_type == AMQP_RESPONSE_NORMAL. RPC completed successfully + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. An exception occurred + * within the library. Examine r.library_error and compare it against + * amqp_status_enum values to determine the error. + * + * \sa amqp_simple_rpc_decoded() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_simple_rpc(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t request_id, + amqp_method_number_t *expected_reply_ids, + void *decoded_request_method); + +/** + * Sends a method to the broker and waits for a method response + * + * \param [in] state the connection object + * \param [in] channel the channel object + * \param [in] request_id the method number of the request + * \param [in] reply_id the method number expected in response + * \param [in] decoded_request_method the request method + * \return a pointer to the method returned from the broker, or NULL on error. + * On error amqp_get_rpc_reply() will return an amqp_rpc_reply_t with + * details on the error that occurred. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +void * +AMQP_CALL amqp_simple_rpc_decoded(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_method_number_t request_id, + amqp_method_number_t reply_id, + void *decoded_request_method); + +/** + * Get the last global amqp_rpc_reply + * + * The API methods corresponding to most synchronous AMQP methods + * return a pointer to the decoded method result. Upon error, they + * return NULL, and we need some way of discovering what, if anything, + * went wrong. amqp_get_rpc_reply() returns the most recent + * amqp_rpc_reply_t instance corresponding to such an API operation + * for the given connection. + * + * Only use it for operations that do not themselves return + * amqp_rpc_reply_t; operations that do return amqp_rpc_reply_t + * generally do NOT update this per-connection-global amqp_rpc_reply_t + * instance. + * + * \param [in] state the connection object + * \return the most recent amqp_rpc_reply_t: + * - r.reply_type == AMQP_RESPONSE_NORMAL. RPC completed successfully + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. An exception occurred + * within the library. Examine r.library_error and compare it against + * amqp_status_enum values to determine the error. + * + * \sa amqp_simple_rpc_decoded() + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_get_rpc_reply(amqp_connection_state_t state); + +/** + * Login to the broker + * + * After using amqp_open_socket and amqp_set_sockfd, call + * amqp_login to complete connecting to the broker + * + * \param [in] state the connection object + * \param [in] vhost the virtual host to connect to on the broker. The default + * on most brokers is "/" + * \param [in] channel_max the limit for number of channels for the connection. + * 0 means no limit, and is a good default (AMQP_DEFAULT_MAX_CHANNELS) + * Note that the maximum number of channels the protocol supports + * is 65535 (2^16, with the 0-channel reserved) + * \param [in] frame_max the maximum size of an AMQP frame on the wire to + * request of the broker for this connection. 4096 is the minimum + * size, 2^31-1 is the maximum, a good default is 131072 (128KB), or + * AMQP_DEFAULT_FRAME_SIZE + * \param [in] heartbeat the number of seconds between heartbeat frames to + * request of the broker. A value of 0 disables heartbeats. + * Note rabbitmq-c only has partial support for heartbeats, as of + * v0.4.0 they are only serviced during amqp_basic_publish() and + * amqp_simple_wait_frame()/amqp_simple_wait_frame_noblock() + * \param [in] sasl_method the SASL method to authenticate with the broker. + * followed by the authentication information. + * For AMQP_SASL_METHOD_PLAIN, the AMQP_SASL_METHOD_PLAIN + * should be followed by two arguments in this order: + * const char* username, and const char* password. + * \return amqp_rpc_reply_t indicating success or failure. + * - r.reply_type == AMQP_RESPONSE_NORMAL. Login completed successfully + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. In most cases errors + * from the broker when logging in will be represented by the broker closing + * the socket. In this case r.library_error will be set to + * AMQP_STATUS_CONNECTION_CLOSED. This error can represent a number of + * error conditions including: invalid vhost, authentication failure. + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_login(amqp_connection_state_t state, char const *vhost, + int channel_max, int frame_max, int heartbeat, + amqp_sasl_method_enum sasl_method, ...); + +/** + * Login to the broker passing a properties table + * + * This function is similar to amqp_login() and differs in that it provides a + * way to pass client properties to the broker. This is commonly used to + * negotiate newer protocol features as they are supported by the broker. + * + * \param [in] state the connection object + * \param [in] vhost the virtual host to connect to on the broker. The default + * on most brokers is "/" + * \param [in] channel_max the limit for the number of channels for the connection. + * 0 means no limit, and is a good default (AMQP_DEFAULT_MAX_CHANNELS) + * Note that the maximum number of channels the protocol supports + * is 65535 (2^16, with the 0-channel reserved) + * \param [in] frame_max the maximum size of an AMQP frame ont he wire to + * request of the broker for this connection. 4096 is the minimum + * size, 2^31-1 is the maximum, a good default is 131072 (128KB), or + * AMQP_DEFAULT_FRAME_SIZE + * \param [in] heartbeat the number of seconds between heartbeat frame to + * request of the broker. A value of 0 disables heartbeats. + * Note rabbitmq-c only has partial support for hearts, as of + * v0.4.0 heartbeats are only serviced during amqp_basic_publish(), + * and amqp_simple_wait_frame()/amqp_simple_wait_frame_noblock() + * \param [in] properties a table of properties to send the broker. + * \param [in] sasl_method the SASL method to authenticate with the broker + * followed by the authentication information. + * For AMQP_SASL_METHOD_PLAN, the AMQP_SASL_METHOD_PLAIN parameter + * should be followed by two arguments in this order: + * const char* username, and const char* password. + * \return amqp_rpc_reply_t indicating success or failure. + * - r.reply_type == AMQP_RESPONSE_NORMAL. Login completed successfully + * - r.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION. In most cases errors + * from the broker when logging in will be represented by the broker closing + * the socket. In this case r.library_error will be set to + * AMQP_STATUS_CONNECTION_CLOSED. This error can represent a number of + * error conditions including: invalid vhost, authentication failure. + * - r.reply_type == AMQP_RESPONSE_SERVER_EXCEPTION. The broker returned an + * exception: + * - If r.reply.id == AMQP_CHANNEL_CLOSE_METHOD a channel exception + * occurred, cast r.reply.decoded to amqp_channel_close_t* to see details + * of the exception. The client should amqp_send_method() a + * amqp_channel_close_ok_t. The channel must be re-opened before it + * can be used again. Any resources associated with the channel + * (auto-delete exchanges, auto-delete queues, consumers) are invalid + * and must be recreated before attempting to use them again. + * - If r.reply.id == AMQP_CONNECTION_CLOSE_METHOD a connection exception + * occurred, cast r.reply.decoded to amqp_connection_close_t* to see + * details of the exception. The client amqp_send_method() a + * amqp_connection_close_ok_t and disconnect from the broker. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_login_with_properties(amqp_connection_state_t state, char const *vhost, + int channel_max, int frame_max, int heartbeat, + const amqp_table_t *properties, amqp_sasl_method_enum sasl_method, ...); + +struct amqp_basic_properties_t_; + +/** + * Publish a message to the broker + * + * Publish a message on an exchange with a routing key. + * + * Note that at the AMQ protocol level basic.publish is an async method: + * this means error conditions that occur on the broker (such as publishing to + * a non-existent exchange) will not be reflected in the return value of this + * function. + * + * in the return value from this function. + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] exchange the exchange on the broker to publish to + * \param [in] routing_key the routing key to use when publishing the message + * \param [in] mandatory indicate to the broker that the message MUST be routed + * to a queue. If the broker cannot do this it should respond with + * a basic.reject method. + * \param [in] immediate indicate to the broker that the message MUST be delivered + * to a consumer immediately. If the broker cannot do this it should + * response with a basic.reject method. + * \param [in] properties the properties associated with the message + * \param [in] body the message body + * \return AMQP_STATUS_OK on success, amqp_status_enum value on failure. Note + * that basic.publish is an async method, the return value from this + * function only indicates that the message data was successfully + * transmitted to the broker. It does not indicate failures that occur + * on the broker, such as publishing to a non-existent exchange. + * Possible error values: + * - AMQP_STATUS_TIMER_FAILURE: system timer facility returned an error + * the message was not sent. + * - AMQP_STATUS_HEARTBEAT_TIMEOUT: connection timed out waiting for a + * heartbeat from the broker. The message was not sent. + * - AMQP_STATUS_NO_MEMORY: memory allocation failed. The message was + * not sent. + * - AMQP_STATUS_TABLE_TOO_BIG: a table in the properties was too large + * to fit in a single frame. Message was not sent. + * - AMQP_STATUS_CONNECTION_CLOSED: the connection was closed. + * - AMQP_STATUS_SSL_ERROR: a SSL error occurred. + * - AMQP_STATUS_TCP_ERROR: a TCP error occurred. errno or + * WSAGetLastError() may provide more information + * + * Note: this function does heartbeat processing as of v0.4.0 + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_basic_publish(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t exchange, amqp_bytes_t routing_key, + amqp_boolean_t mandatory, amqp_boolean_t immediate, + struct amqp_basic_properties_t_ const *properties, + amqp_bytes_t body); + +/** + * Closes an channel + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] code the reason for closing the channel, AMQP_REPLY_SUCCESS is a good default + * \return amqp_rpc_reply_t indicating success or failure + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_channel_close(amqp_connection_state_t state, amqp_channel_t channel, + int code); + +/** + * Closes the entire connection + * + * Implicitly closes all channels and informs the broker the connection + * is being closed, after receiving acknowldgement from the broker it closes + * the socket. + * + * \param [in] state the connection object + * \param [in] code the reason code for closing the connection. AMQP_REPLY_SUCCESS is a good default. + * \return amqp_rpc_reply_t indicating the result + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_connection_close(amqp_connection_state_t state, int code); + +/** + * Acknowledges a message + * + * Does a basic.ack on a received message + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] delivery_tag the delivery take of the message to be ack'd + * \param [in] multiple if true, ack all messages up to this delivery tag, if + * false ack only this delivery tag + * \return 0 on success, 0 > on failing to send the ack to the broker. + * this will not indicate failure if something goes wrong on the broker + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_basic_ack(amqp_connection_state_t state, amqp_channel_t channel, + uint64_t delivery_tag, amqp_boolean_t multiple); + +/** + * Do a basic.get + * + * Synchonously polls the broker for a message in a queue, and + * retrieves the message if a message is in the queue. + * + * \param [in] state the connection object + * \param [in] channel the channel identifier to use + * \param [in] queue the queue name to retrieve from + * \param [in] no_ack if true the message is automatically ack'ed + * if false amqp_basic_ack should be called once the message + * retrieved has been processed + * \return amqp_rpc_reply indicating success or failure + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_basic_get(amqp_connection_state_t state, amqp_channel_t channel, + amqp_bytes_t queue, amqp_boolean_t no_ack); + +/** + * Do a basic.reject + * + * Actively reject a message that has been delivered + * + * \param [in] state the connection object + * \param [in] channel the channel identifier + * \param [in] delivery_tag the delivery tag of the message to reject + * \param [in] requeue indicate to the broker whether it should requeue the + * message or just discard it. + * \return 0 on success, 0 > on failing to send the reject method to the broker. + * This will not indicate failure if something goes wrong on the broker. + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_basic_reject(amqp_connection_state_t state, amqp_channel_t channel, + uint64_t delivery_tag, amqp_boolean_t requeue); + +/** + * Check to see if there is data left in the receive buffer + * + * Can be used to see if there is data still in the buffer, if so + * calling amqp_simple_wait_frame will not immediately enter a + * blocking read. + * + * \param [in] state the connection object + * \return true if there is data in the recieve buffer, false otherwise + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +amqp_boolean_t +AMQP_CALL amqp_data_in_buffer(amqp_connection_state_t state); + +/** + * Get the error string for the given error code. + * + * \deprecated This function has been deprecated in favor of + * \ref amqp_error_string2() which returns statically allocated + * string which do not need to be freed by the caller. + * + * The returned string resides on the heap; the caller is responsible + * for freeing it. + * + * \param [in] err return error code + * \return the error string + * + * \since v0.1 + */ +AMQP_DEPRECATED( + AMQP_PUBLIC_FUNCTION + char * + AMQP_CALL amqp_error_string(int err) +); + + +/** + * Get the error string for the given error code. + * + * Get an error string associated with an error code. The string is statically + * allocated and does not need to be freed + * + * \param [in] err the error code + * \return the error string + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +const char * +AMQP_CALL amqp_error_string2(int err); + + +/** + * Deserialize an amqp_table_t from AMQP wireformat + * + * This is an internal function and is not typically used by + * client applications + * + * \param [in] encoded the buffer containing the serialized data + * \param [in] pool memory pool used to allocate the table entries from + * \param [in] output the amqp_table_t structure to fill in. Any existing + * entries will be erased + * \param [in,out] offset The offset into the encoded buffer to start + * reading the serialized table. It will be updated + * by this function to end of the table + * \return AMQP_STATUS_OK on success, an amqp_status_enum value on failure + * Possible error codes: + * - AMQP_STATUS_NO_MEMORY out of memory + * - AMQP_STATUS_BAD_AMQP_DATA invalid wireformat + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool, + amqp_table_t *output, size_t *offset); + +/** + * Serializes an amqp_table_t to the AMQP wireformat + * + * This is an internal function and is not typically used by + * client applications + * + * \param [in] encoded the buffer where to serialize the table to + * \param [in] input the amqp_table_t to serialize + * \param [in,out] offset The offset into the encoded buffer to start + * writing the serialized table. It will be updated + * by this function to where writing left off + * \return AMQP_STATUS_OK on success, an amqp_status_enum value on failure + * Possible error codes: + * - AMQP_STATUS_TABLE_TOO_BIG the serialized form is too large for the + * buffer + * - AMQP_STATUS_BAD_AMQP_DATA invalid table + * + * \since v0.1 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input, size_t *offset); + + +/** + * Create a deep-copy of an amqp_table_t object + * + * Creates a deep-copy of an amqp_table_t object, using the provided pool + * object to allocate the necessary memory. This memory can be freed later by + * call recycle_amqp_pool(), or empty_amqp_pool() + * + * \param [in] original the table to copy + * \param [in,out] clone the table to copy to + * \param [in] pool the initialized memory pool to do allocations for the table + * from + * \return AMQP_STATUS_OK on success, amqp_status_enum value on failure. + * Possible error values: + * - AMQP_STATUS_NO_MEMORY - memory allocation failure. + * - AMQP_STATUS_INVALID_PARAMETER - invalid table (e.g., no key name) + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_table_clone(amqp_table_t *original, amqp_table_t *clone, amqp_pool_t *pool); + +/** + * A message object + * + * \since v0.4.0 + */ +typedef struct amqp_message_t_ { + amqp_basic_properties_t properties; /**< message properties */ + amqp_bytes_t body; /**< message body */ + amqp_pool_t pool; /**< pool used to allocate properties */ +} amqp_message_t; + +/** + * Reads the next message on a channel + * + * Reads a complete message (header + body) on a specified channel. This + * function is intended to be used with amqp_basic_get() or when an + * AMQP_BASIC_DELIVERY_METHOD method is received. + * + * \param [in,out] state the connection object + * \param [in] channel the channel on which to read the message from + * \param [in,out] message a pointer to a amqp_message_t object. Caller should + * call amqp_message_destroy() when it is done using the + * fields in the message object. The caller is responsible for + * allocating/destroying the amqp_message_t object itself. + * \param [in] flags pass in 0. Currently unused. + * \returns a amqp_rpc_reply_t object. ret.reply_type == AMQP_RESPONSE_NORMAL on success. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_read_message(amqp_connection_state_t state, + amqp_channel_t channel, + amqp_message_t *message, int flags); + +/** + * Frees memory associated with a amqp_message_t allocated in amqp_read_message + * + * \param [in] message + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_destroy_message(amqp_message_t *message); + +/** + * Envelope object + * + * \since v0.4.0 + */ +typedef struct amqp_envelope_t_ { + amqp_channel_t channel; /**< channel message was delivered on */ + amqp_bytes_t consumer_tag; /**< the consumer tag the message was delivered to */ + uint64_t delivery_tag; /**< the messages delivery tag */ + amqp_boolean_t redelivered; /**< flag indicating whether this message is being redelivered */ + amqp_bytes_t exchange; /**< exchange this message was published to */ + amqp_bytes_t routing_key; /**< the routing key this message was published with */ + amqp_message_t message; /**< the message */ +} amqp_envelope_t; + +/** + * Wait for and consume a message + * + * Waits for a basic.deliver method on any channel, upon receipt of + * basic.deliver it reads that message, and returns. If any other method is + * received before basic.deliver, this function will return an amqp_rpc_reply_t + * with ret.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, and + * ret.library_error == AMQP_STATUS_UNEXPECTED_FRAME. The caller should then + * call amqp_simple_wait_frame() to read this frame and take appropriate action. + * + * This function should be used after starting a consumer with the + * amqp_basic_consume() function + * + * \param [in,out] state the connection object + * \param [in,out] envelope a pointer to a amqp_envelope_t object. Caller + * should call amqp_envelope_destroy() when it is done using + * the fields in the envelope object. The caller is responsible + * for allocating/destroying the amqp_envelope_t object itself. + * \param [in] timeout a timeout to wait for a message delivery. Passing in + * NULL will result in blocking behavior. + * \param [in] flags pass in 0. Currently unused. + * \returns a amqp_rpc_reply_t object. ret.reply_type == AMQP_RESPONSE_NORMAL + * on success. If ret.reply_type == AMQP_RESPONSE_LIBRARY_EXCEPTION, and + * ret.library_error == AMQP_STATUS_UNEXPECTED_FRAME, a frame other + * than AMQP_BASIC_DELIVER_METHOD was received, the caller should call + * amqp_simple_wait_frame() to read this frame and take appropriate + * action. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_rpc_reply_t +AMQP_CALL amqp_consume_message(amqp_connection_state_t state, + amqp_envelope_t *envelope, + struct timeval *timeout, int flags); + +/** + * Frees memory associated with a amqp_envelope_t allocated in amqp_consume_message() + * + * \param [in] envelope + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_destroy_envelope(amqp_envelope_t *envelope); + + +/** + * Parameters used to connect to the RabbitMQ broker + * + * \since v0.2 + */ +struct amqp_connection_info { + char *user; /**< the username to authenticate with the broker, default on most broker is 'guest' */ + char *password; /**< the password to authenticate with the broker, default on most brokers is 'guest' */ + char *host; /**< the hostname of the broker */ + char *vhost; /**< the virtual host on the broker to connect to, a good default is "/" */ + int port; /**< the port that the broker is listening on, default on most brokers is 5672 */ + amqp_boolean_t ssl; +}; + +/** + * Initialze an amqp_connection_info to default values + * + * The default values are: + * - user: "guest" + * - password: "guest" + * - host: "localhost" + * - vhost: "/" + * - port: 5672 + * + * \param [out] parsed the connection info to set defaults on + * + * \since v0.2 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL amqp_default_connection_info(struct amqp_connection_info *parsed); + +/** + * Parse a connection URL + * + * An amqp connection url takes the form: + * + * amqp://[$USERNAME[:$PASSWORD]\@]$HOST[:$PORT]/[$VHOST] + * + * Examples: + * amqp://guest:guest\@localhost:5672// + * amqp://guest:guest\@localhost/myvhost + * + * \note This function modifies url parameter. + * + * \param [in] url URI to parse, note that this parameter is modified by the + * function. + * \param [out] parsed the connection info gleaned from the URI. The char* + * members will point to parts of the url input parameter. + * Memory management will depend on how the url is allocated. + * \returns AMQP_STATUS_OK on success, AMQP_STATUS_BAD_URL on failure + * + * \since v0.2 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_parse_url(char *url, struct amqp_connection_info *parsed); + +/* socket API */ + +/** + * Open a socket connection. + * + * This function opens a socket connection returned from amqp_tcp_socket_new() + * or amqp_ssl_socket_new(). This function should be called after setting + * socket options and prior to assigning the socket to an AMQP connection with + * amqp_set_socket(). + * + * \param [in,out] self A socket object. + * \param [in] host Connect to this host. + * \param [in] port Connect on this remote port. + * + * \return AMQP_STATUS_OK on success, an amqp_status_enum on failure + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL +amqp_socket_open(amqp_socket_t *self, const char *host, int port); + +/** + * Open a socket connection. + * + * This function opens a socket connection returned from amqp_tcp_socket_new() + * or amqp_ssl_socket_new(). This function should be called after setting + * socket options and prior to assigning the socket to an AMQP connection with + * amqp_set_socket(). + * + * \param [in,out] self A socket object. + * \param [in] host Connect to this host. + * \param [in] port Connect on this remote port. + * \param [in] timeout Max allowed time to spent on opening. If NULL - run in blocking mode + * + * \return AMQP_STATUS_OK on success, an amqp_status_enum on failure. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL +amqp_socket_open_noblock(amqp_socket_t *self, const char *host, int port, struct timeval *timeout); + +/** + * Get the socket descriptor in use by a socket object. + * + * Retrieve the underlying socket descriptor. This function can be used to + * perform low-level socket operations that aren't supported by the socket + * interface. Use with caution! + * + * \param [in,out] self A socket object. + * + * \return The underlying socket descriptor, or -1 if there is no socket descriptor + * associated with + * with + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL +amqp_socket_get_sockfd(amqp_socket_t *self); + +/** + * Get the socket object associated with a amqp_connection_state_t + * + * \param [in] state the connection object to get the socket from + * \return a pointer to the socket object, or NULL if one has not been assigned + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_socket_t * +amqp_get_socket(amqp_connection_state_t state); + +AMQP_END_DECLS + + +#endif /* AMQP_H */ diff --git a/c-demo-agent/amqp_framing.h b/c-demo-agent/amqp_framing.h new file mode 100644 index 00000000..f6dafe43 --- /dev/null +++ b/c-demo-agent/amqp_framing.h @@ -0,0 +1,630 @@ +/* Generated code. Do not edit. Edit and re-run codegen.py instead. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MIT + * + * Portions created by Alan Antonuk are Copyright (c) 2012-2013 + * Alan Antonuk. All Rights Reserved. + * + * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc. + * All Rights Reserved. + * + * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010 + * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ***** END LICENSE BLOCK ***** + */ + +#ifndef AMQP_FRAMING_H +#define AMQP_FRAMING_H + +#include + +AMQP_BEGIN_DECLS + +#define AMQP_PROTOCOL_VERSION_MAJOR 0 +#define AMQP_PROTOCOL_VERSION_MINOR 9 +#define AMQP_PROTOCOL_VERSION_REVISION 1 +#define AMQP_PROTOCOL_PORT 5672 +#define AMQP_FRAME_METHOD 1 +#define AMQP_FRAME_HEADER 2 +#define AMQP_FRAME_BODY 3 +#define AMQP_FRAME_HEARTBEAT 8 +#define AMQP_FRAME_MIN_SIZE 4096 +#define AMQP_FRAME_END 206 +#define AMQP_REPLY_SUCCESS 200 +#define AMQP_CONTENT_TOO_LARGE 311 +#define AMQP_NO_ROUTE 312 +#define AMQP_NO_CONSUMERS 313 +#define AMQP_ACCESS_REFUSED 403 +#define AMQP_NOT_FOUND 404 +#define AMQP_RESOURCE_LOCKED 405 +#define AMQP_PRECONDITION_FAILED 406 +#define AMQP_CONNECTION_FORCED 320 +#define AMQP_INVALID_PATH 402 +#define AMQP_FRAME_ERROR 501 +#define AMQP_SYNTAX_ERROR 502 +#define AMQP_COMMAND_INVALID 503 +#define AMQP_CHANNEL_ERROR 504 +#define AMQP_UNEXPECTED_FRAME 505 +#define AMQP_RESOURCE_ERROR 506 +#define AMQP_NOT_ALLOWED 530 +#define AMQP_NOT_IMPLEMENTED 540 +#define AMQP_INTERNAL_ERROR 541 + +/* Function prototypes. */ + +AMQP_PUBLIC_FUNCTION +char const * +AMQP_CALL amqp_constant_name(int constantNumber); + +AMQP_PUBLIC_FUNCTION +amqp_boolean_t +AMQP_CALL amqp_constant_is_hard_error(int constantNumber); + +AMQP_PUBLIC_FUNCTION +char const * +AMQP_CALL amqp_method_name(amqp_method_number_t methodNumber); + +AMQP_PUBLIC_FUNCTION +amqp_boolean_t +AMQP_CALL amqp_method_has_content(amqp_method_number_t methodNumber); + +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_decode_method(amqp_method_number_t methodNumber, + amqp_pool_t *pool, + amqp_bytes_t encoded, + void **decoded); + +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_decode_properties(uint16_t class_id, + amqp_pool_t *pool, + amqp_bytes_t encoded, + void **decoded); + +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_encode_method(amqp_method_number_t methodNumber, + void *decoded, + amqp_bytes_t encoded); + +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL amqp_encode_properties(uint16_t class_id, + void *decoded, + amqp_bytes_t encoded); + +/* Method field records. */ + +#define AMQP_CONNECTION_START_METHOD ((amqp_method_number_t) 0x000A000A) /* 10, 10; 655370 */ +typedef struct amqp_connection_start_t_ { + uint8_t version_major; + uint8_t version_minor; + amqp_table_t server_properties; + amqp_bytes_t mechanisms; + amqp_bytes_t locales; +} amqp_connection_start_t; + +#define AMQP_CONNECTION_START_OK_METHOD ((amqp_method_number_t) 0x000A000B) /* 10, 11; 655371 */ +typedef struct amqp_connection_start_ok_t_ { + amqp_table_t client_properties; + amqp_bytes_t mechanism; + amqp_bytes_t response; + amqp_bytes_t locale; +} amqp_connection_start_ok_t; + +#define AMQP_CONNECTION_SECURE_METHOD ((amqp_method_number_t) 0x000A0014) /* 10, 20; 655380 */ +typedef struct amqp_connection_secure_t_ { + amqp_bytes_t challenge; +} amqp_connection_secure_t; + +#define AMQP_CONNECTION_SECURE_OK_METHOD ((amqp_method_number_t) 0x000A0015) /* 10, 21; 655381 */ +typedef struct amqp_connection_secure_ok_t_ { + amqp_bytes_t response; +} amqp_connection_secure_ok_t; + +#define AMQP_CONNECTION_TUNE_METHOD ((amqp_method_number_t) 0x000A001E) /* 10, 30; 655390 */ +typedef struct amqp_connection_tune_t_ { + uint16_t channel_max; + uint32_t frame_max; + uint16_t heartbeat; +} amqp_connection_tune_t; + +#define AMQP_CONNECTION_TUNE_OK_METHOD ((amqp_method_number_t) 0x000A001F) /* 10, 31; 655391 */ +typedef struct amqp_connection_tune_ok_t_ { + uint16_t channel_max; + uint32_t frame_max; + uint16_t heartbeat; +} amqp_connection_tune_ok_t; + +#define AMQP_CONNECTION_OPEN_METHOD ((amqp_method_number_t) 0x000A0028) /* 10, 40; 655400 */ +typedef struct amqp_connection_open_t_ { + amqp_bytes_t virtual_host; + amqp_bytes_t capabilities; + amqp_boolean_t insist; +} amqp_connection_open_t; + +#define AMQP_CONNECTION_OPEN_OK_METHOD ((amqp_method_number_t) 0x000A0029) /* 10, 41; 655401 */ +typedef struct amqp_connection_open_ok_t_ { + amqp_bytes_t known_hosts; +} amqp_connection_open_ok_t; + +#define AMQP_CONNECTION_CLOSE_METHOD ((amqp_method_number_t) 0x000A0032) /* 10, 50; 655410 */ +typedef struct amqp_connection_close_t_ { + uint16_t reply_code; + amqp_bytes_t reply_text; + uint16_t class_id; + uint16_t method_id; +} amqp_connection_close_t; + +#define AMQP_CONNECTION_CLOSE_OK_METHOD ((amqp_method_number_t) 0x000A0033) /* 10, 51; 655411 */ +typedef struct amqp_connection_close_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_connection_close_ok_t; + +#define AMQP_CHANNEL_OPEN_METHOD ((amqp_method_number_t) 0x0014000A) /* 20, 10; 1310730 */ +typedef struct amqp_channel_open_t_ { + amqp_bytes_t out_of_band; +} amqp_channel_open_t; + +#define AMQP_CHANNEL_OPEN_OK_METHOD ((amqp_method_number_t) 0x0014000B) /* 20, 11; 1310731 */ +typedef struct amqp_channel_open_ok_t_ { + amqp_bytes_t channel_id; +} amqp_channel_open_ok_t; + +#define AMQP_CHANNEL_FLOW_METHOD ((amqp_method_number_t) 0x00140014) /* 20, 20; 1310740 */ +typedef struct amqp_channel_flow_t_ { + amqp_boolean_t active; +} amqp_channel_flow_t; + +#define AMQP_CHANNEL_FLOW_OK_METHOD ((amqp_method_number_t) 0x00140015) /* 20, 21; 1310741 */ +typedef struct amqp_channel_flow_ok_t_ { + amqp_boolean_t active; +} amqp_channel_flow_ok_t; + +#define AMQP_CHANNEL_CLOSE_METHOD ((amqp_method_number_t) 0x00140028) /* 20, 40; 1310760 */ +typedef struct amqp_channel_close_t_ { + uint16_t reply_code; + amqp_bytes_t reply_text; + uint16_t class_id; + uint16_t method_id; +} amqp_channel_close_t; + +#define AMQP_CHANNEL_CLOSE_OK_METHOD ((amqp_method_number_t) 0x00140029) /* 20, 41; 1310761 */ +typedef struct amqp_channel_close_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_channel_close_ok_t; + +#define AMQP_ACCESS_REQUEST_METHOD ((amqp_method_number_t) 0x001E000A) /* 30, 10; 1966090 */ +typedef struct amqp_access_request_t_ { + amqp_bytes_t realm; + amqp_boolean_t exclusive; + amqp_boolean_t passive; + amqp_boolean_t active; + amqp_boolean_t write; + amqp_boolean_t read; +} amqp_access_request_t; + +#define AMQP_ACCESS_REQUEST_OK_METHOD ((amqp_method_number_t) 0x001E000B) /* 30, 11; 1966091 */ +typedef struct amqp_access_request_ok_t_ { + uint16_t ticket; +} amqp_access_request_ok_t; + +#define AMQP_EXCHANGE_DECLARE_METHOD ((amqp_method_number_t) 0x0028000A) /* 40, 10; 2621450 */ +typedef struct amqp_exchange_declare_t_ { + uint16_t ticket; + amqp_bytes_t exchange; + amqp_bytes_t type; + amqp_boolean_t passive; + amqp_boolean_t durable; + amqp_boolean_t auto_delete; + amqp_boolean_t internal; + amqp_boolean_t nowait; + amqp_table_t arguments; +} amqp_exchange_declare_t; + +#define AMQP_EXCHANGE_DECLARE_OK_METHOD ((amqp_method_number_t) 0x0028000B) /* 40, 11; 2621451 */ +typedef struct amqp_exchange_declare_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_exchange_declare_ok_t; + +#define AMQP_EXCHANGE_DELETE_METHOD ((amqp_method_number_t) 0x00280014) /* 40, 20; 2621460 */ +typedef struct amqp_exchange_delete_t_ { + uint16_t ticket; + amqp_bytes_t exchange; + amqp_boolean_t if_unused; + amqp_boolean_t nowait; +} amqp_exchange_delete_t; + +#define AMQP_EXCHANGE_DELETE_OK_METHOD ((amqp_method_number_t) 0x00280015) /* 40, 21; 2621461 */ +typedef struct amqp_exchange_delete_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_exchange_delete_ok_t; + +#define AMQP_EXCHANGE_BIND_METHOD ((amqp_method_number_t) 0x0028001E) /* 40, 30; 2621470 */ +typedef struct amqp_exchange_bind_t_ { + uint16_t ticket; + amqp_bytes_t destination; + amqp_bytes_t source; + amqp_bytes_t routing_key; + amqp_boolean_t nowait; + amqp_table_t arguments; +} amqp_exchange_bind_t; + +#define AMQP_EXCHANGE_BIND_OK_METHOD ((amqp_method_number_t) 0x0028001F) /* 40, 31; 2621471 */ +typedef struct amqp_exchange_bind_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_exchange_bind_ok_t; + +#define AMQP_EXCHANGE_UNBIND_METHOD ((amqp_method_number_t) 0x00280028) /* 40, 40; 2621480 */ +typedef struct amqp_exchange_unbind_t_ { + uint16_t ticket; + amqp_bytes_t destination; + amqp_bytes_t source; + amqp_bytes_t routing_key; + amqp_boolean_t nowait; + amqp_table_t arguments; +} amqp_exchange_unbind_t; + +#define AMQP_EXCHANGE_UNBIND_OK_METHOD ((amqp_method_number_t) 0x00280033) /* 40, 51; 2621491 */ +typedef struct amqp_exchange_unbind_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_exchange_unbind_ok_t; + +#define AMQP_QUEUE_DECLARE_METHOD ((amqp_method_number_t) 0x0032000A) /* 50, 10; 3276810 */ +typedef struct amqp_queue_declare_t_ { + uint16_t ticket; + amqp_bytes_t queue; + amqp_boolean_t passive; + amqp_boolean_t durable; + amqp_boolean_t exclusive; + amqp_boolean_t auto_delete; + amqp_boolean_t nowait; + amqp_table_t arguments; +} amqp_queue_declare_t; + +#define AMQP_QUEUE_DECLARE_OK_METHOD ((amqp_method_number_t) 0x0032000B) /* 50, 11; 3276811 */ +typedef struct amqp_queue_declare_ok_t_ { + amqp_bytes_t queue; + uint32_t message_count; + uint32_t consumer_count; +} amqp_queue_declare_ok_t; + +#define AMQP_QUEUE_BIND_METHOD ((amqp_method_number_t) 0x00320014) /* 50, 20; 3276820 */ +typedef struct amqp_queue_bind_t_ { + uint16_t ticket; + amqp_bytes_t queue; + amqp_bytes_t exchange; + amqp_bytes_t routing_key; + amqp_boolean_t nowait; + amqp_table_t arguments; +} amqp_queue_bind_t; + +#define AMQP_QUEUE_BIND_OK_METHOD ((amqp_method_number_t) 0x00320015) /* 50, 21; 3276821 */ +typedef struct amqp_queue_bind_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_queue_bind_ok_t; + +#define AMQP_QUEUE_PURGE_METHOD ((amqp_method_number_t) 0x0032001E) /* 50, 30; 3276830 */ +typedef struct amqp_queue_purge_t_ { + uint16_t ticket; + amqp_bytes_t queue; + amqp_boolean_t nowait; +} amqp_queue_purge_t; + +#define AMQP_QUEUE_PURGE_OK_METHOD ((amqp_method_number_t) 0x0032001F) /* 50, 31; 3276831 */ +typedef struct amqp_queue_purge_ok_t_ { + uint32_t message_count; +} amqp_queue_purge_ok_t; + +#define AMQP_QUEUE_DELETE_METHOD ((amqp_method_number_t) 0x00320028) /* 50, 40; 3276840 */ +typedef struct amqp_queue_delete_t_ { + uint16_t ticket; + amqp_bytes_t queue; + amqp_boolean_t if_unused; + amqp_boolean_t if_empty; + amqp_boolean_t nowait; +} amqp_queue_delete_t; + +#define AMQP_QUEUE_DELETE_OK_METHOD ((amqp_method_number_t) 0x00320029) /* 50, 41; 3276841 */ +typedef struct amqp_queue_delete_ok_t_ { + uint32_t message_count; +} amqp_queue_delete_ok_t; + +#define AMQP_QUEUE_UNBIND_METHOD ((amqp_method_number_t) 0x00320032) /* 50, 50; 3276850 */ +typedef struct amqp_queue_unbind_t_ { + uint16_t ticket; + amqp_bytes_t queue; + amqp_bytes_t exchange; + amqp_bytes_t routing_key; + amqp_table_t arguments; +} amqp_queue_unbind_t; + +#define AMQP_QUEUE_UNBIND_OK_METHOD ((amqp_method_number_t) 0x00320033) /* 50, 51; 3276851 */ +typedef struct amqp_queue_unbind_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_queue_unbind_ok_t; + +#define AMQP_BASIC_QOS_METHOD ((amqp_method_number_t) 0x003C000A) /* 60, 10; 3932170 */ +typedef struct amqp_basic_qos_t_ { + uint32_t prefetch_size; + uint16_t prefetch_count; + amqp_boolean_t global; +} amqp_basic_qos_t; + +#define AMQP_BASIC_QOS_OK_METHOD ((amqp_method_number_t) 0x003C000B) /* 60, 11; 3932171 */ +typedef struct amqp_basic_qos_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_basic_qos_ok_t; + +#define AMQP_BASIC_CONSUME_METHOD ((amqp_method_number_t) 0x003C0014) /* 60, 20; 3932180 */ +typedef struct amqp_basic_consume_t_ { + uint16_t ticket; + amqp_bytes_t queue; + amqp_bytes_t consumer_tag; + amqp_boolean_t no_local; + amqp_boolean_t no_ack; + amqp_boolean_t exclusive; + amqp_boolean_t nowait; + amqp_table_t arguments; +} amqp_basic_consume_t; + +#define AMQP_BASIC_CONSUME_OK_METHOD ((amqp_method_number_t) 0x003C0015) /* 60, 21; 3932181 */ +typedef struct amqp_basic_consume_ok_t_ { + amqp_bytes_t consumer_tag; +} amqp_basic_consume_ok_t; + +#define AMQP_BASIC_CANCEL_METHOD ((amqp_method_number_t) 0x003C001E) /* 60, 30; 3932190 */ +typedef struct amqp_basic_cancel_t_ { + amqp_bytes_t consumer_tag; + amqp_boolean_t nowait; +} amqp_basic_cancel_t; + +#define AMQP_BASIC_CANCEL_OK_METHOD ((amqp_method_number_t) 0x003C001F) /* 60, 31; 3932191 */ +typedef struct amqp_basic_cancel_ok_t_ { + amqp_bytes_t consumer_tag; +} amqp_basic_cancel_ok_t; + +#define AMQP_BASIC_PUBLISH_METHOD ((amqp_method_number_t) 0x003C0028) /* 60, 40; 3932200 */ +typedef struct amqp_basic_publish_t_ { + uint16_t ticket; + amqp_bytes_t exchange; + amqp_bytes_t routing_key; + amqp_boolean_t mandatory; + amqp_boolean_t immediate; +} amqp_basic_publish_t; + +#define AMQP_BASIC_RETURN_METHOD ((amqp_method_number_t) 0x003C0032) /* 60, 50; 3932210 */ +typedef struct amqp_basic_return_t_ { + uint16_t reply_code; + amqp_bytes_t reply_text; + amqp_bytes_t exchange; + amqp_bytes_t routing_key; +} amqp_basic_return_t; + +#define AMQP_BASIC_DELIVER_METHOD ((amqp_method_number_t) 0x003C003C) /* 60, 60; 3932220 */ +typedef struct amqp_basic_deliver_t_ { + amqp_bytes_t consumer_tag; + uint64_t delivery_tag; + amqp_boolean_t redelivered; + amqp_bytes_t exchange; + amqp_bytes_t routing_key; +} amqp_basic_deliver_t; + +#define AMQP_BASIC_GET_METHOD ((amqp_method_number_t) 0x003C0046) /* 60, 70; 3932230 */ +typedef struct amqp_basic_get_t_ { + uint16_t ticket; + amqp_bytes_t queue; + amqp_boolean_t no_ack; +} amqp_basic_get_t; + +#define AMQP_BASIC_GET_OK_METHOD ((amqp_method_number_t) 0x003C0047) /* 60, 71; 3932231 */ +typedef struct amqp_basic_get_ok_t_ { + uint64_t delivery_tag; + amqp_boolean_t redelivered; + amqp_bytes_t exchange; + amqp_bytes_t routing_key; + uint32_t message_count; +} amqp_basic_get_ok_t; + +#define AMQP_BASIC_GET_EMPTY_METHOD ((amqp_method_number_t) 0x003C0048) /* 60, 72; 3932232 */ +typedef struct amqp_basic_get_empty_t_ { + amqp_bytes_t cluster_id; +} amqp_basic_get_empty_t; + +#define AMQP_BASIC_ACK_METHOD ((amqp_method_number_t) 0x003C0050) /* 60, 80; 3932240 */ +typedef struct amqp_basic_ack_t_ { + uint64_t delivery_tag; + amqp_boolean_t multiple; +} amqp_basic_ack_t; + +#define AMQP_BASIC_REJECT_METHOD ((amqp_method_number_t) 0x003C005A) /* 60, 90; 3932250 */ +typedef struct amqp_basic_reject_t_ { + uint64_t delivery_tag; + amqp_boolean_t requeue; +} amqp_basic_reject_t; + +#define AMQP_BASIC_RECOVER_ASYNC_METHOD ((amqp_method_number_t) 0x003C0064) /* 60, 100; 3932260 */ +typedef struct amqp_basic_recover_async_t_ { + amqp_boolean_t requeue; +} amqp_basic_recover_async_t; + +#define AMQP_BASIC_RECOVER_METHOD ((amqp_method_number_t) 0x003C006E) /* 60, 110; 3932270 */ +typedef struct amqp_basic_recover_t_ { + amqp_boolean_t requeue; +} amqp_basic_recover_t; + +#define AMQP_BASIC_RECOVER_OK_METHOD ((amqp_method_number_t) 0x003C006F) /* 60, 111; 3932271 */ +typedef struct amqp_basic_recover_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_basic_recover_ok_t; + +#define AMQP_BASIC_NACK_METHOD ((amqp_method_number_t) 0x003C0078) /* 60, 120; 3932280 */ +typedef struct amqp_basic_nack_t_ { + uint64_t delivery_tag; + amqp_boolean_t multiple; + amqp_boolean_t requeue; +} amqp_basic_nack_t; + +#define AMQP_TX_SELECT_METHOD ((amqp_method_number_t) 0x005A000A) /* 90, 10; 5898250 */ +typedef struct amqp_tx_select_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_tx_select_t; + +#define AMQP_TX_SELECT_OK_METHOD ((amqp_method_number_t) 0x005A000B) /* 90, 11; 5898251 */ +typedef struct amqp_tx_select_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_tx_select_ok_t; + +#define AMQP_TX_COMMIT_METHOD ((amqp_method_number_t) 0x005A0014) /* 90, 20; 5898260 */ +typedef struct amqp_tx_commit_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_tx_commit_t; + +#define AMQP_TX_COMMIT_OK_METHOD ((amqp_method_number_t) 0x005A0015) /* 90, 21; 5898261 */ +typedef struct amqp_tx_commit_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_tx_commit_ok_t; + +#define AMQP_TX_ROLLBACK_METHOD ((amqp_method_number_t) 0x005A001E) /* 90, 30; 5898270 */ +typedef struct amqp_tx_rollback_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_tx_rollback_t; + +#define AMQP_TX_ROLLBACK_OK_METHOD ((amqp_method_number_t) 0x005A001F) /* 90, 31; 5898271 */ +typedef struct amqp_tx_rollback_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_tx_rollback_ok_t; + +#define AMQP_CONFIRM_SELECT_METHOD ((amqp_method_number_t) 0x0055000A) /* 85, 10; 5570570 */ +typedef struct amqp_confirm_select_t_ { + amqp_boolean_t nowait; +} amqp_confirm_select_t; + +#define AMQP_CONFIRM_SELECT_OK_METHOD ((amqp_method_number_t) 0x0055000B) /* 85, 11; 5570571 */ +typedef struct amqp_confirm_select_ok_t_ { + char dummy; /* Dummy field to avoid empty struct */ +} amqp_confirm_select_ok_t; + +/* Class property records. */ +#define AMQP_CONNECTION_CLASS (0x000A) /* 10 */ +typedef struct amqp_connection_properties_t_ { + amqp_flags_t _flags; + char dummy; /* Dummy field to avoid empty struct */ +} amqp_connection_properties_t; + +#define AMQP_CHANNEL_CLASS (0x0014) /* 20 */ +typedef struct amqp_channel_properties_t_ { + amqp_flags_t _flags; + char dummy; /* Dummy field to avoid empty struct */ +} amqp_channel_properties_t; + +#define AMQP_ACCESS_CLASS (0x001E) /* 30 */ +typedef struct amqp_access_properties_t_ { + amqp_flags_t _flags; + char dummy; /* Dummy field to avoid empty struct */ +} amqp_access_properties_t; + +#define AMQP_EXCHANGE_CLASS (0x0028) /* 40 */ +typedef struct amqp_exchange_properties_t_ { + amqp_flags_t _flags; + char dummy; /* Dummy field to avoid empty struct */ +} amqp_exchange_properties_t; + +#define AMQP_QUEUE_CLASS (0x0032) /* 50 */ +typedef struct amqp_queue_properties_t_ { + amqp_flags_t _flags; + char dummy; /* Dummy field to avoid empty struct */ +} amqp_queue_properties_t; + +#define AMQP_BASIC_CLASS (0x003C) /* 60 */ +#define AMQP_BASIC_CONTENT_TYPE_FLAG (1 << 15) +#define AMQP_BASIC_CONTENT_ENCODING_FLAG (1 << 14) +#define AMQP_BASIC_HEADERS_FLAG (1 << 13) +#define AMQP_BASIC_DELIVERY_MODE_FLAG (1 << 12) +#define AMQP_BASIC_PRIORITY_FLAG (1 << 11) +#define AMQP_BASIC_CORRELATION_ID_FLAG (1 << 10) +#define AMQP_BASIC_REPLY_TO_FLAG (1 << 9) +#define AMQP_BASIC_EXPIRATION_FLAG (1 << 8) +#define AMQP_BASIC_MESSAGE_ID_FLAG (1 << 7) +#define AMQP_BASIC_TIMESTAMP_FLAG (1 << 6) +#define AMQP_BASIC_TYPE_FLAG (1 << 5) +#define AMQP_BASIC_USER_ID_FLAG (1 << 4) +#define AMQP_BASIC_APP_ID_FLAG (1 << 3) +#define AMQP_BASIC_CLUSTER_ID_FLAG (1 << 2) +typedef struct amqp_basic_properties_t_ { + amqp_flags_t _flags; + amqp_bytes_t content_type; + amqp_bytes_t content_encoding; + amqp_table_t headers; + uint8_t delivery_mode; + uint8_t priority; + amqp_bytes_t correlation_id; + amqp_bytes_t reply_to; + amqp_bytes_t expiration; + amqp_bytes_t message_id; + uint64_t timestamp; + amqp_bytes_t type; + amqp_bytes_t user_id; + amqp_bytes_t app_id; + amqp_bytes_t cluster_id; +} amqp_basic_properties_t; + +#define AMQP_TX_CLASS (0x005A) /* 90 */ +typedef struct amqp_tx_properties_t_ { + amqp_flags_t _flags; + char dummy; /* Dummy field to avoid empty struct */ +} amqp_tx_properties_t; + +#define AMQP_CONFIRM_CLASS (0x0055) /* 85 */ +typedef struct amqp_confirm_properties_t_ { + amqp_flags_t _flags; + char dummy; /* Dummy field to avoid empty struct */ +} amqp_confirm_properties_t; + +/* API functions for methods */ + +AMQP_PUBLIC_FUNCTION amqp_channel_open_ok_t * AMQP_CALL amqp_channel_open(amqp_connection_state_t state, amqp_channel_t channel); +AMQP_PUBLIC_FUNCTION amqp_channel_flow_ok_t * AMQP_CALL amqp_channel_flow(amqp_connection_state_t state, amqp_channel_t channel, amqp_boolean_t active); +AMQP_PUBLIC_FUNCTION amqp_exchange_declare_ok_t * AMQP_CALL amqp_exchange_declare(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t exchange, amqp_bytes_t type, amqp_boolean_t passive, amqp_boolean_t durable, amqp_table_t arguments); +AMQP_PUBLIC_FUNCTION amqp_exchange_delete_ok_t * AMQP_CALL amqp_exchange_delete(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t exchange, amqp_boolean_t if_unused); +AMQP_PUBLIC_FUNCTION amqp_exchange_bind_ok_t * AMQP_CALL amqp_exchange_bind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t destination, amqp_bytes_t source, amqp_bytes_t routing_key, amqp_table_t arguments); +AMQP_PUBLIC_FUNCTION amqp_exchange_unbind_ok_t * AMQP_CALL amqp_exchange_unbind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t destination, amqp_bytes_t source, amqp_bytes_t routing_key, amqp_table_t arguments); +AMQP_PUBLIC_FUNCTION amqp_queue_declare_ok_t * AMQP_CALL amqp_queue_declare(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_boolean_t passive, amqp_boolean_t durable, amqp_boolean_t exclusive, amqp_boolean_t auto_delete, amqp_table_t arguments); +AMQP_PUBLIC_FUNCTION amqp_queue_bind_ok_t * AMQP_CALL amqp_queue_bind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments); +AMQP_PUBLIC_FUNCTION amqp_queue_purge_ok_t * AMQP_CALL amqp_queue_purge(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue); +AMQP_PUBLIC_FUNCTION amqp_queue_delete_ok_t * AMQP_CALL amqp_queue_delete(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_boolean_t if_unused, amqp_boolean_t if_empty); +AMQP_PUBLIC_FUNCTION amqp_queue_unbind_ok_t * AMQP_CALL amqp_queue_unbind(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t exchange, amqp_bytes_t routing_key, amqp_table_t arguments); +AMQP_PUBLIC_FUNCTION amqp_basic_qos_ok_t * AMQP_CALL amqp_basic_qos(amqp_connection_state_t state, amqp_channel_t channel, uint32_t prefetch_size, uint16_t prefetch_count, amqp_boolean_t global); +AMQP_PUBLIC_FUNCTION amqp_basic_consume_ok_t * AMQP_CALL amqp_basic_consume(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t queue, amqp_bytes_t consumer_tag, amqp_boolean_t no_local, amqp_boolean_t no_ack, amqp_boolean_t exclusive, amqp_table_t arguments); +AMQP_PUBLIC_FUNCTION amqp_basic_cancel_ok_t * AMQP_CALL amqp_basic_cancel(amqp_connection_state_t state, amqp_channel_t channel, amqp_bytes_t consumer_tag); +AMQP_PUBLIC_FUNCTION amqp_basic_recover_ok_t * AMQP_CALL amqp_basic_recover(amqp_connection_state_t state, amqp_channel_t channel, amqp_boolean_t requeue); +AMQP_PUBLIC_FUNCTION amqp_tx_select_ok_t * AMQP_CALL amqp_tx_select(amqp_connection_state_t state, amqp_channel_t channel); +AMQP_PUBLIC_FUNCTION amqp_tx_commit_ok_t * AMQP_CALL amqp_tx_commit(amqp_connection_state_t state, amqp_channel_t channel); +AMQP_PUBLIC_FUNCTION amqp_tx_rollback_ok_t * AMQP_CALL amqp_tx_rollback(amqp_connection_state_t state, amqp_channel_t channel); +AMQP_PUBLIC_FUNCTION amqp_confirm_select_ok_t * AMQP_CALL amqp_confirm_select(amqp_connection_state_t state, amqp_channel_t channel); + +AMQP_END_DECLS + +#endif /* AMQP_FRAMING_H */ diff --git a/c-demo-agent/amqp_ssl_socket.h b/c-demo-agent/amqp_ssl_socket.h new file mode 100644 index 00000000..b9621f32 --- /dev/null +++ b/c-demo-agent/amqp_ssl_socket.h @@ -0,0 +1,161 @@ +/* vim:set ft=c ts=2 sw=2 sts=2 et cindent: */ +/** \file */ +/* + * Copyright 2012-2013 Michael Steinert + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AMQP_SSL_H +#define AMQP_SSL_H + +#include + +AMQP_BEGIN_DECLS + +/** + * Create a new SSL/TLS socket object. + * + * The returned socket object is owned by the \ref amqp_connection_state_t object + * and will be destroyed when the state object is destroyed or a new socket + * object is created. + * + * If the socket object creation fails, the \ref amqp_connection_state_t object + * will not be changed. + * + * The object returned by this function can be retrieved from the + * amqp_connection_state_t object later using the amqp_get_socket() function. + * + * Calling this function may result in the underlying SSL library being initialized. + * \sa amqp_set_initialize_ssl_library() + * + * \param [in,out] state The connection object that owns the SSL/TLS socket + * \return A new socket object or NULL if an error occurred. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_socket_t * +AMQP_CALL +amqp_ssl_socket_new(amqp_connection_state_t state); + +/** + * Set the CA certificate. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] cacert Path to the CA cert file in PEM format. + * + * \return \ref AMQP_STATUS_OK on success an enum + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL +amqp_ssl_socket_set_cacert(amqp_socket_t *self, + const char *cacert); + +/** + * Set the client key. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] cert Path to the client certificate in PEM foramt. + * \param [in] key Path to the client key in PEM format. + * + * \return Zero if successful, -1 otherwise. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL +amqp_ssl_socket_set_key(amqp_socket_t *self, + const char *cert, + const char *key); + +/** + * Set the client key from a buffer. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] cert Path to the client certificate in PEM foramt. + * \param [in] key A buffer containing client key in PEM format. + * \param [in] n The length of the buffer. + * + * \return Zero if successful, -1 otherwise. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +int +AMQP_CALL +amqp_ssl_socket_set_key_buffer(amqp_socket_t *self, + const char *cert, + const void *key, + size_t n); + +/** + * Enable or disable peer verification. + * + * If peer verification is enabled then the common name in the server + * certificate must match the server name. Peer verification is enabled by + * default. + * + * \param [in,out] self An SSL/TLS socket object. + * \param [in] verify Enable or disable peer verification. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL +amqp_ssl_socket_set_verify(amqp_socket_t *self, + amqp_boolean_t verify); + +/** + * Sets whether rabbitmq-c initializes the underlying SSL library. + * + * For SSL libraries that require a one-time initialization across + * a whole program (e.g., OpenSSL) this sets whether or not rabbitmq-c + * will initialize the SSL library when the first call to + * amqp_open_socket() is made. You should call this function with + * do_init = 0 if the underlying SSL library is initialized somewhere else + * the program. + * + * Failing to initialize or double initialization of the SSL library will + * result in undefined behavior + * + * By default rabbitmq-c will initialize the underlying SSL library + * + * NOTE: calling this function after the first socket has been opened with + * amqp_open_socket() will not have any effect. + * + * \param [in] do_initalize If 0 rabbitmq-c will not initialize the SSL + * library, otherwise rabbitmq-c will initialize the + * SSL library + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL +amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize); + +AMQP_END_DECLS + +#endif /* AMQP_SSL_H */ diff --git a/c-demo-agent/amqp_tcp_socket.h b/c-demo-agent/amqp_tcp_socket.h new file mode 100644 index 00000000..4b1b1333 --- /dev/null +++ b/c-demo-agent/amqp_tcp_socket.h @@ -0,0 +1,69 @@ +/* vim:set ft=c ts=2 sw=2 sts=2 et cindent: */ +/** \file */ +/* + * Copyright 2012-2013 Michael Steinert + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/** + * A TCP socket connection. + */ + +#ifndef AMQP_TCP_SOCKET_H +#define AMQP_TCP_SOCKET_H + +#include + +AMQP_BEGIN_DECLS + +/** + * Create a new TCP socket. + * + * Call amqp_socket_close() to release socket resources. + * + * \return A new socket object or NULL if an error occurred. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +amqp_socket_t * +AMQP_CALL +amqp_tcp_socket_new(amqp_connection_state_t state); + +/** + * Assign an open file descriptor to a socket object. + * + * This function must not be used in conjunction with amqp_socket_open(), i.e. + * the socket connection should already be open(2) when this function is + * called. + * + * \param [in,out] self A TCP socket object. + * \param [in] sockfd An open socket descriptor. + * + * \since v0.4.0 + */ +AMQP_PUBLIC_FUNCTION +void +AMQP_CALL +amqp_tcp_socket_set_sockfd(amqp_socket_t *base, int sockfd); + +AMQP_END_DECLS + +#endif /* AMQP_TCP_SOCKET_H */ diff --git a/c-demo-agent/lcfg_static.c b/c-demo-agent/lcfg_static.c new file mode 100644 index 00000000..189e26cb --- /dev/null +++ b/c-demo-agent/lcfg_static.c @@ -0,0 +1,1641 @@ +/* This file is an autogenerated single-file version of liblcfg. + * It is recommended that you update this file on a regular + * basis from the original liblcfg distribution package. + * + * The most recent version of liblcfg is available at + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lcfg_static.h" +/*** begin file include/lcfg/lcfg_string.h ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LCFG_STRING_H +#define LCFG_STRING_H + +struct lcfg_string * lcfg_string_new(); +struct lcfg_string * lcfg_string_new_copy(struct lcfg_string *); +int lcfg_string_set(struct lcfg_string *, const char *); +int lcfg_string_cat_char(struct lcfg_string *, char); +int lcfg_string_cat_cstr(struct lcfg_string *, const char *); +int lcfg_string_cat_uint(struct lcfg_string *, unsigned int); +int lcfg_string_find(struct lcfg_string *, char); +int lcfg_string_rfind(struct lcfg_string *, char); +void lcfg_string_trunc(struct lcfg_string *, unsigned int); +const char * lcfg_string_cstr(struct lcfg_string *); +unsigned int lcfg_string_len(struct lcfg_string *); +void lcfg_string_delete(struct lcfg_string *); + +#endif +/*** end file include/lcfg/lcfg_string.h ***/ +/*** begin file include/lcfg/lcfg_token.h ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LCFG_TOKEN_H +#define LCFG_TOKEN_H + + +enum lcfg_token_type +{ + lcfg_null_token = 0, + lcfg_identifier, + lcfg_equals, + lcfg_string, + lcfg_sbracket_open, + lcfg_sbracket_close, + lcfg_comma, + lcfg_brace_open, + lcfg_brace_close +}; + +extern const char *lcfg_token_map[]; + +struct lcfg_token +{ + enum lcfg_token_type type; + struct lcfg_string *string; + short line; + short col; +}; + + +#endif +/*** end file include/lcfg/lcfg_token.h ***/ +/*** begin file include/lcfg/lcfg_scanner.h ***/ +#/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LCFG_SCANNER_H +#define LCFG_SCANNER_H + + +struct lcfg_scanner; +struct lcfg_token; + +struct lcfg_scanner * lcfg_scanner_new(struct lcfg *, int fd); +enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *); +enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *, struct lcfg_token *); +int lcfg_scanner_has_next(struct lcfg_scanner *); +void lcfg_scanner_delete(struct lcfg_scanner *); + +#endif +/*** end file include/lcfg/lcfg_scanner.h ***/ +/*** begin file include/lcfg/lcfg_parser.h ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LCFG_PARSER_H +#define LCFG_PARSER_H + + +struct lcfg_parser; + +struct lcfg_parser * lcfg_parser_new(struct lcfg *, const char *); +enum lcfg_status lcfg_parser_run(struct lcfg_parser *); +enum lcfg_status lcfg_parser_accept(struct lcfg_parser *, lcfg_visitor_function, void *); +void lcfg_parser_delete(struct lcfg_parser *); +enum lcfg_status lcfg_parser_get(struct lcfg_parser *, const char *, void **, size_t *); + +#endif +/*** end file include/lcfg/lcfg_parser.h ***/ +/*** begin file include/lcfgx/lcfgx_tree.h ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LCFGX_TREE_H +#define LCFGX_TREE_H + + +enum lcfgx_type +{ + lcfgx_string, + lcfgx_list, + lcfgx_map, +}; + +struct lcfgx_tree_node +{ + enum lcfgx_type type; + char *key; /* NULL for root node */ + + union + { + struct + { + void *data; + size_t len; + } string; + struct lcfgx_tree_node *elements; /* in case of list or map type */ + } value; + + struct lcfgx_tree_node *next; +}; + +struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *); + +void lcfgx_tree_delete(struct lcfgx_tree_node *); +void lcfgx_tree_dump(struct lcfgx_tree_node *node, int depth); + +enum lcfgx_path_access +{ + LCFGX_PATH_NOT_FOUND, + LCFGX_PATH_FOUND_WRONG_TYPE_BAD, + LCFGX_PATH_FOUND_TYPE_OK, +}; + +extern const char *lcfgx_path_access_strings[]; + + +enum lcfgx_path_access lcfgx_get(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key, enum lcfgx_type type); +enum lcfgx_path_access lcfgx_get_list(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key); +enum lcfgx_path_access lcfgx_get_map(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key); +enum lcfgx_path_access lcfgx_get_string(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key); + + +#endif + +/*** end file include/lcfgx/lcfgx_tree.h ***/ +/*** begin file src/lcfg_string.c ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +struct lcfg_string +{ + char *str; + unsigned int size; + unsigned int capacity; +}; + +int lcfg_string_set(struct lcfg_string *s, const char *cstr) +{ + lcfg_string_trunc(s, 0); + return lcfg_string_cat_cstr(s, cstr); +} + + +/* make sure new_size bytes fit into the string */ +inline static void lcfg_string_grow(struct lcfg_string *s, unsigned int new_size) +{ + /* always allocate one byte more than needed + * to make _cstr() working in any case without realloc. */ + while( (new_size + 1) > s->capacity ) + { + s->capacity *= 2; + s->str = realloc(s->str, s->capacity); + } +} + +struct lcfg_string *lcfg_string_new() +{ + struct lcfg_string *s = malloc(sizeof(struct lcfg_string)); + assert(s); + + s->capacity = 8; + s->size = 0; + s->str = malloc(s->capacity); + + assert(s->str); + + return s; +} + +struct lcfg_string *lcfg_string_new_copy(struct lcfg_string *s) +{ + struct lcfg_string *s_new = malloc(sizeof(struct lcfg_string)); + assert(s_new); + + s_new->capacity = s->capacity; + s_new->size = s->size; + s_new->str = malloc(s_new->capacity); + + memcpy(s_new->str, s->str, s_new->size); + + return s_new; +} + +int lcfg_string_cat_uint(struct lcfg_string *s, unsigned int i) +{ + unsigned int size_needed = 1; + unsigned int ii = i; + char c; + + while( ii >= 10 ) + { + size_needed++; + ii /= 10; + } + + lcfg_string_grow(s, s->size + size_needed); + + ii = size_needed - 1; + do + { + c = '0' + i % 10; + s->str[s->size + ii--] = c; + i /= 10; + } + while( i != 0 ); + + s->size += size_needed; + + return s->size; +} + +int lcfg_string_find(struct lcfg_string *s, char c) +{ + int i; + + for( i = 0; i < s->size; i++ ) + { + if( s->str[i] == c ) + { + return i; + } + } + + return -1; +} + +int lcfg_string_rfind(struct lcfg_string *s, char c) +{ + int i; + + for( i = s->size - 1; i >= 0; i-- ) + { + if( s->str[i] == c ) + { + return i; + } + } + + return -1; +} + +void lcfg_string_trunc(struct lcfg_string *s, unsigned int max_size) +{ + if( max_size < s->size ) + { + s->size = max_size; + } +} + +int lcfg_string_cat_cstr(struct lcfg_string *s, const char *cstr) +{ + size_t len = strlen(cstr); + + lcfg_string_grow(s, s->size + len); + + memcpy(s->str + s->size, cstr, len); + + s->size += len; + + return s->size; +} + +int lcfg_string_cat_char(struct lcfg_string *s, char c) +{ + lcfg_string_grow(s, s->size + 1); + + s->str[s->size++] = c; + + return s->size; +} + +const char *lcfg_string_cstr(struct lcfg_string *s) +{ + s->str[s->size] = '\0'; + return s->str; +} + +unsigned int lcfg_string_len(struct lcfg_string *s) +{ + return s->size; +} + +void lcfg_string_delete(struct lcfg_string *s) +{ + free(s->str); + free(s); +} +/*** end file src/lcfg_string.c ***/ +/*** begin file src/lcfg_token.c ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +const char *lcfg_token_map[] = +{ + "null_token", + "T_IDENTIFIER", + "`='", + "T_STRING", + "`['", + "`]'", + "`,'", + "`{'", + "`}'" +}; +/*** end file src/lcfg_token.c ***/ +/*** begin file src/lcfg_scanner.c ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#define BUFFER_SIZE 0xff + +struct lcfg_scanner +{ + struct lcfg *lcfg; + + int fd; + char buffer[BUFFER_SIZE]; + int offset; + int size; + int eof; + + short line; + short col; + + struct lcfg_token prepared_token; + int token_eof; +}; + + +static enum lcfg_status lcfg_scanner_buffer_fill(struct lcfg_scanner *s) +{ + if( (s->size = read(s->fd, s->buffer, BUFFER_SIZE)) < 0 ) + { + lcfg_error_set(s->lcfg, "read(): %s", strerror(errno)); + return lcfg_status_error; + } + else if( s->size == 0 ) + { + s->eof = !0; + } + else + { + s->offset = 0; + } + + return lcfg_status_ok; +} + +static inline int lcfg_scanner_char_eof(struct lcfg_scanner *s) +{ + if( s->eof ) + { + return !0; + } + else + { + if( s->size == 0 || s->offset == BUFFER_SIZE ) + { + lcfg_scanner_buffer_fill(s); + } + if( s->size < BUFFER_SIZE && s->offset == s->size ) + { + s->eof = !0; + } + + return s->eof; + } +} + +static enum lcfg_status lcfg_scanner_char_read(struct lcfg_scanner *s, char *c) +{ + if( lcfg_scanner_char_eof(s) ) + { + lcfg_error_set(s->lcfg, "%s", "cannot read beyond eof"); + return lcfg_status_error; + } + + *c = s->buffer[s->offset++]; + + return lcfg_status_ok; +} + +static enum lcfg_status lcfg_scanner_char_peek(struct lcfg_scanner *s, char *c) +{ + if( lcfg_scanner_char_eof(s) ) + { + lcfg_error_set(s->lcfg, "%s", "cannot peek beyond eof"); + return lcfg_status_error; + } + + *c = s->buffer[s->offset]; + + return lcfg_status_ok; +} + +/* the beautiful lowlevel fsm */ +static enum lcfg_status lcfg_scanner_token_read(struct lcfg_scanner *s) +{ + enum scanner_state { start = 0, comm_start, in_oneline, in_multiline, multiline_end, in_identifier, in_str, in_esc, esc_hex_exp_first, esc_hex_exp_second, invalid }; + enum scanner_state state = start; + char c = '\0'; + char hex[3]; + + s->prepared_token.type = lcfg_null_token; + + while( !lcfg_scanner_char_eof(s) ) + { + int consume = !0; + lcfg_scanner_char_peek(s, &c); + + switch( state ) + { + case start: + switch( c ) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '=': + s->prepared_token.type = lcfg_equals; + break; + case '[': + s->prepared_token.type = lcfg_sbracket_open; + break; + case ']': + s->prepared_token.type = lcfg_sbracket_close; + break; + case '{': + s->prepared_token.type = lcfg_brace_open; + break; + case '}': + s->prepared_token.type = lcfg_brace_close; + break; + case ',': + s->prepared_token.type = lcfg_comma; + break; + case '/': + state = comm_start; + break; + case '"': + state = in_str; + lcfg_string_trunc(s->prepared_token.string, 0); + break; + default: + if( isalpha(c) ) + { + lcfg_string_trunc(s->prepared_token.string, 0); + lcfg_string_cat_char(s->prepared_token.string, c); + state = in_identifier; + } + else + { + lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col); + state = invalid; + } + } + break; + case comm_start: + if( c == '/' ) + { + state = in_oneline; + } + else if( c == '*' ) + { + state = in_multiline; + } + else + { + lcfg_error_set(s->lcfg, "parse error: invalid input character `%c' (0x%02x) near line %d, col %d", isprint(c) ? c : '.', c, s->line, s->col); + state = invalid; + } + break; + case in_oneline: + if( c == '\n' ) + { + state = start; + } + break; + case in_multiline: + if( c == '*' ) + { + state = multiline_end; + } + break; + case multiline_end: + if( c == '/' ) + { + state = start; + } + else if( c != '*' ) + { + state = in_multiline; + } + break; + case in_identifier: + if( isalnum(c) || c == '-' || c == '_' ) + { + lcfg_string_cat_char(s->prepared_token.string, c); + } + else + { + s->prepared_token.type = lcfg_identifier; + consume = 0; + state = start; + } + break; + case in_str: + if( c == '"' ) + { + s->prepared_token.type = lcfg_string; + state = start; + } + else if( c == '\\' ) + { + state = in_esc; + } + else + { + lcfg_string_cat_char(s->prepared_token.string, c); + } + break; + case in_esc: + state = in_str; + switch( c ) + { + case '"': + lcfg_string_cat_char(s->prepared_token.string, '"'); + break; + case 'n': + lcfg_string_cat_char(s->prepared_token.string, '\n'); + break; + case 't': + lcfg_string_cat_char(s->prepared_token.string, '\t'); + break; + case 'r': + lcfg_string_cat_char(s->prepared_token.string, '\r'); + break; + case '0': + lcfg_string_cat_char(s->prepared_token.string, '\0'); + break; + case '\\': + lcfg_string_cat_char(s->prepared_token.string, '\\'); + break; + case 'x': + state = esc_hex_exp_first; + break; + default: + lcfg_error_set(s->lcfg, "invalid string escape sequence `%c' near line %d, col %d", c, s->line, s->col); + state = invalid; + } + break; + case esc_hex_exp_first: + if( !isxdigit(c) ) + { + lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col); + state = invalid; + } + hex[0] = c; + state = esc_hex_exp_second; + break; + case esc_hex_exp_second: + if( !isxdigit(c) ) + { + lcfg_error_set(s->lcfg, "invalid hex escape sequence `%c' on line %d column %d", c, s->line, s->col); + state = invalid; + } + hex[1] = c; + hex[2] = '\0'; + lcfg_string_cat_char(s->prepared_token.string, strtoul(hex, NULL, 16)); + state = in_str; + break; + case invalid: + break; + } + /*#include + printf("read %c at line %d column %d, new state is %d\n", isprint(c) ? c : '.', s->line, s->col, state);*/ + + /* this is technically not optimal (token position identified by last char), but it will suffice for now */ + s->prepared_token.line = s->line; + s->prepared_token.col = s->col; + + if( consume ) + { + lcfg_scanner_char_read(s, &c); + if( c == '\n' ) + { + s->line++; + s->col = 1; + } + else + { + s->col++; + } + } + + if( s->prepared_token.type != lcfg_null_token || state == invalid ) + { + break; + } + } + + if( state != start ) + { + if( state != invalid ) + { + lcfg_error_set(s->lcfg, "parse error: premature end of file near line %d, col %d", s->line, s->col); + } + + return lcfg_status_error; + } + + return lcfg_status_ok; +} + +enum lcfg_status lcfg_scanner_init(struct lcfg_scanner *s) +{ + /* prepare the first token */ + return lcfg_scanner_token_read(s); +} + +int lcfg_scanner_has_next(struct lcfg_scanner *s) +{ + return s->prepared_token.type != lcfg_null_token; +} + +enum lcfg_status lcfg_scanner_next_token(struct lcfg_scanner *s, struct lcfg_token *t) +{ + if( !lcfg_scanner_has_next(s) ) + { + lcfg_error_set(s->lcfg, "%s", "cannot access tokenstream beyond eof"); + return lcfg_status_error; + } + + memcpy(t, &s->prepared_token, sizeof(struct lcfg_token)); + t->string = lcfg_string_new_copy(s->prepared_token.string); + + /* prepare the next token */ + return lcfg_scanner_token_read(s); +} + +struct lcfg_scanner *lcfg_scanner_new(struct lcfg *c, int fd) +{ + struct lcfg_scanner *s = malloc(sizeof(struct lcfg_scanner)); + assert(s); + + memset(s, 0, sizeof(struct lcfg_scanner)); + + s->lcfg = c; + s->fd = fd; + + s->line = s->col = 1; + + s->prepared_token.string = lcfg_string_new(); + + return s; +} + +void lcfg_scanner_delete(struct lcfg_scanner *s) +{ + lcfg_string_delete(s->prepared_token.string); + free(s); +} + +/*** end file src/lcfg_scanner.c ***/ +/*** begin file src/lcfg_parser.c ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef strdup +char *strdup(const char *s) +{ + char *sdup; + + size_t len = strlen(s) + 1; + sdup = malloc(len); + + if( sdup != NULL ) + { + memcpy(sdup, s, len); + } + + return sdup; +} +#endif + +struct lcfg_parser_value_pair +{ + char *key; + struct lcfg_string *value; +}; + + +struct lcfg_parser +{ + struct lcfg *lcfg; + char *filename; + struct lcfg_scanner *scanner; + + struct lcfg_parser_value_pair *values; + unsigned int value_length; + unsigned int value_capacity; +}; + +static int lcfg_parser_add_value(struct lcfg_parser *p, const char *key, struct lcfg_string *value) +{ + if( p->value_length == p->value_capacity ) + { + p->value_capacity *= 2; + p->values = realloc(p->values, sizeof(struct lcfg_parser_value_pair) * p->value_capacity); + assert(p->values); + } + + p->values[p->value_length].key = strdup(key); + p->values[p->value_length].value = lcfg_string_new_copy(value); + + return ++p->value_length; +} + +struct lcfg_parser *lcfg_parser_new(struct lcfg *c, const char *filename) +{ + struct lcfg_parser *p = malloc(sizeof(struct lcfg_parser)); + assert(p); + + memset(p, 0, sizeof(struct lcfg_parser)); + + p->filename = strdup(filename); + p->lcfg = c; + + p->value_length = 0; + p->value_capacity = 8; + p->values = malloc(sizeof(struct lcfg_parser_value_pair) * p->value_capacity); + assert(p->values); + + return p; +} + +/* this is a basic push down automata */ +static enum lcfg_status lcfg_parser_parse(struct lcfg_parser *p) +{ + enum state { top_level = 0, exp_equals, exp_value, in_list, in_map, invalid }; + /*const char *state_map[] = { "top_level", "exp_equals", "exp_value", "in_list", "in_map", "invalid" };*/ + + struct state_element + { + enum state s; + int list_counter; + }; + + /* start of ugly preproc stuff */ +#define STATE_STACK_PUSH(t) \ + if( ssi + 1 == state_stack_size ) \ + { \ + state_stack_size *= 2; \ + state_stack = realloc(state_stack, state_stack_size * sizeof(struct state_element)); \ + } \ + state_stack[++ssi].s = t; \ + state_stack[ssi].list_counter = 0 +#define STATE_STACK_POP() ssi-- +#define PATH_PUSH_STR(s) \ + if( lcfg_string_len(current_path) != 0 ) \ + { \ + lcfg_string_cat_char(current_path, '.'); \ + } \ + lcfg_string_cat_cstr(current_path, s); +#define PATH_PUSH_INT(i) \ + if( lcfg_string_len(current_path) != 0 ) \ + { \ + lcfg_string_cat_char(current_path, '.'); \ + } \ + lcfg_string_cat_uint(current_path, i); +#define PATH_POP() \ + if( lcfg_string_rfind(current_path, '.') != -1 ) \ + { \ + lcfg_string_trunc(current_path, lcfg_string_rfind(current_path, '.')); \ + } \ + else \ + { \ + lcfg_string_trunc(current_path, 0); \ + } + /* end of ugly preproc stuff */ + + if( lcfg_scanner_init(p->scanner) != lcfg_status_ok ) + { + return lcfg_status_error; + } + + int state_stack_size = 8; + int ssi = 0; /* ssi = state stack index */ + struct state_element *state_stack = malloc(sizeof(struct state_element) * state_stack_size); + + state_stack[ssi].s = top_level; + state_stack[ssi].list_counter = 0; + + struct lcfg_token t; + struct lcfg_string *current_path = lcfg_string_new(); + + while( lcfg_scanner_has_next(p->scanner) && state_stack[ssi].s != invalid ) + { + if( lcfg_scanner_next_token(p->scanner, &t) != lcfg_status_ok ) + { + free(state_stack); + lcfg_string_delete(t.string); + lcfg_string_delete(current_path); + return lcfg_status_error; + } + + switch( state_stack[ssi].s ) + { + case top_level: + case in_map: + if( t.type == lcfg_identifier ) + { + PATH_PUSH_STR(lcfg_string_cstr(t.string)); + STATE_STACK_PUSH(exp_equals); + } + else if( state_stack[ssi].s == in_map && t.type == lcfg_brace_close ) + { + STATE_STACK_POP(); + PATH_POP(); + } + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected identifier%s", lcfg_token_map[t.type], t.line, t.col, state_stack[ssi].s == in_map ? " or `}'" : ""); + state_stack[ssi].s = invalid; + } + break; + case exp_equals: + if( t.type == lcfg_equals ) + state_stack[ssi].s = exp_value; + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected `='", lcfg_token_map[t.type], t.line, t.col); + state_stack[ssi].s = invalid; + } + break; + case exp_value: + if( t.type == lcfg_string ) + { + lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string); + /*printf("adding string value for single statement\n");*/ + STATE_STACK_POP(); + PATH_POP(); + } + else if( t.type == lcfg_sbracket_open ) + { + state_stack[ssi].s = in_list; + } + else if( t.type == lcfg_brace_open ) + { + state_stack[ssi].s = in_map; + } + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[' or `{'", lcfg_token_map[t.type], t.line, t.col); + state_stack[ssi].s = invalid; + } + break; + case in_list: + if( t.type == lcfg_comma ); /* ignore comma */ + else if( t.type == lcfg_string ) + { + PATH_PUSH_INT(state_stack[ssi].list_counter); + lcfg_parser_add_value(p, lcfg_string_cstr(current_path), t.string); + PATH_POP(); + /*printf("adding string to list pos %d\n", state_stack[ssi].list_counter);*/ + state_stack[ssi].list_counter++; + } + else if( t.type == lcfg_sbracket_open ) + { + PATH_PUSH_INT(state_stack[ssi].list_counter); + /*printf("adding list to list pos %d\n", state_stack[ssi].list_counter);*/ + state_stack[ssi].list_counter++; + STATE_STACK_PUSH(in_list); + } + else if( t.type == lcfg_brace_open ) + { + PATH_PUSH_INT(state_stack[ssi].list_counter); + /*printf("adding map to list pos %d\n", state_stack[ssi].list_counter);*/ + state_stack[ssi].list_counter++; + STATE_STACK_PUSH(in_map); + } + else if( t.type == lcfg_sbracket_close ) + { + PATH_POP(); + STATE_STACK_POP(); + } + else + { + lcfg_error_set(p->lcfg, "invalid token (%s) near line %d column %d: expected string, `[', `{', `,' or `]'", lcfg_token_map[t.type], t.line, t.col); + state_stack[ssi].s = invalid; + } + break; + case invalid: /* unreachable */ + assert(0); + break; + } + + lcfg_string_delete(t.string); + + /*printf(" *** pda: read %s, state is now %s\n", lcfg_token_map[t.type], state_map[state_stack[ssi].s]);*/ + } + + lcfg_string_delete(current_path); + + if( state_stack[ssi].s == top_level && ssi == 0 ) + { + free(state_stack); + return lcfg_status_ok; + } + else + { + free(state_stack); + lcfg_error_set(p->lcfg, "%s", "unexpected end of file: unterminated list/map?"); + return lcfg_status_error; + } +} + +enum lcfg_status lcfg_parser_run(struct lcfg_parser *p) +{ + int fd = open(p->filename, 0); + enum lcfg_status status; + + if( fd < 0 ) + { + lcfg_error_set(p->lcfg, "open(): %s", strerror(errno)); + return lcfg_status_error; + } + + p->scanner = lcfg_scanner_new(p->lcfg, fd); + + status = lcfg_parser_parse(p); + + close(fd); + + return status; +} +enum lcfg_status lcfg_parser_accept(struct lcfg_parser *p, lcfg_visitor_function fn, void *user_data) +{ + int i; + + for( i = 0; i < p->value_length; i++ ) + { + if( fn(p->values[i].key, (void *)lcfg_string_cstr(p->values[i].value), lcfg_string_len(p->values[i].value), user_data) != lcfg_status_ok ) + { + lcfg_error_set(p->lcfg, "%s", "configuration value traversal aborted upon user request"); + return lcfg_status_error; + } + } + + return lcfg_status_ok; +} + +enum lcfg_status lcfg_parser_get(struct lcfg_parser *p, const char *key, void **data, size_t *len) +{ + int i; + + for( i = 0; i < p->value_length; i++ ) + { + if( !strcmp(p->values[i].key, key) ) + { + *data = (void *)lcfg_string_cstr(p->values[i].value); + *len = lcfg_string_len(p->values[i].value); + return lcfg_status_ok; + } + } + + return lcfg_status_error; +} + + +void lcfg_parser_delete(struct lcfg_parser *p) +{ + if( p->scanner != NULL ) + { + lcfg_scanner_delete(p->scanner); + } + + int i; + + for( i = 0; i < p->value_length; i++ ) + { + free(p->values[i].key); + lcfg_string_delete(p->values[i].value); + } + free(p->values); + free(p->filename); + free(p); +} +/*** end file src/lcfg_parser.c ***/ +/*** begin file src/lcfg.c ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +struct lcfg +{ + char error[0xff]; + struct lcfg_parser *parser; +}; + +struct lcfg *lcfg_new(const char *filename) +{ + struct lcfg *c = malloc(sizeof(struct lcfg)); + assert(c); + memset(c, 0, sizeof(struct lcfg)); + + c->parser = lcfg_parser_new(c, filename); + assert(c->parser); + + return c; +} + +void lcfg_delete(struct lcfg *c) +{ + lcfg_parser_delete(c->parser); + free(c); +} + +const char *lcfg_error_get(struct lcfg *c) +{ + return c->error; +} + +enum lcfg_status lcfg_parse(struct lcfg *c) +{ + return lcfg_parser_run(c->parser); +} + +enum lcfg_status lcfg_accept(struct lcfg *c, lcfg_visitor_function fn, void *user_data) +{ + return lcfg_parser_accept(c->parser, fn, user_data); +} + +enum lcfg_status lcfg_value_get(struct lcfg *c, const char *key, void **data, size_t *len) +{ + return lcfg_parser_get(c->parser, key, data, len); +} + +void lcfg_error_set(struct lcfg *c, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(c->error, sizeof(c->error), fmt, ap); + va_end(ap); +} + +/*** end file src/lcfg.c ***/ +/*** begin file src/lcfgx_tree.c ***/ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static struct lcfgx_tree_node *lcfgx_tree_node_new(enum lcfgx_type type, const char *key) +{ + struct lcfgx_tree_node *node = malloc(sizeof(struct lcfgx_tree_node)); + + node->type = type; + + if( key != NULL ) + node->key = strdup(key); + else + node->key = NULL; + + node->next = NULL; + + return node; +} + + +void lcfgx_tree_dump(struct lcfgx_tree_node *node, int depth) +{ +// printf("%s node %p node->key %s depth %i\n", __PRETTY_FUNCTION__, node, node->key, depth); + void sp(int n) + { + int i; + + for( i = 0; i < n; i++ ) + printf("%c", ' '); + } + + sp(depth); + if( node->key != NULL ) + printf("%s", node->key); + else + printf("%s", "(none)"); + + struct lcfgx_tree_node *n; + + switch( node->type ) + { + case lcfgx_string: + printf(" = \"%s\"\n", (char *)node->value.string.data); + break; + + case lcfgx_list: + case lcfgx_map: + printf("%c", '\n'); + n = node->value.elements; + for( ; n != NULL; n = n->next ) + lcfgx_tree_dump(n, depth + 2); + break; + } +} + +static void lcfgx_tree_insert(int pathc, char **pathv, void *data, size_t len, struct lcfgx_tree_node *node) +{ + struct lcfgx_tree_node *n; + + for( n = node->value.elements; n != NULL; n = n->next ) + if( !strcmp(pathv[0], n->key) ) + break; + + if( pathc == 1 ) + { + /* leaf node --> string value */ + if( n == NULL ) + { + /* not found, insert */ + n = lcfgx_tree_node_new(lcfgx_string, pathv[0]); + n->value.string.len = len; + n->value.string.data = malloc(len+1); + memset(n->value.string.data, 0, len+1); + memcpy(n->value.string.data, data, len); + n->next = NULL; + if ( node->value.elements != NULL ) + { + struct lcfgx_tree_node *end = node->value.elements; + while (end->next != NULL) + end = end->next; + end->next = n; + }else + { + node->value.elements = n; + } + } + } + else + { + /* inner node --> (map/list) */ + if( n == NULL ) + { + /* not found, insert it */ + n = lcfgx_tree_node_new(lcfgx_map, pathv[0]); + n->value.elements = NULL; + n->next = NULL; + if ( node->value.elements != NULL ) + { + struct lcfgx_tree_node *end = node->value.elements; + while (end->next != NULL) + end = end->next; + end->next = n; + }else + { + node->value.elements = n; + } + } + + /* recurse into map/list */ + lcfgx_tree_insert(pathc - 1, &pathv[1], data, len, n); + } +} + +enum lcfg_status lcfgx_tree_visitor(const char *key, void *data, size_t len, void *user_data) +{ + struct lcfgx_tree_node *root = user_data; + char path[strlen(key) + 1]; + int path_components = 1; + + strncpy(path, key, strlen(key) + 1); + + while( *key != 0 ) + if( *key++ == '.' ) + path_components++; + + char *pathv[path_components]; + char *token; + char *saveptr = NULL; + int pathc = 0; + + while( (token = strtok_r(pathc == 0 ? path : NULL, ".", &saveptr)) != NULL ) + pathv[pathc++] = token; + + lcfgx_tree_insert(pathc, pathv, data, len, root); + + return lcfg_status_ok; +} + + + +void lcfgx_correct_type(struct lcfgx_tree_node *node) +{ + if( node->type == lcfgx_string ) + return; + + struct lcfgx_tree_node *n = NULL; + if( node->key == NULL ) /* root node */ + n = node; + + if( node->type == lcfgx_map || node->type == lcfgx_list ) + n = node->value.elements; + + if( n == NULL ) + return; + + /* child key is integer, we have a list */ + char *end; + if( strtol(n->key, &end, 10) >= 0 && n->key != end ) + node->type = lcfgx_list; + + struct lcfgx_tree_node *it; + for( it = n; it != NULL; it = it->next ) + lcfgx_correct_type(it); +} + +struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *c) +{ + struct lcfgx_tree_node *root = lcfgx_tree_node_new(lcfgx_map, NULL); + + root->value.elements = NULL; + + lcfg_accept(c, lcfgx_tree_visitor, root); + lcfgx_correct_type(root); + return root; +} + +void lcfgx_tree_delete(struct lcfgx_tree_node *n) +{ + + if( n->type != lcfgx_string ) + { + struct lcfgx_tree_node *m, *next; + + for( m = n->value.elements; m != NULL; ) + { + next = m->next; + lcfgx_tree_delete(m); + m = next; + } + } + else + { + free(n->value.string.data); + } + + if( n->key != NULL ) + free(n->key); + + free(n); +} + +const char *lcfgx_path_access_strings[] = +{ + "LCFGX_PATH_NOT_FOUND", + "LCFGX_PATH_FOUND_WRONG_TYPE_BAD", + "LCFGX_PATH_FOUND_TYPE_OK", +}; + +struct lcfgx_tree_node *cfg_get_recursive(struct lcfgx_tree_node *node, int pathc, char **pathv) +{ + struct lcfgx_tree_node *it;// = node; + + for( it = node->value.elements; it != NULL; it = it->next ) + { + if( strcmp(pathv[0], it->key) == 0 ) + break; + } + + if( it != NULL ) + { + if( pathc == 1 ) + return it; + else + return cfg_get_recursive(it, pathc - 1, &pathv[1]); + } + else + return NULL; +} + + +enum lcfgx_path_access lcfgx_get(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key, enum lcfgx_type type) +{ + char path[strlen(key) + 1]; + int path_components = 1; + + strncpy(path, key, strlen(key) + 1); + + while( *key != 0 ) + if( *key++ == '.' ) + path_components++; + + char *pathv[path_components]; + char *token; + char *saveptr = NULL; + int pathc = 0; + + while( (token = strtok_r(pathc == 0 ? path : NULL, ".", &saveptr)) != NULL ) + pathv[pathc++] = token; + + + struct lcfgx_tree_node *node; + + if( pathc == 0 ) + node = root; + else + node = cfg_get_recursive(root, pathc, pathv); + + if( node == NULL ) + return LCFGX_PATH_NOT_FOUND; + + if( node->type != type ) + return LCFGX_PATH_FOUND_WRONG_TYPE_BAD; + + *n = node; + + return LCFGX_PATH_FOUND_TYPE_OK; +} + +enum lcfgx_path_access lcfgx_get_list(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key) +{ + return lcfgx_get(root, n, key, lcfgx_list); +} + +enum lcfgx_path_access lcfgx_get_map(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key) +{ + return lcfgx_get(root, n, key, lcfgx_map); +} + +enum lcfgx_path_access lcfgx_get_string(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key) +{ + return lcfgx_get(root, n, key, lcfgx_string); +} + +/*** end file src/lcfgx_tree.c ***/ diff --git a/c-demo-agent/lcfg_static.h b/c-demo-agent/lcfg_static.h new file mode 100644 index 00000000..068f37e4 --- /dev/null +++ b/c-demo-agent/lcfg_static.h @@ -0,0 +1,146 @@ +/* This file is an autogenerated single-file version of liblcfg. + * It is recommended that you update this file on a regular + * basis from the original liblcfg distribution package. + * + * The most recent version of liblcfg is available at + * + */ +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LCFG_H +#define LCFG_H + +#include + +struct lcfg; + +enum lcfg_status { lcfg_status_ok, lcfg_status_error }; + +typedef enum lcfg_status (*lcfg_visitor_function)(const char *key, void *data, size_t size, void *user_data); + + +/* open a new config file */ +struct lcfg * lcfg_new(const char *filename); + +/* parse config into memory */ +enum lcfg_status lcfg_parse(struct lcfg *); + +/* visit all configuration elements */ +enum lcfg_status lcfg_accept(struct lcfg *, lcfg_visitor_function, void *); + +/* access a value by path */ +enum lcfg_status lcfg_value_get(struct lcfg *, const char *, void **, size_t *); + +/* return the last error message */ +const char * lcfg_error_get(struct lcfg *); + +/* set error */ +void lcfg_error_set(struct lcfg *, const char *fmt, ...); + +/* destroy lcfg context */ +void lcfg_delete(struct lcfg *); + + +#endif +/* + Copyright (c) 2012, Paul Baecher + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef LCFGX_TREE_H +#define LCFGX_TREE_H + + +enum lcfgx_type +{ + lcfgx_string, + lcfgx_list, + lcfgx_map, +}; + +struct lcfgx_tree_node +{ + enum lcfgx_type type; + char *key; /* NULL for root node */ + + union + { + struct + { + void *data; + size_t len; + } string; + struct lcfgx_tree_node *elements; /* in case of list or map type */ + } value; + + struct lcfgx_tree_node *next; +}; + +struct lcfgx_tree_node *lcfgx_tree_new(struct lcfg *); + +void lcfgx_tree_delete(struct lcfgx_tree_node *); +void lcfgx_tree_dump(struct lcfgx_tree_node *node, int depth); + +enum lcfgx_path_access +{ + LCFGX_PATH_NOT_FOUND, + LCFGX_PATH_FOUND_WRONG_TYPE_BAD, + LCFGX_PATH_FOUND_TYPE_OK, +}; + +extern const char *lcfgx_path_access_strings[]; + + +enum lcfgx_path_access lcfgx_get(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key, enum lcfgx_type type); +enum lcfgx_path_access lcfgx_get_list(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key); +enum lcfgx_path_access lcfgx_get_map(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key); +enum lcfgx_path_access lcfgx_get_string(struct lcfgx_tree_node *root, struct lcfgx_tree_node **n, const char *key); + + +#endif + diff --git a/c-demo-agent/librabbitmq.a b/c-demo-agent/librabbitmq.a new file mode 100644 index 0000000000000000000000000000000000000000..7061ee184050931d2cb31dc6cd5e8b993fdb002b GIT binary patch literal 104484 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 z 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "utils.h" +#include "lcfg_static.h" + + +static void run ( amqp_connection_state_t conn, int log_fd , const char *result_routing_key ) +{ + int received = 0; + amqp_frame_t frame; + + while ( 1 ) + { + amqp_rpc_reply_t ret; + amqp_envelope_t envelope; + + amqp_maybe_release_buffers ( conn ); + ret = amqp_consume_message ( conn, &envelope, NULL, 0 ); + + if ( AMQP_RESPONSE_NORMAL == ret.reply_type ) + { + int i; + amqp_bytes_t body = envelope.message.body; + const char *title = "A new message received:\n"; + + fprintf ( stdout, title, received ); + for ( i = 0; i < body.len; i++ ) + { + fprintf ( stdout, "%c", * ( char* ) ( body.bytes + i ) ); + } + puts ( "\n" ); + + write ( log_fd, ( void * ) title, strlen ( title ) ); + write ( log_fd, body.bytes, body.len ); + write ( log_fd, ( void * ) "\n\n", 2 ); + + /* Send a reply. */ + amqp_basic_properties_t props; + props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_MESSAGE_ID_FLAG; + + printf("message id: %s", (const char*)envelope.message.properties.message_id.bytes); + + props.message_id = amqp_bytes_malloc_dup ( envelope.message.properties.message_id ); + props.content_type = amqp_cstring_bytes ( "text/json" ); + props.delivery_mode = 2; /* persistent delivery mode */ + + const char *result_body = "{\"IsException\": false, \"Result\": [{\"IsException\": false, \"Result\": []}]}"; + + die_on_error ( amqp_basic_publish ( conn, + 1, + amqp_cstring_bytes ( "" ), + amqp_cstring_bytes ( result_routing_key ), + 0, + 0, + &props, + amqp_cstring_bytes ( result_body ) ), + "Publishing" ); + + amqp_destroy_envelope ( &envelope ); + } + else + { + if ( AMQP_RESPONSE_LIBRARY_EXCEPTION == ret.reply_type && + AMQP_STATUS_UNEXPECTED_STATE == ret.library_error ) + { + if ( AMQP_STATUS_OK != amqp_simple_wait_frame ( conn, &frame ) ) + { + return; + } + + if ( AMQP_FRAME_METHOD == frame.frame_type ) + { + switch ( frame.payload.method.id ) + { + case AMQP_BASIC_ACK_METHOD: + /* if we've turned publisher confirms on, and we've published a message + * here is a message being confirmed + */ + + break; + case AMQP_BASIC_RETURN_METHOD: + /* if a published message couldn't be routed and the mandatory flag was set + * this is what would be returned. The message then needs to be read. + */ + { + amqp_message_t message; + ret = amqp_read_message ( conn, frame.channel, &message, 0 ); + + if ( AMQP_RESPONSE_NORMAL != ret.reply_type ) + { + return; + } + + amqp_destroy_message ( &message ); + } + + break; + + case AMQP_CHANNEL_CLOSE_METHOD: + /* a channel.close method happens when a channel exception occurs, this + * can happen by publishing to an exchange that doesn't exist for example + * + * In this case you would need to open another channel redeclare any queues + * that were declared auto-delete, and restart any consumers that were attached + * to the previous channel + */ + return; + + case AMQP_CONNECTION_CLOSE_METHOD: + /* a connection.close method happens when a connection exception occurs, + * this can happen by trying to use a channel that isn't open for example. + * + * In this case the whole connection must be restarted. + */ + return; + + default: + fprintf ( stderr ,"An unexpected method was received %d\n", frame.payload.method.id ); + return; + } + } + } + + } + + received++; + } +} + +static const char* get_config_value ( struct lcfg *cfg, const char *key, int verbose ) +{ + void *data; + size_t len; + + if ( lcfg_value_get ( cfg, key, &data, &len ) != lcfg_status_ok ) + { + fprintf ( stderr, "Key %s is not found in the configuration file", key ); + } + + const char *val = ( const char * ) data; + + if ( verbose ) + { + fprintf ( stdout, "%s = %s\n", key, val ); + } + + return val; +} + +int main ( int argc, char const *const *argv ) +{ + if ( argc != 3 ) + { + printf ( "usage: %s CFG_FILE LOG_FILE\n", argv[0] ); + + return -1; + } + + const char *log_filename = argv[2]; + int flags = O_CREAT | O_APPEND | O_RDWR; + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + int log_fd = open ( log_filename, flags, mode ); + + if ( log_fd < 0 ) + { + fprintf ( stderr, "ERROR: Falied to open the log file '%s'\n", log_filename ); + + exit ( 1 ); + } + + /* Read the configuration file. */ + struct lcfg *cfg = lcfg_new ( argv[1] ); + + if ( lcfg_parse ( cfg ) != lcfg_status_ok ) + { + printf ( "lcfg error: %s\n", lcfg_error_get ( cfg ) ); + + return -1; + } + + /* Read all the configuration parameters. */ + fprintf ( stdout, "Starting Murano agent with the following configuration:\n\n" ); + + const char *host = get_config_value ( cfg, "RABBITMQ_HOST" , 1 ); + int port = atoi ( get_config_value ( cfg, "RABBITMQ_PORT" , 1 ) ); + const char *vhost = get_config_value ( cfg, "RABBITMQ_VHOST" , 1 ); + const char *username = get_config_value ( cfg, "RABBITMQ_USERNAME" , 1 ); + const char *password = get_config_value ( cfg, "RABBITMQ_PASSWORD" , 1 ); + const char *queuename = get_config_value ( cfg, "RABBITMQ_INPUT_QUEUE" , 1 ); + const char *result_routing_key = get_config_value ( cfg, "RABBITMQ_RESULT_ROUTING_KEY", 1 ); + + amqp_connection_state_t conn = amqp_new_connection(); + amqp_socket_t *socket = NULL; + amqp_bytes_t queuename_bytes = amqp_cstring_bytes ( queuename ); + + socket = amqp_tcp_socket_new ( conn ); + if ( !socket ) + { + die ( "creating TCP socket" ); + } + + if ( amqp_socket_open ( socket, host, port ) ) + { + die ( "opening TCP socket" ); + } + + die_on_amqp_error ( amqp_login ( conn, vhost, 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, username, password ), + "Logging in" ); + amqp_channel_open ( conn, 1 ); + die_on_amqp_error ( amqp_get_rpc_reply ( conn ), "Opening channel" ); + + amqp_basic_consume ( conn, 1, queuename_bytes, amqp_empty_bytes, 0, 1, 0, amqp_empty_table ); + die_on_amqp_error ( amqp_get_rpc_reply ( conn ), "Consuming" ); + + puts ( "\nSuccessfully connected to Rabbit MQ server! Ready for messages..." ); + + run ( conn, log_fd , result_routing_key ); + + close ( log_fd ); + lcfg_delete ( cfg ); + + die_on_amqp_error ( amqp_channel_close ( conn, 1, AMQP_REPLY_SUCCESS ), "Closing channel" ); + die_on_amqp_error ( amqp_connection_close ( conn, AMQP_REPLY_SUCCESS ), "Closing connection" ); + die_on_error ( amqp_destroy_connection ( conn ), "Ending connection" ); + + return 0; +} diff --git a/c-demo-agent/murano-agent.conf.example b/c-demo-agent/murano-agent.conf.example new file mode 100644 index 00000000..92bc2da1 --- /dev/null +++ b/c-demo-agent/murano-agent.conf.example @@ -0,0 +1,7 @@ +RABBITMQ_HOST = "localhost" +RABBITMQ_PORT = "5672" +RABBITMQ_USERNAME = "guest" +RABBITMQ_PASSWORD = "guest" +RABBITMQ_VHOST = "/" +RABBITMQ_INPUT_QUEUE = "test queue" +RABBITMQ_RESULT_ROUTING_KEY = "result queue" \ No newline at end of file diff --git a/c-demo-agent/producer.c b/c-demo-agent/producer.c new file mode 100644 index 00000000..4d635fab --- /dev/null +++ b/c-demo-agent/producer.c @@ -0,0 +1,128 @@ + +#include +#include +#include + +#include +#include +#include +#include + +#include "utils.h" + +#define SUMMARY_EVERY_US 1000000 + +static void send_batch ( amqp_connection_state_t conn, + char const *queue_name, + int rate_limit, + int message_count ) +{ + uint64_t start_time = now_microseconds(); + int i; + int sent = 0; + int previous_sent = 0; + uint64_t previous_report_time = start_time; + uint64_t next_summary_time = start_time + SUMMARY_EVERY_US; + + const char *message = "Hello from the producer!"; + amqp_bytes_t message_bytes; + + message_bytes.len = strlen ( message ); + message_bytes.bytes = ( void * ) message; + + for ( i = 0; i < message_count; i++ ) + { + uint64_t now = now_microseconds(); + + amqp_basic_properties_t props; + props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_MESSAGE_ID_FLAG; + props.message_id = amqp_cstring_bytes( " msg_id " ); + props.content_type = amqp_cstring_bytes ( "text/json" ); + props.delivery_mode = 2; /* persistent delivery mode */ + + die_on_error ( amqp_basic_publish ( conn, + 1, + amqp_cstring_bytes ( "" ), + amqp_cstring_bytes ( queue_name ), + 0, + 0, + &props, + message_bytes ), + "Publishing" ); + sent++; + if ( now > next_summary_time ) + { + int countOverInterval = sent - previous_sent; + double intervalRate = countOverInterval / ( ( now - previous_report_time ) / 1000000.0 ); + printf ( "%d ms: Sent %d - %d since last report (%d Hz)\n", + ( int ) ( now - start_time ) / 1000, sent, countOverInterval, ( int ) intervalRate ); + + previous_sent = sent; + previous_report_time = now; + next_summary_time += SUMMARY_EVERY_US; + } + + while ( ( ( i * 1000000.0 ) / ( now - start_time ) ) > rate_limit ) + { + microsleep ( 2000 ); + now = now_microseconds(); + } + } + + { + uint64_t stop_time = now_microseconds(); + int total_delta = stop_time - start_time; + + printf ( "PRODUCER - Message count: %d\n", message_count ); + printf ( "Total time, milliseconds: %d\n", total_delta / 1000 ); + printf ( "Overall messages-per-second: %g\n", ( message_count / ( total_delta / 1000000.0 ) ) ); + } +} + +int main ( int argc, char const *const *argv ) +{ + char const *hostname; + int port, status; + int rate_limit; + int message_count; + amqp_socket_t *socket = NULL; + amqp_connection_state_t conn; + + if ( argc < 5 ) + { + fprintf ( stderr, "Usage: producer host port rate_limit message_count\n" ); + return 1; + } + + hostname = argv[1]; + port = atoi ( argv[2] ); + rate_limit = atoi ( argv[3] ); + message_count = atoi ( argv[4] ); + + conn = amqp_new_connection(); + + socket = amqp_tcp_socket_new ( conn ); + if ( !socket ) + { + die ( "creating TCP socket" ); + } + + status = amqp_socket_open ( socket, hostname, port ); + if ( status ) + { + die ( "opening TCP socket" ); + } + + die_on_amqp_error ( amqp_login ( conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, "guest", "guest" ), + "Logging in" ); + amqp_channel_open ( conn, 1 ); + die_on_amqp_error ( amqp_get_rpc_reply ( conn ), "Opening channel" ); + + send_batch ( conn, "test queue", rate_limit, message_count ); + + die_on_amqp_error ( amqp_channel_close ( conn, 1, AMQP_REPLY_SUCCESS ), "Closing channel" ); + die_on_amqp_error ( amqp_connection_close ( conn, AMQP_REPLY_SUCCESS ), "Closing connection" ); + die_on_error ( amqp_destroy_connection ( conn ), "Ending connection" ); + + return 0; +} diff --git a/c-demo-agent/utils.c b/c-demo-agent/utils.c new file mode 100644 index 00000000..e15dece3 --- /dev/null +++ b/c-demo-agent/utils.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "utils.h" + +/* For usleep */ +/* #define _BSD_SOURCE */ + +#include + +#include +#include + +uint64_t now_microseconds ( void ) +{ + struct timeval tv; + gettimeofday ( &tv, NULL ); + return ( uint64_t ) tv.tv_sec * 1000000 + ( uint64_t ) tv.tv_usec; +} + +void microsleep ( int usec ) +{ + usleep ( usec ); +} + +void die ( const char *fmt, ... ) +{ + va_list ap; + va_start ( ap, fmt ); + vfprintf ( stderr, fmt, ap ); + va_end ( ap ); + fprintf ( stderr, "\n" ); + exit ( 1 ); +} + +void die_on_error ( int x, char const *context ) +{ + if ( x < 0 ) + { + fprintf ( stderr, "%s: %s\n", context, amqp_error_string2 ( x ) ); + exit ( 1 ); + } +} + +void die_on_amqp_error ( amqp_rpc_reply_t x, char const *context ) +{ + switch ( x.reply_type ) + { + case AMQP_RESPONSE_NORMAL: + return; + + case AMQP_RESPONSE_NONE: + fprintf ( stderr, "%s: missing RPC reply type!\n", context ); + break; + + case AMQP_RESPONSE_LIBRARY_EXCEPTION: + fprintf ( stderr, "%s: %s\n", context, amqp_error_string2 ( x.library_error ) ); + break; + + case AMQP_RESPONSE_SERVER_EXCEPTION: + switch ( x.reply.id ) + { + case AMQP_CONNECTION_CLOSE_METHOD: + { + amqp_connection_close_t *m = ( amqp_connection_close_t * ) x.reply.decoded; + fprintf ( stderr, "%s: server connection error %d, message: %.*s\n", + context, + m->reply_code, + ( int ) m->reply_text.len, ( char * ) m->reply_text.bytes ); + break; + } + case AMQP_CHANNEL_CLOSE_METHOD: + { + amqp_channel_close_t *m = ( amqp_channel_close_t * ) x.reply.decoded; + fprintf ( stderr, "%s: server channel error %d, message: %.*s\n", + context, + m->reply_code, + ( int ) m->reply_text.len, ( char * ) m->reply_text.bytes ); + break; + } + default: + fprintf ( stderr, "%s: unknown server error, method id 0x%08X\n", context, x.reply.id ); + break; + } + break; + } + + exit ( 1 ); +} + +static void dump_row ( long count, int numinrow, int *chs ) +{ + int i; + + printf ( "%08lX:", count - numinrow ); + + if ( numinrow > 0 ) + { + for ( i = 0; i < numinrow; i++ ) + { + if ( i == 8 ) + { + printf ( " :" ); + } + printf ( " %02X", chs[i] ); + } + for ( i = numinrow; i < 16; i++ ) + { + if ( i == 8 ) + { + printf ( " :" ); + } + printf ( " " ); + } + printf ( " " ); + for ( i = 0; i < numinrow; i++ ) + { + if ( isprint ( chs[i] ) ) + { + printf ( "%c", chs[i] ); + } + else + { + printf ( "." ); + } + } + } + printf ( "\n" ); +} + +static int rows_eq ( int *a, int *b ) +{ + int i; + + for ( i=0; i<16; i++ ) + if ( a[i] != b[i] ) + { + return 0; + } + + return 1; +} + +void amqp_dump ( void const *buffer, size_t len ) +{ + unsigned char *buf = ( unsigned char * ) buffer; + long count = 0; + int numinrow = 0; + int chs[16]; + int oldchs[16] = {0}; + int showed_dots = 0; + size_t i; + + for ( i = 0; i < len; i++ ) + { + int ch = buf[i]; + + if ( numinrow == 16 ) + { + int i; + + if ( rows_eq ( oldchs, chs ) ) + { + if ( !showed_dots ) + { + showed_dots = 1; + printf ( " .. .. .. .. .. .. .. .. : .. .. .. .. .. .. .. ..\n" ); + } + } + else + { + showed_dots = 0; + dump_row ( count, numinrow, chs ); + } + + for ( i=0; i<16; i++ ) + { + oldchs[i] = chs[i]; + } + + numinrow = 0; + } + + count++; + chs[numinrow++] = ch; + } + + dump_row ( count, numinrow, chs ); + + if ( numinrow != 0 ) + { + printf ( "%08lX:\n", count ); + } +} diff --git a/c-demo-agent/utils.h b/c-demo-agent/utils.h new file mode 100644 index 00000000..372cd642 --- /dev/null +++ b/c-demo-agent/utils.h @@ -0,0 +1,13 @@ +#ifndef agent_utils_h +#define agent_utils_h + +void die(const char *fmt, ...); +extern void die_on_error(int x, char const *context); +extern void die_on_amqp_error(amqp_rpc_reply_t x, char const *context); + +extern void amqp_dump(void const *buffer, size_t len); + +extern uint64_t now_microseconds(void); +extern void microsleep(int usec); + +#endif