-
dmitriy.gerasimov authoredd4059992
/*
* Authors:
* Dmitriy A. Gearasimov <kahovski@gmail.com>
* DeM Labs Inc. https://demlabs.net
* DeM Labs Open source community https://github.com/demlabsinc
* Copyright (c) 2017-2018
* All rights reserved.
This file is part of DAP (Deus Applications Prototypes) the open source project
DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DAP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with any DAP based project. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef DAP_OS_ANDROID
#include <android/log.h>
#endif
#ifndef _WIN32
#include <pthread.h>
#include <syslog.h>
// Quick and dirty, I'm not sure but afair somewhere it was in UNIX systems too
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
#else
#include <stdlib.h>
#include <windows.h>
#include <process.h>
typedef HANDLE pthread_mutex_t;
#define popen _popen
#define pclose _pclose
#define pipe(pfds) _pipe(pfds, 4096, 0x8000)
#define PTHREAD_MUTEX_INITIALIZER 0
int pthread_mutex_lock(HANDLE **obj)
{
return (( *obj = (HANDLE) CreateMutex(0, 1, 0) ) == NULL) ? 0 : 1;
}
int pthread_mutex_unlock(HANDLE *obj) {
return (ReleaseMutex(obj) == 0) ? 0 : 1;
}
#endif
#include <time.h> /* 'nanosleep' */
#include <unistd.h> /* 'pipe', 'read', 'write' */
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "dap_common.h"
#include "dap_strfuncs.h"
#include "dap_string.h"
#include "dap_list.h"
#define LAST_ERROR_MAX 255
#define LOG_TAG "dap_common"
static char s_last_error[LAST_ERROR_MAX] = {0};
static enum dap_log_level dap_log_level = L_DEBUG;
static FILE * s_log_file = NULL;
static char log_tag_fmt_str[10];
/**
* @brief set_log_level Sets the logging level
* @param[in] ll logging level
*/
void dap_log_level_set(enum dap_log_level ll) {
dap_log_level = ll;
}
enum dap_log_level dap_log_level_get(void) {
return dap_log_level ;
}
/**
* @brief dap_set_log_tag_width Sets the length of the label
* @param[in] width Length not more than 99
*/
void dap_set_log_tag_width(size_t width) {
if (width > 99) {
fprintf(stderr,"Can't set width %zd", width);
return;
}
// construct new log_tag_fmt_str
strcpy(log_tag_fmt_str, "[%");
strcat(log_tag_fmt_str, dap_itoa((int)width));
strcat(log_tag_fmt_str, "s]\t");
}
/**
* @brief dap_common_init initialise
* @param[in] a_log_file
* @return
*/
int dap_common_init(const char * a_log_file)
{
srand((unsigned int)time(NULL));
// init default log tag 8 width
strcpy(log_tag_fmt_str, "[%8s]\t");
if (a_log_file) {
s_log_file = fopen(a_log_file , "a");
if(s_log_file == NULL) {
fprintf(stderr,"Can't open log file %s to append\n", a_log_file);
return -1;
}
}
// Set max items in log list
dap_log_set_max_item(10);
return 0;
}
/**
* @brief dap_common_deinit Deinitialise
*/
void dap_common_deinit()
{
if(s_log_file) fclose(s_log_file);
}
// list of logs
static dap_list_t *s_list_logs = NULL;
// for separate access to logs
static pthread_mutex_t s_list_logs_mutex = PTHREAD_MUTEX_INITIALIZER;
static unsigned int s_max_items = 1000;
/*
* Set max items in log list
*/
void dap_log_set_max_item(unsigned int a_max)
{
if(a_max>0)
s_max_items = a_max;
}
/*
* Get logs from list
*/
char *dap_log_get_item(time_t a_start_time, int a_limit)
{
int l_count = 0;
pthread_mutex_lock(&s_list_logs_mutex);
dap_list_t *l_list = s_list_logs;
l_list = dap_list_last(l_list);
// find first item
while(l_list) {
dap_list_logs_item_t *l_item = (dap_list_logs_item_t*) l_list->data;
if(a_start_time > l_item->t) {
l_list = dap_list_next(l_list);
break;
}
l_count++;
l_list = dap_list_previous(l_list);
}
// no new logs
if(!l_count){
pthread_mutex_unlock(&s_list_logs_mutex);
return NULL;
}
// if need all list
if(!l_list)
l_list = s_list_logs;
// create return string
dap_string_t *l_string = dap_string_new("");
l_count = 0;
while(l_list && a_limit > l_count) {
dap_list_logs_item_t *l_item = (dap_list_logs_item_t*) l_list->data;
//dap_string_append_printf(l_string, "%lld;%s\n", (int64_t) l_item->t, l_item->str);
l_list = dap_list_next(l_list);
l_count++;
if(l_list && a_limit > l_count)
dap_string_append_printf(l_string, "%s\n", l_item->str);
else// last item
dap_string_append_printf(l_string, "%s", l_item->str);
}
pthread_mutex_unlock(&s_list_logs_mutex);
char *l_ret_str = dap_string_free(l_string, false);
return l_ret_str;
}
// save log to list
static void log_add_to_list(time_t a_t, const char *a_time_str, const char * a_log_tag, enum dap_log_level a_ll,
const char * a_format, va_list a_ap)
{
pthread_mutex_lock(&s_list_logs_mutex);
dap_string_t *l_string = dap_string_new("");
dap_string_append_printf(l_string, "[%s]\t", a_time_str);
if(a_ll == L_DEBUG) {
l_string = dap_string_append(l_string, "[DBG]\t");
} else if(a_ll == L_INFO) {
l_string = dap_string_append(l_string, "[INF]\t");
} else if(a_ll == L_NOTICE) {
l_string = dap_string_append(l_string, "[ * ]\t");
} else if(a_ll == L_WARNING) {
l_string = dap_string_append(l_string, "[WRN]\t");
} else if(a_ll == L_ERROR) {
l_string = dap_string_append(l_string, "[ERR]\t");
} else if(a_ll == L_CRITICAL) {
l_string = dap_string_append(l_string, "[!!!]\t");
}
if(a_log_tag != NULL) {
dap_string_append_printf(l_string, log_tag_fmt_str, a_log_tag);
}
dap_string_append_vprintf(l_string, a_format, a_ap);
dap_list_logs_item_t *l_item = DAP_NEW(dap_list_logs_item_t);
l_item->t = a_t;
l_item->str = dap_string_free(l_string, false);
s_list_logs = dap_list_append(s_list_logs, l_item);
// remove old items
unsigned int l_count = dap_list_length(s_list_logs);
if(l_count > s_max_items) {
// remove items from the beginning
for(unsigned int i = 0; i < l_count - s_max_items; i++) {
s_list_logs = dap_list_remove(s_list_logs, s_list_logs->data);
}
}
pthread_mutex_unlock(&s_list_logs_mutex);
}
/**
* @brief _log_it Writes information to the log
* @param[in] log_tag Tag
* @param[in] ll Log level
* @param[in] format
*/
void _log_it(const char * log_tag,enum dap_log_level ll, const char * format,...)
{
if(ll<dap_log_level)
return;
va_list ap;
va_start(ap,format);
_vlog_it(log_tag,ll, format,ap);
va_end(ap);
}
void _vlog_it(const char * log_tag,enum dap_log_level ll, const char * format,va_list ap)
{
va_list ap2,ap3;
static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
#ifdef DAP_OS_ANDROID
char buf[4096];
vsnprintf(buf,sizeof(buf),format,ap);
switch (ll) {
case L_INFO:
__android_log_write(ANDROID_LOG_INFO,DAP_BRAND,buf);
break;
case L_WARNING:
__android_log_write(ANDROID_LOG_WARN,DAP_BRAND,buf);
break;
case L_ERROR:
__android_log_write(ANDROID_LOG_ERROR,DAP_BRAND,buf);
break;
case L_CRITICAL:
__android_log_write(ANDROID_LOG_FATAL,DAP_BRAND,buf);
abort();
break;
case L_DEBUG:
default:
__android_log_write(ANDROID_LOG_DEBUG,DAP_BRAND,buf);
}
#endif
va_copy(ap2,ap);
va_copy(ap3,ap);
time_t t=time(NULL);
struct tm* tmp=localtime(&t);
static char s_time[1024]={0};
strftime(s_time,sizeof(s_time),"%x-%X",tmp);
if (s_log_file ) fprintf(s_log_file,"[%s] ",s_time);
printf("[%s] ",s_time);
if(ll==L_DEBUG){
if (s_log_file ) fprintf(s_log_file,"[DBG] ");
printf( "\x1b[37;2m[DBG] ");
}else if(ll==L_INFO){
if (s_log_file ) fprintf(s_log_file,"[INF] ");
printf("\x1b[32;2m[INF] ");
}else if(ll==L_NOTICE){
if (s_log_file ) fprintf(s_log_file,"[ * ] ");
printf("\x1b[32m[ * ] ");
}else if(ll==L_WARNING){
if (s_log_file ) fprintf(s_log_file,"[WRN] ");
printf("\x1b[31;2m[WRN] ");
}else if(ll==L_ERROR){
if (s_log_file ) fprintf(s_log_file,"[ERR] ");
printf("\x1b[31m[ERR] ");
}else if(ll==L_CRITICAL){
if (s_log_file ) fprintf(s_log_file,"[!!!] ");
printf("\x1b[1;5;31m[!!!] ");
}
if(log_tag != NULL) {
if (s_log_file ) fprintf(s_log_file,log_tag_fmt_str,log_tag);
printf(log_tag_fmt_str,log_tag);
}
if (s_log_file ) vfprintf(s_log_file,format,ap);
vprintf(format,ap2);
if (s_log_file ) fprintf(s_log_file,"\n");
printf("\x1b[0m\n");
va_end(ap2);
// save log to list
log_add_to_list(t, s_time, log_tag, ll, format, ap3);
va_end(ap3);
if (s_log_file ) fflush(s_log_file);
fflush(stdout);
pthread_mutex_unlock(&mutex);
}
/**
* @brief log_error Error log
* @return
*/
const char * log_error()
{
return s_last_error;
}
#define INT_DIGITS 19 /* enough for 64 bit integer */
/**
* @brief itoa The function converts an integer num to a string equivalent and places the result in a string
* @param[in] i number
* @return
*/
char *dap_itoa(int i)
{
/* Room for INT_DIGITS digits, - and '\0' */
static char buf[INT_DIGITS + 2];
char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */
if (i >= 0) {
do {
*--p = '0' + (i % 10);
i /= 10;
} while (i != 0);
return p;
}
else { /* i < 0 */
do {
*--p = '0' - (i % 10);
i /= 10;
} while (i != 0);
*--p = '-';
}
return p;
}
/**
* @brief time_to_rfc822 Convert time_t to string with RFC822 formatted date and time
* @param[out] out Output buffer
* @param[out] out_size_mac Maximum size of output buffer
* @param[in] t UNIX time
* @return Length of resulting string if ok or lesser than zero if not
*/
int dap_time_to_str_rfc822(char * out, size_t out_size_max, time_t t)
{
struct tm *tmp;
tmp=localtime(&t);
if(tmp== NULL){
log_it(L_ERROR,"Can't convert data from unix fromat to structured one");
return -2;
}else{
int ret;
ret=strftime(out, out_size_max,"%a, %d %b %y %T %z",tmp);
//free(tmp);
if(ret>0){
return ret;
}else{
log_it(L_ERROR,"Can't print formatted time in string");
return -1;
}
}
}
static int breaker_set[2] = { -1, -1 };
static int initialized = 0;
static struct timespec break_latency = {0, 1 * 1000 * 1000 };
int get_select_breaker()
{
if (!initialized)
{
if (pipe(breaker_set) < 0) return -1;
else initialized = 1;
}
return breaker_set[0];
}
int send_select_break()
{
if (!initialized) return -1;
char buffer[1];
if (write(breaker_set[1], "\0", 1) <= 0) return -1;
nanosleep(&break_latency, NULL);
if (read(breaker_set[0], buffer, 1) <= 0 || buffer[0] != '\0') return -1;
return 0;
}
#ifdef ANDROID1
static u_long myNextRandom = 1;
double atof(const char *nptr)
{
return (strtod(nptr, NULL));
}
int rand(void)
{
return (int)((myNextRandom = (1103515245 * myNextRandom) + 12345) % ((u_long)RAND_MAX + 1));
}
void srand(u_int seed)
{
myNextRandom = seed;
}
#endif
/**
* @brief exec_with_ret Executes a command with result return
* @param[in] a_cmd Command
* @return Result
*/
char * exec_with_ret(const char * a_cmd)
{
FILE * fp;
size_t buf_len = 0;
char buf[4096] = {0};
fp= popen(a_cmd, "r");
if (!fp) {
goto FIN;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
buf_len=strlen(buf);
if(buf[buf_len-1] =='\n')buf[buf_len-1] ='\0';
FIN:
return strdup(buf);
}
/**
* @brief exec_with_ret_multistring performs a command with a result return in the form of a multistring
* @param[in] a_cmd Coomand
* @return Return
*/
char * exec_with_ret_multistring(const char * a_cmd)
{
FILE * fp;
size_t buf_len = 0;
char buf[4096] = {0};
fp= popen(a_cmd, "r");
if (!fp) {
goto FIN;
}
memset(buf,0,sizeof(buf));
char retbuf[4096] = {0};
while(fgets(buf,sizeof(buf)-1,fp)) {
strcat(retbuf, buf);
}
pclose(fp);
buf_len=strlen(retbuf);
if(retbuf[buf_len-1] =='\n')retbuf[buf_len-1] ='\0';
FIN:
return strdup(retbuf);
}
static const char l_possible_chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
/**
* @brief random_string_fill Filling a string with random characters
* @param[out] str A pointer to a char array
* @param[in] length The length of the array or string
*/
void dap_random_string_fill(char *str, size_t length) {
for(size_t i = 0; i < length; i++)
str[i] = l_possible_chars[
rand() % (sizeof(l_possible_chars) - 1)];
}
/**
* @brief random_string_create Generates a random string
* @param[in] a_length lenght
* @return a pointer to an array
*/
char * dap_random_string_create_alloc(size_t a_length)
{
char * ret = DAP_NEW_SIZE(char, a_length+1);
size_t i;
for(i=0; i<a_length; ++i) {
int index = rand() % (sizeof(l_possible_chars)-1);
ret[i] = l_possible_chars[index];
}
return ret;
}
#define MAX_PRINT_WIDTH 100
static void _printrepchar(char c, size_t count) {
assert(count < MAX_PRINT_WIDTH &&
"Too many characters");
static char buff[MAX_PRINT_WIDTH];
memset(buff, (int)c, count);
printf("%s\n", buff);
}
/**
* @brief The function displays a dump
* @param[in] data The data dump you want to display
* @param[in] size The size of the data whose dump you want to display
*
* The function displays a dump, for example an array, in hex format
*/
void dap_dump_hex(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
for (i = 0; i < size; ++i) {
printf("%02X ", ((const unsigned char*)data)[i]);
if (((const unsigned char*)data)[i] >= ' ' && ((const unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((const char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
printf(" ");
if ((i+1) % 16 == 0) {
printf("| %s \n", ascii);
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
printf(" ");
}
for (j = (i+1) % 16; j < 16; ++j) {
printf(" ");
}
printf("| %s \n", ascii);
}
}
}
_printrepchar('-', 70);
}
void *memzero(void *a_buf, size_t n)
{
memset(a_buf,0,n);
return a_buf;
}
/**
* Convert binary data to binhex encoded data.
*
* out output buffer, must be twice the number of bytes to encode.
* len is the size of the data in the in[] buffer to encode.
* return the number of bytes encoded, or -1 on error.
*/
size_t dap_bin2hex(char *a_out, const void *a_in, size_t a_len)
{
size_t ct = a_len;
static char hex[] = "0123456789ABCDEF";
const uint8_t *l_in = (const uint8_t *)a_in;
if(!a_in || !a_out )
return 0;
// hexadecimal lookup table
while(ct-- > 0){
*a_out++ = hex[*l_in >> 4];
*a_out++ = hex[*l_in++ & 0x0F];
}
return a_len;
}
/**
* Convert binhex encoded data to binary data
*
* len is the size of the data in the in[] buffer to decode, and must be even.
* out outputbuffer must be at least half of "len" in size.
* The buffers in[] and out[] can be the same to allow in-place decoding.
* return the number of bytes encoded, or 0 on error.
*/
size_t dap_hex2bin(uint8_t *a_out, const char *a_in, size_t a_len)
{
// '0'-'9' = 0x30-0x39
// 'a'-'f' = 0x61-0x66
// 'A'-'F' = 0x41-0x46
size_t ct = a_len;
if(!a_in || !a_out || (a_len & 1))
return 0;
while(ct > 0) {
char ch1 = ((*a_in >= 'a') ? (*a_in++ - 'a' + 10) : ((*a_in >= 'A') ? (*a_in++ - 'A' + 10) : (*a_in++ - '0'))) << 4;
char ch2 = ((*a_in >= 'a') ? (*a_in++ - 'a' + 10) : ((*a_in >= 'A') ? (*a_in++ - 'A' + 10) : (*a_in++ - '0'))); // ((*in >= 'A') ? (*in++ - 'A' + 10) : (*in++ - '0'));
*a_out++ =(uint8_t) ch1 + (uint8_t) ch2;
ct -= 2;
}
return a_len;
}
/**
* Convert string to digit
*/
void dap_digit_from_string(const char *num_str, uint8_t *raw, size_t raw_len)
{
if(!num_str)
return;
uint64_t val;
if(!strncasecmp(num_str, "0x", 2)) {
val = strtoull(num_str + 2, NULL, 16);
}else {
val = strtoull(num_str, NULL, 10);
}
// for LITTLE_ENDIAN (Intel), do nothing, otherwise swap bytes
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
val = le64toh(val);
#endif
memset(raw, 0, raw_len);
memcpy(raw, &val, min(raw_len, sizeof(uint64_t)));
}
#ifdef __MINGW32__
/*!
* \brief Execute shell command silently
* \param a_cmd command line
* \return 0 if success, -1 otherwise
*/
int exec_silent(const char * a_cmd) {
PROCESS_INFORMATION p_info;
STARTUPINFOA s_info;
memzero(&s_info, sizeof(s_info));
memzero(&p_info, sizeof(p_info));
s_info.cb = sizeof(s_info);
char cmdline[512] = {'\0'};
strcat(cmdline, "C:\\Windows\\System32\\cmd.exe /c ");
strcat(cmdline, a_cmd);
if (CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0x08000000, NULL, NULL, &s_info, &p_info)) {
WaitForSingleObject(p_info.hProcess, 0xffffffff);
CloseHandle(p_info.hProcess);
CloseHandle(p_info.hThread);
return 0;
}
else {
return -1;
}
}
#endif