Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/libknet/libknet.h b/libknet/libknet.h
index ccd0a3b0..43ce0588 100644
--- a/libknet/libknet.h
+++ b/libknet/libknet.h
@@ -1,2266 +1,2272 @@
/*
* Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved.
*
* Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
* Federico Simoncelli <fsimon@kronosnet.org>
*
* This software licensed under GPL-2.0+, LGPL-2.0+
*/
#ifndef __LIBKNET_H__
#define __LIBKNET_H__
#include <stdint.h>
#include <time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <limits.h>
/**
* @file libknet.h
* @brief kronosnet API include file
* @copyright Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved.
*
* Kronosnet is an advanced VPN system for High Availability applications.
*/
#define KNET_API_VER 2
/*
* libknet limits
*/
/*
* Maximum number of hosts
*/
typedef uint16_t knet_node_id_t;
#define KNET_MAX_HOST 65536
/*
* Maximum number of links between 2 hosts
*/
#define KNET_MAX_LINK 8
/*
* Maximum packet size that should be written to datafd
* see knet_handle_new for details
*/
#define KNET_MAX_PACKET_SIZE 65536
/*
* Buffers used for pretty logging
* host is used to store both ip addresses and hostnames
*/
#define KNET_MAX_HOST_LEN 256
#define KNET_MAX_PORT_LEN 6
/*
* Some notifications can be generated either on TX or RX
*/
#define KNET_NOTIFY_TX 0
#define KNET_NOTIFY_RX 1
/*
* Link flags
*/
/*
* Where possible, set traffic priority to high.
* On Linux this sets the TOS to INTERACTIVE (6),
* see tc-prio(8) for more infomation
*/
#define KNET_LINK_FLAG_TRAFFICHIPRIO (1ULL << 0)
/*
* Handle flags
*/
/*
* Use privileged operations during socket setup.
*/
#define KNET_HANDLE_FLAG_PRIVILEGED (1ULL << 0)
/*
* threads timer resolution (see knet_handle_set_threads_timer_res below)
*/
#define KNET_THREADS_TIMER_RES 200000
typedef struct knet_handle *knet_handle_t;
/*
* Handle structs/API calls
*/
/**
* knet_handle_new
*
* @brief create a new instance of a knet handle
*
* host_id - Each host in a knet is identified with a unique
* ID. when creating a new handle local host_id
* must be specified (0 to UINT16_MAX are all valid).
* It is the user's responsibility to check that the value
* is unique, or bad things might happen.
*
* log_fd - Write file descriptor. If set to a value > 0, it will be used
* to write log packets from libknet to the application.
* Setting to 0 will disable logging from libknet.
* It is possible to enable logging at any given time (see logging API).
* Make sure to either read from this filedescriptor properly and/or
* mark it O_NONBLOCK, otherwise if the fd becomes full, libknet could
* block.
* It is strongly encouraged to use pipes (ex: pipe(2) or pipe2(2)) for
* logging fds due to the atomic nature of writes between fds.
* See also libknet test suite for reference and guidance.
*
* default_log_level -
* If logfd is specified, it will initialize all subsystems to log
* at default_log_level value. (see logging API)
*
* flags - bitwise OR of some of the following flags:
* KNET_HANDLE_FLAG_PRIVILEGED: use privileged operations setting up the
* communication sockets. If disabled, failure to acquire large
* enough socket buffers is ignored but logged. Inadequate buffers
* lead to poor performance.
*
* @return
* on success, a new knet_handle_t is returned.
* on failure, NULL is returned and errno is set.
* knet-specific errno values:
* ENAMETOOLONG - socket buffers couldn't be set big enough and KNET_HANDLE_FLAG_PRIVILEGED was specified
* ERANGE - buffer size readback returned unexpected type
*/
knet_handle_t knet_handle_new(knet_node_id_t host_id,
int log_fd,
uint8_t default_log_level,
uint64_t flags);
/**
* knet_handle_free
* @brief Destroy a knet handle, free all resources
*
* knet_h - pointer to knet_handle_t
*
* @return
* knet_handle_free returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_free(knet_handle_t knet_h);
/**
* knet_handle_set_threads_timer_res
* @brief Change internal thread timer resolution
*
* knet_h - pointer to knet_handle_t
*
* timeres - some threads inside knet will use usleep(timeres)
* to check if any activity has to be performed, or wait
* for the next cycle. 'timeres' (expressed in nano seconds)
* defines this interval, with a default of KNET_THREADS_TIMER_RES
* (200000).
* The lower this value is, the more often knet will perform
* those checks and allows a more (time) precise execution of
* some operations (for example ping/pong), at the cost of higher
* CPU usage.
* Accepted values:
* 0 - reset timer res to default
* 1 - 999 invalid (as it would cause 100% CPU spinning on some
* epoll operations)
* 1000 or higher - valid
*
* Unless you know exactly what you are doing, stay away from
* changing the default or seek written and notarized approval
* from the knet developer team.
*
* @return
* knet_handle_set_threads_timer_res returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_set_threads_timer_res(knet_handle_t knet_h,
useconds_t timeres);
/**
* knet_handle_get_threads_timer_res
* @brief Get internal thread timer resolutions
*
* knet_h - pointer to knet_handle_t
*
* timeres - current timer res value
*
* @return
* knet_handle_set_threads_timer_res returns
* 0 on success and timerres will contain the current value
* -1 on error and errno is set.
*/
int knet_handle_get_threads_timer_res(knet_handle_t knet_h,
useconds_t *timeres);
/**
* knet_handle_enable_sock_notify
* @brief Register a callback to receive socket events
*
* knet_h - pointer to knet_handle_t
*
* sock_notify_fn_private_data
* void pointer to data that can be used to identify
* the callback.
*
* sock_notify_fn
* A callback function that is invoked every time
* a socket in the datafd pool will report an error (-1)
* or an end of read (0) (see socket.7).
* This function MUST NEVER block or add substantial delays.
* The callback is invoked in an internal unlocked area
* to allow calls to knet_handle_add_datafd/knet_handle_remove_datafd
* to swap/replace the bad fd.
* if both err and errno are 0, it means that the socket
* has received a 0 byte packet (EOF?).
* The callback function must either remove the fd from knet
* (by calling knet_handle_remove_fd()) or dup a new fd in its place.
* Failure to do this can cause problems.
*
* @return
* knet_handle_enable_sock_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_enable_sock_notify(knet_handle_t knet_h,
void *sock_notify_fn_private_data,
void (*sock_notify_fn) (
void *private_data,
int datafd,
int8_t channel,
uint8_t tx_rx,
int error,
int errorno)); /* sorry! can't call it errno ;) */
#define KNET_DATAFD_MAX 32
/**
* knet_handle_add_datafd
*
* @brief Install a file descriptor for communication
*
* IMPORTANT: In order to add datafd to knet, knet_handle_enable_sock_notify
* _MUST_ be set and be able to handle both errors (-1) and
* 0 bytes read / write from the provided datafd.
* On read error (< 0) from datafd, the socket is automatically
* removed from polling to avoid spinning on dead sockets.
* It is safe to call knet_handle_remove_datafd even on sockets
* that have been removed.
*
* knet_h - pointer to knet_handle_t
*
* *datafd - read/write file descriptor.
* knet will read data here to send to the other hosts
* and will write data received from the network.
* Each data packet can be of max size KNET_MAX_PACKET_SIZE!
* Applications using knet_send/knet_recv will receive a
* proper error if the packet size is not within boundaries.
* Applications using their own functions to write to the
* datafd should NOT write more than KNET_MAX_PACKET_SIZE.
*
* Please refer to handle.c on how to set up a socketpair.
*
* datafd can be 0, and knet_handle_add_datafd will create a properly
* populated socket pair the same way as ping_test, or a value
* higher than 0. A negative number will return an error.
* On exit knet_handle_free will take care to cleanup the
* socketpair only if they have been created by knet_handle_add_datafd.
*
* It is possible to pass either sockets or normal fds.
* User provided datafd will be marked as non-blocking and close-on-exec.
*
* *channel - This value is analogous to the tag in VLAN tagging.
* A negative value will auto-allocate a channel.
* Setting a value between 0 and 31 will try to allocate that
* specific channel (unless already in use).
*
* It is possible to add up to 32 datafds but be aware that each
* one of them must have a receiving end on the other host.
*
* Example:
* hostA channel 0 will be delivered to datafd on hostB channel 0
* hostA channel 1 to hostB channel 1.
*
* Each channel must have a unique file descriptor.
*
* If your application could have 2 channels on one host and one
* channel on another host, then you can use dst_host_filter
* to manipulate channel values on TX and RX.
*
* @return
* knet_handle_add_datafd returns
* @retval 0 on success,
* *datafd will be populated with a socket if the original value was 0
* or if a specific fd was set, the value is untouched.
* *channel will be populated with a channel number if the original value
* was negative or the value is untouched if a specific channel
* was requested.
*
* @retval -1 on error and errno is set.
* *datafd and *channel are untouched or empty.
*/
int knet_handle_add_datafd(knet_handle_t knet_h, int *datafd, int8_t *channel);
/**
* knet_handle_remove_datafd
* @brief Remove a file descriptor from knet
*
* knet_h - pointer to knet_handle_t
*
* datafd - file descriptor to remove.
* NOTE that if the socket/fd was created by knet_handle_add_datafd,
* the socket will be closed by libknet.
*
* @return
* knet_handle_remove_datafd returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_remove_datafd(knet_handle_t knet_h, int datafd);
/**
* knet_handle_get_channel
* @brief Get the channel associated with a file descriptor
*
* knet_h - pointer to knet_handle_t
*
* datafd - get the channel associated to this datafd
*
* *channel - will contain the result
*
* @return
* knet_handle_get_channel returns
* @retval 0 on success
* and *channel will contain the result
* @retval -1 on error and errno is set.
* and *channel content is meaningless
*/
int knet_handle_get_channel(knet_handle_t knet_h, const int datafd, int8_t *channel);
/**
* knet_handle_get_datafd
* @brief Get the file descriptor associated with a channel
*
* knet_h - pointer to knet_handle_t
*
* channel - get the datafd associated to this channel
*
* *datafd - will contain the result
*
* @return
* knet_handle_get_datafd returns
* @retval 0 on success
* and *datafd will contain the results
* @retval -1 on error and errno is set.
* and *datafd content is meaningless
*/
int knet_handle_get_datafd(knet_handle_t knet_h, const int8_t channel, int *datafd);
/**
* knet_recv
* @brief Receive data from knet nodes
*
* knet_h - pointer to knet_handle_t
*
* buff - pointer to buffer to store the received data
*
* buff_len - buffer length
*
* channel - channel number
*
* @return
* knet_recv is a commodity function to wrap iovec operations
* around a socket. It returns a call to readv(2).
*/
ssize_t knet_recv(knet_handle_t knet_h,
char *buff,
const size_t buff_len,
const int8_t channel);
/**
* knet_send
* @brief Send data to knet nodes
*
* knet_h - pointer to knet_handle_t
*
* buff - pointer to the buffer of data to send
*
* buff_len - length of data to send
*
* channel - channel number
*
* @return
* knet_send is a commodity function to wrap iovec operations
* around a socket. It returns a call to writev(2).
*/
ssize_t knet_send(knet_handle_t knet_h,
const char *buff,
const size_t buff_len,
const int8_t channel);
/**
* knet_send_sync
*
* @brief Synchronously send data to knet nodes
*
* knet_h - pointer to knet_handle_t
*
* buff - pointer to the buffer of data to send
*
* buff_len - length of data to send
*
* channel - data channel to use (see knet_handle_add_datafd(3))
*
* All knet RX/TX operations are async for performance reasons.
* There are applications that might need a sync version of data
* transmission and receive errors in case of failure to deliver
* to another host.
* knet_send_sync bypasses the whole TX async layer and delivers
* data directly to the link layer, and returns errors accordingly.
* knet_send_sync sends only one packet to one host at a time.
* It does NOT support multiple destinations or multicast packets.
* Decision is still based on dst_host_filter_fn.
*
* @return
* knet_send_sync returns 0 on success and -1 on error.
* In addition to normal sendmmsg errors, knet_send_sync can fail
* due to:
*
* @retval ECANCELED - data forward is disabled
* @retval EFAULT - dst_host_filter fatal error
* @retval EINVAL - dst_host_filter did not provide dst_host_ids_entries on unicast pckts
* @retval E2BIG - dst_host_filter did return more than one dst_host_ids_entries on unicast pckts
* @retval ENOMSG - received unknown message type
* @retval EHOSTDOWN - unicast pckt cannot be delivered because dest host is not connected yet
* @retval ECHILD - crypto failed
* @retval EAGAIN - sendmmsg was unable to send all messages and there was no progress during retry
*/
int knet_send_sync(knet_handle_t knet_h,
const char *buff,
const size_t buff_len,
const int8_t channel);
/**
* knet_handle_enable_filter
*
* @brief install a filter to route packets
*
* knet_h - pointer to knet_handle_t
*
* dst_host_filter_fn_private_data
* void pointer to data that can be used to identify
* the callback.
*
* dst_host_filter_fn -
* is a callback function that is invoked every time
* a packet hits datafd (see knet_handle_new(3)).
* the function allows users to tell libknet where the
* packet has to be delivered.
*
* const unsigned char *outdata - is a pointer to the
* current packet
* ssize_t outdata_len - length of the above data
* uint8_t tx_rx - filter is called on tx or rx
* (KNET_NOTIFY_TX, KNET_NOTIFY_RX)
* knet_node_id_t this_host_id - host_id processing the packet
* knet_node_id_t src_host_id - host_id that generated the
* packet
* knet_node_id_t *dst_host_ids - array of KNET_MAX_HOST knet_node_id_t
* where to store the destinations
* size_t *dst_host_ids_entries - number of hosts to send the message
*
* dst_host_filter_fn should return
* -1 on error, packet is discarded.
* 0 packet is unicast and should be sent to dst_host_ids and there are
* dst_host_ids_entries in the buffer.
* 1 packet is broadcast/multicast and is sent all hosts.
* contents of dst_host_ids and dst_host_ids_entries are ignored.
* (see also kronosnetd/etherfilter.* for an example that filters based
* on ether protocol)
*
* @return
* knet_handle_enable_filter returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_enable_filter(knet_handle_t knet_h,
void *dst_host_filter_fn_private_data,
int (*dst_host_filter_fn) (
void *private_data,
const unsigned char *outdata,
ssize_t outdata_len,
uint8_t tx_rx,
knet_node_id_t this_host_id,
knet_node_id_t src_host_id,
int8_t *channel,
knet_node_id_t *dst_host_ids,
size_t *dst_host_ids_entries));
/**
* knet_handle_setfwd
*
* @brief Start packet forwarding
*
* knet_h - pointer to knet_handle_t
*
* enable - set to 1 to allow data forwarding, 0 to disable data forwarding.
*
* @return
* knet_handle_setfwd returns
* 0 on success
* -1 on error and errno is set.
*
* By default data forwarding is off and no traffic will pass through knet until
* it is set on.
*/
int knet_handle_setfwd(knet_handle_t knet_h, unsigned int enabled);
/**
* knet_handle_enable_access_lists
*
* @brief Enable or disable usage of access lists (default: off)
*
* knet_h - pointer to knet_handle_t
*
* enable - set to 1 to use access lists, 0 to disable access_lists.
*
* @return
* knet_handle_enable_access_lists returns
* 0 on success
* -1 on error and errno is set.
*
* access lists are bound to links. There are 2 types of links:
* 1) point to point, where both source and destinations are well known
* at configuration time.
* 2) open links, where only the source is known at configuration time.
*
* knet will automatically generate access lists for point to point links.
*
* For open links, knet provides 4 API calls to manipulate access lists:
* knet_link_add_acl(3), knet_link_rm_acl(3), knet_link_insert_acl(3)
* and knet_link_clear_acl(3).
* Those API calls will work exclusively on open links as they
* are of no use on point to point links.
*
* knet will not enforce any access list unless specifically enabled by
* knet_handle_enable_access_lists(3).
*
* From a security / programming perspective we recommend:
* - create the knet handle
* - enable access lists
* - configure hosts and links
* - configure access lists for open links
*/
int knet_handle_enable_access_lists(knet_handle_t knet_h, unsigned int enabled);
#define KNET_PMTUD_DEFAULT_INTERVAL 60
/**
* knet_handle_pmtud_setfreq
*
* @brief Set the interval between PMTUd scans
*
* knet_h - pointer to knet_handle_t
*
* interval - define the interval in seconds between PMTUd scans
* range from 1 to 86400 (24h)
*
* @return
* knet_handle_pmtud_setfreq returns
* 0 on success
* -1 on error and errno is set.
*
* default interval is 60.
*/
int knet_handle_pmtud_setfreq(knet_handle_t knet_h, unsigned int interval);
/**
* knet_handle_pmtud_getfreq
*
* @brief Get the interval between PMTUd scans
*
* knet_h - pointer to knet_handle_t
*
* interval - pointer where to store the current interval value
*
* @return
* knet_handle_pmtud_setfreq returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_pmtud_getfreq(knet_handle_t knet_h, unsigned int *interval);
/**
* knet_handle_enable_pmtud_notify
*
* @brief install a callback to receive PMTUd changes
*
* knet_h - pointer to knet_handle_t
*
* pmtud_notify_fn_private_data
* void pointer to data that can be used to identify
* the callback.
*
* pmtud_notify_fn
* is a callback function that is invoked every time
* a path MTU size change is detected.
* The function allows libknet to notify the user
* of data MTU, that's the max value that can be send
* onwire without fragmentation. The data MTU will always
* be lower than real link MTU because it accounts for
* protocol overhead, knet packet header and (if configured)
* crypto overhead,
* This function MUST NEVER block or add substantial delays.
*
* @return
* knet_handle_enable_pmtud_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_enable_pmtud_notify(knet_handle_t knet_h,
void *pmtud_notify_fn_private_data,
void (*pmtud_notify_fn) (
void *private_data,
unsigned int data_mtu));
/**
* knet_handle_pmtud_get
*
* @brief Get the current data MTU
*
* knet_h - pointer to knet_handle_t
*
* data_mtu - pointer where to store data_mtu
*
* @return
* knet_handle_pmtud_get returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_pmtud_get(knet_handle_t knet_h,
unsigned int *data_mtu);
#define KNET_MIN_KEY_LEN 256
#define KNET_MAX_KEY_LEN 4096
struct knet_handle_crypto_cfg {
char crypto_model[16];
char crypto_cipher_type[16];
char crypto_hash_type[16];
unsigned char private_key[KNET_MAX_KEY_LEN];
unsigned int private_key_len;
};
/**
* knet_handle_crypto
*
* @brief set up packet cryptographic signing & encryption
*
* knet_h - pointer to knet_handle_t
*
* knet_handle_crypto_cfg -
* pointer to a knet_handle_crypto_cfg structure
*
* crypto_model should contain the model name.
* Currently only "openssl" and "nss" are supported.
* Setting to "none" will disable crypto.
*
* crypto_cipher_type
* should contain the cipher algo name.
* It can be set to "none" to disable
* encryption.
* Currently supported by "nss" model:
* "3des", "aes128", "aes192" and "aes256".
* "openssl" model supports more modes and it strictly
* depends on the openssl build. See: EVP_get_cipherbyname
* openssl API call for details.
*
* crypto_hash_type
* should contain the hashing algo name.
* It can be set to "none" to disable
* hashing.
* Currently supported by "nss" model:
* "md5", "sha1", "sha256", "sha384" and "sha512".
* "openssl" model supports more modes and it strictly
* depends on the openssl build. See: EVP_get_digestbyname
* openssl API call for details.
*
* private_key will contain the private shared key.
* It has to be at least KNET_MIN_KEY_LEN long.
*
* private_key_len
* length of the provided private_key.
*
* Implementation notes/current limitations:
* - enabling crypto, will increase latency as packets have
* to processed.
* - enabling crypto might reduce the overall throughtput
* due to crypto data overhead.
* - re-keying is not implemented yet.
* - private/public key encryption/hashing is not currently
* planned.
* - crypto key must be the same for all hosts in the same
* knet instance.
* - it is safe to call knet_handle_crypto multiple times at runtime.
* The last config will be used.
* IMPORTANT: a call to knet_handle_crypto can fail due to:
* 1) failure to obtain locking
* 2) errors to initializing the crypto level.
* This can happen even in subsequent calls to knet_handle_crypto.
* A failure in crypto init, might leave your traffic unencrypted!
* It's best to stop data forwarding (see knet_handle_setfwd(3)), change crypto config,
* start forward again.
*
* @return
* knet_handle_crypto returns:
* @retval 0 on success
* @retval -1 on error and errno is set.
* @retval -2 on crypto subsystem initialization error. No errno is provided at the moment (yet).
*/
int knet_handle_crypto(knet_handle_t knet_h,
struct knet_handle_crypto_cfg *knet_handle_crypto_cfg);
#define KNET_COMPRESS_THRESHOLD 100
struct knet_handle_compress_cfg {
char compress_model[16];
uint32_t compress_threshold;
int compress_level;
};
/**
* knet_handle_compress
*
* @brief Set up packet compression
*
* knet_h - pointer to knet_handle_t
*
* knet_handle_compress_cfg -
* pointer to a knet_handle_compress_cfg structure
*
* compress_model contains the model name.
* See "compress_level" for the list of accepted values.
* Setting the value to "none" disables compression.
*
* compress_threshold
* tells the transmission thread to NOT compress
* any packets that are smaller than the value
* indicated. Default 100 bytes.
* Set to 0 to reset to the default.
* Set to 1 to compress everything.
* Max accepted value is KNET_MAX_PACKET_SIZE.
*
* compress_level is the "level" parameter for most models:
* zlib: 0 (no compression), 1 (minimal) .. 9 (max compression).
* lz4: 1 (max compression)... 9 (fastest compression).
* lz4hc: 1 (min compression) ... LZ4HC_MAX_CLEVEL (16) or LZ4HC_CLEVEL_MAX (12)
* depending on the version of lz4hc libknet was built with.
* lzma: 0 (minimal) .. 9 (max compression)
* bzip2: 1 (minimal) .. 9 (max compression)
* For lzo2 it selects the algorithm to use:
* 1 : lzo1x_1_compress (default)
* 11 : lzo1x_1_11_compress
* 12 : lzo1x_1_12_compress
* 15 : lzo1x_1_15_compress
* 999: lzo1x_999_compress
* Other values select the default algorithm.
* Please refer to the documentation of the respective
* compression library for guidance about setting this
* value.
*
* Implementation notes:
* - it is possible to enable/disable compression at any time.
* - nodes can be using a different compression algorithm at any time.
* - knet does NOT implement the compression algorithm directly. it relies
* on external libraries for this functionality. Please read
* the libraries man pages to figure out which algorithm/compression
* level is best for the data you are planning to transmit.
*
* @return
* knet_handle_compress returns
* 0 on success
* -1 on error and errno is set. EINVAL means that either the model or the
* level are not supported.
*/
int knet_handle_compress(knet_handle_t knet_h,
struct knet_handle_compress_cfg *knet_handle_compress_cfg);
struct knet_handle_stats {
size_t size;
uint64_t tx_uncompressed_packets;
uint64_t tx_compressed_packets;
uint64_t tx_compressed_original_bytes;
uint64_t tx_compressed_size_bytes;
uint64_t tx_compress_time_ave;
uint64_t tx_compress_time_min;
uint64_t tx_compress_time_max;
uint64_t tx_failed_to_compress;
uint64_t tx_unable_to_compress;
uint64_t rx_compressed_packets;
uint64_t rx_compressed_original_bytes;
uint64_t rx_compressed_size_bytes;
uint64_t rx_compress_time_ave;
uint64_t rx_compress_time_min;
uint64_t rx_compress_time_max;
uint64_t rx_failed_to_decompress;
/* Overhead times, measured in usecs */
uint64_t tx_crypt_packets;
uint64_t tx_crypt_byte_overhead;
uint64_t tx_crypt_time_ave;
uint64_t tx_crypt_time_min;
uint64_t tx_crypt_time_max;
uint64_t rx_crypt_packets;
uint64_t rx_crypt_time_ave;
uint64_t rx_crypt_time_min;
uint64_t rx_crypt_time_max;
};
/**
* knet_handle_get_stats
*
* @brief Get statistics for compression & crypto
*
* knet_h - pointer to knet_handle_t
*
* knet_handle_stats
* pointer to a knet_handle_stats structure
*
* struct_size
* size of knet_handle_stats structure to allow
* for backwards compatibility. libknet will only
* copy this much data into the stats structure
* so that older callers will not get overflowed if
* new fields are added.
*
* @return
* 0 on success
* -1 on error and errno is set.
*
*/
int knet_handle_get_stats(knet_handle_t knet_h, struct knet_handle_stats *stats, size_t struct_size);
/*
* Tell knet_handle_clear_stats whether to clear just the handle stats
* or all of them.
*/
#define KNET_CLEARSTATS_HANDLE_ONLY 1
#define KNET_CLEARSTATS_HANDLE_AND_LINK 2
/**
* knet_handle_clear_stats
*
* @brief Clear knet stats, link and/or handle
*
* knet_h - pointer to knet_handle_t
*
* clear_option - Which stats to clear, must be one of
*
* KNET_CLEARSTATS_HANDLE_ONLY or
* KNET_CLEARSTATS_HANDLE_AND_LINK
*
* @return
* 0 on success
* -1 on error and errno is set.
*
*/
int knet_handle_clear_stats(knet_handle_t knet_h, int clear_option);
struct knet_crypto_info {
const char *name; /* openssl,nss,etc.. */
uint8_t properties; /* currently unused */
char pad[256]; /* currently unused */
};
/**
* knet_get_crypto_list
*
* @brief Get a list of supported crypto libraries
*
* crypto_list - array of struct knet_crypto_info *
* If NULL then only the number of structs is returned in crypto_list_entries
* to allow the caller to allocate sufficient space.
* libknet does not allow more than 256 crypto methods at the moment.
* it is safe to allocate 256 structs to avoid calling
* knet_get_crypto_list twice.
*
* crypto_list_entries - returns the number of structs in crypto_list
*
* @return
* knet_get_crypto_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_get_crypto_list(struct knet_crypto_info *crypto_list,
size_t *crypto_list_entries);
struct knet_compress_info {
const char *name; /* bzip2, lz4, etc.. */
uint8_t properties; /* currently unused */
char pad[256]; /* currently unused */
};
/**
* knet_get_compress_list
*
* @brief Get a list of support compression types
*
* compress_list - array of struct knet_compress_info *
* If NULL then only the number of structs is returned in compress_list_entries
* to allow the caller to allocate sufficient space.
* libknet does not allow more than 256 compress methods at the moment.
* it is safe to allocate 256 structs to avoid calling
* knet_get_compress_list twice.
*
* compress_list_entries - returns the number of structs in compress_list
*
* @return
* knet_get_compress_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_get_compress_list(struct knet_compress_info *compress_list,
size_t *compress_list_entries);
/*
* host structs/API calls
*/
/**
* knet_host_add
*
* @brief Add a new host ID to knet
*
* knet_h - pointer to knet_handle_t
*
* host_id - each host in a knet is identified with a unique ID
* (see also knet_handle_new(3))
*
* @return
* knet_host_add returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_add(knet_handle_t knet_h, knet_node_id_t host_id);
/**
* knet_host_remove
*
* @brief Remove a host ID from knet
*
* knet_h - pointer to knet_handle_t
*
* host_id - each host in a knet is identified with a unique ID
* (see also knet_handle_new(3))
*
* @return
* knet_host_remove returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_remove(knet_handle_t knet_h, knet_node_id_t host_id);
/**
* knet_host_set_name
*
* @brief Set the name of a knet host
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* name - this name will be used for pretty logging and eventually
* search for hosts (see also knet_handle_host_get_name(2) and knet_handle_host_get_id(3)).
* Only up to KNET_MAX_HOST_LEN - 1 bytes will be accepted and
* name has to be unique for each host.
*
* @return
* knet_host_set_name returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_set_name(knet_handle_t knet_h, knet_node_id_t host_id,
const char *name);
/**
* knet_host_get_name_by_host_id
*
* @brief Get the name of a host given its ID
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* name - pointer to a preallocated buffer of at least size KNET_MAX_HOST_LEN
* where the current host name will be stored
* (as set by knet_host_set_name or default by knet_host_add)
*
* @return
* knet_host_get_name_by_host_id returns:
* 0 on success
* -1 on error and errno is set (name is left untouched)
*/
int knet_host_get_name_by_host_id(knet_handle_t knet_h, knet_node_id_t host_id,
char *name);
/**
* knet_host_get_id_by_host_name
*
* @brief Get the ID of a host given its name
*
* knet_h - pointer to knet_handle_t
*
* name - name to lookup, max len KNET_MAX_HOST_LEN
*
* host_id - where to store the result
*
* @return
* knet_host_get_id_by_host_name returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_id_by_host_name(knet_handle_t knet_h, const char *name,
knet_node_id_t *host_id);
/**
* knet_host_get_host_list
*
* @brief Get a list of hosts known to knet
*
* knet_h - pointer to knet_handle_t
*
* host_ids - array of at lest KNET_MAX_HOST size
*
* host_ids_entries -
* number of entries writted in host_ids
*
* @return
* knet_host_get_host_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_host_list(knet_handle_t knet_h,
knet_node_id_t *host_ids, size_t *host_ids_entries);
/*
* define switching policies
*/
#define KNET_LINK_POLICY_PASSIVE 0
#define KNET_LINK_POLICY_ACTIVE 1
#define KNET_LINK_POLICY_RR 2
/**
* knet_host_set_policy
*
* knet_h - pointer to knet_handle_t
*
* @brief Set the switching policy for a host's links
*
* host_id - see knet_host_add(3)
*
* policy - there are currently 3 kind of simple switching policies
* based on link configuration.
* KNET_LINK_POLICY_PASSIVE - the active link with the lowest
* priority will be used.
* if one or more active links share
* the same priority, the one with
* lowest link_id will be used.
*
* KNET_LINK_POLICY_ACTIVE - all active links will be used
* simultaneously to send traffic.
* link priority is ignored.
*
* KNET_LINK_POLICY_RR - round-robin policy, every packet
* will be send on a different active
* link.
*
* @return
* knet_host_set_policy returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_set_policy(knet_handle_t knet_h, knet_node_id_t host_id,
uint8_t policy);
/**
* knet_host_get_policy
*
* @brief Get the switching policy for a host's links
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* policy - will contain the current configured switching policy.
* Default is passive when creating a new host.
*
* @return
* knet_host_get_policy returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_policy(knet_handle_t knet_h, knet_node_id_t host_id,
uint8_t *policy);
/**
* knet_host_enable_status_change_notify
*
* @brief Install a callback to get host status change events
*
* knet_h - pointer to knet_handle_t
*
* host_status_change_notify_fn_private_data -
* void pointer to data that can be used to identify
* the callback
*
* host_status_change_notify_fn -
* is a callback function that is invoked every time
* there is a change in the host status.
* host status is identified by:
* - reachable, this host can send/receive data to/from host_id
* - remote, 0 if the host_id is connected locally or 1 if
* the there is one or more knet host(s) in between.
* NOTE: re-switching is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* - external, 0 if the host_id is configured locally or 1 if
* it has been added from remote nodes config.
* NOTE: dynamic topology is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* This function MUST NEVER block or add substantial delays.
*
* @return
* knet_host_status_change_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_enable_status_change_notify(knet_handle_t knet_h,
void *host_status_change_notify_fn_private_data,
void (*host_status_change_notify_fn) (
void *private_data,
knet_node_id_t host_id,
uint8_t reachable,
uint8_t remote,
uint8_t external));
/*
* define host status structure for quick lookup
* struct is in flux as more stats will be added soon
*
* reachable host_id can be seen either directly connected
* or via another host_id
*
* remote 0 = node is connected locally, 1 is visible via
* via another host_id
*
* external 0 = node is configured/known locally,
* 1 host_id has been received via another host_id
*/
struct knet_host_status {
uint8_t reachable;
uint8_t remote;
uint8_t external;
/* add host statistics */
};
/**
* knet_host_status_get
*
* @brief Get the status of a host
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* status - pointer to knet_host_status struct
*
* @return
* knet_handle_pmtud_get returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_status(knet_handle_t knet_h, knet_node_id_t host_id,
struct knet_host_status *status);
/*
* link structs/API calls
*
* every host allocated/managed by knet_host_* has
* KNET_MAX_LINK structures to define the network
* paths that connect 2 hosts.
*
* Each link is identified by a link_id that has a
* values between 0 and KNET_MAX_LINK - 1.
*
* KNOWN LIMITATIONS:
*
* - let's assume the scenario where two hosts are connected
* with any number of links. link_id must match on both sides.
* If host_id 0 link_id 0 is configured to connect IP1 to IP2 and
* host_id 0 link_id 1 is configured to connect IP3 to IP4,
* host_id 1 link_id 0 _must_ connect IP2 to IP1 and likewise
* host_id 1 link_id 1 _must_ connect IP4 to IP3.
* We might be able to lift this restriction in future, by using
* other data to determine src/dst link_id, but for now, deal with it.
*/
/*
* commodity functions to convert strings to sockaddr and viceversa
*/
/**
* knet_strtoaddr
*
* @brief Convert a hostname string to an address
*
* host - IPaddr/hostname to convert
* be aware only the first IP address will be returned
* in case a hostname resolves to multiple IP
*
* port - port to connect to
*
* ss - sockaddr_storage where to store the converted data
*
* sslen - len of the sockaddr_storage
*
* @return
* knet_strtoaddr returns same error codes as getaddrinfo
*
*/
int knet_strtoaddr(const char *host, const char *port,
struct sockaddr_storage *ss, socklen_t sslen);
/**
* knet_addrtostr
*
* @brief Convert an address to a host name
*
* ss - sockaddr_storage to convert
*
* sslen - len of the sockaddr_storage
*
* host - IPaddr/hostname where to store data
* (recommended size: KNET_MAX_HOST_LEN)
*
* port - port buffer where to store data
* (recommended size: KNET_MAX_PORT_LEN)
*
* @return
* knet_strtoaddr returns same error codes as getnameinfo
*/
int knet_addrtostr(const struct sockaddr_storage *ss, socklen_t sslen,
char *addr_buf, size_t addr_buf_size,
char *port_buf, size_t port_buf_size);
#define KNET_TRANSPORT_LOOPBACK 0
#define KNET_TRANSPORT_UDP 1
#define KNET_TRANSPORT_SCTP 2
#define KNET_MAX_TRANSPORTS UINT8_MAX
/*
* The Loopback transport is only valid for connections to localhost, the host
* with the same node_id specified in knet_handle_new(). Only one link of this
* type is allowed. Data sent down a LOOPBACK link will be copied directly from
* the knet send datafd to the knet receive datafd so the application must be set
* up to take data from that socket at least as often as it is sent or deadlocks
* could occur. If used, a LOOPBACK link must be the only link configured to the
* local host.
*/
struct knet_transport_info {
const char *name; /* UDP/SCTP/etc... */
uint8_t id; /* value that can be used for link_set_config */
uint8_t properties; /* currently unused */
char pad[256]; /* currently unused */
};
/**
* knet_get_transport_list
*
* @brief Get a list of the transports support by this build of knet
*
* transport_list - an array of struct transport_info that must be
* at least of size struct transport_info * KNET_MAX_TRANSPORTS
*
* transport_list_entries - pointer to a size_t where to store how many transports
* are available in this build of libknet.
*
* @return
* knet_get_transport_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_get_transport_list(struct knet_transport_info *transport_list,
size_t *transport_list_entries);
/**
* knet_get_transport_name_by_id
*
* @brief Get a transport name from its ID number
*
* transport - one of the KNET_TRANSPORT_xxx constants
*
* @return
* knet_get_transport_name_by_id returns:
*
* @retval pointer to the name on success or
* @retval NULL on error and errno is set.
*/
const char *knet_get_transport_name_by_id(uint8_t transport);
/**
* knet_get_transport_id_by_name
*
* @brief Get a transport ID from its name
*
* name - transport name (UDP/SCTP/etc)
*
* @return
* knet_get_transport_name_by_id returns:
*
* @retval KNET_MAX_TRANSPORTS on error and errno is set accordingly
* @retval KNET_TRANSPORT_xxx on success.
*/
uint8_t knet_get_transport_id_by_name(const char *name);
#define KNET_TRANSPORT_DEFAULT_RECONNECT_INTERVAL 1000
/**
* knet_handle_set_transport_reconnect_interval
*
* @brief Set the interval between transport attempts to reconnect a failed link
*
* knet_h - pointer to knet_handle_t
*
* msecs - milliseconds
*
* @return
* knet_handle_set_transport_reconnect_interval returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_set_transport_reconnect_interval(knet_handle_t knet_h, uint32_t msecs);
/**
* knet_handle_get_transport_reconnect_interval
*
* @brief Get the interval between transport attempts to reconnect a failed link
*
* knet_h - pointer to knet_handle_t
*
* msecs - milliseconds
*
* @return
* knet_handle_get_transport_reconnect_interval returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_get_transport_reconnect_interval(knet_handle_t knet_h, uint32_t *msecs);
/**
* knet_link_set_config
*
* @brief Configure the link to a host
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* transport - one of the KNET_TRANSPORT_xxx constants
*
* src_addr - sockaddr_storage that can be either IPv4 or IPv6
*
* dst_addr - sockaddr_storage that can be either IPv4 or IPv6
* this can be null if we don't know the incoming
* IP address/port and the link will remain quiet
* till the node on the other end will initiate a
* connection
*
* flags - KNET_LINK_FLAG_*
*
* @return
* knet_link_set_config returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t transport,
struct sockaddr_storage *src_addr,
struct sockaddr_storage *dst_addr,
uint64_t flags);
/**
* knet_link_get_config
*
* @brief Get the link configutation information
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* transport - see knet_link_set_config(3)
*
* src_addr - sockaddr_storage that can be either IPv4 or IPv6
*
* dst_addr - sockaddr_storage that can be either IPv4 or IPv6
*
* dynamic - 0 if dst_addr is static or 1 if dst_addr is dynamic.
* In case of 1, dst_addr can be NULL and it will be left
* untouched.
*
* flags - KNET_LINK_FLAG_*
*
* @return
* knet_link_get_config returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_get_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *transport,
struct sockaddr_storage *src_addr,
struct sockaddr_storage *dst_addr,
uint8_t *dynamic,
uint64_t *flags);
/**
* knet_link_clear_config
*
* @brief Clear link information and disconnect the link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* @return
* knet_link_clear_config returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_clear_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id);
/*
* Access lists management for open links
* see also knet_handle_enable_access_lists(3)
*/
-/*
+/**
+ * check_type_t
+ * @brief address type enum for knet access lists
+ *
* CHECK_TYPE_ADDRESS is the equivalent of a single entry / IP address.
* for example: 10.1.9.3
* and the entry is stored in ss1. ss2 can be NULL.
*
* CHECK_TYPE_MASK is used to configure network/netmask.
* for example: 192.168.0.0/24
* the network is stored in ss1 and the netmask in ss2.
*
* CHECK_TYPE_RANGE defines a value / range of ip addresses.
* for example: 172.16.0.1-172.16.0.10
* the start is stored in ss1 and the end in ss2.
*
* Please be aware that the above examples refer only to IP based protocols.
* Other protocols might use ss1 and ss2 in slightly different ways.
* At the moment knet only supports IP based protocol, though that might change
* in the future.
*/
typedef enum {
CHECK_TYPE_ADDRESS,
CHECK_TYPE_MASK,
CHECK_TYPE_RANGE
} check_type_t;
-/*
+/**
+ * check_acceptreject_t
+ * @brief enum for accept/reject in knet access lists
+ *
* accept or reject incoming packets defined in the access list entry
*/
typedef enum {
CHECK_ACCEPT,
CHECK_REJECT
} check_acceptreject_t;
/**
* knet_link_add_acl
*
* @brief Add access list entry to an open link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* ss1 / ss2 / type / acceptreject - see typedef definitions for details
*
* IMPORTANT: the order in which access lists are added is critical and it
* is left to the user to add them in the right order. knet
* will not attempt to logically sort them.
*
* For example:
* 1 - accept from 10.0.0.0/8
* 2 - reject from 10.0.0.1/32
*
* is not the same as:
*
* 1 - reject from 10.0.0.1/32
* 2 - accept from 10.0.0.0/8
*
* In the first example, rule number 2 will never match because
* packets from 10.0.0.1 will be accepted by rule number 1.
*
* @return
* knet_link_add_acl
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_add_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
struct sockaddr_storage *ss1,
struct sockaddr_storage *ss2,
check_type_t type, check_acceptreject_t acceptreject);
/**
* knet_link_insert_acl
*
* @brief Insert access list entry to an open link at given index
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* index - insert at position "index" where 0 is the first entry and -1
* appends to the current list.
*
* ss1 / ss2 / type / acceptreject - see typedef definitions for details
*
* @return
* knet_link_insert_acl
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_insert_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
int index,
struct sockaddr_storage *ss1,
struct sockaddr_storage *ss2,
check_type_t type, check_acceptreject_t acceptreject);
/**
* knet_link_rm_acl
*
* @brief Remove access list entry from an open link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* ss1 / ss2 / type / acceptreject - see typedef definitions for details
*
* IMPORTANT: the data passed to this API call must match exactly that passed
* to knet_link_add_acl(3).
*
* @return
* knet_link_rm_acl
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_rm_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
struct sockaddr_storage *ss1,
struct sockaddr_storage *ss2,
check_type_t type, check_acceptreject_t acceptreject);
/**
* knet_link_clear_acl
*
* @brief Remove all access list entries from an open link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* @return
* knet_link_clear_acl
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_clear_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id);
/**
* knet_link_set_enable
*
* @brief Enable traffic on a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* enabled - 0 disable the link, 1 enable the link
*
* @return
* knet_link_set_enable returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
unsigned int enabled);
/**
* knet_link_get_enable
*
* @brief Find out whether a link is enabled or not
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* enabled - 0 disable the link, 1 enable the link
*
* @return
* knet_link_get_enable returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
unsigned int *enabled);
#define KNET_LINK_DEFAULT_PING_INTERVAL 1000 /* 1 second */
#define KNET_LINK_DEFAULT_PING_TIMEOUT 2000 /* 2 seconds */
#define KNET_LINK_DEFAULT_PING_PRECISION 2048 /* samples */
/**
* knet_link_set_ping_timers
*
* @brief Set the ping timers for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* interval - specify the ping interval in milliseconds.
*
* timeout - if no pong is received within this time,
* the link is declared dead, in milliseconds.
* NOTE: in future it will be possible to set timeout to 0
* for an autocalculated timeout based on interval, pong_count
* and latency. The API already accept 0 as value and it will
* return ENOSYS / -1. Once the automatic calculation feature
* will be implemented, this call will only return EINVAL
* for incorrect values.
*
* precision - how many values of latency are used to calculate
* the average link latency (see also knet_link_get_status(3))
*
* @return
* knet_link_set_ping_timers returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
time_t interval, time_t timeout, unsigned int precision);
/**
* knet_link_get_ping_timers
*
* @brief Get the ping timers for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* interval - ping interval
*
* timeout - if no pong is received within this time,
* the link is declared dead
*
* precision - how many values of latency are used to calculate
* the average link latency (see also knet_link_get_status(3))
*
* @return
* knet_link_get_ping_timers returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
time_t *interval, time_t *timeout, unsigned int *precision);
#define KNET_LINK_DEFAULT_PONG_COUNT 5
/**
* knet_link_set_pong_count
*
* @brief Set the pong count for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* pong_count - how many valid ping/pongs before a link is marked UP.
* default: 5, value should be > 0
*
* @return
* knet_link_set_pong_count returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t pong_count);
/**
* knet_link_get_pong_count
*
* @brief Get the pong count for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* pong_count - how many valid ping/pongs before a link is marked UP.
* default: 5, value should be > 0
*
* @return
* knet_link_get_pong_count returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *pong_count);
/**
* knet_link_set_priority
*
* @brief Set the priority for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* priority - specify the switching priority for this link
* see also knet_host_set_policy
*
* @return
* knet_link_set_priority returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t priority);
/**
* knet_link_get_priority
*
* @brief Get the priority for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* priority - gather the switching priority for this link
* see also knet_host_set_policy
*
* @return
* knet_link_get_priority returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *priority);
/**
* knet_link_get_link_list
*
* @brief Get a list of links connecting a host
*
* knet_h - pointer to knet_handle_t
*
* link_ids - array of at lest KNET_MAX_LINK size
* with the list of configured links for a certain host.
*
* link_ids_entries -
* number of entries contained in link_ids
*
* @return
* knet_link_get_link_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_link_list(knet_handle_t knet_h, knet_node_id_t host_id,
uint8_t *link_ids, size_t *link_ids_entries);
/*
* define link status structure for quick lookup
*
* src/dst_{ipaddr,port} strings are filled by
* getnameinfo(3) when configuring the link.
* if the link is dynamic (see knet_link_set_config(3))
* dst_ipaddr/port will contain ipaddr/port of the currently
* connected peer or "Unknown" if it was not possible
* to determine the ipaddr/port at runtime.
*
* enabled see also knet_link_set/get_enable.
*
* connected the link is connected to a peer and ping/pong traffic
* is flowing.
*
* dynconnected the link has dynamic ip on the other end, and
* we can see the other host is sending pings to us.
*
* latency average latency of this link
* see also knet_link_set/get_timeout.
*
* pong_last if the link is down, this value tells us how long
* ago this link was active. A value of 0 means that the link
* has never been active.
*
* knet_link_stats structure that contains details statistics for the link
*/
#define MAX_LINK_EVENTS 16
struct knet_link_stats {
/* onwire values */
uint64_t tx_data_packets;
uint64_t rx_data_packets;
uint64_t tx_data_bytes;
uint64_t rx_data_bytes;
uint64_t rx_ping_packets;
uint64_t tx_ping_packets;
uint64_t rx_ping_bytes;
uint64_t tx_ping_bytes;
uint64_t rx_pong_packets;
uint64_t tx_pong_packets;
uint64_t rx_pong_bytes;
uint64_t tx_pong_bytes;
uint64_t rx_pmtu_packets;
uint64_t tx_pmtu_packets;
uint64_t rx_pmtu_bytes;
uint64_t tx_pmtu_bytes;
/* Only filled in when requested */
uint64_t tx_total_packets;
uint64_t rx_total_packets;
uint64_t tx_total_bytes;
uint64_t rx_total_bytes;
uint64_t tx_total_errors;
uint64_t tx_total_retries;
uint32_t tx_pmtu_errors;
uint32_t tx_pmtu_retries;
uint32_t tx_ping_errors;
uint32_t tx_ping_retries;
uint32_t tx_pong_errors;
uint32_t tx_pong_retries;
uint32_t tx_data_errors;
uint32_t tx_data_retries;
/* measured in usecs */
uint32_t latency_min;
uint32_t latency_max;
uint32_t latency_ave;
uint32_t latency_samples;
/* how many times the link has been going up/down */
uint32_t down_count;
uint32_t up_count;
/*
* circular buffer of time_t structs collecting the history
* of up/down events on this link.
* the index indicates current/last event.
* it is safe to walk back the history by decreasing the index
*/
time_t last_up_times[MAX_LINK_EVENTS];
time_t last_down_times[MAX_LINK_EVENTS];
int8_t last_up_time_index;
int8_t last_down_time_index;
/* Always add new stats at the end */
};
struct knet_link_status {
size_t size; /* For ABI checking */
char src_ipaddr[KNET_MAX_HOST_LEN];
char src_port[KNET_MAX_PORT_LEN];
char dst_ipaddr[KNET_MAX_HOST_LEN];
char dst_port[KNET_MAX_PORT_LEN];
uint8_t enabled; /* link is configured and admin enabled for traffic */
uint8_t connected; /* link is connected for data (local view) */
uint8_t dynconnected; /* link has been activated by remote dynip */
unsigned long long latency; /* average latency computed by fix/exp */
struct timespec pong_last;
unsigned int mtu; /* current detected MTU on this link */
unsigned int proto_overhead; /* contains the size of the IP protocol, knet headers and
* crypto headers (if configured). This value is filled in
* ONLY after the first PMTUd run on that given link,
* and can change if link configuration or crypto configuration
* changes at runtime.
* WARNING: in general mtu + proto_overhead might or might
* not match the output of ifconfig mtu due to crypto
* requirements to pad packets to some specific boundaries. */
/* Link statistics */
struct knet_link_stats stats;
};
/**
* knet_link_get_status
*
* @brief Get the status (and statistics) for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* status - pointer to knet_link_status struct
*
* struct_size - max size of knet_link_status - allows library to
* add fields without ABI change. Returned structure
* will be truncated to this length and .size member
* indicates the full size.
*
* @return
* knet_link_get_status returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_status(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
struct knet_link_status *status, size_t struct_size);
/**
* knet_link_enable_status_change_notify
*
* @brief Install a callback to get a link status change events
*
* knet_h - pointer to knet_handle_t
*
* host_status_change_notify_fn_private_data -
* void pointer to data that can be used to identify
* the callback
*
* host_status_change_notify_fn -
* is a callback function that is invoked every time
* there is a change in a link status.
* host status is identified by:
* - connected, 0 if the link has been disconnected, 1 if the link
* is connected.
* - remote, 0 if the host_id is connected locally or 1 if
* the there is one or more knet host(s) in between.
* NOTE: re-switching is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* - external, 0 if the host_id is configured locally or 1 if
* it has been added from remote nodes config.
* NOTE: dynamic topology is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* This function MUST NEVER block or add substantial delays.
*
* @return
* knet_host_status_change_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_enable_status_change_notify(knet_handle_t knet_h,
void *link_status_change_notify_fn_private_data,
void (*link_status_change_notify_fn) (
void *private_data,
knet_node_id_t host_id,
uint8_t link_id,
uint8_t connected,
uint8_t remote,
uint8_t external));
/*
* logging structs/API calls
*/
/*
* libknet is composed of several subsystems. In order
* to easily distinguish log messages coming from different
* places, each subsystem has its own ID.
*
* 0-19 config/management
* 20-39 internal threads
* 40-59 transports
* 60-69 crypto implementations
*/
#define KNET_SUB_COMMON 0 /* common.c */
#define KNET_SUB_HANDLE 1 /* handle.c alloc/dealloc config changes */
#define KNET_SUB_HOST 2 /* host add/del/modify */
#define KNET_SUB_LISTENER 3 /* listeners add/del/modify... */
#define KNET_SUB_LINK 4 /* link add/del/modify */
#define KNET_SUB_TRANSPORT 5 /* Transport common */
#define KNET_SUB_CRYPTO 6 /* crypto.c config generic layer */
#define KNET_SUB_COMPRESS 7 /* compress.c config generic layer */
#define KNET_SUB_FILTER 19 /* allocated for users to log from dst_filter */
#define KNET_SUB_DSTCACHE 20 /* switching thread (destination cache handling) */
#define KNET_SUB_HEARTBEAT 21 /* heartbeat thread */
#define KNET_SUB_PMTUD 22 /* Path MTU Discovery thread */
#define KNET_SUB_TX 23 /* send to link thread */
#define KNET_SUB_RX 24 /* recv from link thread */
#define KNET_SUB_TRANSP_BASE 40 /* Base log level for transports */
#define KNET_SUB_TRANSP_LOOPBACK (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_LOOPBACK)
#define KNET_SUB_TRANSP_UDP (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_UDP)
#define KNET_SUB_TRANSP_SCTP (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_SCTP)
#define KNET_SUB_NSSCRYPTO 60 /* nsscrypto.c */
#define KNET_SUB_OPENSSLCRYPTO 61 /* opensslcrypto.c */
#define KNET_SUB_ZLIBCOMP 70 /* compress_zlib.c */
#define KNET_SUB_LZ4COMP 71 /* compress_lz4.c */
#define KNET_SUB_LZ4HCCOMP 72 /* compress_lz4.c */
#define KNET_SUB_LZO2COMP 73 /* compress_lzo.c */
#define KNET_SUB_LZMACOMP 74 /* compress_lzma.c */
#define KNET_SUB_BZIP2COMP 75 /* compress_bzip2.c */
#define KNET_SUB_UNKNOWN UINT8_MAX - 1
#define KNET_MAX_SUBSYSTEMS UINT8_MAX
/*
* Convert between subsystem IDs and names
*/
/**
* knet_log_get_subsystem_name
*
* @brief Get a logging system name from its numeric ID
*
* @return
* returns internal name of the subsystem or "common"
*/
const char *knet_log_get_subsystem_name(uint8_t subsystem);
/**
* knet_log_get_subsystem_id
*
* @brief Get a logging system ID from its name
*
* @return
* returns internal ID of the subsystem or KNET_SUB_COMMON
*/
uint8_t knet_log_get_subsystem_id(const char *name);
/*
* 4 log levels are enough for everybody
*/
#define KNET_LOG_ERR 0 /* unrecoverable errors/conditions */
#define KNET_LOG_WARN 1 /* recoverable errors/conditions */
#define KNET_LOG_INFO 2 /* info, link up/down, config changes.. */
#define KNET_LOG_DEBUG 3
/*
* Convert between log level values and names
*/
/**
* knet_log_get_loglevel_name
*
* @brief Get a logging level name from its numeric ID
*
* @return
* returns internal name of the log level or "ERROR" for unknown values
*/
const char *knet_log_get_loglevel_name(uint8_t level);
/**
* knet_log_get_loglevel_id
*
* @brief Get a logging level ID from its name
*
* @return
* returns internal log level ID or KNET_LOG_ERR for invalid names
*/
uint8_t knet_log_get_loglevel_id(const char *name);
/*
* every log message is composed by a text message
* and message level/subsystem IDs.
* In order to make debugging easier it is possible to send those packets
* straight to stdout/stderr (see knet_bench.c stdout option).
*/
#define KNET_MAX_LOG_MSG_SIZE 254
#if KNET_MAX_LOG_MSG_SIZE > PIPE_BUF
#error KNET_MAX_LOG_MSG_SIZE cannot be bigger than PIPE_BUF for guaranteed system atomic writes
#endif
struct knet_log_msg {
char msg[KNET_MAX_LOG_MSG_SIZE];
uint8_t subsystem; /* KNET_SUB_* */
uint8_t msglevel; /* KNET_LOG_* */
knet_handle_t knet_h; /* pointer to the handle generating the log */
};
/**
* knet_log_set_log_level
*
* @brief Set the logging level for a subsystem
*
* knet_h - same as above
*
* subsystem - same as above
*
* level - same as above
*
* knet_log_set_loglevel allows fine control of log levels by subsystem.
* See also knet_handle_new for defaults.
*
* @return
* knet_log_set_loglevel returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_log_set_loglevel(knet_handle_t knet_h, uint8_t subsystem,
uint8_t level);
/**
* knet_log_get_log_level
*
* @brief Get the logging level for a subsystem
*
* knet_h - same as above
*
* subsystem - same as above
*
* level - same as above
*
* @return
* knet_log_get_loglevel returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_log_get_loglevel(knet_handle_t knet_h, uint8_t subsystem,
uint8_t *level);
#endif
diff --git a/man/doxyxml.c b/man/doxyxml.c
index 0caaf9b0..b4b49a9e 100644
--- a/man/doxyxml.c
+++ b/man/doxyxml.c
@@ -1,863 +1,920 @@
/*
- * Copyright (C) 2018 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2018-2019 Red Hat, Inc. All rights reserved.
*
* Author: Christine Caulfield <ccaulfie@redhat.com>
*
* This software licensed under GPL-2.0+, LGPL-2.0+
*/
/*
* NOTE: this code is very rough, it does the bare minimum to parse the
* XML out from doxygen and is probably very fragile to changes in that XML
* schema. It probably leaks memory all over the place too.
*
* In its favour, it *does* generate man pages and should only be run very ocasionally
*/
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <libxml/tree.h>
#include <qb/qblist.h>
#include <qb/qbmap.h>
#define XML_DIR "../man/xml-knet"
#define XML_FILE "libknet_8h.xml"
static int print_ascii = 1;
static int print_man = 0;
static int print_params = 0;
static int num_functions = 0;
static const char *man_section="3";
static const char *package_name="Kronosnet";
static const char *header="Kronosnet Programmer's Manual";
static const char *output_dir="./";
static const char *xml_dir = XML_DIR;
static const char *xml_file = XML_FILE;
static const char *manpage_date = NULL;
static long manpage_year = LONG_MIN;
static struct qb_list_head params_list;
static struct qb_list_head retval_list;
static qb_map_t *function_map;
static qb_map_t *structures_map;
static qb_map_t *used_structures_map;
struct param_info {
char *paramname;
char *paramtype;
char *paramdesc;
struct param_info *next;
struct qb_list_head list;
};
struct struct_info {
+ enum {STRUCTINFO_STRUCT, STRUCTINFO_ENUM} kind;
char *structname;
struct qb_list_head params_list; /* our params */
struct qb_list_head list;
};
static char *get_texttree(int *type, xmlNode *cur_node, char **returntext);
static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg);
static void free_paraminfo(struct param_info *pi)
{
free(pi->paramname);
free(pi->paramtype);
free(pi->paramdesc);
free(pi);
}
static char *get_attr(xmlNode *node, const char *tag)
{
xmlAttr *this_attr;
for (this_attr = node->properties; this_attr; this_attr = this_attr->next) {
if (this_attr->type == XML_ATTRIBUTE_NODE && strcmp((char *)this_attr->name, tag) == 0) {
return strdup((char *)this_attr->children->content);
}
}
return NULL;
}
static char *get_child(xmlNode *node, const char *tag)
{
xmlNode *this_node;
xmlNode *child;
char buffer[1024] = {'\0'};
char *refid = NULL;
char *declname = NULL;
for (this_node = node->children; this_node; this_node = this_node->next) {
if ((strcmp( (char*)this_node->name, "declname") == 0)) {
declname = strdup((char*)this_node->children->content);
}
if ((this_node->type == XML_ELEMENT_NODE && this_node->children) && ((strcmp((char *)this_node->name, tag) == 0))) {
refid = NULL;
for (child = this_node->children; child; child = child->next) {
if (child->content) {
strcat(buffer, (char *)child->content);
}
if ((strcmp( (char*)child->name, "ref") == 0)) {
if (child->children->content) {
strcat(buffer,(char *)child->children->content);
}
refid = get_attr(child, "refid");
}
}
}
if (declname && refid) {
qb_map_put(used_structures_map, refid, declname);
}
}
return strdup(buffer);
}
static struct param_info *find_param_by_name(struct qb_list_head *list, const char *name)
{
struct qb_list_head *iter;
struct param_info *pi;
qb_list_for_each(iter, list) {
pi = qb_list_entry(iter, struct param_info, list);
if (strcmp(pi->paramname, name) == 0) {
return pi;
}
}
return NULL;
}
static int not_all_whitespace(char *string)
{
unsigned int i;
for (i=0; i<strlen(string); i++) {
if (string[i] != ' ' &&
string[i] != '\n' &&
string[i] != '\r' &&
string[i] != '\t')
return 1;
}
return 0;
}
static void get_param_info(xmlNode *cur_node, struct qb_list_head *list)
{
xmlNode *this_tag;
xmlNode *sub_tag;
char *paramname = NULL;
char *paramdesc = NULL;
struct param_info *pi;
/* This is not robust, and very inflexible */
for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) {
if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameternamelist") == 0) {
paramname = (char*)sub_tag->children->next->children->content;
}
if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "parameterdescription") == 0) {
paramdesc = (char*)sub_tag->children->next->children->content;
/* Add text to the param_map */
pi = find_param_by_name(list, paramname);
if (pi) {
pi->paramdesc = paramdesc;
}
else {
pi = malloc(sizeof(struct param_info));
if (pi) {
pi->paramname = paramname;
pi->paramdesc = paramdesc;
pi->paramtype = NULL; /* retval */
qb_list_add_tail(&pi->list, list);
}
}
}
}
}
}
static char *get_text(xmlNode *cur_node, char **returntext)
{
xmlNode *this_tag;
xmlNode *sub_tag;
char *kind;
char buffer[4096] = {'\0'};
for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
if (this_tag->type == XML_TEXT_NODE && strcmp((char *)this_tag->name, "text") == 0) {
if (not_all_whitespace((char*)this_tag->content)) {
strcat(buffer, (char*)this_tag->content);
strcat(buffer, "\n");
}
}
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "emphasis") == 0) {
if (print_man) {
strcat(buffer, "\\fB");
}
strcat(buffer, (char*)this_tag->children->content);
if (print_man) {
strcat(buffer, "\\fR");
}
}
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "itemizedlist") == 0) {
for (sub_tag = this_tag->children; sub_tag; sub_tag = sub_tag->next) {
if (sub_tag->type == XML_ELEMENT_NODE && strcmp((char *)sub_tag->name, "listitem") == 0) {
strcat(buffer, (char*)sub_tag->children->children->content);
strcat(buffer, "\n");
}
}
}
/* Look for subsections - return value & params */
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "simplesect") == 0) {
char *tmp;
kind = get_attr(this_tag, "kind");
tmp = get_text(this_tag->children, NULL);
if (returntext && strcmp(kind, "return") == 0) {
*returntext = tmp;
}
}
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "parameterlist") == 0) {
kind = get_attr(this_tag, "kind");
if (strcmp(kind, "param") == 0) {
get_param_info(this_tag, &params_list);
}
if (strcmp(kind, "retval") == 0) {
get_param_info(this_tag, &retval_list);
}
}
}
return strdup(buffer);
}
+static void read_structname(xmlNode *cur_node, void *arg)
+{
+ struct struct_info *si=arg;
+ xmlNode *this_tag;
+
+ for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
+ if (strcmp((char*)this_tag->name, "compoundname") == 0) {
+ si->structname = strdup((char*)this_tag->children->content);
+ }
+ }
+}
+
/* Called from traverse_node() */
static void read_struct(xmlNode *cur_node, void *arg)
{
xmlNode *this_tag;
struct struct_info *si=arg;
struct param_info *pi;
char fullname[1024];
char *type = NULL;
char *name = NULL;
const char *args="";
for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
if (strcmp((char*)this_tag->name, "type") == 0) {
type = (char*)this_tag->children->content ;
}
if (strcmp((char*)this_tag->name, "name") == 0) {
name = (char*)this_tag->children->content ;
}
if (this_tag->children && strcmp((char*)this_tag->name, "argsstring") == 0) {
args = (char*)this_tag->children->content;
}
}
if (name) {
pi = malloc(sizeof(struct param_info));
if (pi) {
snprintf(fullname, sizeof(fullname), "%s%s", name, args);
- pi->paramtype = strdup(type);
+ pi->paramtype = type?strdup(type):strdup("");
pi->paramname = strdup(fullname);
pi->paramdesc = NULL;
qb_list_add_tail(&pi->list, &si->params_list);
}
}
}
static int read_structure_from_xml(char *refid, char *name)
{
char fname[PATH_MAX];
xmlNode *rootdoc;
xmlDocPtr doc;
struct struct_info *si;
int ret = -1;
snprintf(fname, sizeof(fname), "%s/%s.xml", xml_dir, refid);
doc = xmlParseFile(fname);
if (doc == NULL) {
fprintf(stderr, "Error: unable to open xml file for %s\n", refid);
return -1;
}
rootdoc = xmlDocGetRootElement(doc);
if (!rootdoc) {
fprintf(stderr, "Can't find \"document root\"\n");
return -1;
}
si = malloc(sizeof(struct struct_info));
if (si) {
+ si->kind = STRUCTINFO_STRUCT;
qb_list_init(&si->params_list);
- si->structname = strdup(name);
traverse_node(rootdoc, "memberdef", read_struct, si);
+ traverse_node(rootdoc, "compounddef", read_structname, si);
ret = 0;
qb_map_put(structures_map, refid, si);
}
xmlFreeDoc(doc);
return ret;
}
-/* Reformat pointer params so they look nicer */
+
static void print_param(FILE *manfile, struct param_info *pi, int field_width, int bold, const char *delimiter)
{
char asterisk = ' ';
char *type = pi->paramtype;
+ /* Reformat pointer params so they look nicer */
if (pi->paramtype[strlen(pi->paramtype)-1] == '*') {
asterisk='*';
type = strdup(pi->paramtype);
type[strlen(type)-1] = '\0';
}
fprintf(manfile, " %s%-*s%c%s\\fI%s\\fP%s\n",
bold?"\\fB":"", field_width, type,
asterisk, bold?"\\fP":"", pi->paramname, delimiter);
if (type != pi->paramtype) {
free(type);
}
}
static void print_structure(FILE *manfile, char *refid, char *name)
{
struct struct_info *si;
struct param_info *pi;
struct qb_list_head *iter;
unsigned int max_param_length=0;
/* If it's not been read in - go and look for it */
si = qb_map_get(structures_map, refid);
if (!si) {
if (!read_structure_from_xml(refid, name)) {
si = qb_map_get(structures_map, refid);
}
}
if (si) {
qb_list_for_each(iter, &si->params_list) {
pi = qb_list_entry(iter, struct param_info, list);
if (strlen(pi->paramtype) > max_param_length) {
max_param_length = strlen(pi->paramtype);
}
}
- fprintf(manfile, "struct %s {\n", si->structname);
+ if (si->kind == STRUCTINFO_STRUCT) {
+ fprintf(manfile, "struct %s {\n", si->structname);
+ } else if (si->kind == STRUCTINFO_ENUM) {
+ fprintf(manfile, "enum %s {\n", si->structname);
+ } else {
+ fprintf(manfile, "%s {\n", si->structname);
+ }
qb_list_for_each(iter, &si->params_list) {
pi = qb_list_entry(iter, struct param_info, list);
print_param(manfile, pi, max_param_length, 0,";");
}
fprintf(manfile, "};\n");
}
}
char *get_texttree(int *type, xmlNode *cur_node, char **returntext)
{
xmlNode *this_tag;
char *tmp = NULL;
char buffer[4096] = {'\0'};
for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "para") == 0) {
tmp = get_text(this_tag, returntext);
strcat(buffer, tmp);
strcat(buffer, "\n");
free(tmp);
}
}
if (buffer[0]) {
tmp = strdup(buffer);
}
return tmp;
}
/* The text output is VERY basic and just a check that it's working really */
-static void print_text(char *name, char *def, char *brief, char *args, char *detailed, struct qb_list_head *param_list, char *returntext)
+static void print_text(char *name, char *def, char *brief, char *args, char *detailed,
+ struct qb_list_head *param_list, char *returntext)
{
printf(" ------------------ %s --------------------\n", name);
printf("NAME\n");
printf(" %s - %s\n", name, brief);
printf("SYNOPSIS\n");
printf(" %s %s\n\n", name, args);
printf("DESCRIPTION\n");
printf(" %s\n", detailed);
if (returntext) {
printf("RETURN VALUE\n");
printf(" %s\n", returntext);
}
}
/* Print a long string with para marks in it. */
static void man_print_long_string(FILE *manfile, char *text)
{
char *next_nl;
char *current = text;
next_nl = strchr(text, '\n');
while (next_nl && *next_nl != '\0') {
*next_nl = '\0';
if (strlen(current)) {
fprintf(manfile, ".PP\n%s\n", current);
}
*next_nl = '\n';
current = next_nl+1;
next_nl = strchr(current, '\n');
}
}
-static void print_manpage(char *name, char *def, char *brief, char *args, char *detailed, struct qb_list_head *param_map, char *returntext)
+static void print_manpage(char *name, char *def, char *brief, char *args, char *detailed,
+ struct qb_list_head *param_map, char *returntext)
{
char manfilename[PATH_MAX];
char gendate[64];
const char *dateptr = gendate;
FILE *manfile;
time_t t;
struct tm *tm;
qb_map_iter_t *map_iter;
struct qb_list_head *iter;
struct qb_list_head *tmp;
const char *p;
void *data;
unsigned int max_param_type_len;
unsigned int max_param_name_len;
unsigned int num_param_descs;
int param_count = 0;
int param_num = 0;
struct param_info *pi;
t = time(NULL);
tm = localtime(&t);
if (!tm) {
perror("unable to get localtime");
exit(1);
}
strftime(gendate, sizeof(gendate), "%Y-%m-%d", tm);
if (manpage_date) {
dateptr = manpage_date;
}
if (manpage_year == LONG_MIN) {
manpage_year = tm->tm_year+1900;
}
snprintf(manfilename, sizeof(manfilename), "%s/%s.%s", output_dir, name, man_section);
manfile = fopen(manfilename, "w+");
if (!manfile) {
perror("unable to open output file");
printf("%s", manfilename);
exit(1);
}
/* Work out the length of the parameters, so we can line them up */
max_param_type_len = 0;
max_param_name_len = 0;
num_param_descs = 0;
qb_list_for_each(iter, &params_list) {
pi = qb_list_entry(iter, struct param_info, list);
if (strlen(pi->paramtype) > max_param_type_len) {
max_param_type_len = strlen(pi->paramtype);
}
if (strlen(pi->paramname) > max_param_name_len) {
max_param_name_len = strlen(pi->paramname);
}
if (pi->paramdesc) {
num_param_descs++;
}
param_count++;
}
/* Off we go */
fprintf(manfile, ".\\\" Automatically generated man page, do not edit\n");
fprintf(manfile, ".TH %s %s %s \"%s\" \"%s\"\n", name, man_section, dateptr, package_name, header);
fprintf(manfile, ".SH NAME\n");
fprintf(manfile, "%s \\- %s\n", name, brief);
fprintf(manfile, ".SH SYNOPSIS\n");
fprintf(manfile, ".nf\n");
fprintf(manfile, ".B #include <libknet.h>\n");
fprintf(manfile, ".sp\n");
fprintf(manfile, "\\fB%s\\fP(\n", def);
qb_list_for_each(iter, &params_list) {
pi = qb_list_entry(iter, struct param_info, list);
print_param(manfile, pi, max_param_type_len, 1, ++param_num < param_count?",":"");
}
fprintf(manfile, ");\n");
fprintf(manfile, ".fi\n");
if (print_params && num_param_descs) {
fprintf(manfile, ".SH PARAMS\n");
qb_list_for_each(iter, &params_list) {
pi = qb_list_entry(iter, struct param_info, list);
fprintf(manfile, "\\fB%-*s \\fP\\fI%s\\fP\n", (int)max_param_name_len, pi->paramname,
pi->paramdesc);
fprintf(manfile, ".PP\n");
}
}
fprintf(manfile, ".SH DESCRIPTION\n");
man_print_long_string(manfile, detailed);
if (qb_map_count_get(used_structures_map)) {
fprintf(manfile, ".SH STRUCTURES\n");
map_iter = qb_map_iter_create(used_structures_map);
for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) {
fprintf(manfile, ".SS \"\"\n");
fprintf(manfile, ".PP\n");
fprintf(manfile, ".sp\n");
fprintf(manfile, ".sp\n");
fprintf(manfile, ".RS\n");
fprintf(manfile, ".nf\n");
fprintf(manfile, "\\fB\n");
print_structure(manfile, (char*)p, (char *)data);
fprintf(manfile, "\\fP\n");
fprintf(manfile, ".fi\n");
}
qb_map_iter_free(map_iter);
fprintf(manfile, ".RE\n");
}
if (returntext) {
fprintf(manfile, ".SH RETURN VALUE\n");
man_print_long_string(manfile, returntext);
}
qb_list_for_each(iter, &retval_list) {
pi = qb_list_entry(iter, struct param_info, list);
fprintf(manfile, "\\fB%-*s \\fP\\fI%s\\fP\n", 10, pi->paramname,
pi->paramdesc);
fprintf(manfile, ".PP\n");
}
fprintf(manfile, ".SH SEE ALSO\n");
fprintf(manfile, ".PP\n");
fprintf(manfile, ".nh\n");
fprintf(manfile, ".ad l\n");
param_num = 0;
map_iter = qb_map_iter_create(function_map);
for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) {
/* Exclude us! */
if (strcmp(data, name)) {
fprintf(manfile, "\\fI%s(%s)%s", (char *)data, man_section,
param_num < (num_functions - 1)?", ":"");
}
param_num++;
}
qb_map_iter_free(map_iter);
fprintf(manfile, "\n");
fprintf(manfile, ".ad\n");
fprintf(manfile, ".hy\n");
fprintf(manfile, ".SH \"COPYRIGHT\"\n");
fprintf(manfile, ".PP\n");
fprintf(manfile, "Copyright (C) 2010-%4ld Red Hat, Inc. All rights reserved.\n", manpage_year);
fclose(manfile);
/* Free the params & retval info */
qb_list_for_each_safe(iter, tmp, &params_list) {
pi = qb_list_entry(iter, struct param_info, list);
qb_list_del(&pi->list);
free_paraminfo(pi);
}
qb_list_for_each_safe(iter, tmp, &retval_list) {
pi = qb_list_entry(iter, struct param_info, list);
qb_list_del(&pi->list);
free_paraminfo(pi);
}
/* Free used-structures map */
map_iter = qb_map_iter_create(used_structures_map);
for (p = qb_map_iter_next(map_iter, &data); p; p = qb_map_iter_next(map_iter, &data)) {
qb_map_rm(used_structures_map, p);
free(data);
}
}
/* Same as traverse_members, but to collect function names */
static void collect_functions(xmlNode *cur_node, void *arg)
{
xmlNode *this_tag;
char *kind;
char *name = NULL;
if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) {
kind = get_attr(cur_node, "kind");
if (kind && strcmp(kind, "function") == 0) {
for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) {
name = strdup((char *)this_tag->children->content);
}
}
if (name) {
qb_map_put(function_map, name, name);
num_functions++;
}
}
}
}
+/* Same as traverse_members, but to collect enums. The behave like structures for,
+ but, for some reason, are in the main XML file rather than their own */
+static void collect_enums(xmlNode *cur_node, void *arg)
+{
+ xmlNode *this_tag;
+ struct struct_info *si;
+ char *kind;
+ char *refid = NULL;
+ char *name = NULL;
+
+ if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) {
+
+ kind = get_attr(cur_node, "kind");
+ if (kind && strcmp(kind, "enum") == 0) {
+ refid = get_attr(cur_node, "id");
+
+ for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next) {
+ if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0) {
+ name = strdup((char *)this_tag->children->content);
+ }
+ }
+
+ si = malloc(sizeof(struct struct_info));
+ if (si) {
+ si->kind = STRUCTINFO_ENUM;
+ qb_list_init(&si->params_list);
+ si->structname = strdup(name);
+ traverse_node(cur_node, "enumvalue", read_struct, si);
+ qb_map_put(structures_map, refid, si);
+ }
+
+ }
+
+ }
+}
static void traverse_members(xmlNode *cur_node, void *arg)
{
xmlNode *this_tag;
if (cur_node->name && strcmp((char *)cur_node->name, "memberdef") == 0) {
char *kind = NULL;
char *def = NULL;
char *args = NULL;
char *name = NULL;
char *brief = NULL;
char *detailed = NULL;
char *returntext = NULL;
int type;
kind=def=args=name=NULL;
kind = get_attr(cur_node, "kind");
for (this_tag = cur_node->children; this_tag; this_tag = this_tag->next)
{
if (!this_tag->children || !this_tag->children->content)
continue;
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "definition") == 0)
def = strdup((char *)this_tag->children->content);
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "argsstring") == 0)
args = strdup((char *)this_tag->children->content);
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "name") == 0)
name = strdup((char *)this_tag->children->content);
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "briefdescription") == 0) {
brief = get_texttree(&type, this_tag, &returntext);
if (brief) {
/*
* apparently brief text contains extra trailing space and 2 \n.
* remove them.
*/
brief[strlen(brief) - 3] = '\0';
}
}
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "detaileddescription") == 0) {
detailed = get_texttree(&type, this_tag, &returntext);
}
/* Get all the params */
if (this_tag->type == XML_ELEMENT_NODE && strcmp((char *)this_tag->name, "param") == 0) {
char *param_type = get_child(this_tag, "type");
char *param_name = get_child(this_tag, "declname");
struct param_info *pi = malloc(sizeof(struct param_info));
if (pi) {
pi->paramname = param_name;
pi->paramtype = param_type;
pi->paramdesc = NULL;
qb_list_add_tail(&pi->list, &params_list);
}
}
}
- if (kind && strcmp(kind, "typedef") == 0) {
- /* Collect typedefs? */
- }
-
if (kind && strcmp(kind, "function") == 0) {
/* Make sure function has a doxygen description */
if (!detailed) {
fprintf(stderr, "No doxygen description for function '%s' - please fix this\n", name);
exit(1);
}
if (print_man) {
print_manpage(name, def, brief, args, detailed, &params_list, returntext);
}
else {
print_text(name, def, brief, args, detailed, &params_list, returntext);
}
}
free(kind);
free(def);
free(args);
// free(name); /* don't free, it's in the map */
}
}
static void traverse_node(xmlNode *parentnode, const char *leafname, void (do_members(xmlNode*, void*)), void *arg)
{
xmlNode *cur_node;
for (cur_node = parentnode->children; cur_node; cur_node = cur_node->next) {
if (cur_node->type == XML_ELEMENT_NODE && cur_node->name
&& strcmp((char*)cur_node->name, leafname)==0) {
do_members(cur_node, arg);
continue;
}
if (cur_node->type == XML_ELEMENT_NODE) {
traverse_node(cur_node, leafname, do_members, arg);
}
}
}
static void usage(char *name)
{
printf("Usage:\n");
printf(" %s [OPTIONS] [<XML file>]\n", name);
printf("\n");
printf(" <XML file> defaults to %s\n", XML_FILE);
printf("\n");
printf(" -a Print ASCII dump of man pages to stdout\n");
printf(" -m Write man page files to <output dir>\n");
printf(" -P Print PARAMS section\n");
printf(" -s <s> Write man pages into section <s> <default 3)\n");
printf(" -p <package> Use <package> name. default <Kronosnet>\n");
printf(" -H <header> Set header (default \"Kronosnet Programmer's Manual\"\n");
printf(" -D <date> Date to print at top of man pages (format not checked, default: today)\n");
printf(" -Y <year> Year to print at end of copyright line (default: today's year)\n");
printf(" -o <dir> Write all man pages to <dir> (default .)\n");
printf(" -d <dir> Directory for XML files (default %s)\n", XML_DIR);
printf(" -h Print this usage text\n");
}
int main(int argc, char *argv[])
{
xmlNode *rootdoc;
xmlDocPtr doc;
int quiet=0;
int opt;
char xml_filename[PATH_MAX];
while ( (opt = getopt_long(argc, argv, "H:amPD:Y:s:d:o:p:f:h?", NULL, NULL)) != EOF)
{
switch(opt)
{
case 'a':
print_ascii = 1;
print_man = 0;
break;
case 'm':
print_man = 1;
print_ascii = 0;
break;
case 'P':
print_params = 1;
break;
case 's':
man_section = optarg;
break;
case 'd':
xml_dir = optarg;
break;
case 'D':
manpage_date = optarg;
break;
case 'Y':
manpage_year = strtol(optarg, NULL, 10);
/*
* Don't make too many assumptions about the year. I was on call at the
* 2000 rollover. #experience
*/
if (manpage_year == LONG_MIN || manpage_year == LONG_MAX ||
manpage_year < 1900) {
fprintf(stderr, "Value passed to -Y is not a valid year number\n");
return 1;
}
break;
case 'p':
package_name = optarg;
break;
case 'H':
header = optarg;
break;
case 'o':
output_dir = optarg;
break;
case '?':
case 'h':
usage(argv[0]);
return 0;
}
}
if (argv[optind]) {
xml_file = argv[optind];
}
if (!quiet) {
fprintf(stderr, "reading xml ... ");
}
snprintf(xml_filename, sizeof(xml_filename), "%s/%s", xml_dir, xml_file);
doc = xmlParseFile(xml_filename);
if (doc == NULL) {
fprintf(stderr, "Error: unable to read xml file %s\n", xml_filename);
exit(1);
}
rootdoc = xmlDocGetRootElement(doc);
if (!rootdoc) {
fprintf(stderr, "Can't find \"document root\"\n");
exit(1);
}
if (!quiet)
fprintf(stderr, "done.\n");
qb_list_init(&params_list);
qb_list_init(&retval_list);
structures_map = qb_hashtable_create(10);
function_map = qb_hashtable_create(10);
used_structures_map = qb_hashtable_create(10);
/* Collect functions */
traverse_node(rootdoc, "memberdef", collect_functions, NULL);
+ /* Collect enums */
+ traverse_node(rootdoc, "memberdef", collect_enums, NULL);
+
/* print pages */
traverse_node(rootdoc, "memberdef", traverse_members, NULL);
return 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 4, 6:03 AM (6 h, 28 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1854718
Default Alt Text
(94 KB)

Event Timeline