From e82d74f89809c6c01f71168b3b8fdcf490e93fa7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 13 Oct 2010 21:17:51 +0200 Subject: [PATCH] Initial import --- .gitignore | 1 + Makefile | 28 ++ blob.c | 177 +++++++++++ blob.h | 271 +++++++++++++++++ blobmsg.c | 148 +++++++++ blobmsg.h | 133 ++++++++ examples/blobmsg-example.c | 163 ++++++++++ examples/uhtbl-test.c | 99 ++++++ hash.c | 54 ++++ hash.h | 8 + list.h | 601 +++++++++++++++++++++++++++++++++++++ uapi.h | 36 +++ ucix.c | 176 +++++++++++ ucix.h | 133 ++++++++ uhtbl.c | 340 +++++++++++++++++++++ uhtbl.h | 375 +++++++++++++++++++++++ ulog.h | 31 ++ uloop.c | 272 +++++++++++++++++ uloop.h | 66 ++++ unl.c | 290 ++++++++++++++++++ unl.h | 46 +++ usock.c | 102 +++++++ usock.h | 17 ++ 23 files changed, 3567 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 blob.c create mode 100644 blob.h create mode 100644 blobmsg.c create mode 100644 blobmsg.h create mode 100644 examples/blobmsg-example.c create mode 100644 examples/uhtbl-test.c create mode 100644 hash.c create mode 100644 hash.h create mode 100644 list.h create mode 100644 uapi.h create mode 100644 ucix.c create mode 100644 ucix.h create mode 100644 uhtbl.c create mode 100644 uhtbl.h create mode 100644 ulog.h create mode 100644 uloop.c create mode 100644 uloop.h create mode 100644 unl.c create mode 100644 unl.h create mode 100644 usock.c create mode 100644 usock.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..140f8cf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8af237d --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CC?=gcc +CFLAGS?=-O2 +CFLAGS+=-std=gnu99 -Wall -Werror -pedantic -fpic +LDFLAGS?= +LIBNL=-lnl-tiny +PREFIX=/usr +INCLUDE_DIR=$(PREFIX)/include/libubox +LIBDIR=$(PREFIX)/lib +CPPFLAGS= + +all: libubox.so + +libubox.so: ucix.c blob.c blobmsg.c hash.c uhtbl.c usock.c uloop.c unl.c + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -shared -Wl,-soname,libubox.so $^ $(LDFLAGS) -luci $(LIBNL) + +install-headers: + mkdir -p $(INCLUDE_DIR) + cp *.h $(INCLUDE_DIR)/ + +install-lib: + mkdir -p $(LIBDIR) + cp libubox.so $(LIBDIR)/ + +install: install-lib install-headers + +clean: + rm -f *.so + diff --git a/blob.c b/blob.c new file mode 100644 index 0000000..823eb24 --- /dev/null +++ b/blob.c @@ -0,0 +1,177 @@ +/* + * blob - library for generating/parsing tagged binary data + * + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "blob.h" + +static bool +blob_buffer_grow(struct blob_buf *buf, int minlen) +{ + buf->buflen += ((minlen / 256) + 1) * 256; + buf->buf = realloc(buf->buf, buf->buflen); + return !!buf->buf; +} + +static void +blob_init(struct blob_attr *attr, int id, unsigned int len) +{ + len &= BLOB_ATTR_LEN_MASK; + len |= (id << BLOB_ATTR_ID_SHIFT) & BLOB_ATTR_ID_MASK; + attr->id_len = cpu_to_be32(len); +} + +static inline struct blob_attr * +offset_to_attr(struct blob_buf *buf, int offset) +{ + void *ptr = (char *)buf->buf + offset; + return ptr; +} + +static inline int +attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) +{ + return (char *)attr - (char *) buf->buf; +} + +static struct blob_attr * +blob_add(struct blob_buf *buf, struct blob_attr *pos, int id, int payload) +{ + int offset = attr_to_offset(buf, pos); + int required = (offset + sizeof(struct blob_attr) + payload) - buf->buflen; + struct blob_attr *attr; + + if (required > 0) { + int offset_head = attr_to_offset(buf, buf->head); + + if (!buf->grow || !buf->grow(buf, required)) + return NULL; + + buf->head = offset_to_attr(buf, offset_head); + attr = offset_to_attr(buf, offset); + } else { + attr = pos; + } + + blob_init(attr, id, payload + sizeof(struct blob_attr)); + return attr; +} + +int +blob_buf_init(struct blob_buf *buf, int id) +{ + if (!buf->grow) + buf->grow = blob_buffer_grow; + + buf->head = buf->buf; + if (blob_add(buf, buf->buf, id, 0) == NULL) + return -ENOMEM; + + return 0; +} + +struct blob_attr * +blob_new(struct blob_buf *buf, int id, int payload) +{ + struct blob_attr *attr; + + attr = blob_add(buf, blob_next(buf->head), id, payload); + if (!attr) + return NULL; + + blob_set_raw_len(buf->head, blob_pad_len(buf->head) + blob_pad_len(attr)); + return attr; +} + +struct blob_attr * +blob_put(struct blob_buf *buf, int id, const void *ptr, int len) +{ + struct blob_attr *attr; + + attr = blob_new(buf, id, len); + if (!attr) + return NULL; + + if (ptr) + memcpy(blob_data(attr), ptr, len); + return attr; +} + +void * +blob_nest_start(struct blob_buf *buf, int id) +{ + unsigned long offset = attr_to_offset(buf, buf->head); + buf->head = blob_new(buf, id, 0); + return (void *) offset; +} + +void +blob_nest_end(struct blob_buf *buf, void *cookie) +{ + struct blob_attr *attr = offset_to_attr(buf, (unsigned long) cookie); + blob_set_raw_len(attr, blob_pad_len(attr) + blob_len(buf->head)); + buf->head = attr; +} + +static const int blob_type_minlen[BLOB_ATTR_LAST] = { + [BLOB_ATTR_STRING] = 1, + [BLOB_ATTR_INT8] = sizeof(uint8_t), + [BLOB_ATTR_INT16] = sizeof(uint16_t), + [BLOB_ATTR_INT32] = sizeof(uint32_t), + [BLOB_ATTR_INT64] = sizeof(uint64_t), +}; + +int +blob_parse(struct blob_attr *attr, struct blob_attr **data, struct blob_attr_info *info, int max) +{ + struct blob_attr *pos; + int found = 0; + int rem; + + memset(data, 0, sizeof(struct blob_attr *) * max); + blob_for_each_attr(pos, attr, rem) { + int id = blob_id(pos); + int len = blob_len(pos); + + if (id >= max) + continue; + + if (info) { + int type = info[id].type; + if (type < BLOB_ATTR_LAST) { + if (type >= BLOB_ATTR_INT8 && type <= BLOB_ATTR_INT64) { + if (len != blob_type_minlen[type]) + continue; + } else { + if (len < blob_type_minlen[type]) + continue; + } + } + + if (info[id].minlen && len < info[id].minlen) + continue; + + if (info[id].maxlen && len > info[id].maxlen) + continue; + + if (info[id].validate && !info[id].validate(&info[id], attr)) + continue; + } + + if (!data[id]) + found++; + + data[id] = pos; + } + return found; +} diff --git a/blob.h b/blob.h new file mode 100644 index 0000000..e598c5e --- /dev/null +++ b/blob.h @@ -0,0 +1,271 @@ +/* + * blob - library for generating/parsing tagged binary data + * + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _BLOB_H__ +#define _BLOB_H__ + +#include +#include +#include +#include +#include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#if defined(__linux__) || defined(__CYGWIN__) +#include +#include + +#elif defined(__APPLE__) +#include +#include +#define bswap_16(x) OSSwapInt16(x) +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) +#elif defined(__FreeBSD__) +#include +#define bswap_16(x) bswap16(x) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#else +#include +#define bswap_16(x) swap16(x) +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) +#endif + +#ifndef __BYTE_ORDER +#define __BYTE_ORDER BYTE_ORDER +#endif +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#endif +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#endif + +#define cpu_to_be64(x) bswap_64(x) +#define cpu_to_be32(x) bswap_32(x) +#define cpu_to_be16(x) bswap_16(x) + +#define be64_to_cpu(x) bswap_64(x) +#define be32_to_cpu(x) bswap_32(x) +#define be16_to_cpu(x) bswap_16(x) + +#else + +#define cpu_to_be64(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_be16(x) (x) + +#define be64_to_cpu(x) (x) +#define be32_to_cpu(x) (x) +#define be16_to_cpu(x) (x) + +#endif + +enum { + BLOB_ATTR_UNSPEC, + BLOB_ATTR_NESTED, + BLOB_ATTR_BINARY, + BLOB_ATTR_STRING, + BLOB_ATTR_INT8, + BLOB_ATTR_INT16, + BLOB_ATTR_INT32, + BLOB_ATTR_INT64, + BLOB_ATTR_LAST +}; + +#define BLOB_ATTR_ID_MASK 0xff000000 +#define BLOB_ATTR_ID_SHIFT 24 +#define BLOB_ATTR_LEN_MASK 0x00ffffff +#define BLOB_ATTR_ALIGN 4 + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +struct blob_attr { + uint32_t id_len; + char data[]; +} __packed; + +struct blob_attr_info { + unsigned int type; + unsigned int minlen; + unsigned int maxlen; + bool (*validate)(struct blob_attr_info *, struct blob_attr *); +}; + +struct blob_buf { + struct blob_attr *head; + bool (*grow)(struct blob_buf *buf, int minlen); + int buflen; + void *buf; + void *priv; +}; + +/* + * blob_data: returns the data pointer for an attribute + */ +static inline void * +blob_data(struct blob_attr *attr) +{ + return attr->data; +} + +/* + * blob_id: returns the id of an attribute + */ +static inline unsigned int +blob_id(struct blob_attr *attr) +{ + int id = (be32_to_cpu(attr->id_len) & BLOB_ATTR_ID_MASK) >> BLOB_ATTR_ID_SHIFT; + return id; +} + +/* + * blob_len: returns the length of the attribute's payload + */ +static inline unsigned int +blob_len(struct blob_attr *attr) +{ + return (be32_to_cpu(attr->id_len) & BLOB_ATTR_LEN_MASK) - sizeof(struct blob_attr); +} + +/* + * blob_pad_len: returns the complete length of an attribute (including the header) + */ +static inline unsigned int +blob_raw_len(struct blob_attr *attr) +{ + return blob_len(attr) + sizeof(struct blob_attr); +} + +/* + * blob_pad_len: returns the padded length of an attribute (including the header) + */ +static inline unsigned int +blob_pad_len(struct blob_attr *attr) +{ + int len = blob_raw_len(attr); + len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1); + return len; +} + +static inline void +blob_set_raw_len(struct blob_attr *attr, unsigned int len) +{ + int id = blob_id(attr); + len &= BLOB_ATTR_LEN_MASK; + len |= (id << BLOB_ATTR_ID_SHIFT) & BLOB_ATTR_ID_MASK; + attr->id_len = cpu_to_be32(len); +} + +static inline uint8_t +blob_get_int8(struct blob_attr *attr) +{ + return *((uint8_t *) attr->data); +} + +static inline uint16_t +blob_get_int16(struct blob_attr *attr) +{ + uint16_t *tmp = (uint16_t*)attr->data; + return be16_to_cpu(*tmp); +} + +static inline uint32_t +blob_get_int32(struct blob_attr *attr) +{ + uint32_t *tmp = (uint32_t*)attr->data; + return be32_to_cpu(*tmp); +} + +static inline uint64_t +blob_get_int64(struct blob_attr *attr) +{ + uint64_t *tmp = (uint64_t*)attr->data; + return be64_to_cpu(*tmp); +} + +static inline const char * +blob_get_string(struct blob_attr *attr) +{ + return attr->data; +} + +static inline struct blob_attr * +blob_next(struct blob_attr *attr) +{ + return (struct blob_attr *) ((char *) attr + blob_pad_len(attr)); +} + +extern int blob_buf_init(struct blob_buf *buf, int id); +extern struct blob_attr *blob_new(struct blob_buf *buf, int id, int payload); +extern void *blob_nest_start(struct blob_buf *buf, int id); +extern void blob_nest_end(struct blob_buf *buf, void *cookie); +extern struct blob_attr *blob_put(struct blob_buf *buf, int id, const void *ptr, int len); +extern int blob_parse(struct blob_attr *attr, struct blob_attr **data, struct blob_attr_info *info, int max); + +static inline struct blob_attr * +blob_put_string(struct blob_buf *buf, int id, const char *str) +{ + return blob_put(buf, id, str, strlen(str) + 1); +} + +static inline struct blob_attr * +blob_put_int8(struct blob_buf *buf, int id, uint8_t val) +{ + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_int16(struct blob_buf *buf, int id, uint16_t val) +{ + val = cpu_to_be16(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_int32(struct blob_buf *buf, int id, uint32_t val) +{ + val = cpu_to_be32(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_int64(struct blob_buf *buf, int id, uint64_t val) +{ + val = cpu_to_be64(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +#define __blob_for_each_attr(pos, attr, rem) \ + for (pos = (void *) attr; \ + (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + + +#define blob_for_each_attr(pos, attr, rem) \ + for (rem = blob_len(attr), pos = blob_data(attr); \ + (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + + +#endif diff --git a/blobmsg.c b/blobmsg.c new file mode 100644 index 0000000..25b72ae --- /dev/null +++ b/blobmsg.c @@ -0,0 +1,148 @@ +/* + * blobmsg - library for generating/parsing structured blob messages + * + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "blobmsg.h" + +int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, int len) +{ + struct blobmsg_hdr *hdr; + struct blob_attr *attr; + uint8_t *pslen; + int i; + + memset(tb, 0, policy_len * sizeof(*tb)); + pslen = alloca(policy_len); + for (i = 0; i < policy_len; i++) { + if (!policy[i].name) + continue; + + pslen[i] = strlen(policy[i].name); + } + + __blob_for_each_attr(attr, data, len) { + hdr = blob_data(attr); + for (i = 0; i < policy_len; i++) { + if (!policy[i].name) + continue; + + if (policy[i].type != BLOBMSG_TYPE_UNSPEC && + blob_id(attr) != policy[i].type) + continue; + + if (hdr->namelen != pslen[i]) + continue; + + if (!hdr->namelen) + return -1; + + if (sizeof(*attr) + blobmsg_hdrlen(hdr->namelen) > blob_pad_len(attr)) + return -1; + + if (hdr->name[hdr->namelen] != 0) + return -1; + + if (tb[i]) + return -1; + + if (strcmp(policy[i].name, (char *) hdr->name) != 0) + continue; + + tb[i] = attr; + } + } + + return 0; +} + + +static struct blob_attr * +blobmsg_new(struct blob_buf *buf, int type, const char *name, int payload_len, void **data) +{ + struct blob_attr *attr; + struct blobmsg_hdr *hdr; + int attrlen, namelen; + + if (blob_id(buf->head) == BLOBMSG_TYPE_ARRAY && !name) { + attr = blob_new(buf, type, payload_len); + *data = blob_data(attr); + return attr; + } + + if (blob_id(buf->head) != BLOBMSG_TYPE_TABLE || !name) + return NULL; + + namelen = strlen(name); + attrlen = blobmsg_hdrlen(namelen) + payload_len; + attr = blob_new(buf, type, attrlen); + if (!attr) + return NULL; + + hdr = blob_data(attr); + hdr->namelen = namelen; + strcpy((char *) hdr->name, (const char *)name); + *data = blobmsg_data(attr); + + return attr; +} + +static inline int +attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) +{ + return (char *)attr - (char *) buf->buf; +} + + +void * +blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array) +{ + struct blob_attr *head = buf->head; + int type = array ? BLOBMSG_TYPE_ARRAY : BLOBMSG_TYPE_TABLE; + unsigned long offset = attr_to_offset(buf, buf->head); + void *data; + + if (blob_id(head) == BLOBMSG_TYPE_ARRAY && !name) + return blob_nest_start(buf, type); + + if (blob_id(head) == BLOBMSG_TYPE_TABLE && name) { + head = blobmsg_new(buf, type, name, 0, &data); + blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blobmsg_hdrlen(strlen(name))); + buf->head = head; + return (void *)offset; + } + + return NULL; +} + +int +blobmsg_add_field(struct blob_buf *buf, int type, const char *name, + const void *data, int len) +{ + struct blob_attr *attr; + void *data_dest; + + if (type == BLOBMSG_TYPE_ARRAY || + type == BLOBMSG_TYPE_TABLE) + return -1; + + attr = blobmsg_new(buf, type, name, len, &data_dest); + if (!attr) + return -1; + + if (len > 0) + memcpy(data_dest, data, len); + + return 0; +} diff --git a/blobmsg.h b/blobmsg.h new file mode 100644 index 0000000..9e71939 --- /dev/null +++ b/blobmsg.h @@ -0,0 +1,133 @@ +/* + * blobmsg - library for generating/parsing structured blob messages + * + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __BLOBMSG_H +#define __BLOBMSG_H + +#include "blob.h" + +#define BLOBMSG_ALIGN 2 +#define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1)) + +enum blobmsg_type { + BLOBMSG_TYPE_UNSPEC, + BLOBMSG_TYPE_ARRAY, + BLOBMSG_TYPE_TABLE, + BLOBMSG_TYPE_STRING, + BLOBMSG_TYPE_INT64, + BLOBMSG_TYPE_INT32, + BLOBMSG_TYPE_INT16, + BLOBMSG_TYPE_INT8 +}; + +struct blobmsg_hdr { + uint8_t namelen; + uint8_t name[]; +} __packed; + +struct blobmsg_policy { + const char *name; + enum blobmsg_type type; +}; + +static inline int blobmsg_hdrlen(int namelen) +{ + return BLOBMSG_PADDING(sizeof(struct blobmsg_hdr) + namelen + 1); +} + +static inline void *blobmsg_data(struct blob_attr *attr) +{ + struct blobmsg_hdr *hdr = blob_data(attr); + return &hdr->name[blobmsg_hdrlen(hdr->namelen) - 1]; +} + +static inline int blobmsg_data_len(struct blob_attr *attr) +{ + uint8_t *start, *end; + + start = blob_data(attr); + end = blobmsg_data(attr); + + return blob_len(attr) - (end - start); +} + +int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, int len); + +int blobmsg_add_field(struct blob_buf *buf, int type, const char *name, + const void *data, int len); + +static inline int +blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT8, name, &val, 1); +} + +static inline int +blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT16, name, &val, 2); +} + +static inline int +blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT32, name, &val, 4); +} + +static inline int +blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT64, name, &val, 8); +} + +static inline int +blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_STRING, name, string, strlen(string) + 1); +} + +void *blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array); + +static inline void * +blobmsg_open_array(struct blob_buf *buf, const char *name) +{ + return blobmsg_open_nested(buf, name, true); +} + +static inline void * +blobmsg_open_table(struct blob_buf *buf, const char *name) +{ + return blobmsg_open_nested(buf, name, false); +} + +static inline void +blobmsg_close_array(struct blob_buf *buf, void *cookie) +{ + blob_nest_end(buf, cookie); +} + +static inline void +blobmsg_close_table(struct blob_buf *buf, void *cookie) +{ + blob_nest_end(buf, cookie); +} + +static inline int blobmsg_buf_init(struct blob_buf *buf) +{ + return blob_buf_init(buf, BLOBMSG_TYPE_TABLE); +} + +#endif diff --git a/examples/blobmsg-example.c b/examples/blobmsg-example.c new file mode 100644 index 0000000..c88f53a --- /dev/null +++ b/examples/blobmsg-example.c @@ -0,0 +1,163 @@ +#include + +#include "blobmsg.h" + +static const char *indent_str = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; + +#define indent_printf(indent, ...) do { \ + if (indent > 0) \ + fwrite(indent_str, indent, 1, stderr); \ + fprintf(stderr, __VA_ARGS__); \ +} while(0) + +static void dump_attr_data(void *data, int len, int type, int indent, int next_indent); + +static void +dump_array(struct blob_attr *head, int len, int indent) +{ + struct blob_attr *attr; + + indent_printf(indent, "{\n"); + __blob_for_each_attr(attr, head, len) { + dump_attr_data(blob_data(attr), blob_len(attr), blob_id(attr), indent + 1, indent + 1); + } + indent_printf(indent, "}\n"); +} + +static void +dump_table(struct blob_attr *head, int len, int indent) +{ + struct blob_attr *attr, *last_attr; + struct blobmsg_hdr *hdr; + + indent_printf(indent, "{\n"); + __blob_for_each_attr(attr, head, len) { + hdr = blob_data(attr); + indent_printf(indent + 1, "%s : ", hdr->name); + dump_attr_data(blobmsg_data(attr), blobmsg_data_len(attr), blob_id(attr), 0, indent + 1); + last_attr = attr; + } + indent_printf(indent, "}\n"); +} + +static void dump_attr_data(void *data, int len, int type, int indent, int next_indent) +{ + switch(type) { + case BLOBMSG_TYPE_STRING: + indent_printf(indent, "%s\n", (char *) data); + break; + case BLOBMSG_TYPE_INT8: + indent_printf(indent, "%d\n", *(uint8_t *)data); + break; + case BLOBMSG_TYPE_INT16: + indent_printf(indent, "%d\n", *(uint16_t *)data); + break; + case BLOBMSG_TYPE_INT32: + indent_printf(indent, "%d\n", *(uint32_t *)data); + break; + case BLOBMSG_TYPE_INT64: + indent_printf(indent, "%lld\n", *(uint64_t *)data); + break; + case BLOBMSG_TYPE_TABLE: + if (!indent) + indent_printf(indent, "\n"); + dump_table(data, len, next_indent); + break; + case BLOBMSG_TYPE_ARRAY: + if (!indent) + indent_printf(indent, "\n"); + dump_array(data, len, next_indent); + break; + } +} + +enum { + FOO_MESSAGE, + FOO_LIST, + FOO_TESTDATA +}; + +static const struct blobmsg_policy pol[] = { + [FOO_MESSAGE] = { + .name = "message", + .type = BLOBMSG_TYPE_STRING, + }, + [FOO_LIST] = { + .name = "list", + .type = BLOBMSG_TYPE_ARRAY, + }, + [FOO_TESTDATA] = { + .name = "testdata", + .type = BLOBMSG_TYPE_TABLE, + }, +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +static void dump_message(struct blob_buf *buf) +{ + struct blob_attr *tb[ARRAY_SIZE(pol)]; + + if (blobmsg_parse(pol, ARRAY_SIZE(pol), tb, blob_data(buf->head), blob_len(buf->head)) != 0) { + fprintf(stderr, "Parse failed\n"); + return; + } + if (tb[FOO_MESSAGE]) + fprintf(stderr, "Message: %s\n", (char *) blobmsg_data(tb[FOO_MESSAGE])); + + if (tb[FOO_LIST]) { + fprintf(stderr, "List: "); + dump_array(blobmsg_data(tb[FOO_LIST]), blob_len(tb[FOO_LIST]), 0); + } + if (tb[FOO_TESTDATA]) { + fprintf(stderr, "Testdata: "); + dump_table(blobmsg_data(tb[FOO_TESTDATA]), blob_len(tb[FOO_TESTDATA]), 0); + } +} + +static void +fill_message(struct blob_buf *buf) +{ + void *tbl; +#if 0 + int i; +#endif + + blobmsg_add_string(buf, "message", "Hello, world!"); + + tbl = blobmsg_open_array(buf, "list"); + blobmsg_add_u32(buf, NULL, 0); + blobmsg_add_u32(buf, NULL, 1); + blobmsg_add_u32(buf, NULL, 2); + blobmsg_close_table(buf, tbl); + + tbl = blobmsg_open_table(buf, "testdata"); + blobmsg_add_u32(buf, "hello", 1); + blobmsg_add_string(buf, "world", "2"); + blobmsg_close_table(buf, tbl); + +#if 0 + for (i = 0; i < buf->buflen; i++) { + if (i % 0x10 == 0) + fprintf(stderr, "\n"); + fprintf(stderr, "%02x ", ((uint8_t *) buf->buf)[i]); + } + fprintf(stderr, "\n"); +#endif +} + +int main(int argc, char **argv) +{ + static struct blob_buf buf; + + blobmsg_buf_init(&buf); + fill_message(&buf); + dump_message(&buf); + + if (buf.buf) + free(buf.buf); + + return 0; +} diff --git a/examples/uhtbl-test.c b/examples/uhtbl-test.c new file mode 100644 index 0000000..d8f2f32 --- /dev/null +++ b/examples/uhtbl-test.c @@ -0,0 +1,99 @@ +#include "../hash.h" +#include "../uhtbl.h" +#include +#include + +struct mybucket { + uhtbl_head_t __head; + long double mydata; +}; + +int main() { + printf("%i\n", (int)sizeof(struct mybucket)); + + uhtbl_t tbl; + uhtbl_init(&tbl, sizeof(struct mybucket), 16, hash_murmur2, NULL); + struct mybucket *bucket; + + const char *t[] = {"null", "eins", "zwei", "drei", "vier", "fünf", "sechs", + "sieben", "acht", "neun", "zehn", "elf", "zwölf", "dreizehn", + "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", + "neunzehn", "zwanzig", "einundzwanzig", "zweiundzwanzig", + "dreiundzwanzig", "virundzwanzig", "fünfundzwanzig", "sechsundzwanzig", + "siebenundzwanzig", "achtundzwanzig", "neunundzwanzig", "dreißig", + "einunddreißig", "zweiunddreißig"}; + for (int i = 0; i < 33; i++) { + bucket = (struct mybucket*)uhtbl_set(&tbl, t[i], strlen(t[i])); + bucket->mydata = i; + } + + uint32_t iter = 0; + while ((bucket = (struct mybucket*)uhtbl_next(&tbl, &iter))) { + printf("%i\t", (int)bucket->mydata); + } + printf("\nSize: %i, Used: %i\n\n", tbl.size, tbl.used); + + for (int i = 0; i < 33; i++) { + bucket = (struct mybucket*)uhtbl_set(&tbl, 0, i); + bucket->mydata = i; + } + + iter = 0; + while ((bucket = (struct mybucket*)uhtbl_next(&tbl, &iter))) { + printf("%i\t", (int)bucket->mydata); + } + printf("\nSize: %i, Used: %i\n\n", tbl.size, tbl.used); + + for (int i = 0; i < 33; i++) { + if (uhtbl_unset(&tbl, 0, i)) { + printf("Unset failed %i\n", i); + } + if (uhtbl_unset(&tbl, t[i], strlen(t[i]))) { + printf("Unset failed %s\n", t[i]); + } + } + + iter = 0; + while ((bucket = (struct mybucket*)uhtbl_next(&tbl, &iter))) { + printf("%i\t", (int)bucket->mydata); + } + printf("\nSize: %i, Used: %i\n\n", tbl.size, tbl.used); + + for (int i = 0; i < 33; i++) { + bucket = (struct mybucket*)uhtbl_set(&tbl, t[i], strlen(t[i])); + bucket->mydata = i; + } + + for (int i = 0; i < 33; i++) { + bucket =(struct mybucket*) uhtbl_set(&tbl, 0, i); + bucket->mydata = i; + } + + for (int i = 0; i < 33; i++) { + bucket = (struct mybucket*)uhtbl_set(&tbl, t[i], strlen(t[i])); + bucket->mydata = i; + } + + for (int i = 0; i < 33; i++) { + bucket = (struct mybucket*)uhtbl_set(&tbl, 0, i); + bucket->mydata = i; + } + + iter = 0; + while ((bucket = (struct mybucket*)uhtbl_next(&tbl, &iter))) { + printf("%i\t", (int)bucket->mydata); + } + printf("\nSize: %i, Used: %i\n\n", tbl.size, tbl.used); + + for (int i = 0; i < 33; i++) { + bucket = (struct mybucket*)uhtbl_get(&tbl, t[i], strlen(t[i])); + printf("%i\t", (int)bucket->mydata); + bucket = (struct mybucket*)uhtbl_get(&tbl, 0, i); + printf("%i\t", (int)bucket->mydata); + } + printf("\nSize: %i, Used: %i\n\n", tbl.size, tbl.used); + + uhtbl_finalize(&tbl); + + return 0; +} diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..16a0655 --- /dev/null +++ b/hash.c @@ -0,0 +1,54 @@ +#include +#include +#include "hash.h" + +//----------------------------------------------------------------------------- +// MurmurHashNeutral2, by Austin Appleby + +// Same as MurmurHash2, but endian- and alignment-neutral. +uint32_t hash_murmur2(const void * key, int len) +{ + const unsigned int seed = 0xdeadc0de; + const unsigned int m = 0x5bd1e995; + const int r = 24; + + unsigned int h = seed ^ len; + + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + unsigned int k; + + k = data[0]; + k |= data[1] << 8; + k |= data[2] << 16; + k |= data[3] << 24; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + + diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..82b7d3c --- /dev/null +++ b/hash.h @@ -0,0 +1,8 @@ +#ifndef _HASH_H__ +#define _HASH_H__ + +#include + +uint32_t hash_murmur2(const void * key, int len); + +#endif diff --git a/list.h b/list.h new file mode 100644 index 0000000..2959a06 --- /dev/null +++ b/list.h @@ -0,0 +1,601 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#ifndef container_of +#define container_of(ptr, type, member) ( \ + (type *)( (char *)ptr - offsetof(type,member) )) +#endif + + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = NULL; + n->pprev = NULL; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos; pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos; pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +#endif diff --git a/uapi.h b/uapi.h new file mode 100644 index 0000000..db01949 --- /dev/null +++ b/uapi.h @@ -0,0 +1,36 @@ +/** + * uapi - Common API macros + * Copyright (C) 2010 Steven Barth + * Copyright (C) 2010 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef UAPI_H_ +#define UAPI_H_ + +#define API_CTOR __attribute__((constructor)) +#define API_DTOR __attribute__((destructor)) +#define API_HIDDEN __attribute__((visibility("hidden"))) +#define API_INTERNAL __attribute__((visibility("internal"))) +#define API_DEFAULT __attribute__((visibility("default"))) +#define API_ALLOC __attribute__((malloc)) +#define API_NONNULL(...) __attribute__((nonnull(__VA_ARGS_))) +#define API_FORCEINLINE __attribute__((always_inline)) inline + +#define API_PACKED __attribute__((packed)) +#define API_CLEANUP(gc) __attribute__((cleanup(gc))) +#endif /* UAPI_H_ */ diff --git a/ucix.c b/ucix.c new file mode 100644 index 0000000..72e8052 --- /dev/null +++ b/ucix.c @@ -0,0 +1,176 @@ +/* + * ucix + * Copyright (C) 2010 John Crispin + * Copyright (C) 2010 Steven Barth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include + +#include "ucix.h" + +struct uci_ptr uci_ptr; + +int ucix_get_ptr(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t) +{ + memset(&uci_ptr, 0, sizeof(uci_ptr)); + uci_ptr.package = p; + uci_ptr.section = s; + uci_ptr.option = o; + uci_ptr.value = t; + return uci_lookup_ptr(ctx, &uci_ptr, NULL, true); +} + +struct uci_context* ucix_init(const char *config_file, int state) +{ + struct uci_context *ctx = uci_alloc_context(); + uci_set_confdir(ctx, "/etc/config"); + if(state) + uci_set_savedir(ctx, "/var/state/"); + else + uci_set_savedir(ctx, "/tmp/.uci/"); + if(uci_load(ctx, config_file, NULL) != UCI_OK) + { + printf("%s/%s is missing or corrupt\n", ctx->confdir, config_file); + return NULL; + } + return ctx; +} + +struct uci_context* ucix_init_path(const char *vpath, const char *config_file, int state) +{ + struct uci_context *ctx; + char buf[256]; + if(!vpath) + return ucix_init(config_file, state); + ctx = uci_alloc_context(); + buf[255] = '\0'; + snprintf(buf, 255, "%s%s", vpath, "/etc/config"); + uci_set_confdir(ctx, buf); + snprintf(buf, 255, "%s%s", vpath, (state)?("/var/state"):("/tmp/.uci")); + uci_add_delta_path(ctx, buf); + if(uci_load(ctx, config_file, NULL) != UCI_OK) + { + printf("%s/%s is missing or corrupt\n", ctx->confdir, config_file); + return NULL; + } + return ctx; +} + +int ucix_get_option_list(struct uci_context *ctx, const char *p, + const char *s, const char *o, struct list_head *l) +{ + struct uci_element *e = NULL; + if(ucix_get_ptr(ctx, p, s, o, NULL)) + return 1; + if (!(uci_ptr.flags & UCI_LOOKUP_COMPLETE)) + return 1; + e = uci_ptr.last; + switch (e->type) + { + case UCI_TYPE_OPTION: + switch(uci_ptr.o->type) { + case UCI_TYPE_LIST: + uci_foreach_element(&uci_ptr.o->v.list, e) + { + struct ucilist *ul = malloc(sizeof(struct ucilist)); + ul->val = strdup((e->name)?(e->name):("")); + list_add_tail(&ul->list, l); + } + break; + default: + break; + } + break; + default: + return 1; + } + + return 0; +} + +char* ucix_get_option(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + struct uci_element *e = NULL; + const char *value = NULL; + if(ucix_get_ptr(ctx, p, s, o, NULL)) + return NULL; + if (!(uci_ptr.flags & UCI_LOOKUP_COMPLETE)) + return NULL; + e = uci_ptr.last; + switch (e->type) + { + case UCI_TYPE_SECTION: + value = uci_to_section(e)->type; + break; + case UCI_TYPE_OPTION: + switch(uci_ptr.o->type) { + case UCI_TYPE_STRING: + value = uci_ptr.o->v.string; + break; + default: + value = NULL; + break; + } + break; + default: + return 0; + } + + return (value) ? (strdup(value)):(NULL); +} + +void ucix_add_list(struct uci_context *ctx, const char *p, const char *s, const char *o, struct list_head *vals) +{ + struct list_head *q; + list_for_each(q, vals) + { + struct ucilist *ul = container_of(q, struct ucilist, list); + if(ucix_get_ptr(ctx, p, s, o, (ul->val)?(ul->val):(""))) + return; + uci_add_list(ctx, &uci_ptr); + } +} + +void ucix_for_each_section_type(struct uci_context *ctx, + const char *p, const char *t, + void (*cb)(const char*, void*), void *priv) +{ + struct uci_element *e; + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return; + uci_foreach_element(&uci_ptr.p->sections, e) + if (!strcmp(t, uci_to_section(e)->type)) + cb(e->name, priv); +} + +void ucix_for_each_section_option(struct uci_context *ctx, + const char *p, const char *s, + void (*cb)(const char*, const char*, void*), void *priv) +{ + struct uci_element *e; + if(ucix_get_ptr(ctx, p, s, NULL, NULL)) + return; + uci_foreach_element(&uci_ptr.s->options, e) + { + struct uci_option *o = uci_to_option(e); + cb(o->e.name, o->v.string, priv); + } +} + + diff --git a/ucix.h b/ucix.h new file mode 100644 index 0000000..80f0f62 --- /dev/null +++ b/ucix.h @@ -0,0 +1,133 @@ +/* + * ucix + * Copyright (C) 2010 John Crispin + * Copyright (C) 2010 Steven Barth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _UCI_H__ +#define _UCI_H__ +#include +#include "list.h" + +struct ucilist { + struct list_head list; + char *val; +}; + +extern struct uci_ptr uci_ptr; + +int ucix_get_ptr(struct uci_context *ctx, const char *p, + const char *s, const char *o, const char *t); +struct uci_context* ucix_init(const char *config_file, int state); +struct uci_context* ucix_init_path(const char *vpath, const char *config_file, int state); +int ucix_save_state(struct uci_context *ctx, const char *p); +char* ucix_get_option(struct uci_context *ctx, + const char *p, const char *s, const char *o); +int ucix_get_option_list(struct uci_context *ctx, const char *p, + const char *s, const char *o, struct list_head *l); +void ucix_for_each_section_type(struct uci_context *ctx, + const char *p, const char *t, + void (*cb)(const char*, void*), void *priv); +void ucix_for_each_section_option(struct uci_context *ctx, + const char *p, const char *s, + void (*cb)(const char*, const char*, void*), void *priv); +void ucix_add_list(struct uci_context *ctx, const char *p, + const char *s, const char *o, struct list_head *vals); + +static inline void ucix_del(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + if (!ucix_get_ptr(ctx, p, s, o, NULL)) + uci_delete(ctx, &uci_ptr); +} + +static inline void ucix_revert(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + if (!ucix_get_ptr(ctx, p, s, o, NULL)) + uci_revert(ctx, &uci_ptr); +} + +static inline void ucix_add_list_single(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t) +{ + if (ucix_get_ptr(ctx, p, s, o, t)) + return; + uci_add_list(ctx, &uci_ptr); +} + +static inline void ucix_add_option(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t) +{ + if (ucix_get_ptr(ctx, p, s, o, t)) + return; + uci_set(ctx, &uci_ptr); +} + +static inline void ucix_add_section(struct uci_context *ctx, const char *p, const char *s, const char *t) +{ + if(ucix_get_ptr(ctx, p, s, NULL, t)) + return; + uci_set(ctx, &uci_ptr); +} + +static inline void ucix_add_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int t) +{ + char tmp[64]; + snprintf(tmp, 64, "%d", t); + ucix_add_option(ctx, p, s, o, tmp); +} + +static inline void ucix_add_list_single_int(struct uci_context *ctx, const char *p, const char *s, const char *o, const int t) +{ + char tmp[64]; + snprintf(tmp, 64, "%d", t); + ucix_add_list_single(ctx, p, s, o, tmp); +} + +static inline int ucix_get_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int def) +{ + char *tmp = ucix_get_option(ctx, p, s, o); + int ret = def; + + if (tmp) + { + ret = atoi(tmp); + free(tmp); + } + return ret; +} + +static inline int ucix_save(struct uci_context *ctx, const char *p) +{ + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return 1; + uci_save(ctx, uci_ptr.p); + return 0; +} + +static inline int ucix_commit(struct uci_context *ctx, const char *p) +{ + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return 1; + return uci_commit(ctx, &uci_ptr.p, false); +} + +static inline void ucix_cleanup(struct uci_context *ctx) +{ + uci_free_context(ctx); +} + + +#endif diff --git a/uhtbl.c b/uhtbl.c new file mode 100644 index 0000000..9e119b8 --- /dev/null +++ b/uhtbl.c @@ -0,0 +1,340 @@ +/** + * uhtbl - Generic coalesced hash table implementation + * Copyright (C) 2010 Steven Barth + * Copyright (C) 2010 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include "uhtbl.h" + +/* Forward static helpers */ +UHTBL_INLINE uhtbl_size_t _uhtbl_address(uhtbl_t *tbl, uhtbl_bucket_t *bucket); +UHTBL_INLINE uhtbl_bucket_t* _uhtbl_bucket(uhtbl_t *tbl, uhtbl_size_t address); +static uhtbl_bucket_t* _uhtbl_allocate(uhtbl_t *tbl); +static uhtbl_bucket_t* _uhtbl_find(uhtbl_t *tbl, const void *key, +long len, uhtbl_bucket_t **previous, uhtbl_size_t *mainaddress); + + + +UHTBL_API int uhtbl_init(uhtbl_t *tbl, uint32_t bucketsize, +uhtbl_size_t sizehint, uhtbl_hash_t *fct_hash, uhtbl_gc_t *fct_gc) { + sizehint = (sizehint) ? sizehint : UHTBL_MINIMUMSIZE; + tbl->bucketsize = bucketsize; + tbl->fct_hash = fct_hash; + tbl->fct_gc = fct_gc; + if (!tbl->fct_hash || tbl->bucketsize < sizeof(uhtbl_bucket_t)) { + return UHTBL_EINVAL; + } + tbl->payload = 0; + tbl->buckets = NULL; + tbl->used = 0; + + return uhtbl_resize(tbl, sizehint); +} + +UHTBL_API void uhtbl_clear(uhtbl_t *tbl) { + for (uhtbl_size_t i = 0; i < tbl->size; i++) { + uhtbl_bucket_t *bucket = _uhtbl_bucket(tbl, i); + if (tbl->fct_gc && bucket->head.flags & UHTBL_FLAG_OCCUPIED) { + tbl->fct_gc(bucket); + } + bucket->head.flags = 0; + } + tbl->used = 0; + tbl->nextfree = tbl->size - 1; +} + +UHTBL_API void* uhtbl_get (uhtbl_t *tbl, const void *key, long len) { + return _uhtbl_find(tbl, key, len, NULL, NULL); +} + +UHTBL_API void* uhtbl_set (uhtbl_t *tbl, void *key, long len) { + int localkey = 0; + uint16_t keysize = len, flags; + if (!key) { /* Check whether key is treated as a pointer */ + key = &len; + keysize = sizeof(len); + localkey = 1; + } + + uhtbl_size_t mainaddr; + uhtbl_bucket_t *resolve, *match, *prev = NULL; + uhtbl_bucket_t *lookup = _uhtbl_find(tbl, key, keysize, &prev, &mainaddr); + + if (lookup) { + match = lookup; + flags = match->head.flags; + if (flags & UHTBL_FLAG_OCCUPIED) { /* We are replacing an entry */ + if (tbl->fct_gc) { + tbl->fct_gc(match); + } + tbl->used--; + } + } else { + match = prev; + flags = match->head.flags; + if ((flags & UHTBL_FLAG_STRANGER) + && _uhtbl_address(tbl, match) == mainaddr) { + /* Mainposition occupied by key with different hash -> move it away */ + + /* Find old prev and update its next pointer */ + if ((flags & UHTBL_FLAG_LOCALKEY)) { + _uhtbl_find(tbl, 0, + match->head.key.handle, &prev, NULL); + } else { + _uhtbl_find(tbl, match->head.key.ptr, + match->head.keysize, &prev, NULL); + } + + if (!(resolve = _uhtbl_allocate(tbl))) { + if (!uhtbl_resize(tbl, tbl->payload * UHTBL_GROWFACTOR)) { + return uhtbl_set(tbl, (localkey) ? NULL : key, len); + } else { + return NULL; + } + } + memcpy(resolve, match, tbl->bucketsize); /* Copy bucket data */ + prev->head.next = _uhtbl_address(tbl, resolve); + flags = 0; + } else if ((flags & UHTBL_FLAG_OCCUPIED) && + (match->head.keysize != keysize || memcmp((flags & UHTBL_FLAG_LOCALKEY) + ? &match->head.key.handle : match->head.key.ptr, key, keysize))) { + /* Mainposition has different key (but same hash) */ + if (!(resolve = _uhtbl_allocate(tbl))) { + if (!uhtbl_resize(tbl, tbl->payload * UHTBL_GROWFACTOR)) { + return uhtbl_set(tbl, (localkey) ? NULL : key, len); + } else { + return NULL; + } + } + + prev = match; + match = resolve; + flags = UHTBL_FLAG_STRANGER; /* We will not be in main position */ + prev->head.flags |= UHTBL_FLAG_WITHNEXT; /* Main now has next */ + prev->head.next = _uhtbl_address(tbl, match); /* main->next = us */ + } + } + + if (localkey) { + match->head.key.handle = len; + flags |= UHTBL_FLAG_LOCALKEY; + } else { + match->head.key.ptr = key; + flags &= ~UHTBL_FLAG_LOCALKEY; + } + match->head.keysize = keysize; + flags |= UHTBL_FLAG_OCCUPIED; + match->head.flags = flags; + + tbl->used++; + return match; +} + +UHTBL_API void* uhtbl_next(uhtbl_t *tbl, uhtbl_size_t *iter) { + for (; *iter < tbl->size; (*iter)++) { + if (_uhtbl_bucket(tbl, *iter)->head.flags & UHTBL_FLAG_OCCUPIED) { + return _uhtbl_bucket(tbl, (*iter)++); + } + } + return NULL; +} + +UHTBL_API int uhtbl_remove(uhtbl_t *tbl, uhtbl_head_t *head) { + void *key; + long len; + uhtbl_key(head, &key, &len); + return uhtbl_unset(tbl, key, len); +} + +UHTBL_API int uhtbl_unset(uhtbl_t *tbl, const void *key, long len) { + uhtbl_bucket_t *prev = NULL; + uhtbl_bucket_t *bucket = _uhtbl_find(tbl, key, len, &prev, NULL); + if (!bucket) { + return UHTBL_ENOENT; + } + + if (tbl->fct_gc) { + tbl->fct_gc(bucket); + } + + bucket->head.flags &= ~UHTBL_FLAG_OCCUPIED; + tbl->used--; + + /* If not in main position, get us out of the next-list */ + if (bucket->head.flags & UHTBL_FLAG_STRANGER) { + if (bucket->head.flags & UHTBL_FLAG_WITHNEXT) {/* We had next buckets */ + prev->head.next = bucket->head.next; /* Give them to out prev */ + } else { + prev->head.flags &= ~UHTBL_FLAG_WITHNEXT;/* We were the only next */ + } + bucket->head.flags = 0; + } + + uhtbl_size_t address = _uhtbl_address(tbl, bucket); + if (address > tbl->nextfree) { + tbl->nextfree = address; + } + + return UHTBL_OK; +} + +UHTBL_API int uhtbl_resize(uhtbl_t *tbl, uhtbl_size_t payload) { + uhtbl_size_t size = payload / UHTBL_PAYLOADFACTOR; + if (size < payload || size < tbl->used) { + return UHTBL_EINVAL; + } + if (payload == tbl->payload) { + return UHTBL_OK; + } + + void *buckets = calloc(size, tbl->bucketsize); + if (!buckets) { + return UHTBL_ENOMEM; + } + + uhtbl_t oldtbl; /* Save essentials of table for rehashing */ + oldtbl.buckets = tbl->buckets; + oldtbl.bucketsize = tbl->bucketsize; + oldtbl.size = tbl->size; + oldtbl.used = tbl->used; + + tbl->buckets = buckets; + tbl->payload = payload; + tbl->size = size; + tbl->used = 0; + tbl->nextfree = size - 1; + + if (oldtbl.used) { /* Rehash if table had entries before */ + uhtbl_size_t iter = 0; + uhtbl_bucket_t *old, *new; + while ((old = uhtbl_next(&oldtbl, &iter))) { + new = uhtbl_set(tbl, (old->head.flags & UHTBL_FLAG_LOCALKEY) + ? NULL : old->head.key.ptr, + (old->head.flags & UHTBL_FLAG_LOCALKEY) + ? old->head.key.handle : old->head.keysize); + new->head.user = old->head.user; + memcpy(((char*)new) + sizeof(uhtbl_head_t), + ((char*)old) + sizeof(uhtbl_head_t), + tbl->bucketsize - sizeof(uhtbl_head_t)); + } + } + + free(oldtbl.buckets); + return UHTBL_OK; +} + +UHTBL_API void uhtbl_finalize(uhtbl_t *tbl) { + uhtbl_clear(tbl); + free(tbl->buckets); + tbl->buckets = NULL; +} + +UHTBL_API void uhtbl_key(uhtbl_head_t *head, void **key, long *len) { + if (key) { + *key = (head->flags & UHTBL_FLAG_LOCALKEY) + ? NULL : head->key.ptr; + } + if (len) { + *len = (head->flags & UHTBL_FLAG_LOCALKEY) + ? head->key.handle : head->keysize; + } +} + +UHTBL_API void uhtbl_gc_key(void *bucket) { + void *key; + uhtbl_key(bucket, &key, NULL); + free(key); +} + + +/* Static auxiliary */ + +UHTBL_INLINE uhtbl_size_t _uhtbl_address(uhtbl_t *tbl, uhtbl_bucket_t *bucket) { + return ((uint8_t*)bucket - (uint8_t*)tbl->buckets) / tbl->bucketsize; +} + +UHTBL_INLINE uhtbl_bucket_t* _uhtbl_bucket(uhtbl_t *tbl, uhtbl_size_t address) { + return (uhtbl_bucket_t*) + ((uint8_t*)tbl->buckets + (address * tbl->bucketsize)); +} + +static uhtbl_bucket_t* _uhtbl_allocate(uhtbl_t *tbl) { + uhtbl_size_t address = tbl->nextfree; + do { + uhtbl_bucket_t *bucket = _uhtbl_bucket(tbl, address); + if (!(bucket->head.flags & UHTBL_FLAG_OCCUPIED)) { + if (bucket->head.flags & UHTBL_FLAG_WITHNEXT) { + /* Empty bucket still has a successor -> swap it with its */ + /* successor and return the old successor-bucket as free */ + uhtbl_bucket_t *old = bucket; + bucket = _uhtbl_bucket(tbl, old->head.next); + memcpy(old, bucket, tbl->bucketsize); + old->head.flags &= ~UHTBL_FLAG_STRANGER; /* sucessor now main */ + } + /* WARN: If set will ever fail in the future we'd take care here */ + tbl->nextfree = (address) ? address - 1 : 0; + return bucket; + } + } while(address--); + return NULL; +} + +static uhtbl_bucket_t* _uhtbl_find(uhtbl_t *tbl, const void *key, +long len, uhtbl_bucket_t **previous, uhtbl_size_t *mainaddress) { + uint16_t keysize = len; + if (!key) { + key = &len; + keysize = sizeof(len); + } + uhtbl_size_t address = tbl->fct_hash(key, keysize) % tbl->payload; + uhtbl_bucket_t *buck = _uhtbl_bucket(tbl, address); + if (mainaddress) { + *mainaddress = address; + if (!(buck->head.flags & UHTBL_FLAG_OCCUPIED)) { + return buck; + } + } + if (buck->head.flags & UHTBL_FLAG_STRANGER) { + if (previous) { + *previous = buck; + } + return NULL; + } + for (;; buck = _uhtbl_bucket(tbl, address)) { + if (buck->head.flags & UHTBL_FLAG_OCCUPIED + && buck->head.keysize == keysize + && !memcmp((buck->head.flags & UHTBL_FLAG_LOCALKEY) + ? &buck->head.key.handle : buck->head.key.ptr, key, keysize)) { + return buck; + } + if (!(buck->head.flags & UHTBL_FLAG_WITHNEXT)) { + if (previous) { + *previous = buck; + } + return NULL; + } + + address = buck->head.next; + if (previous) { + *previous = buck; + } + } +} diff --git a/uhtbl.h b/uhtbl.h new file mode 100644 index 0000000..55a791e --- /dev/null +++ b/uhtbl.h @@ -0,0 +1,375 @@ +/** + * uhtbl - Generic coalesced hash table implementation + * Copyright (C) 2010 Steven Barth + * Copyright (C) 2010 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ +/** + * uhtbl is a coalesced cellared generic hash table implementation with the aim + * to be both small in code size and heap memory requirements. This hash table + * uses a hybrid approach called coalesced addressing which means that pointers + * to other buckets will be used in the case of a collisions. In this case no + * linked lists have to be used and less allocation calls have to be done. + * To improve performance this hash table carries a cellar for collision + * handling which is an additional address area that lies behind the + * hash-addressable space. + * + * Overhead (on x86 32bit): + * Bookkeeping: 32 Bytes (per hash table) + * Metadata: 12 Bytes including pointer to key and keysize (per bucket) + * + */ + +#ifndef UHTBL_H_ +#define UHTBL_H_ + + +/* compile-time configurables */ + +#ifndef UHTBL_PAYLOADFACTOR + #define UHTBL_PAYLOADFACTOR 0.86 +#endif + +#ifndef UHTBL_GROWFACTOR + #define UHTBL_GROWFACTOR 2 +#endif + +#ifndef UHTBL_MINIMUMSIZE + #define UHTBL_MINIMUMSIZE 16 +#endif + + +#include + +/* Internal flags and values */ +#define UHTBL_FLAG_OCCUPIED 0x01 +#define UHTBL_FLAG_STRANGER 0x02 +#define UHTBL_FLAG_WITHNEXT 0x04 +#define UHTBL_FLAG_LOCALKEY 0x08 +#define UHTBL_MAXIMUMSIZE 2147483648 + +/* Status codes */ +#define UHTBL_OK 0 +#define UHTBL_EINVAL -1 +#define UHTBL_ENOMEM -2 +#define UHTBL_ENOENT -3 + +/* API */ +#if __GNUC__ >= 4 + #ifndef UHTBL_API + #define UHTBL_API + #endif + #define UHTBL_INLINE static inline __attribute__((always_inline)) +#else + #ifndef UHTBL_API + #define UHTBL_API + #endif + #define UHTBL_INLINE static inline +#endif + + +typedef union uhtbl_key uhtbl_key_t; +typedef struct uhtbl_head uhtbl_head_t; +typedef struct uhtbl_bucket uhtbl_bucket_t; +typedef struct uhtbl_config uhtbl_config_t; +typedef struct uhtbl uhtbl_t; +typedef uint32_t uhtbl_size_t; + +typedef uhtbl_size_t(uhtbl_hash_t)(const void*, int len); +typedef void(uhtbl_gc_t)(void *bucket); + +union uhtbl_key { + void *ptr; + long handle; +}; + +struct uhtbl_head { + uint8_t user; + uint8_t flags; + uint16_t keysize; + uhtbl_size_t next; + uhtbl_key_t key; +}; + +struct uhtbl_bucket { + uhtbl_head_t head; +}; + +struct uhtbl { + uint32_t bucketsize; + uhtbl_size_t size; + uhtbl_size_t used; + uhtbl_size_t payload; + uhtbl_size_t nextfree; + uhtbl_hash_t *fct_hash; + uhtbl_gc_t *fct_gc; + void *buckets; +}; + +/** + * uhtbl_init() - Initialize a hash table. + * @tbl: hash table + * @bucketsize: size of a bucket + * @sizehint: estimated maximum of needed buckets (optional) + * @fct_hash: hash function + * @fct_gc: bucket garbage collector (optional) + * + * Initializes a new hash table and preallocates memory. + * + * bucketsize is the size in Bytes each bucket will use but note the following: + * Each bucket needs to begin with a struct uhtbl_head_t that keeps its metadata + * in addition to the payload you want it to carry. You are advised to define a + * bucket struct with the first element being a uhtbl_head_t followed by your + * desired payload and pass the size of this struct to bucketsize. + * + * sizehint is a hint on how many distinct entries will be stored in the hash + * table. This will be used to preallocate space for the buckets and is useful + * if you know how many entries will be stored in the hash table as it avoids + * expensive rehashing cycles. sizehint should be a power of 2. + * + * fct_hash is the hash function used. It takes a constant void pointer and a + * integer as size parameter and returns an unsigned (32bit) int. + * + * fct_gc is the garbage collector for buckets. Every time a bucket gets unset + * or the hash table gets cleared or finalized the garbage collector function + * taking a pointer to a bucket will take care of doing any finalization for + * the buckets' payload and key data. You may use uhtbl_key() to get a reference + * to your key pointer or handle for deallocation or cleaning up any other + * references. There is an optionally selectable garbage collector that will + * take care of free()ing key pointers if your keys point to memory areas. + * You have to pass uhtbl_gc_key as fct_gc parameter to use it. You may also + * call this function in your custom garbage collector. + * + * WARNING: Your garbage collector function must otherwise not change the + * metadata in the uhtbl_head_t structure of the bucket else behaviour will be + * undefined for all subsequent actions. + * + * + * Example: + * struct mybucket { + * uhtbl_head_t head; + * int mypayload1; + * int mypayload2; + * } + * + * uhtbl_t table; + * uhtbl_init(&table, sizeof(struct mybucket), 32, MurmurHash2, NULL); + * + * Returns 0 on success or a negative error code. + */ +UHTBL_API int uhtbl_init(uhtbl_t *tbl, uint32_t bucketsize, +uhtbl_size_t sizehint, uhtbl_hash_t *fct_hash, uhtbl_gc_t *fct_gc); + +/** + * uhtbl_get() - Get a bucket by its key. + * @tbl: hash table + * @key: key + * @len: length of key + * + * Finds and returns the bucket with a given key. + * + * Key can either be: + * 1. A pointer to a memory area then len is its length (must be < 64KB) + * 2. A NULL-pointer then len is a locally stored numerical key + * + * + * Example: + * struct mybucket *bucket; + * bucket = uhtbl_get(table, "foo", sizeof("foo")); + * printf("%i", bucket->mypayload1); + * + * bucket = uhtbl_get(table, NULL, 42); + * printf("%i", bucket->mypayload1); + * + * Returns the bucket or NULL if no bucket with given key was found. + */ +UHTBL_API void* uhtbl_get(uhtbl_t *tbl, const void *key, long len); + +/** + * uhtbl_set() - Sets a bucket for given key. + * @tbl: hash table + * @key: key + * @len: length of key + * + * Sets a new bucket for the given key and returns a pointer to the bucket for + * you to assign your payload data. If there is already a bucket with that key + * it will be unset first. + * + * Key can either be: + * 1. A pointer to a memory area then len is its length (must be < 64KB) + * 2. A NULL-pointer then len is a locally stored numerical key + * + * NOTE: If key is a pointer memory management of it will be your business. + * You might want to use a garbage collection function (see uhtbl_init()) + * + * NOTE: The payload area of your bucket is NOT initialized to zeroes. + * + * WARNING: Note the following side effects when setting previously unset keys: + * 1. A set may trigger several moving actions changing the order of buckets. + * 2. A set may trigger a rehashing cycle if all buckets are occupied. + * Therefore accessing any previously acquired pointers to any bucket results in + * undefined behaviour. In addition iterations which have started before may + * result in unwanted behaviour (e.g. buckets may be skipped or visited twice). + * + * + * Example: + * struct mybucket *bucket; + * bucket = uhtbl_set(table, "foo", sizeof("foo")); + * bucket->mypayload1 = 42: + * + * bucket = uhtbl_set(table, NULL, 42); + * bucket->mypayload1 = 1337; + * + * + * Returns the bucket or NULL if no bucket could be allocated (out of memory). + */ +UHTBL_API void* uhtbl_set(uhtbl_t *tbl, void *key, long len); + +/** + * uhtbl_next() - Iterates over all entries of the hash table. + * @tbl: hash table + * @iter: Iteration counter + * + * Iterates over all entries of the hash table. + * + * iter is a pointer to a numeric variable that should be set to zero before + * the first call and will save the iteration state. + * + * NOTE: You may safely do several iterations in parallel. You may also safely + * unset any buckets of the hashtable or set keys that are currently in the + * hash table. However setting buckets with keys that don't have an assigned + * bucket yet results in undefined behaviour. + * + * Example: + * uint32_t iter = 0; + * struct mybucket *bucket; + * while ((bucket = uhtbl_next(table, &iter))) { + * printf("%i", bucket->mypayload1); + * } + * + * Return the next bucket or NULL if all buckets were already visited. + */ +UHTBL_API void* uhtbl_next(uhtbl_t *tbl, uhtbl_size_t *iter); + +/** + * uhtbl_unset() - Unsets the bucket with given key. + * @tbl: hash table + * @key: key + * @len: length of key (optional) + * + * Unsets the bucket with given key and calls the garbage collector to free + * any payload resources - if any. + * + * Key can either be: + * 1. A pointer to a memory area then len is its length (must be < 64KB) + * 2. A NULL-pointer then len is a locally stored numerical key + * + * Example: + * uhtbl_unset(table, NULL, 42); + * + * Returns 0 on success or a negative error code if there was no matching bucket + */ +UHTBL_API int uhtbl_unset(uhtbl_t *tbl, const void *key, long len); + +/** + * uhtbl_remove() - Unsets a bucket. + * @tbl: hash table + * @head: bucket head + * + * Unsets the bucket with given address and calls the garbage collector to free + * any payload resources - if any. + * + * Example: + * uhtbl_remove(table, &bucket->head); + * + * Returns 0 on success or a negative error code if the bucket was not found + */ +UHTBL_API int uhtbl_remove(uhtbl_t *tbl, uhtbl_head_t *head); + +/** + * uhtbl_clear() - Clears the hashtable without freeing its memory. + * @tbl: hash table + * + * Clears all buckets of the hashtable invoking the garbage collector - if any + * but does not free the memory of the hash table. This is usually more + * efficient than iterating and using unset. + * + * Returns nothing. + */ +UHTBL_API void uhtbl_clear(uhtbl_t *tbl); + +/** + * uhtbl_resize() - Resizes and rehashes the hash table. + * @tbl: hash table + * @payload: Buckets to reserve. + * + * Resizes the hash table and rehashes its entries. + * + * payload is the number of buckets the hashtable should allocate. It must be + * greater or at least equal to the number of buckets currently occupied. + * + * NOTE: Rehashing is an expensive process which should be avoided if possible. + * However resizing will be automatically done if you try to set a new bucket + * but all buckets are already occupied. In this case the payload bucket count + * is usually doubled. There is currently no automatic resizing done when the + * bucket usage count decreases. You have to take care of this by yourself. + * + * Returns 0 on success or a negative error code if out of memory. + */ +UHTBL_API int uhtbl_resize(uhtbl_t *tbl, uhtbl_size_t payload); + +/** + * uhtbl_clear() - Clears the hashtable and frees the bucket memory. + * @tbl: hash table + * + * Clears all buckets of the hashtable invoking the garbage collector - if any + * and frees the memory occupied by the buckets. + * + * Returns nothing. + */ +UHTBL_API void uhtbl_finalize(uhtbl_t *tbl); + +/** + * uhtbl_key() - Returns the key parameters as passed to set. + * @head: Bucket head + * @key: Pointer where key pointer should be stored (optional) + * @len: Pointer where key len should be stored (optional) + * + * This function might be useful to obtain the key parameters of a bucket + * when doing garbage collection. + * + * Returns nothing. + */ +UHTBL_API void uhtbl_key(uhtbl_head_t *head, void **key, long *len); + +/** + * uhtbl_gc_key() - Basic garbage collector that frees key memory. + * @bucket: Bucket + * + * This function is a basic garbage collector that will free any key pointers. + * However it will not touch your payload data. + * + * WARNING: You must not call this function directly on any bucket otherwise + * behaviour will be unspecified. Instead you may pass this function to the + * uhtbl_init function. You may also call this function from inside a custom + * garbage collector. + * + * Returns nothing. + */ +UHTBL_API void uhtbl_gc_key(void *bucket); + +#endif /* UHTBL_H_ */ diff --git a/ulog.h b/ulog.h new file mode 100644 index 0000000..ce0cfb2 --- /dev/null +++ b/ulog.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 John Crispin + * Copyright (C) 2010 Steven Barth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _ULOG_H__ +#define _ULOG_H__ + +#include + +#define LOG log_printf +#define log_start(name, daemon) \ + openlog(name, (LOG_PERROR | LOG_CONS), LOG_USER) +#define log_printf(...) syslog(LOG_NOTICE, __VA_ARGS__) + +#endif diff --git a/uloop.c b/uloop.c new file mode 100644 index 0000000..627ba1c --- /dev/null +++ b/uloop.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2010 Felix Fietkau + * Copyright (C) 2010 John Crispin + * Copyright (C) 2010 Steven Barth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uloop.h" + +/** + * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17 + */ +#ifndef EPOLLRDHUP +#define EPOLLRDHUP 0x2000 +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +struct uloop_timeout *first_timeout; +static int epoll_fd; +static bool cancel; + +int uloop_fd_add(struct uloop_fd *sock, unsigned int flags) +{ + struct epoll_event ev; + int op = sock->registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; + unsigned int fl; + int ret; + + if (!sock->registered) { + fl = fcntl(sock->fd, F_GETFL, 0); + fl |= O_NONBLOCK; + fcntl(sock->fd, F_SETFL, fl); + } + + memset(&ev, 0, sizeof(struct epoll_event)); + + if (flags & ULOOP_READ) + ev.events |= EPOLLIN | EPOLLRDHUP; + + if (flags & ULOOP_WRITE) + ev.events |= EPOLLOUT; + + if (flags & ULOOP_EDGE_TRIGGER) + ev.events |= EPOLLET; + + ev.data.fd = sock->fd; + ev.data.ptr = sock; + + ret = epoll_ctl(epoll_fd, op, sock->fd, &ev); + if (ret < 0) + goto out; + + sock->registered = true; + sock->eof = false; + +out: + return ret; +} + +int uloop_fd_delete(struct uloop_fd *sock) +{ + sock->registered = false; + return epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sock->fd, 0); +} + +static int tv_diff(struct timeval *t1, struct timeval *t2) +{ + if (t1->tv_sec != t2->tv_sec) + return (t1->tv_sec - t2->tv_sec) * 1000; + else + return (t1->tv_usec - t2->tv_usec) / 1000; +} + +int uloop_timeout_add(struct uloop_timeout *timeout) +{ + struct uloop_timeout **head = &first_timeout; + struct uloop_timeout *prev = NULL; + + if (timeout->pending) + return -1; + + while (*head) { + if (tv_diff(&(*head)->time, &timeout->time) > 0) + break; + + prev = *head; + head = &(*head)->next; + } + + timeout->prev = prev; + timeout->next = *head; + if (timeout->next) + timeout->next->prev = timeout; + *head = timeout; + timeout->pending = true; + + return 0; +} + +int uloop_timeout_set(struct uloop_timeout *timeout, int msecs) +{ + struct timeval *time = &timeout->time; + + if (timeout->pending) + uloop_timeout_cancel(timeout); + + gettimeofday(&timeout->time, NULL); + + time->tv_sec += msecs / 1000; + time->tv_usec += msecs % 1000; + + if (time->tv_usec > 1000000) { + time->tv_sec++; + time->tv_usec %= 100000; + } + + return uloop_timeout_add(timeout); +} + +int uloop_timeout_cancel(struct uloop_timeout *timeout) +{ + if (!timeout->pending) + return -1; + + if (timeout->prev) + timeout->prev->next = timeout->next; + else + first_timeout = timeout->next; + + if (timeout->next) + timeout->next->prev = timeout->prev; + + timeout->pending = false; + + return 0; +} + +static void uloop_handle_sigint(int signo) +{ + cancel = true; +} + +static void uloop_setup_signals(void) +{ + struct sigaction s; + memset(&s, 0, sizeof(struct sigaction)); + s.sa_handler = uloop_handle_sigint; + s.sa_flags = 0; + sigaction(SIGINT, &s, NULL); +} + +static int uloop_get_next_timeout(struct timeval *tv) +{ + int diff; + + if (!first_timeout) + return -1; + + diff = tv_diff(&first_timeout->time, tv); + if (diff < 0) + return 0; + + return diff; +} + +static void uloop_process_timeouts(struct timeval *tv) +{ + struct uloop_timeout *timeout; + + while (first_timeout) { + if (tv_diff(&first_timeout->time, tv) > 0) + break; + + timeout = first_timeout; + uloop_timeout_cancel(timeout); + if (timeout->cb) + timeout->cb(timeout); + } +} + +void uloop_end(void) +{ + cancel = true; +} + +int uloop_init(void) +{ + epoll_fd = epoll_create(32); + if (epoll_fd < 0) + return -1; + + fcntl(epoll_fd, F_SETFD, fcntl(epoll_fd, F_GETFD) | FD_CLOEXEC); + return 0; +} + +void uloop_run(void) +{ + struct epoll_event events[10]; + struct timeval tv; + int timeout; + int nfds, n; + + uloop_setup_signals(); + while(!cancel) + { + gettimeofday(&tv, NULL); + uloop_process_timeouts(&tv); + timeout = uloop_get_next_timeout(&tv); + nfds = epoll_wait(epoll_fd, events, ARRAY_SIZE(events), timeout); + for(n = 0; n < nfds; ++n) + { + struct uloop_fd *u = events[n].data.ptr; + unsigned int ev = 0; + + if(events[n].events & EPOLLERR) { + u->error = true; + uloop_fd_delete(u); + } + + if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR))) + continue; + + if(events[n].events & EPOLLRDHUP) + u->eof = true; + + if(events[n].events & EPOLLIN) + ev |= ULOOP_READ; + + if(events[n].events & EPOLLOUT) + ev |= ULOOP_WRITE; + + if(u->cb) + u->cb(u, ev); + } + } +} + +void uloop_done(void) +{ + close(epoll_fd); +} diff --git a/uloop.h b/uloop.h new file mode 100644 index 0000000..f84050a --- /dev/null +++ b/uloop.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _ULOOP_H__ +#define _ULOOP_H__ + +#include +#include + +struct uloop_fd; +struct uloop_timeout; + +typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events); +typedef void (*uloop_timeout_handler)(struct uloop_timeout *t); + +#define ULOOP_READ (1 << 0) +#define ULOOP_WRITE (1 << 1) +#define ULOOP_EDGE_TRIGGER (1 << 2) + +struct uloop_fd +{ + uloop_fd_handler cb; + int fd; + bool eof; + bool error; + bool registered; +}; + +struct uloop_timeout +{ + uloop_timeout_handler cb; + struct uloop_timeout *prev; + struct uloop_timeout *next; + struct timeval time; + bool pending; +}; + +int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); +int uloop_fd_delete(struct uloop_fd *sock); + +int uloop_timeout_add(struct uloop_timeout *timeout); +int uloop_timeout_set(struct uloop_timeout *timeout, int msecs); +int uloop_timeout_cancel(struct uloop_timeout *timeout); + +void uloop_end(void); +int uloop_init(void); +void uloop_run(void); +void uloop_done(void); + +#endif diff --git a/unl.c b/unl.c new file mode 100644 index 0000000..a60ba08 --- /dev/null +++ b/unl.c @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unl.h" + +static int unl_init(struct unl *unl) +{ + unl->sock = nl_socket_alloc(); + if (!unl->sock) + return -1; + + return 0; +} + +int unl_genl_init(struct unl *unl, const char *family) +{ + memset(unl, 0, sizeof(*unl)); + + if (unl_init(unl)) + goto error_out; + + unl->hdrlen = NLMSG_ALIGN(sizeof(struct genlmsghdr)); + unl->family_name = strdup(family); + if (!unl->family_name) + goto error; + + if (genl_connect(unl->sock)) + goto error; + + if (genl_ctrl_alloc_cache(unl->sock, &unl->cache)) + goto error; + + unl->family = genl_ctrl_search_by_name(unl->cache, family); + if (!unl->family) + goto error; + + return 0; + +error: + unl_free(unl); +error_out: + return -1; +} + +void unl_free(struct unl *unl) +{ + if (unl->family_name) + free(unl->family_name); + + if (unl->sock) + nl_socket_free(unl->sock); + + if (unl->cache) + nl_cache_free(unl->cache); + + memset(unl, 0, sizeof(*unl)); +} + +static int +ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int +finish_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_SKIP; +} + +static int +error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +struct nl_msg *unl_genl_msg(struct unl *unl, int cmd, bool dump) +{ + struct nl_msg *msg; + int flags = 0; + + msg = nlmsg_alloc(); + if (!msg) + goto out; + + if (dump) + flags |= NLM_F_DUMP; + + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, + genl_family_get_id(unl->family), 0, flags, cmd, 0); + +out: + return msg; +} + +int unl_genl_request(struct unl *unl, struct nl_msg *msg, unl_cb handler, void *arg) +{ + struct nlmsghdr *nlh; + struct nl_cb *cb; + int err; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + nlh = nlmsg_hdr(msg); + + err = nl_send_auto_complete(unl->sock, msg); + if (err < 0) + goto out; + + err = 1; + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + if (handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handler, arg); + + while (err > 0) + nl_recvmsgs(unl->sock, cb); + +out: + nlmsg_free(msg); + nl_cb_put(cb); + return err; +} + +static int request_single_cb(struct nl_msg *msg, void *arg) +{ + struct nl_msg **dest = arg; + + if (!*dest) { + nlmsg_get(msg); + *dest = msg; + } + return NL_SKIP; +} + +int unl_genl_request_single(struct unl *unl, struct nl_msg *msg, struct nl_msg **dest) +{ + *dest = NULL; + return unl_genl_request(unl, msg, request_single_cb, dest); +} + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +void unl_genl_loop(struct unl *unl, unl_cb handler, void *arg) +{ + struct nl_cb *cb; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + unl->loop_done = false; + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handler, arg); + + while (!unl->loop_done) + nl_recvmsgs(unl->sock, cb); + + nl_cb_put(cb); +} + +static int unl_genl_multicast_id(struct unl *unl, const char *name) +{ + struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1]; + struct nlattr *groups, *group; + struct nl_msg *msg; + int ctrlid; + int ret = -1; + int rem; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + ctrlid = genl_ctrl_resolve(unl->sock, "nlctrl"); + genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, unl->family_name); + unl_genl_request_single(unl, msg, &msg); + if (!msg) + goto nla_put_failure; + + groups = unl_find_attr(unl, msg, CTRL_ATTR_MCAST_GROUPS); + if (!groups) + goto fail; + + nla_for_each_nested(group, groups, rem) { + const char *gn; + + nla_parse(tb, CTRL_ATTR_MCAST_GRP_MAX, nla_data(group), + nla_len(group), NULL); + + if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || + !tb[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + gn = nla_data(tb[CTRL_ATTR_MCAST_GRP_NAME]); + if (strcmp(gn, name) != 0) + continue; + + ret = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); + break; + } + +fail: + nlmsg_free(msg); +nla_put_failure: + return ret; +} + +int unl_genl_subscribe(struct unl *unl, const char *name) +{ + int mcid; + + mcid = unl_genl_multicast_id(unl, name); + if (mcid < 0) + return mcid; + + return nl_socket_add_membership(unl->sock, mcid); +} + +int unl_genl_unsubscribe(struct unl *unl, const char *name) +{ + int mcid; + + mcid = unl_genl_multicast_id(unl, name); + if (mcid < 0) + return mcid; + + return nl_socket_drop_membership(unl->sock, mcid); +} + +int unl_nl80211_phy_lookup(const char *name) +{ + char buf[32]; + int fd, pos; + + snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name); + + fd = open(buf, O_RDONLY); + if (fd < 0) + return -1; + pos = read(fd, buf, sizeof(buf) - 1); + if (pos < 0) { + close(fd); + return -1; + } + buf[pos] = '\0'; + close(fd); + return atoi(buf); +} + +int unl_nl80211_wdev_to_phy(struct unl *unl, int wdev) +{ + struct nl_msg *msg; + struct nlattr *attr; + int ret = -1; + + msg = unl_genl_msg(unl, NL80211_CMD_GET_INTERFACE, false); + if (!msg) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev); + if (unl_genl_request_single(unl, msg, &msg) < 0) + return -1; + + attr = unl_find_attr(unl, msg, NL80211_ATTR_WIPHY); + if (!attr) + goto out; + + ret = nla_get_u32(attr); +out: +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + diff --git a/unl.h b/unl.h new file mode 100644 index 0000000..57e348a --- /dev/null +++ b/unl.h @@ -0,0 +1,46 @@ +#ifndef __UNL_H +#define __UNL_H + +#include +#include +#include +#include + +struct unl { + struct nl_sock *sock; + struct nl_cache *cache; + struct genl_family *family; + char *family_name; + int hdrlen; + bool loop_done; +}; + +int unl_genl_init(struct unl *unl, const char *family); +void unl_free(struct unl *unl); + +typedef int (*unl_cb)(struct nl_msg *, void *); + +struct nl_msg *unl_genl_msg(struct unl *unl, int cmd, bool dump); +int unl_genl_request(struct unl *unl, struct nl_msg *msg, unl_cb handler, void *arg); +int unl_genl_request_single(struct unl *unl, struct nl_msg *msg, struct nl_msg **dest); +void unl_genl_loop(struct unl *unl, unl_cb handler, void *arg); + +int unl_genl_subscribe(struct unl *unl, const char *name); +int unl_genl_unsubscribe(struct unl *unl, const char *name); + +int unl_nl80211_phy_lookup(const char *name); +int unl_nl80211_wdev_to_phy(struct unl *unl, int wdev); +struct nl_msg *unl_nl80211_phy_msg(struct unl *unl, int phy, int cmd, bool dump); +struct nl_msg *unl_nl80211_vif_msg(struct unl *unl, int dev, int cmd, bool dump); + +static inline void unl_loop_done(struct unl *unl) +{ + unl->loop_done = true; +} + +static inline struct nlattr *unl_find_attr(struct unl *unl, struct nl_msg *msg, int attr) +{ + return nlmsg_find_attr(nlmsg_hdr(msg), unl->hdrlen, attr); +} + +#endif diff --git a/usock.c b/usock.c new file mode 100644 index 0000000..613b2dd --- /dev/null +++ b/usock.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usock.h" + +int usock(int type, const char *host, const char *service) { + int sock = -1; + + if (service && !(type & USOCK_UNIX)) { + struct addrinfo *result, *rp; + + struct addrinfo hints = { + .ai_family = (type & USOCK_IPV6ONLY) ? AF_INET6 : + (type & USOCK_IPV4ONLY) ? AF_INET : AF_UNSPEC, + .ai_socktype = ((type & 0xff) == USOCK_TCP) + ? SOCK_STREAM : SOCK_DGRAM, + .ai_flags = AI_ADDRCONFIG + | ((type & USOCK_SERVER) ? AI_PASSIVE : 0) + | ((type & USOCK_NUMERIC) ? AI_NUMERICHOST : 0), + }; + + if (getaddrinfo(host, service, &hints, &result)) { + return -1; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + if ((sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) + == -1) { + continue; + } + + if (!(type & USOCK_NOCLOEXEC)) { + fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); + } + + if (type & USOCK_NONBLOCK) { + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); + } + + if (type & USOCK_SERVER) { + const int one = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + + if (!bind(sock, rp->ai_addr, rp->ai_addrlen) + && ((type & 0xff) != USOCK_TCP || !listen(sock, SOMAXCONN))) { + break; + } + } else { + if (!connect(sock, rp->ai_addr, rp->ai_addrlen) + || errno == EINPROGRESS) { + break; + } + } + + close(sock); + sock = -1; + } + freeaddrinfo(result); + } else { + struct sockaddr_un sun = {.sun_family = AF_UNIX}; + if (strlen(host) >= sizeof(sun.sun_path)) { + errno = EINVAL; + return -1; + } + strcpy(sun.sun_path, host); + + if ((sock = socket(AF_UNIX, ((type & 0xff) == USOCK_TCP) + ? SOCK_STREAM : SOCK_DGRAM, 0)) == -1) { + return -1; + } + + if (!(type & USOCK_NOCLOEXEC)) { + fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); + } + + if (type & USOCK_NONBLOCK) { + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); + } + + if (type & USOCK_SERVER) { + if (bind(sock, (struct sockaddr*)&sun, sizeof(sun)) || + ((type & 0xff) == USOCK_TCP && listen(sock, SOMAXCONN))) { + close(sock); + return -1; + } + } else { + if (connect(sock, (struct sockaddr*)&sun, sizeof(sun)) + && errno != EINPROGRESS) { + close(sock); + return -1; + } + } + } + return sock; +} diff --git a/usock.h b/usock.h new file mode 100644 index 0000000..12f8127 --- /dev/null +++ b/usock.h @@ -0,0 +1,17 @@ +#ifndef USOCK_H_ +#define USOCK_H_ + +#define USOCK_TCP 0 +#define USOCK_UDP 1 + +#define USOCK_SERVER 0x0100 +#define USOCK_NOCLOEXEC 0x0200 +#define USOCK_NONBLOCK 0x0400 +#define USOCK_NUMERIC 0x0800 +#define USOCK_IPV6ONLY 0x2000 +#define USOCK_IPV4ONLY 0x4000 +#define USOCK_UNIX 0x8000 + +int usock(int type, const char *host, const char *service); + +#endif /* USOCK_H_ */