From: John Crispin Date: Thu, 14 Nov 2013 12:41:13 +0000 (+0100) Subject: debloat and reorganize code X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fprocd.git;a=commitdiff_plain;h=916f95cb58604038695347ee41a430d8ca1f0556 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 --- 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)); }