Newer
Older
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <limits.h>
#include <netdb.h>
#include <ifaddrs.h>
Jakob Olsson
committed
static void uloop_add_get_devices(struct uloop_timeout *t);
static int get_devices_from_path(char *input_path);
static int get_devices(void);
static int poll_devices(struct uloop_timeout *t);
static int tag_missing_devices(void);
static int add_device(struct device *new_dev);
static int delete_all_devices(void);
static int delete_device_by_name(char *name);
static int delete_device(struct device *dev);
static void free_device(struct device *dev);
static void free_usb(struct USB *usb);
static struct device *search_list(char *name);
static char *get_usb_stat(char *path, char *file);
static char *get_device_name(char *dir_name);
static char *get_device_ip(char *device_name);
static int list_to_blob(struct blob_buf *bb);
static int print_list(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int clear(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int test(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static int remove_device(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg);
static void init_ubus(void);
static int publish_object(struct ubus_context *ctx);
static struct ubus_context *global_ctx;
static struct uloop_timeout timeout = {
static const struct blobmsg_policy dev_policy[__DEV_MAX] = {
[DEV] = {.name = "dev", .type = BLOBMSG_TYPE_STRING},
};
static const struct blobmsg_policy usb_policy[__USB_MAX] = {
[USB_PATH] = {.name = "path", .type = BLOBMSG_TYPE_STRING},
};
LIST_HEAD(devices);
LIST_HEAD(stack);
LIST_HEAD(visited);
static int parse_args(int argc, char **argv)
while ((ch = getopt_long(argc, argv, ":d:", long_options, NULL)) != -1) {
printf("%s: option '-%c' is invalid.\n", argv[0], optopt);
break;
Jakob Olsson
committed
fprintf(stderr, "%s: option '-%c' requires an argument\n", argv[0], optopt);
break;
Jakob Olsson
committed
fprintf(stderr, "%s: option '-%c' is invalid: ignored\n", argv[0], optopt);
break;
default:
goto fail;
break;
return 0;
fail:
return -1;
}
static int poll_devices(struct uloop_timeout *t)
{
get_devices();
tag_missing_devices();
uloop_add_get_devices(t);
return 0;
}
static int tag_missing_devices(void)
{
struct device *dev, *tmp;
list_for_each_entry_safe(dev, tmp, &devices, list) {
if (dev->present) {
dev->missing = 0;
dev->present = false;
continue;
}
dev->missing++;
if (dev->missing == 1) {
/* TEMPORARY FIX -- TODO: HOW TO CHECK IF IT CONTAINS ELEMENT */
list_del(&dev->list);
static int update_device(struct device *dev)
prev_dev = search_list(dev->usb.if_name);
if (dev->ip)
prev_dev->ip = strdup(dev->ip);
else
prev_dev->ip = NULL;
if (!prev_dev->ubus_obj && prev_dev->ip) {
prev_dev->ubus_obj = dongle_create_dynamic_object(prev_dev);
if (!prev_dev->ubus_obj)
goto fail;
rv = publish_ubus_object(global_ctx, prev_dev->ubus_obj);
if (rv)
goto fail;
global_dev = prev_dev;
} else if (prev_dev->ubus_obj && !prev_dev->ip) {
unpublish_ubus_object(global_ctx, prev_dev->ubus_obj);
dongle_destroy_dynamic_object(&(prev_dev->ubus_obj));
}
prev_dev->present = true;
return 0;
not_present:
return -1;
fail:
return -2;
}
static int add_device(struct device *dev)
{
int rv;
rv = update_device(dev);
if (rv == 0)
if (list_empty(&devices))
INIT_LIST_HEAD(&devices);
list_add_tail(&dev->list, &devices);
if (dev->ip) {
dev->ubus_obj = dongle_create_dynamic_object(dev);
if (!dev->ubus_obj)
rv = publish_ubus_object(global_ctx, dev->ubus_obj);
goto fail_publish;
return 0;
already_present:
return -1;
dongle_destroy_dynamic_object(&(dev->ubus_obj));
static int delete_all_devices(void)
{
struct device *dev, *tmp;
list_for_each_entry_safe(dev, tmp, &devices, list) {
/* TEMPORARY FIX -- TODO: HOW TO CHECK IF IT CONTAINS ELEMENT */
list_del(&dev->list);
delete_device(dev);
}
return 0;
}
static int delete_device_by_name(char *name)
{
struct device *dev, *tmp;
list_for_each_entry_safe(dev, tmp, &devices, list) {
if (strncmp(dev->usb.if_name, name, 128) != 0)
continue;
/* TEMPORARY FIX -- TODO: HOW TO CHECK IF IT CONTAINS ELEMENT */
list_del(&dev->list);
delete_device(dev);
return 0;
}
return -1;
}
static int delete_device(struct device *dev)
unpublish_ubus_object(global_ctx, dev->ubus_obj);
dongle_destroy_dynamic_object(&(dev->ubus_obj));
/*if (dev->list) // how to check if list contains this element?
list_del(&dev->list);*/
static void free_device(struct device *dev)
static void free_usb(struct USB *usb)
free(usb->product);
free(usb->product_id);
free(usb->vendor_id);
free(usb->if_name);
}
static struct device *search_list(char *name)
{
struct device *dev;
list_for_each_entry(dev, &devices, list) {
if (strncmp(name, dev->usb.if_name, 128) == 0)
return dev;
}
return NULL;
}
static char *get_usb_stat(char *path, char *file)
{
char stat_path[PATH_MAX], contents[1024];
FILE *f;
snprintf(stat_path, PATH_MAX, "%s%s", path, file);
f = fopen(stat_path, "r");
if (!f) {
perror("fopen");
}
fgets(contents, 1024, f);
remove_newline(contents);
static char *get_device_name(char *dir_name)
{
char path[PATH_MAX], *name = NULL;
struct directory *dr, *sub_dr;
struct dirent *de;
struct stat st;
DIR *dir;
dr = (struct directory *)malloc(sizeof(*dr));
if (!dr) {
perror("malloc");
goto fail_dr;
}
dr->path = strdup(dir_name);
if (!dr->path)
goto fail_path;
push(dr, &stack);
while (!list_empty(&stack) && !name) {
dr = pop(&stack);
if (!dir) {
free(dr->path);
free(dr);
push(dr, &visited);
while ((de = readdir(dir)) != NULL) {
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
continue;
if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
perror("fstatat");
continue;
}
if (!S_ISDIR(st.st_mode))
continue;
if (!strstr(dr->path, "net")) {
snprintf(path, PATH_MAX, "%s%s/", dr->path, de->d_name);
if (search(path, &visited))
continue;
sub_dr = (struct directory *)malloc(sizeof(*sub_dr));
if (!sub_dr) {
perror("malloc");
continue;
}
sub_dr->path = strdup(path);
if (!sub_dr->path) {
free(sub_dr);
continue;
}
push(sub_dr, &stack);
continue;
}
if (!strstr(de->d_name, "eth") && !strstr(de->d_name, "usb"))
break;
name = strdup(de->d_name);
// how to manage failure on this strdup
break;
}
closedir(dir);
}
clear_list(&visited);
clear_list(&stack);
return name;
fail_path:
free(dr);
fail_dr:
return NULL;
}
static int get_devices_from_path(char *input_path)
struct dirent *de;
DIR *dir;
struct stat st;
struct device *dev;
dir = opendir(input_path);
if (!dir) {
perror("opendir");
goto fail_dr;
}
path_len = strlen(input_path) + strlen("/product") + 1;
path = malloc(path_len);
if (!path) {
perror("malloc");
goto fail_path;
}
continue;
if (strstr(de->d_name, ":") || strstr(de->d_name, "usb"))
continue;
snprintf(path, path_len, "%s/product", input_path);
if (rv < 0) {
//perror("stat");
continue;
}
snprintf(path, path_len, "%s/", input_path);
name = get_device_name(path);
if (!name)
continue;
if (!dev) {
perror("calloc");
/*goto fail_dev;*/
free(name);
dev->usb.product = get_usb_stat(path, "product");
dev->usb.product_id = get_usb_stat(path, "idProduct");
dev->usb.vendor_id = get_usb_stat(path, "idVendor");
dev->usb.if_name = name;
dev->ip = get_device_ip(dev->usb.if_name);
rv = add_device(dev);
if (rv < 0)
delete_device(dev);
Jakob Olsson
committed
break;
static int get_devices(void)
char devices_path[PATH_MAX], mount_path[PATH_MAX];
struct dirent *de;
DIR *dir;
int rv;
strncpy(devices_path, "/sys/bus/usb/devices/", PATH_MAX);
dir = opendir(devices_path);
if (!dir) {
perror("opendir");
goto fail_dr;
}
while ((de = readdir(dir)) != NULL) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
if (strstr(de->d_name, ":") || strstr(de->d_name, "usb"))
continue;
snprintf(mount_path, PATH_MAX, "%s%s/", devices_path, de->d_name);
rv = get_devices_from_path(mount_path);
if (rv < 0)
break;
closedir(dir);
return 0;
fail_dr:
return -1;
}
static char *get_device_ip(char *device_name)
{
char *iface, *destination, *gateway, *flags, *route, *ipv4_addr = NULL;
int host_flag;
struct in_addr addr;
FILE *fp;
fp = fopen("/proc/net/route", "r");
if (!fp) {
perror("fopen");
goto fail_fopen;
while ((fgets(route, 1024, fp)) != NULL) {
remove_newline(route);
iface = strtok(route, "\t");
destination = strtok(NULL, "\t");
gateway = strtok(NULL, "\t");
flags = strtok(NULL, "\t");
if (strncmp(iface, device_name, 10) != 0)
if (!host_flag)
ipv4_addr = (char *)calloc(1, IPV4_MAX);
addr.s_addr = strtoul(destination, NULL, IPV4_MAX);
inet_ntop(AF_INET, &(addr.s_addr), ipv4_addr, IPV4_MAX);
fail_fopen:
static int list_to_blob(struct blob_buf *bb)
{
struct device *net_dev;
void *arr, *table;
int rv;
memset(bb, 0, sizeof(*bb));
rv = blob_buf_init(bb, 0);
arr = blobmsg_open_array(bb, "network_devices");
list_for_each_entry(net_dev, &devices, list) {
table = blobmsg_open_table(bb, "");
rv = blobmsg_add_string(bb, "product", net_dev->usb.product);
rv = blobmsg_add_string(bb, "interface_name", net_dev->usb.if_name);
rv = blobmsg_add_string(bb, "idproduct", net_dev->usb.product_id);
rv = blobmsg_add_string(bb, "idvendor", net_dev->usb.vendor_id);
rv = blobmsg_add_string(bb, "ip", net_dev->ip);
return 0;
fail_string:
fail_table:
fail_arr:
fail:
return UBUS_STATUS_UNKNOWN_ERROR;
}
static int print_list(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_buf bb;
int rv;
rv = list_to_blob(&bb);
goto fail;
ubus_send_reply(ctx, req, bb.head);
fail:
blob_buf_free(&bb);
return rv;
static int clear(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
printf("%d\n", delete_all_devices());
return 0;
}
static int test(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
printf("%s\n", get_device_ip("usb0"));
return 0;
}
static int alert(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__USB_MAX];
char *usb_path;
blobmsg_parse(usb_policy, __USB_MAX, tb, blob_data(msg), blob_len(msg));
if (!tb[USB_PATH])
goto fail;
usb_path = (char *)blobmsg_data(tb[USB_PATH]);
get_devices_from_path(usb_path);
return 0;
fail:
return UBUS_STATUS_INVALID_ARGUMENT;
}
static int remove_device(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__DEV_MAX];
char *dev;
blobmsg_parse(dev_policy, __DEV_MAX, tb, blob_data(msg), blob_len(msg));
if (!tb[DEV])
goto fail;
dev = (char *)blobmsg_data(tb[DEV]);
printf("%d\n", delete_device_by_name(dev));
return 0;
fail:
return UBUS_STATUS_INVALID_ARGUMENT;
}
static int update(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
get_devices();
tag_missing_devices();
return 0;
}
static struct ubus_method dongle_object_methods[] = {
UBUS_METHOD_NOARG("test", test),
UBUS_METHOD_NOARG("list", print_list),
UBUS_METHOD_NOARG("clear", clear),
UBUS_METHOD("remove_device", remove_device, dev_policy),
UBUS_METHOD("alert", alert, usb_policy),
UBUS_METHOD_NOARG("update", update)};
static struct ubus_object_type dongle_object_type = UBUS_OBJECT_TYPE("dongle", dongle_object_methods);
static struct ubus_object dongle_object = {
.type = &dongle_object_type,
.methods = dongle_object_methods,
.n_methods = ARRAY_SIZE(dongle_object_methods),
static void uloop_add_get_devices(struct uloop_timeout *t)
uloop_timeout_set(t, 60000);
static void init_ubus(void)
Jakob Olsson
committed
{
global_ctx = ubus_connect(NULL);
if (!global_ctx) {
Jakob Olsson
committed
perror("ubus");
exit(1);
}
ubus_add_uloop(global_ctx);
Jakob Olsson
committed
}
static int publish_object(struct ubus_context *ctx)
if (rv) {
debug_print("failed to add dongle.pin to ubus!\n");
return -1;
}
return 0;
}
Jakob Olsson
committed
int main(int argc, char **argv)
{
int rv;
rv = parse_args(argc, argv);
if (rv < 0)
goto fail;
Jakob Olsson
committed
uloop_init();
init_ubus();
uloop_timeout_set(&timeout, 0);
uloop_timeout_add(&timeout);
Jakob Olsson
committed
uloop_run();
return 0;
fail:
return -1;