-
Jakob Olsson authored
In accordance with EasyMesh R6
Jakob Olsson authoredIn accordance with EasyMesh R6
utils.c 11.57 KiB
/*
* utils.c - implements utility functions
*
* Copyright (C) 2019 IOPSYS Software Solutions AB. All rights reserved.
*
* Author: anjan.chanda@iopsys.eu
*
*/
#include "utils.h"
#include <1905_tlvs.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <easy/utils.h>
#include <fcntl.h>
#include <json-c/json_object.h>
#include <json-c/json_types.h>
#include <libubox/list.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <easy/easy.h>
#include <wifidefs.h>
#include "debug.h"
struct json_object;
#ifndef IEEE80211_FREQUENCY_BAND_6_GHZ
#define IEEE80211_FREQUENCY_BAND_6_GHZ 0x03
#endif
static int hex2num(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
int hex2byte(const char *hex)
{
int a, b;
a = hex2num(*hex++);
if (a < 0)
return -1;
b = hex2num(*hex++);
if (b < 0)
return -1;
return (a << 4) | b;
}
/* convert hex string to byte array */
unsigned char *strtob(char *str, int len, unsigned char *bytes)
{
size_t i;
for (i = 0; i < len; i++) {
int a;
a = hex2byte(str);
if (a < 0)
return NULL;
str += 2;
bytes[i] = a;
}
return bytes;
}
/* Convert "00:11:22:33:44:55" --> \x001122334455 */
unsigned char *hwaddr_aton(const char *macstr, unsigned char *mac)
{
size_t i;
for (i = 0; i < 6; i++) {
int a;
a = hex2byte(macstr);
if (a < 0)
return NULL;
macstr += 2;
mac[i] = a;
if (i < 6 - 1 && *macstr++ != ':')
return NULL;
}
return mac;
}
/* Convert \x001122334455 --> "00:11:22:33:44:55" */
char *hwaddr_ntoa(const unsigned char *mac, char *macstr)
{
sprintf(macstr, "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0]&0xff, mac[1]&0xff,
mac[2]&0xff, mac[3]&0xff,
mac[4]&0xff, mac[5]&0xff);
return macstr;
}
/**
* Get hw address from ip address using ARP cache
* @ifname: interface name
* @ipstr: ipaddress in dotted decimal string format
* @hw: hwaddress returned from arp cache lookup
*/
int hwaddr_from_ip(char *ifname, char *ipstr, unsigned char *hw)
{
int s;
struct arpreq r;
struct in_addr ip;
struct sockaddr_in *sin;
if (!ifname)
return -1;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return -1;
inet_aton(ipstr, &ip);
memset(&r, 0, sizeof(struct arpreq));
snprintf(r.arp_dev, sizeof(r.arp_dev), "%s", ifname);
sin = (struct sockaddr_in *)&r.arp_pa;
sin->sin_family = AF_INET;
memcpy(&sin->sin_addr, &ip, sizeof(struct in_addr));
if (ioctl(s, SIOCGARP, &r) < 0) {
close(s);
return -1;
}
memcpy(hw, r.arp_ha.sa_data, 6);
close(s);
return 0;
}
/** list utility functions */
/**
* list_dup - duplicates a list
* @h: head of the list
* @new: head of the duplicate list
* @alloc_entry: function to allocate a new entry
* @free_entry: function to free entry in case of error
* @copy_entry: function to copy an entry
*/
int list_dup(struct list_head *h, struct list_head *new,
void *(*alloc_entry)(void),
void (*free_entry)(struct list_head *n),
void (*copy_entry)(struct list_head *from, struct list_head *to))
{
struct list_head *p, *tmp, *n;
if (!alloc_entry || !copy_entry)
return -1;
list_for_each(p, h) {
n = alloc_entry();
if (!n)
goto rollback_list_dup;
copy_entry(p, n);
list_add_tail(n, new);
}
return 0;
rollback_list_dup:
list_for_each_safe(p, tmp, new) {
list_del(p);
if (free_entry)
free_entry(p);
}
return -1;
}
/**
* list_join_uniq - joins two lists merging duplicates based on 'match' criteria
* @priv: opaque private data
* @a: head of the first list
* @b: head of second list
* @out: head of the final joined list
* @match: matching function which determines duplicate entries
* @create_entry: function to create combined merged entry
* @free_entry: function to free combined merged entry in case on error
*/
int list_join_uniq_strict(void *priv, struct list_head *a, struct list_head *b,
struct list_head *out,
int (*match)(void *priv, struct list_head *a, struct list_head *b),
struct list_head *(*create_entry)(void *priv, struct list_head *a, struct list_head *b),
void (*free_entry)(void *priv, struct list_head *a))
{
struct list_head *p, *q, *t1, *t2, *n;
if (!create_entry || !free_entry)
return -1;
list_for_each_safe(p, t1, a) {
list_for_each_safe(q, t2, b) {
if (match && match(priv, p, q)) {
n = create_entry(priv, p, q);
if (!n)
goto err_list_join_uniq;
list_add_tail(n, out);
}
}
}
#if 0 /* because returns only matching entries */
list_for_each_safe(p, t1, a) {
n = create_entry(priv, p, NULL);
if (!n)
goto err_list_join_uniq;
list_add_tail(n, out);
list_del(p);
}
list_for_each_safe(q, t1, b) {
n = create_entry(priv, NULL, q);
if (!n)
goto err_list_join_uniq;
list_add_tail(n, out);
list_del(q);
}
#endif
return 0;
err_list_join_uniq:
list_for_each_safe(p, t1, out) {
list_del(p);
free_entry(priv, p);
}
return -1;
}
int list_join_uniq(void *priv, struct list_head *a, struct list_head *b,
struct list_head *out,
int (*match)(void *priv, struct list_head *a, struct list_head *b),
struct list_head *(*create_jentry)(void *priv, struct list_head *a, struct list_head *b),
void (*free_jentry)(void *priv, struct list_head *),
void (*free_entry_a)(struct list_head *),
void (*free_entry_b)(struct list_head *))
{
struct list_head *p, *q, *t1, *t2, *n;
if (!create_jentry || !free_jentry)
return -1;
list_for_each_safe(p, t1, a) {
list_for_each_safe(q, t2, b) {
if (match && match(priv, p, q)) {
n = create_jentry(priv, p, q);
if (!n)
goto err_list_join_uniq;
list_add_tail(n, out);
list_del(p);
free_entry_a(p);
list_del(q);
free_entry_b(q);
}
}
}
list_for_each_safe(p, t1, a) {
n = create_jentry(priv, p, NULL);
if (!n)
goto err_list_join_uniq;
list_add_tail(n, out);
list_del(p);
free_entry_a(p);
}
list_for_each_safe(q, t1, b) {
n = create_jentry(priv, NULL, q);
if (!n)
goto err_list_join_uniq;
list_add_tail(n, out);
list_del(q);
free_entry_b(q);
}
return 0;
err_list_join_uniq:
list_for_each_safe(p, t1, out) {
list_del(p);
free_jentry(priv, p);
}
return -1;
}
/* daemonize process */
void do_daemonize(const char *pidfile)
{
char buf[128] = {0};
int flags;
int f;
info("daemonizing ...\n");
if (daemon(0, 0)) {
fprintf(stderr, "Can't becomes a daemon. "
"Continue as foreground process...\n");
}
if (!pidfile)
return;
f = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (f >= 0) {
flags = fcntl(f, F_GETFD);
if (flags != -1) {
flags |= FD_CLOEXEC;
fcntl(f, F_SETFD, flags);
}
if (lockf(f, F_TLOCK, 0) < 0) {
fprintf(stderr, "File '%s' exists. Aborting...\n",
pidfile);
exit(-1);
}
if (ftruncate(f, 0)) {
fprintf(stderr, "Continue with invalid pid file %s\n",
pidfile);
return;
}
snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
if (write(f, buf, strlen(buf)) != strlen(buf)) {
fprintf(stderr, "Continue with invalid pid file %s\n",
pidfile);
return;
}
}
}
/* install a signal handler */
int set_sighandler(int sig, void (*handler)(int))
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = handler;
if (sigaction(sig, &sa, NULL) < 0) {
fprintf(stderr, "Error sigaction %d\n", sig);
return -1;
}
return 0;
}
/* uninstall a signal handler */
int unset_sighandler(int sig)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_DFL;
return sigaction(sig, &sa, NULL);
}
/* json helpers copied from uproxyd.c */
const char *json_get_string(struct json_object *object, const char *key)
{
json_bool ret;
struct json_object *value;
if (!object || !json_object_is_type(object, json_type_object))
return NULL;
ret = json_object_object_get_ex(object, key, &value);
if (!ret || !value || !json_object_is_type(value, json_type_string))
return NULL;
return json_object_get_string(value);
}
int json_get_int(struct json_object *object, const char *key)
{
json_bool ret;
struct json_object *value;
if (!object || !json_object_is_type(object, json_type_object))
return -1;
ret = json_object_object_get_ex(object, key, &value);
if (!ret || !value || !json_object_is_type(value, json_type_int))
return -1;
return json_object_get_int(value);
}
int json_get_bool(struct json_object *object, const char *key)
{
json_bool ret;
struct json_object *value;
if (!object || !json_object_is_type(object, json_type_object))
return -1;
ret = json_object_object_get_ex(object, key, &value);
if (!ret || !value || !json_object_is_type(value, json_type_boolean))
return -1;
return json_object_get_boolean(value) ? 1 : 0;
}
uint8_t wifi_band_to_ieee1905band(uint8_t band)
{
uint8_t ieee1905band = -1;
switch (band) {
case BAND_2:
ieee1905band = IEEE80211_FREQUENCY_BAND_2_4_GHZ;
break;
case BAND_5:
ieee1905band = IEEE80211_FREQUENCY_BAND_5_GHZ;
break;
case BAND_60:
ieee1905band = IEEE80211_FREQUENCY_BAND_60_GHZ;
break;
case BAND_6:
ieee1905band = IEEE80211_FREQUENCY_BAND_6_GHZ;
break;
default:
break;
}
return ieee1905band;
}
uint8_t wifi_bandstr_to_band(char *bandstr)
{
enum wifi_band band = BAND_UNKNOWN;
if (!strncmp(bandstr, "2.4GHz", strlen("2.4GHz")))
band = BAND_2;
else if (!strncmp(bandstr, "5GHz", strlen("5GHz")))
band = BAND_5;
else if (!strncmp(bandstr, "6GHz", strlen("6GHz")))
band = BAND_6;
return band;
}
const char *band_to_str(uint32_t band)
{
if (band == BAND_2)
return "2";
else if (band == BAND_5)
return "5";
else if (band == BAND_6)
return "6";
return NULL;
}
uint32_t int_to_band(uint32_t band)
{
if (band == 2)
return BAND_2;
else if (band == 5)
return BAND_5;
else if (band == 6)
return BAND_6;
return BAND_UNKNOWN;
}
uint8_t get_device_num_from_name(char *device)
{
char *p = device;
/* Workaround for radio0_band0, radio0_band1, radio0_band2 */
p = strstr(device, "band");
if (!p)
p = device;
while (*p && !isdigit(*p))
p++;
return atoi(p);
}
bool is_local_cntlr_available(void)
{
struct stat sb1, sb2, sb3;
int ret;
ret = stat("/usr/sbin/mapcontroller", &sb1) ||
stat("/etc/init.d/mapcontroller", &sb2) ||
stat("/etc/config/mapcontroller", &sb3);
if (ret)
return false;
if (S_ISREG(sb1.st_mode) && S_ISREG(sb2.st_mode) && S_ISREG(sb3.st_mode))
return true;
return false;
}
bool is_local_cntlr_running(void)
{
char pid[8] = {0};
chrCmd(pid, sizeof(pid), "pidof mapcontroller");
if (strlen(pid))
return true;
return false;
}
int readfrom_configfile(const char *filename, uint8_t **out, size_t *olen)
{
struct stat sb;
ssize_t rem, res;
uint8_t *pos;
int ret;
int fp;
*olen = 0;
ret = stat(filename, &sb);
if (ret == -1)
return -1;
fp = open(filename, O_RDONLY);
if (!fp)
return -1;
*out = calloc(1, sb.st_size);
if (!*out) {
close(fp);
return -1;
}
*olen = sb.st_size;
rem = *olen;
pos = *out;
while (rem != 0) {
res = read(fp, pos, rem);
if (res == -1)
continue;
pos += res;
rem -= res;
}
close(fp);
return 0;
}
int writeto_configfile(const char *filename, void *in, size_t len)
{
ssize_t rem;
int fp;
fp = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fp < 0)
return -1;
if (!in || len == 0) {
close(fp);
return 0;
}
while (len != 0) {
rem = write(fp, in, len);
if (rem == -1)
continue;
len -= rem;
in = (uint8_t *)in + rem;
}
close(fp);
return 0;
}
bool is_vid_valid(unsigned int vid)
{
return (vid < TS_VID_INVALID) && (vid > 0);
}