1 /* 2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org> 3 * Copyright (C) 2013 John Crispin <blogic@openwrt.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public License version 2.1 7 * as published by the Free Software Foundation 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 #include <sys/stat.h> 16 #include <sys/socket.h> 17 #include <sys/types.h> 18 19 #include <libubox/blobmsg_json.h> 20 #include <libubox/json_script.h> 21 #include <libubox/runqueue.h> 22 #include <libubox/ustream.h> 23 #include <libubox/uloop.h> 24 #include <libubox/avl.h> 25 #include <libubox/avl-cmp.h> 26 27 #include <fcntl.h> 28 #include <unistd.h> 29 #include <stdlib.h> 30 #include <libgen.h> 31 32 #include "../procd.h" 33 34 struct trigger { 35 struct list_head list; 36 37 void *id; 38 char *type; 39 int timeout; 40 41 struct blob_attr *rule; 42 struct blob_attr *data; 43 44 struct json_script_ctx jctx; 45 }; 46 47 struct trigger_command { 48 struct avl_node avl; 49 struct uloop_timeout delay; 50 bool requeue; 51 52 struct runqueue_process proc; 53 struct json_script_ctx jctx; 54 55 struct blob_attr data[]; 56 }; 57 58 static LIST_HEAD(triggers); 59 static RUNQUEUE(q, 1); 60 static AVL_TREE(trigger_pending, avl_blobcmp, false, NULL); 61 62 static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) 63 { 64 return NULL; 65 } 66 67 static struct json_script_file * 68 rule_load_script(struct json_script_ctx *ctx, const char *name) 69 { 70 struct trigger *t = container_of(ctx, struct trigger, jctx); 71 72 if (strcmp(name, t->type) != 0) 73 return NULL; 74 75 return json_script_file_from_blobmsg(t->type, t->rule, blob_pad_len(t->rule)); 76 } 77 78 static void trigger_free(struct trigger *t) 79 { 80 json_script_free(&t->jctx); 81 free(t->data); 82 list_del(&t->list); 83 free(t); 84 } 85 86 static void trigger_command_complete(struct runqueue *q, struct runqueue_task *p) 87 { 88 struct trigger_command *cmd = container_of(p, struct trigger_command, proc.task); 89 90 if (cmd->requeue) { 91 cmd->requeue = false; 92 runqueue_task_add(q, p, false); 93 return; 94 } 95 96 avl_delete(&trigger_pending, &cmd->avl); 97 free(cmd); 98 } 99 100 static void trigger_command_run(struct runqueue *q, struct runqueue_task *t) 101 { 102 struct trigger_command *cmd = container_of(t, struct trigger_command, proc.task); 103 struct blob_attr *cur; 104 char **argv; 105 pid_t pid; 106 int n = 0; 107 int rem; 108 int fd; 109 110 pid = fork(); 111 if (pid < 0) { 112 trigger_command_complete(q, t); 113 return; 114 } 115 116 if (pid) { 117 runqueue_process_add(q, &cmd->proc, pid); 118 return; 119 } 120 121 if (debug < 3 && (fd = open("/dev/null", O_RDWR)) >= 0) { 122 dup2(fd, STDIN_FILENO); 123 dup2(fd, STDOUT_FILENO); 124 dup2(fd, STDERR_FILENO); 125 if (fd > STDERR_FILENO) 126 close(fd); 127 } 128 129 blobmsg_for_each_attr(cur, cmd->data, rem) 130 n++; 131 132 argv = alloca((n + 1) * sizeof(*argv)); 133 n = 0; 134 blobmsg_for_each_attr(cur, cmd->data, rem) 135 argv[n++] = blobmsg_get_string(cur); 136 argv[n] = NULL; 137 138 if (n > 0) 139 execvp(argv[0], &argv[0]); 140 141 exit(1); 142 } 143 144 static void trigger_command_start(struct uloop_timeout *timeout) 145 { 146 static const struct runqueue_task_type trigger_command_type = { 147 .run = trigger_command_run, 148 .cancel = runqueue_process_cancel_cb, 149 .kill = runqueue_process_kill_cb, 150 }; 151 struct trigger_command *cmd = container_of(timeout, struct trigger_command, delay); 152 153 cmd->proc.task.type = &trigger_command_type; 154 cmd->proc.task.complete = trigger_command_complete; 155 runqueue_task_add(&q, &cmd->proc.task, false); 156 } 157 158 static void trigger_command_add(struct trigger *t, struct blob_attr *data) 159 { 160 struct trigger_command *cmd; 161 int64_t remaining; 162 163 cmd = avl_find_element(&trigger_pending, data, cmd, avl); 164 if (cmd) { 165 /* Command currently running? */ 166 if (!cmd->delay.pending) { 167 cmd->requeue = true; 168 return; 169 } 170 171 /* Extend timer if trigger timeout is bigger than remaining time */ 172 remaining = uloop_timeout_remaining64(&cmd->delay); 173 if (remaining < t->timeout) 174 uloop_timeout_set(&cmd->delay, t->timeout); 175 176 return; 177 } 178 179 cmd = calloc(1, sizeof(*cmd) + blob_pad_len(data)); 180 if (!cmd) 181 return; 182 183 cmd->avl.key = cmd->data; 184 cmd->delay.cb = trigger_command_start; 185 memcpy(cmd->data, data, blob_pad_len(data)); 186 avl_insert(&trigger_pending, &cmd->avl); 187 uloop_timeout_set(&cmd->delay, t->timeout > 0 ? t->timeout : 1); 188 } 189 190 static void rule_handle_command(struct json_script_ctx *ctx, const char *name, 191 struct blob_attr *exec, struct blob_attr *vars) 192 { 193 struct trigger *t = container_of(ctx, struct trigger, jctx); 194 195 if (!strcmp(name, "run_script")) { 196 trigger_command_add(t, exec); 197 return; 198 } 199 } 200 201 static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, 202 struct blob_attr *context) 203 { 204 char *s; 205 206 s = blobmsg_format_json(context, false); 207 ERROR("ERROR: %s in block: %s\n", msg, s); 208 free(s); 209 } 210 211 static struct trigger* _trigger_add(char *type, struct blob_attr *rule, int timeout, void *id) 212 { 213 char *_t; 214 struct blob_attr *_r; 215 struct trigger *t = calloc_a(sizeof(*t), &_t, strlen(type) + 1, &_r, blob_pad_len(rule)); 216 217 t->type = _t; 218 t->rule = _r; 219 t->timeout = timeout; 220 t->id = id; 221 t->jctx.handle_var = rule_handle_var, 222 t->jctx.handle_error = rule_handle_error, 223 t->jctx.handle_command = rule_handle_command, 224 t->jctx.handle_file = rule_load_script, 225 226 strcpy(t->type, type); 227 memcpy(t->rule, rule, blob_pad_len(rule)); 228 229 list_add(&t->list, &triggers); 230 json_script_init(&t->jctx); 231 232 return t; 233 } 234 235 void trigger_add(struct blob_attr *rule, void *id) 236 { 237 struct blob_attr *cur; 238 int rem; 239 240 blobmsg_for_each_attr(cur, rule, rem) { 241 struct blob_attr *_cur, *type = NULL, *script = NULL, *timeout = NULL; 242 int _rem; 243 int i = 0; 244 245 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY) 246 continue; 247 248 blobmsg_for_each_attr(_cur, cur, _rem) { 249 switch (i++) { 250 case 0: 251 if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) 252 type = _cur; 253 break; 254 255 case 1: 256 if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY) 257 script = _cur; 258 break; 259 260 case 2: 261 if (blobmsg_type(_cur) == BLOBMSG_TYPE_INT32) 262 timeout = _cur; 263 break; 264 } 265 } 266 267 if (type && script) { 268 int t = 0; 269 270 if (timeout) 271 t = blobmsg_get_u32(timeout); 272 _trigger_add(blobmsg_get_string(type), script, t, id); 273 } 274 } 275 } 276 277 void trigger_del(void *id) 278 { 279 struct trigger *t, *n; 280 281 list_for_each_entry_safe(t, n, &triggers, list) { 282 if (t->id != id) 283 continue; 284 285 trigger_free(t); 286 } 287 } 288 289 static bool trigger_match(const char *event, const char *match) 290 { 291 char *wildcard = strstr(match, ".*"); 292 if (wildcard) 293 return !strncmp(event, match, wildcard - match); 294 return !strcmp(event, match); 295 } 296 297 void trigger_event(const char *type, struct blob_attr *data) 298 { 299 struct trigger *t; 300 301 list_for_each_entry(t, &triggers, list) { 302 if (!trigger_match(type, t->type)) 303 continue; 304 json_script_run(&t->jctx, t->type, data); 305 } 306 } 307
This page was automatically generated by LXR 0.3.1. • OpenWrt