debloat and reorganize code
authorJohn Crispin <blogic@openwrt.org>
Thu, 14 Nov 2013 12:41:13 +0000 (13:41 +0100)
committerJohn Crispin <blogic@openwrt.org>
Fri, 15 Nov 2013 16:05:20 +0000 (17:05 +0100)
split app into procd and init binaries
remove log support, this is an external service now

Signed-off-by: John Crispin <blogic@openwrt.org>
60 files changed:
.gitignore
CMakeLists.txt
askfirst.c [deleted file]
coldplug.c [deleted file]
debug.c [deleted file]
early.c [deleted file]
hotplug.c [deleted file]
hotplug.h [deleted file]
initd/early.c [new file with mode: 0644]
initd/init.c [new file with mode: 0644]
initd/init.h [new file with mode: 0644]
initd/mkdev.c [new file with mode: 0644]
initd/preinit.c [new file with mode: 0644]
inittab.c
instance.c [deleted file]
instance.h [deleted file]
libvalidate.h [deleted file]
log.c [deleted file]
log.h [new file with mode: 0644]
logread.c [deleted file]
main.c [deleted file]
md5.c [deleted file]
md5.h [deleted file]
measure.c [deleted file]
mkdev.c [deleted file]
plug/coldplug.c [new file with mode: 0644]
plug/hotplug.c [new file with mode: 0644]
plug/hotplug.h [new file with mode: 0644]
plug/udevtrigger.c [new file with mode: 0644]
preinit.c [deleted file]
procd.c [new file with mode: 0644]
procd.h
rcS.c
service.c [deleted file]
service.h [deleted file]
service/instance.c [new file with mode: 0644]
service/instance.h [new file with mode: 0644]
service/service.c [new file with mode: 0644]
service/service.h [new file with mode: 0644]
service/trigger.c [new file with mode: 0644]
service/validate.c [new file with mode: 0644]
service_validate.c [deleted file]
signal.c
state.c
syslog.c [deleted file]
syslog.h [deleted file]
system.c
trigger.c [deleted file]
ubus.c
udevtrigger.c [deleted file]
utils.c [deleted file]
utils.h [deleted file]
utils/askfirst.c [new file with mode: 0644]
utils/md5.c [new file with mode: 0644]
utils/md5.h [new file with mode: 0644]
utils/utils.c [new file with mode: 0644]
utils/utils.h [new file with mode: 0644]
validate.c [deleted file]
validate_data.c [deleted file]
watchdog.c

index 7d82eca..4bd7b28 100644 (file)
@@ -1,13 +1,10 @@
 procd
-validate_data
-logread
 askfirst
 udevtrigger
+init
 .*
-*.so
 Makefile
 CMakeCache.txt
 CMakeFiles
 *.cmake
 install_manifest.txt
