From 9f6ea62b0a90625d0bd1589f5ac4106374c0d210 Mon Sep 17 00:00:00 2001 From: jbnadal Date: Tue, 6 Dec 2016 17:42:06 +0100 Subject: [PATCH] Bump ubox-2016-07-19-aead2c0cbffdda9b46d74a998a4c6aeef423b21a --- src/3P/ubox/CMakeLists.txt | 62 ++ src/3P/ubox/builders/cmake/CMakeLists.txt | 33 + src/3P/ubox/getrandom.c | 58 + src/3P/ubox/kmodloader.c | 878 +++++++++++++++ src/3P/ubox/log/logd.c | 202 ++++ src/3P/ubox/log/logread.c | 358 ++++++ src/3P/ubox/log/syslog.c | 309 ++++++ src/3P/ubox/log/syslog.h | 42 + src/3P/ubox/lsbloader.c | 187 ++++ src/3P/ubox/validate/cli.c | 255 +++++ src/3P/ubox/validate/libvalidate.h | 13 + src/3P/ubox/validate/validate.c | 1227 +++++++++++++++++++++ 12 files changed, 3624 insertions(+) create mode 100644 src/3P/ubox/CMakeLists.txt create mode 100644 src/3P/ubox/builders/cmake/CMakeLists.txt create mode 100644 src/3P/ubox/getrandom.c create mode 100644 src/3P/ubox/kmodloader.c create mode 100644 src/3P/ubox/log/logd.c create mode 100644 src/3P/ubox/log/logread.c create mode 100644 src/3P/ubox/log/syslog.c create mode 100644 src/3P/ubox/log/syslog.h create mode 100644 src/3P/ubox/lsbloader.c create mode 100644 src/3P/ubox/validate/cli.c create mode 100644 src/3P/ubox/validate/libvalidate.h create mode 100644 src/3P/ubox/validate/validate.c diff --git a/src/3P/ubox/CMakeLists.txt b/src/3P/ubox/CMakeLists.txt new file mode 100644 index 00000000..6cf0c934 --- /dev/null +++ b/src/3P/ubox/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(ubox C) +ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +IF(APPLE) + INCLUDE_DIRECTORIES(/opt/local/include) + LINK_DIRECTORIES(/opt/local/lib) +ENDIF() + +find_library(json NAMES json-c json) + +IF(DEBUG) + ADD_DEFINITIONS(-DDEBUG -g3) +ENDIF() + +ADD_EXECUTABLE(getrandom getrandom.c) +INSTALL(TARGETS getrandom + RUNTIME DESTINATION bin +) + +ADD_EXECUTABLE(kmodloader kmodloader.c) +TARGET_LINK_LIBRARIES(kmodloader ubox) + +INSTALL(TARGETS kmodloader + RUNTIME DESTINATION sbin +) + +ADD_EXECUTABLE(lsbloader lsbloader.c) +TARGET_LINK_LIBRARIES(lsbloader ubox ubus) + +INSTALL(TARGETS lsbloader + RUNTIME DESTINATION sbin +) + +ADD_LIBRARY(validate SHARED validate/validate.c) +INSTALL(TARGETS validate + LIBRARY DESTINATION lib +) + +FIND_PATH(uci_include_dir uci.h) +INCLUDE_DIRECTORIES(${uci_include_dir}) + +ADD_EXECUTABLE(validate_data validate/cli.c) +TARGET_LINK_LIBRARIES(validate_data ubox uci validate) +INSTALL(TARGETS validate_data + RUNTIME DESTINATION sbin +) + +ADD_EXECUTABLE(logd log/logd.c log/syslog.c) +TARGET_LINK_LIBRARIES(logd ubox ubus) +INSTALL(TARGETS logd + RUNTIME DESTINATION sbin +) + +ADD_EXECUTABLE(logread log/logread.c) +TARGET_LINK_LIBRARIES(logread ubox ubus ${json} blobmsg_json) +INSTALL(TARGETS logread + RUNTIME DESTINATION sbin +) diff --git a/src/3P/ubox/builders/cmake/CMakeLists.txt b/src/3P/ubox/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..80c9bb49 --- /dev/null +++ b/src/3P/ubox/builders/cmake/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required (VERSION 3.0) + +project (ubox) + +ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) + +set (CMAKE_MODULE_PATH "${MODULE_PATH}") + +set(DISABLE_TARGET_OPTIMIZATION ON) + +include (aw) + +# Logd + +add_executable (logd + $ENV{AWOXCVS}/AwoxAudio/Products/External/ubox/log/logd.c + $ENV{AWOXCVS}/AwoxAudio/Products/External/ubox/log/syslog.c) +target_link_libraries (logd ubox ubus) +install (TARGETS logd RUNTIME DESTINATION sbin) + +# Logread + +add_executable (logread + $ENV{AWOXCVS}/AwoxAudio/Products/External/ubox/log/logread.c) +target_link_libraries (logread ubox ubus json-c blobmsg_json) +install (TARGETS logread RUNTIME DESTINATION sbin) + +# kmodloader + +add_executable (kmodloader + $ENV{AWOXCVS}/AwoxAudio/Products/External/ubox/kmodloader.c) +target_link_libraries (kmodloader ubox) +install (TARGETS kmodloader RUNTIME DESTINATION ../sbin) diff --git a/src/3P/ubox/getrandom.c b/src/3P/ubox/getrandom.c new file mode 100644 index 00000000..96712028 --- /dev/null +++ b/src/3P/ubox/getrandom.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Etienne Champetier + * + * 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 2 of the License, or + * (at your option) 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. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#define ERROR_EXIT(fmt, ...) do { \ + fprintf(stderr, fmt, ## __VA_ARGS__); \ + return EXIT_FAILURE; \ + } while (0) + +static int usage(char *name) +{ + fprintf(stderr, "Usage: %s \n", name); + fprintf(stderr, " => return bytes from getrandom()\n"); + return EXIT_FAILURE; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + return usage(argv[0]); + + if (isatty(STDOUT_FILENO)) + ERROR_EXIT("Not outputting random to a tty\n"); + + int nbtot = atoi(argv[1]); + if (nbtot < 1) + ERROR_EXIT("Invalid param (must be > 0)\n"); + + char buf[256]; + int len = sizeof(buf); + while (nbtot > 0) { + if (nbtot <= sizeof(buf)) + len = nbtot; + if (syscall(SYS_getrandom, buf, len, 0) == -1) + ERROR_EXIT("getrandom() failed: %s\n", strerror(errno)); + if (write(STDOUT_FILENO, buf, len) != len) + ERROR_EXIT("write() failed: %s\n", strerror(errno)); + nbtot -= sizeof(buf); + } +} diff --git a/src/3P/ubox/kmodloader.c b/src/3P/ubox/kmodloader.c new file mode 100644 index 00000000..54bef3af --- /dev/null +++ b/src/3P/ubox/kmodloader.c @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * 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. + */ + +//#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DEF_MOD_PATH "/modules/%s/" + +enum { + SCANNED, + PROBE, + LOADED, +}; + +struct module { + struct avl_node avl; + + char *name; + char *depends; + char *opts; + + int size; + int usage; + int state; + int error; +}; + +static struct avl_tree modules; + +static char **module_folders = NULL; + +static int init_module_folders(void) +{ + int n = 0; + struct stat st; + struct utsname ver; + char *s, *e, *p, path[256], ldpath[256]; + + e = ldpath; + s = getenv("LD_LIBRARY_PATH"); + + if (s) + e += snprintf(ldpath, sizeof(ldpath), "%s:", s); + + e += snprintf(e, sizeof(ldpath) - (e - ldpath), "/lib"); + + uname(&ver); + + for (s = p = ldpath; p <= e; p++) { + if (*p != ':' && *p != '\0') + continue; + + *p = 0; + snprintf(path, sizeof(path), "%s" DEF_MOD_PATH, s, ver.release); + + if (!stat(path, &st) && S_ISDIR(st.st_mode)) { + module_folders = realloc(module_folders, sizeof(p) * (n + 2)); + + if (!module_folders) + return -1; + + module_folders[n++] = strdup(path); + } + + s = p + 1; + } + + if (!module_folders) + return -1; + + module_folders[n] = NULL; + return 0; +} + +static struct module *find_module(const char *name) +{ + struct module *m; + return avl_find_element(&modules, name, m, avl); +} + +static void free_modules(void) +{ + struct module *m, *tmp; + + avl_remove_all_elements(&modules, m, avl, tmp) + free(m); +} + +static char* get_module_path(char *name) +{ + char **p; + static char path[256]; + struct stat s; + + if (!stat(name, &s) && S_ISREG(s.st_mode)) + return name; + + for (p = module_folders; *p; p++) { + snprintf(path, sizeof(path), "%s%s.ko", *p, name); + if (!stat(path, &s) && S_ISREG(s.st_mode)) + return path; + } + + return NULL; +} + +static char* get_module_name(char *path) +{ + static char name[32]; + char *t; + + strncpy(name, basename(path), sizeof(name)); + + t = strstr(name, ".ko"); + if (t) + *t = '\0'; + + return name; +} + +static int elf64_find_section(char *map, const char *section, unsigned int *offset, unsigned int *size) +{ + const char *secnames; + Elf64_Ehdr *e; + Elf64_Shdr *sh; + int i; + + e = (Elf64_Ehdr *) map; + sh = (Elf64_Shdr *) (map + e->e_shoff); + + secnames = map + sh[e->e_shstrndx].sh_offset; + for (i = 0; i < e->e_shnum; i++) { + if (!strcmp(section, secnames + sh[i].sh_name)) { + *size = sh[i].sh_size; + *offset = sh[i].sh_offset; + return 0; + } + } + + return -1; +} + +static int elf32_find_section(char *map, const char *section, unsigned int *offset, unsigned int *size) +{ + const char *secnames; + Elf32_Ehdr *e; + Elf32_Shdr *sh; + int i; + + e = (Elf32_Ehdr *) map; + sh = (Elf32_Shdr *) (map + e->e_shoff); + + secnames = map + sh[e->e_shstrndx].sh_offset; + for (i = 0; i < e->e_shnum; i++) { + if (!strcmp(section, secnames + sh[i].sh_name)) { + *size = sh[i].sh_size; + *offset = sh[i].sh_offset; + return 0; + } + } + + return -1; +} + +static int elf_find_section(char *map, const char *section, unsigned int *offset, unsigned int *size) +{ + int clazz = map[EI_CLASS]; + + if (clazz == ELFCLASS32) + return elf32_find_section(map, section, offset, size); + else if (clazz == ELFCLASS64) + return elf64_find_section(map, section, offset, size); + + ULOG_ERR("unknown elf format %d\n", clazz); + + return -1; +} + +static struct module * +alloc_module(const char *name, const char *depends, int size) +{ + struct module *m; + char *_name, *_dep; + + m = calloc_a(sizeof(*m), + &_name, strlen(name) + 1, + &_dep, depends ? strlen(depends) + 2 : 0); + if (!m) + return NULL; + + m->avl.key = m->name = strcpy(_name, name); + m->opts = 0; + + if (depends) { + m->depends = strcpy(_dep, depends); + while (*_dep) { + if (*_dep == ',') + *_dep = '\0'; + _dep++; + } + } + + m->size = size; + avl_insert(&modules, &m->avl); + + return m; +} + +static int scan_loaded_modules(void) +{ + size_t buf_len = 0; + char *buf = NULL; + FILE *fp; + + fp = fopen("/proc/modules", "r"); + if (!fp) { + ULOG_ERR("failed to open /proc/modules\n"); + return -1; + } + + while (getline(&buf, &buf_len, fp) > 0) { + struct module m; + struct module *n; + + m.name = strtok(buf, " "); + m.size = atoi(strtok(NULL, " ")); + m.usage = atoi(strtok(NULL, " ")); + m.depends = strtok(NULL, " "); + + if (!m.name || !m.depends) + continue; + + n = alloc_module(m.name, m.depends, m.size); + n->usage = m.usage; + n->state = LOADED; + } + free(buf); + fclose(fp); + + return 0; +} + +static struct module* get_module_info(const char *module, const char *name) +{ + int fd = open(module, O_RDONLY); + unsigned int offset, size; + char *map = MAP_FAILED, *strings, *dep = NULL; + struct module *m = NULL; + struct stat s; + + if (fd < 0) { + ULOG_ERR("failed to open %s\n", module); + goto out; + } + + if (fstat(fd, &s) == -1) { + ULOG_ERR("failed to stat %s\n", module); + goto out; + } + + map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + ULOG_ERR("failed to mmap %s\n", module); + goto out; + } + + if (elf_find_section(map, ".modinfo", &offset, &size)) { + ULOG_ERR("failed to load the .modinfo section from %s\n", module); + goto out; + } + + strings = map + offset; + while (strings && (strings < map + offset + size)) { + char *sep; + int len; + + while (!strings[0]) + strings++; + sep = strstr(strings, "="); + if (!sep) + break; + len = sep - strings; + sep++; + if (!strncmp(strings, "depends=", len + 1)) + dep = sep; + strings = &sep[strlen(sep)]; + } + + m = alloc_module(name, dep, s.st_size); + + if (m) + m->state = SCANNED; + +out: + if (map != MAP_FAILED) + munmap(map, s.st_size); + + if (fd >= 0) + close(fd); + + return m; +} + +static int scan_module_folder(const char *dir) +{ + int gl_flags = GLOB_NOESCAPE | GLOB_MARK; + struct utsname ver; + char *path; + glob_t gl; + int j; + + uname(&ver); + path = alloca(strlen(dir) + sizeof("*.ko") + 1); + sprintf(path, "%s*.ko", dir); + + if (glob(path, gl_flags, NULL, &gl) < 0) + return -1; + + for (j = 0; j < gl.gl_pathc; j++) { + char *name = get_module_name(gl.gl_pathv[j]); + struct module *m; + + if (!name) + continue; + + m = find_module(name); + if (!m) + get_module_info(gl.gl_pathv[j], name); + } + + globfree(&gl); + + return 0; +} + +static int scan_module_folders(void) +{ + int rv = 0; + char **p; + + if (init_module_folders()) + return -1; + + for (p = module_folders; *p; p++) + rv |= scan_module_folder(*p); + + return rv; +} + +static int print_modinfo(char *module) +{ + int fd = open(module, O_RDONLY); + unsigned int offset, size; + struct stat s; + char *map = MAP_FAILED, *strings; + int rv = -1; + + if (fd < 0) { + ULOG_ERR("failed to open %s\n", module); + goto out; + } + + if (fstat(fd, &s) == -1) { + ULOG_ERR("failed to stat %s\n", module); + goto out; + } + + map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map == MAP_FAILED) { + ULOG_ERR("failed to mmap %s\n", module); + goto out; + } + + if (elf_find_section(map, ".modinfo", &offset, &size)) { + ULOG_ERR("failed to load the .modinfo section from %s\n", module); + goto out; + } + + strings = map + offset; + printf("module:\t\t%s\n", module); + while (strings && (strings < map + offset + size)) { + char *dup = NULL; + char *sep; + + while (!strings[0]) + strings++; + sep = strstr(strings, "="); + if (!sep) + break; + dup = strndup(strings, sep - strings); + sep++; + if (strncmp(strings, "parm", 4)) { + if (strlen(dup) < 7) + printf("%s:\t\t%s\n", dup, sep); + else + printf("%s:\t%s\n", dup, sep); + } + strings = &sep[strlen(sep)]; + if (dup) + free(dup); + } + + rv = 0; + +out: + if (map != MAP_FAILED) + munmap(map, s.st_size); + + if (fd >= 0) + close(fd); + + return rv; +} + +static int deps_available(struct module *m, int verbose) +{ + char *dep; + int err = 0; + + if (!m->depends || !strcmp(m->depends, "-") || !strcmp(m->depends, "")) + return 0; + + dep = m->depends; + + while (*dep) { + m = find_module(dep); + + if (verbose && !m) + ULOG_ERR("missing dependency %s\n", dep); + if (verbose && m && (m->state != LOADED)) + ULOG_ERR("dependency not loaded %s\n", dep); + if (!m || (m->state != LOADED)) + err++; + dep += strlen(dep) + 1; + } + + return err; +} + +static int insert_module(char *path, const char *options) +{ + void *data = 0; + struct stat s; + int fd, ret = -1; + + if (stat(path, &s)) { + ULOG_ERR("missing module %s\n", path); + return ret; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + ULOG_ERR("cannot open %s\n", path); + return ret; + } + + data = malloc(s.st_size); + if (read(fd, data, s.st_size) == s.st_size) + ret = syscall(__NR_init_module, data, (unsigned long) s.st_size, options); + else + ULOG_ERR("failed to read full module %s\n", path); + + close(fd); + free(data); + + return ret; +} + +static void load_moddeps(struct module *_m) +{ + char *dep; + struct module *m; + + if (!strcmp(_m->depends, "-") || !strcmp(_m->depends, "")) + return; + + dep = _m->depends; + + while (*dep) { + m = find_module(dep); + + if (!m) + ULOG_ERR("failed to find dependency %s\n", dep); + if (m && (m->state != LOADED)) { + m->state = PROBE; + load_moddeps(m); + } + + dep = dep + strlen(dep) + 1; + } +} + +static int iterations = 0; +static int load_modprobe(void) +{ + int loaded, todo; + struct module *m; + + avl_for_each_element(&modules, m, avl) + if (m->state == PROBE) + load_moddeps(m); + + do { + loaded = 0; + todo = 0; + avl_for_each_element(&modules, m, avl) { + if ((m->state == PROBE) && (!deps_available(m, 0))) { + if (!insert_module(get_module_path(m->name), (m->opts) ? (m->opts) : (""))) { + m->state = LOADED; + m->error = 0; + loaded++; + continue; + } + m->error = 1; + } + + if ((m->state == PROBE) || m->error) + todo++; + } + iterations++; + } while (loaded); + + return todo; +} + +static int print_insmod_usage(void) +{ + ULOG_INFO("Usage:\n\tinsmod filename [args]\n"); + + return -1; +} + +static int print_usage(char *arg) +{ + ULOG_INFO("Usage:\n\t%s module\n", arg); + + return -1; +} + +static int main_insmod(int argc, char **argv) +{ + char *name, *cur, *options; + int i, ret, len; + + if (argc < 2) + return print_insmod_usage(); + + name = get_module_name(argv[1]); + if (!name) { + ULOG_ERR("cannot find module - %s\n", argv[1]); + return -1; + } + + if (scan_loaded_modules()) + return -1; + + if (find_module(name)) { + ULOG_ERR("module is already loaded - %s\n", name); + return -1; + + } + + free_modules(); + + for (len = 0, i = 2; i < argc; i++) + len += strlen(argv[i]) + 1; + + options = malloc(len); + options[0] = 0; + cur = options; + for (i = 2; i < argc; i++) { + if (options[0]) { + *cur = ' '; + cur++; + } + cur += sprintf(cur, "%s", argv[i]); + } + + init_module_folders(); + + if (get_module_path(argv[1])) { + name = argv[1]; + } else if (!get_module_path(name)) { + fprintf(stderr, "Failed to find %s. Maybe it is a built in module ?\n", name); + return -1; + } + + ret = insert_module(get_module_path(name), options); + free(options); + + if (ret) + ULOG_ERR("failed to insert %s\n", get_module_path(name)); + + return ret; +} + +static int main_rmmod(int argc, char **argv) +{ + struct module *m; + char *name; + int ret; + + if (argc != 2) + return print_usage("rmmod"); + + if (scan_loaded_modules()) + return -1; + + name = get_module_name(argv[1]); + m = find_module(name); + if (!m) { + ULOG_ERR("module is not loaded\n"); + return -1; + } + ret = syscall(__NR_delete_module, m->name, 0); + + if (ret) + ULOG_ERR("unloading the module failed\n"); + + free_modules(); + + return ret; +} + +static int main_lsmod(int argc, char **argv) +{ + struct module *m; + char *dep; + + if (scan_loaded_modules()) + return -1; + + avl_for_each_element(&modules, m, avl) + if (m->state == LOADED) { + printf("%-20s%8d%3d ", + m->name, m->size, m->usage); + if (m->depends && strcmp(m->depends, "-") && strcmp(m->depends, "")) { + dep = m->depends; + while (*dep) { + printf("%s", dep); + dep = dep + strlen(dep) + 1; + if (*dep) + printf(","); + } + } + printf("\n"); + } + + free_modules(); + + return 0; +} + +static int main_modinfo(int argc, char **argv) +{ + struct module *m; + char *name; + + if (argc != 2) + return print_usage("modinfo"); + + if (scan_module_folders()) + return -1; + + name = get_module_name(argv[1]); + m = find_module(name); + if (!m) { + ULOG_ERR("cannot find module - %s\n", argv[1]); + return -1; + } + + name = get_module_path(m->name); + if (!name) { + ULOG_ERR("cannot find path of module - %s\n", m->name); + return -1; + } + + print_modinfo(name); + + return 0; +} + +static int main_modprobe(int argc, char **argv) +{ + struct module *m; + char *name; + + if (argc != 2) + return print_usage("modprobe"); + + if (scan_loaded_modules()) + return -1; + + if (scan_module_folders()) + return -1; + + name = get_module_name(argv[1]); + m = find_module(name); + if (m && m->state == LOADED) { + ULOG_ERR("%s is already loaded\n", name); + return -1; + } else if (!m) { + ULOG_ERR("failed to find a module named %s\n", name); + } else { + int fail; + + m->state = PROBE; + + fail = load_modprobe(); + + if (fail) { + ULOG_ERR("%d module%s could not be probed\n", + fail, (fail == 1) ? ("") : ("s")); + + avl_for_each_element(&modules, m, avl) + if ((m->state == PROBE) || m->error) + ULOG_ERR("- %s\n", m->name); + } + } + + free_modules(); + + return 0; +} + +static int main_loader(int argc, char **argv) +{ + int gl_flags = GLOB_NOESCAPE | GLOB_MARK; + char *dir = "/etc/modules.d/*"; + struct module *m; + glob_t gl; + char *path; + int fail, j; + + if (argc > 1) + dir = argv[1]; + + path = malloc(strlen(dir) + 2); + strcpy(path, dir); + strcat(path, "*"); + + if (scan_loaded_modules()) + return -1; + + if (scan_module_folders()) + return -1; + + syslog(LOG_INFO, "kmodloader: loading kernel modules from %s\n", path); + + if (glob(path, gl_flags, NULL, &gl) < 0) + goto out; + + for (j = 0; j < gl.gl_pathc; j++) { + FILE *fp = fopen(gl.gl_pathv[j], "r"); + size_t mod_len = 0; + char *mod = NULL; + + if (!fp) { + ULOG_ERR("failed to open %s\n", gl.gl_pathv[j]); + continue; + } + + while (getline(&mod, &mod_len, fp) > 0) { + char *nl = strchr(mod, '\n'); + struct module *m; + char *opts; + + if (nl) + *nl = '\0'; + + opts = strchr(mod, ' '); + if (opts) + *opts++ = '\0'; + + m = find_module(get_module_name(mod)); + if (!m || (m->state == LOADED)) + continue; + + if (opts) + m->opts = strdup(opts); + m->state = PROBE; + if (basename(gl.gl_pathv[j])[0] - '0' <= 9) + load_modprobe(); + + } + free(mod); + fclose(fp); + } + + fail = load_modprobe(); + + if (fail) { + ULOG_ERR("%d module%s could not be probed\n", + fail, (fail == 1) ? ("") : ("s")); + + avl_for_each_element(&modules, m, avl) + if ((m->state == PROBE) || (m->error)) + ULOG_ERR("- %s - %d\n", m->name, deps_available(m, 1)); + } + +out: + globfree(&gl); + free(path); + + return 0; +} + +static int avl_modcmp(const void *k1, const void *k2, void *ptr) +{ + const char *s1 = k1; + const char *s2 = k2; + + while (*s1 && ((*s1 == *s2) || + ((*s1 == '_') && (*s2 == '-')) || + ((*s1 == '-') && (*s2 == '_')))) + { + s1++; + s2++; + } + + return *(const unsigned char *)s1 - *(const unsigned char *)s2; +} + +int main(int argc, char **argv) +{ + char *exec = basename(*argv); + + avl_init(&modules, avl_modcmp, false, NULL); + if (!strcmp(exec, "insmod")) + return main_insmod(argc, argv); + + if (!strcmp(exec, "rmmod")) + return main_rmmod(argc, argv); + + if (!strcmp(exec, "lsmod")) + return main_lsmod(argc, argv); + + if (!strcmp(exec, "modinfo")) + return main_modinfo(argc, argv); + + if (!strcmp(exec, "modprobe")) + return main_modprobe(argc, argv); + + return main_loader(argc, argv); +} diff --git a/src/3P/ubox/log/logd.c b/src/3P/ubox/log/logd.c new file mode 100644 index 00000000..b39b4719 --- /dev/null +++ b/src/3P/ubox/log/logd.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * 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. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "syslog.h" + +int debug = 0; +static struct blob_buf b; +static struct ubus_auto_conn conn; +static LIST_HEAD(clients); + +static const struct blobmsg_policy read_policy = + { .name = "lines", .type = BLOBMSG_TYPE_INT32 }; + +static const struct blobmsg_policy write_policy = + { .name = "event", .type = BLOBMSG_TYPE_STRING }; + +struct client { + struct list_head list; + + struct ustream_fd s; + int fd; +}; + +static void +client_close(struct ustream *s) +{ + struct client *cl = container_of(s, struct client, s.stream); + + list_del(&cl->list); + ustream_free(s); + close(cl->fd); + free(cl); +} + +static void client_notify_state(struct ustream *s) +{ + client_close(s); +} + +static int +read_log(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct client *cl; + struct blob_attr *tb; + struct log_head *l; + int count = 0; + int fds[2]; + int ret; + + if (msg) { + blobmsg_parse(&read_policy, 1, &tb, blob_data(msg), blob_len(msg)); + if (tb) + count = blobmsg_get_u32(tb); + } + + if (pipe(fds) == -1) { + fprintf(stderr, "logd: failed to create pipe: %s\n", strerror(errno)); + return -1; + } + ubus_request_set_fd(ctx, req, fds[0]); + cl = calloc(1, sizeof(*cl)); + cl->s.stream.notify_state = client_notify_state; + cl->fd = fds[1]; + ustream_fd_init(&cl->s, cl->fd); + list_add(&cl->list, &clients); + l = log_list(count, NULL); + while ((!tb || count) && l) { + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "msg", l->data); + blobmsg_add_u32(&b, "id", l->id); + blobmsg_add_u32(&b, "priority", l->priority); + blobmsg_add_u32(&b, "source", l->source); + blobmsg_add_u64(&b, "time", l->ts.tv_sec * 1000LL); + l = log_list(count, l); + ret = ustream_write(&cl->s.stream, (void *) b.head, blob_len(b.head) + sizeof(struct blob_attr), false); + blob_buf_free(&b); + if (ret < 0) + break; + } + return 0; +} + +static int +write_log(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb; + char *event; + + if (msg) { + blobmsg_parse(&write_policy, 1, &tb, blob_data(msg), blob_len(msg)); + if (tb) { + event = blobmsg_get_string(tb); + log_add(event, strlen(event) + 1, SOURCE_SYSLOG); + } + } + + return 0; +} + +static const struct ubus_method log_methods[] = { + { .name = "read", .handler = read_log, .policy = &read_policy, .n_policy = 1 }, + { .name = "write", .handler = write_log, .policy = &write_policy, .n_policy = 1 }, +}; + +static struct ubus_object_type log_object_type = + UBUS_OBJECT_TYPE("log", log_methods); + +static struct ubus_object log_object = { + .name = "log", + .type = &log_object_type, + .methods = log_methods, + .n_methods = ARRAY_SIZE(log_methods), +}; + +void +ubus_notify_log(struct log_head *l) +{ + struct client *c; + + if (list_empty(&clients)) + return; + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "msg", l->data); + blobmsg_add_u32(&b, "id", l->id); + blobmsg_add_u32(&b, "priority", l->priority); + blobmsg_add_u32(&b, "source", l->source); + blobmsg_add_u64(&b, "time", (((__u64) l->ts.tv_sec) * 1000) + (l->ts.tv_nsec / 1000000)); + + list_for_each_entry(c, &clients, list) + ustream_write(&c->s.stream, (void *) b.head, blob_len(b.head) + sizeof(struct blob_attr), false); + + blob_buf_free(&b); +} + +static void +ubus_connect_handler(struct ubus_context *ctx) +{ + int ret; + + ret = ubus_add_object(ctx, &log_object); + if (ret) { + fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); + exit(1); + } + fprintf(stderr, "log: connected to ubus\n"); +} + +int +main(int argc, char **argv) +{ + int ch, log_size = 16; + + signal(SIGPIPE, SIG_IGN); + while ((ch = getopt(argc, argv, "S:")) != -1) { + switch (ch) { + case 'S': + log_size = atoi(optarg); + if (log_size < 1) + log_size = 16; + break; + } + } + log_size *= 1024; + + uloop_init(); + log_init(log_size); + conn.cb = ubus_connect_handler; + ubus_auto_connect(&conn); + uloop_run(); + log_shutdown(); + uloop_done(); + + return 0; +} diff --git a/src/3P/ubox/log/logread.c b/src/3P/ubox/log/logread.c new file mode 100644 index 00000000..ed0f9d41 --- /dev/null +++ b/src/3P/ubox/log/logread.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * 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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SYSLOG_NAMES +#include + +#include +#include +#include +#include +#include +#include "syslog.h" + +enum { + LOG_STDOUT, + LOG_FILE, + LOG_NET, +}; + +enum { + LOG_MSG, + LOG_ID, + LOG_PRIO, + LOG_SOURCE, + LOG_TIME, + __LOG_MAX +}; + +static const struct blobmsg_policy log_policy[] = { + [LOG_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING }, + [LOG_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, + [LOG_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 }, + [LOG_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_INT32 }, + [LOG_TIME] = { .name = "time", .type = BLOBMSG_TYPE_INT64 }, +}; + +static struct uloop_timeout retry; +static struct uloop_fd sender; +static regex_t regexp_preg; +static const char *log_file, *log_ip, *log_port, *log_prefix, *pid_file, *hostname, *regexp_pattern; +static int log_type = LOG_STDOUT; +static int log_size, log_udp, log_follow, log_trailer_null = 0; + +static const char* getcodetext(int value, CODE *codetable) { + CODE *i; + + if (value >= 0) + for (i = codetable; i->c_val != -1; i++) + if (i->c_val == value) + return (i->c_name); + return ""; +}; + +static void log_handle_reconnect(struct uloop_timeout *timeout) +{ + sender.fd = usock((log_udp) ? (USOCK_UDP) : (USOCK_TCP), log_ip, log_port); + if (sender.fd < 0) { + fprintf(stderr, "failed to connect: %s\n", strerror(errno)); + uloop_timeout_set(&retry, 1000); + } else { + uloop_fd_add(&sender, ULOOP_READ); + syslog(LOG_INFO, "Logread connected to %s:%s\n", log_ip, log_port); + } +} + +static void log_handle_fd(struct uloop_fd *u, unsigned int events) +{ + if (u->eof) { + uloop_fd_delete(u); + close(sender.fd); + sender.fd = -1; + uloop_timeout_set(&retry, 1000); + } +} + +static int log_notify(struct blob_attr *msg) +{ + struct blob_attr *tb[__LOG_MAX]; + struct stat s; + char buf[512]; + uint32_t p; + char *str; + time_t t; + char *c, *m; + int ret = 0; + + if (sender.fd < 0) + return 0; + + blobmsg_parse(log_policy, ARRAY_SIZE(log_policy), tb, blob_data(msg), blob_len(msg)); + if (!tb[LOG_ID] || !tb[LOG_PRIO] || !tb[LOG_SOURCE] || !tb[LOG_TIME] || !tb[LOG_MSG]) + return 1; + + if ((log_type == LOG_FILE) && log_size && (!stat(log_file, &s)) && (s.st_size > log_size)) { + char *old = malloc(strlen(log_file) + 5); + + close(sender.fd); + if (old) { + sprintf(old, "%s.old", log_file); + rename(log_file, old); + free(old); + } + sender.fd = open(log_file, O_CREAT | O_WRONLY | O_APPEND, 0600); + if (sender.fd < 0) { + fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno)); + exit(-1); + } + } + + m = blobmsg_get_string(tb[LOG_MSG]); + if (regexp_pattern && + regexec(®exp_preg, m, 0, NULL, 0) == REG_NOMATCH) + return 0; + t = blobmsg_get_u64(tb[LOG_TIME]) / 1000; + c = ctime(&t); + p = blobmsg_get_u32(tb[LOG_PRIO]); + c[strlen(c) - 1] = '\0'; + str = blobmsg_format_json(msg, true); + if (log_type == LOG_NET) { + int err; + + snprintf(buf, sizeof(buf), "<%u>", p); + strncat(buf, c + 4, 16); + if (hostname) { + strncat(buf, hostname, sizeof(buf) - strlen(buf) - 1); + strncat(buf, " ", sizeof(buf) - strlen(buf) - 1); + } + if (log_prefix) { + strncat(buf, log_prefix, sizeof(buf) - strlen(buf) - 1); + strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1); + } + if (blobmsg_get_u32(tb[LOG_SOURCE]) == SOURCE_KLOG) + strncat(buf, "kernel: ", sizeof(buf) - strlen(buf) - 1); + strncat(buf, m, sizeof(buf) - strlen(buf) - 1); + if (log_udp) + err = write(sender.fd, buf, strlen(buf)); + else { + size_t buflen = strlen(buf); + if (!log_trailer_null) + buf[buflen] = '\n'; + err = send(sender.fd, buf, buflen + 1, 0); + } + + if (err < 0) { + syslog(LOG_INFO, "failed to send log data to %s:%s via %s\n", + log_ip, log_port, (log_udp) ? ("udp") : ("tcp")); + uloop_fd_delete(&sender); + close(sender.fd); + sender.fd = -1; + uloop_timeout_set(&retry, 1000); + } + } else { + snprintf(buf, sizeof(buf), "%s %s.%s%s %s\n", + c, getcodetext(LOG_FAC(p) << 3, facilitynames), getcodetext(LOG_PRI(p), prioritynames), + (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"), m); + ret = write(sender.fd, buf, strlen(buf)); + } + + free(str); + if (log_type == LOG_FILE) + fsync(sender.fd); + + return ret; +} + +static int usage(const char *prog) +{ + fprintf(stderr, "Usage: %s [options]\n" + "Options:\n" + " -s Path to ubus socket\n" + " -l Got only the last 'count' messages\n" + " -e Filter messages with a regexp\n" + " -r Stream message to a server\n" + " -F Log file\n" + " -S Log size\n" + " -p PID file\n" + " -h Add hostname to the message\n" + " -P Prefix custom text to streamed messages\n" + " -f Follow log messages\n" + " -u Use UDP as the protocol\n" + " -0 Use \\0 instead of \\n as trailer when using TCP\n" + "\n", prog); + return 1; +} + +static void logread_fd_data_cb(struct ustream *s, int bytes) +{ + while (true) { + struct blob_attr *a; + int len, cur_len; + + a = (void*) ustream_get_read_buf(s, &len); + if (len < sizeof(*a)) + break; + + cur_len = blob_len(a) + sizeof(*a); + if (len < cur_len) + break; + + log_notify(a); + ustream_consume(s, cur_len); + } + if (!log_follow) + uloop_end(); +} + +static void logread_fd_cb(struct ubus_request *req, int fd) +{ + static struct ustream_fd test_fd; + + test_fd.stream.notify_read = logread_fd_data_cb; + ustream_fd_init(&test_fd, fd); +} + +int main(int argc, char **argv) +{ + static struct ubus_request req; + struct ubus_context *ctx; + uint32_t id; + const char *ubus_socket = NULL; + int ch, ret, lines = 0; + static struct blob_buf b; + int tries = 5; + + signal(SIGPIPE, SIG_IGN); + + while ((ch = getopt(argc, argv, "u0fcs:l:r:F:p:S:P:h:e:")) != -1) { + switch (ch) { + case 'u': + log_udp = 1; + break; + case '0': + log_trailer_null = 1; + break; + case 's': + ubus_socket = optarg; + break; + case 'r': + log_ip = optarg++; + log_port = argv[optind++]; + break; + case 'F': + log_file = optarg; + break; + case 'p': + pid_file = optarg; + break; + case 'P': + log_prefix = optarg; + break; + case 'f': + log_follow = 1; + break; + case 'l': + lines = atoi(optarg); + break; + case 'S': + log_size = atoi(optarg); + if (log_size < 1) + log_size = 1; + log_size *= 1024; + break; + case 'h': + hostname = optarg; + break; + case 'e': + if (!regcomp(®exp_preg, optarg, REG_NOSUB)) { + regexp_pattern = optarg; + } + break; + default: + return usage(*argv); + } + } + uloop_init(); + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + ubus_add_uloop(ctx); + + /* ugly ugly ugly ... we need a real reconnect logic */ + do { + ret = ubus_lookup_id(ctx, "log", &id); + if (ret) { + fprintf(stderr, "Failed to find log object: %s\n", ubus_strerror(ret)); + sleep(1); + continue; + } + + blob_buf_init(&b, 0); + if (lines) + blobmsg_add_u32(&b, "lines", lines); + else if (log_follow) + blobmsg_add_u32(&b, "lines", 0); + if (log_follow) { + if (pid_file) { + FILE *fp = fopen(pid_file, "w+"); + if (fp) { + fprintf(fp, "%d", getpid()); + fclose(fp); + } + } + } + + if (log_ip && log_port) { + openlog("logread", LOG_PID, LOG_DAEMON); + log_type = LOG_NET; + sender.cb = log_handle_fd; + retry.cb = log_handle_reconnect; + uloop_timeout_set(&retry, 1000); + } else if (log_file) { + log_type = LOG_FILE; + sender.fd = open(log_file, O_CREAT | O_WRONLY| O_APPEND, 0600); + if (sender.fd < 0) { + fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno)); + exit(-1); + } + } else { + sender.fd = STDOUT_FILENO; + } + + ubus_invoke_async(ctx, id, "read", b.head, &req); + req.fd_cb = logread_fd_cb; + ubus_complete_request_async(ctx, &req); + + uloop_run(); + ubus_free(ctx); + uloop_done(); + + } while (ret && tries--); + + return ret; +} diff --git a/src/3P/ubox/log/syslog.c b/src/3P/ubox/log/syslog.c new file mode 100644 index 00000000..b7e1e46e --- /dev/null +++ b/src/3P/ubox/log/syslog.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * 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. + */ + +#include + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,21) +typedef unsigned short sa_family_t; +#endif + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "syslog.h" + +#define LOG_DEFAULT_SIZE (16 * 1024) +#define LOG_DEFAULT_SOCKET "/dev/log" +#define LOG_LINE_LEN 256 +#define SYSLOG_PADDING 16 + +#define KLOG_DEFAULT_PROC "/proc/kmsg" + +#define PAD(x) (x % 4) ? (((x) - (x % 4)) + 4) : (x) + +static char *log_dev = LOG_DEFAULT_SOCKET; +static int log_size = LOG_DEFAULT_SIZE; +static struct log_head *log, *log_end, *oldest, *newest; +static int current_id = 0; +static regex_t pat_prio; +static regex_t pat_tstamp; + +static struct log_head* +log_next(struct log_head *h, int size) +{ + struct log_head *n = (struct log_head *) &h->data[PAD(sizeof(struct log_head) + size)]; + + return (n >= log_end) ? (log) : (n); +} + +void +log_add(char *buf, int size, int source) +{ + regmatch_t matches[4]; + struct log_head *next; + int priority = 0; + int ret; + + /* bounce out if we don't have init'ed yet (regmatch etc will blow) */ + if (!log) { + fprintf(stderr, "%s", buf); + return; + } + + /* strip trailing newline */ + if (buf[size - 2] == '\n') { + buf[size - 2] = '\0'; + size -= 1; + } + + /* strip the priority */ + ret = regexec(&pat_prio, buf, 3, matches, 0); + if (!ret) { + priority = atoi(&buf[matches[1].rm_so]); + size -= matches[2].rm_so; + buf += matches[2].rm_so; + } + +#if 0 + /* strip kernel timestamp */ + ret = regexec(&pat_tstamp,buf, 4, matches, 0); + if ((source == SOURCE_KLOG) && !ret) { + size -= matches[3].rm_so; + buf += matches[3].rm_so; + } +#endif + + /* strip syslog timestamp */ + if ((source == SOURCE_SYSLOG) && (size > SYSLOG_PADDING) && (buf[SYSLOG_PADDING - 1] == ' ')) { + size -= SYSLOG_PADDING; + buf += SYSLOG_PADDING; + } + + //fprintf(stderr, "-> %d - %s\n", priority, buf); + + /* find new oldest entry */ + next = log_next(newest, size); + if (next > newest) { + while ((oldest > newest) && (oldest <= next) && (oldest != log)) + oldest = log_next(oldest, oldest->size); + } else { + //fprintf(stderr, "Log wrap\n"); + newest->size = 0; + next = log_next(log, size); + for (oldest = log; oldest <= next; oldest = log_next(oldest, oldest->size)) + ; + newest = log; + } + + /* add the log message */ + newest->size = size; + newest->id = current_id++; + newest->priority = priority; + newest->source = source; + clock_gettime(CLOCK_REALTIME, &newest->ts); + strcpy(newest->data, buf); + + ubus_notify_log(newest); + + newest = next; +} + +static void +slog_cb(struct ustream *s, int bytes) +{ + struct ustream_buf *buf = s->r.head; + char *str; + int len; + + do { + str = ustream_get_read_buf(s, NULL); + if (!str) + break; + len = strlen(buf->data); + if (!len) { + bytes -= 1; + ustream_consume(s, 1); + continue; + } + log_add(buf->data, len + 1, SOURCE_SYSLOG); + ustream_consume(s, len); + bytes -= len; + } while (bytes > 0); +} + +static void +klog_cb(struct ustream *s, int bytes) +{ + struct ustream_buf *buf = s->r.head; + char *newline, *str; + int len; + + do { + str = ustream_get_read_buf(s, NULL); + if (!str) + break; + newline = strchr(buf->data, '\n'); + if (!newline) + break; + *newline = 0; + len = newline + 1 - str; + log_add(buf->data, len, SOURCE_KLOG); + ustream_consume(s, len); + } while (1); +} + +struct ustream_fd slog = { + .stream.string_data = true, + .stream.notify_read = slog_cb, +}; + +struct ustream_fd klog = { + .stream.string_data = true, + .stream.notify_read = klog_cb, +}; + +static int +klog_open(void) +{ + int fd; + + fd = open(KLOG_DEFAULT_PROC, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "Failed to open %s\n", KLOG_DEFAULT_PROC); + return -1; + } + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + ustream_fd_init(&klog, fd); + return 0; +} + +static int +syslog_open(void) +{ + int fd; + + unlink(log_dev); + fd = usock(USOCK_UNIX | USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, log_dev, NULL); + if (fd < 0) { + fprintf(stderr,"Failed to open %s\n", log_dev); + return -1; + } + chmod(log_dev, 0666); + ustream_fd_init(&slog, fd); + return 0; +} + +struct log_head* +log_list(int count, struct log_head *h) +{ + unsigned int min = count; + + if (count) + min = (count < current_id) ? (current_id - count) : (0); + if (!h && oldest->id >= min) + return oldest; + if (!h) + h = oldest; + + while (h != newest) { + h = log_next(h, h->size); + if (!h->size && (h > newest)) + h = log; + if (h->id >= min && (h != newest)) + return h; + } + + return NULL; +} + +int +log_buffer_init(int size) +{ + struct log_head *_log = malloc(size); + + if (!_log) { + fprintf(stderr, "Failed to initialize log buffer with size %d\n", log_size); + return -1; + } + + memset(_log, 0, size); + + if (log && ((log_size + sizeof(struct log_head)) < size)) { + struct log_head *start = _log; + struct log_head *end = ((void*) _log) + size; + struct log_head *l; + + l = log_list(0, NULL); + while ((start < end) && l && l->size) { + memcpy(start, l, PAD(sizeof(struct log_head) + l->size)); + start = (struct log_head *) &l->data[PAD(l->size)]; + l = log_list(0, l); + } + free(log); + newest = start; + newest->size = 0; + oldest = log = _log; + log_end = ((void*) log) + size; + } else { + oldest = newest = log = _log; + log_end = ((void*) log) + size; + } + log_size = size; + + return 0; +} + +void +log_init(int _log_size) +{ + if (_log_size > 0) + log_size = _log_size; + + regcomp(&pat_prio, "^<([0-9]*)>(.*)", REG_EXTENDED); + regcomp(&pat_tstamp, "^\[[ 0]*([0-9]*).([0-9]*)] (.*)", REG_EXTENDED); + + if (log_buffer_init(log_size)) { + fprintf(stderr, "Failed to allocate log memory\n"); + exit(-1); + } + + syslog_open(); + klog_open(); + openlog("sysinit", LOG_CONS, LOG_DAEMON); +} + +void +log_shutdown(void) +{ + ustream_free(&slog.stream); + ustream_free(&klog.stream); + close(slog.fd.fd); + close(klog.fd.fd); + free(log); +} diff --git a/src/3P/ubox/log/syslog.h b/src/3P/ubox/log/syslog.h new file mode 100644 index 00000000..81a039f3 --- /dev/null +++ b/src/3P/ubox/log/syslog.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * 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. + */ + +#ifndef __SYSLOG_H +#define __SYSLOG_H + +enum { + SOURCE_KLOG = 0, + SOURCE_SYSLOG = 1, + SOURCE_INTERNAL = 2, + SOURCE_ANY = 0xff, +}; + +struct log_head { + unsigned int size; + unsigned int id; + int priority; + int source; + struct timespec ts; + char data[]; +}; + +void log_init(int log_size); +void log_shutdown(void); + +typedef void (*log_list_cb)(struct log_head *h); +struct log_head* log_list(int count, struct log_head *h); +int log_buffer_init(int size); +void log_add(char *buf, int size, int source); +void ubus_notify_log(struct log_head *l); + +#endif diff --git a/src/3P/ubox/lsbloader.c b/src/3P/ubox/lsbloader.c new file mode 100644 index 00000000..b40a505b --- /dev/null +++ b/src/3P/ubox/lsbloader.c @@ -0,0 +1,187 @@ +/* + * 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; version 2 of the License + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2012 John Crispin + */ + +#include +#include +#define __USE_GNU +#include +#include +#include + +#include +#include +#include "libubus.h" + +struct initd { + struct list_head list; + + char *name; + char *exec; + char *desc; + char *tpl; + char **deps; + int start; + int stop; +}; + +static LIST_HEAD(initds); +static regex_t pat_provides, pat_require, pat_start, pat_stop, pat_desc, pat_exec, pat_tpl; +static struct ubus_context *ctx; +static struct blob_buf b; +static uint32_t service; + +static void initd_free(struct initd *i) +{ + if (i->name) + free(i->name); + if (i->exec) + free(i->exec); + if (i->desc) + free(i->desc); + if (i->tpl) + free(i->tpl); +} + +static int initd_parse(const char *file) +{ + FILE *fp; + struct initd *i; + regmatch_t matches[2]; + char buffer[1024]; + ssize_t len; + + fp = fopen(file, "r"); + if (!fp) { + fprintf(stderr, "failed to open %s\n", file); + return -1; + } + len = fread(buffer, 1, sizeof(buffer) - 1, fp); + fclose(fp); + + if (len < 1) { + fprintf(stderr, "failed to read from %s\n", file); + return -1; + } + buffer[len] = '\0'; + + i = malloc(sizeof(struct initd)); + if (!i) { + fprintf(stderr, "failed to alloc initd struct\n"); + return -1; + } + memset(i, 0, sizeof(*i)); + + if (!regexec(&pat_provides, buffer, 2, matches, 0)) + i->name = strndup(buffer + matches[1].rm_so, (size_t)matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_exec, buffer, 2, matches, 0)) + i->exec = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_desc, buffer, 2, matches, 0)) + i->desc = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_tpl, buffer, 2, matches, 0)) + i->tpl = strndup(buffer + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (!regexec(&pat_start, buffer, 2, matches, 0)) + i->start += atoi(buffer + matches[1].rm_so); + if (!regexec(&pat_stop, buffer, 2, matches, 0)) + i->stop += atoi(buffer + matches[1].rm_so); + + if (i->name && i->exec) + list_add(&i->list, &initds); + else + initd_free(i); + + return 0; +} + +static void initd_init(void) +{ + int gl_flags = GLOB_NOESCAPE | GLOB_MARK; + glob_t gl; + + regcomp(&pat_provides, "# Provides:[ \t]*([a-zA-Z0-9]+)", REG_EXTENDED); + regcomp(&pat_require, "# Required-Start:[ \t]*([a-zA-Z0-9 ]+)", REG_EXTENDED); + regcomp(&pat_start, "# Default-Start:[ \t]*([0-9])", REG_EXTENDED); + regcomp(&pat_stop, "# Default-Stop:[ \t]*([0-9])", REG_EXTENDED); + regcomp(&pat_desc, "# Description:[ \t]*([a-zA-Z0-9 ]+)", REG_EXTENDED); + regcomp(&pat_exec, "# X-Exec:[ \t]*([a-zA-Z0-9/ ]+)", REG_EXTENDED); + regcomp(&pat_tpl, "# X-Template:[ \t]*([a-zA-Z0-9/.]+)", REG_EXTENDED); + + if (glob("/etc/rc.d/P*", gl_flags, NULL, &gl) >= 0) { + int j; + for (j = 0; j < gl.gl_pathc; j++) + initd_parse(gl.gl_pathv[j]); + } + globfree(&gl); + + regfree(&pat_provides); + regfree(&pat_require); + regfree(&pat_start); + regfree(&pat_stop); + regfree(&pat_desc); + regfree(&pat_exec); + regfree(&pat_tpl); +} + +static int init_services(void) +{ + struct initd *i; + void *instances, *instance, *command; + + list_for_each_entry(i, &initds, list) { + char *t; + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "name", i->name); + instances = blobmsg_open_table(&b, "instances"); + instance = blobmsg_open_table(&b, "instance"); + command = blobmsg_open_array(&b, "command"); + t = strtok(i->exec, " "); + while (t) { + blobmsg_add_string(&b, NULL, t); + t = strtok(NULL, " "); + } + blobmsg_close_array(&b, command); + blobmsg_close_table(&b, instance); + blobmsg_close_table(&b, instances); + ubus_invoke(ctx, service, "add", b.head, NULL, 0, 1000); + } + + return 0; +} + +int main(int argc, char **argv) +{ + int ret; + + initd_init(); + + if (list_empty(&initds)) + return 0; + + ctx = ubus_connect(NULL); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + ret = ubus_lookup_id(ctx, "service", &service); + if (ret) { + fprintf(stderr, "Failed to find service object: %s\n", ubus_strerror(ret)); + return -1; + } + + return init_services(); +} diff --git a/src/3P/ubox/validate/cli.c b/src/3P/ubox/validate/cli.c new file mode 100644 index 00000000..07d2b2ef --- /dev/null +++ b/src/3P/ubox/validate/cli.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "libvalidate.h" + +static void +print_usage(char *argv) +{ + fprintf(stderr, "%s \t- validate a value against a type\n", argv); + fprintf(stderr, "%s 'option:datatype:default' 'option:datatype:default' ...\n", argv); +} + +static const char * +bool_to_num(const char *val) +{ + if (!strcmp(val, "0") || !strcmp(val, "off") || !strcmp(val, "false") || !strcmp(val, "no") || !strcmp(val, "disabled")) + return "0"; + if (!strcmp(val, "1") || !strcmp(val, "on") || !strcmp(val, "true") || !strcmp(val, "yes") || !strcmp(val, "enabled")) + return "1"; + + return ""; +} + +static bool +parse_tuple(char *tuple, char **option, char **expr, char **def) +{ + char *p; + bool esc; + + for (esc = false, p = *option = tuple, *expr = NULL, *def = NULL; *p; p++) + { + if (!esc && *p == '\\') + { + esc = true; + continue; + } + + if (!esc && *p == ':') + { + *p++ = 0; + + if (!*expr) + *expr = p; + else if (!*def) + *def = p; + else + break; + } + + esc = false; + } + + return (*expr != NULL); +} + +static void +escape_value(enum dt_type type, const char *val) +{ + const char *p; + + switch(type) + { + case DT_BOOL: + printf("%s", bool_to_num(val)); + break; + + case DT_STRING: + printf("'"); + + for (p = val; *p; p++) + if (*p == '\'') + printf("'\"'\"'"); + else + printf("%c", *p); + + printf("'"); + break; + + default: + printf("%s", val); + break; + } +} + +static void +export_value(enum dt_type type, const char *name, const char *val) +{ + if ((type == DT_INVALID) || !val || !*val) + { + printf("unset -v %s; ", name); + return; + } + + printf("%s=", name); + escape_value(type, val); + printf("; "); +} + +static int +validate_value(struct uci_ptr *ptr, const char *expr, const char *def) +{ + int i = 0; + bool empty = true, first = true; + enum dt_type type = DT_INVALID; + struct uci_element *e; + struct uci_option *opt = ptr->o; + + if (opt->type == UCI_TYPE_LIST) + { + uci_foreach_element(&opt->v.list, e) + { + if (!e->name || !*e->name) + continue; + + empty = false; + break; + } + + if (empty) + { + export_value(DT_STRING, ptr->option, def); + return 0; + } + + uci_foreach_element(&opt->v.list, e) + { + if (!e->name || !*e->name) + continue; + + if (first) + printf("%s=", ptr->option); + else + printf("\\ "); + + first = false; + type = dt_parse(expr, e->name); + + if (type != DT_INVALID) + escape_value(type, e->name); + + fprintf(stderr, "%s.%s.%s[%u]=%s validates as %s with %s\n", + ptr->package, ptr->section, ptr->option, i++, e->name, + expr, type ? "true" : "false"); + } + + printf("; "); + } + else + { + if (!opt->v.string || !*opt->v.string) + { + export_value(DT_STRING, ptr->option, def); + return 0; + } + + type = dt_parse(expr, opt->v.string); + export_value(type, ptr->option, opt->v.string); + + fprintf(stderr, "%s.%s.%s=%s validates as %s with %s\n", + ptr->package, ptr->section, ptr->option, opt->v.string, + expr, type ? "true" : "false"); + } + return type ? 0 : -1; +} + +static int +validate_option(struct uci_context *ctx, char *package, char *section, char *option) +{ + char *opt, *expr, *def; + struct uci_ptr ptr = { 0 }; + + if (!parse_tuple(option, &opt, &expr, &def)) + { + fprintf(stderr, "%s is not a valid option\n", option); + return -1; + } + + ptr.package = package; + ptr.section = section; + ptr.option = opt; + + if (uci_lookup_ptr(ctx, &ptr, NULL, false) || + !(ptr.flags & UCI_LOOKUP_COMPLETE) || + (ptr.last->type != UCI_TYPE_OPTION)) + { + export_value(DT_STRING, opt, def); + return 0; + } + + return validate_value(&ptr, expr, def); +} + +int +main(int argc, char **argv) +{ + struct uci_context *ctx; + struct uci_package *package; + char *opt, *expr, *def; + int len = argc - 4; + enum dt_type rv; + int i, rc; + + if (argc == 3) { + rv = dt_parse(argv[1], argv[2]); + fprintf(stderr, "%s - %s = %s\n", argv[1], argv[2], rv ? "true" : "false"); + return rv ? 0 : 1; + } else if (argc < 5) { + print_usage(*argv); + return -1; + } + + if (*argv[3] == '\0') { + printf("json_add_object; "); + printf("json_add_string \"package\" \"%s\"; ", argv[1]); + printf("json_add_string \"type\" \"%s\"; ", argv[2]); + printf("json_add_object \"data\"; "); + + for (i = 0; i < len; i++) { + if (!parse_tuple(argv[4 + i], &opt, &expr, &def)) + continue; + + printf("json_add_string \"%s\" \"%s\"; ", opt, expr); + } + + printf("json_close_object; "); + printf("json_close_object; "); + + return 0; + } + + ctx = uci_alloc_context(); + if (!ctx) + return -1; + + if (uci_load(ctx, argv[1], &package)) + return -1; + + rc = 0; + for (i = 0; i < len; i++) { + if (validate_option(ctx, argv[1], argv[3], argv[4 + i])) { + rc = -1; + } + } + + return rc; +} diff --git a/src/3P/ubox/validate/libvalidate.h b/src/3P/ubox/validate/libvalidate.h new file mode 100644 index 00000000..145e08e3 --- /dev/null +++ b/src/3P/ubox/validate/libvalidate.h @@ -0,0 +1,13 @@ +#ifndef _VALIDATE_H__ +#define _VALIDATE_H__ + +enum dt_type { + DT_INVALID, + DT_BOOL, + DT_NUMBER, + DT_STRING +}; + +enum dt_type dt_parse(const char *code, const char *value); + +#endif diff --git a/src/3P/ubox/validate/validate.c b/src/3P/ubox/validate/validate.c new file mode 100644 index 00000000..5bc3bc4e --- /dev/null +++ b/src/3P/ubox/validate/validate.c @@ -0,0 +1,1227 @@ +/* + * Copyright (C) 2013 Jo-Philipp Wich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include "libvalidate.h" + +enum dt_optype { + OP_UNKNOWN, + OP_NUMBER, + OP_STRING, + OP_FUNCTION +}; + +struct dt_fun; + +struct dt_op { + enum dt_optype type; + const char *next; + int length; + int nextop; + union { + bool boolean; + double number; + const char *string; + struct dt_fun *function; + } value; +}; + +struct dt_state { + int pos; + int depth; + struct uci_context *ctx; + const char *value; + enum dt_type valtype; + struct dt_op stack[32]; +}; + +struct dt_fun { + const char *name; + enum dt_type valtype; + bool (*call)(struct dt_state *s, int nargs); +}; + +static bool +dt_test_number(double number, const char *value) +{ + char *e; + double n; + + n = strtod(value, &e); + + return (e > value && *e == 0 && n == number); +} + +static bool +dt_test_string(const char *s, const char *end, const char *value) +{ + bool esc = false; + + while (*value) + { + if (s > end) + return false; + + if (!esc && *s == '\\') + { + s++; + + if (s >= end) + break; + + esc = true; + continue; + } + + if (*s != *value) + return false; + + esc = false; + value++; + s++; + } + + return (*s == *value || (s >= end && *value == 0)); +} + +static bool +dt_step(struct dt_state *s); + +static bool +dt_call(struct dt_state *s); + +#define dt_getint(n, v) \ + ((n < nargs && s->stack[s->pos + n].type == OP_NUMBER) \ + ? (v = s->stack[s->pos + n].value.number, 1) : 0) + +static bool +dt_type_or(struct dt_state *s, int nargs) +{ + while (nargs--) + if (dt_step(s)) + return true; + + return false; +} + +static bool +dt_type_and(struct dt_state *s, int nargs) +{ + while (nargs--) + if (!dt_step(s)) + return false; + + return true; +} + +static bool +dt_type_not(struct dt_state *s, int nargs) +{ + if (!nargs) + return false; + + return !dt_step(s); +} + +static bool +dt_type_neg(struct dt_state *s, int nargs) +{ + bool rv; + const char *value = s->value; + + if (!nargs) + return false; + + if (*s->value == '!') + while (isspace(*++s->value)); + + rv = dt_step(s); + s->value = value; + + return rv; +} + +static bool +dt_type_list(struct dt_state *s, int nargs) +{ + bool rv = true; + int pos = s->pos; + char *p, *str = strdup(s->value); + const char *value = s->value; + + if (!str || !nargs) + return false; + + for (p = strtok(str, " \t"); p; p = strtok(NULL, " \t")) + { + s->value = p; + + if (!dt_step(s)) + { + rv = false; + break; + } + + s->pos = pos; + } + + s->value = value; + free(str); + + return rv; +} + +static bool +dt_type_min(struct dt_state *s, int nargs) +{ + int n, min; + char *e; + + if (dt_getint(0, min)) + { + n = strtol(s->value, &e, 0); + return (e > s->value && *e == 0 && n >= min); + } + + return false; +} + +static bool +dt_type_max(struct dt_state *s, int nargs) +{ + int n, max; + char *e; + + if (dt_getint(0, max)) + { + n = strtol(s->value, &e, 0); + return (e > s->value && *e == 0 && n <= max); + } + + return false; +} + +static bool +dt_type_range(struct dt_state *s, int nargs) +{ + int n, min, max; + char *e; + + if (dt_getint(0, min) && dt_getint(1, max)) + { + n = strtol(s->value, &e, 0); + return (e > s->value && *e == 0 && n >= min && n <= max); + } + + return false; +} + +static bool +dt_type_minlen(struct dt_state *s, int nargs) +{ + int min; + + if (dt_getint(0, min)) + return (strlen(s->value) >= min); + + return false; +} + +static bool +dt_type_maxlen(struct dt_state *s, int nargs) +{ + int max; + + if (dt_getint(0, max)) + return (strlen(s->value) <= max); + + return false; +} + +static bool +dt_type_rangelen(struct dt_state *s, int nargs) +{ + int min, max; + int len = strlen(s->value); + + if (dt_getint(0, min) && dt_getint(1, max)) + return (len >= min && len <= max); + + return false; +} + +static bool +dt_type_int(struct dt_state *s, int nargs) +{ + char *e; + int base = 0; + + if (!isxdigit(*s->value) && *s->value != '-') + return false; + + dt_getint(0, base); + strtol(s->value, &e, base); + + return (e > s->value && *e == 0); +} + +static bool +dt_type_uint(struct dt_state *s, int nargs) +{ + char *e; + int base = 0; + + if (!isxdigit(*s->value)) + return false; + + dt_getint(0, base); + strtoul(s->value, &e, base); + + return (e > s->value && *e == 0); +} + +static bool +dt_type_float(struct dt_state *s, int nargs) +{ + char *e; + + strtod(s->value, &e); + + return (e > s->value && *e == 0); +} + +static bool +dt_type_ufloat(struct dt_state *s, int nargs) +{ + int n; + char *e; + + n = strtod(s->value, &e); + + return (e > s->value && *e == 0 && n >= 0.0); +} + +static bool +dt_type_bool(struct dt_state *s, int nargs) +{ + int i; + const char *values[] = { + "0", "off", "false", "no", "disabled", + "1", "on", "true", "yes", "enabled" + }; + + for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) + if (!strcasecmp(values[i], s->value)) + return true; + + return false; +} + +static bool +dt_type_string(struct dt_state *s, int nargs) +{ + int min, max; + int len = strlen(s->value); + + if (dt_getint(0, min) && (len < min)) + return false; + + if (dt_getint(1, max) && (len > max)) + return false; + + return true; +} + +static bool +dt_type_hexstring(struct dt_state *s, int nargs) +{ + int min, max; + int len = strlen(s->value); + const char *p; + + if (len % 2) + return false; + + if (dt_getint(0, min) && (len < min)) + return false; + + if (dt_getint(1, max) && (len > max)) + return false; + + for (p = s->value; *p; p++) + if (!isxdigit(*p)) + return false; + + return true; +} + +static bool +dt_type_ip4addr(struct dt_state *s, int nargs) +{ + struct in6_addr a; + return inet_pton(AF_INET, s->value, &a); +} + +static bool +dt_type_ip6addr(struct dt_state *s, int nargs) +{ + struct in6_addr a; + return inet_pton(AF_INET6, s->value, &a); +} + +static bool +dt_type_ipaddr(struct dt_state *s, int nargs) +{ + return (dt_type_ip4addr(s, 0) || dt_type_ip6addr(s, 0)); +} + +static bool +dt_type_netmask4(struct dt_state *s, int nargs) +{ + int i; + struct in_addr a; + + if (!inet_pton(AF_INET, s->value, &a)) + return false; + + if (a.s_addr == 0) + return true; + + a.s_addr = ntohl(a.s_addr); + + for (i = 0; (i < 32) && !(a.s_addr & (1 << i)); i++); + + return ((uint32_t)(~((1 << i) - 1)) == a.s_addr); +} + +static bool +dt_type_netmask6(struct dt_state *s, int nargs) +{ + int i; + struct in6_addr a; + + if (!inet_pton(AF_INET6, s->value, &a)) + return false; + + for (i = 0; (i < 16) && (a.s6_addr[i] == 0xFF); i++); + + if (i == 16) + return true; + + if ((a.s6_addr[i] != 255) && (a.s6_addr[i] != 254) && + (a.s6_addr[i] != 252) && (a.s6_addr[i] != 248) && + (a.s6_addr[i] != 240) && (a.s6_addr[i] != 224) && + (a.s6_addr[i] != 192) && (a.s6_addr[i] != 128) && + (a.s6_addr[i] != 0)) + return false; + + for (; (i < 16) && (a.s6_addr[i] == 0); i++); + + return (i == 16); +} + +static bool +dt_type_cidr4(struct dt_state *s, int nargs) +{ + int n; + struct in_addr a; + char *p, buf[sizeof("255.255.255.255/32\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + n = strtoul(p, &p, 10); + + if ((*p != 0) || (n > 32)) + return false; + } + + return inet_pton(AF_INET, buf, &a); +} + +static bool +dt_type_cidr6(struct dt_state *s, int nargs) +{ + int n; + struct in6_addr a; + char *p, buf[sizeof("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255/128\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + n = strtoul(p, &p, 10); + + if ((*p != 0) || (n > 128)) + return false; + } + + return inet_pton(AF_INET6, buf, &a); +} + +static bool +dt_type_cidr(struct dt_state *s, int nargs) +{ + return (dt_type_cidr4(s, 0) || dt_type_cidr6(s, 0)); +} + +static bool +dt_type_ipmask4(struct dt_state *s, int nargs) +{ + bool rv; + struct in_addr a; + const char *value; + char *p, buf[sizeof("255.255.255.255/255.255.255.255\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + value = s->value; + s->value = p; + rv = dt_type_netmask4(s, 0); + s->value = value; + + if (!rv) + return false; + } + + return inet_pton(AF_INET, buf, &a); +} + +static bool +dt_type_ipmask6(struct dt_state *s, int nargs) +{ + bool rv; + struct in6_addr a; + const char *value; + char *p, buf[sizeof("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255/" + "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + value = s->value; + s->value = p; + rv = dt_type_netmask6(s, 0); + s->value = value; + + if (!rv) + return false; + } + + return inet_pton(AF_INET6, buf, &a); +} + +static bool +dt_type_ipmask(struct dt_state *s, int nargs) +{ + return (dt_type_ipmask4(s, 0) || dt_type_ipmask6(s, 0)); +} + +static bool +dt_type_port(struct dt_state *s, int nargs) +{ + int n; + char *e; + + n = strtoul(s->value, &e, 10); + + return (e > s->value && *e == 0 && n <= 65535); +} + +static bool +dt_type_portrange(struct dt_state *s, int nargs) +{ + int n, m; + char *e; + + n = strtoul(s->value, &e, 10); + + if (e == s->value || *e != '-') + return false; + + m = strtoul(e + 1, &e, 10); + + return (*e == 0 && n <= 65535 && m <= 65535 && n <= m); +} + +static bool +dt_type_macaddr(struct dt_state *s, int nargs) +{ + return !!ether_aton(s->value); +} + +static bool +dt_type_uciname(struct dt_state *s, int nargs) +{ + const char *p; + + for (p = s->value; + *p && ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || (*p == '_')); + p++); + + return (*p == 0); +} + +static bool +dt_type_wpakey(struct dt_state *s, int nargs) +{ + int len = strlen(s->value); + const char *p = s->value; + + if (len == 64) + { + while (isxdigit(*p)) + p++; + + return (*p == 0); + } + + return (len >= 8 && len <= 63); +} + +static bool +dt_type_wepkey(struct dt_state *s, int nargs) +{ + int len = strlen(s->value); + const char *p = s->value; + + if (!strncmp(p, "s:", 2)) + { + len -= 2; + p += 2; + } + + if (len == 10 || len == 26) + { + while (isxdigit(*p)) + p++; + + return (*p == 0); + } + + return (len == 5 || len == 13); +} + +static bool +dt_type_hostname(struct dt_state *s, int nargs) +{ + const char *p, *last; + + for (p = last = s->value; *p; p++) + { + if (*p == '.') + { + if ((p - last) == 0 || (p - last) > 63) + return false; + + last = p + 1; + continue; + } + else if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || (*p == '_') || (*p == '-')) + { + continue; + } + + return false; + } + + return ((p - last) > 0 && (p - last) <= 255); +} + +static bool +dt_type_host(struct dt_state *s, int nargs) +{ + return (dt_type_hostname(s, 0) || dt_type_ipaddr(s, 0)); +} + +static bool +dt_type_network(struct dt_state *s, int nargs) +{ + return (dt_type_uciname(s, 0) || dt_type_host(s, 0)); +} + +static bool +dt_type_phonedigit(struct dt_state *s, int nargs) +{ + const char *p; + + for (p = s->value; + *p && ((*p >= '0' && *p <= '9') || (*p == '*') || (*p == '#') || + (*p == '!') || (*p == '.')); + p++); + + return (*p == 0); +} + +static bool +dt_type_directory(struct dt_state *s, int nargs) +{ + struct stat st; + return (!stat(s->value, &st) && S_ISDIR(st.st_mode)); +} + + +static bool +dt_type_device(struct dt_state *s, int nargs) +{ + struct stat st; + return (!stat(s->value, &st) && + (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))); +} + +static bool +dt_type_file(struct dt_state *s, int nargs) +{ + struct stat st; + return (!stat(s->value, &st) && S_ISREG(st.st_mode)); +} + +static bool +dt_type_regex(struct dt_state *s, int nargs) +{ + bool rv; + int relen; + regex_t pattern; + char *re = NULL; + + if (nargs < 1 || s->stack[s->pos].type != OP_STRING) + return false; + + relen = s->stack[s->pos].length; + re = alloca(relen + 3); + + if (!re) + return false; + + memset(re, 0, relen + 3); + memcpy(re + 1, s->stack[s->pos].value.string, relen); + + re[0] = '^'; + re[relen + 1] = '$'; + + if (regcomp(&pattern, re, REG_EXTENDED | REG_NOSUB)) + return false; + + rv = !regexec(&pattern, s->value, 0, NULL, 0); + + regfree(&pattern); + + return rv; +} + +static void * +dt_uci_lookup(struct dt_state *s, const char *pkg, + const char *sct, const char *opt, enum uci_type type) +{ + struct uci_ptr ptr = { + .package = pkg, + .section = sct, + .option = opt + }; + + if (!s->ctx || uci_lookup_ptr(s->ctx, &ptr, NULL, false) || + !(ptr.flags & UCI_LOOKUP_COMPLETE)) + return NULL; + + if (ptr.last->type != type) + return NULL; + + switch (type) + { + case UCI_TYPE_PACKAGE: + return uci_to_package(ptr.last); + + case UCI_TYPE_SECTION: + return uci_to_section(ptr.last); + + case UCI_TYPE_OPTION: + return uci_to_option(ptr.last); + + default: + return NULL; + } +} + +static bool +dt_uci_cmp(struct dt_state *s, + const char *pkg, const char *sct, const char *opt) +{ + struct uci_element *e; + struct uci_option *o = dt_uci_lookup(s, pkg, sct, opt, UCI_TYPE_OPTION); + + if (!o) + return false; + + switch (o->type) + { + case UCI_TYPE_STRING: + if (!strcmp(s->value, o->v.string)) + return true; + break; + + case UCI_TYPE_LIST: + uci_foreach_element(&o->v.list, e) + if (!strcmp(s->value, e->name)) + return true; + break; + } + + return false; +} + +static bool +dt_type_uci(struct dt_state *s, int nargs) +{ + int i, len; + struct uci_element *e; + struct uci_package *p; + char *cso[3] = { }; + + if (!s->ctx) + return false; + + for (i = 0; i < nargs && i < 3; i++) + { + if (s->stack[s->pos + i].type != OP_STRING) + continue; + + len = s->stack[s->pos + i].length; + cso[i] = alloca(len + 1); + + if (!cso[i]) + continue; + + memset(cso[i], 0, len + 1); + memcpy(cso[i], s->stack[s->pos + i].value.string, len); + } + + if (!cso[0] || !cso[1] || (*cso[1] != '@' && !cso[2])) + return false; + + if (*cso[1] != '@') + return dt_uci_cmp(s, cso[0], cso[1], cso[2]); + + p = dt_uci_lookup(s, cso[0], NULL, NULL, UCI_TYPE_PACKAGE); + + if (!p) + return false; + + uci_foreach_element(&p->sections, e) + { + if (strcmp(uci_to_section(e)->type, cso[1] + 1)) + continue; + + if (!cso[2]) + { + if (!strcmp(s->value, e->name)) + return true; + } + else + { + if (dt_uci_cmp(s, cso[0], e->name, cso[2])) + return true; + } + } + + return false; +} + + +static struct dt_fun dt_types[] = { + { "or", DT_INVALID, dt_type_or }, + { "and", DT_INVALID, dt_type_and }, + { "not", DT_INVALID, dt_type_not }, + { "neg", DT_INVALID, dt_type_neg }, + { "list", DT_INVALID, dt_type_list }, + { "min", DT_NUMBER, dt_type_min }, + { "max", DT_NUMBER, dt_type_max }, + { "range", DT_NUMBER, dt_type_range }, + { "minlength", DT_STRING, dt_type_minlen }, + { "maxlength", DT_STRING, dt_type_maxlen }, + { "rangelength", DT_STRING, dt_type_rangelen }, + { "integer", DT_NUMBER, dt_type_int }, + { "uinteger", DT_NUMBER, dt_type_uint }, + { "float", DT_NUMBER, dt_type_float }, + { "ufloat", DT_NUMBER, dt_type_ufloat }, + { "bool", DT_BOOL, dt_type_bool }, + { "string", DT_STRING, dt_type_string }, + { "hexstring", DT_STRING, dt_type_hexstring }, + { "ip4addr", DT_STRING, dt_type_ip4addr }, + { "ip6addr", DT_STRING, dt_type_ip6addr }, + { "ipaddr", DT_STRING, dt_type_ipaddr }, + { "cidr4", DT_STRING, dt_type_cidr4 }, + { "cidr6", DT_STRING, dt_type_cidr6 }, + { "cidr", DT_STRING, dt_type_cidr }, + { "netmask4", DT_STRING, dt_type_netmask4 }, + { "netmask6", DT_STRING, dt_type_netmask6 }, + { "ipmask4", DT_STRING, dt_type_ipmask4 }, + { "ipmask6", DT_STRING, dt_type_ipmask6 }, + { "ipmask", DT_STRING, dt_type_ipmask }, + { "port", DT_NUMBER, dt_type_port }, + { "portrange", DT_STRING, dt_type_portrange }, + { "macaddr", DT_STRING, dt_type_macaddr }, + { "uciname", DT_STRING, dt_type_uciname }, + { "wpakey", DT_STRING, dt_type_wpakey }, + { "wepkey", DT_STRING, dt_type_wepkey }, + { "hostname", DT_STRING, dt_type_hostname }, + { "host", DT_STRING, dt_type_host }, + { "network", DT_STRING, dt_type_network }, + { "phonedigit", DT_STRING, dt_type_phonedigit }, + { "directory", DT_STRING, dt_type_directory }, + { "device", DT_STRING, dt_type_device }, + { "file", DT_STRING, dt_type_file }, + { "regex", DT_STRING, dt_type_regex }, + { "uci", DT_STRING, dt_type_uci }, + + { } +}; + +static struct dt_fun * +dt_lookup_function(const char *s, const char *e) +{ + struct dt_fun *fun = dt_types; + + while (fun->name) + { + if (!strncmp(fun->name, s, e - s) && *(fun->name + (e - s)) == '\0') + return fun; + + fun++; + } + + return NULL; +} + +static bool +dt_parse_atom(struct dt_state *s, const char *label, const char *end) +{ + char q, *e; + const char *p; + bool esc; + double dval; + struct dt_fun *func; + struct dt_op *op = &s->stack[s->depth]; + + if ((s->depth + 1) >= (sizeof(s->stack) / sizeof(s->stack[0]))) + { + printf("Syntax error, expression too long\n"); + return false; + } + + while (isspace(*label)) + label++; + + /* test whether label is a float */ + dval = strtod(label, &e); + + if (e > label) + { + op->next = e; + op->type = OP_NUMBER; + op->value.number = dval; + op->nextop = ++s->depth; + + return true; + } + else if ((*label == '"') || (*label == '\'')) + { + for (p = label + 1, q = *label, esc = false; p <= end; p++) + { + if (esc) + { + esc = false; + continue; + } + else if (*p == '\\') + { + esc = true; + continue; + } + else if (*p == q) + { + op->next = p + 1; + op->type = OP_STRING; + op->length = (p - label) - 1; + op->value.string = label + 1; + op->nextop = ++s->depth; + + return true; + } + } + + printf("Syntax error, unterminated string\n"); + return false; + } + else if (*label) + { + for (p = label; + p <= end && ((*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || + (*p == '_')); + p++); + + func = dt_lookup_function(label, p); + + if (!func) + { + printf("Syntax error, unrecognized function\n"); + return false; + } + + op->next = p; + op->type = OP_FUNCTION; + op->value.function = func; + op->nextop = ++s->depth; + + return true; + } + + printf("Syntax error, unexpected EOF\n"); + return false; +} + +static bool +dt_parse_list(struct dt_state *s, const char *code, const char *end); + +static bool +dt_parse_expr(const char *code, const char *end, struct dt_state *s) +{ + struct dt_op *tok; + + if (!dt_parse_atom(s, code, end)) + return false; + + tok = &s->stack[s->depth - 1]; + + while (isspace(*tok->next)) + tok->next++; + + if (tok->type == OP_FUNCTION) + { + if (*tok->next == '(') + { + end--; + + while (isspace(*end) && end > tok->next + 1) + end--; + + return dt_parse_list(s, tok->next + 1, end); + } + else if (tok->next == end) + { + return dt_parse_list(s, tok->next, tok->next); + } + + printf("Syntax error, expected '(' or EOF after function label\n"); + return false; + } + else if (tok->next == end) + { + return true; + } + + printf("Syntax error, expected ',' after literal\n"); + return false; +} + +static bool +dt_parse_list(struct dt_state *s, const char *code, const char *end) +{ + char c; + bool esc; + int nest; + const char *p, *last; + struct dt_op *fptr; + + if (!code) + return false; + + fptr = &s->stack[s->depth - 1]; + + for (nest = 0, p = last = code, esc = false, c = *p; + p <= end; + p++, c = (p < end) ? *p : '\0') + { + if (esc) + { + esc = false; + continue; + } + + switch (c) + { + case '\\': + esc = true; + break; + + case '(': + nest++; + break; + + case ')': + nest--; + break; + + case ',': + case '\0': + if (nest <= 0) + { + if (p > last) + { + if (!dt_parse_expr(last, p, s)) + return false; + + fptr->length++; + } + + last = p + 1; + } + + break; + } + } + + fptr->nextop = s->depth; + return true; +} + +static bool +dt_step(struct dt_state *s) +{ + bool rv; + struct dt_op *op = &s->stack[s->pos]; + + switch (op->type) + { + case OP_NUMBER: + rv = dt_test_number(op->value.number, s->value); + if (rv) + s->valtype = DT_NUMBER; + break; + + case OP_STRING: + rv = dt_test_string(op->value.string, op->value.string + op->length, s->value); + if (rv) + s->valtype = DT_STRING; + break; + + case OP_FUNCTION: + rv = dt_call(s); + break; + + default: + rv = false; + break; + } + + s->pos = op->nextop; + return rv; +} + +static bool +dt_call(struct dt_state *s) +{ + bool rv; + struct dt_op *fptr = &s->stack[s->pos]; + struct dt_fun *func = fptr->value.function; + + s->pos++; + + rv = func->call(s, fptr->length); + + if (rv && func->valtype) + s->valtype = func->valtype; + + s->pos = fptr->nextop; + + return rv; +} + +enum dt_type +dt_parse(const char *code, const char *value) +{ + enum dt_type rv = DT_INVALID; + + struct dt_state s = { + .depth = 1, + .stack = { + { + .type = OP_FUNCTION, + .value.function = &dt_types[0], + .next = code + } + } + }; + + if (!value || !*value) + return false; + + if (!dt_parse_list(&s, code, code + strlen(code))) + return false; + + s.ctx = uci_alloc_context(); + s.value = value; + + if (dt_call(&s)) + rv = s.valtype; + + if (s.ctx) + uci_free_context(s.ctx); + + return rv; +}