diff options
Diffstat (limited to 'shared')
-rw-r--r-- | shared/.gitignore | 5 | ||||
-rw-r--r-- | shared/array.c | 113 | ||||
-rw-r--r-- | shared/array.h | 22 | ||||
-rw-r--r-- | shared/hash.c | 341 | ||||
-rw-r--r-- | shared/hash.h | 22 | ||||
-rw-r--r-- | shared/macro.h | 76 | ||||
-rw-r--r-- | shared/missing.h | 59 | ||||
-rw-r--r-- | shared/scratchbuf.c | 60 | ||||
-rw-r--r-- | shared/scratchbuf.h | 31 | ||||
-rw-r--r-- | shared/strbuf.c | 128 | ||||
-rw-r--r-- | shared/strbuf.h | 30 | ||||
-rw-r--r-- | shared/util.c | 548 | ||||
-rw-r--r-- | shared/util.h | 104 |
13 files changed, 1539 insertions, 0 deletions
diff --git a/shared/.gitignore b/shared/.gitignore new file mode 100644 index 0000000..088ef79 --- /dev/null +++ b/shared/.gitignore @@ -0,0 +1,5 @@ +.dirstamp +.deps/ +.libs/ +*.la +*.lo diff --git a/shared/array.c b/shared/array.c new file mode 100644 index 0000000..c2e2e14 --- /dev/null +++ b/shared/array.c @@ -0,0 +1,113 @@ +/* + * libkmod - interface to kernel module operations + * + * Copyright (C) 2011-2013 ProFUSION embedded systems + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <shared/array.h> + +/* basic pointer array growing in steps */ + + +static int array_realloc(struct array *array, size_t new_total) +{ + void *tmp = realloc(array->array, sizeof(void *) * new_total); + if (tmp == NULL) + return -ENOMEM; + array->array = tmp; + array->total = new_total; + return 0; +} + +void array_init(struct array *array, size_t step) +{ + assert(step > 0); + array->array = NULL; + array->count = 0; + array->total = 0; + array->step = step; +} + +int array_append(struct array *array, const void *element) +{ + size_t idx; + + if (array->count + 1 >= array->total) { + int r = array_realloc(array, array->total + array->step); + if (r < 0) + return r; + } + idx = array->count; + array->array[idx] = (void *)element; + array->count++; + return idx; +} + +int array_append_unique(struct array *array, const void *element) +{ + void **itr = array->array; + void **itr_end = itr + array->count; + for (; itr < itr_end; itr++) + if (*itr == element) + return -EEXIST; + return array_append(array, element); +} + +void array_pop(struct array *array) { + array->count--; + if (array->count + array->step < array->total) { + int r = array_realloc(array, array->total - array->step); + if (r < 0) + return; + } +} + +void array_free_array(struct array *array) { + free(array->array); + array->count = 0; + array->total = 0; +} + + +void array_sort(struct array *array, int (*cmp)(const void *a, const void *b)) +{ + qsort(array->array, array->count, sizeof(void *), cmp); +} + +int array_remove_at(struct array *array, unsigned int pos) +{ + if (array->count <= pos) + return -ENOENT; + + array->count--; + if (pos < array->count) + memmove(array->array + pos, array->array + pos + 1, + sizeof(void *) * (array->count - pos)); + + if (array->count + array->step < array->total) { + int r = array_realloc(array, array->total - array->step); + /* ignore error */ + if (r < 0) + return 0; + } + + return 0; +} diff --git a/shared/array.h b/shared/array.h new file mode 100644 index 0000000..b88482f --- /dev/null +++ b/shared/array.h @@ -0,0 +1,22 @@ +#pragma once + +#include <stddef.h> + +/* + * Declaration of struct array is in header because we may want to embed the + * structure into another, so we need to know its size + */ +struct array { + void **array; + size_t count; + size_t total; + size_t step; +}; + +void array_init(struct array *array, size_t step); +int array_append(struct array *array, const void *element); +int array_append_unique(struct array *array, const void *element); +void array_pop(struct array *array); +void array_free_array(struct array *array); +void array_sort(struct array *array, int (*cmp)(const void *a, const void *b)); +int array_remove_at(struct array *array, unsigned int pos); diff --git a/shared/hash.c b/shared/hash.c new file mode 100644 index 0000000..a87bc50 --- /dev/null +++ b/shared/hash.c @@ -0,0 +1,341 @@ +/* + * libkmod - interface to kernel module operations + * + * Copyright (C) 2011-2013 ProFUSION embedded systems + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> + +#include <shared/hash.h> +#include <shared/util.h> + +struct hash_entry { + const char *key; + const void *value; +}; + +struct hash_bucket { + struct hash_entry *entries; + unsigned int used; + unsigned int total; +}; + +struct hash { + unsigned int count; + unsigned int step; + unsigned int n_buckets; + void (*free_value)(void *value); + struct hash_bucket buckets[]; +}; + +struct hash *hash_new(unsigned int n_buckets, + void (*free_value)(void *value)) +{ + struct hash *hash; + + n_buckets = ALIGN_POWER2(n_buckets); + hash = calloc(1, sizeof(struct hash) + + n_buckets * sizeof(struct hash_bucket)); + if (hash == NULL) + return NULL; + hash->n_buckets = n_buckets; + hash->free_value = free_value; + hash->step = n_buckets / 32; + if (hash->step == 0) + hash->step = 4; + else if (hash->step > 64) + hash->step = 64; + return hash; +} + +void hash_free(struct hash *hash) +{ + struct hash_bucket *bucket, *bucket_end; + + if (hash == NULL) + return; + + bucket = hash->buckets; + bucket_end = bucket + hash->n_buckets; + for (; bucket < bucket_end; bucket++) { + if (hash->free_value) { + struct hash_entry *entry, *entry_end; + entry = bucket->entries; + entry_end = entry + bucket->used; + for (; entry < entry_end; entry++) + hash->free_value((void *)entry->value); + } + free(bucket->entries); + } + free(hash); +} + +static inline unsigned int hash_superfast(const char *key, unsigned int len) +{ + /* Paul Hsieh (http://www.azillionmonkeys.com/qed/hash.html) + * used by WebCore (http://webkit.org/blog/8/hashtables-part-2/) + * EFL's eina and possible others. + */ + unsigned int tmp, hash = len, rem = len & 3; + + len /= 4; + + /* Main loop */ + for (; len > 0; len--) { + hash += get_unaligned((uint16_t *) key); + tmp = (get_unaligned((uint16_t *)(key + 2)) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + key += 4; + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: + hash += get_unaligned((uint16_t *) key); + hash ^= hash << 16; + hash ^= key[2] << 18; + hash += hash >> 11; + break; + + case 2: + hash += get_unaligned((uint16_t *) key); + hash ^= hash << 11; + hash += hash >> 17; + break; + + case 1: + hash += *key; + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} + +/* + * add or replace key in hash map. + * + * none of key or value are copied, just references are remembered as is, + * make sure they are live while pair exists in hash! + */ +int hash_add(struct hash *hash, const char *key, const void *value) +{ + unsigned int keylen = strlen(key); + unsigned int hashval = hash_superfast(key, keylen); + unsigned int pos = hashval & (hash->n_buckets - 1); + struct hash_bucket *bucket = hash->buckets + pos; + struct hash_entry *entry, *entry_end; + + if (bucket->used + 1 >= bucket->total) { + unsigned new_total = bucket->total + hash->step; + size_t size = new_total * sizeof(struct hash_entry); + struct hash_entry *tmp = realloc(bucket->entries, size); + if (tmp == NULL) + return -errno; + bucket->entries = tmp; + bucket->total = new_total; + } + + entry = bucket->entries; + entry_end = entry + bucket->used; + for (; entry < entry_end; entry++) { + int c = strcmp(key, entry->key); + if (c == 0) { + if (hash->free_value) + hash->free_value((void *)entry->value); + entry->key = key; + entry->value = value; + return 0; + } else if (c < 0) { + memmove(entry + 1, entry, + (entry_end - entry) * sizeof(struct hash_entry)); + break; + } + } + + entry->key = key; + entry->value = value; + bucket->used++; + hash->count++; + return 0; +} + +/* similar to hash_add(), but fails if key already exists */ +int hash_add_unique(struct hash *hash, const char *key, const void *value) +{ + unsigned int keylen = strlen(key); + unsigned int hashval = hash_superfast(key, keylen); + unsigned int pos = hashval & (hash->n_buckets - 1); + struct hash_bucket *bucket = hash->buckets + pos; + struct hash_entry *entry, *entry_end; + + if (bucket->used + 1 >= bucket->total) { + unsigned new_total = bucket->total + hash->step; + size_t size = new_total * sizeof(struct hash_entry); + struct hash_entry *tmp = realloc(bucket->entries, size); + if (tmp == NULL) + return -errno; + bucket->entries = tmp; + bucket->total = new_total; + } + + entry = bucket->entries; + entry_end = entry + bucket->used; + for (; entry < entry_end; entry++) { + int c = strcmp(key, entry->key); + if (c == 0) + return -EEXIST; + else if (c < 0) { + memmove(entry + 1, entry, + (entry_end - entry) * sizeof(struct hash_entry)); + break; + } + } + + entry->key = key; + entry->value = value; + bucket->used++; + hash->count++; + return 0; +} + +static int hash_entry_cmp(const void *pa, const void *pb) +{ + const struct hash_entry *a = pa; + const struct hash_entry *b = pb; + return strcmp(a->key, b->key); +} + +void *hash_find(const struct hash *hash, const char *key) +{ + unsigned int keylen = strlen(key); + unsigned int hashval = hash_superfast(key, keylen); + unsigned int pos = hashval & (hash->n_buckets - 1); + const struct hash_bucket *bucket = hash->buckets + pos; + const struct hash_entry se = { + .key = key, + .value = NULL + }; + const struct hash_entry *entry; + + if (!bucket->entries) + return NULL; + + entry = bsearch(&se, bucket->entries, bucket->used, + sizeof(struct hash_entry), hash_entry_cmp); + + return entry ? (void *)entry->value : NULL; +} + +int hash_del(struct hash *hash, const char *key) +{ + unsigned int keylen = strlen(key); + unsigned int hashval = hash_superfast(key, keylen); + unsigned int pos = hashval & (hash->n_buckets - 1); + unsigned int steps_used, steps_total; + struct hash_bucket *bucket = hash->buckets + pos; + struct hash_entry *entry, *entry_end; + const struct hash_entry se = { + .key = key, + .value = NULL + }; + + entry = bsearch(&se, bucket->entries, bucket->used, + sizeof(struct hash_entry), hash_entry_cmp); + if (entry == NULL) + return -ENOENT; + + if (hash->free_value) + hash->free_value((void *)entry->value); + + entry_end = bucket->entries + bucket->used; + memmove(entry, entry + 1, + (entry_end - entry) * sizeof(struct hash_entry)); + + bucket->used--; + hash->count--; + + steps_used = bucket->used / hash->step; + steps_total = bucket->total / hash->step; + if (steps_used + 1 < steps_total) { + size_t size = (steps_used + 1) * + hash->step * sizeof(struct hash_entry); + struct hash_entry *tmp = realloc(bucket->entries, size); + if (tmp) { + bucket->entries = tmp; + bucket->total = (steps_used + 1) * hash->step; + } + } + + return 0; +} + +unsigned int hash_get_count(const struct hash *hash) +{ + return hash->count; +} + +void hash_iter_init(const struct hash *hash, struct hash_iter *iter) +{ + iter->hash = hash; + iter->bucket = 0; + iter->entry = -1; +} + +bool hash_iter_next(struct hash_iter *iter, const char **key, + const void **value) +{ + const struct hash_bucket *b = iter->hash->buckets + iter->bucket; + const struct hash_entry *e; + + iter->entry++; + + if (iter->entry >= b->used) { + iter->entry = 0; + + for (iter->bucket++; iter->bucket < iter->hash->n_buckets; + iter->bucket++) { + b = iter->hash->buckets + iter->bucket; + + if (b->used > 0) + break; + } + + if (iter->bucket >= iter->hash->n_buckets) + return false; + } + + e = b->entries + iter->entry; + + if (value != NULL) + *value = e->value; + if (key != NULL) + *key = e->key; + + return true; +} diff --git a/shared/hash.h b/shared/hash.h new file mode 100644 index 0000000..ca0af05 --- /dev/null +++ b/shared/hash.h @@ -0,0 +1,22 @@ +#pragma once + +#include <stdbool.h> + +struct hash; + +struct hash_iter { + const struct hash *hash; + unsigned int bucket; + unsigned int entry; +}; + +struct hash *hash_new(unsigned int n_buckets, void (*free_value)(void *value)); +void hash_free(struct hash *hash); +int hash_add(struct hash *hash, const char *key, const void *value); +int hash_add_unique(struct hash *hash, const char *key, const void *value); +int hash_del(struct hash *hash, const char *key); +void *hash_find(const struct hash *hash, const char *key); +unsigned int hash_get_count(const struct hash *hash); +void hash_iter_init(const struct hash *hash, struct hash_iter *iter); +bool hash_iter_next(struct hash_iter *iter, const char **key, + const void **value); diff --git a/shared/macro.h b/shared/macro.h new file mode 100644 index 0000000..b59f7dc --- /dev/null +++ b/shared/macro.h @@ -0,0 +1,76 @@ +/* + * kmod - interface to kernel module operations + * + * Copyright (C) 2011-2013 ProFUSION embedded systems + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#pragma once + +#include <stddef.h> + +#if defined(HAVE_STATIC_ASSERT) +#define assert_cc(expr) \ + _Static_assert((expr), #expr) +#else +#define assert_cc(expr) \ + do { (void) sizeof(char [1 - 2*!(expr)]); } while(0) +#endif + +#define check_types_match(expr1, expr2) \ + ((typeof(expr1) *)0 != (typeof(expr2) *)0) + +#define container_of(member_ptr, containing_type, member) \ + ((containing_type *) \ + ((char *)(member_ptr) - offsetof(containing_type, member)) \ + - check_types_match(*(member_ptr), ((containing_type *)0)->member)) + + +/* Two gcc extensions. + * &a[0] degrades to a pointer: a different type from an array */ +#define _array_size_chk(arr) ({ \ + assert_cc(!__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0]))); \ + 0; \ + }) + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + _array_size_chk(arr)) + +#define XSTRINGIFY(x) #x +#define STRINGIFY(x) XSTRINGIFY(x) + +#define XCONCATENATE(x, y) x ## y +#define CONCATENATE(x, y) XCONCATENATE(x, y) +#define UNIQ(x) CONCATENATE(x, __COUNTER__) + +/* Temporaries for importing index handling */ +#define NOFAIL(x) (x) +#define fatal(x...) do { } while (0) + +/* Attributes */ + +#define _must_check_ __attribute__((warn_unused_result)) +#define _printf_format_(a,b) __attribute__((format (printf, a, b))) +#define _unused_ __attribute__((unused)) +#define _always_inline_ __inline__ __attribute__((always_inline)) +#define _cleanup_(x) __attribute__((cleanup(x))) + +/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc + * compiler versions */ +#ifndef noreturn +#if defined(HAVE_NORETURN) +#define noreturn _Noreturn +#else +#define noreturn __attribute__((noreturn)) +#endif +#endif diff --git a/shared/missing.h b/shared/missing.h new file mode 100644 index 0000000..2629444 --- /dev/null +++ b/shared/missing.h @@ -0,0 +1,59 @@ +#pragma once + +#include <unistd.h> +#include <sys/syscall.h> + +#ifdef HAVE_LINUX_MODULE_H +#include <linux/module.h> +#endif + +#ifndef MODULE_INIT_IGNORE_MODVERSIONS +# define MODULE_INIT_IGNORE_MODVERSIONS 1 +#endif + +#ifndef MODULE_INIT_IGNORE_VERMAGIC +# define MODULE_INIT_IGNORE_VERMAGIC 2 +#endif + +#ifndef MODULE_INIT_COMPRESSED_FILE +# define MODULE_INIT_COMPRESSED_FILE 4 +#endif + +#ifndef __NR_finit_module +# define __NR_finit_module -1 +#endif + +#ifndef HAVE_FINIT_MODULE +#include <errno.h> + +static inline int finit_module(int fd, const char *uargs, int flags) +{ + if (__NR_finit_module == -1) { + errno = ENOSYS; + return -1; + } + + return syscall(__NR_finit_module, fd, uargs, flags); +} +#endif + +#if !HAVE_DECL_STRNDUPA +#define strndupa(s, n) \ + ({ \ + const char *__old = (s); \ + size_t __len = strnlen(__old, (n)); \ + char *__new = alloca(__len + 1); \ + __new[__len] = '\0'; \ + memcpy(__new, __old, __len); \ + }) +#endif + +#if !HAVE_DECL_BE32TOH +#include <endian.h> +#include <byteswap.h> +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define be32toh(x) bswap_32 (x) +#else +#define be32toh(x) (x) +#endif +#endif diff --git a/shared/scratchbuf.c b/shared/scratchbuf.c new file mode 100644 index 0000000..8d9eb83 --- /dev/null +++ b/shared/scratchbuf.c @@ -0,0 +1,60 @@ +/* + * kmod - interface to kernel module operations + * + * Copyright (C) 2016 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "scratchbuf.h" + +#include <errno.h> +#include <string.h> + +void scratchbuf_init(struct scratchbuf *buf, char *stackbuf, size_t size) +{ + buf->bytes = stackbuf; + buf->size = size; + buf->need_free = false; +} + +int scratchbuf_alloc(struct scratchbuf *buf, size_t size) +{ + char *tmp; + + if (size <= buf->size) + return 0; + + if (buf->need_free) { + tmp = realloc(buf->bytes, size); + if (tmp == NULL) + return -ENOMEM; + } else { + tmp = malloc(size); + if (tmp == NULL) + return -ENOMEM; + memcpy(tmp, buf->bytes, buf->size); + } + + buf->size = size; + buf->bytes = tmp; + buf->need_free = true; + + return 0; +} + +void scratchbuf_release(struct scratchbuf *buf) +{ + if (buf->need_free) + free(buf->bytes); +} diff --git a/shared/scratchbuf.h b/shared/scratchbuf.h new file mode 100644 index 0000000..27ea9d9 --- /dev/null +++ b/shared/scratchbuf.h @@ -0,0 +1,31 @@ +#pragma once + +#include <stdbool.h> +#include <stdlib.h> + +#include <shared/macro.h> + +/* + * Buffer abstract data type + */ +struct scratchbuf { + char *bytes; + size_t size; + bool need_free; +}; + +void scratchbuf_init(struct scratchbuf *buf, char *stackbuf, size_t size); +int scratchbuf_alloc(struct scratchbuf *buf, size_t sz); +void scratchbuf_release(struct scratchbuf *buf); + +/* Return a C string */ +static inline char *scratchbuf_str(struct scratchbuf *buf) +{ + return buf->bytes; +} + +#define SCRATCHBUF_INITIALIZER(buf_) { \ + .bytes = buf_, \ + .size = sizeof(buf_) + _array_size_chk(buf_), \ + .need_free = false, \ +} diff --git a/shared/strbuf.c b/shared/strbuf.c new file mode 100644 index 0000000..a69d914 --- /dev/null +++ b/shared/strbuf.c @@ -0,0 +1,128 @@ +/* + * libkmod - interface to kernel module operations + * + * Copyright (C) 2011-2013 ProFUSION embedded systems + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "strbuf.h" + +#define BUF_STEP 128 + +static bool buf_grow(struct strbuf *buf, size_t newsize) +{ + void *tmp; + size_t sz; + + if (newsize <= buf->size) + return true; + + if (newsize % BUF_STEP == 0) + sz = newsize; + else + sz = ((newsize / BUF_STEP) + 1) * BUF_STEP; + + tmp = realloc(buf->bytes, sz); + if (sz > 0 && tmp == NULL) + return false; + buf->bytes = tmp; + buf->size = sz; + return true; +} + +void strbuf_init(struct strbuf *buf) +{ + buf->bytes = NULL; + buf->size = 0; + buf->used = 0; +} + +void strbuf_release(struct strbuf *buf) +{ + free(buf->bytes); +} + +char *strbuf_steal(struct strbuf *buf) +{ + char *bytes; + + bytes = realloc(buf->bytes, buf->used + 1); + if (!bytes) { + free(buf->bytes); + return NULL; + } + bytes[buf->used] = '\0'; + return bytes; +} + +const char *strbuf_str(struct strbuf *buf) +{ + if (!buf_grow(buf, buf->used + 1)) + return NULL; + buf->bytes[buf->used] = '\0'; + return buf->bytes; +} + +bool strbuf_pushchar(struct strbuf *buf, char ch) +{ + if (!buf_grow(buf, buf->used + 1)) + return false; + buf->bytes[buf->used] = ch; + buf->used++; + return true; +} + +unsigned strbuf_pushchars(struct strbuf *buf, const char *str) +{ + unsigned int len; + + assert(str != NULL); + assert(buf != NULL); + + len = strlen(str); + + if (!buf_grow(buf, buf->used + len)) + return 0; + + memcpy(buf->bytes + buf->used, str, len); + buf->used += len; + + return len; +} + +void strbuf_popchar(struct strbuf *buf) +{ + assert(buf->used > 0); + buf->used--; +} + +void strbuf_popchars(struct strbuf *buf, unsigned n) +{ + assert(buf->used >= n); + buf->used -= n; +} + +void strbuf_clear(struct strbuf *buf) +{ + buf->used = 0; +} + diff --git a/shared/strbuf.h b/shared/strbuf.h new file mode 100644 index 0000000..0f7ceb1 --- /dev/null +++ b/shared/strbuf.h @@ -0,0 +1,30 @@ +#pragma once + +#include <stdbool.h> + +/* + * Buffer abstract data type + */ +struct strbuf { + char *bytes; + unsigned size; + unsigned used; +}; + +void strbuf_init(struct strbuf *buf); +void strbuf_release(struct strbuf *buf); +void strbuf_clear(struct strbuf *buf); + +/* Destroy buffer and return a copy as a C string */ +char *strbuf_steal(struct strbuf *buf); + +/* + * Return a C string owned by the buffer invalidated if the buffer is + * changed). + */ +const char *strbuf_str(struct strbuf *buf); + +bool strbuf_pushchar(struct strbuf *buf, char ch); +unsigned strbuf_pushchars(struct strbuf *buf, const char *str); +void strbuf_popchar(struct strbuf *buf); +void strbuf_popchars(struct strbuf *buf, unsigned n); diff --git a/shared/util.c b/shared/util.c new file mode 100644 index 0000000..e2bab83 --- /dev/null +++ b/shared/util.c @@ -0,0 +1,548 @@ +/* + * kmod - interface to kernel module operations + * + * Copyright (C) 2011-2013 ProFUSION embedded systems + * Copyright (C) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com> + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <shared/missing.h> +#include <shared/util.h> + +#define USEC_PER_SEC 1000000ULL +#define NSEC_PER_USEC 1000ULL + +static const struct kmod_ext { + const char *ext; + size_t len; +} kmod_exts[] = { + {KMOD_EXTENSION_UNCOMPRESSED, sizeof(KMOD_EXTENSION_UNCOMPRESSED) - 1}, +#ifdef ENABLE_ZLIB + {".ko.gz", sizeof(".ko.gz") - 1}, +#endif +#ifdef ENABLE_XZ + {".ko.xz", sizeof(".ko.xz") - 1}, +#endif +#ifdef ENABLE_ZSTD + {".ko.zst", sizeof(".ko.zst") - 1}, +#endif + { } +}; + +/* string handling functions and memory allocations */ +/* ************************************************************************ */ + +void *memdup(const void *p, size_t n) +{ + void *r = malloc(n); + + if (r == NULL) + return NULL; + + return memcpy(r, p, n); +} + +char *strchr_replace(char *s, char c, char r) +{ + char *p; + + for (p = s; *p != '\0'; p++) { + if (*p == c) + *p = r; + } + + return s; +} + +/* module-related functions */ +/* ************************************************************************ */ +int alias_normalize(const char *alias, char buf[static PATH_MAX], size_t *len) +{ + size_t i; + + for (i = 0; i < PATH_MAX - 1; i++) { + const char c = alias[i]; + switch (c) { + case '-': + buf[i] = '_'; + break; + case ']': + return -EINVAL; + case '[': + while (alias[i] != ']' && alias[i] != '\0') { + buf[i] = alias[i]; + i++; + } + + if (alias[i] != ']') + return -EINVAL; + + buf[i] = alias[i]; + break; + case '\0': + goto finish; + default: + buf[i] = c; + } + } + +finish: + buf[i] = '\0'; + if (len) + *len = i; + + return 0; +} + +/* + * Replace dashes with underscores. + * Dashes inside character range patterns (e.g. [0-9]) are left unchanged. + * + * For convenience, it returns error if @s is NULL + */ +int underscores(char *s) +{ + unsigned int i; + + if (!s) + return -EINVAL; + + for (i = 0; s[i]; i++) { + switch (s[i]) { + case '-': + s[i] = '_'; + break; + case ']': + return -EINVAL; + case '[': + i += strcspn(&s[i], "]"); + if (!s[i]) + return -EINVAL; + break; + } + } + + return 0; +} + +char *modname_normalize(const char *modname, char buf[static PATH_MAX], size_t *len) +{ + size_t s; + + for (s = 0; s < PATH_MAX - 1; s++) { + const char c = modname[s]; + if (c == '-') + buf[s] = '_'; + else if (c == '\0' || c == '.') + break; + else + buf[s] = c; + } + + buf[s] = '\0'; + + if (len) + *len = s; + + return buf; +} + +char *path_to_modname(const char *path, char buf[static PATH_MAX], size_t *len) +{ + char *modname; + + modname = basename(path); + if (modname == NULL || modname[0] == '\0') + return NULL; + + return modname_normalize(modname, buf, len); +} + +bool path_ends_with_kmod_ext(const char *path, size_t len) +{ + const struct kmod_ext *eitr; + + for (eitr = kmod_exts; eitr->ext != NULL; eitr++) { + if (len <= eitr->len) + continue; + if (streq(path + len - eitr->len, eitr->ext)) + return true; + } + + return false; +} + +/* read-like and fread-like functions */ +/* ************************************************************************ */ +ssize_t read_str_safe(int fd, char *buf, size_t buflen) +{ + size_t todo = buflen - 1; + size_t done = 0; + + assert_cc(EAGAIN == EWOULDBLOCK); + + do { + ssize_t r = read(fd, buf + done, todo); + + if (r == 0) + break; + else if (r > 0) { + todo -= r; + done += r; + } else { + if (errno == EAGAIN || errno == EINTR) + continue; + else + return -errno; + } + } while (todo > 0); + + buf[done] = '\0'; + return done; +} + +ssize_t write_str_safe(int fd, const char *buf, size_t buflen) +{ + size_t todo = buflen; + size_t done = 0; + + assert_cc(EAGAIN == EWOULDBLOCK); + + do { + ssize_t r = write(fd, buf + done, todo); + + if (r == 0) + break; + else if (r > 0) { + todo -= r; + done += r; + } else { + if (errno == EAGAIN || errno == EINTR) + continue; + else + return -errno; + } + } while (todo > 0); + + return done; +} + +int read_str_long(int fd, long *value, int base) +{ + char buf[32], *end; + long v; + int err; + + *value = 0; + err = read_str_safe(fd, buf, sizeof(buf)); + if (err < 0) + return err; + errno = 0; + v = strtol(buf, &end, base); + if (end == buf || !isspace(*end)) + return -EINVAL; + + *value = v; + return 0; +} + +int read_str_ulong(int fd, unsigned long *value, int base) +{ + char buf[32], *end; + long v; + int err; + + *value = 0; + err = read_str_safe(fd, buf, sizeof(buf)); + if (err < 0) + return err; + errno = 0; + v = strtoul(buf, &end, base); + if (end == buf || !isspace(*end)) + return -EINVAL; + *value = v; + return 0; +} + +/* + * Read one logical line from a configuration file. + * + * Line endings may be escaped with backslashes, to form one logical line from + * several physical lines. No end of line character(s) are included in the + * result. + * + * If linenum is not NULL, it is incremented by the number of physical lines + * which have been read. + */ +char *freadline_wrapped(FILE *fp, unsigned int *linenum) +{ + int size = 256; + int i = 0, n = 0; + _cleanup_free_ char *buf = malloc(size); + + if (buf == NULL) + return NULL; + + for(;;) { + int ch = getc_unlocked(fp); + + switch(ch) { + case EOF: + if (i == 0) + return NULL; + /* else fall through */ + + case '\n': + n++; + + { + char *ret = buf; + ret[i] = '\0'; + buf = NULL; + if (linenum) + *linenum += n; + return ret; + } + + case '\\': + ch = getc_unlocked(fp); + + if (ch == '\n') { + n++; + continue; + } + /* else fall through */ + + default: + buf[i++] = ch; + + if (i == size) { + char *tmp; + size *= 2; + tmp = realloc(buf, size); + if (!tmp) + return NULL; + buf = tmp; + } + } + } +} + +/* path handling functions */ +/* ************************************************************************ */ + +static bool path_is_absolute(const char *p) +{ + assert(p != NULL); + + return p[0] == '/'; +} + +char *path_make_absolute_cwd(const char *p) +{ + _cleanup_free_ char *cwd = NULL; + size_t plen, cwdlen; + char *r; + + if (path_is_absolute(p)) + return strdup(p); + + cwd = get_current_dir_name(); + if (!cwd) + return NULL; + + plen = strlen(p); + cwdlen = strlen(cwd); + + /* cwd + '/' + p + '\0' */ + r = realloc(cwd, cwdlen + 1 + plen + 1); + if (r == NULL) + return NULL; + + cwd = NULL; + r[cwdlen] = '/'; + memcpy(&r[cwdlen + 1], p, plen + 1); + + return r; +} + +static inline int is_dir(const char *path) +{ + struct stat st; + + if (stat(path, &st) >= 0) + return S_ISDIR(st.st_mode); + + return -errno; +} + +int mkdir_p(const char *path, int len, mode_t mode) +{ + char *start, *end; + + start = strndupa(path, len); + end = start + len; + + /* + * scan backwards, replacing '/' with '\0' while the component doesn't + * exist + */ + for (;;) { + int r = is_dir(start); + if (r > 0) { + end += strlen(end); + + if (end == start + len) + return 0; + + /* end != start, since it would be caught on the first + * iteration */ + *end = '/'; + break; + } else if (r == 0) + return -ENOTDIR; + + if (end == start) + break; + + *end = '\0'; + + /* Find the next component, backwards, discarding extra '/'*/ + while (end > start && *end != '/') + end--; + + while (end > start && *(end - 1) == '/') + end--; + } + + for (; end < start + len;) { + if (mkdir(start, mode) < 0 && errno != EEXIST) + return -errno; + + end += strlen(end); + *end = '/'; + } + + return 0; +} + +int mkdir_parents(const char *path, mode_t mode) +{ + char *end = strrchr(path, '/'); + + /* no parent directories */ + if (end == NULL) + return 0; + + return mkdir_p(path, end - path, mode); +} + +static unsigned long long ts_usec(const struct timespec *ts) +{ + return (unsigned long long) ts->tv_sec * USEC_PER_SEC + + (unsigned long long) ts->tv_nsec / NSEC_PER_USEC; +} + +static unsigned long long ts_msec(const struct timespec *ts) +{ + return (unsigned long long) ts->tv_sec * MSEC_PER_SEC + + (unsigned long long) ts->tv_nsec / NSEC_PER_MSEC; +} + +static struct timespec msec_ts(unsigned long long msec) +{ + struct timespec ts = { + .tv_sec = msec / MSEC_PER_SEC, + .tv_nsec = (msec % MSEC_PER_SEC) * NSEC_PER_MSEC, + }; + + return ts; +} + +int sleep_until_msec(unsigned long long msec) +{ + struct timespec ts = msec_ts(msec); + + if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) < 0 && + errno != EINTR) + return -errno; + + return 0; +} + +/* + * Exponential retry backoff with tail + */ +unsigned long long get_backoff_delta_msec(unsigned long long t0, + unsigned long long tend, + unsigned long long *delta) +{ + unsigned long long t; + + t = now_msec(); + + if (!*delta) + *delta = 1; + else + *delta <<= 1; + + while (t + *delta > tend) + *delta >>= 1; + + if (!*delta && tend > t) + *delta = tend - t; + + return t + *delta; +} + +unsigned long long now_usec(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + return 0; + + return ts_usec(&ts); +} + +unsigned long long now_msec(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + return 0; + + return ts_msec(&ts); +} + +unsigned long long stat_mstamp(const struct stat *st) +{ +#ifdef HAVE_STRUCT_STAT_ST_MTIM + return ts_usec(&st->st_mtim); +#else + return (unsigned long long) st->st_mtime; +#endif +} diff --git a/shared/util.h b/shared/util.h new file mode 100644 index 0000000..c4a3916 --- /dev/null +++ b/shared/util.h @@ -0,0 +1,104 @@ +#pragma once + +#include <inttypes.h> +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> + +#include <shared/macro.h> + +/* string handling functions and memory allocations */ +/* ************************************************************************ */ +#define streq(a, b) (strcmp((a), (b)) == 0) +#define strstartswith(a, b) (strncmp(a, b, strlen(b)) == 0) +char *strchr_replace(char *s, char c, char r); +void *memdup(const void *p, size_t n) __attribute__((nonnull(1))); + +/* module-related functions */ +/* ************************************************************************ */ +#define KMOD_EXTENSION_UNCOMPRESSED ".ko" + +int alias_normalize(const char *alias, char buf[static PATH_MAX], size_t *len) _must_check_ __attribute__((nonnull(1,2))); +int underscores(char *s) _must_check_; +char *modname_normalize(const char *modname, char buf[static PATH_MAX], size_t *len) __attribute__((nonnull(1, 2))); +char *path_to_modname(const char *path, char buf[static PATH_MAX], size_t *len) __attribute__((nonnull(2))); +bool path_ends_with_kmod_ext(const char *path, size_t len) __attribute__((nonnull(1))); + +/* read-like and fread-like functions */ +/* ************************************************************************ */ +ssize_t read_str_safe(int fd, char *buf, size_t buflen) _must_check_ __attribute__((nonnull(2))); +ssize_t write_str_safe(int fd, const char *buf, size_t buflen) __attribute__((nonnull(2))); +int read_str_long(int fd, long *value, int base) _must_check_ __attribute__((nonnull(2))); +int read_str_ulong(int fd, unsigned long *value, int base) _must_check_ __attribute__((nonnull(2))); +char *freadline_wrapped(FILE *fp, unsigned int *linenum) __attribute__((nonnull(1))); + +/* path handling functions */ +/* ************************************************************************ */ +char *path_make_absolute_cwd(const char *p) _must_check_ __attribute__((nonnull(1))); +int mkdir_p(const char *path, int len, mode_t mode); +int mkdir_parents(const char *path, mode_t mode); +unsigned long long stat_mstamp(const struct stat *st); + +/* time-related functions + * ************************************************************************ */ +#define USEC_PER_SEC 1000000ULL +#define USEC_PER_MSEC 1000ULL +#define MSEC_PER_SEC 1000ULL +#define NSEC_PER_MSEC 1000000ULL + +unsigned long long now_usec(void); +unsigned long long now_msec(void); +int sleep_until_msec(unsigned long long msec); +unsigned long long get_backoff_delta_msec(unsigned long long t0, + unsigned long long tend, + unsigned long long *delta); + + +/* endianess and alignments */ +/* ************************************************************************ */ +#define get_unaligned(ptr) \ +({ \ + struct __attribute__((packed)) { \ + typeof(*(ptr)) __v; \ + } *__p = (typeof(__p)) (ptr); \ + __p->__v; \ +}) + +#define put_unaligned(val, ptr) \ +do { \ + struct __attribute__((packed)) { \ + typeof(*(ptr)) __v; \ + } *__p = (typeof(__p)) (ptr); \ + __p->__v = (val); \ +} while(0) + +static _always_inline_ unsigned int ALIGN_POWER2(unsigned int u) +{ + return 1 << ((sizeof(u) * 8) - __builtin_clz(u - 1)); +} + +/* misc */ +/* ************************************************************************ */ +static inline void freep(void *p) { + free(*(void**) p); +} +#define _cleanup_free_ _cleanup_(freep) + +static inline bool addu64_overflow(uint64_t a, uint64_t b, uint64_t *res) +{ +#if (HAVE___BUILTIN_UADDL_OVERFLOW && HAVE___BUILTIN_UADDLL_OVERFLOW) +#if __SIZEOF_LONG__ == 8 + return __builtin_uaddl_overflow(a, b, res); +#elif __SIZEOF_LONG_LONG__ == 8 + return __builtin_uaddll_overflow(a, b, res); +#else +#error "sizeof(long long) != 8" +#endif +#endif + *res = a + b; + return UINT64_MAX - a < b; +} |