From 3018420f7fd004b48715100d2f60b27c64d48b75 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 24 Jun 2013 18:31:51 +0200 Subject: [PATCH 01/16] add respawn support https://dev.openwrt.org/ticket/13751 Signed-off-by: John Crispin --- inittab.c | 13 +++++++++++++ state.c | 1 + 2 files changed, 14 insertions(+) diff --git a/inittab.c b/inittab.c index 2ed1395..686d389 100644 --- a/inittab.c +++ b/inittab.c @@ -175,6 +175,15 @@ err_out: regfree(&pat_cmdline); } +static void rcrespawn(struct init_action *a) +{ + a->tout.cb = respawn; + a->respawn = 500; + + a->proc.cb = child_exit; + fork_worker(a); +} + static struct init_handler handlers[] = { { .name = "sysinit", @@ -190,6 +199,10 @@ static struct init_handler handlers[] = { .name = "askconsole", .cb = askconsole, .multi = 1, + }, { + .name = "respawn", + .cb = rcrespawn, + .multi = 1, } }; diff --git a/state.c b/state.c index 8fe931e..618d758 100644 --- a/state.c +++ b/state.c @@ -50,6 +50,7 @@ static void state_enter(void) log_init(); procd_connect_ubus(); procd_inittab(); + procd_inittab_run("respawn"); procd_inittab_run("askconsole"); procd_inittab_run("askfirst"); procd_inittab_run("sysinit"); -- 2.11.0 From bdac146facb72ce3f88f682d598d3124ec73fe78 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 24 Jun 2013 18:56:34 +0200 Subject: [PATCH 02/16] set global umask to 0 Signed-off-by: John Crispin --- main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main.c b/main.c index f97a9ea..71531e3 100644 --- a/main.c +++ b/main.c @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -67,6 +68,7 @@ int main(int argc, char **argv) return usage(argv[0]); } } + umask(0); uloop_init(); procd_signal(); if (getpid() != 1) -- 2.11.0 From 7f31a19db4c88ca17837a2aaa9144622522d0726 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 27 Jun 2013 19:09:12 +0200 Subject: [PATCH 03/16] make ubus handling use uloop timers Signed-off-by: John Crispin --- ubus.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/ubus.c b/ubus.c index 202646a..54ead33 100644 --- a/ubus.c +++ b/ubus.c @@ -23,6 +23,7 @@ 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 void procd_ubus_connection_lost(struct ubus_context *old_ctx); @@ -67,9 +68,9 @@ static void procd_ubus_try_connect(void) 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"); return; } @@ -82,20 +83,25 @@ static void procd_ubus_try_connect(void) ubus_init_log(ctx); } -static void procd_ubus_connection_lost(struct ubus_context *old_ctx) +static void +procd_ubus_reconnect_timer(struct uloop_timeout *timeout) { - if (!reconnect) - return; - procd_ubus_try_connect(); - while (!ubus_connected) { - procd_restart_ubus(); - sleep(1); - procd_ubus_try_connect(); + if (ubus_connected) { + DEBUG(1, "Connected to ubus, id=%08x\n", ctx->local_id); + ubus_add_uloop(ctx); + return; } - DEBUG(1, "Connected to ubus, id=%08x\n", ctx->local_id); - ubus_add_uloop(ctx); + uloop_timeout_set(&retry, 1000); + procd_restart_ubus(); +} + +static void procd_ubus_connection_lost(struct ubus_context *old_ctx) +{ + retry.cb = procd_ubus_reconnect_timer; + procd_restart_ubus(); + uloop_timeout_set(&retry, 1000); } void procd_connect_ubus(void) -- 2.11.0 From 0d44f0de9eacf687182ebebfcb1f9496dbf3bc32 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 27 Jun 2013 19:14:03 +0200 Subject: [PATCH 04/16] get rid of sleep() calls Signed-off-by: John Crispin --- signal.c | 1 - state.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/signal.c b/signal.c index b62da0a..0bb881e 100644 --- a/signal.c +++ b/signal.c @@ -26,7 +26,6 @@ static void do_reboot(void) LOG("reboot\n"); fflush(stderr); sync(); - sleep(1); reboot(RB_AUTOBOOT); while (1) ; diff --git a/state.c b/state.c index 618d758..cf0c81d 100644 --- a/state.c +++ b/state.c @@ -63,12 +63,11 @@ static void state_enter(void) case STATE_SHUTDOWN: LOG("- shutdown -\n"); procd_inittab_run("shutdown"); + sync(); break; case STATE_HALT: LOG("- reboot -\n"); - sync(); - sleep(1); reboot(reboot_event); break; -- 2.11.0 From d2f216cddaf4ee73117bb2693fc53b77f67517ad Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sat, 29 Jun 2013 22:19:04 +0200 Subject: [PATCH 05/16] add support for the system.event node Signed-off-by: John Crispin --- system.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/system.c b/system.c index c96cb63..e1f4a64 100644 --- a/system.c +++ b/system.c @@ -259,11 +259,41 @@ static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj, return 0; } +enum { + EVENT_TYPE, + EVENT_DATA, + __EVENT_MAX +}; + +static const struct blobmsg_policy event_policy[__WDT_MAX] = { + [EVENT_TYPE] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 }, + [EVENT_DATA] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 }, +}; + +static int system_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]) + return UBUS_STATUS_INVALID_ARGUMENT; + + fprintf(stderr, "%s\n", blobmsg_get_string(tb[EVENT_TYPE])); + + return 0; +} + static const struct ubus_method system_methods[] = { UBUS_METHOD_NOARG("board", system_board), UBUS_METHOD_NOARG("info", system_info), UBUS_METHOD_NOARG("upgrade", system_upgrade), UBUS_METHOD("watchdog", watchdog_set, watchdog_policy), + UBUS_METHOD("event", system_event, event_policy), }; static struct ubus_object_type system_object_type = -- 2.11.0 From a2953547e345eb6dd4e48474ca1e48082d3d564f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 3 Jul 2013 15:55:26 +0200 Subject: [PATCH 06/16] add 2 second sleep before rebooting stderr is lost without this whena crash happens Signed-off-by: John Crispin --- signal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/signal.c b/signal.c index 0bb881e..ebaf7bc 100644 --- a/signal.c +++ b/signal.c @@ -26,6 +26,7 @@ static void do_reboot(void) LOG("reboot\n"); fflush(stderr); sync(); + sleep(2); reboot(RB_AUTOBOOT); while (1) ; -- 2.11.0 From 4ec2545ed6a0626715802a1c7919fe0e38436301 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 3 Jul 2013 19:16:09 +0200 Subject: [PATCH 07/16] properly fix umask handling https://dev.openwrt.org/ticket/13752 https://dev.openwrt.org/ticket/13794 Signed-off-by: John Crispin --- hotplug.c | 2 ++ main.c | 1 - mkdev.c | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hotplug.c b/hotplug.c index c2276ed..21efcf5 100644 --- a/hotplug.c +++ b/hotplug.c @@ -80,6 +80,7 @@ static void mkdir_p(char *dir) 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 }, @@ -104,6 +105,7 @@ static void handle_makedev(struct blob_attr *msg, struct blob_attr *data) 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) diff --git a/main.c b/main.c index 71531e3..6610c37 100644 --- a/main.c +++ b/main.c @@ -68,7 +68,6 @@ int main(int argc, char **argv) return usage(argv[0]); } } - umask(0); uloop_init(); procd_signal(); if (getpid() != 1) diff --git a/mkdev.c b/mkdev.c index d507939..0f55554 100644 --- a/mkdev.c +++ b/mkdev.c @@ -47,12 +47,15 @@ static bool find_pattern(const char *name) 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) -- 2.11.0 From 3283d681f9da65b500ad0cb3d4550cb840ed481a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 1 Jul 2013 18:34:13 +0200 Subject: [PATCH 08/16] add trigger support Signed-off-by: John Crispin --- CMakeLists.txt | 2 +- instance.c | 12 ++- instance.h | 3 +- main.c | 1 + procd.h | 6 ++ service.c | 87 ++++++++++++++- service.h | 1 + system.c | 30 ------ trigger.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 439 insertions(+), 37 deletions(-) create mode 100644 trigger.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d3a989..b174a15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ IF(APPLE) LINK_DIRECTORIES(/opt/local/lib) ENDIF() -SET(SOURCES main.c ubus.c service.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) +SET(SOURCES main.c ubus.c service.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) find_library(json NAMES json-c json) SET(LIBS ubox ubus ${json} blobmsg_json json_script) diff --git a/instance.c b/instance.c index a1459b7..b5ecad0 100644 --- a/instance.c +++ b/instance.c @@ -31,6 +31,7 @@ enum { INSTANCE_ATTR_DATA, INSTANCE_ATTR_NETDEV, INSTANCE_ATTR_FILE, + INSTANCE_ATTR_TRIGGER, INSTANCE_ATTR_NICE, __INSTANCE_ATTR_MAX }; @@ -41,6 +42,7 @@ static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = { [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_NICE] = { "nice", BLOBMSG_TYPE_INT32 }, }; @@ -272,7 +274,11 @@ instance_config_parse(struct service_instance *in) return false; in->command = cur; + in->trigger = tb[INSTANCE_ATTR_TRIGGER]; + if (in->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) @@ -309,6 +315,7 @@ instance_config_move(struct service_instance *in, struct service_instance *in_sr 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; @@ -344,6 +351,7 @@ instance_free(struct service_instance *in) { uloop_process_delete(&in->proc); uloop_timeout_cancel(&in->timeout); + trigger_del(in); instance_config_cleanup(in); free(in->config); free(in); @@ -366,7 +374,7 @@ instance_init(struct service_instance *in, struct service *s, struct blob_attr * in->valid = instance_config_parse(in); } -void instance_dump(struct blob_buf *b, struct service_instance *in) +void instance_dump(struct blob_buf *b, struct service_instance *in, int verbose) { void *i; @@ -375,5 +383,7 @@ void instance_dump(struct blob_buf *b, struct service_instance *in) if (in->proc.pending) blobmsg_add_u32(b, "pid", in->proc.pid); blobmsg_add_blob(b, in->command); + if (verbose && in->trigger) + blobmsg_add_blob(b, in->trigger); blobmsg_close_table(b, i); } diff --git a/instance.h b/instance.h index 530041c..ceae834 100644 --- a/instance.h +++ b/instance.h @@ -33,6 +33,7 @@ struct service_instance { struct uloop_timeout timeout; struct blob_attr *command; + struct blob_attr *trigger; struct blobmsg_list env; struct blobmsg_list data; struct blobmsg_list netdev; @@ -44,6 +45,6 @@ void instance_stop(struct service_instance *in, bool restart); 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); +void instance_dump(struct blob_buf *b, struct service_instance *in, int debug); #endif diff --git a/main.c b/main.c index 6610c37..a1b4fdb 100644 --- a/main.c +++ b/main.c @@ -70,6 +70,7 @@ int main(int argc, char **argv) } uloop_init(); procd_signal(); + trigger_init(); if (getpid() != 1) procd_connect_ubus(); else diff --git a/procd.h b/procd.h index 7f6c7cb..4fd45f2 100644 --- a/procd.h +++ b/procd.h @@ -69,4 +69,10 @@ 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); + #endif diff --git a/service.c b/service.c index 8083e93..7db1c4a 100644 --- a/service.c +++ b/service.c @@ -83,6 +83,7 @@ enum { SERVICE_SET_NAME, SERVICE_SET_SCRIPT, SERVICE_SET_INSTANCES, + SERVICE_SET_TRIGGER, __SERVICE_SET_MAX }; @@ -90,15 +91,17 @@ 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 }, }; - static int service_update(struct service *s, struct blob_attr *config, struct blob_attr **tb, bool add) { struct blob_attr *cur; int rem; + s->trigger = tb[SERVICE_SET_TRIGGER]; + if (tb[SERVICE_SET_INSTANCES]) { if (!add) vlist_update(&s->instances); @@ -141,6 +144,25 @@ static const struct blobmsg_policy service_del_attrs[__SERVICE_DEL_ATTR_MAX] = { [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_INT32 }, +}; + +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 }, +}; static int service_handle_set(struct ubus_context *ctx, struct ubus_object *obj, @@ -185,15 +207,17 @@ free: } static void -service_dump(struct service *s) +service_dump(struct service *s, int verbose) { struct service_instance *in; void *c, *i; c = blobmsg_open_table(&b, s->name); i = blobmsg_open_table(&b, "instances"); + if (verbose && s->trigger) + blobmsg_add_blob(&b, s->trigger); vlist_for_each_element(&s->instances, in, node) - instance_dump(&b, in); + instance_dump(&b, in, verbose); blobmsg_close_table(&b, i); blobmsg_close_table(&b, c); } @@ -203,11 +227,18 @@ 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_u32(tb[SERVICE_LIST_ATTR_VERBOSE])) + verbose = 1; blob_buf_init(&b, 0); avl_for_each_element(&services, s, avl) - service_dump(s); + service_dump(s, verbose); ubus_send_reply(ctx, req, b.head); @@ -279,6 +310,52 @@ service_handle_update(struct ubus_context *ctx, struct ubus_object *obj, 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; +} + +enum { + TRIGGER_ATTR, + __TRIGGER_MAX +}; + +static const struct blobmsg_policy trigger_policy[__TRIGGER_MAX] = { + [TRIGGER_ATTR] = { .name = "triggers", .type = BLOBMSG_TYPE_ARRAY }, +}; + +static int service_handle_trigger(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__TRIGGER_MAX]; + + if (!msg) + return UBUS_STATUS_INVALID_ARGUMENT; + + blobmsg_parse(trigger_policy, __TRIGGER_MAX, tb, blob_data(msg), blob_len(msg)); + if (!tb[TRIGGER_ATTR]) + return UBUS_STATUS_INVALID_ARGUMENT; + + trigger_add(tb[TRIGGER_ATTR], NULL); + + 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), @@ -286,6 +363,8 @@ static struct ubus_method main_object_methods[] = { 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("trigger", service_handle_trigger, trigger_policy), }; static struct ubus_object_type main_object_type = diff --git a/service.h b/service.h index 4e68eed..64c8a2f 100644 --- a/service.h +++ b/service.h @@ -25,6 +25,7 @@ struct service { const char *name; struct blob_attr *config; + struct blob_attr *trigger; struct vlist_tree instances; }; diff --git a/system.c b/system.c index e1f4a64..c96cb63 100644 --- a/system.c +++ b/system.c @@ -259,41 +259,11 @@ static int watchdog_set(struct ubus_context *ctx, struct ubus_object *obj, return 0; } -enum { - EVENT_TYPE, - EVENT_DATA, - __EVENT_MAX -}; - -static const struct blobmsg_policy event_policy[__WDT_MAX] = { - [EVENT_TYPE] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 }, - [EVENT_DATA] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 }, -}; - -static int system_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]) - return UBUS_STATUS_INVALID_ARGUMENT; - - fprintf(stderr, "%s\n", blobmsg_get_string(tb[EVENT_TYPE])); - - return 0; -} - static const struct ubus_method system_methods[] = { UBUS_METHOD_NOARG("board", system_board), UBUS_METHOD_NOARG("info", system_info), UBUS_METHOD_NOARG("upgrade", system_upgrade), UBUS_METHOD("watchdog", watchdog_set, watchdog_policy), - UBUS_METHOD("event", system_event, event_policy), }; static struct ubus_object_type system_object_type = diff --git a/trigger.c b/trigger.c new file mode 100644 index 0000000..fff1855 --- /dev/null +++ b/trigger.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "procd.h" + +struct trigger { + struct list_head list; + + char *type; + + int pending; + int remove; + int timeout; + + void *id; + + struct blob_attr *rule; + struct blob_attr *data; + struct uloop_timeout delay; + + struct json_script_ctx jctx; +}; + +struct job; +struct cmd { + char *name; + void (*handler)(struct job *job, struct blob_attr *exec, struct blob_attr *env); +}; + +struct job { + struct runqueue_process proc; + struct cmd *cmd; + struct trigger *trigger; + struct blob_attr *exec; + struct blob_attr *env; +}; + +static LIST_HEAD(triggers); +static struct runqueue q; + +static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) +{ + return NULL; +} + +static struct json_script_file * +rule_load_script(struct json_script_ctx *ctx, const char *name) +{ + struct trigger *t = container_of(ctx, struct trigger, jctx); + + return json_script_file_from_blobmsg(t->type, t->rule, blob_pad_len(t->rule)); +} + +static void q_job_run(struct runqueue *q, struct runqueue_task *t) +{ + struct job *j = container_of(t, struct job, proc.task); + + LOG("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); + } + } + } +} -- 2.11.0 From cac289316bed4282f798dbe710398e6f53f66f1f Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Fri, 5 Jul 2013 12:04:14 +0200 Subject: [PATCH 09/16] bugfix of error handling while open() The normal return value from open() is a non-negative integer. In the case of an error, a value of -1 is returned instead. Signed-off-by: Thomas Huehn --- debug.c | 2 +- inittab.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debug.c b/debug.c index 7d7798b..91e874f 100644 --- a/debug.c +++ b/debug.c @@ -29,7 +29,7 @@ void debug_init(void) regex_t pat_cmdline; regmatch_t matches[2]; - if (!fd) + if (fd < 0) return; r = read(fd, line, sizeof(line) - 1); diff --git a/inittab.c b/inittab.c index 686d389..d73e0b8 100644 --- a/inittab.c +++ b/inittab.c @@ -139,7 +139,7 @@ static void askconsole(struct init_action *a) regex_t pat_cmdline; regmatch_t matches[2]; - if (!fd) + if (fd < 0) return; r = read(fd, line, sizeof(line) - 1); -- 2.11.0 From 725e6bbbe403a950c0ddcd23cb521ffefa705c7d Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 8 Jul 2013 13:10:47 +0200 Subject: [PATCH 10/16] extend logread * log to file * add pidfile support * reconnect when logging over network --- logread.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 107 insertions(+), 19 deletions(-) diff --git a/logread.c b/logread.c index 5d1be68..fc1d0ce 100644 --- a/logread.c +++ b/logread.c @@ -12,9 +12,15 @@ * GNU General Public License for more details. */ +#include +#include + +#include #include -#include #include +#include +#include +#include #define SYSLOG_NAMES #include @@ -25,6 +31,12 @@ #include "libubus.h" enum { + LOG_STDOUT, + LOG_FILE, + LOG_NET, +}; + +enum { LOG_MSG, LOG_ID, LOG_PRIO, @@ -41,14 +53,24 @@ static const struct blobmsg_policy log_policy[] = { [LOG_TIME] = { .name = "time", .type = BLOBMSG_TYPE_INT64 }, }; -enum { - WATCH_ID, - WATCH_COUNTER, - __WATCH_MAX -}; - 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, *pid_file; +static int log_type = LOG_STDOUT; +static int log_size; + +static void log_handle_reconnect(struct uloop_timeout *timeout) +{ + sender.fd = usock(USOCK_TCP | USOCK_NUMERIC, 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) @@ -56,21 +78,51 @@ static void log_handle_remove(struct ubus_context *ctx, struct ubus_subscriber * 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[256]; 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_TRUNC); + 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]); @@ -80,17 +132,33 @@ static int log_notify(struct ubus_context *ctx, struct ubus_object *obj, c, facilitynames[LOG_FAC(p)].c_name, prioritynames[LOG_PRI(p)].c_name, (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"), method); - write(sender.fd, buf, strlen(buf)); + if (log_type == LOG_NET) + send(sender.fd, buf, strlen(buf), 0); + else + 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, const char *url, const char *port) +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); @@ -104,13 +172,18 @@ static void follow_log(struct ubus_context *ctx, int id, const char *url, const if (ret) fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); - if (url && port) { - sender.fd = usock(USOCK_TCP | USOCK_NUMERIC, url, port); + 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); if (sender.fd < 0) { - fprintf(stderr, "failed to connect: %s\n", strerror(errno)); + fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno)); exit(-1); - } else { - uloop_fd_add(&sender, ULOOP_READ); } } else { sender.fd = STDOUT_FILENO; @@ -174,6 +247,9 @@ static int usage(const char *prog) " -s Path to ubus socket\n" " -l Got only the last 'count' messages\n" " -r Stream message to a server\n" + " -F Log file\n" + " -S Log size\n" + " -p PID file\n" " -f Follow log messages\n" "\n", prog); return 1; @@ -183,18 +259,24 @@ int main(int argc, char **argv) { struct ubus_context *ctx; uint32_t id; - const char *ubus_socket = NULL, *url = NULL, *port = NULL; + const char *ubus_socket = NULL; int ch, ret, subscribe = 0, lines = 0; static struct blob_buf b; - while ((ch = getopt(argc, argv, "fs:l:r:")) != -1) { + while ((ch = getopt(argc, argv, "fcs:l:r:F:p:S:")) != -1) { switch (ch) { case 's': ubus_socket = optarg; break; case 'r': - url = optarg++; - port = argv[optind++]; + log_ip = optarg++; + log_port = argv[optind++]; + break; + case 'F': + log_file = optarg; + break; + case 'p': + pid_file = optarg; break; case 'f': subscribe = 1; @@ -202,6 +284,12 @@ int main(int argc, char **argv) case 'l': lines = atoi(optarg); break; + case 'S': + log_size = atoi(optarg); + if (log_size < 1) + log_size = 1; + log_size *= 1024; + break; default: return usage(*argv); } @@ -225,7 +313,7 @@ int main(int argc, char **argv) } if (subscribe) - follow_log(ctx, id, url, port); + follow_log(ctx, id); return 0; } -- 2.11.0 From 9f8c266ba9a7232710b492974750cdbf0edf0bfe Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 8 Jul 2013 18:40:09 +0200 Subject: [PATCH 11/16] remove superflous define Signed-off-by: John Crispin --- system.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/system.c b/system.c index c96cb63..7ca4360 100644 --- a/system.c +++ b/system.c @@ -27,8 +27,6 @@ #include "watchdog.h" #include "hotplug.h" -#define HOSTNAME_PATH "/proc/sys/kernel/hostname" - static struct blob_buf b; static int system_board(struct ubus_context *ctx, struct ubus_object *obj, -- 2.11.0 From 8643c5876241474be551980e51740f2dcb8046bd Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 8 Jul 2013 21:40:14 +0200 Subject: [PATCH 12/16] close stdin/out/err before forking an instance Signed-off-by: John Crispin --- instance.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/instance.c b/instance.c index b5ecad0..28e5c94 100644 --- a/instance.c +++ b/instance.c @@ -81,6 +81,9 @@ instance_run(struct service_instance *in) argv[argc++] = blobmsg_data(cur); argv[argc] = NULL; + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); execvp(argv[0], argv); exit(127); } -- 2.11.0 From 5bf4764d977d35c2075fc183bd75ef5b28a03cb4 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 8 Jul 2013 22:12:28 +0200 Subject: [PATCH 13/16] dont list services that have no instances Signed-off-by: John Crispin --- service.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service.c b/service.c index 7db1c4a..ffbc584 100644 --- a/service.c +++ b/service.c @@ -212,6 +212,9 @@ service_dump(struct service *s, int verbose) struct service_instance *in; void *c, *i; + if (avl_is_empty(&s->instances.avl)) + return; + c = blobmsg_open_table(&b, s->name); i = blobmsg_open_table(&b, "instances"); if (verbose && s->trigger) -- 2.11.0 From 8e1c60874f0ec195340b52953311e1521c3a063e Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 9 Jul 2013 11:49:55 +0200 Subject: [PATCH 14/16] fix up the loging over network Signed-off-by: John Crispin --- logread.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/logread.c b/logread.c index fc1d0ce..ba12f44 100644 --- a/logread.c +++ b/logread.c @@ -58,11 +58,11 @@ static struct uloop_timeout retry; static struct uloop_fd sender; static const char *log_file, *log_ip, *log_port, *pid_file; static int log_type = LOG_STDOUT; -static int log_size; +static int log_size, log_udp; static void log_handle_reconnect(struct uloop_timeout *timeout) { - sender.fd = usock(USOCK_TCP | USOCK_NUMERIC, log_ip, log_port); + 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); @@ -128,14 +128,18 @@ static int log_notify(struct ubus_context *ctx, struct ubus_object *obj, p = blobmsg_get_u32(tb[LOG_PRIO]); c[strlen(c) - 1] = '\0'; str = blobmsg_format_json(msg, true); - snprintf(buf, sizeof(buf), "%s %s.%s%s %s\n", - c, facilitynames[LOG_FAC(p)].c_name, prioritynames[LOG_PRI(p)].c_name, - (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"), - method); - if (log_type == LOG_NET) + if (log_type == LOG_NET) { + snprintf(buf, sizeof(buf), "%s%s\n", + (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : ("kernel: "), + method); send(sender.fd, buf, strlen(buf), 0); - else + } else { + snprintf(buf, sizeof(buf), "%s %s.%s%s %s\n", + c, facilitynames[LOG_FAC(p)].c_name, prioritynames[LOG_PRI(p)].c_name, + (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"), + method); write(sender.fd, buf, strlen(buf)); + } free(str); if (log_type == LOG_FILE) @@ -251,6 +255,7 @@ static int usage(const char *prog) " -S Log size\n" " -p PID file\n" " -f Follow log messages\n" + " -u Use UDP as the protocol\n" "\n", prog); return 1; } @@ -263,8 +268,11 @@ int main(int argc, char **argv) int ch, ret, subscribe = 0, lines = 0; static struct blob_buf b; - while ((ch = getopt(argc, argv, "fcs:l:r:F:p:S:")) != -1) { + while ((ch = getopt(argc, argv, "ufcs:l:r:F:p:S:")) != -1) { switch (ch) { + case 'u': + log_udp = 1; + break; case 's': ubus_socket = optarg; break; -- 2.11.0 From 84c114b0756eaff7caf2693c61bdc307a080558c Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 9 Jul 2013 14:33:14 +0200 Subject: [PATCH 15/16] Fix logread file logging This patch fixes logread and now appends to files instead of writting to the beginning the specified logfile. It also sets the access rights to 0600. Signed-off-by: Peter Wagner --- logread.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logread.c b/logread.c index ba12f44..16af3ef 100644 --- a/logread.c +++ b/logread.c @@ -116,7 +116,7 @@ static int log_notify(struct ubus_context *ctx, struct ubus_object *obj, rename(log_file, old); free(old); } - sender.fd = open(log_file, O_CREAT | O_WRONLY | O_TRUNC); + 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); @@ -184,7 +184,7 @@ static void follow_log(struct ubus_context *ctx, int id) uloop_timeout_set(&retry, 1000); } else if (log_file) { log_type = LOG_FILE; - sender.fd = open(log_file, O_CREAT | O_WRONLY); + 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); -- 2.11.0 From d8695bda1728d205fd76a4a5f8b329de418928dc Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 9 Jul 2013 17:23:33 +0200 Subject: [PATCH 16/16] the delete handle should return an error if no service is named instead of killing all services Signed-off-by: John Crispin --- service.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/service.c b/service.c index ffbc584..b5f4f3e 100644 --- a/service.c +++ b/service.c @@ -254,17 +254,14 @@ service_handle_delete(struct ubus_context *ctx, struct ubus_object *obj, struct blob_attr *msg) { struct blob_attr *tb[__SERVICE_DEL_ATTR_MAX], *cur; - struct service *s, *tmp; + 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) { - avl_for_each_element_safe(&services, s, avl, tmp) - service_delete(s); - return 0; - } + if (!cur) + return UBUS_STATUS_NOT_FOUND; s = avl_find_element(&services, blobmsg_data(cur), s, avl); if (!s) -- 2.11.0