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 109 pid = fork(); 110 if (pid < 0) { 111 trigger_command_complete(q, t); 112 return; 113 } 114 115 if (pid) { 116 runqueue_process_add(q, &cmd->proc, pid); 117 return; 118 } 119 120 if (debug < 3) { 121 close(STDIN_FILENO); 122 close(STDOUT_FILENO); 123 close(STDERR_FILENO); 124 } 125 126 blobmsg_for_each_attr(cur, cmd->data, rem) 127 n++; 128 129 argv = alloca((n + 1) * sizeof(*argv)); 130 n = 0; 131 blobmsg_for_each_attr(cur, cmd->data, rem) 132 argv[n++] = blobmsg_get_string(cur); 133 argv[n] = NULL; 134 135 if (n > 0) 136 execvp(argv[0], &argv[0]); 137 138 exit(1); 139 } 140 141 static void trigger_command_start(struct uloop_timeout *timeout) 142 { 143 static const struct runqueue_task_type trigger_command_type = { 144 .run = trigger_command_run, 145 .cancel = runqueue_process_cancel_cb, 146 .kill = runqueue_process_kill_cb, 147 }; 148 struct trigger_command *cmd = container_of(timeout, struct trigger_command, delay); 149 150 cmd->proc.task.type = &trigger_command_type; 151 cmd->proc.task.complete = trigger_command_complete; 152 runqueue_task_add(&q, &cmd->proc.task, false); 153 } 154 155 static void trigger_command_add(struct trigger *t, struct blob_attr *data) 156 { 157 struct trigger_command *cmd; 158 int64_t remaining; 159 160 cmd = avl_find_element(&trigger_pending, data, cmd, avl); 161 if (cmd) { 162 /* Command currently running? */ 163 if (!cmd->delay.pending) { 164 cmd->requeue = true; 165 return; 166 } 167 168 /* Extend timer if trigger timeout is bigger than remaining time */ 169 remaining = uloop_timeout_remaining64(&cmd->delay); 170 if (remaining < t->timeout) 171 uloop_timeout_set(&cmd->delay, t->timeout); 172 173 return; 174 } 175 176 cmd = calloc(1, sizeof(*cmd) + blob_pad_len(data)); 177 if (!cmd) 178 return; 179 180 cmd->avl.key = cmd->data; 181 cmd->delay.cb = trigger_command_start; 182 memcpy(cmd->data, data, blob_pad_len(data)); 183 avl_insert(&trigger_pending, &cmd->avl); 184 uloop_timeout_set(&cmd->delay, t->timeout > 0 ? t->timeout : 1); 185 } 186 187 static void rule_handle_command(struct json_script_ctx *ctx, const char *name, 188 struct blob_attr *exec, struct blob_attr *vars) 189 { 190 struct trigger *t = container_of(ctx, struct trigger, jctx); 191 192 if (!strcmp(name, "run_script")) { 193 trigger_command_add(t, exec); 194 return; 195 } 196 } 197 198 static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, 199 struct blob_attr *context) 200 { 201 char *s; 202 203 s = blobmsg_format_json(context, false); 204 ERROR("ERROR: %s in block: %s\n", msg, s); 205 free(s); 206 } 207 208 static struct trigger* _trigger_add(char *type, struct blob_attr *rule, int timeout, void *id) 209 { 210 char *_t; 211 struct blob_attr *_r; 212 struct trigger *t = calloc_a(sizeof(*t), &_t, strlen(type) + 1, &_r, blob_pad_len(rule)); 213 214 t->type = _t; 215 t->rule = _r; 216 t->timeout = timeout; 217 t->id = id; 218 t->jctx.handle_var = rule_handle_var, 219 t->jctx.handle_error = rule_handle_error, 220 t->jctx.handle_command = rule_handle_command, 221 t->jctx.handle_file = rule_load_script, 222 223 strcpy(t->type, type); 224 memcpy(t->rule, rule, blob_pad_len(rule)); 225 226 list_add(&t->list, &triggers); 227 json_script_init(&t->jctx); 228 229 return t; 230 } 231 232 void trigger_add(struct blob_attr *rule, void *id) 233 { 234 struct blob_attr *cur; 235 int rem; 236 237 blobmsg_for_each_attr(cur, rule, rem) { 238 struct blob_attr *_cur, *type = NULL, *script = NULL, *timeout = NULL; 239 int _rem; 240 int i = 0; 241 242 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY) 243 continue; 244 245 blobmsg_for_each_attr(_cur, cur, _rem) { 246 switch (i++) { 247 case 0: 248 if (blobmsg_type(_cur) == BLOBMSG_TYPE_STRING) 249 type = _cur; 250 break; 251 252 case 1: 253 if (blobmsg_type(_cur) == BLOBMSG_TYPE_ARRAY) 254 script = _cur; 255 break; 256 257 case 2: 258 if (blobmsg_type(_cur) == BLOBMSG_TYPE_INT32) 259 timeout = _cur; 260 break; 261 } 262 } 263 264 if (type && script) { 265 int t = 0; 266 267 if (timeout) 268 t = blobmsg_get_u32(timeout); 269 _trigger_add(blobmsg_get_string(type), script, t, id); 270 } 271 } 272 } 273 274 void trigger_del(void *id) 275 { 276 struct trigger *t, *n; 277 278 list_for_each_entry_safe(t, n, &triggers, list) { 279 if (t->id != id) 280 continue; 281 282 trigger_free(t); 283 } 284 } 285 286 static bool trigger_match(const char *event, const char *match) 287 { 288 char *wildcard = strstr(match, ".*"); 289 if (wildcard) 290 return !strncmp(event, match, wildcard - match); 291 return !strcmp(event, match); 292 } 293 294 void trigger_event(const char *type, struct blob_attr *data) 295 { 296 struct trigger *t; 297 298 list_for_each_entry(t, &triggers, list) { 299 if (!trigger_match(type, t->type)) 300 continue; 301 json_script_run(&t->jctx, t->type, data); 302 } 303 } 304
This page was automatically generated by LXR 0.3.1. • OpenWrt