procd
-validate_data
-logread
askfirst
udevtrigger
+init
.*
-*.so
Makefile
CMakeCache.txt
CMakeFiles
*.cmake
install_manifest.txt
-
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)
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
)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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");
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
}
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);
{
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);
}
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;
}
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);
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';
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-#ifndef _VALIDATE_H__
-#define _VALIDATE_H__
-
-bool dt_parse(const char *code, const char *value);
-
-#endif
+++ /dev/null
-/*
- * 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));
-}
--- /dev/null
+/*
+ * 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
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/**
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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;
+}
#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);
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
break;
*newline = 0;
len = newline + 1 - str;
- log_printf(buf->data);
+ syslog(0, buf->data);
ustream_consume(s, len);
} while (1);
}
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;
{
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);
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;
}
+++ /dev/null
-/*
- * 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();
-}
-
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+}
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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);
-}
#include "procd.h"
-static int preinit;
-
static void do_reboot(void)
{
LOG("reboot\n");
int event = 0;
char *msg = NULL;
- if (preinit)
- do_reboot();
-
switch(signal) {
case SIGTERM:
event = RB_AUTOBOOT;
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);
-}
#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,
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");
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();
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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
#include "procd.h"
#include "watchdog.h"
-#include "hotplug.h"
static struct blob_buf b;
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;
}
+++ /dev/null
-/*
- * 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);
- }
- }
- }
-}
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;
-}
-
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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;
-}
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);
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);
return 0;
if (frequency) {
- DEBUG(2, "Set watchdog frequency: %ds\n", frequency);
+ DEBUG(4, "Set watchdog frequency: %ds\n", frequency);
wdt_frequency = frequency;
}
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 {
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));
}