2018-08-21 00:25:44 +02:00
|
|
|
/** Utilities.
|
|
|
|
*
|
|
|
|
* @author Daniel Krebs <github@daniel-krebs.net>
|
2020-01-20 17:15:25 +01:00
|
|
|
* @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC
|
2018-08-21 00:25:44 +02:00
|
|
|
* @license GNU General Public License (version 3)
|
|
|
|
*
|
2018-08-21 16:54:58 +02:00
|
|
|
* VILLAScommon
|
2018-08-21 00:25:44 +02:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
2018-08-27 11:09:25 +02:00
|
|
|
#include <iostream>
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2019-06-23 16:26:44 +02:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstring>
|
2018-08-27 11:09:25 +02:00
|
|
|
#include <unistd.h>
|
2019-06-23 16:26:44 +02:00
|
|
|
#include <cmath>
|
2019-04-07 15:12:32 +02:00
|
|
|
#include <pthread.h>
|
2019-06-23 16:26:44 +02:00
|
|
|
#include <cctype>
|
2019-04-07 15:12:32 +02:00
|
|
|
#include <fcntl.h>
|
2018-08-27 11:09:25 +02:00
|
|
|
|
2019-05-28 10:55:24 +02:00
|
|
|
#include <openssl/bio.h>
|
|
|
|
#include <openssl/buffer.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
|
2019-04-07 15:12:32 +02:00
|
|
|
#include <villas/config.h>
|
2018-08-21 00:25:44 +02:00
|
|
|
#include <villas/utils.hpp>
|
2019-04-23 12:57:51 +02:00
|
|
|
#include <villas/colors.hpp>
|
2020-01-27 12:17:57 +01:00
|
|
|
#include <villas/exceptions.hpp>
|
2018-11-01 14:31:41 +01:00
|
|
|
#include <villas/log.hpp>
|
2018-08-21 00:25:44 +02:00
|
|
|
|
2019-04-23 12:57:51 +02:00
|
|
|
static pthread_t main_thread;
|
|
|
|
|
2018-08-21 00:25:44 +02:00
|
|
|
namespace villas {
|
|
|
|
namespace utils {
|
|
|
|
|
2019-05-28 10:55:24 +02:00
|
|
|
std::vector<std::string> tokenize(std::string s, std::string delimiter)
|
2018-08-21 00:25:44 +02:00
|
|
|
{
|
|
|
|
std::vector<std::string> tokens;
|
|
|
|
|
|
|
|
size_t lastPos = 0;
|
|
|
|
size_t curentPos;
|
|
|
|
|
2019-05-28 10:55:24 +02:00
|
|
|
while ((curentPos = s.find(delimiter, lastPos)) != std::string::npos) {
|
2018-08-21 00:25:44 +02:00
|
|
|
const size_t tokenLength = curentPos - lastPos;
|
|
|
|
tokens.push_back(s.substr(lastPos, tokenLength));
|
|
|
|
|
2019-03-26 06:50:56 +01:00
|
|
|
/* Advance in string */
|
2018-08-21 00:25:44 +02:00
|
|
|
lastPos = curentPos + delimiter.length();
|
|
|
|
}
|
|
|
|
|
2019-03-26 06:50:56 +01:00
|
|
|
/* Check if there's a last token behind the last delimiter. */
|
2019-05-28 10:55:24 +02:00
|
|
|
if (lastPos != s.length()) {
|
2018-08-21 00:25:44 +02:00
|
|
|
const size_t lastTokenLength = s.length() - lastPos;
|
|
|
|
tokens.push_back(s.substr(lastPos, lastTokenLength));
|
|
|
|
}
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
2018-08-27 11:09:25 +02:00
|
|
|
ssize_t read_random(char *buf, size_t len)
|
|
|
|
{
|
|
|
|
int fd;
|
2018-10-21 21:53:16 +01:00
|
|
|
ssize_t bytes = -1;
|
2018-08-27 11:09:25 +02:00
|
|
|
|
|
|
|
fd = open("/dev/urandom", O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
bytes = read(fd, buf, len);
|
|
|
|
if (bytes < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
len -= bytes;
|
|
|
|
buf += bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup exit handler */
|
2019-06-30 14:33:26 +02:00
|
|
|
int signals_init(void (*cb)(int signal, siginfo_t *sinfo, void *ctx), std::list<int> cbSignals, std::list<int> ignoreSignals)
|
2018-08-27 11:09:25 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2018-11-30 21:43:37 +01:00
|
|
|
Logger logger = logging.get("signals");
|
2018-11-01 14:31:41 +01:00
|
|
|
|
|
|
|
logger->info("Initialize subsystem");
|
2018-08-27 11:09:25 +02:00
|
|
|
|
2019-06-30 14:33:26 +02:00
|
|
|
struct sigaction sa_cb;
|
|
|
|
sa_cb.sa_flags = SA_SIGINFO | SA_NODEFER;
|
|
|
|
sa_cb.sa_sigaction = cb;
|
2018-08-27 11:09:25 +02:00
|
|
|
|
2019-06-30 14:33:26 +02:00
|
|
|
struct sigaction sa_ign;
|
|
|
|
sa_ign.sa_flags = 0;
|
|
|
|
sa_ign.sa_handler = SIG_IGN;
|
2018-08-27 11:09:25 +02:00
|
|
|
|
|
|
|
main_thread = pthread_self();
|
|
|
|
|
2019-06-30 14:33:26 +02:00
|
|
|
sigemptyset(&sa_cb.sa_mask);
|
|
|
|
sigemptyset(&sa_ign.sa_mask);
|
2018-08-27 11:09:25 +02:00
|
|
|
|
2019-06-30 14:33:26 +02:00
|
|
|
cbSignals.insert(cbSignals.begin(), { SIGINT, SIGTERM, SIGUSR1, SIGALRM });
|
|
|
|
cbSignals.sort();
|
|
|
|
cbSignals.unique();
|
2018-08-27 11:09:25 +02:00
|
|
|
|
2019-06-30 14:33:26 +02:00
|
|
|
for (auto signal : cbSignals) {
|
|
|
|
ret = sigaction(signal, &sa_cb, nullptr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2018-08-27 11:09:25 +02:00
|
|
|
|
2019-06-30 14:33:26 +02:00
|
|
|
for (auto signal : ignoreSignals) {
|
|
|
|
ret = sigaction(signal, &sa_ign, nullptr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2018-08-27 11:09:25 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char * decolor(char *str)
|
|
|
|
{
|
|
|
|
char *p, *q;
|
|
|
|
bool inseq = false;
|
|
|
|
|
|
|
|
for (p = q = str; *p; p++) {
|
|
|
|
switch (*p) {
|
|
|
|
case 0x1b:
|
|
|
|
if (*(++p) == '[') {
|
|
|
|
inseq = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'm':
|
|
|
|
if (inseq) {
|
|
|
|
inseq = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!inseq) {
|
|
|
|
*q = *p;
|
|
|
|
q++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*q = '\0';
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2019-01-22 16:00:46 +01:00
|
|
|
void killme(int sig)
|
|
|
|
{
|
|
|
|
/* Send only to main thread in case the ID was initilized by signals_init() */
|
|
|
|
if (main_thread)
|
|
|
|
pthread_kill(main_thread, sig);
|
|
|
|
else
|
|
|
|
kill(0, sig);
|
|
|
|
}
|
|
|
|
|
2019-04-07 15:12:32 +02:00
|
|
|
double box_muller(float m, float s)
|
|
|
|
{
|
|
|
|
double x1, x2, y1;
|
|
|
|
static double y2;
|
|
|
|
static int use_last = 0;
|
|
|
|
|
|
|
|
if (use_last) { /* use value from previous call */
|
|
|
|
y1 = y2;
|
|
|
|
use_last = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
double w;
|
|
|
|
do {
|
|
|
|
x1 = 2.0 * randf() - 1.0;
|
|
|
|
x2 = 2.0 * randf() - 1.0;
|
|
|
|
w = x1*x1 + x2*x2;
|
|
|
|
} while (w >= 1.0);
|
|
|
|
|
|
|
|
w = sqrt(-2.0 * log(w) / w);
|
|
|
|
y1 = x1 * w;
|
|
|
|
y2 = x2 * w;
|
|
|
|
use_last = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m + y1 * s;
|
|
|
|
}
|
|
|
|
|
|
|
|
double randf()
|
|
|
|
{
|
|
|
|
return (double) random() / RAND_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
char * vstrcatf(char **dest, const char *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
int n = *dest ? strlen(*dest) : 0;
|
|
|
|
int i = vasprintf(&tmp, fmt, ap);
|
|
|
|
|
|
|
|
*dest = (char *)(realloc(*dest, n + i + 1));
|
2019-04-08 10:42:45 +02:00
|
|
|
if (*dest != nullptr)
|
2019-04-07 15:12:32 +02:00
|
|
|
strncpy(*dest+n, tmp, i + 1);
|
|
|
|
|
|
|
|
free(tmp);
|
|
|
|
|
|
|
|
return *dest;
|
|
|
|
}
|
|
|
|
|
2019-04-23 12:57:51 +02:00
|
|
|
char * strcatf(char **dest, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vstrcatf(dest, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return *dest;
|
|
|
|
}
|
|
|
|
|
2019-04-07 15:12:32 +02:00
|
|
|
char * strf(const char *fmt, ...)
|
|
|
|
{
|
2019-04-08 10:42:45 +02:00
|
|
|
char *buf = nullptr;
|
2019-04-07 15:12:32 +02:00
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vstrcatf(&buf, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
char * vstrf(const char *fmt, va_list va)
|
|
|
|
{
|
2019-04-08 10:42:45 +02:00
|
|
|
char *buf = nullptr;
|
2019-04-07 15:12:32 +02:00
|
|
|
|
|
|
|
vstrcatf(&buf, fmt, va);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void * memdup(const void *src, size_t bytes)
|
|
|
|
{
|
2019-07-01 08:30:05 +02:00
|
|
|
void *dst = new char[bytes];
|
2020-01-27 12:17:57 +01:00
|
|
|
if (!dst)
|
2020-07-04 16:20:21 +02:00
|
|
|
throw MemoryAllocationError();
|
2019-04-07 15:12:32 +02:00
|
|
|
|
|
|
|
memcpy(dst, src, bytes);
|
|
|
|
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid_t spawn(const char* name, char *const argv[])
|
|
|
|
{
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
switch (pid) {
|
|
|
|
case -1: return -1;
|
|
|
|
case 0: return execvp(name, (char * const*) argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pid;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t strlenp(const char *str)
|
|
|
|
{
|
|
|
|
size_t sz = 0;
|
|
|
|
|
|
|
|
for (const char *d = str; *d; d++) {
|
|
|
|
const unsigned char *c = (const unsigned char *) d;
|
|
|
|
|
|
|
|
if (isprint(*c))
|
|
|
|
sz++;
|
|
|
|
else if (c[0] == '\b')
|
|
|
|
sz--;
|
|
|
|
else if (c[0] == '\t')
|
|
|
|
sz += 4; /* tab width == 4 */
|
|
|
|
/* CSI sequence */
|
|
|
|
else if (c[0] == '\e' && c[1] == '[') {
|
|
|
|
c += 2;
|
|
|
|
while (*c && *c != 'm')
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
/* UTF-8 */
|
|
|
|
else if (c[0] >= 0xc2 && c[0] <= 0xdf) {
|
|
|
|
sz++;
|
|
|
|
c += 1;
|
|
|
|
}
|
|
|
|
else if (c[0] >= 0xe0 && c[0] <= 0xef) {
|
|
|
|
sz++;
|
|
|
|
c += 2;
|
|
|
|
}
|
|
|
|
else if (c[0] >= 0xf0 && c[0] <= 0xf4) {
|
|
|
|
sz++;
|
|
|
|
c += 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
d = (const char *) c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz;
|
|
|
|
}
|
2019-04-23 12:57:51 +02:00
|
|
|
|
2019-06-03 18:11:03 +02:00
|
|
|
int log2i(long long x) {
|
|
|
|
if (x == 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return sizeof(x) * 8 - __builtin_clzll(x) - 1;
|
|
|
|
}
|
|
|
|
|
2019-05-28 10:55:24 +02:00
|
|
|
int sha1sum(FILE *f, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
SHA_CTX c;
|
|
|
|
char buf[512];
|
|
|
|
ssize_t bytes;
|
|
|
|
long seek;
|
|
|
|
|
|
|
|
seek = ftell(f);
|
|
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
|
|
|
|
SHA1_Init(&c);
|
|
|
|
|
|
|
|
bytes = fread(buf, 1, 512, f);
|
|
|
|
while (bytes > 0) {
|
|
|
|
SHA1_Update(&c, buf, bytes);
|
|
|
|
bytes = fread(buf, 1, 512, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
SHA1_Final(sha1, &c);
|
|
|
|
|
|
|
|
fseek(f, seek, SEEK_SET);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-23 12:57:51 +02:00
|
|
|
} /* namespace utils */
|
|
|
|
} /* namespace villas */
|