-
index a521ea7..d353801 100644 (file)
@@ -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 (file)
index 6ad77aa..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/types.h>
-#include <sys/stat.h>
-
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-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 (file)
index 71b09f0..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-
-#include <unistd.h>
-
-#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 (file)
index fbf1e4f..0000000
--- a/debug.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <regex.h>
-#include <unistd.h>
-
-#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 (file)
index 7da1e4b..0000000
--- a/early.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/mount.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#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 (file)
index 422e849..0000000
--- a/hotplug.c
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include <linux/types.h>
-#include <linux/netlink.h>
-
-#include <libubox/blobmsg_json.h>
-#include <libubox/json_script.h>
-#include <libubox/uloop.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <libgen.h>
-
-#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 (file)
index e33afcb..0000000
--- a/hotplug.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <libubox/uloop.h>
-
-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 (file)
index 0000000..77ced77
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#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 (file)
index 0000000..d458f29
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/reboot.h>
+
+#include <libubox/uloop.h>
+#include <libubus.h>
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <regex.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#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 (file)
index 0000000..1321cf8
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 (file)
index 0000000..3471461
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <limits.h>
+#include <fnmatch.h>
+
+#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 (file)
index 0000000..eeadbeb
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <libubox/uloop.h>
+#include <libubox/utils.h>
+#include <libubus.h>
+
+#include <stdio.h>
+
+#include <unistd.h>
+
+#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);
+}
index d73e0b8..e935ece 100644 (file)
--- 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 (file)
index 05b0f99..0000000
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/resource.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <fcntl.h>
-
-#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 (file)
index 6e69086..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <libubox/vlist.h>
-#include <libubox/uloop.h>
-#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 (file)
index d3b8e05..0000000
+++ /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 (file)
index d367388..0000000
--- a/log.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <linux/types.h>
-
-#include <libubox/uloop.h>
-#include <libubox/blobmsg_json.h>
-
-#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 (file)
index 0000000..968c136
--- /dev/null
+++ b/log.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <syslog.h>
+
+#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 (file)
index e8749f8..0000000
--- a/logread.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/types.h>
-#include <sys/stat.h>
-
-#include <fcntl.h>
-#include <time.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#define SYSLOG_NAMES
-#include <syslog.h>
-
-#include <libubox/blobmsg_json.h>
-#include <libubox/usock.h>
-#include <libubox/uloop.h>
-#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 "<unknown>";
-};
-
-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>          Path to ubus socket\n"
-               "    -l <count>         Got only the last 'count' messages\n"
-               "    -r <server> <port> Stream message to a server\n"
-               "    -F <file>          Log file\n"
-               "    -S <bytes>         Log size\n"
-               "    -p <file>          PID file\n"
-               "    -h <hostname>      Add hostname to the message\n"
-               "    -P <prefix>        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 (file)
index 49d85f4..0000000
--- a/main.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/wait.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <unistd.h>
-#include <getopt.h>
-#include <libgen.h>
-
-#include "procd.h"
-#include "hotplug.h"
-#include "watchdog.h"
-
-static int usage(const char *prog)
-{
-       ERROR("Usage: %s [options]\n"
-               "Options:\n"
-               "    -s <path>:         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 (file)
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 <drepper@gnu.ai.mit.edu>, 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 <libubox/blob.h> /* 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 (file)
index e1731ef..0000000
--- a/md5.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <stdint.h>
-#include <stddef.h>
-
-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 (file)
index 9e21a66..0000000
--- a/measure.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- *   Copyright (C) 2010 Steven Barth <steven@midlink.org>
- *   Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- *   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 <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <regex.h>
-#include <stdio.h>
-#include <string.h>
-#include <glob.h>
-#include <libgen.h>
-
-#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 (file)
index 0f55554..0000000
--- a/mkdev.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/stat.h>
-#include <sys/types.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <dirent.h>
-#include <limits.h>
-#include <fnmatch.h>
-
-#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 (file)
index 0000000..466b759
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+
+#include <unistd.h>
+
+#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 (file)
index 0000000..ca1e823
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+#include <libubox/blobmsg_json.h>
+#include <libubox/json_script.h>
+#include <libubox/uloop.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libgen.h>
+
+#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 (file)
index 0000000..2a44442
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/uloop.h>
+
+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 (file)
index 0000000..5013189
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
+ *
+ *     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 <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+
+#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 (file)
index c015ebd..0000000
--- a/preinit.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-
-#include <unistd.h>
-#include <unistd.h>
-
-#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 (file)
index 0000000..a72912a
--- /dev/null
+++ b/procd.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#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 <path>\tPath to ubus socket\n"
+               "\t-h <path>\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 (file)
--- a/procd.h
+++ b/procd.h
 #include <stdio.h>
 #include <syslog.h>
 
-#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 (file)
--- 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 (file)
index c5f5bf3..0000000
--- a/service.c
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <libubox/blobmsg_json.h>
-#include <libubox/avl-cmp.h>
-#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 (file)
index 6448e5e..0000000
--- a/service.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <libubox/avl.h>
-#include <libubox/vlist.h>
-#include <libubox/list.h>
-
-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 (file)
index 0000000..5ac7d57
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/resource.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+
+#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 (file)
index 0000000..65b670e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/vlist.h>
+#include <libubox/uloop.h>
+#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 (file)
index 0000000..aa393b9
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/blobmsg_json.h>
+#include <libubox/avl-cmp.h>
+
+#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 (file)
index 0000000..46ba746
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/avl.h>
+#include <libubox/vlist.h>
+#include <libubox/list.h>
+
+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 (file)
index 0000000..41fb55d
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+#include <libubox/blobmsg_json.h>
+#include <libubox/json_script.h>
+#include <libubox/runqueue.h>
+#include <libubox/ustream.h>
+#include <libubox/uloop.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libgen.h>
+
+#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 (file)
index 0000000..ca9bb39
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/blobmsg_json.h>
+#include <libubox/avl-cmp.h>
+
+#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 (file)
index 3522cde..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <libubox/blobmsg_json.h>
-#include <libubox/avl-cmp.h>
-
-#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);
-}
index ebaf7bc..74cabcb 100644 (file)
--- 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 (file)
--- 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 (file)
index 7a2839e..0000000
--- a/syslog.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <linux/un.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-
-#include <fcntl.h>
-#include <regex.h>
-#include <time.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include <libubox/uloop.h>
-#include <libubox/usock.h>
-#include <libubox/ustream.h>
-
-#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 (file)
index fd78363..0000000
--- a/syslog.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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
index 54a84b4..be02621 100644 (file)
--- 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 (file)
index d14101e..0000000
--- a/trigger.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include <linux/types.h>
-#include <linux/netlink.h>
-
-#include <libubox/blobmsg_json.h>
-#include <libubox/json_script.h>
-#include <libubox/runqueue.h>
-#include <libubox/ustream.h>
-#include <libubox/uloop.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <libgen.h>
-
-#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 (file)
--- a/ubus.c
+++ b/ubus.c
 
 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 (file)
index 5013189..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
- * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
- *
- *     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 <stdlib.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <syslog.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <string.h>
-
-#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 (file)
index 59d02f1..0000000
--- a/utils.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <libubox/avl.h>
-#include <libubox/avl-cmp.h>
-#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 (file)
index 37fa216..0000000
--- a/utils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
- *
- * 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 <libubox/avl.h>
-#include <libubox/blob.h>
-#include <libubox/blobmsg.h>
-
-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 (file)
index 0000000..6ad77aa
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+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 (file)
index 0000000..ec24dd2
--- /dev/null
@@ -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 <drepper@gnu.ai.mit.edu>, 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 <libubox/blob.h> /* 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 (file)
index 0000000..e1731ef
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <stdint.h>
+#include <stddef.h>
+
+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 (file)
index 0000000..59d02f1
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/avl.h>
+#include <libubox/avl-cmp.h>
+#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 (file)
index 0000000..37fa216
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * 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 <libubox/avl.h>
+#include <libubox/blob.h>
+#include <libubox/blobmsg.h>
+
+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 (file)
index f94a071..0000000
+++ /dev/null
@@ -1,1002 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <ctype.h>
-
-#include <arpa/inet.h>
-#include <netinet/ether.h>
-#include <sys/stat.h>
-
-#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 (file)
index dc5e96b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <ctype.h>
-
-#include <arpa/inet.h>
-#include <netinet/ether.h>
-#include <sys/stat.h>
-
-#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;
-}
index d927c53..de9556c 100644 (file)
@@ -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));
 }