summaryrefslogtreecommitdiffstats
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/.gitignore5
-rw-r--r--shared/array.c113
-rw-r--r--shared/array.h22
-rw-r--r--shared/hash.c341
-rw-r--r--shared/hash.h22
-rw-r--r--shared/macro.h76
-rw-r--r--shared/missing.h59
-rw-r--r--shared/scratchbuf.c60
-rw-r--r--shared/scratchbuf.h31
-rw-r--r--shared/strbuf.c128
-rw-r--r--shared/strbuf.h30
-rw-r--r--shared/util.c548
-rw-r--r--shared/util.h104
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;
+}