mirror of
https://github.com/pumpitupdev/pumptools.git
synced 2026-09-30 18:57:59 +03:00
Version 1.08 with public source code release.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
#include "util/adler32.h"
|
||||
|
||||
#define BASE 65521
|
||||
|
||||
uint32_t util_adler32_calc(uint32_t initval, const uint8_t* input, size_t length)
|
||||
{
|
||||
unsigned int s1 = initval & 0xffff;
|
||||
unsigned int s2 = (initval >> 16) & 0xffff;
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; n < length; n++) {
|
||||
s1 = (s1 + input[n]) % BASE;
|
||||
s2 = (s2 + s1) % BASE;
|
||||
}
|
||||
|
||||
return (s2 << 16) + s1;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef UTIL_ADLER32_H
|
||||
#define UTIL_ADLER32_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Calculate the adler32 checksum
|
||||
*
|
||||
* @param initval Initial value for calculation
|
||||
* @param input Input buffer to checksum
|
||||
* @param length Length of the input buffer
|
||||
* @return Adler32 value of input buffer
|
||||
*/
|
||||
uint32_t util_adler32_calc(uint32_t initval, const uint8_t* input, size_t length);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
void util_array_init(struct util_array* array)
|
||||
{
|
||||
memset(array, 0, sizeof(*array));
|
||||
}
|
||||
|
||||
void util_array_remove_(size_t itemsz, struct util_array* array, size_t i)
|
||||
{
|
||||
array->nitems--;
|
||||
|
||||
memmove(((uint8_t *) array->items) + i * itemsz,
|
||||
((uint8_t *) array->items) + (i + 1) * itemsz,
|
||||
(array->nitems - i) * itemsz);
|
||||
}
|
||||
|
||||
void* util_array_reserve_(size_t itemsz, struct util_array* array, size_t nitems)
|
||||
{
|
||||
size_t new_nalloced;
|
||||
size_t new_nitems;
|
||||
void* result;
|
||||
|
||||
new_nitems = array->nitems + nitems;
|
||||
|
||||
if (new_nitems > array->nalloced) {
|
||||
new_nalloced = array->nalloced;
|
||||
|
||||
if (new_nalloced == 0) {
|
||||
new_nalloced = 1;
|
||||
}
|
||||
|
||||
while (new_nalloced < new_nitems) {
|
||||
new_nalloced *= 2;
|
||||
}
|
||||
|
||||
array->items = util_xrealloc(array->items, new_nalloced * itemsz);
|
||||
array->nalloced = new_nalloced;
|
||||
}
|
||||
|
||||
result = ((uint8_t *) array->items) + array->nitems * itemsz;
|
||||
array->nitems = new_nitems;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void util_array_fini(struct util_array* array)
|
||||
{
|
||||
free(array->items);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef UTIL_ARRAY_H
|
||||
#define UTIL_ARRAY_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Array with a fixed number of items
|
||||
*/
|
||||
struct util_array {
|
||||
void* items;
|
||||
size_t nitems;
|
||||
size_t nalloced;
|
||||
};
|
||||
|
||||
#define util_array_item(type, array, i) \
|
||||
(&(((type*) (array)->items)[i]))
|
||||
|
||||
#define util_array_reserve(type, array, nitems) \
|
||||
((type*) util_array_reserve_(sizeof(type), array, nitems))
|
||||
|
||||
#define util_array_remove(type, array, i) \
|
||||
util_array_remove_(sizeof(type), array, i)
|
||||
|
||||
#define util_array_append(type, array) \
|
||||
util_array_reserve(type, array, 1)
|
||||
|
||||
/**
|
||||
* Initialize the array after allocation
|
||||
*
|
||||
* @param array Pointer to allocated array to initialize
|
||||
*/
|
||||
void util_array_init(struct util_array* array);
|
||||
|
||||
/**
|
||||
* Reserve/allocate space for the specified number of items in the array
|
||||
*
|
||||
* @param itemsz Size of a single item
|
||||
* @param array Array to use
|
||||
* @param Number of items with itemsz each to reserve
|
||||
* @return Pointer to first index of reserved slots in array
|
||||
*/
|
||||
void* util_array_reserve_(size_t itemsz, struct util_array* array, size_t nitems);
|
||||
|
||||
/**
|
||||
* Remove an item from the array. The array will be shrunken to current_size - 1
|
||||
*
|
||||
* @param itemsz Size of a single item
|
||||
* @param array Array to remove the item from
|
||||
* @param i Index of the item to remove
|
||||
*/
|
||||
void util_array_remove_(size_t itemsz, struct util_array* array, size_t i);
|
||||
|
||||
/**
|
||||
* Free/Deallocate an allocated array
|
||||
*
|
||||
* @param array Allocated array to free
|
||||
*/
|
||||
void util_array_fini(struct util_array* array);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,170 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include "util/base64.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
// Copy-pasted with minor tweaks from: https://github.com/littlstar/b64.c
|
||||
|
||||
static const char b64_table[] = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
uint8_t* util_base64_encode(const uint8_t* src, size_t len, size_t* out_len)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char *enc = NULL;
|
||||
size_t size = 0;
|
||||
unsigned char buf[4];
|
||||
unsigned char tmp[3];
|
||||
|
||||
enc = (unsigned char *) util_xmalloc(0);
|
||||
|
||||
while (len--) {
|
||||
tmp[i++] = *(src++);
|
||||
|
||||
if (3 == i) {
|
||||
buf[0] = (tmp[0] & 0xfc) >> 2;
|
||||
buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4);
|
||||
buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6);
|
||||
buf[3] = tmp[2] & 0x3f;
|
||||
|
||||
enc = (unsigned char *) util_xrealloc(enc, size + 4);
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
enc[size++] = b64_table[buf[i]];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
for (j = i; j < 3; ++j) {
|
||||
tmp[j] = '\0';
|
||||
}
|
||||
|
||||
buf[0] = (tmp[0] & 0xfc) >> 2;
|
||||
buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4);
|
||||
buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6);
|
||||
buf[3] = tmp[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); ++j) {
|
||||
enc = (unsigned char *) util_xrealloc(enc, size + 1);
|
||||
enc[size++] = b64_table[buf[j]];
|
||||
}
|
||||
|
||||
while ((i++ < 3)) {
|
||||
enc = (unsigned char *) util_xrealloc(enc, size + 1);
|
||||
enc[size++] = '=';
|
||||
}
|
||||
}
|
||||
|
||||
enc = (unsigned char *) util_xrealloc(enc, size + 1);
|
||||
enc[size] = '\0';
|
||||
|
||||
*out_len = size;
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
uint8_t* util_base64_decode(const uint8_t* src, size_t len, size_t* out_len)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int l = 0;
|
||||
size_t size = 0;
|
||||
unsigned char *dec = NULL;
|
||||
unsigned char buf[3];
|
||||
unsigned char tmp[4];
|
||||
|
||||
dec = (unsigned char *) util_xmalloc(0);
|
||||
|
||||
while (len--) {
|
||||
if ('=' == src[j]) {
|
||||
break;
|
||||
}
|
||||
if (!(isalnum(src[j]) || '+' == src[j] || '/' == src[j])) {
|
||||
break;
|
||||
}
|
||||
|
||||
tmp[i++] = src[j++];
|
||||
|
||||
if (4 == i) {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (l = 0; l < 64; ++l) {
|
||||
if (tmp[i] == b64_table[l]) {
|
||||
tmp[i] = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] = (tmp[0] << 2) + ((tmp[1] & 0x30) >> 4);
|
||||
buf[1] = ((tmp[1] & 0xf) << 4) + ((tmp[2] & 0x3c) >> 2);
|
||||
buf[2] = ((tmp[2] & 0x3) << 6) + tmp[3];
|
||||
|
||||
dec = (unsigned char *) util_xrealloc(dec, size + 3);
|
||||
|
||||
if (dec != NULL){
|
||||
for (i = 0; i < 3; ++i) {
|
||||
dec[size++] = buf[i];
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
for (j = i; j < 4; ++j) {
|
||||
tmp[j] = '\0';
|
||||
}
|
||||
|
||||
for (j = 0; j < 4; ++j) {
|
||||
for (l = 0; l < 64; ++l) {
|
||||
if (tmp[j] == b64_table[l]) {
|
||||
tmp[j] = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] = (tmp[0] << 2) + ((tmp[1] & 0x30) >> 4);
|
||||
buf[1] = ((tmp[1] & 0xf) << 4) + ((tmp[2] & 0x3c) >> 2);
|
||||
buf[2] = ((tmp[2] & 0x3) << 6) + tmp[3];
|
||||
|
||||
dec = (unsigned char *)util_xrealloc(dec, size + (i - 1));
|
||||
|
||||
if (dec != NULL){
|
||||
for (j = 0; (j < i - 1); ++j) {
|
||||
dec[size++] = buf[j];
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dec = (unsigned char *)util_xrealloc(dec, size + 1);
|
||||
|
||||
if (dec != NULL){
|
||||
dec[size] = '\0';
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (out_len != NULL) {
|
||||
*out_len = size;
|
||||
}
|
||||
|
||||
return dec;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint8_t* util_base64_encode(const uint8_t* src, size_t len, size_t* out_len);
|
||||
|
||||
uint8_t* util_base64_decode(const uint8_t* src, size_t len, size_t* out_len);
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef UTIL_DEFS_H
|
||||
#define UTIL_DEFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
|
||||
#define STRINGIFY_(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_(x)
|
||||
#define XSTRINGIFY(arg) STRINGIFY(arg)
|
||||
|
||||
#define lengthof(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
#define containerof(ptr, outer_t, member) \
|
||||
((void *) (((uint8_t *) ptr) - offsetof(outer_t, member)))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
#define LOG_MODULE "util-fs"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
bool util_file_load(const char* filename, void** out_bytes, size_t *out_nbytes,
|
||||
bool text_mode)
|
||||
{
|
||||
FILE *f;
|
||||
void *bytes;
|
||||
size_t nbytes;
|
||||
int result;
|
||||
|
||||
log_assert(out_bytes != NULL);
|
||||
*out_bytes = NULL;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
|
||||
if (f == NULL) {
|
||||
log_warn("%s: Error opening file: %s", filename, strerror(errno));
|
||||
|
||||
goto open_fail;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
nbytes = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
/* Add for null terminator */
|
||||
if (text_mode) {
|
||||
nbytes++;
|
||||
}
|
||||
|
||||
bytes = malloc(nbytes);
|
||||
|
||||
if (bytes == NULL) {
|
||||
log_warn("%s: malloc(%u) failed", filename, (unsigned int) nbytes);
|
||||
|
||||
goto malloc_fail;
|
||||
}
|
||||
|
||||
if (text_mode) {
|
||||
result = fread(bytes, nbytes - 1, 1, f);
|
||||
} else {
|
||||
result = fread(bytes, nbytes, 1, f);
|
||||
}
|
||||
|
||||
if (result != 1) {
|
||||
log_warn("%s: Error reading file: %s", filename, strerror(errno));
|
||||
|
||||
goto read_fail;
|
||||
}
|
||||
|
||||
*out_bytes = bytes;
|
||||
|
||||
if (out_nbytes != NULL) {
|
||||
*out_nbytes = nbytes;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
/* Add null terminator */
|
||||
if (text_mode) {
|
||||
((char*) bytes)[nbytes - 1] = '\0';
|
||||
}
|
||||
|
||||
log_debug("File loaded %s, size %d", filename, *out_nbytes);
|
||||
|
||||
return true;
|
||||
|
||||
read_fail:
|
||||
free(bytes);
|
||||
|
||||
malloc_fail:
|
||||
fclose(f);
|
||||
|
||||
open_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool util_file_save(const char* filename, const void* bytes, size_t nbytes)
|
||||
{
|
||||
FILE *f;
|
||||
int result;
|
||||
|
||||
f = fopen(filename, "wb");
|
||||
|
||||
if (f == NULL) {
|
||||
log_warn("%s: Error creating file: %s", filename, strerror(errno));
|
||||
|
||||
goto open_fail;
|
||||
}
|
||||
|
||||
result = fwrite(bytes, nbytes, 1, f);
|
||||
|
||||
if (result != 1) {
|
||||
log_warn("%s: Error writing file: %s", filename, strerror(errno));
|
||||
|
||||
goto write_fail;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
log_debug("File saved %s, size %d", filename, nbytes);
|
||||
|
||||
return true;
|
||||
|
||||
write_fail:
|
||||
fclose(f);
|
||||
|
||||
open_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool util_fs_path_exists(const char* path)
|
||||
{
|
||||
// Don't use stat here because that doesn't work on mounts, e.g. sqashfs/appimage
|
||||
|
||||
bool exists;
|
||||
FILE* file = fopen(path, "r");
|
||||
|
||||
exists = file != NULL;
|
||||
|
||||
if (file != NULL) {
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool util_fs_mkdir(const char* path)
|
||||
{
|
||||
return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0;
|
||||
}
|
||||
|
||||
bool util_fs_mkfile(const char* path)
|
||||
{
|
||||
FILE* file;
|
||||
bool res;
|
||||
|
||||
file = fopen(path, "a");
|
||||
|
||||
res = file != NULL;
|
||||
|
||||
if (file) {
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
char* util_fs_get_path_to_file(const char* path)
|
||||
{
|
||||
size_t len;
|
||||
char* res;
|
||||
|
||||
len = strlen(path);
|
||||
|
||||
for (ssize_t i = len - 1; i >= 0; i--) {
|
||||
if (path[i] == '/') {
|
||||
len--;
|
||||
break;
|
||||
}
|
||||
|
||||
len--;
|
||||
}
|
||||
|
||||
res = (char*) util_xmalloc(sizeof(char) * (len + 1));
|
||||
strncpy(res, path, len);
|
||||
res[len] = '\0';
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
char* util_fs_get_filename(const char* path)
|
||||
{
|
||||
size_t path_len;
|
||||
size_t len;
|
||||
char* res;
|
||||
|
||||
len = strlen(path);
|
||||
path_len = len;
|
||||
|
||||
for (ssize_t i = len - 1; i >= 0; i--) {
|
||||
if (path[i] == '/') {
|
||||
len++;
|
||||
break;
|
||||
}
|
||||
|
||||
len--;
|
||||
}
|
||||
|
||||
res = (char*) util_xmalloc(sizeof(char) * ((path_len - len) + 1));
|
||||
strncpy(res, path + len, path_len - len);
|
||||
res[path_len - len] = '\0';
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
char* util_fs_get_abs_path(const char* path)
|
||||
{
|
||||
char* real_path;
|
||||
|
||||
real_path = realpath(path, NULL);
|
||||
|
||||
if (!real_path) {
|
||||
log_error("Resolving path '%s' to absolute path failed: %s", path,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return real_path;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef UTIL_FS_H
|
||||
#define UTIL_FS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* Load a file fully into memory
|
||||
*
|
||||
* @param filename Path of the file to load
|
||||
* @param bytes Pointer to a pointer to return an allocated location with the
|
||||
* loaded data to
|
||||
* @param nbytes Pointer to a variable to return the read size of the file to
|
||||
* @param text_mode True to read the file in text mode (add terminating \0),
|
||||
* false for binary mode
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
bool util_file_load(const char* filename, void** bytes, size_t* nbytes, bool text_mode);
|
||||
|
||||
/**
|
||||
* Save a buffer to a file (overwrite any existing files with the same name)
|
||||
*
|
||||
* @param filename Path to save the data to
|
||||
* @param bytes Pointer to buffer with contents to save
|
||||
* @param nbytes Number of bytes to write to the file
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
bool util_file_save(const char* filename, const void* bytes, size_t nbytes);
|
||||
|
||||
/**
|
||||
* Check if a normal file, folder, symlink, ... exists
|
||||
*
|
||||
* @return True if exists, false otherwise
|
||||
*/
|
||||
bool util_fs_path_exists(const char* path);
|
||||
|
||||
/**
|
||||
* Create a new directory
|
||||
*
|
||||
* @param path Path to directory
|
||||
* @return True if successful, false on failure
|
||||
*/
|
||||
bool util_fs_mkdir(const char* path);
|
||||
|
||||
/**
|
||||
* Create a new empty file
|
||||
*
|
||||
* @param path Path to the file to create
|
||||
* @return True if successful, false on failure
|
||||
*/
|
||||
bool util_fs_mkfile(const char* path);
|
||||
|
||||
/**
|
||||
* Get the path pointing to the file (without the filename)
|
||||
*
|
||||
* @param path Full path to file
|
||||
* @return Newly allocated string (caller has to free) containing the path, only
|
||||
*/
|
||||
char* util_fs_get_path_to_file(const char* path);
|
||||
|
||||
/**
|
||||
* Get the start position of the filename in the path
|
||||
*
|
||||
* @param path Full path to file
|
||||
* @return Newly allocated string (caller has to free) containing the filename,
|
||||
* only
|
||||
*/
|
||||
char* util_fs_get_filename(const char* path);
|
||||
|
||||
/**
|
||||
* Get the absolute path of the give (relative) path
|
||||
*
|
||||
* @param path (Relative) path
|
||||
* @return Allocated string (caller has to free) containing the full absolute
|
||||
* path for the given (relative) path
|
||||
*/
|
||||
char* util_fs_get_abs_path(const char* path);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#define LOG_MODULE "util-glibc"
|
||||
|
||||
#include <gnu/libc-version.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
void util_glibc_info_log()
|
||||
{
|
||||
log_info("glibc release '%s', version '%s'", gnu_get_libc_release(), gnu_get_libc_version());
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef PUMPTOOLS_UTIL_GLIBC_H
|
||||
#define PUMPTOOLS_UTIL_GLIBC_H
|
||||
|
||||
void util_glibc_info_log();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static bool util_hex_decode_nibble(char c, uint8_t *nibble)
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
*nibble = c - '0';
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
*nibble = c - 'A' + 10;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
*nibble = c - 'a' + 10;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool util_hex_decode(void *ptr, size_t nbytes, const char *chars, size_t nchars)
|
||||
{
|
||||
uint8_t *bytes = ptr;
|
||||
|
||||
uint32_t i;
|
||||
uint8_t hi;
|
||||
uint8_t lo;
|
||||
|
||||
log_assert(nchars >= 2 * nbytes);
|
||||
|
||||
for (i = 0 ; i < nbytes ; i++) {
|
||||
if ( !util_hex_decode_nibble(chars[2 * i + 0], &hi) ||
|
||||
!util_hex_decode_nibble(chars[2 * i + 1], &lo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes[i] = (hi << 4) | lo;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void util_hex_encode(const void *ptr, size_t nbytes, char *chars,
|
||||
size_t nchars, const char *digits)
|
||||
{
|
||||
const uint8_t *bytes = ptr;
|
||||
|
||||
size_t i;
|
||||
|
||||
log_assert(nchars >= 2 * nbytes + 1);
|
||||
|
||||
for (i = 0 ; i < nbytes ; i++) {
|
||||
chars[i * 2 + 0] = digits[bytes[i] >> 4];
|
||||
chars[i * 2 + 1] = digits[bytes[i] & 15];
|
||||
}
|
||||
|
||||
chars[nbytes * 2] = '\0';
|
||||
}
|
||||
|
||||
void util_hex_encode_lc(const void *bytes, size_t nbytes, char *chars,
|
||||
size_t nchars)
|
||||
{
|
||||
util_hex_encode(bytes, nbytes, chars, nchars, "0123456789abcdef");
|
||||
}
|
||||
|
||||
void util_hex_encode_uc(const void *bytes, size_t nbytes, char *chars,
|
||||
size_t nchars)
|
||||
{
|
||||
util_hex_encode(bytes, nbytes, chars, nchars, "0123456789ABCDEF");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef UTIL_HEX_H
|
||||
#define UTIL_HEX_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
bool util_hex_decode(void *bytes, size_t nbytes, const char *chars,
|
||||
size_t nchars);
|
||||
void util_hex_encode_uc(const void *bytes, size_t nbytes, char *chars,
|
||||
size_t nchars);
|
||||
void util_hex_encode_lc(const void *bytes, size_t nbytes, char *chars,
|
||||
size_t nchars);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "util/iobuf.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
size_t util_iobuf_move(struct util_iobuf* dest, struct util_const_iobuf* src)
|
||||
{
|
||||
size_t dest_avail;
|
||||
size_t src_avail;
|
||||
size_t chunksz;
|
||||
|
||||
dest_avail = dest->nbytes - dest->pos;
|
||||
src_avail = src->nbytes - src->pos;
|
||||
chunksz = dest_avail < src_avail ? dest_avail : src_avail;
|
||||
|
||||
memcpy(&dest->bytes[dest->pos], &src->bytes[src->pos], chunksz);
|
||||
|
||||
dest->pos += chunksz;
|
||||
src->pos += chunksz;
|
||||
|
||||
return chunksz;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef UTIL_IOBUF_H
|
||||
#define UTIL_IOBUF_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Iobuffer struct holding data, size and position
|
||||
*/
|
||||
struct util_iobuf {
|
||||
uint8_t* bytes;
|
||||
size_t nbytes;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Const variant of iobuffer struct holding data, size and position
|
||||
*/
|
||||
struct util_const_iobuf {
|
||||
const uint8_t* bytes;
|
||||
size_t nbytes;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the contents of one iobuffer to another one (data, size and pos)
|
||||
*
|
||||
* @param dest Destination to move to
|
||||
* @param src Source to move from
|
||||
* @return Number of bytes moved (this can be less than the source's size
|
||||
* e.g. if destination size < src size)
|
||||
*/
|
||||
size_t util_iobuf_move(struct util_iobuf* dest, struct util_const_iobuf* src);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "util/list.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
void util_list_init(struct util_list* list)
|
||||
{
|
||||
memset(list, 0, sizeof(*list));
|
||||
}
|
||||
|
||||
void util_list_append(struct util_list* list, struct util_list_node* node)
|
||||
{
|
||||
node->next = NULL;
|
||||
|
||||
if (list->tail != NULL) {
|
||||
list->tail->next = node;
|
||||
} else {
|
||||
list->head = node;
|
||||
}
|
||||
|
||||
list->tail = node;
|
||||
}
|
||||
|
||||
bool util_list_contains(struct util_list* list, struct util_list_node* node)
|
||||
{
|
||||
struct util_list_node* pos;
|
||||
|
||||
for (pos = list->head ; pos != NULL ; pos = pos->next) {
|
||||
if (pos == node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct util_list_node* util_list_pop_head(struct util_list* list)
|
||||
{
|
||||
struct util_list_node* node;
|
||||
|
||||
if (list->head == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = list->head;
|
||||
list->head = node->next;
|
||||
|
||||
if (node->next == NULL) {
|
||||
list->tail = NULL;
|
||||
}
|
||||
|
||||
node->next = NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void util_list_remove(struct util_list* list, struct util_list_node* node)
|
||||
{
|
||||
struct util_list_node* pos;
|
||||
struct util_list_node* prev;
|
||||
|
||||
for (prev = NULL, pos = list->head
|
||||
; pos != NULL
|
||||
; prev = pos, pos = pos->next) {
|
||||
if (pos == node) {
|
||||
if (prev != NULL) {
|
||||
prev->next = node->next;
|
||||
} else {
|
||||
list->head = node->next;
|
||||
}
|
||||
|
||||
if (node->next == NULL) {
|
||||
list->tail = prev;
|
||||
}
|
||||
|
||||
node->next = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool util_list_empty(struct util_list* list)
|
||||
{
|
||||
return list->head == NULL;
|
||||
}
|
||||
|
||||
inline struct util_list_node* util_list_peek_head(struct util_list* list)
|
||||
{
|
||||
return list->head;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef UTIL_LIST_H
|
||||
#define UTIL_LIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* (Doubly linked) list object
|
||||
*/
|
||||
struct util_list {
|
||||
struct util_list_node* head;
|
||||
struct util_list_node* tail;
|
||||
};
|
||||
|
||||
/**
|
||||
* List element/node
|
||||
*/
|
||||
struct util_list_node {
|
||||
struct util_list_node* next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize an allocated list
|
||||
*
|
||||
* @param list Pointer to a list to initialize
|
||||
*/
|
||||
void util_list_init(struct util_list* list);
|
||||
|
||||
/**
|
||||
* Append an element to a list
|
||||
*
|
||||
* @param list List to append to
|
||||
* @param node Node to append to the list
|
||||
*/
|
||||
void util_list_append(struct util_list* list, struct util_list_node* node);
|
||||
|
||||
/**
|
||||
* Check if the list contains the specified element
|
||||
*
|
||||
* @param list List to check
|
||||
* @param node Node to look for in the list
|
||||
* @return True if list contains the specified node, false otherwise
|
||||
*/
|
||||
bool util_list_contains(struct util_list* list, struct util_list_node* node);
|
||||
|
||||
/**
|
||||
* Peak (get but do not remove) the head of the list
|
||||
*
|
||||
* @param list List to peak
|
||||
* @return Head element of the list (or null if empty)
|
||||
*/
|
||||
struct util_list_node* util_list_peek_head(struct util_list* list);
|
||||
|
||||
/**
|
||||
* Pop (get and remove) the head of the list
|
||||
*
|
||||
* @param list List to pop
|
||||
* @return Removed head element of the list (or null if empty)
|
||||
*/
|
||||
struct util_list_node* util_list_pop_head(struct util_list* list);
|
||||
|
||||
/**
|
||||
* Remove a specific element of the list. If the element is not part of the,
|
||||
* the list is not modified.
|
||||
*
|
||||
* @param list List to remove an element from
|
||||
* @param node Element to remove from the list
|
||||
*/
|
||||
void util_list_remove(struct util_list* list, struct util_list_node* node);
|
||||
|
||||
/**
|
||||
* Check if a list is empty
|
||||
*
|
||||
* @param list List to check
|
||||
* @return True if empty, false otherwise
|
||||
*/
|
||||
bool util_list_empty(struct util_list* list);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
#define LOG_MODULE "util-log"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
#include "util/time.h"
|
||||
|
||||
#define COLORIZING_CONTROL_SEQ "\033[%d;%dm%s\033[0m"
|
||||
|
||||
static void util_log_format(enum util_log_level level, const char* module,
|
||||
const char* trace_file, size_t trace_line, const char* fmt, va_list ap);
|
||||
static void util_log_writer_console(void* ctx, enum util_log_level level, const char* module, const char* trace_file,
|
||||
size_t trace_line, const char* msg);
|
||||
static void util_log_writer_file(void* ctx, enum util_log_level level, const char* module, const char* trace_file,
|
||||
size_t trace_line, const char* msg);
|
||||
|
||||
static const char util_log_level_chars[] = "DEWIM";
|
||||
|
||||
static enum util_log_level util_log_level = LOG_LEVEL_DEBUG;
|
||||
static FILE* util_log_file;
|
||||
|
||||
void util_log_set_file(const char* path, bool append)
|
||||
{
|
||||
const char* mode = append ? "a+" : "w+";
|
||||
|
||||
if (path) {
|
||||
util_log_file = fopen(path, mode);
|
||||
log_info("Open log file: %s", path);
|
||||
}
|
||||
}
|
||||
|
||||
void util_log_set_level(enum util_log_level new_level)
|
||||
{
|
||||
util_log_level = new_level;
|
||||
}
|
||||
|
||||
FILE* util_log_get_file_handle(void)
|
||||
{
|
||||
return util_log_file;
|
||||
}
|
||||
|
||||
void util_log(enum util_log_level level, const char* module, const char* trace_file, size_t trace_line,
|
||||
const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
util_log_format(level, module, trace_file, trace_line, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void util_log_colorize_text(enum util_log_level level, const char* text, char* buffer)
|
||||
{
|
||||
switch (level) {
|
||||
case LOG_LEVEL_DEBUG:
|
||||
sprintf(buffer, COLORIZING_CONTROL_SEQ, 0, 37, text);
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_INFO:
|
||||
sprintf(buffer, COLORIZING_CONTROL_SEQ, 0, 34, text);
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_WARN:
|
||||
sprintf(buffer, COLORIZING_CONTROL_SEQ, 0, 33, text);
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_ERROR:
|
||||
sprintf(buffer, COLORIZING_CONTROL_SEQ, 0, 31, text);
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_DIE:
|
||||
sprintf(buffer, COLORIZING_CONTROL_SEQ, 0, 31, text);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_die_illegal_state();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void util_log_format(enum util_log_level level, const char* module,
|
||||
const char* trace_file, size_t trace_line, const char* fmt, va_list ap)
|
||||
{
|
||||
char msg[8192];
|
||||
|
||||
if (level <= util_log_level) {
|
||||
util_str_vformat(msg, sizeof(msg), fmt, ap);
|
||||
|
||||
util_log_writer_console(NULL, level, module, trace_file, trace_line, msg);
|
||||
|
||||
if (util_log_file != NULL) {
|
||||
util_log_writer_file(util_log_file, level, module, trace_file, trace_line, msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (level == LOG_LEVEL_DIE) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void util_log_writer_console(void* ctx, enum util_log_level level, const char* module, const char* trace_file,
|
||||
size_t trace_line, const char* msg)
|
||||
{
|
||||
char text_level[128];
|
||||
char text_level_color[128];
|
||||
|
||||
util_str_format(text_level, sizeof(text_level), "[%c]", util_log_level_chars[level]);
|
||||
util_log_colorize_text(level, text_level, text_level_color);
|
||||
|
||||
// ---------------------------
|
||||
|
||||
struct util_time_timestamp timestamp;
|
||||
|
||||
util_time_get_current_time(×tamp);
|
||||
|
||||
// ---------------------------
|
||||
|
||||
char line[8192];
|
||||
|
||||
util_str_format(line, sizeof(line), "%s[%d/%d/%d-%d:%d:%d:%d][%s][%s:%d] %s\n", text_level_color, timestamp.year,
|
||||
timestamp.month, timestamp.day, timestamp.hour, timestamp.min, timestamp.sec, timestamp.millisec,
|
||||
module, trace_file, trace_line, msg);
|
||||
|
||||
printf("%s", line);
|
||||
}
|
||||
|
||||
static void util_log_writer_file(void* ctx, enum util_log_level level, const char* module, const char* trace_file,
|
||||
size_t trace_line, const char* msg)
|
||||
{
|
||||
struct util_time_timestamp timestamp;
|
||||
|
||||
util_time_get_current_time(×tamp);
|
||||
|
||||
// ---------------------------
|
||||
|
||||
char line[8192];
|
||||
size_t line_len;
|
||||
|
||||
line_len = util_str_format(line, sizeof(line), "[%c][%d/%d/%d-%d:%d:%d:%d][%s][%s:%d] %s\n",
|
||||
util_log_level_chars[level], timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.min,
|
||||
timestamp.sec, timestamp.millisec, module, trace_file, trace_line, msg);
|
||||
|
||||
fwrite(line, 1, line_len, (FILE*) ctx);
|
||||
fflush((FILE*) ctx);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifndef UTIL_LOG_H
|
||||
#define UTIL_LOG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/defs.h"
|
||||
|
||||
#ifndef LOG_MODULE
|
||||
#define LOG_MODULE FILENAME
|
||||
#endif
|
||||
|
||||
#ifndef LOG_SUPPRESS
|
||||
|
||||
/* Don't expose source file names and absolute paths on public releases */
|
||||
#ifdef PT_PUBLIC_RELEASE
|
||||
#undef FILENAME
|
||||
#define FILENAME ""
|
||||
#define SOURCE_LINE 0
|
||||
#else
|
||||
#define SOURCE_LINE __LINE__
|
||||
#endif
|
||||
|
||||
#define log_debug(...) util_log(LOG_LEVEL_DEBUG, LOG_MODULE, FILENAME, SOURCE_LINE, __VA_ARGS__)
|
||||
#define log_info(...) util_log(LOG_LEVEL_INFO, LOG_MODULE, FILENAME, SOURCE_LINE, __VA_ARGS__)
|
||||
#define log_warn(...) util_log(LOG_LEVEL_WARN, LOG_MODULE, FILENAME, SOURCE_LINE, __VA_ARGS__)
|
||||
#define log_error(...) util_log(LOG_LEVEL_ERROR, LOG_MODULE, FILENAME, SOURCE_LINE, __VA_ARGS__)
|
||||
#define log_die(...) util_log(LOG_LEVEL_DIE, LOG_MODULE, FILENAME, SOURCE_LINE, __VA_ARGS__)
|
||||
#define log_die_illegal_state() util_log(LOG_LEVEL_DIE, LOG_MODULE, FILENAME, SOURCE_LINE, "Illegal state")
|
||||
|
||||
#else
|
||||
|
||||
#define log_debug(...)
|
||||
#define log_info(...)
|
||||
#define log_warn(...)
|
||||
#define log_error(...)
|
||||
#define log_die(...)
|
||||
|
||||
#endif
|
||||
|
||||
#define log_assert(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
log_die(FILENAME, __LINE__, __FUNCTION__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Different log levels for logging
|
||||
*/
|
||||
enum util_log_level {
|
||||
LOG_LEVEL_DIE = 0,
|
||||
LOG_LEVEL_ERROR = 1,
|
||||
LOG_LEVEL_WARN = 2,
|
||||
LOG_LEVEL_INFO = 3,
|
||||
LOG_LEVEL_DEBUG = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a log file to write log output to in addition to stdout
|
||||
*
|
||||
* @param path Path of the log file (existing files are overwritten of append argument false)
|
||||
* @param append True to append to an existing file, false to create a new file with the specified name
|
||||
*/
|
||||
void util_log_set_file(const char* path, bool append);
|
||||
|
||||
/**
|
||||
* Set the log level for logging
|
||||
*
|
||||
* @param new_level New log level
|
||||
*/
|
||||
void util_log_set_level(enum util_log_level new_level);
|
||||
|
||||
/**
|
||||
* Get the log file handle (required for hooking to filter it on fwrite calls on the filehook monitor)
|
||||
*
|
||||
* @return FILE handle of log file or NULL if logging to file disabled
|
||||
*/
|
||||
FILE* util_log_get_file_handle(void);
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* Don't use this call to log something, use the macros instead
|
||||
*
|
||||
* @param level Log level of the message
|
||||
* @param module Name of the module where this function is called
|
||||
* @param trace_file Additional trace information, file name
|
||||
* @param trace_line Additional trace information, line number
|
||||
* @param fmt Format string of log message
|
||||
* @param ... Arguments for format string
|
||||
*/
|
||||
void util_log(enum util_log_level level, const char* module, const char* trace_file, size_t trace_line,
|
||||
const char* fmt, ...);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
#define LOG_MODULE "util-mem"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static bool _util_mem_debug_output;
|
||||
|
||||
static void* _util_xmalloc(size_t nbytes);
|
||||
static void* _util_xrealloc(void* mem, size_t nbytes);
|
||||
static void _util_xfree(void** mem);
|
||||
|
||||
static const struct util_mem_interface _util_mem_interface_default = {
|
||||
.xmalloc = _util_xmalloc,
|
||||
.xrealloc = _util_xrealloc,
|
||||
.xfree = _util_xfree,
|
||||
};
|
||||
|
||||
static const struct util_mem_interface* _util_mem_interface = &_util_mem_interface_default;
|
||||
|
||||
static void* _util_xmalloc(size_t nbytes)
|
||||
{
|
||||
void* mem;
|
||||
|
||||
mem = malloc(nbytes);
|
||||
|
||||
if (_util_mem_debug_output) {
|
||||
log_debug("xmalloc(%d): %p", nbytes, mem);
|
||||
}
|
||||
|
||||
if (mem == NULL) {
|
||||
log_die("xmalloc(%u) failed", (uint32_t) nbytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void* _util_xrealloc(void* mem, size_t nbytes)
|
||||
{
|
||||
void* newmem;
|
||||
|
||||
newmem = realloc(mem, nbytes);
|
||||
|
||||
if (_util_mem_debug_output) {
|
||||
log_debug("xrealloc(%p %d): %p", mem, nbytes, newmem);
|
||||
}
|
||||
|
||||
if (newmem == NULL) {
|
||||
log_die("xrealloc(%p, %u) failed", mem, (uint32_t) nbytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return newmem;
|
||||
}
|
||||
|
||||
static void _util_xfree(void** mem)
|
||||
{
|
||||
log_assert(mem);
|
||||
|
||||
if (*mem != NULL) {
|
||||
if (_util_mem_debug_output) {
|
||||
log_debug("xfree(%p %p)", mem, *mem);
|
||||
}
|
||||
|
||||
free(*mem);
|
||||
*mem = NULL;
|
||||
} else {
|
||||
if (_util_mem_debug_output) {
|
||||
log_warn("xfree(%p NULL)", mem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void util_mem_init(const struct util_mem_interface* interface)
|
||||
{
|
||||
log_assert(interface);
|
||||
|
||||
_util_mem_interface = interface;
|
||||
}
|
||||
|
||||
void util_mem_init_default()
|
||||
{
|
||||
_util_mem_interface = &_util_mem_interface_default;
|
||||
}
|
||||
|
||||
void util_mem_enable_debug_output()
|
||||
{
|
||||
_util_mem_debug_output = true;
|
||||
}
|
||||
|
||||
void* util_xmalloc(size_t nbytes)
|
||||
{
|
||||
return _util_mem_interface->xmalloc(nbytes);
|
||||
}
|
||||
|
||||
void* util_xrealloc(void* mem, size_t nbytes)
|
||||
{
|
||||
return _util_mem_interface->xrealloc(mem, nbytes);
|
||||
}
|
||||
|
||||
void util_xfree(void** mem)
|
||||
{
|
||||
_util_mem_interface->xfree(mem);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef UTIL_MEM_H
|
||||
#define UTIL_MEM_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef void* (*util_xmalloc_t)(size_t nbytes);
|
||||
typedef void* (*util_xrealloc_t)(void* mem, size_t nbytes);
|
||||
typedef void (*util_free_t)(void** mem);
|
||||
|
||||
struct util_mem_interface {
|
||||
util_xmalloc_t xmalloc;
|
||||
util_xrealloc_t xrealloc;
|
||||
util_free_t xfree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Install a different memory interfacee, e.g. to use a different allocator or hooks for unit-testing.
|
||||
*/
|
||||
void util_mem_init(const struct util_mem_interface* interface);
|
||||
|
||||
/**
|
||||
* Optional, initialize the default memory interface. This needs to be called only if you want to switch back from
|
||||
* a non-default interface that got installed using util_mem_init.
|
||||
*/
|
||||
void util_mem_init_default();
|
||||
|
||||
/**
|
||||
* Enable debug output of all allocations and de-allocations that go through the default interface of this module.
|
||||
*/
|
||||
void util_mem_enable_debug_output();
|
||||
|
||||
/**
|
||||
* Allocate memory on the heap (malloc wrapper)
|
||||
*
|
||||
* @param nbytes Number of bytes to allocate
|
||||
* @return Pointer to allocated memory
|
||||
*/
|
||||
void* util_xmalloc(size_t nbytes);
|
||||
|
||||
/**
|
||||
* Realloc memory (realloc wrapper)
|
||||
*
|
||||
* @param mem Pointer to allocated memory to realloc/expand
|
||||
* @param nbytes New size of memory region
|
||||
* @return Pointer to reallocated memory region
|
||||
*/
|
||||
void* util_xrealloc(void* mem, size_t nbytes);
|
||||
|
||||
/**
|
||||
* Free allocated memory.
|
||||
*
|
||||
* @param mem Pointer to the pointer with allocated memory to free. The pointer to the memory will be set to NULL.
|
||||
*/
|
||||
void util_xfree(void** mem);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/net.h"
|
||||
#include "util/str.h"
|
||||
|
||||
bool util_net_split_ipv4_str(const char* str, char** address, uint16_t* port)
|
||||
{
|
||||
char* tmp = util_str_dup(str);
|
||||
char* pch = strtok(tmp, ":");
|
||||
int counter = 0;
|
||||
|
||||
*address = NULL;
|
||||
*port = 0xFFFF;
|
||||
|
||||
while (pch != NULL) {
|
||||
switch (counter) {
|
||||
case 0:
|
||||
*address = util_str_dup(pch);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*port = (uint16_t) strtol(pch, NULL, 10);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (*address) {
|
||||
free(*address);
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
pch = strtok (NULL, ":");
|
||||
counter++;
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef UTIL_NET_H
|
||||
#define UTIL_NET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Split an ipv4 string (e.g. 127.0.0.1:22 or just 127.0.0.1) into address and port
|
||||
*
|
||||
* @param str String to split
|
||||
* @param address Address part (must be free'd by caller)
|
||||
* @param port Port part (or -1 if it doesn't exist)
|
||||
* @return True if successful, false on format error
|
||||
*/
|
||||
bool util_net_split_ipv4_str(const char* str, char** address, uint16_t* port);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,758 @@
|
||||
#define LOG_MODULE "util-options"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/fs.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/options.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct util_options_opts* util_options_get2(int argc, char** argv,
|
||||
const struct util_options_defs* option_defs);
|
||||
static bool util_options_init_from_args(struct util_options_opts* options,
|
||||
const struct util_options_defs* option_defs, int argc, char** argv);
|
||||
static bool util_options_init_from_file(struct util_options_opts* options,
|
||||
const struct util_options_defs* option_defs, char* file_path);
|
||||
static bool util_options_write_default_options_file(
|
||||
const struct util_options_defs* option_defs, char* file_path);
|
||||
|
||||
static int util_options_argc;
|
||||
static char** util_options_argv;
|
||||
|
||||
void util_options_init(int argc, char** argv)
|
||||
{
|
||||
util_options_argc = argc;
|
||||
util_options_argv = argv;
|
||||
}
|
||||
|
||||
struct util_options_opts* util_options_get(
|
||||
const struct util_options_defs* option_defs)
|
||||
{
|
||||
struct util_options_opts* options;
|
||||
|
||||
options = util_options_get2(util_options_argc, util_options_argv,
|
||||
option_defs);
|
||||
|
||||
if (!options) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
int util_options_get_int(const struct util_options_opts* opts,
|
||||
const char* name)
|
||||
{
|
||||
for (uint32_t i = 0; i < opts->entries; i++) {
|
||||
|
||||
if (!strcmp(opts->defs->defs[i].name, name)) {
|
||||
|
||||
if (opts->defs->defs[i].type != UTIL_OPTIONS_TYPE_INT) {
|
||||
log_warn("Option %s is not of type int", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opts->values[i].avail) {
|
||||
return opts->values[i].value.i;
|
||||
} else {
|
||||
return opts->defs->defs[i].default_value.i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_warn("Could not find int option %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
double util_options_get_double(const struct util_options_opts* opts, const char* name)
|
||||
{
|
||||
for (uint32_t i = 0; i < opts->entries; i++) {
|
||||
|
||||
if (!strcmp(opts->defs->defs[i].name, name)) {
|
||||
|
||||
if (opts->defs->defs[i].type != UTIL_OPTIONS_TYPE_DOUBLE) {
|
||||
log_warn("Option %s is not of type double", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opts->values[i].avail) {
|
||||
return opts->values[i].value.d;
|
||||
} else {
|
||||
return opts->defs->defs[i].default_value.d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_warn("Could not find double option %s", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* util_options_get_str(const struct util_options_opts* opts,
|
||||
const char* name)
|
||||
{
|
||||
for (uint32_t i = 0; i < opts->entries; i++) {
|
||||
|
||||
if (!strcmp(opts->defs->defs[i].name, name)) {
|
||||
|
||||
if (opts->defs->defs[i].type != UTIL_OPTIONS_TYPE_STR) {
|
||||
log_warn("Option %s is not of type str", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opts->values[i].avail) {
|
||||
return opts->values[i].value.str;
|
||||
} else {
|
||||
return opts->defs->defs[i].default_value.str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_warn("Could not find str option %s", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool util_options_get_bool(const struct util_options_opts* opts,
|
||||
const char* name)
|
||||
{
|
||||
for (uint32_t i = 0; i < opts->entries; i++) {
|
||||
|
||||
if (!strcmp(opts->defs->defs[i].name, name)) {
|
||||
|
||||
if (opts->defs->defs[i].type != UTIL_OPTIONS_TYPE_BOOL) {
|
||||
log_warn("Option %s is not of type bool", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts->values[i].avail) {
|
||||
return opts->values[i].value.b;
|
||||
} else {
|
||||
return opts->defs->defs[i].default_value.b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_warn("Could not find bool option %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* util_options_get_bin(const struct util_options_opts* opts,
|
||||
const char* name, size_t* length)
|
||||
{
|
||||
for (uint32_t i = 0; i < opts->entries; i++) {
|
||||
|
||||
if (!strcmp(opts->defs->defs[i].name, name)) {
|
||||
|
||||
if (opts->defs->defs[i].type != UTIL_OPTIONS_TYPE_BIN) {
|
||||
log_warn("Option %s is not of type bin", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opts->values[i].avail) {
|
||||
if (length) {
|
||||
*length = opts->values[i].value.bin.len;
|
||||
}
|
||||
|
||||
return opts->values[i].value.bin.data;
|
||||
} else {
|
||||
if (length) {
|
||||
*length = opts->defs->defs[i].default_value.bin.len;
|
||||
}
|
||||
|
||||
return opts->defs->defs[i].default_value.bin.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_warn("Could not find bin option %s", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void util_options_print_usage(const struct util_options_defs* option_defs)
|
||||
{
|
||||
printf(
|
||||
"%s\nSpecify options as arguments or use --options [path file] to "
|
||||
"specify a config file containing key=value per line entries\n",
|
||||
option_defs->usage_header);
|
||||
|
||||
for (uint32_t i = 0; i < option_defs->ndefs; i++) {
|
||||
|
||||
switch (option_defs->defs[i].type) {
|
||||
case UTIL_OPTIONS_TYPE_INT:
|
||||
{
|
||||
printf(
|
||||
" -%c [int] %s: %s\n"
|
||||
" default: %d\n",
|
||||
option_defs->defs[i].param, option_defs->defs[i].name,
|
||||
option_defs->defs[i].description,
|
||||
option_defs->defs[i].default_value.i);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_DOUBLE:
|
||||
{
|
||||
printf(
|
||||
" -%c [double] %s: %s\n"
|
||||
" default: %f\n",
|
||||
option_defs->defs[i].param, option_defs->defs[i].name,
|
||||
option_defs->defs[i].description,
|
||||
option_defs->defs[i].default_value.d);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_STR:
|
||||
{
|
||||
printf(
|
||||
" -%c [str] %s: %s\n"
|
||||
" default: %s\n",
|
||||
option_defs->defs[i].param, option_defs->defs[i].name,
|
||||
option_defs->defs[i].description,
|
||||
option_defs->defs[i].default_value.str);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BOOL:
|
||||
{
|
||||
printf(
|
||||
" -%c [bool] %s: %s\n"
|
||||
" default: %d\n",
|
||||
option_defs->defs[i].param, option_defs->defs[i].name,
|
||||
option_defs->defs[i].description,
|
||||
option_defs->defs[i].default_value.b);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BIN:
|
||||
{
|
||||
size_t len = option_defs->defs[i].default_value.bin.len * 2 + 1;
|
||||
char* buf = util_xmalloc(len);
|
||||
|
||||
util_hex_encode_uc(option_defs->defs[i].default_value.bin.data,
|
||||
option_defs->defs[i].default_value.bin.len, buf, len);
|
||||
|
||||
printf(
|
||||
" -%c [bin] %s: %s\n"
|
||||
" default (len %Iu): %s\n",
|
||||
option_defs->defs[i].param, option_defs->defs[i].name,
|
||||
option_defs->defs[i].description,
|
||||
option_defs->defs[i].default_value.bin.len,
|
||||
buf);
|
||||
|
||||
free(buf);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
printf(
|
||||
" -%c [unkn] %s: %s\n"
|
||||
" default: -invalid-\n",
|
||||
option_defs->defs[i].param, option_defs->defs[i].name,
|
||||
option_defs->defs[i].description);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void util_options_free(struct util_options_opts* opts)
|
||||
{
|
||||
for (uint32_t i = 0; i < opts->entries; i++) {
|
||||
|
||||
switch (opts->defs->defs[i].type) {
|
||||
case UTIL_OPTIONS_TYPE_STR:
|
||||
{
|
||||
if (opts->values[i].value.str) {
|
||||
free(opts->values[i].value.str);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BIN:
|
||||
{
|
||||
if (opts->values[i].value.bin.data) {
|
||||
free(opts->values[i].value.bin.data);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(opts->values);
|
||||
free(opts);
|
||||
}
|
||||
|
||||
// if NULL, usage is printed
|
||||
static struct util_options_opts* util_options_get2(
|
||||
int argc, char** argv, const struct util_options_defs* option_defs)
|
||||
{
|
||||
bool success;
|
||||
char* options_file = NULL;
|
||||
struct util_options_opts* options =
|
||||
(struct util_options_opts*)
|
||||
util_xmalloc(sizeof(struct util_options_opts));
|
||||
|
||||
if (option_defs == NULL) {
|
||||
options->defs = NULL;
|
||||
options->values = NULL;
|
||||
options->entries = 0;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
options->defs = option_defs;
|
||||
options->entries = option_defs->ndefs;
|
||||
|
||||
options->values = (struct util_options_value*) util_xmalloc(
|
||||
sizeof(struct util_options_value) * options->entries);
|
||||
|
||||
memset(options->values, 0,
|
||||
sizeof(struct util_options_value) * options->entries);
|
||||
|
||||
/* detect if we read options from a file */
|
||||
for (int i = 0; i < argc; i++) {
|
||||
|
||||
if (!strcmp(argv[i], "--options")) {
|
||||
|
||||
if (i + 1 >= argc) {
|
||||
log_warn("Missing argument for --options\n");
|
||||
|
||||
util_options_print_usage(option_defs);
|
||||
util_options_free(options);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
options_file = argv[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (options_file) {
|
||||
log_debug("Processing options file %s", options_file);
|
||||
success = util_options_init_from_file(options, option_defs,
|
||||
options_file);
|
||||
|
||||
if (!success) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Processing cmd args");
|
||||
success = util_options_init_from_args(options, option_defs, argc, argv);
|
||||
|
||||
if (!success) {
|
||||
util_options_print_usage(option_defs);
|
||||
util_options_free(options);
|
||||
return NULL;
|
||||
} else {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
static bool util_options_init_from_args(struct util_options_opts* options,
|
||||
const struct util_options_defs* option_defs, int argc, char** argv)
|
||||
{
|
||||
for (int i = 0; i < argc; i++) {
|
||||
|
||||
if (argv[i][0] != '-') {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < options->entries; j++) {
|
||||
/* prints usage */
|
||||
if (argv[i][1] == option_defs->usage_param) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options->defs->defs[j].param == argv[i][1]) {
|
||||
|
||||
switch (options->defs->defs[j].type) {
|
||||
case UTIL_OPTIONS_TYPE_INT:
|
||||
{
|
||||
if (i + 1 >= argc) {
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
options->values[j].value.i = atoi(argv[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_DOUBLE:
|
||||
{
|
||||
if (i + 1 >= argc) {
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
options->values[j].value.d = atof(argv[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_STR:
|
||||
{
|
||||
if (i + 1 >= argc) {
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
/* FIXME memory leak if overriding string from options
|
||||
file */
|
||||
|
||||
if (strlen(argv[i]) > 0) {
|
||||
options->values[j].value.str = util_str_dup(argv[i]);
|
||||
} else {
|
||||
options->values[j].value.str = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BOOL:
|
||||
{
|
||||
options->values[j].value.b = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BIN:
|
||||
{
|
||||
if (i + 1 >= argc) {
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
/* FIXME memory leak if overriding bin data from options
|
||||
file */
|
||||
|
||||
if (strlen(argv[i]) > 0) {
|
||||
size_t len = strlen(argv[i]);
|
||||
len = len / 2 + len % 2;
|
||||
|
||||
options->values[j].value.bin.len = len;
|
||||
options->values[j].value.bin.data = util_xmalloc(len);
|
||||
|
||||
if (!util_hex_decode(options->values[j].value.bin.data,
|
||||
options->values[j].value.bin.len, argv[i],
|
||||
strlen(argv[i]))) {
|
||||
log_warn("Decoding binary data for key %s "
|
||||
"failed", options->defs->defs[j].name);
|
||||
options->values[j].value.bin.len = 0;
|
||||
options->values[j].value.bin.data = NULL;
|
||||
}
|
||||
} else {
|
||||
options->values[j].value.bin.len = 0;
|
||||
options->values[j].value.bin.data = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("Cmd arg: -%c %s (type %d): %s",
|
||||
options->defs->defs[j].param, options->defs->defs[j].name,
|
||||
options->defs->defs[j].type, argv[i]);
|
||||
|
||||
options->values[j].avail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool util_options_init_from_file(struct util_options_opts* options,
|
||||
const struct util_options_defs* option_defs, char* file_path)
|
||||
{
|
||||
char* pos_lines;
|
||||
char* pos_key_val;
|
||||
char* ctx_lines;
|
||||
char* ctx_key_val;
|
||||
char* data;
|
||||
size_t len;
|
||||
|
||||
if (!util_file_load(file_path, (void**) &data, &len, true)) {
|
||||
/* If file does not exist, create one with default configuration
|
||||
values */
|
||||
if (util_fs_path_exists(file_path)) {
|
||||
log_warn("Loading options file %s failed, faulty or corrupted "
|
||||
"configuration", file_path);
|
||||
} else {
|
||||
/* Create a default config file if the usage param is set. This
|
||||
indicates that we are currently not on the inject process
|
||||
but already on the inject library. We want all config params
|
||||
of the inject lib */
|
||||
if (option_defs->usage_param) {
|
||||
log_info("Options file %s does not exist, creating default...",
|
||||
file_path);
|
||||
|
||||
if (!util_options_write_default_options_file(option_defs,
|
||||
file_path)) {
|
||||
log_warn("Creating default options file %s failed",
|
||||
file_path);
|
||||
} else {
|
||||
log_info("Options file %s created. Tweak it to your needs and restart the game.",
|
||||
file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pos_lines = strtok_r(data, "\n", &ctx_lines);
|
||||
|
||||
while (pos_lines != NULL) {
|
||||
char* pos_line_dup;
|
||||
char* key = NULL;
|
||||
char* val = NULL;
|
||||
|
||||
/* ignore comments and empty lines */
|
||||
if (strlen(pos_lines) > 0 && pos_lines[0] != '#') {
|
||||
pos_line_dup = util_str_dup(pos_lines);
|
||||
pos_key_val = strtok_r(pos_line_dup, "=", &ctx_key_val);
|
||||
|
||||
log_debug("Line: %s", pos_lines);
|
||||
|
||||
if (pos_key_val != NULL) {
|
||||
key = pos_key_val;
|
||||
val = &pos_key_val[strlen(key) + 1];
|
||||
} else {
|
||||
log_warn("Invalid options line %s in options file %s", pos_lines, file_path);
|
||||
free(pos_line_dup);
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_debug("Key: %s, Value: %s", key, val);
|
||||
|
||||
/* search for key */
|
||||
for (uint32_t j = 0; j < options->entries; j++) {
|
||||
|
||||
if (!strcmp(options->defs->defs[j].name, key)) {
|
||||
|
||||
switch (options->defs->defs[j].type) {
|
||||
case UTIL_OPTIONS_TYPE_INT:
|
||||
{
|
||||
if (val) {
|
||||
options->values[j].value.i = atoi(val);
|
||||
} else {
|
||||
options->values[j].value.i = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_DOUBLE:
|
||||
{
|
||||
if (val) {
|
||||
options->values[j].value.d = atof(val);
|
||||
} else {
|
||||
options->values[j].value.d = 0.0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_STR:
|
||||
{
|
||||
if (val && strlen(val) > 0) {
|
||||
options->values[j].value.str =
|
||||
util_str_dup(val);
|
||||
} else {
|
||||
options->values[j].value.str = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BOOL:
|
||||
{
|
||||
if (val) {
|
||||
if (atoi(val) <= 0) {
|
||||
options->values[j].value.b = false;
|
||||
} else {
|
||||
options->values[j].value.b = true;
|
||||
}
|
||||
} else {
|
||||
options->values[j].value.b = false;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BIN:
|
||||
{
|
||||
if (val && strlen(val) > 0) {
|
||||
size_t lenBin = strlen(val);
|
||||
lenBin = lenBin / 2 + lenBin % 2;
|
||||
|
||||
options->values[j].value.bin.len = lenBin;
|
||||
options->values[j].value.bin.data =
|
||||
util_xmalloc(lenBin);
|
||||
|
||||
if (!util_hex_decode(
|
||||
options->values[j].value.bin.data,
|
||||
options->values[j].value.bin.len, val,
|
||||
strlen(val))) {
|
||||
log_warn("Decoding binary data for key "
|
||||
"%s failed",
|
||||
options->defs->defs[j].name);
|
||||
options->values[j].value.bin.len = 0;
|
||||
options->values[j].value.bin.data = NULL;
|
||||
}
|
||||
} else {
|
||||
options->values[j].value.bin.len = 0;
|
||||
options->values[j].value.bin.data = NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("Options file param: -%c %s (type %d): %s",
|
||||
options->defs->defs[j].param,
|
||||
options->defs->defs[j].name,
|
||||
options->defs->defs[j].type, val ? val : "NULL");
|
||||
|
||||
options->values[j].avail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(pos_line_dup);
|
||||
}
|
||||
|
||||
pos_lines = strtok_r(NULL, "\n", &ctx_lines);
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool util_options_write_default_options_file(
|
||||
const struct util_options_defs* option_defs, char* file_path)
|
||||
{
|
||||
FILE* file;
|
||||
|
||||
file = fopen(file_path, "wb");
|
||||
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < option_defs->ndefs; i++) {
|
||||
|
||||
switch (option_defs->defs[i].type) {
|
||||
case UTIL_OPTIONS_TYPE_INT:
|
||||
{
|
||||
fprintf(file, "# [int]: %s\n",
|
||||
option_defs->defs[i].description);
|
||||
fprintf(file, "%s=%d\n\n", option_defs->defs[i].name,
|
||||
option_defs->defs[i].default_value.i);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_DOUBLE:
|
||||
{
|
||||
fprintf(file, "# [double]: %s\n",
|
||||
option_defs->defs[i].description);
|
||||
fprintf(file, "%s=%f\n\n", option_defs->defs[i].name,
|
||||
option_defs->defs[i].default_value.d);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_STR:
|
||||
{
|
||||
fprintf(file, "# [str]: %s\n",
|
||||
option_defs->defs[i].description);
|
||||
|
||||
if (option_defs->defs[i].default_value.str) {
|
||||
fprintf(file, "%s=%s\n\n", option_defs->defs[i].name,
|
||||
option_defs->defs[i].default_value.str);
|
||||
} else {
|
||||
fprintf(file, "%s=\n\n", option_defs->defs[i].name);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BOOL:
|
||||
{
|
||||
fprintf(file, "# [bool (0/1)]: %s\n",
|
||||
option_defs->defs[i].description);
|
||||
fprintf(file, "%s=%d\n\n", option_defs->defs[i].name,
|
||||
option_defs->defs[i].default_value.b);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UTIL_OPTIONS_TYPE_BIN:
|
||||
{
|
||||
fprintf(file, "# [bin]: %s\n",
|
||||
option_defs->defs[i].description);
|
||||
|
||||
if (option_defs->defs[i].default_value.bin.data) {
|
||||
size_t len =
|
||||
option_defs->defs[i].default_value.bin.len * 2 + 1;
|
||||
char* buf = util_xmalloc(len);
|
||||
|
||||
util_hex_encode_uc(
|
||||
option_defs->defs[i].default_value.bin.data,
|
||||
option_defs->defs[i].default_value.bin.len, buf, len);
|
||||
|
||||
|
||||
fprintf(file, "%s=%s\n\n", option_defs->defs[i].name, buf);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
log_warn("Invalid value type %d found",
|
||||
option_defs->defs[i].type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
#ifndef UTIL_OPTIONS_H
|
||||
#define UTIL_OPTIONS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* Value types support by option values
|
||||
*/
|
||||
enum util_options_type {
|
||||
UTIL_OPTIONS_TYPE_INT,
|
||||
UTIL_OPTIONS_TYPE_DOUBLE,
|
||||
UTIL_OPTIONS_TYPE_STR,
|
||||
UTIL_OPTIONS_TYPE_BOOL,
|
||||
UTIL_OPTIONS_TYPE_BIN,
|
||||
};
|
||||
|
||||
/**
|
||||
* Single option definition element. Use this to define possible option
|
||||
* key-value tuples to parse from an argument list
|
||||
*/
|
||||
struct util_options_def {
|
||||
const char* name;
|
||||
const char* description;
|
||||
const char param;
|
||||
enum util_options_type type;
|
||||
union {
|
||||
int i;
|
||||
double d;
|
||||
const char* str;
|
||||
bool b;
|
||||
struct {
|
||||
const uint8_t* data;
|
||||
size_t len;
|
||||
} bin;
|
||||
} default_value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Option definitions. A list of key-value elements defining option values
|
||||
* as well as usage information for the application
|
||||
*/
|
||||
struct util_options_defs {
|
||||
const char* usage_header;
|
||||
char usage_param;
|
||||
const struct util_options_def* defs;
|
||||
uint32_t ndefs;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single option value as part of the key-value tuple
|
||||
*/
|
||||
struct util_options_value {
|
||||
bool avail;
|
||||
union {
|
||||
int i;
|
||||
double d;
|
||||
char* str;
|
||||
bool b;
|
||||
struct {
|
||||
uint8_t* data;
|
||||
size_t len;
|
||||
} bin;
|
||||
} value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parsed option values based on an options definition
|
||||
*/
|
||||
struct util_options_opts {
|
||||
const struct util_options_defs* defs;
|
||||
struct util_options_value* values;
|
||||
uint32_t entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the options module by passing the arguments from main on
|
||||
*
|
||||
* Make sure to call this as early as possible when entering main and before
|
||||
* any other calls of this module are invoked
|
||||
*
|
||||
* @param argc Argc from main
|
||||
* @param argv Argv from main
|
||||
*/
|
||||
void util_options_init(int argc, char** argv);
|
||||
|
||||
/**
|
||||
* Get options from the initial argument list
|
||||
*
|
||||
* @param option_defs A list of option definitions. The arguments are parsed
|
||||
* using this list. If NULL, usage information with the definition
|
||||
* is printed and this call also returns NULL.
|
||||
* @return A list of parsed options based on the specified definitions or
|
||||
* NULL if usage information was printed.
|
||||
*/
|
||||
struct util_options_opts* util_options_get(
|
||||
const struct util_options_defs* option_defs);
|
||||
|
||||
/**
|
||||
* Get an integer value from an options list
|
||||
*
|
||||
* @param opts Parsed options list received from util_options_get
|
||||
* @param name Name of the options key
|
||||
* @return Value of the options key converted to an integer value. If the type
|
||||
* of the options key is not matching, 0 is returned
|
||||
*/
|
||||
int util_options_get_int(const struct util_options_opts* opts,
|
||||
const char* name);
|
||||
|
||||
/**
|
||||
* Get a double value from an options list
|
||||
*
|
||||
* @param opts Parsed options list received from util_options_get
|
||||
* @param name Name of the options key
|
||||
* @return Value of the options key converted to a double value. If the type
|
||||
* of the options key is not matching, 0 is returned
|
||||
*/
|
||||
double util_options_get_double(const struct util_options_opts* opts, const char* name);
|
||||
|
||||
/**
|
||||
* Get a string from an options list
|
||||
*
|
||||
* @param opts Parsed options list received from util_options_get
|
||||
* @param name Name of the options key
|
||||
* @return Value of the options key converted to a string. If the type
|
||||
* of the options key is not matching, NULL is returned
|
||||
*/
|
||||
const char* util_options_get_str(const struct util_options_opts* opts,
|
||||
const char* name);
|
||||
|
||||
/**
|
||||
* Get a bool value from an options list
|
||||
*
|
||||
* @param opts Parsed options list received from util_options_get
|
||||
* @param name Name of the options key
|
||||
* @return Value of the options key converted to a bool value. If the type
|
||||
* of the options key is not matching, false is returned
|
||||
*/
|
||||
bool util_options_get_bool(const struct util_options_opts* opts,
|
||||
const char* name);
|
||||
|
||||
/**
|
||||
* Get binary data from an options list
|
||||
*
|
||||
* @param opts Parsed options list received from util_options_get
|
||||
* @param name Name of the options key
|
||||
* @param length Pointer to return the length of the binary data to
|
||||
* (set to NULL if not required)
|
||||
* @return Value of the options key converted to a binary array. If the type
|
||||
* of the options key is not matching, NULL is returned
|
||||
*/
|
||||
const uint8_t* util_options_get_bin(const struct util_options_opts* opts,
|
||||
const char* name, size_t* length);
|
||||
|
||||
/**
|
||||
* Print usage information
|
||||
*
|
||||
* @param option_defs A list of option definitions.
|
||||
*/
|
||||
void util_options_print_usage(const struct util_options_defs* opts);
|
||||
|
||||
/**
|
||||
* Free an options list
|
||||
*
|
||||
* @param opts Options list to free
|
||||
*/
|
||||
void util_options_free(struct util_options_opts* opts);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
#define LOG_MODULE "util-patch"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/patch.h"
|
||||
|
||||
/* addr needs to be a multiple of page size */
|
||||
#define UNPROTECT(addr,len) mprotect((void*)(addr-(addr%4096)),(addr%4096) + \
|
||||
len,PROT_READ|PROT_WRITE|PROT_EXEC)
|
||||
|
||||
void util_patch_write_memory(uintptr_t mem, const void* data, size_t len)
|
||||
{
|
||||
if (UNPROTECT(mem, len) != 0) {
|
||||
log_die("Unprotecting memory %p, len %d failed", mem, len);
|
||||
}
|
||||
|
||||
memcpy((void*) mem, data, len);
|
||||
}
|
||||
|
||||
void util_patch_write_memory_byte(uintptr_t mem, uint8_t data)
|
||||
{
|
||||
util_patch_write_memory(mem, (const void*) &data, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void util_patch_function(uintptr_t func_addr, void* detour_func)
|
||||
{
|
||||
/* uintptr_t is said to be portable (32/64-bit) */
|
||||
uint8_t offset = 5;
|
||||
|
||||
/* 64-bit binary */
|
||||
if (sizeof(uintptr_t) == 8) {
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
uintptr_t call =
|
||||
(uintptr_t) (((uintptr_t) detour_func) - func_addr - offset);
|
||||
|
||||
if (UNPROTECT(func_addr, 4096) != 0) {
|
||||
log_die("Unprotecting memory %p, len %d failed", func_addr);
|
||||
}
|
||||
|
||||
*((uint8_t*) (func_addr)) = 0xE9;
|
||||
*((uintptr_t*) (func_addr + 1)) = call;
|
||||
}
|
||||
|
||||
void* util_patch_find_signiture(const uint8_t* sig, uint32_t sig_len,
|
||||
int32_t sig_offset, void* start_addr, void* end_addr, int32_t alignment)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
for (pos = (size_t) start_addr; pos < (size_t) end_addr; pos += alignment) {
|
||||
if (!memcmp((void*) pos, sig, sig_len)) {
|
||||
return (void*) (pos + sig_offset);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef UTIL_PATCH_H
|
||||
#define UTIL_PATCH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Write data to an arbitrary memory location
|
||||
*
|
||||
* @param mem Memory location to write to
|
||||
* @param data Pointer to buffer with data to write
|
||||
* @param len Length of data to write
|
||||
*/
|
||||
void util_patch_write_memory(uintptr_t mem, const void* data, size_t len);
|
||||
|
||||
/**
|
||||
* Write a single byte to an arbitrary memory location
|
||||
*
|
||||
* @param mem Memory location to write to
|
||||
* @param data Byte to write
|
||||
*/
|
||||
void util_patch_write_memory_byte(uintptr_t mem, uint8_t data);
|
||||
|
||||
/**
|
||||
* Write a function ptr/function call to a memory location
|
||||
*
|
||||
* Use this to patch existing call instructions and detour to your own code
|
||||
*
|
||||
* @param func_addr Address of the function/call instruction to replace
|
||||
* @param detour_func Address of your function to detour to
|
||||
*/
|
||||
void util_patch_function(uintptr_t func_addr, void* detour_func);
|
||||
|
||||
/**
|
||||
* Scan a memory area limited by a start and end address for a signiture.
|
||||
*
|
||||
* @param sig Buffer with signiture to search for
|
||||
* @param sig_len Length of signiture
|
||||
* @param sig_offset Offset that gets added to the address where the signiture
|
||||
* starts (if found)
|
||||
* @param start_addr Pointer to the address to start searching at
|
||||
* @param end_addr Pointer to the address to stop searching at
|
||||
* @param alignment Aligns the search signiture, i.e. search in steps of
|
||||
* X bytes
|
||||
* @return If found, pointer to memory where the signiture was found (first
|
||||
* occurance) with the signiture offset added, NULL otherwise
|
||||
*/
|
||||
void* util_patch_find_signiture(const uint8_t* sig, uint32_t sig_len,
|
||||
int32_t sig_offset, void* start_addr, void* end_addr, int32_t alignment);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,218 @@
|
||||
#define LOG_MODULE "util-proc"
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/limits.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/proc.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static char* util_sys_read_entries_from_proc_file(const char* path, int* count_out)
|
||||
{
|
||||
const char* sep = "\n";
|
||||
char* str = util_str_dup("");
|
||||
char* tmp;
|
||||
|
||||
FILE* cmdline = fopen(path, "rb");
|
||||
|
||||
if (!cmdline) {
|
||||
log_error("Opening %s failed: %s", path, strerror(errno));
|
||||
return str;
|
||||
}
|
||||
|
||||
char* arg = 0;
|
||||
size_t size = 0;
|
||||
int count = 0;
|
||||
|
||||
while (getdelim(&arg, &size, 0, cmdline) != -1) {
|
||||
if (strlen(str) > 0) {
|
||||
tmp = util_str_merge(str, sep);
|
||||
free(str);
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
tmp = util_str_merge(str, arg);
|
||||
free(str);
|
||||
str = tmp;
|
||||
count++;
|
||||
}
|
||||
|
||||
free(arg);
|
||||
fclose(cmdline);
|
||||
|
||||
if (count_out) {
|
||||
*count_out = count;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void util_proc_log_current_working_dir()
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
|
||||
if (!getcwd(tmp, sizeof(tmp))) {
|
||||
log_error("Getting current working directory failed");
|
||||
} else {
|
||||
log_info("Current working directory: %s", tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static void util_proc_log_current_process()
|
||||
{
|
||||
log_info("PID: %d", getpid());
|
||||
}
|
||||
|
||||
static void util_sys_info_log_path_executable()
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
|
||||
if (!util_proc_get_path_executable(buffer, sizeof(buffer))) {
|
||||
log_error("Getting path of executable failed");
|
||||
} else {
|
||||
log_info("Executable: %s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void util_sys_info_log_user()
|
||||
{
|
||||
log_info("User: %d", getuid());
|
||||
}
|
||||
|
||||
static void util_sys_info_log_running_as_root()
|
||||
{
|
||||
log_info("Running as root: %d", util_proc_is_running_as_root());
|
||||
}
|
||||
|
||||
static void util_sys_info_log_cmd_line_args()
|
||||
{
|
||||
int count;
|
||||
char* str = util_sys_read_entries_from_proc_file("/proc/self/cmdline", &count);
|
||||
|
||||
log_info("Cmd arguments (%d):\n%s", count, str);
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void util_sys_info_log_env_vars()
|
||||
{
|
||||
int count;
|
||||
char* str = util_sys_read_entries_from_proc_file("/proc/self/environ", &count);
|
||||
|
||||
log_info("Environment variables (%d):\n%s", count, str);
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
bool util_proc_is_running_as_root()
|
||||
{
|
||||
return getuid() == 0;
|
||||
}
|
||||
|
||||
bool util_proc_get_path_executable(char* buffer, size_t size)
|
||||
{
|
||||
// readlink does not null terminate
|
||||
memset(buffer, 0, size);
|
||||
|
||||
return readlink("/proc/self/exe", buffer, size) != -1;
|
||||
}
|
||||
|
||||
bool util_proc_get_folder_path_executable(char* buffer, size_t size)
|
||||
{
|
||||
if (!util_proc_get_path_executable(buffer, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t pos = strlen(buffer) - 1;
|
||||
|
||||
// If executable in the root folder, keep the single /
|
||||
while (pos > 0 && buffer[pos] != '/') {
|
||||
buffer[pos] = '\0';
|
||||
pos--;
|
||||
}
|
||||
|
||||
// delete /
|
||||
if (pos > 0) {
|
||||
buffer[pos] = '\0';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool util_proc_get_folder_path_executable_no_ld_linux(char* buffer, size_t size)
|
||||
{
|
||||
if (!util_proc_get_path_executable(buffer, size)) {
|
||||
log_error("Getting executable path failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (util_str_ends_with(buffer, "ld-linux.so.2")) {
|
||||
int argc;
|
||||
char* args = util_sys_read_entries_from_proc_file("/proc/self/cmdline", &argc);
|
||||
|
||||
char* tok = strtok(args, "\n");
|
||||
bool awaiting_value = false;
|
||||
|
||||
// Skip first token, ld-linux binary
|
||||
tok = strtok(NULL, "\n");
|
||||
|
||||
while (tok != NULL) {
|
||||
// Check if the next arg is declaring an option key for ld-linux
|
||||
if (util_str_starts_with(tok, "--")) {
|
||||
awaiting_value = 1;
|
||||
} else if (awaiting_value) {
|
||||
// Just hit the awaited value for the previously skipped option key
|
||||
awaiting_value = false;
|
||||
} else {
|
||||
// Got executable arg passed to ld-linux
|
||||
if (strlen(tok) > size) {
|
||||
free(args);
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(buffer, tok);
|
||||
|
||||
// Remove executable name
|
||||
// If executable in the root folder, keep the single /
|
||||
size_t pos = strlen(buffer) - 1;
|
||||
while (pos > 0 && buffer[pos] != '/') {
|
||||
buffer[pos] = '\0';
|
||||
pos--;
|
||||
}
|
||||
|
||||
// delete /
|
||||
if (pos > 0) {
|
||||
buffer[pos] = '\0';
|
||||
}
|
||||
|
||||
free(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
tok = strtok(NULL, "\n");
|
||||
}
|
||||
|
||||
free(args);
|
||||
return false;
|
||||
} else {
|
||||
return util_proc_get_folder_path_executable(buffer, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void util_proc_log_info()
|
||||
{
|
||||
log_info("================================ Current process information ================================");
|
||||
|
||||
util_proc_log_current_process();
|
||||
util_sys_info_log_path_executable();
|
||||
util_proc_log_current_working_dir();
|
||||
util_sys_info_log_user();
|
||||
util_sys_info_log_running_as_root();
|
||||
util_sys_info_log_cmd_line_args();
|
||||
util_sys_info_log_env_vars();
|
||||
|
||||
log_info("================================================================================================");
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef UTIL_PROC_H
|
||||
#define UTIL_PROC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Check if the current process is running as a root user.
|
||||
*
|
||||
* @return True if running as root, false otherwise.
|
||||
*/
|
||||
bool util_proc_is_running_as_root();
|
||||
|
||||
/**
|
||||
* Get the full path of the executable of the current process (including the executable name).
|
||||
*
|
||||
* @param buffer Buffer to read the path into.
|
||||
* @param size Size of the buffer
|
||||
* @return True on success, false on failure, e.g. buffer too small.
|
||||
*/
|
||||
bool util_proc_get_path_executable(char* buffer, size_t size);
|
||||
|
||||
/**
|
||||
* Get the full path of the folder the executable of the current process is located in.
|
||||
*
|
||||
* @param buffer Buffer to read the path into.
|
||||
* @param size Size of the buffer
|
||||
* @return True on success, false on failure, e.g. buffer too small.
|
||||
*/
|
||||
bool util_proc_get_folder_path_executable(char* buffer, size_t size);
|
||||
|
||||
/**
|
||||
* Special version of util_proc_get_folder_path_executable that considers when the process is run explicitly with a
|
||||
* ld-linux binary. Returns the path of the application's executable and not ld-linux in that case.
|
||||
*
|
||||
* @param buffer Buffer to read the path into.
|
||||
* @param size Size of the buffer
|
||||
* @return True on success, false on failure, e.g. buffer too small.
|
||||
*/
|
||||
bool util_proc_get_folder_path_executable_no_ld_linux(char* buffer, size_t size);
|
||||
|
||||
/**
|
||||
* Log information about the current process to the console.
|
||||
*/
|
||||
void util_proc_log_info();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rand.h"
|
||||
|
||||
static int _util_rand_init = 0;
|
||||
|
||||
static void _util_rand_initialize()
|
||||
{
|
||||
if (!_util_rand_init) {
|
||||
_util_rand_init = 1;
|
||||
srand((unsigned int) time(NULL));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t util_rand_gen_32()
|
||||
{
|
||||
_util_rand_initialize();
|
||||
|
||||
return (uint32_t) rand();
|
||||
}
|
||||
|
||||
uint64_t util_rand_gen_64()
|
||||
{
|
||||
uint64_t value;
|
||||
|
||||
_util_rand_initialize();
|
||||
|
||||
value = 0;
|
||||
value |= (((uint64_t) rand()) << 32);
|
||||
value |= ((uint64_t) rand());
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef UTIL_RAND_H
|
||||
#define UTIL_RAND_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t util_rand_gen_32();
|
||||
|
||||
uint64_t util_rand_gen_64();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,185 @@
|
||||
#define LOG_MODULE "sock-tcp"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util/net.h"
|
||||
#include "util/log.h"
|
||||
#include "util/sock-tcp.h"
|
||||
|
||||
bool util_sock_tcp_is_connectable(const char* ipv4, uint32_t timeout_ms)
|
||||
{
|
||||
char* address;
|
||||
uint16_t port;
|
||||
|
||||
util_net_split_ipv4_str(ipv4, &address, &port);
|
||||
|
||||
bool res = util_sock_tcp_is_connectable2(address, port, timeout_ms);
|
||||
|
||||
free(address);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool util_sock_tcp_is_connectable2(const char* ipv4, uint16_t port, uint32_t timeout_ms)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
// store this IP address in sa:
|
||||
inet_pton(AF_INET, ipv4, &(sa.sin_addr));
|
||||
|
||||
return util_sock_tcp_is_connectable3(htonl(sa.sin_addr.s_addr), port, timeout_ms);
|
||||
}
|
||||
|
||||
bool util_sock_tcp_is_connectable3(uint32_t ipv4, uint16_t port, uint32_t timeout_ms)
|
||||
{
|
||||
int handle;
|
||||
struct sockaddr_in addr;
|
||||
fd_set fdset;
|
||||
struct timeval tv;
|
||||
bool res;
|
||||
|
||||
handle = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (handle == -1) {
|
||||
log_error("Creating socket (%X, %d) failed: %s", ipv4, port, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
fcntl(handle, F_SETFL, O_NONBLOCK);
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = htonl(ipv4);
|
||||
|
||||
connect(handle, (const struct sockaddr*) &addr, sizeof(addr));
|
||||
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(handle, &fdset);
|
||||
tv.tv_sec = (__time_t) (timeout_ms / 1000);
|
||||
tv.tv_usec = (__suseconds_t) ((timeout_ms % 1000) * 1000);
|
||||
|
||||
if (select(handle + 1, NULL, &fdset, NULL, &tv) == 1) {
|
||||
int so_error;
|
||||
socklen_t len = sizeof(so_error);
|
||||
|
||||
getsockopt(handle, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
||||
|
||||
if (so_error == 0) {
|
||||
res = true;
|
||||
} else {
|
||||
res = false;
|
||||
}
|
||||
} else {
|
||||
res = false;
|
||||
}
|
||||
|
||||
close(handle);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int util_sock_tcp_connect(const char* ipv4)
|
||||
{
|
||||
char* address;
|
||||
uint16_t port;
|
||||
|
||||
util_net_split_ipv4_str(ipv4, &address, &port);
|
||||
|
||||
int res = util_sock_tcp_connect2(address, port);
|
||||
|
||||
free(address);
|
||||
return res;
|
||||
}
|
||||
|
||||
int util_sock_tcp_connect2(const char* ipv4, uint16_t port)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
// store this IP address in sa:
|
||||
inet_pton(AF_INET, ipv4, &(sa.sin_addr));
|
||||
|
||||
return util_sock_tcp_connect3(htonl(sa.sin_addr.s_addr), port);
|
||||
}
|
||||
|
||||
int util_sock_tcp_connect3(uint32_t ipv4, uint16_t port)
|
||||
{
|
||||
int handle;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
handle = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (handle == -1) {
|
||||
log_error("Creating socket (%X, %d) failed: %s", ipv4, port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = htonl(ipv4);
|
||||
|
||||
if (connect(handle, (const struct sockaddr*) &addr, sizeof(addr)) == -1) {
|
||||
close(handle);
|
||||
log_error("Connecting to %X, %d failed: %s", ipv4, port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_debug("Connected to %X on port %d", ipv4, port);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
ssize_t util_sock_tcp_send(int handle, void* buffer, size_t size, uint32_t timeout_ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = (__time_t) (timeout_ms / 1000);
|
||||
tv.tv_usec = (__suseconds_t) ((timeout_ms % 1000) * 1000);
|
||||
|
||||
setsockopt(handle, SOL_SOCKET, SO_SNDTIMEO, (const char*) &tv, sizeof(tv));
|
||||
|
||||
ssize_t length = send(handle, buffer, size, 0);
|
||||
|
||||
if (length < 0) {
|
||||
// same as EWOULDBLOCK
|
||||
if (errno == EAGAIN) {
|
||||
// no data for non blocking
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
ssize_t util_sock_tcp_recv(int handle, void* buffer, size_t size, uint32_t timeout_ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = (__time_t) (timeout_ms / 1000);
|
||||
tv.tv_usec = (__suseconds_t) ((timeout_ms % 1000) * 1000);
|
||||
|
||||
setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv));
|
||||
|
||||
ssize_t length = recv(handle, buffer, size, 0);
|
||||
|
||||
if (length < 0) {
|
||||
// same as EWOULDBLOCK
|
||||
if (errno == EAGAIN) {
|
||||
// no data for non blocking
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void util_sock_tcp_close(int handle)
|
||||
{
|
||||
close(handle);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by on 9/16/18.
|
||||
//
|
||||
|
||||
#ifndef UTIL_SOCK_TCP_H
|
||||
#define UTIL_SOCK_TCP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
bool util_sock_tcp_is_connectable(const char* ipv4, uint32_t timeout_ms);
|
||||
|
||||
bool util_sock_tcp_is_connectable2(const char* ipv4, uint16_t port, uint32_t timeout_ms);
|
||||
|
||||
bool util_sock_tcp_is_connectable3(uint32_t ipv4, uint16_t port, uint32_t timeout_ms);
|
||||
|
||||
int util_sock_tcp_connect(const char* ipv4);
|
||||
|
||||
int util_sock_tcp_connect2(const char* ipv4, uint16_t port);
|
||||
|
||||
int util_sock_tcp_connect3(uint32_t ipv4, uint16_t port);
|
||||
|
||||
ssize_t util_sock_tcp_send(int handle, void* buffer, size_t size, uint32_t timeout_ms);
|
||||
|
||||
ssize_t util_sock_tcp_recv(int handle, void* buffer, size_t size, uint32_t timeout_ms);
|
||||
|
||||
void util_sock_tcp_close(int handle);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,212 @@
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
bool util_str_starts_with(const char* haystack, const char* needle)
|
||||
{
|
||||
size_t haystack_len;
|
||||
size_t needle_len;
|
||||
|
||||
haystack_len = strlen(haystack);
|
||||
needle_len = strlen(needle);
|
||||
|
||||
if (needle_len > haystack_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !memcmp(haystack, needle, needle_len);
|
||||
}
|
||||
|
||||
bool util_str_ends_with(const char* haystack, const char* needle)
|
||||
{
|
||||
size_t haystack_len;
|
||||
size_t needle_len;
|
||||
|
||||
haystack_len = strlen(haystack);
|
||||
needle_len = strlen(needle);
|
||||
|
||||
if (needle_len > haystack_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !memcmp(&haystack[haystack_len - needle_len], needle, needle_len);
|
||||
}
|
||||
|
||||
size_t util_str_format(char* buf, size_t nchars, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
size_t result;
|
||||
|
||||
va_start(ap, fmt);
|
||||
result = util_str_vformat(buf, nchars, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t util_str_vformat(char* buf, size_t nchars, const char* fmt, va_list ap)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = vsnprintf(buf, nchars, fmt, ap);
|
||||
|
||||
if (result >= (int) nchars || result < 0) {
|
||||
abort();
|
||||
}
|
||||
|
||||
return (size_t) result;
|
||||
}
|
||||
|
||||
void util_str_cat(char* dest, size_t dnchars, const char* src)
|
||||
{
|
||||
size_t dlen;
|
||||
size_t slen;
|
||||
|
||||
dlen = strlen(dest);
|
||||
slen = strlen(src);
|
||||
|
||||
if (dlen + slen >= dnchars) {
|
||||
abort();
|
||||
}
|
||||
|
||||
memcpy(dest + dlen, src, (slen + 1) * sizeof(char));
|
||||
}
|
||||
|
||||
void util_str_cpy(char* dest, size_t dnchars, const char* src)
|
||||
{
|
||||
size_t slen;
|
||||
|
||||
slen = strlen(src);
|
||||
|
||||
if (slen >= dnchars) {
|
||||
abort();
|
||||
}
|
||||
|
||||
memcpy(dest, src, (slen + 1) * sizeof(char));
|
||||
}
|
||||
|
||||
char* util_str_merge(const char* str1, const char* str2)
|
||||
{
|
||||
char* out;
|
||||
size_t str1_len;
|
||||
size_t str2_len;
|
||||
size_t total;
|
||||
|
||||
str1_len = strlen(str1);
|
||||
str2_len = strlen(str2);
|
||||
total = str1_len + str2_len + 1;
|
||||
|
||||
out = util_xmalloc(total);
|
||||
memcpy(out, str1, str1_len);
|
||||
memcpy(out + str1_len, str2, str2_len);
|
||||
out[total - 1] = '\0';
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
char* util_str_dup(const char* str)
|
||||
{
|
||||
char *dest;
|
||||
size_t nbytes;
|
||||
|
||||
log_assert(str);
|
||||
|
||||
nbytes = strlen(str) + 1;
|
||||
dest = util_xmalloc(nbytes);
|
||||
memcpy(dest, str, nbytes);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void util_str_trim(char* str)
|
||||
{
|
||||
char *pos;
|
||||
|
||||
for (pos = str + strlen(str) - 1 ; pos > str ; pos--) {
|
||||
if (!isspace(*pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
*pos = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
char* util_str_buffer(const uint8_t* buf, size_t len)
|
||||
{
|
||||
char* ret;
|
||||
size_t pos;
|
||||
|
||||
pos = 0;
|
||||
ret = util_xmalloc(len * 3 + 1);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t tmp = buf[i];
|
||||
|
||||
pos += sprintf(ret + pos, "%02X ", tmp);
|
||||
}
|
||||
|
||||
ret[len * 3] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char** util_str_split(const char* str, const char* delim, size_t* count)
|
||||
{
|
||||
char** toks;
|
||||
char* copy;
|
||||
char* cur;
|
||||
size_t counter;
|
||||
|
||||
log_assert(str);
|
||||
log_assert(delim);
|
||||
log_assert(count);
|
||||
|
||||
*count = 0;
|
||||
|
||||
copy = util_str_dup(str);
|
||||
cur = strtok(copy, delim);
|
||||
|
||||
// First iteration, just count items
|
||||
while (cur != NULL) {
|
||||
(*count)++;
|
||||
cur = strtok(NULL, delim);
|
||||
}
|
||||
|
||||
util_xfree((void**) ©);
|
||||
|
||||
toks = util_xmalloc(sizeof(char*) * (*count));
|
||||
|
||||
copy = util_str_dup(str);
|
||||
cur = strtok(copy, delim);
|
||||
|
||||
counter = 0;
|
||||
|
||||
// Second iteration, grab tokens
|
||||
while (cur != NULL) {
|
||||
toks[counter] = util_str_dup(cur);
|
||||
counter++;
|
||||
|
||||
cur = strtok(NULL, delim);
|
||||
}
|
||||
|
||||
util_xfree((void**) ©);
|
||||
|
||||
return toks;
|
||||
}
|
||||
|
||||
void util_str_free_split(char** split, size_t count)
|
||||
{
|
||||
log_assert(split);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
util_xfree((void**) &split[i]);
|
||||
}
|
||||
|
||||
util_xfree((void**) &split);
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
#ifndef UTIL_STR_H
|
||||
#define UTIL_STR_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Check if a string starts with a specific (sub-) string
|
||||
*
|
||||
* @param haystack String to check
|
||||
* @param needle String to compare with start of haystack
|
||||
* @return True if haystack ends with needle, false otherwise
|
||||
*/
|
||||
bool util_str_starts_with(const char* haystack, const char* needle);
|
||||
|
||||
/**
|
||||
* Check if a string ends with a specific (sub-) string
|
||||
*
|
||||
* @param haystack String to check
|
||||
* @param needle String to compare with end of haystack
|
||||
* @return True if haystack ends with needle, false otherwise
|
||||
*/
|
||||
bool util_str_ends_with(const char* haystack, const char* needle);
|
||||
|
||||
/**
|
||||
* Print a formated string to a buffer
|
||||
*
|
||||
* @param buf Allocated buffer to print to
|
||||
* @param nchars Size of allocated buffer
|
||||
* @param fmt Format of the string to print
|
||||
* @param ... Arguments for the format string
|
||||
* @return Size of the string printed to the buffer
|
||||
*/
|
||||
size_t util_str_format(char* buf, size_t nchars, const char* fmt, ...);
|
||||
|
||||
/**
|
||||
* Print a formated string to a buffer
|
||||
*
|
||||
* @param buf Allocated buffer to print to
|
||||
* @param nchars Size of allocated buffer
|
||||
* @param fmt Format of the string to print
|
||||
* @param ap Argument list for format
|
||||
* @return Size of the string printed to the buffer
|
||||
*/
|
||||
size_t util_str_vformat(char* buf, size_t nchars, const char* fmt, va_list ap);
|
||||
|
||||
/**
|
||||
* Concat a string to another one
|
||||
*
|
||||
* @param dest Destination to append to
|
||||
* @param dnchars Size of dest buffer
|
||||
* @param src Source string to concat to dest
|
||||
*/
|
||||
void util_str_cat(char* dest, size_t dnchars, const char* src);
|
||||
|
||||
/**
|
||||
* Copy a string
|
||||
*
|
||||
* @param dest Destination to copy to
|
||||
* @param dnchars Size of destination
|
||||
* @param src Source string to copy from (to dest)
|
||||
*/
|
||||
void util_str_cpy(char* dest, size_t dnchars, const char* src);
|
||||
|
||||
/**
|
||||
* Merge to strings (str1 + str2)
|
||||
*
|
||||
* @param str1 String 1 to merge
|
||||
* @param str2 String 2 to merge
|
||||
* @return Newly allocated merged string: str1 + str2. Caller has to manage
|
||||
* memory returned
|
||||
*/
|
||||
char* util_str_merge(const char* str1, const char* str2);
|
||||
|
||||
/**
|
||||
* Duplicate a string
|
||||
*
|
||||
* @param src String to duplicate
|
||||
* @return Duplicated string (newly allocated memory). Caller has to manage
|
||||
* memory returned
|
||||
*/
|
||||
char* util_str_dup(const char* src);
|
||||
|
||||
/**
|
||||
* Remove trailing spaces of a string
|
||||
*
|
||||
* @param str String to trim
|
||||
*/
|
||||
void util_str_trim(char* str);
|
||||
|
||||
/**
|
||||
* Print a buffer as hex digits to a string
|
||||
*
|
||||
* @param buf Buffer to print
|
||||
* @param len Length of the buffer
|
||||
* @return String representation of the buffer. Caller has to manage returned
|
||||
* memory
|
||||
*/
|
||||
char* util_str_buffer(const uint8_t* buf, size_t len);
|
||||
|
||||
char** util_str_split(const char* str, const char* delim, size_t* count);
|
||||
|
||||
void util_str_free_split(char** split, size_t count);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#define LOG_MODULE "util-sys-info"
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static void util_sys_info_log_kernel()
|
||||
{
|
||||
struct utsname buf;
|
||||
|
||||
if (uname(&buf)) {
|
||||
log_error("Reading kernel version failed");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info("%s|%s|%s|%s|%s", buf.sysname, buf.nodename, buf.release, buf.version, buf.machine);
|
||||
}
|
||||
|
||||
void util_sys_info_log()
|
||||
{
|
||||
util_sys_info_log_kernel();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef UTILSYS_INFO_H
|
||||
#define UTIL_SYS_INFO_H
|
||||
|
||||
/**
|
||||
* Print system information to the log (kernel, cpu, ram etc)
|
||||
*/
|
||||
void util_sys_info_log();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
#define LOG_MODULE "util-test"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "mem.h"
|
||||
#include "test.h"
|
||||
#include "str.h"
|
||||
|
||||
struct util_test_case {
|
||||
struct util_list_node head;
|
||||
char* name;
|
||||
util_test_func_t func;
|
||||
};
|
||||
|
||||
static size_t util_test_count;
|
||||
static struct util_list util_test_list;
|
||||
|
||||
void util_test_init()
|
||||
{
|
||||
util_test_count = 0;
|
||||
util_list_init(&util_test_list);
|
||||
|
||||
log_info("Initialized");
|
||||
}
|
||||
|
||||
void util_test_add(const char* name, util_test_func_t func)
|
||||
{
|
||||
struct util_test_case* test;
|
||||
|
||||
test = util_xmalloc(sizeof(struct util_test_case));
|
||||
|
||||
test->name = util_str_dup(name);
|
||||
test->func = func;
|
||||
|
||||
util_list_append(&util_test_list, &test->head);
|
||||
util_test_count++;
|
||||
|
||||
log_info("Added %s, func %p", name, func);
|
||||
}
|
||||
|
||||
void util_test_exec()
|
||||
{
|
||||
log_info("Exec %d tests", util_test_count);
|
||||
|
||||
struct util_list_node* pos;
|
||||
struct util_test_case* test;
|
||||
size_t counter = 1;
|
||||
|
||||
for (pos = util_test_list.head; pos != NULL; pos = pos->next) {
|
||||
test = containerof(pos, struct util_test_case, head);
|
||||
|
||||
log_debug("%d/%d Executing %s...", counter++, util_test_count, test->name);
|
||||
|
||||
if (!test->func()) {
|
||||
log_error("%s failed", test->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_info("All tests passed");
|
||||
}
|
||||
|
||||
void util_test_shutdown()
|
||||
{
|
||||
log_info("Shutdown");
|
||||
|
||||
struct util_list_node* pos;
|
||||
struct util_test_case* test;
|
||||
|
||||
// TODO list elems cleanup?
|
||||
|
||||
for (pos = util_test_list.head; pos != NULL; pos = pos->next) {
|
||||
test = containerof(pos, struct util_test_case, head);
|
||||
|
||||
free(test->name);
|
||||
}
|
||||
}
|
||||
|
||||
bool util_test_assert_exp(bool exp, const char* exp_str, const char* file, size_t line_num)
|
||||
{
|
||||
if (!exp) {
|
||||
log_error("Assert failed [%s:%d]: Expression %s failed", file, line_num, exp_str);
|
||||
return false;
|
||||
} else {
|
||||
log_debug("Assert successful [%s:%d]: Expression %s", file, line_num, exp_str);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool util_test_assert_uint(size_t val_exp, size_t val_actual, const char* exp, const char* actual, const char* file,
|
||||
size_t line_num)
|
||||
{
|
||||
if (val_exp != val_actual) {
|
||||
log_error("Assert failed [%s:%d]: %d != %d of expected %s and actual %s", file, line_num, val_exp, val_actual,
|
||||
exp, actual);
|
||||
return false;
|
||||
} else {
|
||||
log_debug("Assert successful [%s:%d]: %d == %d of expected %s and actual %s", file, line_num, val_exp,
|
||||
val_actual, exp, actual);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool util_test_assert_str(const char* val_exp, const char* val_actual, const char* exp, const char* actual,
|
||||
const char* file, size_t line_num)
|
||||
{
|
||||
if (strcmp(val_exp, val_actual) != 0) {
|
||||
log_error("Assert failed [%s:%d]: %s != %s of expected %s and actual %s", file, line_num, val_exp, val_actual,
|
||||
exp, actual);
|
||||
return false;
|
||||
} else {
|
||||
log_debug("Assert successful [%s:%d]: %s == %s of expected %s and actual %s", file, line_num, val_exp,
|
||||
val_actual, exp, actual);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool util_test_assert_data(const void* data_exp, const void* data_actual, size_t len, const char* exp,
|
||||
const char* actual, const char* file, size_t line_num)
|
||||
{
|
||||
bool ret;
|
||||
char* data_exp_str = util_str_buffer(data_exp, len);
|
||||
char* data_actual_str = util_str_buffer(data_actual, len);
|
||||
|
||||
if (memcmp(data_exp, data_actual, len) != 0) {
|
||||
log_error("Assert failed [%s:%d]: %s != %s of expected %s and actual %s", file, line_num, data_exp_str,
|
||||
data_actual_str, exp, actual);
|
||||
|
||||
uint32_t cnt = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
/* print the first 5 diffs, only */
|
||||
if (((const uint8_t*) data_exp)[i] != ((const uint8_t*) data_actual)[i]) {
|
||||
if (cnt < 5) {
|
||||
log_error("pos %d: %x != %x", i, ((const uint8_t*) data_exp)[i], ((const uint8_t*) data_actual)[i]);
|
||||
}
|
||||
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt > 5) {
|
||||
log_error("...and more different bytes, total diffs %d", cnt);
|
||||
}
|
||||
|
||||
ret = false;
|
||||
} else {
|
||||
log_debug("Assert successful [%s:%d]: %s == %s of expected %s and actual %s", file, line_num, data_exp_str,
|
||||
data_actual_str, exp, actual);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
free(data_exp_str);
|
||||
free(data_actual_str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef UTIL_TEST_H
|
||||
#define UTIL_TEST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "defs.h"
|
||||
#include "log.h"
|
||||
|
||||
#define UTIL_TEST_MAIN_START \
|
||||
int main() \
|
||||
{ \
|
||||
log_info("Unit test %s", FILENAME); \
|
||||
util_test_init(); \
|
||||
|
||||
#define UTIL_TEST_MAIN_END \
|
||||
util_test_exec(); \
|
||||
util_test_shutdown(); \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
#define UTIL_TEST_FUNC_START(name) \
|
||||
bool name() \
|
||||
{ \
|
||||
|
||||
#define UTIL_TEST_FUNC_END \
|
||||
return true; \
|
||||
} \
|
||||
|
||||
#define UTIL_TEST_ADD(func) util_test_add(STRINGIFY(func), func);
|
||||
|
||||
#define UTIL_TEST_ASSERT(exp) \
|
||||
do { \
|
||||
if (!util_test_assert_exp(exp, STRINGIFY(exp), FILENAME, __LINE__)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0); \
|
||||
|
||||
#define UTIL_TEST_ASSERT_UINT(exp, actual) \
|
||||
do { \
|
||||
if (!util_test_assert_uint(exp, actual, STRINGIFY(exp), STRINGIFY(actual), FILENAME, __LINE__)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0); \
|
||||
|
||||
#define UTIL_TEST_ASSERT_STR(exp, actual) \
|
||||
do { \
|
||||
if (!util_test_assert_str(exp, actual, STRINGIFY(exp), STRINGIFY(actual), FILENAME, __LINE__)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0); \
|
||||
|
||||
#define UTIL_TEST_ASSERT_DATA(exp, actual, len) \
|
||||
do { \
|
||||
if (!util_test_assert_data(exp, actual, len, STRINGIFY(exp), STRINGIFY(actual), FILENAME, __LINE__)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0); \
|
||||
|
||||
typedef bool (*util_test_func_t)();
|
||||
|
||||
void util_test_init();
|
||||
|
||||
void util_test_add(const char* name, util_test_func_t func);
|
||||
|
||||
void util_test_exec();
|
||||
|
||||
void util_test_shutdown();
|
||||
|
||||
bool util_test_assert_exp(bool exp, const char* exp_str, const char* file, size_t line_num);
|
||||
|
||||
bool util_test_assert_uint(size_t val_exp, size_t val_actual, const char* exp, const char* actual, const char* file,
|
||||
size_t line_num);
|
||||
|
||||
bool util_test_assert_str(const char* val_exp, const char* val_actual, const char* exp, const char* actual,
|
||||
const char* file, size_t line_num);
|
||||
|
||||
bool util_test_assert_data(const void* data_exp, const void* data_actual, size_t len, const char* exp,
|
||||
const char* actual, const char* file, size_t line_num);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "util/time.h"
|
||||
|
||||
#define SEC_PER_DAY 86400
|
||||
#define SEC_PER_HOUR 3600
|
||||
#define SEC_PER_MIN 60
|
||||
|
||||
void util_time_sleep_ns(uint64_t time_ns)
|
||||
{
|
||||
struct timespec t;
|
||||
t.tv_sec = (__time_t) (time_ns / (1000 * 1000 * 1000));
|
||||
t.tv_nsec = (__syscall_slong_t) time_ns % (1000 * 1000 * 1000);
|
||||
|
||||
nanosleep(&t, NULL);
|
||||
}
|
||||
|
||||
void util_time_sleep_us(uint64_t time_us)
|
||||
{
|
||||
struct timespec t;
|
||||
t.tv_sec = (__time_t) (time_us / (1000 * 1000));
|
||||
t.tv_nsec = (__syscall_slong_t) (time_us % (1000 * 1000)) * 1000;
|
||||
|
||||
nanosleep(&t, NULL);
|
||||
}
|
||||
|
||||
void util_time_sleep_ms(uint64_t time_ms)
|
||||
{
|
||||
struct timespec t;
|
||||
t.tv_sec = (__time_t) (time_ms / 1000);
|
||||
t.tv_nsec = (__syscall_slong_t) (time_ms % 1000) * 1000 * 1000;
|
||||
|
||||
nanosleep(&t, NULL);
|
||||
}
|
||||
|
||||
void util_time_sleep_sec(uint64_t time_sec)
|
||||
{
|
||||
struct timespec t;
|
||||
t.tv_sec = (__time_t) time_sec;
|
||||
t.tv_nsec = 0;
|
||||
|
||||
nanosleep(&t, NULL);
|
||||
}
|
||||
|
||||
void util_time_get_current_time(struct util_time_timestamp* timestamp)
|
||||
{
|
||||
if (!timestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm* tm = localtime(&t);
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
timestamp->millisec = tv.tv_usec / 1000;
|
||||
timestamp->sec = tm->tm_sec;
|
||||
timestamp->min = tm->tm_min;
|
||||
timestamp->hour = tm->tm_hour;
|
||||
timestamp->day = tm->tm_mday;
|
||||
// Months since January [0, 11]
|
||||
timestamp->month = tm->tm_mon + 1;
|
||||
// Years since 1900
|
||||
timestamp->year = tm->tm_year + 1900;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef UTIL_TIME_H
|
||||
#define UTIL_TIME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct util_time_timestamp {
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t min;
|
||||
uint8_t sec;
|
||||
uint16_t millisec;
|
||||
};
|
||||
|
||||
void util_time_sleep_ns(uint64_t time_ns);
|
||||
|
||||
void util_time_sleep_us(uint64_t time_us);
|
||||
|
||||
void util_time_sleep_ms(uint64_t time_ms);
|
||||
|
||||
void util_time_sleep_sec(uint64_t time_sec);
|
||||
|
||||
void util_time_get_current_time(struct util_time_timestamp* timestamp);
|
||||
|
||||
#endif //UTIL_TIME_H
|
||||
Reference in New Issue
Block a user