From 916f95cb58604038695347ee41a430d8ca1f0556 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 14 Nov 2013 13:41:13 +0100 Subject: debloat and reorganize code split app into procd and init binaries remove log support, this is an external service now Signed-off-by: John Crispin --- .gitignore | 5 +- CMakeLists.txt | 32 +- askfirst.c | 62 ---- coldplug.c | 65 ---- debug.c | 45 --- early.c | 88 ----- hotplug.c | 468 ------------------------ hotplug.h | 24 -- initd/early.c | 95 +++++ initd/init.c | 115 ++++++ initd/init.h | 23 ++ initd/mkdev.c | 129 +++++++ initd/preinit.c | 98 +++++ inittab.c | 10 +- instance.c | 491 ------------------------- instance.h | 61 ---- libvalidate.h | 6 - log.c | 135 ------- log.h | 38 ++ logread.c | 369 ------------------- main.c | 81 ----- md5.c | 242 ------------- md5.h | 35 -- measure.c | 99 ------ mkdev.c | 127 ------- plug/coldplug.c | 66 ++++ plug/hotplug.c | 478 +++++++++++++++++++++++++ plug/hotplug.h | 25 ++ plug/udevtrigger.c | 264 ++++++++++++++ preinit.c | 68 ---- procd.c | 68 ++++ procd.h | 33 +- rcS.c | 10 +- service.c | 439 ----------------------- service.h | 56 --- service/instance.c | 480 +++++++++++++++++++++++++ service/instance.h | 61 ++++ service/service.c | 470 ++++++++++++++++++++++++ service/service.h | 57 +++ service/trigger.c | 334 ++++++++++++++++++ service/validate.c | 162 +++++++++ service_validate.c | 161 --------- signal.c | 13 - state.c | 15 +- syslog.c | 320 ----------------- syslog.h | 43 --- system.c | 6 - trigger.c | 334 ------------------ ubus.c | 97 ++--- udevtrigger.c | 264 -------------- utils.c | 122 ------- utils.h | 53 --- utils/askfirst.c | 62 ++++ utils/md5.c | 242 +++++++++++++ utils/md5.h | 35 ++ utils/utils.c | 122 +++++++ utils/utils.h | 53 +++ validate.c | 1002 ---------------------------------------------------- validate_data.c | 28 -- watchdog.c | 10 +- 60 files changed, 3536 insertions(+), 5460 deletions(-) delete mode 100644 askfirst.c delete mode 100644 coldplug.c delete mode 100644 debug.c delete mode 100644 early.c delete mode 100644 hotplug.c delete mode 100644 hotplug.h create mode 100644 initd/early.c create mode 100644 initd/init.c create mode 100644 initd/init.h create mode 100644 initd/mkdev.c create mode 100644 initd/preinit.c delete mode 100644 instance.c delete mode 100644 instance.h delete mode 100644 libvalidate.h delete mode 100644 log.c create mode 100644 log.h delete mode 100644 logread.c delete mode 100644 main.c delete mode 100644 md5.c delete mode 100644 md5.h delete mode 100644 measure.c delete mode 100644 mkdev.c create mode 100644 plug/coldplug.c create mode 100644 plug/hotplug.c create mode 100644 plug/hotplug.h create mode 100644 plug/udevtrigger.c delete mode 100644 preinit.c create mode 100644 procd.c delete mode 100644 service.c delete mode 100644 service.h create mode 100644 service/instance.c create mode 100644 service/instance.h create mode 100644 service/service.c create mode 100644 service/service.h create mode 100644 service/trigger.c create mode 100644 service/validate.c delete mode 100644 service_validate.c delete mode 100644 syslog.c delete mode 100644 syslog.h delete mode 100644 trigger.c delete mode 100644 udevtrigger.c delete mode 100644 utils.c delete mode 100644 utils.h create mode 100644 utils/askfirst.c create mode 100644 utils/md5.c create mode 100644 utils/md5.h create mode 100644 utils/utils.c create mode 100644 utils/utils.h delete mode 100644 validate.c delete mode 100644 validate_data.c diff --git a/.gitignore b/.gitignore index 7d82eca..4bd7b28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,10 @@ procd -validate_data -logread askfirst udevtrigger +init .* -*.so Makefile CMakeCache.txt CMakeFiles *.cmake install_manifest.txt - diff --git a/CMakeLists.txt b/CMakeLists.txt index a521ea7..d353801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,9 @@ IF(APPLE) LINK_DIRECTORIES(/opt/local/lib) ENDIF() -SET(SOURCES main.c ubus.c service.c service_validate.c instance.c utils.c md5.c hotplug.c state.c mkdev.c early.c inittab.c preinit.c coldplug.c syslog.c log.c watchdog.c signal.c system.c debug.c rcS.c trigger.c measure.c) +SET(SOURCES procd.c signal.c watchdog.c state.c inittab.c rcS.c ubus.c system.c + service/service.c service/instance.c service/validate.c service/trigger.c + plug/coldplug.c plug/hotplug.c utils/utils.c utils/md5.c) find_library(json NAMES json-c json) SET(LIBS ubox ubus ${json} blobmsg_json json_script) @@ -19,42 +21,28 @@ IF(DEBUG) ADD_DEFINITIONS(-DDEBUG -g3) ENDIF() -ADD_LIBRARY(validate SHARED validate.c) - -INSTALL(TARGETS validate - LIBRARY DESTINATION lib -) ADD_EXECUTABLE(procd ${SOURCES}) - -TARGET_LINK_LIBRARIES(procd ${LIBS} validate) - +TARGET_LINK_LIBRARIES(procd ${LIBS}) INSTALL(TARGETS procd RUNTIME DESTINATION sbin ) -ADD_EXECUTABLE(askfirst askfirst.c) -INSTALL(TARGETS askfirst +ADD_EXECUTABLE(init initd/init.c initd/early.c initd/preinit.c initd/mkdev.c watchdog.c) +TARGET_LINK_LIBRARIES(init ${LIBS}) +INSTALL(TARGETS init RUNTIME DESTINATION sbin ) -ADD_EXECUTABLE(udevtrigger udevtrigger.c) +ADD_EXECUTABLE(udevtrigger plug/udevtrigger.c) INSTALL(TARGETS udevtrigger RUNTIME DESTINATION sbin ) -ADD_EXECUTABLE(logread logread.c) -TARGET_LINK_LIBRARIES(logread ${LIBS}) -INSTALL(TARGETS logread - RUNTIME DESTINATION sbin -) - -ADD_EXECUTABLE(validate_data validate_data.c) -TARGET_LINK_LIBRARIES(validate_data ${LIBS} validate) - -INSTALL(TARGETS validate_data +ADD_EXECUTABLE(askfirst utils/askfirst.c) +INSTALL(TARGETS askfirst RUNTIME DESTINATION sbin ) diff --git a/askfirst.c b/askfirst.c deleted file mode 100644 index 6ad77aa..0000000 --- a/askfirst.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 - -static int redirect_output(const char *dev) -{ - pid_t p = setsid(); - int fd; - - chdir("/dev"); - fd = open(dev, O_RDWR); - chdir("/"); - - if (fd < 0) - return -1; - - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - tcsetpgrp(fd, p); - close(fd); - - return 0; -} - -int main(int argc, char **argv) -{ - int c; - - if (redirect_output(argv[1])) - fprintf(stderr, "%s: Failed to open %s\n", argv[0], argv[1]); - - printf("Please press Enter to activate this console.\n"); - do { - c = getchar(); - if (c == EOF) - return -1; - } - while (c != 0xA); - - execvp(argv[2], &argv[2]); - printf("%s: Failed to execute %s\n", argv[0], argv[2]); - - return -1; -} diff --git a/coldplug.c b/coldplug.c deleted file mode 100644 index 71b09f0..0000000 --- a/coldplug.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 "procd.h" -#include "hotplug.h" - -static struct uloop_process udevtrigger; - -static void coldplug_complete(struct uloop_timeout *t) -{ - DEBUG(2, "Coldplug complete\n"); - hotplug_last_event(NULL); - procd_state_next(); -} - -static void udevtrigger_complete(struct uloop_process *proc, int ret) -{ - DEBUG(2, "Finished udevtrigger\n"); - hotplug_last_event(coldplug_complete); -} - -void procd_coldplug(void) -{ - char *argv[] = { "udevtrigger", NULL }; - - umount2("/dev/pts", MNT_DETACH); - umount2("/dev/", MNT_DETACH); - mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755,size=512K"); - mkdir("/dev/shm", 0755); - mkdir("/dev/pts", 0755); - mount("devpts", "/dev/pts", "devpts", 0, 0); - udevtrigger.cb = udevtrigger_complete; - udevtrigger.pid = fork(); - if (!udevtrigger.pid) { - execvp(argv[0], argv); - ERROR("Failed to start coldplug\n"); - exit(-1); - } - - if (udevtrigger.pid <= 0) { - ERROR("Failed to start new coldplug instance\n"); - return; - } - - uloop_process_add(&udevtrigger); - - DEBUG(2, "Launched coldplug instance, pid=%d\n", (int) udevtrigger.pid); -} diff --git a/debug.c b/debug.c deleted file mode 100644 index fbf1e4f..0000000 --- a/debug.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 "procd.h" - -unsigned int debug = 0; - -void debug_init(void) -{ - char line[256]; - int r, fd = open("/proc/cmdline", O_RDONLY); - regex_t pat_cmdline; - regmatch_t matches[2]; - - if (fd < 0) - return; - - r = read(fd, line, sizeof(line) - 1); - line[r] = '\0'; - close(fd); - - regcomp(&pat_cmdline, "init_debug=([0-9]+)", REG_EXTENDED); - if (!regexec(&pat_cmdline, line, 2, matches, 0)) { - line[matches[1].rm_eo] = '\0'; - debug = atoi(&line[matches[1].rm_so]); - } - regfree(&pat_cmdline); -} diff --git a/early.c b/early.c deleted file mode 100644 index 7da1e4b..0000000 --- a/early.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 "procd.h" - -static void early_mounts(void) -{ - mount("proc", "/proc", "proc", MS_NOATIME, 0); - mount("sysfs", "/sys", "sysfs", MS_NOATIME, 0); - - mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOATIME, NULL); - mkdir("/tmp/run", 0777); - mkdir("/tmp/lock", 0777); - mkdir("/tmp/state", 0777); - symlink("/tmp", "/var"); - - mount("tmpfs", "/dev", "tmpfs", MS_NOATIME, "mode=0755,size=512K"); - mkdir("/dev/shm", 0755); - mkdir("/dev/pts", 0755); - mount("devpts", "/dev/pts", "devpts", MS_NOATIME, "mode=600"); -} - -static void early_dev(void) -{ - mkdev("*", 0600); - mknod("/dev/null", 0666, makedev(1, 3)); -} - -static void early_console(const char *dev) -{ - struct stat s; - int dd; - - if (stat(dev, &s)) { - ERROR("Failed to stat %s\n", dev); - return; - } - - dd = open(dev, O_RDWR); - if (dd < 0) - dd = open("/dev/null", O_RDWR); - - dup2(dd, STDIN_FILENO); - dup2(dd, STDOUT_FILENO); - dup2(dd, STDERR_FILENO); - - if (dd != STDIN_FILENO && - dd != STDOUT_FILENO && - dd != STDERR_FILENO) - close(dd); -} - -static void early_env(void) -{ - setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 1); -} - -void procd_early(void) -{ - if (getpid() != 1) - return; - - early_mounts(); - early_dev(); - early_env(); - early_console("/dev/console"); - - LOG("Console is alive\n"); -} diff --git a/hotplug.c b/hotplug.c deleted file mode 100644 index 422e849..0000000 --- a/hotplug.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include "procd.h" -#include "hotplug.h" - -#define HOTPLUG_WAIT 500 - -struct cmd_queue { - struct list_head list; - - struct blob_attr *msg; - struct blob_attr *data; - void (*handler)(struct blob_attr *msg, struct blob_attr *data); -}; - -static LIST_HEAD(cmd_queue); -static struct uloop_process queue_proc; -static struct uloop_timeout last_event; -static struct blob_buf b; -static char *rule_file; -static struct blob_buf script; - -static char *hotplug_msg_find_var(struct blob_attr *msg, const char *name) -{ - struct blob_attr *cur; - int rem; - - blobmsg_for_each_attr(cur, msg, rem) { - if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) - continue; - - if (strcmp(blobmsg_name(cur), name) != 0) - continue; - - return blobmsg_data(cur); - } - - return NULL; -} - -static void mkdir_p(char *dir) -{ - char *l = strrchr(dir, '/'); - - if (l) { - *l = '\0'; - mkdir_p(dir); - *l = '/'; - mkdir(dir, 0755); - } -} - -static void handle_makedev(struct blob_attr *msg, struct blob_attr *data) -{ - unsigned int oldumask = umask(0); - static struct blobmsg_policy mkdev_policy[2] = { - { .type = BLOBMSG_TYPE_STRING }, - { .type = BLOBMSG_TYPE_STRING }, - }; - struct blob_attr *tb[2]; - char *minor = hotplug_msg_find_var(msg, "MINOR"); - char *major = hotplug_msg_find_var(msg, "MAJOR"); - char *subsystem = hotplug_msg_find_var(msg, "SUBSYSTEM"); - - blobmsg_parse_array(mkdev_policy, 2, tb, blobmsg_data(data), blobmsg_data_len(data)); - if (tb[0] && tb[1] && minor && major && subsystem) { - mode_t m = S_IFCHR; - char *d = strdup(blobmsg_get_string(tb[0])); - - d = dirname(d); - mkdir_p(d); - free(d); - - if (!strcmp(subsystem, "block")) - m = S_IFBLK; - mknod(blobmsg_get_string(tb[0]), - m | strtoul(blobmsg_data(tb[1]), NULL, 8), - makedev(atoi(major), atoi(minor))); - } - umask(oldumask); -} - -static void handle_rm(struct blob_attr *msg, struct blob_attr *data) -{ - static struct blobmsg_policy rm_policy = { - .type = BLOBMSG_TYPE_STRING, - }; - struct blob_attr *tb; - - blobmsg_parse_array(&rm_policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data)); - if (tb) - unlink(blobmsg_data(tb)); -} - -static void handle_exec(struct blob_attr *msg, struct blob_attr *data) -{ - char *argv[8]; - struct blob_attr *cur; - int rem, fd; - int i = 0; - - blobmsg_for_each_attr(cur, msg, rem) - setenv(blobmsg_name(cur), blobmsg_data(cur), 1); - - blobmsg_for_each_attr(cur, data, rem) { - argv[i] = blobmsg_data(cur); - i++; - if (i == 7) - break; - } - - if (debug < 2) { - fd = open("/dev/null", O_RDWR); - if (fd > -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); - } - } - - if (i > 0) { - argv[i] = NULL; - execvp(argv[0], &argv[0]); - } - exit(-1); -} - -static void handle_firmware(struct blob_attr *msg, struct blob_attr *data) -{ - char *dir = blobmsg_get_string(blobmsg_data(data)); - char *file = hotplug_msg_find_var(msg, "FIRMWARE"); - char *dev = hotplug_msg_find_var(msg, "DEVPATH"); - void *fw_data; - struct stat s; - char *path, loadpath[256], syspath[256]; - int fw, load, sys, len; - - DEBUG(1, "Firmware request for %s/%s\n", dir, file); - - if (!file || !dir || !dev) { - ERROR("Request for unknown firmware %s/%s\n", dir, file); - exit(-1); - } - - path = malloc(strlen(dir) + strlen(file) + 2); - if (!path) { - ERROR("Failed to allocate memory\n"); - exit(-1); - } - sprintf(path, "%s/%s", dir, file); - - if (stat(path, &s)) { - ERROR("Could not find firmware %s\n", path); - exit(-1); - } - - fw_data = malloc(s.st_size); - if (!fw_data) { - ERROR("Failed to allocate firmware data memory\n"); - exit(-1); - } - - fw = open(path, O_RDONLY); - if (!fw) { - ERROR("Failed to open %s\n", path); - exit(-1); - } - if (read(fw, fw_data, s.st_size) != s.st_size) { - ERROR("Failed to read firmware data\n"); - exit(-1); - } - close(fw); - - snprintf(loadpath, sizeof(loadpath), "/sys/%s/loading", dev); - load = open(loadpath, O_WRONLY); - if (!load) { - ERROR("Failed to open %s\n", loadpath); - exit(-1); - } - write(load, "1", 1); - close(load); - - snprintf(syspath, sizeof(syspath), "/sys/%s/data", dev); - sys = open(syspath, O_WRONLY); - if (!sys) { - ERROR("Failed to open %s\n", syspath); - exit(-1); - } - - len = s.st_size; - while (len > 4096) { - write(fw, fw_data, 4096); - len -= 4096; - } - if (len) - write(fw, fw_data, len); - close(fw); - - load = open(loadpath, O_WRONLY); - write(load, "0", 1); - close(load); - - DEBUG(1, "Done loading %s\n", path); - - exit(-1); -} - -static struct cmd_handler { - char *name; - int atomic; - void (*handler)(struct blob_attr *msg, struct blob_attr *data); -} handlers[] = { - { - .name = "makedev", - .atomic = 1, - .handler = handle_makedev, - }, { - .name = "rm", - .atomic = 1, - .handler = handle_rm, - }, { - .name = "exec", - .handler = handle_exec, - }, { - .name = "load-firmware", - .handler = handle_firmware, - }, -}; - -static void queue_next(void) -{ - struct cmd_queue *c; - - if (queue_proc.pending || list_empty(&cmd_queue)) - return; - - c = list_first_entry(&cmd_queue, struct cmd_queue, list); - - queue_proc.pid = fork(); - if (!queue_proc.pid) { - uloop_done(); - c->handler(c->msg, c->data); - exit(0); - } - - list_del(&c->list); - free(c); - - if (queue_proc.pid <= 0) { - queue_next(); - return; - } - - uloop_process_add(&queue_proc); - - DEBUG(2, "Launched hotplug exec instance, pid=%d\n", (int) queue_proc.pid); -} - -static void queue_proc_cb(struct uloop_process *c, int ret) -{ - DEBUG(2, "Finished hotplug exec instance, pid=%d\n", (int) c->pid); - - queue_next(); -} - -static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data) -{ - struct cmd_queue *c = NULL; - struct blob_attr *_msg, *_data; - - c = calloc_a(sizeof(struct cmd_queue), - &_msg, blob_pad_len(msg), - &_data, blob_pad_len(data), - NULL); - - c->msg = _msg; - c->data = _data; - - if (!c) - return; - - memcpy(c->msg, msg, blob_pad_len(msg)); - memcpy(c->data, data, blob_pad_len(data)); - c->handler = h->handler; - list_add_tail(&c->list, &cmd_queue); - queue_next(); -} - -static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) -{ - const char *str, *sep; - - if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) { - str = json_script_find_var(ctx, vars, "DEVPATH"); - if (!str) - return NULL; - - sep = strrchr(str, '/'); - if (sep) - return sep + 1; - - return str; - } - - return NULL; -} - -static struct json_script_file * -rule_handle_file(struct json_script_ctx *ctx, const char *name) -{ - json_object *obj; - - obj = json_object_from_file((char*)name); - if (is_error(obj)) - return NULL; - - blob_buf_init(&script, 0); - blobmsg_add_json_element(&script, "", obj); - - return json_script_file_from_blobmsg(name, blob_data(script.head), blob_len(script.head)); -} - -static void rule_handle_command(struct json_script_ctx *ctx, const char *name, - struct blob_attr *data, struct blob_attr *vars) -{ - struct blob_attr *cur; - int rem, i; - - if (debug > 1) { - DEBUG(2, "Command: %s", name); - blobmsg_for_each_attr(cur, data, rem) - DEBUG(2, " %s", (char *) blobmsg_data(cur)); - DEBUG(2, "\n"); - - DEBUG(2, "Message:"); - blobmsg_for_each_attr(cur, vars, rem) - DEBUG(2, " %s=%s", blobmsg_name(cur), (char *) blobmsg_data(cur)); - DEBUG(2, "\n"); - } - - for (i = 0; i < ARRAY_SIZE(handlers); i++) - if (!strcmp(handlers[i].name, name)) { - if (handlers[i].atomic) - handlers[i].handler(vars, data); - else - queue_add(&handlers[i], vars, data); - break; - } - - if (last_event.cb) - uloop_timeout_set(&last_event, HOTPLUG_WAIT); -} - -static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, - struct blob_attr *context) -{ - char *s; - - s = blobmsg_format_json(context, false); - ERROR("ERROR: %s in block: %s\n", msg, s); - free(s); -} - -static struct json_script_ctx jctx = { - .handle_var = rule_handle_var, - .handle_error = rule_handle_error, - .handle_command = rule_handle_command, - .handle_file = rule_handle_file, -}; - -static void hotplug_handler(struct uloop_fd *u, unsigned int ev) -{ - int i = 0; - static char buf[4096]; - int len = recv(u->fd, buf, sizeof(buf), MSG_DONTWAIT); - void *index; - if (len < 1) - return; - - blob_buf_init(&b, 0); - index = blobmsg_open_table(&b, NULL); - while (i < len) { - int l = strlen(buf + i) + 1; - char *e = strstr(&buf[i], "="); - - if (e) { - *e = '\0'; - blobmsg_add_string(&b, &buf[i], &e[1]); - } - i += l; - } - blobmsg_close_table(&b, index); - DEBUG(3, "%s\n", blobmsg_format_json(b.head, true)); - json_script_run(&jctx, rule_file, blob_data(b.head)); -} - -static struct uloop_fd hotplug_fd = { - .cb = hotplug_handler, -}; - -void hotplug_last_event(uloop_timeout_handler handler) -{ - last_event.cb = handler; - if (handler) - uloop_timeout_set(&last_event, HOTPLUG_WAIT); - else - uloop_timeout_cancel(&last_event); -} - -void hotplug(char *rules) -{ - struct sockaddr_nl nls; - - rule_file = strdup(rules); - memset(&nls,0,sizeof(struct sockaddr_nl)); - nls.nl_family = AF_NETLINK; - nls.nl_pid = getpid(); - nls.nl_groups = -1; - - if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) { - ERROR("Failed to open hotplug socket: %s\n", strerror(errno)); - exit(1); - } - if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) { - ERROR("Failed to bind hotplug socket: %s\n", strerror(errno)); - exit(1); - } - - json_script_init(&jctx); - queue_proc.cb = queue_proc_cb; - uloop_fd_add(&hotplug_fd, ULOOP_READ); -} - -void hotplug_shutdown(void) -{ - uloop_fd_delete(&hotplug_fd); - close(hotplug_fd.fd); -} diff --git a/hotplug.h b/hotplug.h deleted file mode 100644 index e33afcb..0000000 --- a/hotplug.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -#ifndef __PROCD_HOTPLUG_H -#define __PROCD_HOTPLUG_H - -#include - -void hotplug(char *rules); -void hotplug_shutdown(void); -void hotplug_last_event(uloop_timeout_handler handler); - -#endif diff --git a/initd/early.c b/initd/early.c new file mode 100644 index 0000000..77ced77 --- /dev/null +++ b/initd/early.c @@ -0,0 +1,95 @@ +/* + * 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 "../log.h" +#include "init.h" + +static void +early_mounts(void) +{ + mount("proc", "/proc", "proc", MS_NOATIME, 0); + mount("sysfs", "/sys", "sysfs", MS_NOATIME, 0); + + mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOATIME, NULL); + mkdir("/tmp/run", 0777); + mkdir("/tmp/lock", 0777); + mkdir("/tmp/state", 0777); + symlink("/tmp", "/var"); + + mount("tmpfs", "/dev", "tmpfs", MS_NOATIME, "mode=0755,size=512K"); + mkdir("/dev/shm", 0755); + mkdir("/dev/pts", 0755); + mount("devpts", "/dev/pts", "devpts", MS_NOATIME, "mode=600"); +} + +static void +early_dev(void) +{ + mkdev("*", 0600); + mknod("/dev/null", 0666, makedev(1, 3)); +} + +static void +early_console(const char *dev) +{ + struct stat s; + int dd; + + if (stat(dev, &s)) { + ERROR("Failed to stat %s\n", dev); + return; + } + + dd = open(dev, O_RDWR); + if (dd < 0) + dd = open("/dev/null", O_RDWR); + + dup2(dd, STDIN_FILENO); + dup2(dd, STDOUT_FILENO); + dup2(dd, STDERR_FILENO); + + if (dd != STDIN_FILENO && + dd != STDOUT_FILENO && + dd != STDERR_FILENO) + close(dd); +} + +static void +early_env(void) +{ + setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 1); +} + +void +early(void) +{ + if (getpid() != 1) + return; + + early_mounts(); + early_dev(); + early_env(); + early_console("/dev/console"); + + LOG("Console is alive\n"); +} diff --git a/initd/init.c b/initd/init.c new file mode 100644 index 0000000..d458f29 --- /dev/null +++ b/initd/init.c @@ -0,0 +1,115 @@ +/* + * 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 +#include +#include +#include +#include + +#include "init.h" +#include "../watchdog.h" + +unsigned int debug = 0; + +static void +signal_shutdown(int signal, siginfo_t *siginfo, void *data) +{ + fprintf(stderr, "reboot\n"); + fflush(stderr); + sync(); + sleep(2); + reboot(RB_AUTOBOOT); + while (1) + ; +} + +static struct sigaction sa_shutdown = { + .sa_sigaction = signal_shutdown, + .sa_flags = SA_SIGINFO +}; + +static void +cmdline(void) +{ + char line[256]; + int r, fd = open("/proc/cmdline", O_RDONLY); + regex_t pat_cmdline; + regmatch_t matches[2]; + + if (fd < 0) + return; + + r = read(fd, line, sizeof(line) - 1); + line[r] = '\0'; + close(fd); + + regcomp(&pat_cmdline, "init_debug=([0-9]+)", REG_EXTENDED); + if (!regexec(&pat_cmdline, line, 2, matches, 0)) { + line[matches[1].rm_eo] = '\0'; + debug = atoi(&line[matches[1].rm_so]); + } + regfree(&pat_cmdline); +} + +int +main(int argc, char **argv) +{ + pid_t pid; + + sigaction(SIGTERM, &sa_shutdown, NULL); + sigaction(SIGUSR1, &sa_shutdown, NULL); + sigaction(SIGUSR2, &sa_shutdown, NULL); + + early(); + cmdline(); + watchdog_init(1); + + pid = fork(); + if (!pid) { + char *kmod[] = { "/sbin/kmodloader", "/etc/modules-boot.d/", NULL }; + + if (debug < 3) { + int fd = open("/dev/null", O_RDWR); + + if (fd > -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } + } + execvp(kmod[0], kmod); + ERROR("Failed to start kmodloader\n"); + exit(-1); + } + if (pid <= 0) + ERROR("Failed to start kmodloader instance\n"); + uloop_init(); + preinit(); + uloop_run(); + + return 0; +} diff --git a/initd/init.h b/initd/init.h new file mode 100644 index 0000000..1321cf8 --- /dev/null +++ b/initd/init.h @@ -0,0 +1,23 @@ +/* + * 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 _INIT_H__ +#define _INIT_H__ + +#include "../log.h" + +void preinit(void); +void early(void); +int mkdev(const char *progname, int progmode); + +#endif diff --git a/initd/mkdev.c b/initd/mkdev.c new file mode 100644 index 0000000..3471461 --- /dev/null +++ b/initd/mkdev.c @@ -0,0 +1,129 @@ +/* + * 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 _BSD_SOURCE + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" + +#include "../log.h" + +static char **patterns; +static int n_patterns; +static char buf[PATH_MAX]; +static char buf2[PATH_MAX]; +static unsigned int mode = 0600; + +static bool find_pattern(const char *name) +{ + int i; + + for (i = 0; i < n_patterns; i++) + if (!fnmatch(patterns[i], name, 0)) + return true; + + return false; +} + +static void make_dev(const char *path, bool block, int major, int minor) +{ + unsigned int oldumask = umask(0); + unsigned int _mode = mode | (block ? S_IFBLK : S_IFCHR); + + DEBUG(4, "Creating %s device %s(%d,%d)\n", + block ? "block" : "character", + path, major, minor); + + mknod(path, _mode, makedev(major, minor)); + umask(oldumask); +} + +static void find_devs(bool block) +{ + char *path = block ? "/sys/dev/block" : "/sys/dev/char"; + struct dirent *dp; + DIR *dir; + + dir = opendir(path); + if (!dir) + return; + + path = buf2 + sprintf(buf2, "%s/", path); + while ((dp = readdir(dir)) != NULL) { + char *c; + int major = 0, minor = 0; + int len; + + if (dp->d_type != DT_LNK) + continue; + + if (sscanf(dp->d_name, "%d:%d", &major, &minor) != 2) + continue; + + strcpy(path, dp->d_name); + len = readlink(buf2, buf, sizeof(buf)); + if (len <= 0) + continue; + + buf[len] = 0; + if (!find_pattern(buf)) + continue; + + c = strrchr(buf, '/'); + if (!c) + continue; + + c++; + make_dev(c, block, major, minor); + } + closedir(dir); +} + +static char *add_pattern(const char *name) +{ + char *str = malloc(strlen(name) + 2); + + str[0] = '*'; + strcpy(str + 1, name); + return str; +} + +int mkdev(const char *name, int _mode) +{ + char *pattern; + + if (chdir("/dev")) + return 1; + + pattern = add_pattern(name); + patterns = &pattern; + mode = _mode; + n_patterns = 1; + find_devs(true); + find_devs(false); + chdir("/"); + + return 0; +} diff --git a/initd/preinit.c b/initd/preinit.c new file mode 100644 index 0000000..eeadbeb --- /dev/null +++ b/initd/preinit.c @@ -0,0 +1,98 @@ +/* + * 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 "init.h" +#include "../watchdog.h" + +static struct uloop_process preinit_proc; +static struct uloop_process plugd_proc; + +static void +spawn_procd(struct uloop_process *proc, int ret) +{ + char *wdt_fd = watchdog_fd(); + char *argv[] = { "/sbin/procd", NULL }; + struct stat s; + + if (plugd_proc.pid > 0) + kill(plugd_proc.pid, SIGKILL); + + if (!stat("/tmp/sysupgrade", &s)) + while (true) + sleep(1); + + unsetenv("INITRAMFS"); + unsetenv("PREINIT"); + DEBUG(2, "Exec to real procd now\n"); + if (wdt_fd) + setenv("WDTFD", wdt_fd, 1); + execvp(argv[0], argv); +} + +static void +plugd_proc_cb(struct uloop_process *proc, int ret) +{ + proc->pid = 0; +} + +void +preinit(void) +{ + char *init[] = { "/bin/sh", "/etc/preinit", NULL }; + char *plug[] = { "/sbin/procd", "-h", "/etc/hotplug-preinit.json", NULL }; + + LOG("- preinit -\n"); + + plugd_proc.cb = plugd_proc_cb; + plugd_proc.pid = fork(); + if (!plugd_proc.pid) { + execvp(plug[0], plug); + ERROR("Failed to start plugd\n"); + exit(-1); + } + if (plugd_proc.pid <= 0) { + ERROR("Failed to start new plugd instance\n"); + return; + } + uloop_process_add(&plugd_proc); + + setenv("PREINIT", "1", 1); + + preinit_proc.cb = spawn_procd; + preinit_proc.pid = fork(); + if (!preinit_proc.pid) { + execvp(init[0], init); + ERROR("Failed to start preinit\n"); + exit(-1); + } + if (preinit_proc.pid <= 0) { + ERROR("Failed to start new preinit instance\n"); + return; + } + uloop_process_add(&preinit_proc); + + DEBUG(4, "Launched preinit instance, pid=%d\n", (int) preinit_proc.pid); +} diff --git a/inittab.c b/inittab.c index d73e0b8..e935ece 100644 --- a/inittab.c +++ b/inittab.c @@ -72,7 +72,7 @@ static void fork_worker(struct init_action *a) } if (a->proc.pid > 0) { - DEBUG(2, "Launched new %s action, pid=%d\n", + DEBUG(4, "Launched new %s action, pid=%d\n", a->handler->name, (int) a->proc.pid); uloop_process_add(&a->proc); @@ -83,7 +83,7 @@ static void child_exit(struct uloop_process *proc, int ret) { struct init_action *a = container_of(proc, struct init_action, proc); - DEBUG(2, "pid:%d\n", proc->pid); + DEBUG(4, "pid:%d\n", proc->pid); uloop_timeout_set(&a->tout, a->respawn); } @@ -116,7 +116,7 @@ static void askfirst(struct init_action *a) i = stat(a->id, &s); chdir("/"); if (i || (console && !strcmp(console, a->id))) { - DEBUG(2, "Skipping %s\n", a->id); + DEBUG(4, "Skipping %s\n", a->id); return; } @@ -156,7 +156,7 @@ static void askconsole(struct init_action *a) i = stat(tty, &s); chdir("/"); if (i) { - DEBUG(2, "skipping %s\n", tty); + DEBUG(4, "skipping %s\n", tty); goto err_out; } console = strdup(tty); @@ -265,7 +265,7 @@ void procd_inittab(void) if (regexec(&pat_inittab, line, 5, matches, 0)) continue; - DEBUG(2, "Parsing inittab - %s", line); + DEBUG(4, "Parsing inittab - %s", line); for (i = TAG_ID; i <= TAG_PROCESS; i++) { line[matches[i].rm_eo] = '\0'; diff --git a/instance.c b/instance.c deleted file mode 100644 index 05b0f99..0000000 --- a/instance.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * 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 "procd.h" -#include "service.h" -#include "instance.h" -#include "md5.h" - -enum { - INSTANCE_ATTR_COMMAND, - INSTANCE_ATTR_ENV, - INSTANCE_ATTR_DATA, - INSTANCE_ATTR_NETDEV, - INSTANCE_ATTR_FILE, - INSTANCE_ATTR_TRIGGER, - INSTANCE_ATTR_RESPAWN, - INSTANCE_ATTR_NICE, - __INSTANCE_ATTR_MAX -}; - -static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = { - [INSTANCE_ATTR_COMMAND] = { "command", BLOBMSG_TYPE_ARRAY }, - [INSTANCE_ATTR_ENV] = { "env", BLOBMSG_TYPE_TABLE }, - [INSTANCE_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE }, - [INSTANCE_ATTR_NETDEV] = { "netdev", BLOBMSG_TYPE_ARRAY }, - [INSTANCE_ATTR_FILE] = { "file", BLOBMSG_TYPE_ARRAY }, - [INSTANCE_ATTR_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY }, - [INSTANCE_ATTR_RESPAWN] = { "respawn", BLOBMSG_TYPE_ARRAY }, - [INSTANCE_ATTR_NICE] = { "nice", BLOBMSG_TYPE_INT32 }, -}; - -struct instance_netdev { - struct blobmsg_list_node node; - int ifindex; -}; - -struct instance_file { - struct blobmsg_list_node node; - uint32_t md5[4]; -}; - -static void -instance_run(struct service_instance *in) -{ - struct blobmsg_list_node *var; - struct blob_attr *cur; - char **argv; - int argc = 1; /* NULL terminated */ - int rem, fd; - - if (in->nice) - setpriority(PRIO_PROCESS, 0, in->nice); - - blobmsg_for_each_attr(cur, in->command, rem) - argc++; - - blobmsg_list_for_each(&in->env, var) - setenv(blobmsg_name(var->data), blobmsg_data(var->data), 1); - - argv = alloca(sizeof(char *) * argc); - argc = 0; - - blobmsg_for_each_attr(cur, in->command, rem) - argv[argc++] = blobmsg_data(cur); - - argv[argc] = NULL; - fd = open("/dev/null", O_RDWR); - if (fd > -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); - } - execvp(argv[0], argv); - exit(127); -} - -void -instance_start(struct service_instance *in) -{ - int pid; - - if (in->proc.pending) - return; - - in->restart = false; - in->halt = !in->respawn; - - if (!in->valid) - return; - - pid = fork(); - if (pid < 0) - return; - - if (!pid) { - uloop_done(); - instance_run(in); - return; - } - - DEBUG(1, "Started instance %s::%s\n", in->srv->name, in->name); - in->proc.pid = pid; - clock_gettime(CLOCK_MONOTONIC, &in->start); - uloop_process_add(&in->proc); -} - -static void -instance_timeout(struct uloop_timeout *t) -{ - struct service_instance *in; - - in = container_of(t, struct service_instance, timeout); - - if (!in->halt && (in->restart || in->respawn)) - instance_start(in); -} - -static void -instance_exit(struct uloop_process *p, int ret) -{ - struct service_instance *in; - struct timespec tp; - long runtime; - - in = container_of(p, struct service_instance, proc); - - clock_gettime(CLOCK_MONOTONIC, &tp); - runtime = tp.tv_sec - in->start.tv_sec; - - DEBUG(1, "Instance %s::%s exit with error code %d after %ld seconds\n", in->srv->name, in->name, ret, runtime); - if (upgrade_running) - return; - - uloop_timeout_cancel(&in->timeout); - if (in->halt) { - /* no action */ - } else if (in->restart) { - instance_start(in); - } else if (in->respawn) { - if (runtime < in->respawn_threshold) - in->respawn_count++; - else - in->respawn_count = 0; - if (in->respawn_count > in->respawn_retry) { - LOG("Instance %s::%s s in a crash loop %d crashes, %ld seconds since last crash\n", - in->srv->name, in->name, in->respawn_count, runtime); - in->restart = in->respawn = 0; - in->halt = 1; - } else { - uloop_timeout_set(&in->timeout, in->respawn_timeout * 1000); - } - } -} - -void -instance_stop(struct service_instance *in) -{ - if (!in->proc.pending) - return; - in->halt = true; - in->restart = in->respawn = false; - kill(in->proc.pid, SIGTERM); -} - -static void -instance_restart(struct service_instance *in) -{ - if (!in->proc.pending) - return; - in->halt = false; - in->restart = true; - kill(in->proc.pid, SIGTERM); -} - -static bool -instance_config_changed(struct service_instance *in, struct service_instance *in_new) -{ - if (!in->valid) - return true; - - if (!blob_attr_equal(in->command, in_new->command)) - return true; - - if (!blobmsg_list_equal(&in->env, &in_new->env)) - return true; - - if (!blobmsg_list_equal(&in->data, &in_new->data)) - return true; - - if (!blobmsg_list_equal(&in->netdev, &in_new->netdev)) - return true; - - if (!blobmsg_list_equal(&in->file, &in_new->file)) - return true; - - if (in->nice != in_new->nice) - return true; - - return false; -} - -static bool -instance_netdev_cmp(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2) -{ - struct instance_netdev *n1 = container_of(l1, struct instance_netdev, node); - struct instance_netdev *n2 = container_of(l2, struct instance_netdev, node); - - return n1->ifindex == n2->ifindex; -} - -static void -instance_netdev_update(struct blobmsg_list_node *l) -{ - struct instance_netdev *n = container_of(l, struct instance_netdev, node); - - n->ifindex = if_nametoindex(n->node.avl.key); -} - -static bool -instance_file_cmp(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2) -{ - struct instance_file *f1 = container_of(l1, struct instance_file, node); - struct instance_file *f2 = container_of(l2, struct instance_file, node); - - return !memcmp(f1->md5, f2->md5, sizeof(f1->md5)); -} - -static void -instance_file_update(struct blobmsg_list_node *l) -{ - struct instance_file *f = container_of(l, struct instance_file, node); - md5_ctx_t md5; - char buf[256]; - int len, fd; - - memset(f->md5, 0, sizeof(f->md5)); - - fd = open(l->avl.key, O_RDONLY); - if (fd < 0) - return; - - md5_begin(&md5); - do { - len = read(fd, buf, sizeof(buf)); - if (len < 0) { - if (errno == EINTR) - continue; - - break; - } - if (!len) - break; - - md5_hash(buf, len, &md5); - } while(1); - - md5_end(f->md5, &md5); - close(fd); -} - -static bool -instance_fill_array(struct blobmsg_list *l, struct blob_attr *cur, blobmsg_update_cb cb, bool array) -{ - struct blobmsg_list_node *node; - - if (!cur) - return true; - - if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING)) - return false; - - blobmsg_list_fill(l, blobmsg_data(cur), blobmsg_data_len(cur), array); - if (cb) { - blobmsg_list_for_each(l, node) - cb(node); - } - return true; -} - -static bool -instance_config_parse(struct service_instance *in) -{ - struct blob_attr *tb[__INSTANCE_ATTR_MAX]; - struct blob_attr *cur, *cur2; - int argc = 0; - int rem; - - blobmsg_parse(instance_attr, __INSTANCE_ATTR_MAX, tb, - blobmsg_data(in->config), blobmsg_data_len(in->config)); - - cur = tb[INSTANCE_ATTR_COMMAND]; - if (!cur) - return false; - - if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING)) - return false; - - blobmsg_for_each_attr(cur2, cur, rem) { - argc++; - break; - } - if (!argc) - return false; - - in->command = cur; - - if (tb[INSTANCE_ATTR_RESPAWN]) { - int i = 0; - uint32_t vals[3] = { 3600, 5, 5}; - - blobmsg_for_each_attr(cur2, tb[INSTANCE_ATTR_RESPAWN], rem) { - if ((i >= 3) && (blobmsg_type(cur2) == BLOBMSG_TYPE_STRING)) - continue; - vals[i] = atoi(blobmsg_get_string(cur2)); - i++; - } - in->respawn = true; - in->respawn_count = 0; - in->respawn_threshold = vals[0]; - in->respawn_timeout = vals[1]; - in->respawn_retry = vals[2]; - } - if (tb[INSTANCE_ATTR_TRIGGER]) { - in->trigger = malloc(blob_pad_len(tb[INSTANCE_ATTR_TRIGGER])); - if (!in->trigger) - return -1; - memcpy(in->trigger, tb[INSTANCE_ATTR_TRIGGER], blob_pad_len(tb[INSTANCE_ATTR_TRIGGER])); - trigger_add(in->trigger, in); - } - - if ((cur = tb[INSTANCE_ATTR_NICE])) { - in->nice = (int8_t) blobmsg_get_u32(cur); - if (in->nice < -20 || in->nice > 20) - return false; - } - - if (!instance_fill_array(&in->env, tb[INSTANCE_ATTR_ENV], NULL, false)) - return false; - - if (!instance_fill_array(&in->data, tb[INSTANCE_ATTR_DATA], NULL, false)) - return false; - - if (!instance_fill_array(&in->netdev, tb[INSTANCE_ATTR_NETDEV], instance_netdev_update, true)) - return false; - - if (!instance_fill_array(&in->file, tb[INSTANCE_ATTR_FILE], instance_file_update, true)) - return false; - - return true; -} - -static void -instance_config_cleanup(struct service_instance *in) -{ - blobmsg_list_free(&in->env); - blobmsg_list_free(&in->data); - blobmsg_list_free(&in->netdev); -} - -static void -instance_config_move(struct service_instance *in, struct service_instance *in_src) -{ - instance_config_cleanup(in); - blobmsg_list_move(&in->env, &in_src->env); - blobmsg_list_move(&in->data, &in_src->data); - blobmsg_list_move(&in->netdev, &in_src->netdev); - in->trigger = in_src->trigger; - in->command = in_src->command; - in->name = in_src->name; - in->node.avl.key = in_src->node.avl.key; - - free(in->config); - in->config = in_src->config; - in_src->config = NULL; -} - -bool -instance_update(struct service_instance *in, struct service_instance *in_new) -{ - bool changed = instance_config_changed(in, in_new); - bool running = in->proc.pending; - - if (!changed && running) - return false; - - if (!running) { - if (changed) - instance_config_move(in, in_new); - instance_start(in); - } else { - instance_restart(in); - instance_config_move(in, in_new); - /* restart happens in the child callback handler */ - } - return true; -} - -void -instance_free(struct service_instance *in) -{ - uloop_process_delete(&in->proc); - uloop_timeout_cancel(&in->timeout); - trigger_del(in); - free(in->trigger); - instance_config_cleanup(in); - free(in->config); - free(in); -} - -void -instance_init(struct service_instance *in, struct service *s, struct blob_attr *config) -{ - config = blob_memdup(config); - in->srv = s; - in->name = blobmsg_name(config); - in->config = config; - in->timeout.cb = instance_timeout; - in->proc.cb = instance_exit; - - blobmsg_list_init(&in->netdev, struct instance_netdev, node, instance_netdev_cmp); - blobmsg_list_init(&in->file, struct instance_file, node, instance_file_cmp); - blobmsg_list_simple_init(&in->env); - blobmsg_list_simple_init(&in->data); - in->valid = instance_config_parse(in); -} - -void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose) -{ - void *i; - struct pid_info pi; - - i = blobmsg_open_table(b, in->name); - blobmsg_add_u8(b, "running", in->proc.pending); - if (in->proc.pending) - blobmsg_add_u32(b, "pid", in->proc.pid); - blobmsg_add_blob(b, in->command); - - if (!avl_is_empty(&in->env.avl)) { - struct blobmsg_list_node *var; - void *e = blobmsg_open_table(b, "env"); - blobmsg_list_for_each(&in->env, var) - blobmsg_add_string(b, blobmsg_name(var->data), blobmsg_data(var->data)); - blobmsg_close_table(b, e); - } - - if (in->respawn) { - void *r = blobmsg_open_table(b, "respawn"); - blobmsg_add_u32(b, "timeout", in->respawn_timeout); - blobmsg_add_u32(b, "threshold", in->respawn_threshold); - blobmsg_add_u32(b, "retry", in->respawn_retry); - blobmsg_close_table(b, r); - } - - if (verbose && in->trigger) - blobmsg_add_blob(b, in->trigger); - if (!measure_process(in->proc.pid, &pi)) { - struct timespec tp; - long uptime; - - clock_gettime(CLOCK_MONOTONIC, &tp); - uptime = tp.tv_sec - in->start.tv_sec; - - blobmsg_add_u8(b, "ppid", pi.ppid); - blobmsg_add_u16(b, "uid", pi.uid); - blobmsg_add_u32(b, "fdcount", pi.fdcount); - blobmsg_add_u32(b, "vmsize", pi.vmsize); - blobmsg_add_u32(b, "uptime", uptime); - } - blobmsg_close_table(b, i); -} diff --git a/instance.h b/instance.h deleted file mode 100644 index 6e69086..0000000 --- a/instance.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -#ifndef __PROCD_INSTANCE_H -#define __PROCD_INSTANCE_H - -#include -#include -#include "utils.h" - -#define RESPAWN_ERROR (5 * 60) - -struct service_instance { - struct vlist_node node; - struct service *srv; - const char *name; - - int8_t nice; - bool valid; - - bool halt; - bool restart; - bool respawn; - int respawn_count; - struct timespec start; - - uint32_t respawn_timeout; - uint32_t respawn_threshold; - uint32_t respawn_retry; - - struct blob_attr *config; - struct uloop_process proc; - struct uloop_timeout timeout; - - struct blob_attr *command; - struct blob_attr *trigger; - struct blobmsg_list env; - struct blobmsg_list data; - struct blobmsg_list netdev; - struct blobmsg_list file; -}; - -void instance_start(struct service_instance *in); -void instance_stop(struct service_instance *in); -bool instance_update(struct service_instance *in, struct service_instance *in_new); -void instance_init(struct service_instance *in, struct service *s, struct blob_attr *config); -void instance_free(struct service_instance *in); -void instance_dump(struct blob_buf *b, struct service_instance *in, int debug); - -#endif diff --git a/libvalidate.h b/libvalidate.h deleted file mode 100644 index d3b8e05..0000000 --- a/libvalidate.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _VALIDATE_H__ -#define _VALIDATE_H__ - -bool dt_parse(const char *code, const char *value); - -#endif diff --git a/log.c b/log.c deleted file mode 100644 index d367388..0000000 --- a/log.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 "procd.h" -#include "syslog.h" - -static int notify; -struct ubus_context *_ctx; -static struct blob_buf b; - -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 }; - -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 blob_attr *tb; - struct log_head *l; - void *lines, *entry; - int count = 0; - - if (msg) { - blobmsg_parse(&read_policy, 1, &tb, blob_data(msg), blob_len(msg)); - if (tb) - count = blobmsg_get_u32(tb); - } - - blob_buf_init(&b, 0); - lines = blobmsg_open_array(&b, "lines"); - - l = log_list(count, NULL); - - while (l) { - entry = blobmsg_open_table(&b, NULL); - 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); - blobmsg_close_table(&b, entry); - l = log_list(count, l); - } - blobmsg_close_table(&b, lines); - ubus_send_reply(ctx, req, b.head); - - 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 void log_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) -{ - notify = obj->has_subscribers; -} - -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), - .subscribe_cb = log_subscribe_cb, -}; - -void ubus_notify_log(struct log_head *l) -{ - int ret; - - if (!notify) - return; - - blob_buf_init(&b, 0); - 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)); - - ret = ubus_notify(_ctx, &log_object, l->data, b.head, -1); - if (ret) - ERROR("Failed to notify log: %s\n", ubus_strerror(ret)); -} - -void ubus_init_log(struct ubus_context *ctx) -{ - int ret; - - _ctx = ctx; - - ret = ubus_add_object(ctx, &log_object); - if (ret) - ERROR("Failed to add object: %s\n", ubus_strerror(ret)); -} diff --git a/log.h b/log.h new file mode 100644 index 0000000..968c136 --- /dev/null +++ b/log.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef __LOG_H +#define __LOG_H + +#include + +#define DEBUG(level, fmt, ...) do { \ + if (debug >= level) { \ + syslog(0, fmt, ## __VA_ARGS__); \ + fprintf(stderr, "procd: %s(%d): " fmt, __func__, __LINE__, ## __VA_ARGS__); \ + } } while (0) + +#define LOG(fmt, ...) do { \ + syslog(0, fmt, ## __VA_ARGS__); \ + fprintf(stderr, "procd: "fmt, ## __VA_ARGS__); \ + } while (0) + +#define ERROR(fmt, ...) do { \ + syslog(0, fmt, ## __VA_ARGS__); \ + fprintf(stderr, "procd: "fmt, ## __VA_ARGS__); \ + } while (0) + +extern unsigned int debug; + +#endif diff --git a/logread.c b/logread.c deleted file mode 100644 index e8749f8..0000000 --- a/logread.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * 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 - -#define SYSLOG_NAMES -#include - -#include -#include -#include -#include "libubus.h" -#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 ubus_subscriber log_event; -static struct uloop_timeout retry; -static struct uloop_fd sender; -static const char *log_file, *log_ip, *log_port, *log_prefix, *pid_file, *hostname; -static int log_type = LOG_STDOUT; -static int log_size, log_udp; - -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(0, "Logread connected to %s:%s\n", log_ip, log_port); - } -} - -static void log_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s, - uint32_t id) -{ - fprintf(stderr, "Object %08x went away\n", id); -} - -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 ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - 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; - - 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]) - 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); - } - } - - 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; - - *buf = '\0'; - if (hostname) - snprintf(buf, sizeof(buf), "%s ", hostname); - if (log_prefix) { - strncat(buf, log_prefix, sizeof(buf)); - strncat(buf, ": ", sizeof(buf)); - } - if (blobmsg_get_u32(tb[LOG_SOURCE]) == SOURCE_KLOG) - strncat(buf, "kernel: ", sizeof(buf)); - strncat(buf, method, sizeof(buf)); - if (log_udp) - err = write(sender.fd, buf, strlen(buf)); - else - err = send(sender.fd, buf, strlen(buf), 0); - - if (err < 0) { - syslog(0, "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:"), - method); - write(sender.fd, buf, strlen(buf)); - } - - free(str); - if (log_type == LOG_FILE) - fsync(sender.fd); - - return 0; -} - -static void follow_log(struct ubus_context *ctx, int id) -{ - FILE *fp; - int ret; - - signal(SIGPIPE, SIG_IGN); - - if (pid_file) { - fp = fopen(pid_file, "w+"); - if (fp) { - fprintf(fp, "%d", getpid()); - fclose(fp); - } - } - - uloop_init(); - ubus_add_uloop(ctx); - - log_event.remove_cb = log_handle_remove; - log_event.cb = log_notify; - ret = ubus_register_subscriber(ctx, &log_event); - if (ret) - fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); - - ret = ubus_subscribe(ctx, &log_event, id); - if (ret) - fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); - - 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; - } - - uloop_run(); - ubus_free(ctx); - uloop_done(); -} - -enum { - READ_LINE, - __READ_MAX -}; - - - -static const struct blobmsg_policy read_policy[] = { - [READ_LINE] = { .name = "lines", .type = BLOBMSG_TYPE_ARRAY }, -}; - -static void read_cb(struct ubus_request *req, int type, struct blob_attr *msg) -{ - struct blob_attr *cur; - struct blob_attr *_tb[__READ_MAX]; - time_t t; - int rem; - - if (!msg) - return; - - blobmsg_parse(read_policy, ARRAY_SIZE(read_policy), _tb, blob_data(msg), blob_len(msg)); - if (!_tb[READ_LINE]) - return; - blobmsg_for_each_attr(cur, _tb[READ_LINE], rem) { - struct blob_attr *tb[__LOG_MAX]; - uint32_t p; - char *c; - - if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) - continue; - - blobmsg_parse(log_policy, ARRAY_SIZE(log_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur)); - if (!tb[LOG_MSG] || !tb[LOG_ID] || !tb[LOG_PRIO] || !tb[LOG_SOURCE] || !tb[LOG_TIME]) - continue; - - t = blobmsg_get_u64(tb[LOG_TIME]); - p = blobmsg_get_u32(tb[LOG_PRIO]); - c = ctime(&t); - c[strlen(c) - 1] = '\0'; - - printf("%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:"), - blobmsg_get_string(tb[LOG_MSG])); - } -} - -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" - " -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" - "\n", prog); - return 1; -} - -int main(int argc, char **argv) -{ - struct ubus_context *ctx; - uint32_t id; - const char *ubus_socket = NULL; - int ch, ret, subscribe = 0, lines = 0; - static struct blob_buf b; - - while ((ch = getopt(argc, argv, "ufcs:l:r:F:p:S:P:h:")) != -1) { - switch (ch) { - case 'u': - log_udp = 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': - subscribe = 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; - default: - return usage(*argv); - } - } - - ctx = ubus_connect(ubus_socket); - if (!ctx) { - fprintf(stderr, "Failed to connect to ubus\n"); - return -1; - } - - ret = ubus_lookup_id(ctx, "log", &id); - if (ret) - fprintf(stderr, "Failed to find log object: %s\n", ubus_strerror(ret)); - - if (!subscribe || lines) { - blob_buf_init(&b, 0); - if (lines) - blobmsg_add_u32(&b, "lines", lines); - ubus_invoke(ctx, id, "read", b.head, read_cb, 0, 3000); - } - - if (subscribe) - follow_log(ctx, id); - - return 0; -} diff --git a/main.c b/main.c deleted file mode 100644 index 49d85f4..0000000 --- a/main.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 "procd.h" -#include "hotplug.h" -#include "watchdog.h" - -static int usage(const char *prog) -{ - ERROR("Usage: %s [options]\n" - "Options:\n" - " -s : Path to ubus socket\n" - " -d: Enable debug messages\n" - "\n", prog); - return 1; -} - - -static int main_procd_init(int argc, char **argv) -{ - procd_signal_preinit(); - procd_early(); - debug_init(); - watchdog_init(1); - system("/sbin/kmodloader /etc/modules-boot.d/"); - uloop_init(); - hotplug("/etc/hotplug-preinit.json"); - procd_preinit(); - uloop_run(); - return 0; -} - -int main(int argc, char **argv) -{ - int ch; - - if (!strcmp(basename(*argv), "init")) - return main_procd_init(argc, argv); - - while ((ch = getopt(argc, argv, "ds:")) != -1) { - switch (ch) { - case 's': - ubus_socket = optarg; - break; - case 'd': - debug++; - break; - default: - return usage(argv[0]); - } - } - uloop_init(); - procd_signal(); - trigger_init(); - if (getpid() != 1) - procd_connect_ubus(); - else - procd_state_next(); - uloop_run(); - - return 0; -} diff --git a/md5.c b/md5.c deleted file mode 100644 index ec24dd2..0000000 --- a/md5.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * md5.c - Compute MD5 checksum of strings according to the - * definition of MD5 in RFC 1321 from April 1992. - * - * Written by Ulrich Drepper , 1995. - * - * Copyright (C) 1995-1999 Free Software Foundation, Inc. - * Copyright (C) 2001 Manuel Novoa III - * Copyright (C) 2003 Glenn L. McGrath - * Copyright (C) 2003 Erik Andersen - * - * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. - */ - -#include /* TODO: better include for bswap_32 compat */ -#include "md5.h" - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define SWAP_LE32(x) (x) -#else -#define SWAP_LE32(x) bswap_32(x) -#endif - -/* Initialize structure containing state of computation. - * (RFC 1321, 3.3: Step 3) - */ -void md5_begin(md5_ctx_t *ctx) -{ - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - - ctx->total = 0; - ctx->buflen = 0; -} - -/* These are the four functions used in the four steps of the MD5 algorithm - * and defined in the RFC 1321. The first function is a little bit optimized - * (as found in Colin Plumbs public domain implementation). - * #define FF(b, c, d) ((b & c) | (~b & d)) - */ -# define FF(b, c, d) (d ^ (b & (c ^ d))) -# define FG(b, c, d) FF (d, b, c) -# define FH(b, c, d) (b ^ c ^ d) -# define FI(b, c, d) (c ^ (b | ~d)) - -/* Hash a single block, 64 bytes long and 4-byte aligned. */ -static void md5_hash_block(const void *buffer, md5_ctx_t *ctx) -{ - uint32_t correct_words[16]; - const uint32_t *words = buffer; - - static const uint32_t C_array[] = { - /* round 1 */ - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, - /* round 2 */ - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, - 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, - /* round 3 */ - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, - /* round 4 */ - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 - }; - - static const char P_array[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ - 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ - 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ - 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ - }; - - static const char S_array[] = { - 7, 12, 17, 22, - 5, 9, 14, 20, - 4, 11, 16, 23, - 6, 10, 15, 21 - }; - - uint32_t A = ctx->A; - uint32_t B = ctx->B; - uint32_t C = ctx->C; - uint32_t D = ctx->D; - - uint32_t *cwp = correct_words; - -# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) - - const uint32_t *pc; - const char *pp; - const char *ps; - int i; - uint32_t temp; - - for (i = 0; i < 16; i++) { - cwp[i] = SWAP_LE32(words[i]); - } - words += 16; - - pc = C_array; - pp = P_array; - ps = S_array; - - for (i = 0; i < 16; i++) { - temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++; - CYCLIC(temp, ps[i & 3]); - temp += B; - A = D; - D = C; - C = B; - B = temp; - } - - ps += 4; - for (i = 0; i < 16; i++) { - temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++; - CYCLIC(temp, ps[i & 3]); - temp += B; - A = D; - D = C; - C = B; - B = temp; - } - ps += 4; - for (i = 0; i < 16; i++) { - temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++; - CYCLIC(temp, ps[i & 3]); - temp += B; - A = D; - D = C; - C = B; - B = temp; - } - ps += 4; - for (i = 0; i < 16; i++) { - temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++; - CYCLIC(temp, ps[i & 3]); - temp += B; - A = D; - D = C; - C = B; - B = temp; - } - - - ctx->A += A; - ctx->B += B; - ctx->C += C; - ctx->D += D; -} - -/* Feed data through a temporary buffer to call md5_hash_aligned_block() - * with chunks of data that are 4-byte aligned and a multiple of 64 bytes. - * This function's internal buffer remembers previous data until it has 64 - * bytes worth to pass on. Call md5_end() to flush this buffer. */ - -void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx) -{ - char *buf = (char *)buffer; - - /* RFC 1321 specifies the possible length of the file up to 2^64 bits, - * Here we only track the number of bytes. */ - - ctx->total += len; - - // Process all input. - - while (len) { - unsigned i = 64 - ctx->buflen; - - // Copy data into aligned buffer. - - if (i > len) - i = len; - memcpy(ctx->buffer + ctx->buflen, buf, i); - len -= i; - ctx->buflen += i; - buf += i; - - // When buffer fills up, process it. - - if (ctx->buflen == 64) { - md5_hash_block(ctx->buffer, ctx); - ctx->buflen = 0; - } - } -} - -/* Process the remaining bytes in the buffer and put result from CTX - * in first 16 bytes following RESBUF. The result is always in little - * endian byte order, so that a byte-wise output yields to the wanted - * ASCII representation of the message digest. - * - * IMPORTANT: On some systems it is required that RESBUF is correctly - * aligned for a 32 bits value. - */ -void md5_end(void *resbuf, md5_ctx_t *ctx) -{ - char *buf = ctx->buffer; - int i; - - /* Pad data to block size. */ - - buf[ctx->buflen++] = 0x80; - memset(buf + ctx->buflen, 0, 128 - ctx->buflen); - - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - ctx->total <<= 3; - if (ctx->buflen > 56) - buf += 64; - - for (i = 0; i < 8; i++) - buf[56 + i] = ctx->total >> (i*8); - - /* Process last bytes. */ - if (buf != ctx->buffer) - md5_hash_block(ctx->buffer, ctx); - md5_hash_block(buf, ctx); - - /* Put result from CTX in first 16 bytes following RESBUF. The result is - * always in little endian byte order, so that a byte-wise output yields - * to the wanted ASCII representation of the message digest. - * - * IMPORTANT: On some systems it is required that RESBUF is correctly - * aligned for a 32 bits value. - */ - ((uint32_t *) resbuf)[0] = SWAP_LE32(ctx->A); - ((uint32_t *) resbuf)[1] = SWAP_LE32(ctx->B); - ((uint32_t *) resbuf)[2] = SWAP_LE32(ctx->C); - ((uint32_t *) resbuf)[3] = SWAP_LE32(ctx->D); -} diff --git a/md5.h b/md5.h deleted file mode 100644 index e1731ef..0000000 --- a/md5.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - */ - -#ifndef __PROCD_MD5_H -#define __PROCD_MD5_H - -#include -#include - -typedef struct md5_ctx { - uint32_t A; - uint32_t B; - uint32_t C; - uint32_t D; - uint64_t total; - uint32_t buflen; - char buffer[128]; -} md5_ctx_t; - -void md5_begin(md5_ctx_t *ctx); -void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); -void md5_end(void *resbuf, md5_ctx_t *ctx); - -#endif diff --git a/measure.c b/measure.c deleted file mode 100644 index 9e21a66..0000000 --- a/measure.c +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (C) 2010 Steven Barth - * Copyright (C) 2013 John Crispin - * - * 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. - * - * 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. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "procd.h" - -static regex_t pat_vmsize, pat_ppid, pat_state, pat_uid; - -static void __attribute__((constructor)) measure_init() { - regcomp(&pat_vmsize, "VmSize:[ \t]*([0-9]*) kB", REG_EXTENDED); - regcomp(&pat_uid, "Uid:[ \t]*([0-9]*).*", REG_EXTENDED); - regcomp(&pat_ppid, "PPid:[ \t]*([0-9]+)", REG_EXTENDED); - regcomp(&pat_state, "State:[ \t]*([A-Z])", REG_EXTENDED); -} - -static void __attribute__((destructor)) measure_fini() { - regfree(&pat_vmsize); - regfree(&pat_ppid); - regfree(&pat_uid); - regfree(&pat_state); -} - -int measure_process(pid_t pid, struct pid_info *pi) { - int fd; - char buffer[512] = ""; - ssize_t rxed; - regmatch_t matches[2]; - glob_t gl; - int i; - - memset(pi, 0, sizeof(*pi)); - - snprintf(buffer, sizeof(buffer), "/proc/%i/fd/*", (int)pid); - - if (glob(buffer, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) { - fprintf(stderr, "glob failed on %s\n", buffer); - return -1; - } - - for (i = 0; i < gl.gl_pathc; i++) - if (isdigit(basename(gl.gl_pathv[i])[0])) - pi->fdcount++; - globfree(&gl); - - snprintf(buffer, sizeof(buffer), "/proc/%i/status", (int)pid); - fd = open(buffer, O_RDONLY); - if (fd == -1) - return -1; - - rxed = read(fd, buffer, sizeof(buffer) - 1); - close(fd); - if (rxed == -1) - return -1; - - buffer[rxed] = 0; - - if (!regexec(&pat_state, buffer, 2, matches, 0)) - pi->stat = buffer[matches[1].rm_so]; - - if (!regexec(&pat_ppid, buffer, 2, matches, 0)) - pi->ppid = atoi(buffer + matches[1].rm_so); - - if (!regexec(&pat_uid, buffer, 2, matches, 0)) - pi->uid = atoi(buffer + matches[1].rm_so); - - if (!regexec(&pat_vmsize, buffer, 2, matches, 0)) - pi->vmsize = atoi(buffer + matches[1].rm_so) * 1024; - - return 0; -} diff --git a/mkdev.c b/mkdev.c deleted file mode 100644 index 0f55554..0000000 --- a/mkdev.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 _BSD_SOURCE - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "procd.h" - -static char **patterns; -static int n_patterns; -static char buf[PATH_MAX]; -static char buf2[PATH_MAX]; -static unsigned int mode = 0600; - -static bool find_pattern(const char *name) -{ - int i; - - for (i = 0; i < n_patterns; i++) - if (!fnmatch(patterns[i], name, 0)) - return true; - - return false; -} - -static void make_dev(const char *path, bool block, int major, int minor) -{ - unsigned int oldumask = umask(0); - unsigned int _mode = mode | (block ? S_IFBLK : S_IFCHR); - - DEBUG(2, "Creating %s device %s(%d,%d)\n", - block ? "block" : "character", - path, major, minor); - - mknod(path, _mode, makedev(major, minor)); - umask(oldumask); -} - -static void find_devs(bool block) -{ - char *path = block ? "/sys/dev/block" : "/sys/dev/char"; - struct dirent *dp; - DIR *dir; - - dir = opendir(path); - if (!dir) - return; - - path = buf2 + sprintf(buf2, "%s/", path); - while ((dp = readdir(dir)) != NULL) { - char *c; - int major = 0, minor = 0; - int len; - - if (dp->d_type != DT_LNK) - continue; - - if (sscanf(dp->d_name, "%d:%d", &major, &minor) != 2) - continue; - - strcpy(path, dp->d_name); - len = readlink(buf2, buf, sizeof(buf)); - if (len <= 0) - continue; - - buf[len] = 0; - if (!find_pattern(buf)) - continue; - - c = strrchr(buf, '/'); - if (!c) - continue; - - c++; - make_dev(c, block, major, minor); - } - closedir(dir); -} - -static char *add_pattern(const char *name) -{ - char *str = malloc(strlen(name) + 2); - - str[0] = '*'; - strcpy(str + 1, name); - return str; -} - -int mkdev(const char *name, int _mode) -{ - char *pattern; - - if (chdir("/dev")) - return 1; - - pattern = add_pattern(name); - patterns = &pattern; - mode = _mode; - n_patterns = 1; - find_devs(true); - find_devs(false); - chdir("/"); - - return 0; -} diff --git a/plug/coldplug.c b/plug/coldplug.c new file mode 100644 index 0000000..466b759 --- /dev/null +++ b/plug/coldplug.c @@ -0,0 +1,66 @@ +/* + * 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 "../procd.h" + +#include "hotplug.h" + +static struct uloop_process udevtrigger; + +static void coldplug_complete(struct uloop_timeout *t) +{ + DEBUG(4, "Coldplug complete\n"); + hotplug_last_event(NULL); + procd_state_next(); +} + +static void udevtrigger_complete(struct uloop_process *proc, int ret) +{ + DEBUG(4, "Finished udevtrigger\n"); + hotplug_last_event(coldplug_complete); +} + +void procd_coldplug(void) +{ + char *argv[] = { "udevtrigger", NULL }; + + umount2("/dev/pts", MNT_DETACH); + umount2("/dev/", MNT_DETACH); + mount("tmpfs", "/dev", "tmpfs", 0, "mode=0755,size=512K"); + mkdir("/dev/shm", 0755); + mkdir("/dev/pts", 0755); + mount("devpts", "/dev/pts", "devpts", 0, 0); + udevtrigger.cb = udevtrigger_complete; + udevtrigger.pid = fork(); + if (!udevtrigger.pid) { + execvp(argv[0], argv); + ERROR("Failed to start coldplug\n"); + exit(-1); + } + + if (udevtrigger.pid <= 0) { + ERROR("Failed to start new coldplug instance\n"); + return; + } + + uloop_process_add(&udevtrigger); + + DEBUG(4, "Launched coldplug instance, pid=%d\n", (int) udevtrigger.pid); +} diff --git a/plug/hotplug.c b/plug/hotplug.c new file mode 100644 index 0000000..ca1e823 --- /dev/null +++ b/plug/hotplug.c @@ -0,0 +1,478 @@ +/* + * 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 +#include +#include +#include + +#include "../procd.h" + +#include "hotplug.h" + +#define HOTPLUG_WAIT 500 + +struct cmd_queue { + struct list_head list; + + struct blob_attr *msg; + struct blob_attr *data; + void (*handler)(struct blob_attr *msg, struct blob_attr *data); +}; + +static LIST_HEAD(cmd_queue); +static struct uloop_process queue_proc; +static struct uloop_timeout last_event; +static struct blob_buf b; +static char *rule_file; +static struct blob_buf script; + +static char *hotplug_msg_find_var(struct blob_attr *msg, const char *name) +{ + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, msg, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (strcmp(blobmsg_name(cur), name) != 0) + continue; + + return blobmsg_data(cur); + } + + return NULL; +} + +static void mkdir_p(char *dir) +{ + char *l = strrchr(dir, '/'); + + if (l) { + *l = '\0'; + mkdir_p(dir); + *l = '/'; + mkdir(dir, 0755); + } +} + +static void handle_makedev(struct blob_attr *msg, struct blob_attr *data) +{ + unsigned int oldumask = umask(0); + static struct blobmsg_policy mkdev_policy[2] = { + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_STRING }, + }; + struct blob_attr *tb[2]; + char *minor = hotplug_msg_find_var(msg, "MINOR"); + char *major = hotplug_msg_find_var(msg, "MAJOR"); + char *subsystem = hotplug_msg_find_var(msg, "SUBSYSTEM"); + + blobmsg_parse_array(mkdev_policy, 2, tb, blobmsg_data(data), blobmsg_data_len(data)); + if (tb[0] && tb[1] && minor && major && subsystem) { + mode_t m = S_IFCHR; + char *d = strdup(blobmsg_get_string(tb[0])); + + d = dirname(d); + mkdir_p(d); + free(d); + + if (!strcmp(subsystem, "block")) + m = S_IFBLK; + mknod(blobmsg_get_string(tb[0]), + m | strtoul(blobmsg_data(tb[1]), NULL, 8), + makedev(atoi(major), atoi(minor))); + } + umask(oldumask); +} + +static void handle_rm(struct blob_attr *msg, struct blob_attr *data) +{ + static struct blobmsg_policy rm_policy = { + .type = BLOBMSG_TYPE_STRING, + }; + struct blob_attr *tb; + + blobmsg_parse_array(&rm_policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data)); + if (tb) + unlink(blobmsg_data(tb)); +} + +static void handle_exec(struct blob_attr *msg, struct blob_attr *data) +{ + char *argv[8]; + struct blob_attr *cur; + int rem, fd; + int i = 0; + + blobmsg_for_each_attr(cur, msg, rem) + setenv(blobmsg_name(cur), blobmsg_data(cur), 1); + + blobmsg_for_each_attr(cur, data, rem) { + argv[i] = blobmsg_data(cur); + i++; + if (i == 7) + break; + } + + if (debug < 3) { + fd = open("/dev/null", O_RDWR); + if (fd > -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } + } + + if (i > 0) { + argv[i] = NULL; + execvp(argv[0], &argv[0]); + } + exit(-1); +} + +static void handle_firmware(struct blob_attr *msg, struct blob_attr *data) +{ + char *dir = blobmsg_get_string(blobmsg_data(data)); + char *file = hotplug_msg_find_var(msg, "FIRMWARE"); + char *dev = hotplug_msg_find_var(msg, "DEVPATH"); + void *fw_data; + struct stat s; + char *path, loadpath[256], syspath[256]; + int fw, load, sys, len; + + DEBUG(2, "Firmware request for %s/%s\n", dir, file); + + if (!file || !dir || !dev) { + ERROR("Request for unknown firmware %s/%s\n", dir, file); + exit(-1); + } + + path = malloc(strlen(dir) + strlen(file) + 2); + if (!path) { + ERROR("Failed to allocate memory\n"); + exit(-1); + } + sprintf(path, "%s/%s", dir, file); + + if (stat(path, &s)) { + ERROR("Could not find firmware %s\n", path); + exit(-1); + } + + fw_data = malloc(s.st_size); + if (!fw_data) { + ERROR("Failed to allocate firmware data memory\n"); + exit(-1); + } + + fw = open(path, O_RDONLY); + if (!fw) { + ERROR("Failed to open %s\n", path); + exit(-1); + } + if (read(fw, fw_data, s.st_size) != s.st_size) { + ERROR("Failed to read firmware data\n"); + exit(-1); + } + close(fw); + + snprintf(loadpath, sizeof(loadpath), "/sys/%s/loading", dev); + load = open(loadpath, O_WRONLY); + if (!load) { + ERROR("Failed to open %s\n", loadpath); + exit(-1); + } + write(load, "1", 1); + close(load); + + snprintf(syspath, sizeof(syspath), "/sys/%s/data", dev); + sys = open(syspath, O_WRONLY); + if (!sys) { + ERROR("Failed to open %s\n", syspath); + exit(-1); + } + + len = s.st_size; + while (len > 4096) { + write(fw, fw_data, 4096); + len -= 4096; + } + if (len) + write(fw, fw_data, len); + close(fw); + + load = open(loadpath, O_WRONLY); + write(load, "0", 1); + close(load); + + DEBUG(2, "Done loading %s\n", path); + + exit(-1); +} + +static struct cmd_handler { + char *name; + int atomic; + void (*handler)(struct blob_attr *msg, struct blob_attr *data); +} handlers[] = { + { + .name = "makedev", + .atomic = 1, + .handler = handle_makedev, + }, { + .name = "rm", + .atomic = 1, + .handler = handle_rm, + }, { + .name = "exec", + .handler = handle_exec, + }, { + .name = "load-firmware", + .handler = handle_firmware, + }, +}; + +static void queue_next(void) +{ + struct cmd_queue *c; + + if (queue_proc.pending || list_empty(&cmd_queue)) + return; + + c = list_first_entry(&cmd_queue, struct cmd_queue, list); + + queue_proc.pid = fork(); + if (!queue_proc.pid) { + uloop_done(); + c->handler(c->msg, c->data); + exit(0); + } + + list_del(&c->list); + free(c); + + if (queue_proc.pid <= 0) { + queue_next(); + return; + } + + uloop_process_add(&queue_proc); + + DEBUG(4, "Launched hotplug exec instance, pid=%d\n", (int) queue_proc.pid); +} + +static void queue_proc_cb(struct uloop_process *c, int ret) +{ + DEBUG(4, "Finished hotplug exec instance, pid=%d\n", (int) c->pid); + + queue_next(); +} + +static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data) +{ + struct cmd_queue *c = NULL; + struct blob_attr *_msg, *_data; + + c = calloc_a(sizeof(struct cmd_queue), + &_msg, blob_pad_len(msg), + &_data, blob_pad_len(data), + NULL); + + c->msg = _msg; + c->data = _data; + + if (!c) + return; + + memcpy(c->msg, msg, blob_pad_len(msg)); + memcpy(c->data, data, blob_pad_len(data)); + c->handler = h->handler; + list_add_tail(&c->list, &cmd_queue); + queue_next(); +} + +static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) +{ + const char *str, *sep; + + if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) { + str = json_script_find_var(ctx, vars, "DEVPATH"); + if (!str) + return NULL; + + sep = strrchr(str, '/'); + if (sep) + return sep + 1; + + return str; + } + + return NULL; +} + +static struct json_script_file * +rule_handle_file(struct json_script_ctx *ctx, const char *name) +{ + json_object *obj; + + obj = json_object_from_file((char*)name); + if (is_error(obj)) + return NULL; + + blob_buf_init(&script, 0); + blobmsg_add_json_element(&script, "", obj); + + return json_script_file_from_blobmsg(name, blob_data(script.head), blob_len(script.head)); +} + +static void rule_handle_command(struct json_script_ctx *ctx, const char *name, + struct blob_attr *data, struct blob_attr *vars) +{ + struct blob_attr *cur; + int rem, i; + + if (debug > 3) { + DEBUG(4, "Command: %s", name); + blobmsg_for_each_attr(cur, data, rem) + DEBUG(4, " %s", (char *) blobmsg_data(cur)); + DEBUG(4, "\n"); + + DEBUG(4, "Message:"); + blobmsg_for_each_attr(cur, vars, rem) + DEBUG(4, " %s=%s", blobmsg_name(cur), (char *) blobmsg_data(cur)); + DEBUG(4, "\n"); + } + + for (i = 0; i < ARRAY_SIZE(handlers); i++) + if (!strcmp(handlers[i].name, name)) { + if (handlers[i].atomic) + handlers[i].handler(vars, data); + else + queue_add(&handlers[i], vars, data); + break; + } + + if (last_event.cb) + uloop_timeout_set(&last_event, HOTPLUG_WAIT); +} + +static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, + struct blob_attr *context) +{ + char *s; + + s = blobmsg_format_json(context, false); + ERROR("ERROR: %s in block: %s\n", msg, s); + free(s); +} + +static struct json_script_ctx jctx = { + .handle_var = rule_handle_var, + .handle_error = rule_handle_error, + .handle_command = rule_handle_command, + .handle_file = rule_handle_file, +}; + +static void hotplug_handler(struct uloop_fd *u, unsigned int ev) +{ + int i = 0; + static char buf[4096]; + int len = recv(u->fd, buf, sizeof(buf), MSG_DONTWAIT); + void *index; + if (len < 1) + return; + + blob_buf_init(&b, 0); + index = blobmsg_open_table(&b, NULL); + while (i < len) { + int l = strlen(buf + i) + 1; + char *e = strstr(&buf[i], "="); + + if (e) { + *e = '\0'; + blobmsg_add_string(&b, &buf[i], &e[1]); + } + i += l; + } + blobmsg_close_table(&b, index); + DEBUG(3, "%s\n", blobmsg_format_json(b.head, true)); + json_script_run(&jctx, rule_file, blob_data(b.head)); +} + +static struct uloop_fd hotplug_fd = { + .cb = hotplug_handler, +}; + +void hotplug_last_event(uloop_timeout_handler handler) +{ + last_event.cb = handler; + if (handler) + uloop_timeout_set(&last_event, HOTPLUG_WAIT); + else + uloop_timeout_cancel(&last_event); +} + +void hotplug(char *rules) +{ + struct sockaddr_nl nls; + + rule_file = strdup(rules); + memset(&nls,0,sizeof(struct sockaddr_nl)); + nls.nl_family = AF_NETLINK; + nls.nl_pid = getpid(); + nls.nl_groups = -1; + + if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) { + ERROR("Failed to open hotplug socket: %s\n", strerror(errno)); + exit(1); + } + if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) { + ERROR("Failed to bind hotplug socket: %s\n", strerror(errno)); + exit(1); + } + + json_script_init(&jctx); + queue_proc.cb = queue_proc_cb; + uloop_fd_add(&hotplug_fd, ULOOP_READ); +} + +int hotplug_run(char *rules) +{ + uloop_init(); + hotplug(rules); + uloop_run(); + + return 0; +} + +void hotplug_shutdown(void) +{ + uloop_fd_delete(&hotplug_fd); + close(hotplug_fd.fd); +} diff --git a/plug/hotplug.h b/plug/hotplug.h new file mode 100644 index 0000000..2a44442 --- /dev/null +++ b/plug/hotplug.h @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#ifndef __PROCD_HOTPLUG_H +#define __PROCD_HOTPLUG_H + +#include + +void hotplug(char *rules); +int hotplug_run(char *rules); +void hotplug_shutdown(void); +void hotplug_last_event(uloop_timeout_handler handler); + +#endif diff --git a/plug/udevtrigger.c b/plug/udevtrigger.c new file mode 100644 index 0000000..5013189 --- /dev/null +++ b/plug/udevtrigger.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2004-2006 Kay Sievers + * Copyright (C) 2006 Hannes Reinecke + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PATH_SIZE 512 + +#ifndef strlcpy +#define strlcpy(d,s,l) (strncpy(d,s,l), (d)[(l)-1] = '\0') +#endif + +#ifndef strlcat +#define strlcat(d,s,l) strncat(d,s,(l)-strlen(d)-1) +#endif + +static int verbose; +static int dry_run; + +static void log_message(int priority, const char *format, ...) +{ + va_list args; + + va_start(args, format); + vsyslog(priority, format, args); + va_end(args); +} + +#undef err +#define err(format, arg...) \ + do { \ + log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \ + } while (0) + +#undef info +#define info(format, arg...) \ + do { \ + log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \ + } while (0) + +#ifdef DEBUG +#undef dbg +#define dbg(format, arg...) \ + do { \ + log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \ + } while (0) +#else +#define dbg(...) do {} while(0) +#endif + + +static void trigger_uevent(const char *devpath) +{ + char filename[PATH_SIZE]; + int fd; + + strlcpy(filename, "/sys", sizeof(filename)); + strlcat(filename, devpath, sizeof(filename)); + strlcat(filename, "/uevent", sizeof(filename)); + + if (verbose) + printf("%s\n", devpath); + + if (dry_run) + return; + + fd = open(filename, O_WRONLY); + if (fd < 0) { + dbg("error on opening %s: %s\n", filename, strerror(errno)); + return; + } + + if (write(fd, "add", 3) < 0) + info("error on triggering %s: %s\n", filename, strerror(errno)); + + close(fd); +} + +static int sysfs_resolve_link(char *devpath, size_t size) +{ + char link_path[PATH_SIZE]; + char link_target[PATH_SIZE]; + int len; + int i; + int back; + + strlcpy(link_path, "/sys", sizeof(link_path)); + strlcat(link_path, devpath, sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len <= 0) + return -1; + link_target[len] = '\0'; + dbg("path link '%s' points to '%s'", devpath, link_target); + + for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) + ; + dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); + for (i = 0; i <= back; i++) { + char *pos = strrchr(devpath, '/'); + + if (pos == NULL) + return -1; + pos[0] = '\0'; + } + dbg("after moving back '%s'", devpath); + strlcat(devpath, "/", size); + strlcat(devpath, &link_target[back * 3], size); + return 0; +} + +static bool device_has_attribute(const char *path, const char *attr, + mode_t mode) +{ + char filename[PATH_SIZE]; + struct stat statbuf; + + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, attr, sizeof(filename)); + + if (stat(filename, &statbuf) < 0) + return false; + + if (!(statbuf.st_mode & mode)) + return false; + + return true; +} + +static int device_list_insert(const char *path) +{ + char devpath[PATH_SIZE]; + struct stat statbuf; + + dbg("add '%s'" , path); + + /* we only have a device, if we have a dev and an uevent file */ + if (!device_has_attribute(path, "/dev", S_IRUSR) || + !device_has_attribute(path, "/uevent", S_IWUSR)) + return -1; + + strlcpy(devpath, &path[4], sizeof(devpath)); + + /* resolve possible link to real target */ + if (lstat(path, &statbuf) < 0) + return -1; + if (S_ISLNK(statbuf.st_mode)) + if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) + return -1; + + trigger_uevent(devpath); + return 0; +} + +static void scan_subdir(const char *base, const char *subdir, + bool insert, int depth) +{ + DIR *dir; + struct dirent *dent; + + dir = opendir(base); + if (dir == NULL) + return; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char dirname[PATH_SIZE]; + + if (dent->d_name[0] == '.') + continue; + + strlcpy(dirname, base, sizeof(dirname)); + strlcat(dirname, "/", sizeof(dirname)); + strlcat(dirname, dent->d_name, sizeof(dirname)); + + if (insert) { + int err; + + err = device_list_insert(dirname); + if (err) + continue; + } + + if (subdir) + strlcat(dirname, subdir, sizeof(base)); + + if (depth) + scan_subdir(dirname, NULL, true, depth - 1); + } + + closedir(dir); +} + +int main(int argc, char *argv[], char *envp[]) +{ + struct stat statbuf; + int option; + + openlog("udevtrigger", LOG_PID | LOG_CONS, LOG_DAEMON); + + while (1) { + option = getopt(argc, argv, "vnh"); + if (option == -1) + break; + + switch (option) { + case 'v': + verbose = 1; + break; + case 'n': + dry_run = 1; + break; + case 'h': + printf("Usage: udevtrigger OPTIONS\n" + " -v print the list of devices while running\n" + " -n do not actually trigger the events\n" + " -h print this text\n" + "\n"); + goto exit; + default: + goto exit; + } + } + + + /* if we have /sys/subsystem, forget all the old stuff */ + scan_subdir("/sys/bus", "/devices", false, 1); + scan_subdir("/sys/class", NULL, false, 1); + + /* scan "block" if it isn't a "class" */ + if (stat("/sys/class/block", &statbuf) != 0) + scan_subdir("/sys/block", NULL, true, 1); + +exit: + + closelog(); + return 0; +} diff --git a/preinit.c b/preinit.c deleted file mode 100644 index c015ebd..0000000 --- a/preinit.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 "procd.h" -#include "hotplug.h" -#include "watchdog.h" - -static struct uloop_process preinit; - -static void spawn_procd(struct uloop_process *proc, int ret) -{ - char *wdt_fd = watchdog_fd(); - char *argv[] = { "/sbin/procd", NULL }; - struct stat s; - - if (!stat("/tmp/sysupgrade", &s)) - while (true) - sleep(1); - - unsetenv("INITRAMFS"); - unsetenv("PREINIT"); - DEBUG(1, "Exec to real procd now\n"); - if (wdt_fd) - setenv("WDTFD", wdt_fd, 1); - execvp(argv[0], argv); -} - -void procd_preinit(void) -{ - char *argv[] = { "/bin/sh", "/etc/preinit", NULL }; - - LOG("- preinit -\n"); - - setenv("PREINIT", "1", 1); - preinit.cb = spawn_procd; - preinit.pid = fork(); - if (!preinit.pid) { - execvp(argv[0], argv); - ERROR("Failed to start preinit\n"); - exit(-1); - } - - if (preinit.pid <= 0) { - ERROR("Failed to start new preinit instance\n"); - return; - } - - uloop_process_add(&preinit); - DEBUG(2, "Launched preinit instance, pid=%d\n", (int) preinit.pid); -} diff --git a/procd.c b/procd.c new file mode 100644 index 0000000..a72912a --- /dev/null +++ b/procd.c @@ -0,0 +1,68 @@ +/* + * 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 "procd.h" +#include "watchdog.h" +#include "plug/hotplug.h" + +unsigned int debug; + +static int usage(const char *prog) +{ + ERROR("Usage: %s [options]\n" + "Options:\n" + "\t-s \tPath to ubus socket\n" + "\t-h \trun as hotplug daemon\n" + "\td\t\tEnable debug messages\n" + "\n", prog); + return 1; +} + +int main(int argc, char **argv) +{ + int ch; + + while ((ch = getopt(argc, argv, "ds:h:")) != -1) { + switch (ch) { + case 'h': + return hotplug_run(optarg); + case 's': + ubus_socket = optarg; + break; + case 'd': + debug++; + break; + default: + return usage(argv[0]); + } + } + uloop_init(); + procd_signal(); + trigger_init(); + if (getpid() != 1) + procd_connect_ubus(); + else + procd_state_next(); + uloop_run(); + + return 0; +} diff --git a/procd.h b/procd.h index a0c7a99..dff86f8 100644 --- a/procd.h +++ b/procd.h @@ -22,37 +22,17 @@ #include #include -#include "syslog.h" +#include "log.h" #define __init __attribute__((constructor)) -#define DEBUG(level, fmt, ...) do { \ - if (debug >= level) \ - fprintf(stderr, "procd: %s(%d): " fmt, __func__, __LINE__, ## __VA_ARGS__); \ - } while (0) - -#define LOG(fmt, ...) do { \ - log_printf(fmt, ## __VA_ARGS__); \ - fprintf(stderr, "procd: "fmt, ## __VA_ARGS__); \ - } while (0) - -#define ERROR(fmt, ...) do { \ - log_printf(fmt, ## __VA_ARGS__); \ - fprintf(stderr, "procd: "fmt, ## __VA_ARGS__); \ - } while (0) - extern char *ubus_socket; extern int upgrade_running; -extern unsigned int debug; -void debug_init(void); - void procd_connect_ubus(void); void procd_reconnect_ubus(int reconnect); void ubus_init_service(struct ubus_context *ctx); -void ubus_init_log(struct ubus_context *ctx); void ubus_init_system(struct ubus_context *ctx); -void ubus_notify_log(struct log_head *l); void procd_state_next(void); void procd_shutdown(int event); @@ -64,21 +44,10 @@ void procd_signal_preinit(void); void procd_inittab(void); void procd_inittab_run(const char *action); -int mkdev(const char *progname, int progmode); - struct trigger; void trigger_init(void); void trigger_event(char *type, struct blob_attr *data); void trigger_add(struct blob_attr *rule, void *id); void trigger_del(void *id); -struct pid_info { - char stat; - uint32_t ppid; - uint32_t fdcount; - uint32_t vmsize; - uint16_t uid; -}; -int measure_process(pid_t pid, struct pid_info *pi); - #endif diff --git a/rcS.c b/rcS.c index f041f09..ae8f08e 100644 --- a/rcS.c +++ b/rcS.c @@ -55,7 +55,7 @@ static void pipe_cb(struct ustream *s, int bytes) break; *newline = 0; len = newline + 1 - str; - log_printf(buf->data); + syslog(0, buf->data); ustream_consume(s, len); } while (1); } @@ -66,7 +66,7 @@ static void q_initd_run(struct runqueue *q, struct runqueue_task *t) int pipefd[2]; pid_t pid; - DEBUG(1, "start %s %s \n", s->file, s->param); + DEBUG(2, "start %s %s \n", s->file, s->param); if (pipe(pipefd) == -1) { ERROR("Failed to create pipe\n"); return; @@ -96,7 +96,7 @@ static void q_initd_complete(struct runqueue *q, struct runqueue_task *p) { struct initd *s = container_of(p, struct initd, proc.task); - DEBUG(1, "stop %s %s \n", s->file, s->param); + DEBUG(2, "stop %s %s \n", s->file, s->param); ustream_free(&s->fd.stream); close(s->fd.fd.fd); free(s); @@ -126,10 +126,10 @@ static int _rc(struct runqueue *q, char *path, const char *file, char *pattern, int j; - DEBUG(1, "running %s/%s%s %s\n", path, file, pattern, param); + DEBUG(2, "running %s/%s%s %s\n", path, file, pattern, param); snprintf(dir, sizeof(dir), "%s/%s%s", path, file, pattern); if (glob(dir, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) { - printf("glob failed on %s\n", dir); + DEBUG(2, "glob failed on %s\n", dir); return -1; } diff --git a/service.c b/service.c deleted file mode 100644 index c5f5bf3..0000000 --- a/service.c +++ /dev/null @@ -1,439 +0,0 @@ -/* - * 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 "procd.h" -#include "service.h" -#include "instance.h" -#include "rcS.h" - -struct avl_tree services; -static struct blob_buf b; - -static void -service_instance_add(struct service *s, struct blob_attr *attr) -{ - struct service_instance *in; - - if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE) - return; - - in = calloc(1, sizeof(*in)); - if (!in) - return; - - instance_init(in, s, attr); - vlist_add(&s->instances, &in->node, (void *) in->name); -} - -static void -service_instance_update(struct vlist_tree *tree, struct vlist_node *node_new, - struct vlist_node *node_old) -{ - struct service_instance *in_o = NULL, *in_n = NULL; - - if (node_old) - in_o = container_of(node_old, struct service_instance, node); - - if (node_new) - in_n = container_of(node_new, struct service_instance, node); - - if (in_o && in_n) { - DEBUG(1, "Update instance %s::%s\n", in_o->srv->name, in_o->name); - instance_update(in_o, in_n); - instance_free(in_n); - } else if (in_o) { - DEBUG(1, "Free instance %s::%s\n", in_o->srv->name, in_o->name); - instance_stop(in_o); - instance_free(in_o); - } else if (in_n) { - DEBUG(1, "Create instance %s::%s\n", in_n->srv->name, in_n->name); - instance_start(in_n); - } -} - -static struct service * -service_alloc(const char *name) -{ - struct service *s; - char *new_name; - - s = calloc_a(sizeof(*s), &new_name, strlen(name) + 1); - strcpy(new_name, name); - - vlist_init(&s->instances, avl_strcmp, service_instance_update); - s->instances.keep_old = true; - s->name = new_name; - s->avl.key = s->name; - INIT_LIST_HEAD(&s->validators); - - return s; -} - -enum { - SERVICE_SET_NAME, - SERVICE_SET_SCRIPT, - SERVICE_SET_INSTANCES, - SERVICE_SET_TRIGGER, - SERVICE_SET_VALIDATE, - __SERVICE_SET_MAX -}; - -static const struct blobmsg_policy service_set_attrs[__SERVICE_SET_MAX] = { - [SERVICE_SET_NAME] = { "name", BLOBMSG_TYPE_STRING }, - [SERVICE_SET_SCRIPT] = { "script", BLOBMSG_TYPE_STRING }, - [SERVICE_SET_INSTANCES] = { "instances", BLOBMSG_TYPE_TABLE }, - [SERVICE_SET_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY }, - [SERVICE_SET_VALIDATE] = { "validate", BLOBMSG_TYPE_ARRAY }, -}; - -static int -service_update(struct service *s, struct blob_attr *config, struct blob_attr **tb, bool add) -{ - struct blob_attr *cur; - int rem; - - if (s->trigger) { - trigger_del(s); - free(s->trigger); - s->trigger = NULL; - } - - service_validate_del(s); - - if (tb[SERVICE_SET_TRIGGER] && blobmsg_data_len(tb[SERVICE_SET_TRIGGER])) { - s->trigger = malloc(blob_pad_len(tb[SERVICE_SET_TRIGGER])); - if (!s->trigger) - return -1; - memcpy(s->trigger, tb[SERVICE_SET_TRIGGER], blob_pad_len(tb[SERVICE_SET_TRIGGER])); - trigger_add(s->trigger, s); - } - - if (tb[SERVICE_SET_VALIDATE] && blobmsg_data_len(tb[SERVICE_SET_VALIDATE])) { - blobmsg_for_each_attr(cur, tb[SERVICE_SET_VALIDATE], rem) - service_validate_add(s, cur); - } - - if (tb[SERVICE_SET_INSTANCES]) { - if (!add) - vlist_update(&s->instances); - blobmsg_for_each_attr(cur, tb[SERVICE_SET_INSTANCES], rem) { - service_instance_add(s, cur); - } - if (!add) - vlist_flush(&s->instances); - } - - rc(s->name, "running"); - - return 0; -} - -static void -service_delete(struct service *s) -{ - vlist_flush_all(&s->instances); - avl_delete(&services, &s->avl); - trigger_del(s); - s->trigger = NULL; - free(s->trigger); - free(s); - service_validate_del(s); -} - -enum { - SERVICE_ATTR_NAME, - __SERVICE_ATTR_MAX, -}; - -static const struct blobmsg_policy service_attrs[__SERVICE_ATTR_MAX] = { - [SERVICE_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING }, -}; - -enum { - SERVICE_DEL_ATTR_NAME, - SERVICE_DEL_ATTR_INSTANCE, - __SERVICE_DEL_ATTR_MAX, -}; - -static const struct blobmsg_policy service_del_attrs[__SERVICE_DEL_ATTR_MAX] = { - [SERVICE_DEL_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING }, - [SERVICE_DEL_ATTR_INSTANCE] = { "instance", BLOBMSG_TYPE_STRING }, -}; - -enum { - SERVICE_LIST_ATTR_VERBOSE, - __SERVICE_LIST_ATTR_MAX, -}; - -static const struct blobmsg_policy service_list_attrs[__SERVICE_LIST_ATTR_MAX] = { - [SERVICE_LIST_ATTR_VERBOSE] = { "verbose", BLOBMSG_TYPE_BOOL }, -}; - -enum { - EVENT_TYPE, - EVENT_DATA, - __EVENT_MAX -}; - -static const struct blobmsg_policy event_policy[__EVENT_MAX] = { - [EVENT_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING }, - [EVENT_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, -}; - -enum { - VALIDATE_PACKAGE, - VALIDATE_TYPE, - VALIDATE_SERVICE, - __VALIDATE_MAX -}; - -static const struct blobmsg_policy validate_policy[__VALIDATE_MAX] = { - [VALIDATE_PACKAGE] = { .name = "package", .type = BLOBMSG_TYPE_STRING }, - [VALIDATE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING }, - [VALIDATE_SERVICE] = { .name = "service", .type = BLOBMSG_TYPE_STRING }, -}; - -static int -service_handle_set(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__SERVICE_SET_MAX], *cur; - struct service *s = NULL; - const char *name; - int ret = UBUS_STATUS_INVALID_ARGUMENT; - bool add = !strcmp(method, "add"); - - blobmsg_parse(service_set_attrs, __SERVICE_SET_MAX, tb, blob_data(msg), blob_len(msg)); - cur = tb[SERVICE_ATTR_NAME]; - if (!cur) - goto free; - - name = blobmsg_data(cur); - - s = avl_find_element(&services, name, s, avl); - if (s) { - DEBUG(1, "Update service %s\n", name); - return service_update(s, msg, tb, add); - } - - DEBUG(1, "Create service %s\n", name); - s = service_alloc(name); - if (!s) - return UBUS_STATUS_UNKNOWN_ERROR; - - ret = service_update(s, msg, tb, add); - if (ret) - goto free; - - avl_insert(&services, &s->avl); - - return 0; - -free: - free(msg); - return ret; -} - -static void -service_dump(struct service *s, int verbose) -{ - struct service_instance *in; - void *c, *i; - - c = blobmsg_open_table(&b, s->name); - - if (avl_is_empty(&s->instances.avl)) { - blobmsg_close_table(&b, c); - return; - } - - i = blobmsg_open_table(&b, "instances"); - vlist_for_each_element(&s->instances, in, node) - instance_dump(&b, in, verbose); - blobmsg_close_table(&b, i); - if (verbose && s->trigger) - blobmsg_add_blob(&b, s->trigger); - if (verbose && !list_empty(&s->validators)) - service_validate_dump(&b, s); - blobmsg_close_table(&b, c); -} - -static int -service_handle_list(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__SERVICE_LIST_ATTR_MAX]; - struct service *s; - int verbose = 0; - - blobmsg_parse(service_list_attrs, __SERVICE_LIST_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); - - if (tb[SERVICE_LIST_ATTR_VERBOSE] && blobmsg_get_bool(tb[SERVICE_LIST_ATTR_VERBOSE])) - verbose = 1; - - blob_buf_init(&b, 0); - avl_for_each_element(&services, s, avl) - service_dump(s, verbose); - - ubus_send_reply(ctx, req, b.head); - - return 0; -} - -static int -service_handle_delete(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__SERVICE_DEL_ATTR_MAX], *cur; - struct service *s; - struct service_instance *in; - - blobmsg_parse(service_del_attrs, __SERVICE_DEL_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); - - cur = tb[SERVICE_DEL_ATTR_NAME]; - if (!cur) - return UBUS_STATUS_NOT_FOUND; - - s = avl_find_element(&services, blobmsg_data(cur), s, avl); - if (!s) - return UBUS_STATUS_NOT_FOUND; - - cur = tb[SERVICE_DEL_ATTR_INSTANCE]; - if (!cur) { - service_delete(s); - return 0; - } - - in = vlist_find(&s->instances, blobmsg_data(cur), in, node); - if (!in) { - ERROR("instance %s not found\n", (char *) blobmsg_data(cur)); - return UBUS_STATUS_NOT_FOUND; - } - - vlist_delete(&s->instances, &in->node); - - return 0; -} - -static int -service_handle_update(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__SERVICE_ATTR_MAX], *cur; - struct service *s; - - blobmsg_parse(service_attrs, __SERVICE_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); - - cur = tb[SERVICE_ATTR_NAME]; - if (!cur) - return UBUS_STATUS_INVALID_ARGUMENT; - - s = avl_find_element(&services, blobmsg_data(cur), s, avl); - if (!s) - return UBUS_STATUS_NOT_FOUND; - - if (!strcmp(method, "update_start")) - vlist_update(&s->instances); - else - vlist_flush(&s->instances); - - return 0; -} - -static int -service_handle_event(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__EVENT_MAX]; - - if (!msg) - return UBUS_STATUS_INVALID_ARGUMENT; - - blobmsg_parse(event_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg)); - if (!tb[EVENT_TYPE] || !tb[EVENT_DATA]) - return UBUS_STATUS_INVALID_ARGUMENT; - - trigger_event(blobmsg_get_string(tb[EVENT_TYPE]), tb[EVENT_DATA]); - - return 0; -} - -static int -service_handle_validate(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct blob_attr *tb[__VALIDATE_MAX]; - char *p = NULL, *t = NULL; - - if (!msg) - return UBUS_STATUS_INVALID_ARGUMENT; - - blobmsg_parse(validate_policy, __VALIDATE_MAX, tb, blob_data(msg), blob_len(msg)); - if (tb[VALIDATE_SERVICE]) { - return 0; - } - if (tb[VALIDATE_PACKAGE]) - p = blobmsg_get_string(tb[VALIDATE_PACKAGE]); - - if (tb[VALIDATE_TYPE]) - t = blobmsg_get_string(tb[VALIDATE_TYPE]); - - blob_buf_init(&b, 0); - service_validate_dump_all(&b, p, t); - ubus_send_reply(ctx, req, b.head); - - return 0; -} - -static struct ubus_method main_object_methods[] = { - UBUS_METHOD("set", service_handle_set, service_set_attrs), - UBUS_METHOD("add", service_handle_set, service_set_attrs), - UBUS_METHOD("list", service_handle_list, service_attrs), - UBUS_METHOD("delete", service_handle_delete, service_del_attrs), - UBUS_METHOD("update_start", service_handle_update, service_attrs), - UBUS_METHOD("update_complete", service_handle_update, service_attrs), - UBUS_METHOD("event", service_handle_event, event_policy), - UBUS_METHOD("validate", service_handle_validate, validate_policy), -}; - -static struct ubus_object_type main_object_type = - UBUS_OBJECT_TYPE("service", main_object_methods); - -static struct ubus_object main_object = { - .name = "service", - .type = &main_object_type, - .methods = main_object_methods, - .n_methods = ARRAY_SIZE(main_object_methods), -}; - -void ubus_init_service(struct ubus_context *ctx) -{ - ubus_add_object(ctx, &main_object); -} - -void -service_init(void) -{ - avl_init(&services, avl_strcmp, false, NULL); - service_validate_init(); -} - diff --git a/service.h b/service.h deleted file mode 100644 index 6448e5e..0000000 --- a/service.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -#ifndef __PROCD_SERVICE_H -#define __PROCD_SERVICE_H - -#include -#include -#include - -extern struct avl_tree services; - -struct vrule { - struct avl_node avl; - char *option; - char *rule; -}; - -struct validate { - struct avl_node avl; - struct list_head list; - - char *package; - char *type; - - struct avl_tree rules; -}; - -struct service { - struct avl_node avl; - const char *name; - - struct blob_attr *trigger; - struct vlist_tree instances; - struct list_head validators; -}; - -void service_validate_add(struct service *s, struct blob_attr *attr); -void service_validate_dump(struct blob_buf *b, struct service *s); -void service_validate_dump_all(struct blob_buf *b, char *p, char *s); -void service_validate_del(struct service *s); -void service_validate_init(void); -void service_init(void); - -#endif diff --git a/service/instance.c b/service/instance.c new file mode 100644 index 0000000..5ac7d57 --- /dev/null +++ b/service/instance.c @@ -0,0 +1,480 @@ +/* + * 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 "../procd.h" + +#include "service.h" +#include "instance.h" + +#include "../utils/md5.h" + +enum { + INSTANCE_ATTR_COMMAND, + INSTANCE_ATTR_ENV, + INSTANCE_ATTR_DATA, + INSTANCE_ATTR_NETDEV, + INSTANCE_ATTR_FILE, + INSTANCE_ATTR_TRIGGER, + INSTANCE_ATTR_RESPAWN, + INSTANCE_ATTR_NICE, + __INSTANCE_ATTR_MAX +}; + +static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = { + [INSTANCE_ATTR_COMMAND] = { "command", BLOBMSG_TYPE_ARRAY }, + [INSTANCE_ATTR_ENV] = { "env", BLOBMSG_TYPE_TABLE }, + [INSTANCE_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE }, + [INSTANCE_ATTR_NETDEV] = { "netdev", BLOBMSG_TYPE_ARRAY }, + [INSTANCE_ATTR_FILE] = { "file", BLOBMSG_TYPE_ARRAY }, + [INSTANCE_ATTR_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY }, + [INSTANCE_ATTR_RESPAWN] = { "respawn", BLOBMSG_TYPE_ARRAY }, + [INSTANCE_ATTR_NICE] = { "nice", BLOBMSG_TYPE_INT32 }, +}; + +struct instance_netdev { + struct blobmsg_list_node node; + int ifindex; +}; + +struct instance_file { + struct blobmsg_list_node node; + uint32_t md5[4]; +}; + +static void +instance_run(struct service_instance *in) +{ + struct blobmsg_list_node *var; + struct blob_attr *cur; + char **argv; + int argc = 1; /* NULL terminated */ + int rem, fd; + + if (in->nice) + setpriority(PRIO_PROCESS, 0, in->nice); + + blobmsg_for_each_attr(cur, in->command, rem) + argc++; + + blobmsg_list_for_each(&in->env, var) + setenv(blobmsg_name(var->data), blobmsg_data(var->data), 1); + + argv = alloca(sizeof(char *) * argc); + argc = 0; + + blobmsg_for_each_attr(cur, in->command, rem) + argv[argc++] = blobmsg_data(cur); + + argv[argc] = NULL; + fd = open("/dev/null", O_RDWR); + if (fd > -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } + execvp(argv[0], argv); + exit(127); +} + +void +instance_start(struct service_instance *in) +{ + int pid; + + if (in->proc.pending) + return; + + in->restart = false; + in->halt = !in->respawn; + + if (!in->valid) + return; + + pid = fork(); + if (pid < 0) + return; + + if (!pid) { + uloop_done(); + instance_run(in); + return; + } + + DEBUG(2, "Started instance %s::%s\n", in->srv->name, in->name); + in->proc.pid = pid; + clock_gettime(CLOCK_MONOTONIC, &in->start); + uloop_process_add(&in->proc); +} + +static void +instance_timeout(struct uloop_timeout *t) +{ + struct service_instance *in; + + in = container_of(t, struct service_instance, timeout); + + if (!in->halt && (in->restart || in->respawn)) + instance_start(in); +} + +static void +instance_exit(struct uloop_process *p, int ret) +{ + struct service_instance *in; + struct timespec tp; + long runtime; + + in = container_of(p, struct service_instance, proc); + + clock_gettime(CLOCK_MONOTONIC, &tp); + runtime = tp.tv_sec - in->start.tv_sec; + + DEBUG(2, "Instance %s::%s exit with error code %d after %ld seconds\n", in->srv->name, in->name, ret, runtime); + if (upgrade_running) + return; + + uloop_timeout_cancel(&in->timeout); + if (in->halt) { + /* no action */ + } else if (in->restart) { + instance_start(in); + } else if (in->respawn) { + if (runtime < in->respawn_threshold) + in->respawn_count++; + else + in->respawn_count = 0; + if (in->respawn_count > in->respawn_retry) { + LOG("Instance %s::%s s in a crash loop %d crashes, %ld seconds since last crash\n", + in->srv->name, in->name, in->respawn_count, runtime); + in->restart = in->respawn = 0; + in->halt = 1; + } else { + uloop_timeout_set(&in->timeout, in->respawn_timeout * 1000); + } + } +} + +void +instance_stop(struct service_instance *in) +{ + if (!in->proc.pending) + return; + in->halt = true; + in->restart = in->respawn = false; + kill(in->proc.pid, SIGTERM); +} + +static void +instance_restart(struct service_instance *in) +{ + if (!in->proc.pending) + return; + in->halt = false; + in->restart = true; + kill(in->proc.pid, SIGTERM); +} + +static bool +instance_config_changed(struct service_instance *in, struct service_instance *in_new) +{ + if (!in->valid) + return true; + + if (!blob_attr_equal(in->command, in_new->command)) + return true; + + if (!blobmsg_list_equal(&in->env, &in_new->env)) + return true; + + if (!blobmsg_list_equal(&in->data, &in_new->data)) + return true; + + if (!blobmsg_list_equal(&in->netdev, &in_new->netdev)) + return true; + + if (!blobmsg_list_equal(&in->file, &in_new->file)) + return true; + + if (in->nice != in_new->nice) + return true; + + return false; +} + +static bool +instance_netdev_cmp(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2) +{ + struct instance_netdev *n1 = container_of(l1, struct instance_netdev, node); + struct instance_netdev *n2 = container_of(l2, struct instance_netdev, node); + + return n1->ifindex == n2->ifindex; +} + +static void +instance_netdev_update(struct blobmsg_list_node *l) +{ + struct instance_netdev *n = container_of(l, struct instance_netdev, node); + + n->ifindex = if_nametoindex(n->node.avl.key); +} + +static bool +instance_file_cmp(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2) +{ + struct instance_file *f1 = container_of(l1, struct instance_file, node); + struct instance_file *f2 = container_of(l2, struct instance_file, node); + + return !memcmp(f1->md5, f2->md5, sizeof(f1->md5)); +} + +static void +instance_file_update(struct blobmsg_list_node *l) +{ + struct instance_file *f = container_of(l, struct instance_file, node); + md5_ctx_t md5; + char buf[256]; + int len, fd; + + memset(f->md5, 0, sizeof(f->md5)); + + fd = open(l->avl.key, O_RDONLY); + if (fd < 0) + return; + + md5_begin(&md5); + do { + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + if (errno == EINTR) + continue; + + break; + } + if (!len) + break; + + md5_hash(buf, len, &md5); + } while(1); + + md5_end(f->md5, &md5); + close(fd); +} + +static bool +instance_fill_array(struct blobmsg_list *l, struct blob_attr *cur, blobmsg_update_cb cb, bool array) +{ + struct blobmsg_list_node *node; + + if (!cur) + return true; + + if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING)) + return false; + + blobmsg_list_fill(l, blobmsg_data(cur), blobmsg_data_len(cur), array); + if (cb) { + blobmsg_list_for_each(l, node) + cb(node); + } + return true; +} + +static bool +instance_config_parse(struct service_instance *in) +{ + struct blob_attr *tb[__INSTANCE_ATTR_MAX]; + struct blob_attr *cur, *cur2; + int argc = 0; + int rem; + + blobmsg_parse(instance_attr, __INSTANCE_ATTR_MAX, tb, + blobmsg_data(in->config), blobmsg_data_len(in->config)); + + cur = tb[INSTANCE_ATTR_COMMAND]; + if (!cur) + return false; + + if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING)) + return false; + + blobmsg_for_each_attr(cur2, cur, rem) { + argc++; + break; + } + if (!argc) + return false; + + in->command = cur; + + if (tb[INSTANCE_ATTR_RESPAWN]) { + int i = 0; + uint32_t vals[3] = { 3600, 5, 5}; + + blobmsg_for_each_attr(cur2, tb[INSTANCE_ATTR_RESPAWN], rem) { + if ((i >= 3) && (blobmsg_type(cur2) == BLOBMSG_TYPE_STRING)) + continue; + vals[i] = atoi(blobmsg_get_string(cur2)); + i++; + } + in->respawn = true; + in->respawn_count = 0; + in->respawn_threshold = vals[0]; + in->respawn_timeout = vals[1]; + in->respawn_retry = vals[2]; + } + if (tb[INSTANCE_ATTR_TRIGGER]) { + in->trigger = malloc(blob_pad_len(tb[INSTANCE_ATTR_TRIGGER])); + if (!in->trigger) + return -1; + memcpy(in->trigger, tb[INSTANCE_ATTR_TRIGGER], blob_pad_len(tb[INSTANCE_ATTR_TRIGGER])); + trigger_add(in->trigger, in); + } + + if ((cur = tb[INSTANCE_ATTR_NICE])) { + in->nice = (int8_t) blobmsg_get_u32(cur); + if (in->nice < -20 || in->nice > 20) + return false; + } + + if (!instance_fill_array(&in->env, tb[INSTANCE_ATTR_ENV], NULL, false)) + return false; + + if (!instance_fill_array(&in->data, tb[INSTANCE_ATTR_DATA], NULL, false)) + return false; + + if (!instance_fill_array(&in->netdev, tb[INSTANCE_ATTR_NETDEV], instance_netdev_update, true)) + return false; + + if (!instance_fill_array(&in->file, tb[INSTANCE_ATTR_FILE], instance_file_update, true)) + return false; + + return true; +} + +static void +instance_config_cleanup(struct service_instance *in) +{ + blobmsg_list_free(&in->env); + blobmsg_list_free(&in->data); + blobmsg_list_free(&in->netdev); +} + +static void +instance_config_move(struct service_instance *in, struct service_instance *in_src) +{ + instance_config_cleanup(in); + blobmsg_list_move(&in->env, &in_src->env); + blobmsg_list_move(&in->data, &in_src->data); + blobmsg_list_move(&in->netdev, &in_src->netdev); + in->trigger = in_src->trigger; + in->command = in_src->command; + in->name = in_src->name; + in->node.avl.key = in_src->node.avl.key; + + free(in->config); + in->config = in_src->config; + in_src->config = NULL; +} + +bool +instance_update(struct service_instance *in, struct service_instance *in_new) +{ + bool changed = instance_config_changed(in, in_new); + bool running = in->proc.pending; + + if (!changed && running) + return false; + + if (!running) { + if (changed) + instance_config_move(in, in_new); + instance_start(in); + } else { + instance_restart(in); + instance_config_move(in, in_new); + /* restart happens in the child callback handler */ + } + return true; +} + +void +instance_free(struct service_instance *in) +{ + uloop_process_delete(&in->proc); + uloop_timeout_cancel(&in->timeout); + trigger_del(in); + free(in->trigger); + instance_config_cleanup(in); + free(in->config); + free(in); +} + +void +instance_init(struct service_instance *in, struct service *s, struct blob_attr *config) +{ + config = blob_memdup(config); + in->srv = s; + in->name = blobmsg_name(config); + in->config = config; + in->timeout.cb = instance_timeout; + in->proc.cb = instance_exit; + + blobmsg_list_init(&in->netdev, struct instance_netdev, node, instance_netdev_cmp); + blobmsg_list_init(&in->file, struct instance_file, node, instance_file_cmp); + blobmsg_list_simple_init(&in->env); + blobmsg_list_simple_init(&in->data); + in->valid = instance_config_parse(in); +} + +void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose) +{ + void *i; + + i = blobmsg_open_table(b, in->name); + blobmsg_add_u8(b, "running", in->proc.pending); + if (in->proc.pending) + blobmsg_add_u32(b, "pid", in->proc.pid); + blobmsg_add_blob(b, in->command); + + if (!avl_is_empty(&in->env.avl)) { + struct blobmsg_list_node *var; + void *e = blobmsg_open_table(b, "env"); + blobmsg_list_for_each(&in->env, var) + blobmsg_add_string(b, blobmsg_name(var->data), blobmsg_data(var->data)); + blobmsg_close_table(b, e); + } + + if (in->respawn) { + void *r = blobmsg_open_table(b, "respawn"); + blobmsg_add_u32(b, "timeout", in->respawn_timeout); + blobmsg_add_u32(b, "threshold", in->respawn_threshold); + blobmsg_add_u32(b, "retry", in->respawn_retry); + blobmsg_close_table(b, r); + } + + if (verbose && in->trigger) + blobmsg_add_blob(b, in->trigger); + + blobmsg_close_table(b, i); +} diff --git a/service/instance.h b/service/instance.h new file mode 100644 index 0000000..65b670e --- /dev/null +++ b/service/instance.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef __PROCD_INSTANCE_H +#define __PROCD_INSTANCE_H + +#include +#include +#include "../utils/utils.h" + +#define RESPAWN_ERROR (5 * 60) + +struct service_instance { + struct vlist_node node; + struct service *srv; + const char *name; + + int8_t nice; + bool valid; + + bool halt; + bool restart; + bool respawn; + int respawn_count; + struct timespec start; + + uint32_t respawn_timeout; + uint32_t respawn_threshold; + uint32_t respawn_retry; + + struct blob_attr *config; + struct uloop_process proc; + struct uloop_timeout timeout; + + struct blob_attr *command; + struct blob_attr *trigger; + struct blobmsg_list env; + struct blobmsg_list data; + struct blobmsg_list netdev; + struct blobmsg_list file; +}; + +void instance_start(struct service_instance *in); +void instance_stop(struct service_instance *in); +bool instance_update(struct service_instance *in, struct service_instance *in_new); +void instance_init(struct service_instance *in, struct service *s, struct blob_attr *config); +void instance_free(struct service_instance *in); +void instance_dump(struct blob_buf *b, struct service_instance *in, int debug); + +#endif diff --git a/service/service.c b/service/service.c new file mode 100644 index 0000000..aa393b9 --- /dev/null +++ b/service/service.c @@ -0,0 +1,470 @@ +/* + * 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 "../procd.h" + +#include "service.h" +#include "instance.h" + +#include "../rcS.h" + +struct avl_tree services; +static struct blob_buf b; + +static void +service_instance_add(struct service *s, struct blob_attr *attr) +{ + struct service_instance *in; + + if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE) + return; + + in = calloc(1, sizeof(*in)); + if (!in) + return; + + instance_init(in, s, attr); + vlist_add(&s->instances, &in->node, (void *) in->name); +} + +static void +service_instance_update(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old) +{ + struct service_instance *in_o = NULL, *in_n = NULL; + + if (node_old) + in_o = container_of(node_old, struct service_instance, node); + + if (node_new) + in_n = container_of(node_new, struct service_instance, node); + + if (in_o && in_n) { + DEBUG(2, "Update instance %s::%s\n", in_o->srv->name, in_o->name); + instance_update(in_o, in_n); + instance_free(in_n); + } else if (in_o) { + DEBUG(2, "Free instance %s::%s\n", in_o->srv->name, in_o->name); + instance_stop(in_o); + instance_free(in_o); + } else if (in_n) { + DEBUG(2, "Create instance %s::%s\n", in_n->srv->name, in_n->name); + instance_start(in_n); + } +} + +static struct service * +service_alloc(const char *name) +{ + struct service *s; + char *new_name; + + s = calloc_a(sizeof(*s), &new_name, strlen(name) + 1); + strcpy(new_name, name); + + vlist_init(&s->instances, avl_strcmp, service_instance_update); + s->instances.keep_old = true; + s->name = new_name; + s->avl.key = s->name; + INIT_LIST_HEAD(&s->validators); + + return s; +} + +enum { + SERVICE_SET_NAME, + SERVICE_SET_SCRIPT, + SERVICE_SET_INSTANCES, + SERVICE_SET_TRIGGER, + SERVICE_SET_VALIDATE, + __SERVICE_SET_MAX +}; + +static const struct blobmsg_policy service_set_attrs[__SERVICE_SET_MAX] = { + [SERVICE_SET_NAME] = { "name", BLOBMSG_TYPE_STRING }, + [SERVICE_SET_SCRIPT] = { "script", BLOBMSG_TYPE_STRING }, + [SERVICE_SET_INSTANCES] = { "instances", BLOBMSG_TYPE_TABLE }, + [SERVICE_SET_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY }, + [SERVICE_SET_VALIDATE] = { "validate", BLOBMSG_TYPE_ARRAY }, +}; + +static int +service_update(struct service *s, struct blob_attr *config, struct blob_attr **tb, bool add) +{ + struct blob_attr *cur; + int rem; + + if (s->trigger) { + trigger_del(s); + free(s->trigger); + s->trigger = NULL; + } + + service_validate_del(s); + + if (tb[SERVICE_SET_TRIGGER] && blobmsg_data_len(tb[SERVICE_SET_TRIGGER])) { + s->trigger = malloc(blob_pad_len(tb[SERVICE_SET_TRIGGER])); + if (!s->trigger) + return -1; + memcpy(s->trigger, tb[SERVICE_SET_TRIGGER], blob_pad_len(tb[SERVICE_SET_TRIGGER])); + trigger_add(s->trigger, s); + } + + if (tb[SERVICE_SET_VALIDATE] && blobmsg_data_len(tb[SERVICE_SET_VALIDATE])) { + blobmsg_for_each_attr(cur, tb[SERVICE_SET_VALIDATE], rem) + service_validate_add(s, cur); + } + + if (tb[SERVICE_SET_INSTANCES]) { + if (!add) + vlist_update(&s->instances); + blobmsg_for_each_attr(cur, tb[SERVICE_SET_INSTANCES], rem) { + service_instance_add(s, cur); + } + if (!add) + vlist_flush(&s->instances); + } + + rc(s->name, "running"); + + return 0; +} + +static void +service_delete(struct service *s) +{ + vlist_flush_all(&s->instances); + avl_delete(&services, &s->avl); + trigger_del(s); + s->trigger = NULL; + free(s->trigger); + free(s); + service_validate_del(s); +} + +enum { + SERVICE_ATTR_NAME, + __SERVICE_ATTR_MAX, +}; + +static const struct blobmsg_policy service_attrs[__SERVICE_ATTR_MAX] = { + [SERVICE_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING }, +}; + +enum { + SERVICE_DEL_ATTR_NAME, + SERVICE_DEL_ATTR_INSTANCE, + __SERVICE_DEL_ATTR_MAX, +}; + +static const struct blobmsg_policy service_del_attrs[__SERVICE_DEL_ATTR_MAX] = { + [SERVICE_DEL_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING }, + [SERVICE_DEL_ATTR_INSTANCE] = { "instance", BLOBMSG_TYPE_STRING }, +}; + +enum { + SERVICE_LIST_ATTR_VERBOSE, + __SERVICE_LIST_ATTR_MAX, +}; + +static const struct blobmsg_policy service_list_attrs[__SERVICE_LIST_ATTR_MAX] = { + [SERVICE_LIST_ATTR_VERBOSE] = { "verbose", BLOBMSG_TYPE_BOOL }, +}; + +enum { + EVENT_TYPE, + EVENT_DATA, + __EVENT_MAX +}; + +static const struct blobmsg_policy event_policy[__EVENT_MAX] = { + [EVENT_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING }, + [EVENT_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, +}; + +enum { + VALIDATE_PACKAGE, + VALIDATE_TYPE, + VALIDATE_SERVICE, + __VALIDATE_MAX +}; + +static const struct blobmsg_policy validate_policy[__VALIDATE_MAX] = { + [VALIDATE_PACKAGE] = { .name = "package", .type = BLOBMSG_TYPE_STRING }, + [VALIDATE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING }, + [VALIDATE_SERVICE] = { .name = "service", .type = BLOBMSG_TYPE_STRING }, +}; + +static int +service_handle_set(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__SERVICE_SET_MAX], *cur; + struct service *s = NULL; + const char *name; + int ret = UBUS_STATUS_INVALID_ARGUMENT; + bool add = !strcmp(method, "add"); + + blobmsg_parse(service_set_attrs, __SERVICE_SET_MAX, tb, blob_data(msg), blob_len(msg)); + cur = tb[SERVICE_ATTR_NAME]; + if (!cur) + goto free; + + name = blobmsg_data(cur); + + s = avl_find_element(&services, name, s, avl); + if (s) { + DEBUG(2, "Update service %s\n", name); + return service_update(s, msg, tb, add); + } + + DEBUG(2, "Create service %s\n", name); + s = service_alloc(name); + if (!s) + return UBUS_STATUS_UNKNOWN_ERROR; + + ret = service_update(s, msg, tb, add); + if (ret) + goto free; + + avl_insert(&services, &s->avl); + + return 0; + +free: + free(msg); + return ret; +} + +static void +service_dump(struct service *s, int verbose) +{ + struct service_instance *in; + void *c, *i; + + c = blobmsg_open_table(&b, s->name); + + if (avl_is_empty(&s->instances.avl)) { + blobmsg_close_table(&b, c); + return; + } + + i = blobmsg_open_table(&b, "instances"); + vlist_for_each_element(&s->instances, in, node) + instance_dump(&b, in, verbose); + blobmsg_close_table(&b, i); + if (verbose && s->trigger) + blobmsg_add_blob(&b, s->trigger); + if (verbose && !list_empty(&s->validators)) + service_validate_dump(&b, s); + blobmsg_close_table(&b, c); +} + +static int +service_handle_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__SERVICE_LIST_ATTR_MAX]; + struct service *s; + int verbose = 0; + + blobmsg_parse(service_list_attrs, __SERVICE_LIST_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); + + if (tb[SERVICE_LIST_ATTR_VERBOSE] && blobmsg_get_bool(tb[SERVICE_LIST_ATTR_VERBOSE])) + verbose = 1; + + blob_buf_init(&b, 0); + avl_for_each_element(&services, s, avl) + service_dump(s, verbose); + + ubus_send_reply(ctx, req, b.head); + + return 0; +} + +static int +service_handle_delete(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__SERVICE_DEL_ATTR_MAX], *cur; + struct service *s; + struct service_instance *in; + + blobmsg_parse(service_del_attrs, __SERVICE_DEL_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); + + cur = tb[SERVICE_DEL_ATTR_NAME]; + if (!cur) + return UBUS_STATUS_NOT_FOUND; + + s = avl_find_element(&services, blobmsg_data(cur), s, avl); + if (!s) + return UBUS_STATUS_NOT_FOUND; + + cur = tb[SERVICE_DEL_ATTR_INSTANCE]; + if (!cur) { + service_delete(s); + return 0; + } + + in = vlist_find(&s->instances, blobmsg_data(cur), in, node); + if (!in) { + ERROR("instance %s not found\n", (char *) blobmsg_data(cur)); + return UBUS_STATUS_NOT_FOUND; + } + + vlist_delete(&s->instances, &in->node); + + return 0; +} + +static int +service_handle_update(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__SERVICE_ATTR_MAX], *cur; + struct service *s; + + blobmsg_parse(service_attrs, __SERVICE_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); + + cur = tb[SERVICE_ATTR_NAME]; + if (!cur) + return UBUS_STATUS_INVALID_ARGUMENT; + + s = avl_find_element(&services, blobmsg_data(cur), s, avl); + if (!s) + return UBUS_STATUS_NOT_FOUND; + + if (!strcmp(method, "update_start")) + vlist_update(&s->instances); + else + vlist_flush(&s->instances); + + return 0; +} + +static int +service_handle_event(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__EVENT_MAX]; + + if (!msg) + return UBUS_STATUS_INVALID_ARGUMENT; + + blobmsg_parse(event_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg)); + if (!tb[EVENT_TYPE] || !tb[EVENT_DATA]) + return UBUS_STATUS_INVALID_ARGUMENT; + + trigger_event(blobmsg_get_string(tb[EVENT_TYPE]), tb[EVENT_DATA]); + + return 0; +} + +static int +service_handle_validate(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__VALIDATE_MAX]; + char *p = NULL, *t = NULL; + + if (!msg) + return UBUS_STATUS_INVALID_ARGUMENT; + + blobmsg_parse(validate_policy, __VALIDATE_MAX, tb, blob_data(msg), blob_len(msg)); + if (tb[VALIDATE_SERVICE]) { + return 0; + } + if (tb[VALIDATE_PACKAGE]) + p = blobmsg_get_string(tb[VALIDATE_PACKAGE]); + + if (tb[VALIDATE_TYPE]) + t = blobmsg_get_string(tb[VALIDATE_TYPE]); + + blob_buf_init(&b, 0); + service_validate_dump_all(&b, p, t); + ubus_send_reply(ctx, req, b.head); + + return 0; +} + +static struct ubus_method main_object_methods[] = { + UBUS_METHOD("set", service_handle_set, service_set_attrs), + UBUS_METHOD("add", service_handle_set, service_set_attrs), + UBUS_METHOD("list", service_handle_list, service_attrs), + UBUS_METHOD("delete", service_handle_delete, service_del_attrs), + UBUS_METHOD("update_start", service_handle_update, service_attrs), + UBUS_METHOD("update_complete", service_handle_update, service_attrs), + UBUS_METHOD("event", service_handle_event, event_policy), + UBUS_METHOD("validate", service_handle_validate, validate_policy), +}; + +static struct ubus_object_type main_object_type = + UBUS_OBJECT_TYPE("service", main_object_methods); + +static struct ubus_object main_object = { + .name = "service", + .type = &main_object_type, + .methods = main_object_methods, + .n_methods = ARRAY_SIZE(main_object_methods), +}; + +int +service_start_early(char *name, char *cmdline) +{ + void *instances, *instance, *command, *respawn; + char *t; + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "name", name); + instances = blobmsg_open_table(&b, "instances"); + instance = blobmsg_open_table(&b, "instance1"); + command = blobmsg_open_array(&b, "command"); + t = strtok(cmdline, " "); + while (t) { + blobmsg_add_string(&b, NULL, t); + t = strtok(NULL, " "); + } + blobmsg_close_array(&b, command); + respawn = blobmsg_open_array(&b, "respawn"); + blobmsg_add_string(&b, NULL, "1"); + blobmsg_add_string(&b, NULL, "3600"); + blobmsg_add_string(&b, NULL, "10"); + blobmsg_close_array(&b, respawn); + blobmsg_close_table(&b, instance); + blobmsg_close_table(&b, instances); + + return service_handle_set(NULL, NULL, NULL, "add", b.head); +} + +void ubus_init_service(struct ubus_context *ctx) +{ + ubus_add_object(ctx, &main_object); +} + +void +service_init(void) +{ + avl_init(&services, avl_strcmp, false, NULL); + service_validate_init(); +} + diff --git a/service/service.h b/service/service.h new file mode 100644 index 0000000..46ba746 --- /dev/null +++ b/service/service.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __PROCD_SERVICE_H +#define __PROCD_SERVICE_H + +#include +#include +#include + +extern struct avl_tree services; + +struct vrule { + struct avl_node avl; + char *option; + char *rule; +}; + +struct validate { + struct avl_node avl; + struct list_head list; + + char *package; + char *type; + + struct avl_tree rules; +}; + +struct service { + struct avl_node avl; + const char *name; + + struct blob_attr *trigger; + struct vlist_tree instances; + struct list_head validators; +}; + +void service_validate_add(struct service *s, struct blob_attr *attr); +void service_validate_dump(struct blob_buf *b, struct service *s); +void service_validate_dump_all(struct blob_buf *b, char *p, char *s); +int service_start_early(char *name, char *cmdline); +void service_validate_del(struct service *s); +void service_validate_init(void); +void service_init(void); + +#endif diff --git a/service/trigger.c b/service/trigger.c new file mode 100644 index 0000000..41fb55d --- /dev/null +++ b/service/trigger.c @@ -0,0 +1,334 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +#include "../procd.h" + +struct trigger { + struct list_head list; + + char *type; + + int pending; + int remove; + int timeout; + + void *id; + + struct blob_attr *rule; + struct blob_attr *data; + struct uloop_timeout delay; + + struct json_script_ctx jctx; +}; + +struct job; +struct cmd { + char *name; + void (*handler)(struct job *job, struct blob_attr *exec, struct blob_attr *env); +}; + +struct job { + struct runqueue_process proc; + struct cmd *cmd; + struct trigger *trigger; + struct blob_attr *exec; + struct blob_attr *env; +}; + +static LIST_HEAD(triggers); +static struct runqueue q; + +static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) +{ + return NULL; +} + +static struct json_script_file * +rule_load_script(struct json_script_ctx *ctx, const char *name) +{ + struct trigger *t = container_of(ctx, struct trigger, jctx); + + return json_script_file_from_blobmsg(t->type, t->rule, blob_pad_len(t->rule)); +} + +static void q_job_run(struct runqueue *q, struct runqueue_task *t) +{ + struct job *j = container_of(t, struct job, proc.task); + + DEBUG(4, "handle event %s\n", j->cmd->name); + j->cmd->handler(j, j->exec, j->env); +} + +static void q_job_complete(struct runqueue *q, struct runqueue_task *p) +{ + struct job *j = container_of(p, struct job, proc.task); + + if (j->trigger->remove) { + list_del(&j->trigger->list); + free(j->trigger); + } else { + j->trigger->pending = 0; + } + free(j); +} + +static void add_job(struct trigger *t, struct cmd *cmd, struct blob_attr *exec, struct blob_attr *data) +{ + static const struct runqueue_task_type job_type = { + .run = q_job_run, + .cancel = runqueue_process_cancel_cb, + .kill = runqueue_process_kill_cb, + }; + struct blob_attr *d, *e; + struct job *j = calloc_a(sizeof(*j), &e, blob_pad_len(exec), &d, blob_pad_len(data)); + + j->env = d; + j->exec = e; + j->cmd = cmd; + j->trigger = t; + j->proc.task.type = &job_type; + j->proc.task.complete = q_job_complete; + t->pending = 1; + + memcpy(j->exec, exec, blob_pad_len(exec)); + memcpy(j->env, data, blob_pad_len(data)); + + runqueue_task_add(&q, &j->proc.task, false); +} + +static void _setenv(const char *key, const char *val) +{ + char _key[32]; + + snprintf(_key, sizeof(_key), "PARAM_%s", key); + setenv(_key, val, 1); +} + +static void handle_run_script(struct job *j, struct blob_attr *exec, struct blob_attr *env) +{ + char *argv[8]; + struct blob_attr *cur; + int rem; + int i = 0; + pid_t pid; + + pid = fork(); + if (pid < 0) + return; + + if (pid) { + runqueue_process_add(&q, &j->proc, pid); + return; + } + + if (debug < 3) { + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + _setenv("type", j->trigger->type); + blobmsg_for_each_attr(cur, j->env, rem) + _setenv(blobmsg_name(cur), blobmsg_data(cur)); + + blobmsg_for_each_attr(cur, j->exec, rem) { + argv[i] = blobmsg_data(cur); + i++; + if (i == 7) + break; + } + + if (i > 0) { + argv[i] = NULL; + execvp(argv[0], &argv[0]); + } + + exit(1); +} + +static struct cmd handlers[] = { + { + .name = "run_script", + .handler = handle_run_script, + }, +}; + +static void rule_handle_command(struct json_script_ctx *ctx, const char *name, + struct blob_attr *exec, struct blob_attr *vars) +{ + struct trigger *t = container_of(ctx, struct trigger, jctx); + int i; + + if (t->pending) + return; + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (!strcmp(handlers[i].name, name)) { + add_job(t, &handlers[i], exec, vars); + break; + } + } +} + +static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, + struct blob_attr *context) +{ + char *s; + + s = blobmsg_format_json(context, false); + ERROR("ERROR: %s in block: %s\n", msg, s); + free(s); +} + +static void q_empty(struct runqueue *q) +{ +} + +static void trigger_delay_cb(struct uloop_timeout *tout) +{ + struct trigger *t = container_of(tout, struct trigger, delay); + + json_script_run(&t->jctx, "foo", t->data); + free(t->data); +} + +static struct trigger* _trigger_add(char *type, struct blob_attr *rule, int timeout, void *id) +{ + char *_t; + struct blob_attr *_r; + struct trigger *t = calloc_a(sizeof(*t), &_t, strlen(type) + 1, &_r, blob_pad_len(rule)); + + t->type = _t; + t->rule = _r; + t->delay.cb = trigger_delay_cb; + t->timeout = timeout; + t->pending = 0; + t->remove = 0; + t->id = id; + t->jctx.handle_var = rule_handle_var, + t->jctx.handle_error = rule_handle_error, + t->jctx.handle_command = rule_handle_command, + t->jctx.handle_file = rule_load_script, + + strcpy(t->type, type); + memcpy(t->rule, rule, blob_pad_len(rule)); + + list_add(&t->list, &triggers); + json_script_init(&t->jctx); + + return t; +} + +void trigger_add(struct blob_attr *rule, void *id) +{ + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, rule, rem) { + struct blob_attr *_cur, *type = NULL, *script = NULL, *timeout = NULL; + int _rem; + int i = 0; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY) + continue; + + blobmsg_for_each_attr(_cur, cur, _rem) { + switch (i++) { + case 0: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) + type = _cur; + break; + + case 1: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY) + script = _cur; + break; + + case 2: + if (blobmsg_type(_cur) == BLOBMSG_TYPE_INT32) + timeout = _cur; + break; + } + } + + if (type && script) { + int t = 0; + + if (timeout) + t = blobmsg_get_u32(timeout); + _trigger_add(blobmsg_get_string(type), script, t, id); + } + } +} + +void trigger_del(void *id) +{ + struct trigger *t, *n; + + list_for_each_entry_safe(t, n, &triggers, list) { + if (t->id != id) + continue; + + if (t->pending) { + t->remove = 1; + continue; + } + list_del(&t->list); + free(t); + } +} + +void trigger_init(void) +{ + runqueue_init(&q); + q.empty_cb = q_empty; + q.max_running_tasks = 1; +} + +void trigger_event(char *type, struct blob_attr *data) +{ + struct trigger *t; + + list_for_each_entry(t, &triggers, list) { + if (t->pending || t->remove) + continue; + if (!strcmp(t->type, type)) { + if (t->timeout) { + t->data = malloc(blob_pad_len(data)); + memcpy(t->data, data, blob_pad_len(data)); + uloop_timeout_set(&t->delay, t->timeout); + } else { + json_script_run(&t->jctx, "foo", data); + } + } + } +} diff --git a/service/validate.c b/service/validate.c new file mode 100644 index 0000000..ca9bb39 --- /dev/null +++ b/service/validate.c @@ -0,0 +1,162 @@ +/* + * 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 "../procd.h" + +#include "service.h" + +enum { + SERVICE_VAL_PACKAGE, + SERVICE_VAL_TYPE, + SERVICE_VAL_DATA, + __SERVICE_VAL_MAX +}; + +static const struct blobmsg_policy service_validate_attrs[__SERVICE_VAL_MAX] = { + [SERVICE_VAL_PACKAGE] = { "package", BLOBMSG_TYPE_STRING }, + [SERVICE_VAL_TYPE] = { "type", BLOBMSG_TYPE_STRING }, + [SERVICE_VAL_DATA] = { "data", BLOBMSG_TYPE_TABLE }, +}; + +static struct avl_tree validators; + +void +service_validate_dump_all(struct blob_buf *b, char *p, char *s) +{ + struct json_object *r = json_object_new_object(); + struct validate *v; + + if (!r) + return; + + avl_for_each_element(&validators, v, avl) { + struct json_object *o, *t; + struct vrule *vr; + + if (p && strcmp(p, v->package)) + continue; + + if (s && strcmp(s, v->type)) + continue; + + o = json_object_object_get(r, v->package); + if (!o) { + o = json_object_new_object(); + json_object_object_add(r, v->package, o); + } + t = json_object_object_get(o, v->type); + if (!t) { + t = json_object_new_object(); + json_object_object_add(o, v->type, t); + } + avl_for_each_element(&v->rules, vr, avl) + json_object_object_add(t, vr->option, json_object_new_string(vr->rule)); + } + blobmsg_add_object(b, r); +} + +void +service_validate_dump(struct blob_buf *b, struct service *s) +{ + struct validate *v; + void *i = blobmsg_open_array(b, "validate"); + + list_for_each_entry(v, &s->validators, list) { + struct vrule *vr; + void *k, *j = blobmsg_open_table(b, "validate"); + + blobmsg_add_string(b, "package", v->package); + blobmsg_add_string(b, "type", v->type); + k = blobmsg_open_table(b, "rules"); + avl_for_each_element(&v->rules, vr, avl) + blobmsg_add_string(b, vr->option, vr->rule); + blobmsg_close_table(b, k); + blobmsg_close_table(b, j); + } + blobmsg_close_array(b, i); +} + +void +service_validate_del(struct service *s) +{ + struct validate *v, *n; + + if (list_empty(&s->validators)) + return; + + list_for_each_entry_safe(v, n, &s->validators, list) { + struct vrule *vr, *a; + + avl_for_each_element_safe(&v->rules, vr, avl, a) { + avl_delete(&v->rules, &vr->avl); + free(vr); + } + avl_delete(&validators, &v->avl); + list_del(&v->list); + free(v); + } +} + +void +service_validate_add(struct service *s, struct blob_attr *msg) +{ + struct blob_attr *tb[__SERVICE_VAL_MAX]; + struct validate *v; + char *type, *package; + struct blob_attr *cur; + int rem; + + blobmsg_parse(service_validate_attrs, __SERVICE_VAL_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg)); + if (!tb[SERVICE_VAL_PACKAGE] || !tb[SERVICE_VAL_TYPE] || !tb[SERVICE_VAL_DATA]) + return; + + v = calloc_a(sizeof(*v), &package, blobmsg_data_len(tb[SERVICE_VAL_PACKAGE]) + 1, + &type, blobmsg_data_len(tb[SERVICE_VAL_TYPE]) + 1); + if (!v) + return; + + v->type = type; + v->avl.key = v->package = package; + strcpy(v->package, blobmsg_get_string(tb[SERVICE_VAL_PACKAGE])); + strcpy(v->type, blobmsg_get_string(tb[SERVICE_VAL_TYPE])); + + list_add(&v->list, &s->validators); + if (avl_insert(&validators, &v->avl)) { + free(v); + return; + } + avl_init(&v->rules, avl_strcmp, false, NULL); + + blobmsg_for_each_attr(cur, tb[SERVICE_VAL_DATA], rem) { + char *option; + char *rule; + struct vrule *vr = calloc_a(sizeof(*vr), &option, strlen(blobmsg_name(cur)) + 1, + &rule, strlen(blobmsg_get_string(cur)) + 1); + + vr->avl.key = vr->option = option; + vr->rule = rule; + strcpy(vr->option, blobmsg_name(cur)); + strcpy(vr->rule, blobmsg_get_string(cur)); + if (avl_insert(&v->rules, &vr->avl)) + free(vr); + } +} + +void +service_validate_init(void) +{ + avl_init(&validators, avl_strcmp, true, NULL); +} diff --git a/service_validate.c b/service_validate.c deleted file mode 100644 index 3522cde..0000000 --- a/service_validate.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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 "procd.h" -#include "service.h" - -enum { - SERVICE_VAL_PACKAGE, - SERVICE_VAL_TYPE, - SERVICE_VAL_DATA, - __SERVICE_VAL_MAX -}; - -static const struct blobmsg_policy service_validate_attrs[__SERVICE_VAL_MAX] = { - [SERVICE_VAL_PACKAGE] = { "package", BLOBMSG_TYPE_STRING }, - [SERVICE_VAL_TYPE] = { "type", BLOBMSG_TYPE_STRING }, - [SERVICE_VAL_DATA] = { "data", BLOBMSG_TYPE_TABLE }, -}; - -static struct avl_tree validators; - -void -service_validate_dump_all(struct blob_buf *b, char *p, char *s) -{ - struct json_object *r = json_object_new_object(); - struct validate *v; - - if (!r) - return; - - avl_for_each_element(&validators, v, avl) { - struct json_object *o, *t; - struct vrule *vr; - - if (p && strcmp(p, v->package)) - continue; - - if (s && strcmp(s, v->type)) - continue; - - o = json_object_object_get(r, v->package); - if (!o) { - o = json_object_new_object(); - json_object_object_add(r, v->package, o); - } - t = json_object_object_get(o, v->type); - if (!t) { - t = json_object_new_object(); - json_object_object_add(o, v->type, t); - } - avl_for_each_element(&v->rules, vr, avl) - json_object_object_add(t, vr->option, json_object_new_string(vr->rule)); - } - blobmsg_add_object(b, r); -} - -void -service_validate_dump(struct blob_buf *b, struct service *s) -{ - struct validate *v; - void *i = blobmsg_open_array(b, "validate"); - - list_for_each_entry(v, &s->validators, list) { - struct vrule *vr; - void *k, *j = blobmsg_open_table(b, "validate"); - - blobmsg_add_string(b, "package", v->package); - blobmsg_add_string(b, "type", v->type); - k = blobmsg_open_table(b, "rules"); - avl_for_each_element(&v->rules, vr, avl) - blobmsg_add_string(b, vr->option, vr->rule); - blobmsg_close_table(b, k); - blobmsg_close_table(b, j); - } - blobmsg_close_array(b, i); -} - -void -service_validate_del(struct service *s) -{ - struct validate *v, *n; - - if (list_empty(&s->validators)) - return; - - list_for_each_entry_safe(v, n, &s->validators, list) { - struct vrule *vr, *a; - - avl_for_each_element_safe(&v->rules, vr, avl, a) { - avl_delete(&v->rules, &vr->avl); - free(vr); - } - avl_delete(&validators, &v->avl); - list_del(&v->list); - free(v); - } -} - -void -service_validate_add(struct service *s, struct blob_attr *msg) -{ - struct blob_attr *tb[__SERVICE_VAL_MAX]; - struct validate *v; - char *type, *package; - struct blob_attr *cur; - int rem; - - blobmsg_parse(service_validate_attrs, __SERVICE_VAL_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg)); - if (!tb[SERVICE_VAL_PACKAGE] || !tb[SERVICE_VAL_TYPE] || !tb[SERVICE_VAL_DATA]) - return; - - v = calloc_a(sizeof(*v), &package, blobmsg_data_len(tb[SERVICE_VAL_PACKAGE]) + 1, - &type, blobmsg_data_len(tb[SERVICE_VAL_TYPE]) + 1); - if (!v) - return; - - v->type = type; - v->avl.key = v->package = package; - strcpy(v->package, blobmsg_get_string(tb[SERVICE_VAL_PACKAGE])); - strcpy(v->type, blobmsg_get_string(tb[SERVICE_VAL_TYPE])); - - list_add(&v->list, &s->validators); - if (avl_insert(&validators, &v->avl)) { - free(v); - return; - } - avl_init(&v->rules, avl_strcmp, false, NULL); - - blobmsg_for_each_attr(cur, tb[SERVICE_VAL_DATA], rem) { - char *option; - char *rule; - struct vrule *vr = calloc_a(sizeof(*vr), &option, strlen(blobmsg_name(cur)) + 1, - &rule, strlen(blobmsg_get_string(cur)) + 1); - - vr->avl.key = vr->option = option; - vr->rule = rule; - strcpy(vr->option, blobmsg_name(cur)); - strcpy(vr->rule, blobmsg_get_string(cur)); - if (avl_insert(&v->rules, &vr->avl)) - free(vr); - } -} - -void -service_validate_init(void) -{ - avl_init(&validators, avl_strcmp, true, NULL); -} diff --git a/signal.c b/signal.c index ebaf7bc..74cabcb 100644 --- a/signal.c +++ b/signal.c @@ -19,8 +19,6 @@ #include "procd.h" -static int preinit; - static void do_reboot(void) { LOG("reboot\n"); @@ -37,9 +35,6 @@ static void signal_shutdown(int signal, siginfo_t *siginfo, void *data) int event = 0; char *msg = NULL; - if (preinit) - do_reboot(); - switch(signal) { case SIGTERM: event = RB_AUTOBOOT; @@ -97,11 +92,3 @@ void procd_signal(void) sigaction(SIGKILL, &sa_dummy, NULL); sigaction(SIGSTOP, &sa_dummy, NULL); } - -void procd_signal_preinit(void) -{ - preinit = 1; - sigaction(SIGTERM, &sa_shutdown, NULL); - sigaction(SIGUSR1, &sa_shutdown, NULL); - sigaction(SIGUSR2, &sa_shutdown, NULL); -} diff --git a/state.c b/state.c index f83032c..9f3033b 100644 --- a/state.c +++ b/state.c @@ -18,9 +18,9 @@ #include "procd.h" #include "syslog.h" -#include "hotplug.h" +#include "plug/hotplug.h" #include "watchdog.h" -#include "service.h" +#include "service/service.h" enum { STATE_NONE = 0, @@ -49,10 +49,13 @@ static void state_enter(void) case STATE_INIT: // try to reopen incase the wdt was not available before coldplug watchdog_init(0); - LOG("- init -\n"); - log_init(); + LOG("- ubus -\n"); procd_connect_ubus(); + + LOG("- init -\n"); service_init(); + service_start_early("ubus", "/sbin/ubusd"); + procd_inittab(); procd_inittab_run("respawn"); procd_inittab_run("askconsole"); @@ -83,14 +86,14 @@ static void state_enter(void) void procd_state_next(void) { - DEBUG(2, "Change state %d -> %d\n", state, state + 1); + DEBUG(4, "Change state %d -> %d\n", state, state + 1); state++; state_enter(); } void procd_shutdown(int event) { - DEBUG(1, "Shutting down system with event %x\n", event); + DEBUG(2, "Shutting down system with event %x\n", event); reboot_event = event; state = STATE_SHUTDOWN; state_enter(); diff --git a/syslog.c b/syslog.c deleted file mode 100644 index 7a2839e..0000000 --- a/syslog.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * 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 -#include -#include - -#include -#include -#include - -#include "procd.h" -#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, 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; - } - - DEBUG(2, "-> %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 { - DEBUG(2, "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; -} - -void log_printf(char *fmt, ...) -{ - static int buffer_len = 128; - static char *buffer; - va_list ap; - int n = 0; - - do { - if (n) - buffer_len = n + 1; - if (!buffer) - buffer = malloc(buffer_len); - if (!buffer) - return; - va_start(ap, fmt); - n = vsnprintf(buffer, buffer_len, fmt, ap); - va_end(ap); - if (n < 1) - return; - if (n >= buffer_len) { - free(buffer); - buffer = NULL; - } - } while (n >= buffer_len); - - log_add(buffer, n, SOURCE_INTERNAL); -} - -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; - - DEBUG(1, "Opening %s\n", KLOG_DEFAULT_PROC); - fd = open(KLOG_DEFAULT_PROC, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - ERROR("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; - - DEBUG(1, "Opening %s\n", log_dev); - unlink(log_dev); - fd = usock(USOCK_UNIX | USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, log_dev, NULL); - if (fd < 0) { - ERROR("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) { - ERROR("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(void) -{ - regcomp(&pat_prio, "^<([0-9]*)>(.*)", REG_EXTENDED); - regcomp(&pat_tstamp, "^\[[ 0]*([0-9]*).([0-9]*)] (.*)", REG_EXTENDED); - - if (log_buffer_init(log_size)) { - ERROR("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); -} diff --git a/syslog.h b/syslog.h deleted file mode 100644 index fd78363..0000000 --- a/syslog.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -#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(void); -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 log_printf(char *fmt, ...); - -#endif diff --git a/system.c b/system.c index 54a84b4..be02621 100644 --- a/system.c +++ b/system.c @@ -25,7 +25,6 @@ #include "procd.h" #include "watchdog.h" -#include "hotplug.h" static struct blob_buf b; @@ -189,12 +188,7 @@ static int system_upgrade(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { - procd_reconnect_ubus(0); - log_shutdown(); - hotplug_shutdown(); - upgrade_running = 1; - return 0; } diff --git a/trigger.c b/trigger.c deleted file mode 100644 index d14101e..0000000 --- a/trigger.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * 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 -#include - -#include -#include -#include -#include - -#include "procd.h" - -struct trigger { - struct list_head list; - - char *type; - - int pending; - int remove; - int timeout; - - void *id; - - struct blob_attr *rule; - struct blob_attr *data; - struct uloop_timeout delay; - - struct json_script_ctx jctx; -}; - -struct job; -struct cmd { - char *name; - void (*handler)(struct job *job, struct blob_attr *exec, struct blob_attr *env); -}; - -struct job { - struct runqueue_process proc; - struct cmd *cmd; - struct trigger *trigger; - struct blob_attr *exec; - struct blob_attr *env; -}; - -static LIST_HEAD(triggers); -static struct runqueue q; - -static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) -{ - return NULL; -} - -static struct json_script_file * -rule_load_script(struct json_script_ctx *ctx, const char *name) -{ - struct trigger *t = container_of(ctx, struct trigger, jctx); - - return json_script_file_from_blobmsg(t->type, t->rule, blob_pad_len(t->rule)); -} - -static void q_job_run(struct runqueue *q, struct runqueue_task *t) -{ - struct job *j = container_of(t, struct job, proc.task); - - DEBUG(2, "handle event %s\n", j->cmd->name); - j->cmd->handler(j, j->exec, j->env); -} - -static void q_job_complete(struct runqueue *q, struct runqueue_task *p) -{ - struct job *j = container_of(p, struct job, proc.task); - - if (j->trigger->remove) { - list_del(&j->trigger->list); - free(j->trigger); - } else { - j->trigger->pending = 0; - } - free(j); -} - -static void add_job(struct trigger *t, struct cmd *cmd, struct blob_attr *exec, struct blob_attr *data) -{ - static const struct runqueue_task_type job_type = { - .run = q_job_run, - .cancel = runqueue_process_cancel_cb, - .kill = runqueue_process_kill_cb, - }; - struct blob_attr *d, *e; - struct job *j = calloc_a(sizeof(*j), &e, blob_pad_len(exec), &d, blob_pad_len(data)); - - j->env = d; - j->exec = e; - j->cmd = cmd; - j->trigger = t; - j->proc.task.type = &job_type; - j->proc.task.complete = q_job_complete; - t->pending = 1; - - memcpy(j->exec, exec, blob_pad_len(exec)); - memcpy(j->env, data, blob_pad_len(data)); - - runqueue_task_add(&q, &j->proc.task, false); -} - -static void _setenv(const char *key, const char *val) -{ - char _key[32]; - - snprintf(_key, sizeof(_key), "PARAM_%s", key); - setenv(_key, val, 1); -} - -static void handle_run_script(struct job *j, struct blob_attr *exec, struct blob_attr *env) -{ - char *argv[8]; - struct blob_attr *cur; - int rem; - int i = 0; - pid_t pid; - - pid = fork(); - if (pid < 0) - return; - - if (pid) { - runqueue_process_add(&q, &j->proc, pid); - return; - } - - if (debug < 2) { - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - } - - _setenv("type", j->trigger->type); - blobmsg_for_each_attr(cur, j->env, rem) - _setenv(blobmsg_name(cur), blobmsg_data(cur)); - - blobmsg_for_each_attr(cur, j->exec, rem) { - argv[i] = blobmsg_data(cur); - i++; - if (i == 7) - break; - } - - if (i > 0) { - argv[i] = NULL; - execvp(argv[0], &argv[0]); - } - - exit(1); -} - -static struct cmd handlers[] = { - { - .name = "run_script", - .handler = handle_run_script, - }, -}; - -static void rule_handle_command(struct json_script_ctx *ctx, const char *name, - struct blob_attr *exec, struct blob_attr *vars) -{ - struct trigger *t = container_of(ctx, struct trigger, jctx); - int i; - - if (t->pending) - return; - - for (i = 0; i < ARRAY_SIZE(handlers); i++) { - if (!strcmp(handlers[i].name, name)) { - add_job(t, &handlers[i], exec, vars); - break; - } - } -} - -static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, - struct blob_attr *context) -{ - char *s; - - s = blobmsg_format_json(context, false); - ERROR("ERROR: %s in block: %s\n", msg, s); - free(s); -} - -static void q_empty(struct runqueue *q) -{ -} - -static void trigger_delay_cb(struct uloop_timeout *tout) -{ - struct trigger *t = container_of(tout, struct trigger, delay); - - json_script_run(&t->jctx, "foo", t->data); - free(t->data); -} - -static struct trigger* _trigger_add(char *type, struct blob_attr *rule, int timeout, void *id) -{ - char *_t; - struct blob_attr *_r; - struct trigger *t = calloc_a(sizeof(*t), &_t, strlen(type) + 1, &_r, blob_pad_len(rule)); - - t->type = _t; - t->rule = _r; - t->delay.cb = trigger_delay_cb; - t->timeout = timeout; - t->pending = 0; - t->remove = 0; - t->id = id; - t->jctx.handle_var = rule_handle_var, - t->jctx.handle_error = rule_handle_error, - t->jctx.handle_command = rule_handle_command, - t->jctx.handle_file = rule_load_script, - - strcpy(t->type, type); - memcpy(t->rule, rule, blob_pad_len(rule)); - - list_add(&t->list, &triggers); - json_script_init(&t->jctx); - - return t; -} - -void trigger_add(struct blob_attr *rule, void *id) -{ - struct blob_attr *cur; - int rem; - - blobmsg_for_each_attr(cur, rule, rem) { - struct blob_attr *_cur, *type = NULL, *script = NULL, *timeout = NULL; - int _rem; - int i = 0; - - if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY) - continue; - - blobmsg_for_each_attr(_cur, cur, _rem) { - switch (i++) { - case 0: - if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) - type = _cur; - break; - - case 1: - if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY) - script = _cur; - break; - - case 2: - if (blobmsg_type(_cur) == BLOBMSG_TYPE_INT32) - timeout = _cur; - break; - } - } - - if (type && script) { - int t = 0; - - if (timeout) - t = blobmsg_get_u32(timeout); - _trigger_add(blobmsg_get_string(type), script, t, id); - } - } -} - -void trigger_del(void *id) -{ - struct trigger *t, *n; - - list_for_each_entry_safe(t, n, &triggers, list) { - if (t->id != id) - continue; - - if (t->pending) { - t->remove = 1; - continue; - } - list_del(&t->list); - free(t); - } -} - -void trigger_init(void) -{ - runqueue_init(&q); - q.empty_cb = q_empty; - q.max_running_tasks = 1; -} - -void trigger_event(char *type, struct blob_attr *data) -{ - struct trigger *t; - - list_for_each_entry(t, &triggers, list) { - if (t->pending || t->remove) - continue; - if (!strcmp(t->type, type)) { - if (t->timeout) { - t->data = malloc(blob_pad_len(data)); - memcpy(t->data, data, blob_pad_len(data)); - uloop_timeout_set(&t->delay, t->timeout); - } else { - json_script_run(&t->jctx, "foo", data); - } - } - } -} diff --git a/ubus.c b/ubus.c index 54ead33..6166254 100644 --- a/ubus.c +++ b/ubus.c @@ -21,97 +21,46 @@ char *ubus_socket = NULL; static struct ubus_context *ctx; -static struct uloop_process ubus_proc; -static bool ubus_connected = false; -static struct uloop_timeout retry; -static int reconnect = 1; +static struct uloop_timeout ubus_timer; -static void procd_ubus_connection_lost(struct ubus_context *old_ctx); - -static void ubus_proc_cb(struct uloop_process *proc, int ret) +static void +ubus_reconnect_cb(struct uloop_timeout *timeout) { - /* nothing to do here */ + if (!ubus_reconnect(ctx, ubus_socket)) + ubus_add_uloop(ctx); + else + uloop_timeout_set(timeout, 2000); } -static void procd_restart_ubus(void) +static void +ubus_disconnect_cb(struct ubus_context *ctx) { - char *argv[] = { "ubusd", NULL, ubus_socket, NULL }; - - if (ubus_proc.pending) { - ERROR("Killing existing ubus instance, pid=%d\n", (int) ubus_proc.pid); - kill(ubus_proc.pid, SIGKILL); - uloop_process_delete(&ubus_proc); - } - - if (ubus_socket) - argv[1] = "-s"; - - ubus_proc.pid = fork(); - if (!ubus_proc.pid) { - setpriority(PRIO_PROCESS, 0, -20); - execvp(argv[0], argv); - exit(-1); - } - - if (ubus_proc.pid <= 0) { - ERROR("Failed to start new ubus instance\n"); - return; - } - - DEBUG(1, "Launched new ubus instance, pid=%d\n", (int) ubus_proc.pid); - uloop_process_add(&ubus_proc); + ubus_timer.cb = ubus_reconnect_cb; + uloop_timeout_set(&ubus_timer, 2000); } -static void procd_ubus_try_connect(void) +static void +ubus_connect_cb(struct uloop_timeout *timeout) { - if (ctx) { - ubus_connected = !ubus_reconnect(ctx, ubus_socket); - return; - } ctx = ubus_connect(ubus_socket); + if (!ctx) { - ubus_connected = false; - DEBUG(2, "Connection to ubus failed\n"); + DEBUG(4, "Connection to ubus failed\n"); + uloop_timeout_set(&ubus_timer, 1000); return; } - ctx->connection_lost = procd_ubus_connection_lost; - ubus_connected = true; + ctx->connection_lost = ubus_disconnect_cb; ubus_init_service(ctx); ubus_init_system(ctx); - if (getpid() == 1) - ubus_init_log(ctx); -} - -static void -procd_ubus_reconnect_timer(struct uloop_timeout *timeout) -{ - procd_ubus_try_connect(); - if (ubus_connected) { - DEBUG(1, "Connected to ubus, id=%08x\n", ctx->local_id); - ubus_add_uloop(ctx); - return; - } - uloop_timeout_set(&retry, 1000); - procd_restart_ubus(); + DEBUG(2, "Connected to ubus, id=%08x\n", ctx->local_id); + ubus_add_uloop(ctx); } -static void procd_ubus_connection_lost(struct ubus_context *old_ctx) +void +procd_connect_ubus(void) { - retry.cb = procd_ubus_reconnect_timer; - procd_restart_ubus(); - uloop_timeout_set(&retry, 1000); + ubus_timer.cb = ubus_connect_cb; + uloop_timeout_set(&ubus_timer, 1000); } - -void procd_connect_ubus(void) -{ - ubus_proc.cb = ubus_proc_cb; - procd_ubus_connection_lost(NULL); -} - -void procd_reconnect_ubus(int _reconnect) -{ - reconnect = _reconnect; -} - diff --git a/udevtrigger.c b/udevtrigger.c deleted file mode 100644 index 5013189..0000000 --- a/udevtrigger.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2004-2006 Kay Sievers - * Copyright (C) 2006 Hannes Reinecke - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PATH_SIZE 512 - -#ifndef strlcpy -#define strlcpy(d,s,l) (strncpy(d,s,l), (d)[(l)-1] = '\0') -#endif - -#ifndef strlcat -#define strlcat(d,s,l) strncat(d,s,(l)-strlen(d)-1) -#endif - -static int verbose; -static int dry_run; - -static void log_message(int priority, const char *format, ...) -{ - va_list args; - - va_start(args, format); - vsyslog(priority, format, args); - va_end(args); -} - -#undef err -#define err(format, arg...) \ - do { \ - log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \ - } while (0) - -#undef info -#define info(format, arg...) \ - do { \ - log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \ - } while (0) - -#ifdef DEBUG -#undef dbg -#define dbg(format, arg...) \ - do { \ - log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \ - } while (0) -#else -#define dbg(...) do {} while(0) -#endif - - -static void trigger_uevent(const char *devpath) -{ - char filename[PATH_SIZE]; - int fd; - - strlcpy(filename, "/sys", sizeof(filename)); - strlcat(filename, devpath, sizeof(filename)); - strlcat(filename, "/uevent", sizeof(filename)); - - if (verbose) - printf("%s\n", devpath); - - if (dry_run) - return; - - fd = open(filename, O_WRONLY); - if (fd < 0) { - dbg("error on opening %s: %s\n", filename, strerror(errno)); - return; - } - - if (write(fd, "add", 3) < 0) - info("error on triggering %s: %s\n", filename, strerror(errno)); - - close(fd); -} - -static int sysfs_resolve_link(char *devpath, size_t size) -{ - char link_path[PATH_SIZE]; - char link_target[PATH_SIZE]; - int len; - int i; - int back; - - strlcpy(link_path, "/sys", sizeof(link_path)); - strlcat(link_path, devpath, sizeof(link_path)); - len = readlink(link_path, link_target, sizeof(link_target)); - if (len <= 0) - return -1; - link_target[len] = '\0'; - dbg("path link '%s' points to '%s'", devpath, link_target); - - for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) - ; - dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back); - for (i = 0; i <= back; i++) { - char *pos = strrchr(devpath, '/'); - - if (pos == NULL) - return -1; - pos[0] = '\0'; - } - dbg("after moving back '%s'", devpath); - strlcat(devpath, "/", size); - strlcat(devpath, &link_target[back * 3], size); - return 0; -} - -static bool device_has_attribute(const char *path, const char *attr, - mode_t mode) -{ - char filename[PATH_SIZE]; - struct stat statbuf; - - strlcpy(filename, path, sizeof(filename)); - strlcat(filename, attr, sizeof(filename)); - - if (stat(filename, &statbuf) < 0) - return false; - - if (!(statbuf.st_mode & mode)) - return false; - - return true; -} - -static int device_list_insert(const char *path) -{ - char devpath[PATH_SIZE]; - struct stat statbuf; - - dbg("add '%s'" , path); - - /* we only have a device, if we have a dev and an uevent file */ - if (!device_has_attribute(path, "/dev", S_IRUSR) || - !device_has_attribute(path, "/uevent", S_IWUSR)) - return -1; - - strlcpy(devpath, &path[4], sizeof(devpath)); - - /* resolve possible link to real target */ - if (lstat(path, &statbuf) < 0) - return -1; - if (S_ISLNK(statbuf.st_mode)) - if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) - return -1; - - trigger_uevent(devpath); - return 0; -} - -static void scan_subdir(const char *base, const char *subdir, - bool insert, int depth) -{ - DIR *dir; - struct dirent *dent; - - dir = opendir(base); - if (dir == NULL) - return; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char dirname[PATH_SIZE]; - - if (dent->d_name[0] == '.') - continue; - - strlcpy(dirname, base, sizeof(dirname)); - strlcat(dirname, "/", sizeof(dirname)); - strlcat(dirname, dent->d_name, sizeof(dirname)); - - if (insert) { - int err; - - err = device_list_insert(dirname); - if (err) - continue; - } - - if (subdir) - strlcat(dirname, subdir, sizeof(base)); - - if (depth) - scan_subdir(dirname, NULL, true, depth - 1); - } - - closedir(dir); -} - -int main(int argc, char *argv[], char *envp[]) -{ - struct stat statbuf; - int option; - - openlog("udevtrigger", LOG_PID | LOG_CONS, LOG_DAEMON); - - while (1) { - option = getopt(argc, argv, "vnh"); - if (option == -1) - break; - - switch (option) { - case 'v': - verbose = 1; - break; - case 'n': - dry_run = 1; - break; - case 'h': - printf("Usage: udevtrigger OPTIONS\n" - " -v print the list of devices while running\n" - " -n do not actually trigger the events\n" - " -h print this text\n" - "\n"); - goto exit; - default: - goto exit; - } - } - - - /* if we have /sys/subsystem, forget all the old stuff */ - scan_subdir("/sys/bus", "/devices", false, 1); - scan_subdir("/sys/class", NULL, false, 1); - - /* scan "block" if it isn't a "class" */ - if (stat("/sys/class/block", &statbuf) != 0) - scan_subdir("/sys/block", NULL, true, 1); - -exit: - - closelog(); - return 0; -} diff --git a/utils.c b/utils.c deleted file mode 100644 index 59d02f1..0000000 --- a/utils.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 "utils.h" - -void -__blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp) -{ - avl_init(&list->avl, avl_strcmp, false, NULL); - list->node_offset = offset; - list->node_len = len; - list->cmp = cmp; -} - -int -blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array) -{ - struct avl_tree *tree = &list->avl; - struct blobmsg_list_node *node; - struct blob_attr *cur; - void *ptr; - int count = 0; - int rem = len; - - __blob_for_each_attr(cur, data, rem) { - if (!blobmsg_check_attr(cur, !array)) - continue; - - ptr = calloc(1, list->node_len); - if (!ptr) - return -1; - - node = (void *) ((char *)ptr + list->node_offset); - if (array) - node->avl.key = blobmsg_data(cur); - else - node->avl.key = blobmsg_name(cur); - node->data = cur; - if (avl_insert(tree, &node->avl)) { - free(ptr); - continue; - } - - count++; - } - - return count; -} - -void -blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src) -{ - struct blobmsg_list_node *node, *tmp; - void *ptr; - - avl_remove_all_elements(&src->avl, node, avl, tmp) { - if (avl_insert(&list->avl, &node->avl)) { - ptr = ((char *) node - list->node_offset); - free(ptr); - } - } -} - -void -blobmsg_list_free(struct blobmsg_list *list) -{ - struct blobmsg_list_node *node, *tmp; - void *ptr; - - avl_remove_all_elements(&list->avl, node, avl, tmp) { - ptr = ((char *) node - list->node_offset); - free(ptr); - } -} - -bool -blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2) -{ - struct blobmsg_list_node *n1, *n2; - int count = l1->avl.count; - - if (count != l2->avl.count) - return false; - - n1 = avl_first_element(&l1->avl, n1, avl); - n2 = avl_first_element(&l2->avl, n2, avl); - - while (count-- > 0) { - int len; - - len = blob_len(n1->data); - if (len != blob_len(n2->data)) - return false; - - if (memcmp(n1->data, n2->data, len) != 0) - return false; - - if (l1->cmp && !l1->cmp(n1, n2)) - return false; - - if (!count) - break; - - n1 = avl_next_element(n1, avl); - n2 = avl_next_element(n2, avl); - } - - return true; -} diff --git a/utils.h b/utils.h deleted file mode 100644 index 37fa216..0000000 --- a/utils.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#ifndef __PROCD_UTILS_H -#define __PROCD_UTILS_H - -#include -#include -#include - -struct blobmsg_list_node { - struct avl_node avl; - struct blob_attr *data; -}; - -typedef bool (*blobmsg_list_cmp)(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2); -typedef void (*blobmsg_update_cb)(struct blobmsg_list_node *n); - -struct blobmsg_list { - struct avl_tree avl; - int node_offset; - int node_len; - - blobmsg_list_cmp cmp; -}; - -#define blobmsg_list_simple_init(list) \ - __blobmsg_list_init(list, 0, sizeof(struct blobmsg_list_node), NULL) - -#define blobmsg_list_init(list, type, field, cmp) \ - __blobmsg_list_init(list, offsetof(type, field), sizeof(type), cmp) - -#define blobmsg_list_for_each(list, element) \ - avl_for_each_element(&(list)->avl, element, avl) - -void __blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp); -int blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array); -void blobmsg_list_free(struct blobmsg_list *list); -bool blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2); -void blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src); - -#endif diff --git a/utils/askfirst.c b/utils/askfirst.c new file mode 100644 index 0000000..6ad77aa --- /dev/null +++ b/utils/askfirst.c @@ -0,0 +1,62 @@ +/* + * 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 + +static int redirect_output(const char *dev) +{ + pid_t p = setsid(); + int fd; + + chdir("/dev"); + fd = open(dev, O_RDWR); + chdir("/"); + + if (fd < 0) + return -1; + + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + tcsetpgrp(fd, p); + close(fd); + + return 0; +} + +int main(int argc, char **argv) +{ + int c; + + if (redirect_output(argv[1])) + fprintf(stderr, "%s: Failed to open %s\n", argv[0], argv[1]); + + printf("Please press Enter to activate this console.\n"); + do { + c = getchar(); + if (c == EOF) + return -1; + } + while (c != 0xA); + + execvp(argv[2], &argv[2]); + printf("%s: Failed to execute %s\n", argv[0], argv[2]); + + return -1; +} diff --git a/utils/md5.c b/utils/md5.c new file mode 100644 index 0000000..ec24dd2 --- /dev/null +++ b/utils/md5.c @@ -0,0 +1,242 @@ +/* + * md5.c - Compute MD5 checksum of strings according to the + * definition of MD5 in RFC 1321 from April 1992. + * + * Written by Ulrich Drepper , 1995. + * + * Copyright (C) 1995-1999 Free Software Foundation, Inc. + * Copyright (C) 2001 Manuel Novoa III + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003 Erik Andersen + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include /* TODO: better include for bswap_32 compat */ +#include "md5.h" + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SWAP_LE32(x) (x) +#else +#define SWAP_LE32(x) bswap_32(x) +#endif + +/* Initialize structure containing state of computation. + * (RFC 1321, 3.3: Step 3) + */ +void md5_begin(md5_ctx_t *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total = 0; + ctx->buflen = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + * and defined in the RFC 1321. The first function is a little bit optimized + * (as found in Colin Plumbs public domain implementation). + * #define FF(b, c, d) ((b & c) | (~b & d)) + */ +# define FF(b, c, d) (d ^ (b & (c ^ d))) +# define FG(b, c, d) FF (d, b, c) +# define FH(b, c, d) (b ^ c ^ d) +# define FI(b, c, d) (c ^ (b | ~d)) + +/* Hash a single block, 64 bytes long and 4-byte aligned. */ +static void md5_hash_block(const void *buffer, md5_ctx_t *ctx) +{ + uint32_t correct_words[16]; + const uint32_t *words = buffer; + + static const uint32_t C_array[] = { + /* round 1 */ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + /* round 2 */ + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + /* round 3 */ + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + /* round 4 */ + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + + static const char P_array[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ + }; + + static const char S_array[] = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; + + uint32_t A = ctx->A; + uint32_t B = ctx->B; + uint32_t C = ctx->C; + uint32_t D = ctx->D; + + uint32_t *cwp = correct_words; + +# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + const uint32_t *pc; + const char *pp; + const char *ps; + int i; + uint32_t temp; + + for (i = 0; i < 16; i++) { + cwp[i] = SWAP_LE32(words[i]); + } + words += 16; + + pc = C_array; + pp = P_array; + ps = S_array; + + for (i = 0; i < 16; i++) { + temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + + + ctx->A += A; + ctx->B += B; + ctx->C += C; + ctx->D += D; +} + +/* Feed data through a temporary buffer to call md5_hash_aligned_block() + * with chunks of data that are 4-byte aligned and a multiple of 64 bytes. + * This function's internal buffer remembers previous data until it has 64 + * bytes worth to pass on. Call md5_end() to flush this buffer. */ + +void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx) +{ + char *buf = (char *)buffer; + + /* RFC 1321 specifies the possible length of the file up to 2^64 bits, + * Here we only track the number of bytes. */ + + ctx->total += len; + + // Process all input. + + while (len) { + unsigned i = 64 - ctx->buflen; + + // Copy data into aligned buffer. + + if (i > len) + i = len; + memcpy(ctx->buffer + ctx->buflen, buf, i); + len -= i; + ctx->buflen += i; + buf += i; + + // When buffer fills up, process it. + + if (ctx->buflen == 64) { + md5_hash_block(ctx->buffer, ctx); + ctx->buflen = 0; + } + } +} + +/* Process the remaining bytes in the buffer and put result from CTX + * in first 16 bytes following RESBUF. The result is always in little + * endian byte order, so that a byte-wise output yields to the wanted + * ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ +void md5_end(void *resbuf, md5_ctx_t *ctx) +{ + char *buf = ctx->buffer; + int i; + + /* Pad data to block size. */ + + buf[ctx->buflen++] = 0x80; + memset(buf + ctx->buflen, 0, 128 - ctx->buflen); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->total <<= 3; + if (ctx->buflen > 56) + buf += 64; + + for (i = 0; i < 8; i++) + buf[56 + i] = ctx->total >> (i*8); + + /* Process last bytes. */ + if (buf != ctx->buffer) + md5_hash_block(ctx->buffer, ctx); + md5_hash_block(buf, ctx); + + /* Put result from CTX in first 16 bytes following RESBUF. The result is + * always in little endian byte order, so that a byte-wise output yields + * to the wanted ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ + ((uint32_t *) resbuf)[0] = SWAP_LE32(ctx->A); + ((uint32_t *) resbuf)[1] = SWAP_LE32(ctx->B); + ((uint32_t *) resbuf)[2] = SWAP_LE32(ctx->C); + ((uint32_t *) resbuf)[3] = SWAP_LE32(ctx->D); +} diff --git a/utils/md5.h b/utils/md5.h new file mode 100644 index 0000000..e1731ef --- /dev/null +++ b/utils/md5.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef __PROCD_MD5_H +#define __PROCD_MD5_H + +#include +#include + +typedef struct md5_ctx { + uint32_t A; + uint32_t B; + uint32_t C; + uint32_t D; + uint64_t total; + uint32_t buflen; + char buffer[128]; +} md5_ctx_t; + +void md5_begin(md5_ctx_t *ctx); +void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); +void md5_end(void *resbuf, md5_ctx_t *ctx); + +#endif diff --git a/utils/utils.c b/utils/utils.c new file mode 100644 index 0000000..59d02f1 --- /dev/null +++ b/utils/utils.c @@ -0,0 +1,122 @@ +/* + * 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 "utils.h" + +void +__blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp) +{ + avl_init(&list->avl, avl_strcmp, false, NULL); + list->node_offset = offset; + list->node_len = len; + list->cmp = cmp; +} + +int +blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array) +{ + struct avl_tree *tree = &list->avl; + struct blobmsg_list_node *node; + struct blob_attr *cur; + void *ptr; + int count = 0; + int rem = len; + + __blob_for_each_attr(cur, data, rem) { + if (!blobmsg_check_attr(cur, !array)) + continue; + + ptr = calloc(1, list->node_len); + if (!ptr) + return -1; + + node = (void *) ((char *)ptr + list->node_offset); + if (array) + node->avl.key = blobmsg_data(cur); + else + node->avl.key = blobmsg_name(cur); + node->data = cur; + if (avl_insert(tree, &node->avl)) { + free(ptr); + continue; + } + + count++; + } + + return count; +} + +void +blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src) +{ + struct blobmsg_list_node *node, *tmp; + void *ptr; + + avl_remove_all_elements(&src->avl, node, avl, tmp) { + if (avl_insert(&list->avl, &node->avl)) { + ptr = ((char *) node - list->node_offset); + free(ptr); + } + } +} + +void +blobmsg_list_free(struct blobmsg_list *list) +{ + struct blobmsg_list_node *node, *tmp; + void *ptr; + + avl_remove_all_elements(&list->avl, node, avl, tmp) { + ptr = ((char *) node - list->node_offset); + free(ptr); + } +} + +bool +blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2) +{ + struct blobmsg_list_node *n1, *n2; + int count = l1->avl.count; + + if (count != l2->avl.count) + return false; + + n1 = avl_first_element(&l1->avl, n1, avl); + n2 = avl_first_element(&l2->avl, n2, avl); + + while (count-- > 0) { + int len; + + len = blob_len(n1->data); + if (len != blob_len(n2->data)) + return false; + + if (memcmp(n1->data, n2->data, len) != 0) + return false; + + if (l1->cmp && !l1->cmp(n1, n2)) + return false; + + if (!count) + break; + + n1 = avl_next_element(n1, avl); + n2 = avl_next_element(n2, avl); + } + + return true; +} diff --git a/utils/utils.h b/utils/utils.h new file mode 100644 index 0000000..37fa216 --- /dev/null +++ b/utils/utils.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef __PROCD_UTILS_H +#define __PROCD_UTILS_H + +#include +#include +#include + +struct blobmsg_list_node { + struct avl_node avl; + struct blob_attr *data; +}; + +typedef bool (*blobmsg_list_cmp)(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2); +typedef void (*blobmsg_update_cb)(struct blobmsg_list_node *n); + +struct blobmsg_list { + struct avl_tree avl; + int node_offset; + int node_len; + + blobmsg_list_cmp cmp; +}; + +#define blobmsg_list_simple_init(list) \ + __blobmsg_list_init(list, 0, sizeof(struct blobmsg_list_node), NULL) + +#define blobmsg_list_init(list, type, field, cmp) \ + __blobmsg_list_init(list, offsetof(type, field), sizeof(type), cmp) + +#define blobmsg_list_for_each(list, element) \ + avl_for_each_element(&(list)->avl, element, avl) + +void __blobmsg_list_init(struct blobmsg_list *list, int offset, int len, blobmsg_list_cmp cmp); +int blobmsg_list_fill(struct blobmsg_list *list, void *data, int len, bool array); +void blobmsg_list_free(struct blobmsg_list *list); +bool blobmsg_list_equal(struct blobmsg_list *l1, struct blobmsg_list *l2); +void blobmsg_list_move(struct blobmsg_list *list, struct blobmsg_list *src); + +#endif diff --git a/validate.c b/validate.c deleted file mode 100644 index f94a071..0000000 --- a/validate.c +++ /dev/null @@ -1,1002 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "libvalidate.h" - -enum dt_optype { - OP_UNKNOWN, - OP_BOOL, - 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; - const char *value; - struct dt_op stack[32]; -}; - -struct dt_fun { - const char *name; - 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); - -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; - char *e; - - if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) - { - n = strtol(s->value, &e, 0); - - return (e > s->value && *e == 0 && - n >= s->stack[s->pos].value.number); - } - - return false; -} - -static bool -dt_type_max(struct dt_state *s, int nargs) -{ - int n; - char *e; - - if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) - { - n = strtol(s->value, &e, 0); - - return (e > s->value && *e == 0 && - n <= s->stack[s->pos].value.number); - } - - return false; -} - -static bool -dt_type_range(struct dt_state *s, int nargs) -{ - int n; - char *e; - - if (nargs >= 2 && - s->stack[s->pos].type == OP_NUMBER && - s->stack[s->pos + 1].type == OP_NUMBER) - { - n = strtol(s->value, &e, 0); - - return (e > s->value && *e == 0 && - n >= s->stack[s->pos].value.number && - n <= s->stack[s->pos + 1].value.number); - } - - return false; -} - -static bool -dt_type_minlen(struct dt_state *s, int nargs) -{ - if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) - return (strlen(s->value) >= s->stack[s->pos].value.number); - - return false; -} - -static bool -dt_type_maxlen(struct dt_state *s, int nargs) -{ - if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) - return (strlen(s->value) <= s->stack[s->pos].value.number); - - return false; -} - -static bool -dt_type_rangelen(struct dt_state *s, int nargs) -{ - if (nargs >= 2 && - s->stack[s->pos].type == OP_NUMBER && - s->stack[s->pos + 1].type == OP_NUMBER) - return (strlen(s->value) >= s->stack[s->pos].value.number && - strlen(s->value) <= s->stack[s->pos + 1].value.number); - - return false; -} - -static bool -dt_type_int(struct dt_state *s, int nargs) -{ - char *e; - - strtol(s->value, &e, 0); - - return (e > s->value && *e == 0); -} - -static bool -dt_type_uint(struct dt_state *s, int nargs) -{ - int n; - char *e; - - n = strtol(s->value, &e, 0); - - return (e > s->value && *e == 0 && n >= 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", - "1", "on", "true", "yes" - }; - - 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) -{ - 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 struct dt_fun dt_types[] = { - { "or", dt_type_or }, - { "and", dt_type_and }, - { "not", dt_type_not }, - { "neg", dt_type_neg }, - { "list", dt_type_list }, - { "min", dt_type_min }, - { "max", dt_type_max }, - { "range", dt_type_range }, - { "minlength", dt_type_minlen }, - { "maxlength", dt_type_maxlen }, - { "rangelength", dt_type_rangelen }, - { "integer", dt_type_int }, - { "uinteger", dt_type_uint }, - { "float", dt_type_float }, - { "ufloat", dt_type_ufloat }, - { "bool", dt_type_bool }, - { "string", dt_type_string }, - { "ip4addr", dt_type_ip4addr }, - { "ip6addr", dt_type_ip6addr }, - { "ipaddr", dt_type_ipaddr }, - { "cidr4", dt_type_cidr4 }, - { "cidr6", dt_type_cidr6 }, - { "cidr", dt_type_cidr }, - { "netmask4", dt_type_netmask4 }, - { "netmask6", dt_type_netmask6 }, - { "ipmask4", dt_type_ipmask4 }, - { "ipmask6", dt_type_ipmask6 }, - { "ipmask", dt_type_ipmask }, - { "port", dt_type_port }, - { "portrange", dt_type_portrange }, - { "macaddr", dt_type_macaddr }, - { "uciname", dt_type_uciname }, - { "wpakey", dt_type_wpakey }, - { "wepkey", dt_type_wepkey }, - { "hostname", dt_type_hostname }, - { "host", dt_type_host }, - { "network", dt_type_network }, - { "phonedigit", dt_type_phonedigit }, - { "directory", dt_type_directory }, - { "device", dt_type_device }, - { "file", dt_type_file }, - - { } -}; - -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) - 2; - 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_BOOL: - rv = op->value.boolean; - break; - - case OP_NUMBER: - rv = dt_test_number(op->value.number, s->value); - break; - - case OP_STRING: - rv = dt_test_string(op->value.string, op->value.string + op->length, s->value); - 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); - - s->pos = fptr->nextop; - - return rv; -} - -bool -dt_parse(const char *code, const char *value) -{ - 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.value = value; - - return dt_call(&s); -} diff --git a/validate_data.c b/validate_data.c deleted file mode 100644 index dc5e96b..0000000 --- a/validate_data.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "libvalidate.h" - -int main(int argc, char **argv) -{ - bool rv; - - if (argc == 3) { - rv = dt_parse(argv[1], argv[2]); - - printf("%s - %s = %s\n", argv[1], argv[2], rv ? "true" : "false"); - - return rv ? 0 : 1; - } else if (argc > 3) { - - } - - return 0; -} diff --git a/watchdog.c b/watchdog.c index d927c53..de9556c 100644 --- a/watchdog.c +++ b/watchdog.c @@ -34,7 +34,7 @@ static int wdt_frequency = 5; static void watchdog_timeout_cb(struct uloop_timeout *t) { - DEBUG(2, "Ping\n"); + DEBUG(4, "Ping\n"); if (write(wdt_fd, "X", 1) < 0) ERROR("WDT failed to write: %s\n", strerror(errno)); uloop_timeout_set(t, wdt_frequency * 1000); @@ -59,7 +59,7 @@ int watchdog_timeout(int timeout) return 0; if (timeout) { - DEBUG(2, "Set watchdog timeout: %ds\n", timeout); + DEBUG(4, "Set watchdog timeout: %ds\n", timeout); ioctl(wdt_fd, WDIOC_SETTIMEOUT, &timeout); } ioctl(wdt_fd, WDIOC_GETTIMEOUT, &timeout); @@ -73,7 +73,7 @@ int watchdog_frequency(int frequency) return 0; if (frequency) { - DEBUG(2, "Set watchdog frequency: %ds\n", frequency); + DEBUG(4, "Set watchdog frequency: %ds\n", frequency); wdt_frequency = frequency; } @@ -100,7 +100,7 @@ void watchdog_init(int preinit) wdt_timeout.cb = watchdog_timeout_cb; if (env) { - DEBUG(1, "Watchdog handover: fd=%s\n", env); + DEBUG(2, "Watchdog handover: fd=%s\n", env); wdt_fd = atoi(env); unsetenv("WDTFD"); } else { @@ -117,5 +117,5 @@ void watchdog_init(int preinit) watchdog_timeout(30); watchdog_timeout_cb(&wdt_timeout); - DEBUG(2, "Opened watchdog with timeout %ds\n", watchdog_timeout(0)); + DEBUG(4, "Opened watchdog with timeout %ds\n", watchdog_timeout(0)); } -- cgit v1.2.3