├── .gitignore ├── CMakeLists.txt ├── avl-cmp.c ├── avl-cmp.h ├── avl.c ├── avl.h ├── blob.c ├── blob.h ├── blobmsg.c ├── blobmsg.h ├── blobmsg_json.c ├── blobmsg_json.h ├── examples ├── CMakeLists.txt ├── blobmsg-example.c ├── runqueue-example.c ├── uloop-example.lua ├── uloop_pid_test.sh └── ustream-example.c ├── jshn.c ├── json_script.c ├── json_script.h ├── list.h ├── lua ├── CMakeLists.txt └── uloop.c ├── md5.c ├── md5.h ├── runqueue.c ├── runqueue.h ├── safe_list.c ├── safe_list.h ├── sh └── jshn.sh ├── uloop.c ├── uloop.h ├── usock.c ├── usock.h ├── ustream-fd.c ├── ustream.c ├── ustream.h ├── utils.c ├── utils.h ├── vlist.c └── vlist.h /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | CMakeCache.txt 3 | CMakeFiles 4 | *.cmake 5 | *.a 6 | *.so 7 | *.dylib 8 | install_manifest.txt 9 | jshn 10 | *-example 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | INCLUDE(CheckLibraryExists) 3 | INCLUDE(CheckFunctionExists) 4 | 5 | PROJECT(ubox C) 6 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) 7 | 8 | OPTION(BUILD_LUA "build Lua plugin" ON) 9 | 10 | IF(APPLE) 11 | INCLUDE_DIRECTORIES(/opt/local/include) 12 | LINK_DIRECTORIES(/opt/local/lib) 13 | ENDIF() 14 | 15 | INCLUDE(FindPkgConfig) 16 | PKG_CHECK_MODULES(JSONC json-c json) 17 | IF(JSONC_FOUND) 18 | ADD_DEFINITIONS(-DJSONC) 19 | INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS}) 20 | ENDIF() 21 | 22 | SET(SOURCES avl.c avl-cmp.c blob.c blobmsg.c uloop.c usock.c ustream.c ustream-fd.c vlist.c utils.c safe_list.c runqueue.c md5.c) 23 | 24 | ADD_LIBRARY(ubox SHARED ${SOURCES}) 25 | 26 | SET(LIBS) 27 | CHECK_FUNCTION_EXISTS(clock_gettime HAVE_GETTIME) 28 | IF(NOT HAVE_GETTIME) 29 | CHECK_LIBRARY_EXISTS(rt clock_gettime "" NEED_GETTIME) 30 | IF(NEED_GETTIME) 31 | TARGET_LINK_LIBRARIES(ubox rt) 32 | ENDIF() 33 | ENDIF() 34 | 35 | FILE(GLOB headers *.h) 36 | INSTALL(FILES ${headers} 37 | DESTINATION include/libubox 38 | ) 39 | INSTALL(TARGETS ubox 40 | LIBRARY DESTINATION lib 41 | ) 42 | 43 | ADD_SUBDIRECTORY(lua) 44 | 45 | find_library(json NAMES json-c json) 46 | IF(EXISTS ${json}) 47 | ADD_LIBRARY(blobmsg_json SHARED blobmsg_json.c) 48 | TARGET_LINK_LIBRARIES(blobmsg_json ubox ${json}) 49 | 50 | ADD_EXECUTABLE(jshn jshn.c) 51 | TARGET_LINK_LIBRARIES(jshn ${json}) 52 | 53 | ADD_LIBRARY(json_script SHARED json_script.c) 54 | TARGET_LINK_LIBRARIES(json_script ubox) 55 | 56 | INSTALL(TARGETS blobmsg_json jshn json_script 57 | LIBRARY DESTINATION lib 58 | RUNTIME DESTINATION bin 59 | ) 60 | 61 | FILE(GLOB scripts sh/*.sh) 62 | INSTALL(FILES ${scripts} 63 | DESTINATION share/libubox 64 | ) 65 | 66 | ENDIF() 67 | -------------------------------------------------------------------------------- /avl-cmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include 17 | #include "avl-cmp.h" 18 | 19 | int 20 | avl_strcmp(const void *k1, const void *k2, void *ptr) 21 | { 22 | return strcmp(k1, k2); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /avl-cmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef __AVL_CMP_H 17 | #define __AVL_CMP_H 18 | 19 | int avl_strcmp(const void *k1, const void *k2, void *ptr); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /avl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PacketBB handler library (see RFC 5444) 3 | * Copyright (c) 2010 Henning Rogge 4 | * Original OLSRd implementation by Hannes Gredler 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in 15 | * the documentation and/or other materials provided with the 16 | * distribution. 17 | * * Neither the name of olsr.org, olsrd nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | * POSSIBILITY OF SUCH DAMAGE. 33 | * 34 | * Visit http://www.olsr.org/git for more information. 35 | * 36 | * If you find this software useful feel free to make a donation 37 | * to the project. For more information see the website or contact 38 | * the copyright holders. 39 | */ 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "avl.h" 48 | #include "list.h" 49 | 50 | /** 51 | * internal type save inline function to calculate the maximum of 52 | * to integers without macro implementation. 53 | * 54 | * @param x first parameter of maximum function 55 | * @param y second parameter of maximum function 56 | * @return largest integer of both parameters 57 | */ 58 | static inline int avl_max(int x, int y) { 59 | return x > y ? x : y; 60 | } 61 | 62 | /** 63 | * internal type save inline function to calculate the minimum of 64 | * to integers without macro implementation. 65 | * 66 | * @param x first parameter of minimum function 67 | * @param y second parameter of minimum function 68 | * @return smallest integer of both parameters 69 | */ 70 | static inline int avl_min(int x, int y) { 71 | return x < y ? x : y; 72 | } 73 | 74 | static struct avl_node * 75 | avl_find_rec(struct avl_node *node, const void *key, avl_tree_comp comp, void *ptr, int *cmp_result); 76 | static void avl_insert_before(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node); 77 | static void avl_insert_after(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node); 78 | static void post_insert(struct avl_tree *tree, struct avl_node *node); 79 | static void avl_delete_worker(struct avl_tree *tree, struct avl_node *node); 80 | static void avl_remove(struct avl_tree *tree, struct avl_node *node); 81 | 82 | /** 83 | * Initialize a new avl_tree struct 84 | * @param tree pointer to avl-tree 85 | * @param comp pointer to comparator for the tree 86 | * @param allow_dups true if the tree allows multiple 87 | * elements with the same 88 | * @param ptr custom parameter for comparator 89 | */ 90 | void 91 | avl_init(struct avl_tree *tree, avl_tree_comp comp, bool allow_dups, void *ptr) 92 | { 93 | INIT_LIST_HEAD(&tree->list_head); 94 | tree->root = NULL; 95 | tree->count = 0; 96 | tree->comp = comp; 97 | tree->allow_dups = allow_dups; 98 | tree->cmp_ptr = ptr; 99 | } 100 | 101 | static inline struct avl_node *avl_next(struct avl_node *node) 102 | { 103 | return list_entry(node->list.next, struct avl_node, list); 104 | } 105 | 106 | /** 107 | * Finds a node in an avl-tree with a certain key 108 | * @param tree pointer to avl-tree 109 | * @param key pointer to key 110 | * @return pointer to avl-node with key, NULL if no node with 111 | * this key exists. 112 | */ 113 | struct avl_node * 114 | avl_find(const struct avl_tree *tree, const void *key) 115 | { 116 | struct avl_node *node; 117 | int diff; 118 | 119 | if (tree->root == NULL) 120 | return NULL; 121 | 122 | node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); 123 | 124 | return diff == 0 ? node : NULL; 125 | } 126 | 127 | /** 128 | * Finds the last node in an avl-tree with a key less or equal 129 | * than the specified key 130 | * @param tree pointer to avl-tree 131 | * @param key pointer to specified key 132 | * @return pointer to avl-node, NULL if no node with 133 | * key less or equal specified key exists. 134 | */ 135 | struct avl_node * 136 | avl_find_lessequal(const struct avl_tree *tree, const void *key) { 137 | struct avl_node *node, *next; 138 | int diff; 139 | 140 | if (tree->root == NULL) 141 | return NULL; 142 | 143 | node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); 144 | 145 | /* go left as long as keylist, &tree->list_head)) { 148 | return NULL; 149 | } 150 | 151 | node = (struct avl_node *)node->list.prev; 152 | diff = (*tree->comp) (key, node->key, tree->cmp_ptr); 153 | } 154 | 155 | /* go right as long as key>=next_node.key */ 156 | next = node; 157 | while (diff >= 0) { 158 | node = next; 159 | if (list_is_last(&node->list, &tree->list_head)) { 160 | break; 161 | } 162 | 163 | next = (struct avl_node *)node->list.next; 164 | diff = (*tree->comp) (key, next->key, tree->cmp_ptr); 165 | } 166 | return node; 167 | } 168 | 169 | /** 170 | * Finds the first node in an avl-tree with a key greater or equal 171 | * than the specified key 172 | * @param tree pointer to avl-tree 173 | * @param key pointer to specified key 174 | * @return pointer to avl-node, NULL if no node with 175 | * key greater or equal specified key exists. 176 | */ 177 | struct avl_node * 178 | avl_find_greaterequal(const struct avl_tree *tree, const void *key) { 179 | struct avl_node *node, *next; 180 | int diff; 181 | 182 | if (tree->root == NULL) 183 | return NULL; 184 | 185 | node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); 186 | 187 | /* go right as long as key>node.key */ 188 | while (diff > 0) { 189 | if (list_is_last(&node->list, &tree->list_head)) { 190 | return NULL; 191 | } 192 | 193 | node = (struct avl_node *)node->list.next; 194 | diff = (*tree->comp) (key, node->key, tree->cmp_ptr); 195 | } 196 | 197 | /* go left as long as key<=next_node.key */ 198 | next = node; 199 | while (diff <= 0) { 200 | node = next; 201 | if (list_is_first(&node->list, &tree->list_head)) { 202 | break; 203 | } 204 | 205 | next = (struct avl_node *)node->list.prev; 206 | diff = (*tree->comp) (key, next->key, tree->cmp_ptr); 207 | } 208 | return node; 209 | } 210 | 211 | /** 212 | * Inserts an avl_node into a tree 213 | * @param tree pointer to tree 214 | * @param new pointer to node 215 | * @return 0 if node was inserted successfully, -1 if it was not inserted 216 | * because of a key collision 217 | */ 218 | int 219 | avl_insert(struct avl_tree *tree, struct avl_node *new) 220 | { 221 | struct avl_node *node, *next, *last; 222 | int diff; 223 | 224 | new->parent = NULL; 225 | 226 | new->left = NULL; 227 | new->right = NULL; 228 | 229 | new->balance = 0; 230 | new->leader = true; 231 | 232 | if (tree->root == NULL) { 233 | list_add(&new->list, &tree->list_head); 234 | tree->root = new; 235 | tree->count = 1; 236 | return 0; 237 | } 238 | 239 | node = avl_find_rec(tree->root, new->key, tree->comp, tree->cmp_ptr, &diff); 240 | 241 | last = node; 242 | 243 | while (!list_is_last(&last->list, &tree->list_head)) { 244 | next = avl_next(last); 245 | if (next->leader) { 246 | break; 247 | } 248 | last = next; 249 | } 250 | 251 | diff = (*tree->comp) (new->key, node->key, tree->cmp_ptr); 252 | 253 | if (diff == 0) { 254 | if (!tree->allow_dups) 255 | return -1; 256 | 257 | new->leader = 0; 258 | 259 | avl_insert_after(tree, last, new); 260 | return 0; 261 | } 262 | 263 | if (node->balance == 1) { 264 | avl_insert_before(tree, node, new); 265 | 266 | node->balance = 0; 267 | new->parent = node; 268 | node->left = new; 269 | return 0; 270 | } 271 | 272 | if (node->balance == -1) { 273 | avl_insert_after(tree, last, new); 274 | 275 | node->balance = 0; 276 | new->parent = node; 277 | node->right = new; 278 | return 0; 279 | } 280 | 281 | if (diff < 0) { 282 | avl_insert_before(tree, node, new); 283 | 284 | node->balance = -1; 285 | new->parent = node; 286 | node->left = new; 287 | post_insert(tree, node); 288 | return 0; 289 | } 290 | 291 | avl_insert_after(tree, last, new); 292 | 293 | node->balance = 1; 294 | new->parent = node; 295 | node->right = new; 296 | post_insert(tree, node); 297 | return 0; 298 | } 299 | 300 | /** 301 | * Remove a node from an avl tree 302 | * @param tree pointer to tree 303 | * @param node pointer to node 304 | */ 305 | void 306 | avl_delete(struct avl_tree *tree, struct avl_node *node) 307 | { 308 | struct avl_node *next; 309 | struct avl_node *parent; 310 | struct avl_node *left; 311 | struct avl_node *right; 312 | if (node->leader) { 313 | if (tree->allow_dups 314 | && !list_is_last(&node->list, &tree->list_head) 315 | && !(next = avl_next(node))->leader) { 316 | next->leader = true; 317 | next->balance = node->balance; 318 | 319 | parent = node->parent; 320 | left = node->left; 321 | right = node->right; 322 | 323 | next->parent = parent; 324 | next->left = left; 325 | next->right = right; 326 | 327 | if (parent == NULL) 328 | tree->root = next; 329 | 330 | else { 331 | if (node == parent->left) 332 | parent->left = next; 333 | 334 | else 335 | parent->right = next; 336 | } 337 | 338 | if (left != NULL) 339 | left->parent = next; 340 | 341 | if (right != NULL) 342 | right->parent = next; 343 | } 344 | 345 | else 346 | avl_delete_worker(tree, node); 347 | } 348 | 349 | avl_remove(tree, node); 350 | } 351 | 352 | static struct avl_node * 353 | avl_find_rec(struct avl_node *node, const void *key, avl_tree_comp comp, void *cmp_ptr, int *cmp_result) 354 | { 355 | int diff; 356 | 357 | diff = (*comp) (key, node->key, cmp_ptr); 358 | *cmp_result = diff; 359 | 360 | if (diff < 0) { 361 | if (node->left != NULL) 362 | return avl_find_rec(node->left, key, comp, cmp_ptr, cmp_result); 363 | 364 | return node; 365 | } 366 | 367 | if (diff > 0) { 368 | if (node->right != NULL) 369 | return avl_find_rec(node->right, key, comp, cmp_ptr, cmp_result); 370 | 371 | return node; 372 | } 373 | 374 | return node; 375 | } 376 | 377 | static void 378 | avl_rotate_right(struct avl_tree *tree, struct avl_node *node) 379 | { 380 | struct avl_node *left, *parent; 381 | 382 | left = node->left; 383 | parent = node->parent; 384 | 385 | left->parent = parent; 386 | node->parent = left; 387 | 388 | if (parent == NULL) 389 | tree->root = left; 390 | 391 | else { 392 | if (parent->left == node) 393 | parent->left = left; 394 | 395 | else 396 | parent->right = left; 397 | } 398 | 399 | node->left = left->right; 400 | left->right = node; 401 | 402 | if (node->left != NULL) 403 | node->left->parent = node; 404 | 405 | node->balance += 1 - avl_min(left->balance, 0); 406 | left->balance += 1 + avl_max(node->balance, 0); 407 | } 408 | 409 | static void 410 | avl_rotate_left(struct avl_tree *tree, struct avl_node *node) 411 | { 412 | struct avl_node *right, *parent; 413 | 414 | right = node->right; 415 | parent = node->parent; 416 | 417 | right->parent = parent; 418 | node->parent = right; 419 | 420 | if (parent == NULL) 421 | tree->root = right; 422 | 423 | else { 424 | if (parent->left == node) 425 | parent->left = right; 426 | 427 | else 428 | parent->right = right; 429 | } 430 | 431 | node->right = right->left; 432 | right->left = node; 433 | 434 | if (node->right != NULL) 435 | node->right->parent = node; 436 | 437 | node->balance -= 1 + avl_max(right->balance, 0); 438 | right->balance -= 1 - avl_min(node->balance, 0); 439 | } 440 | 441 | static void 442 | post_insert(struct avl_tree *tree, struct avl_node *node) 443 | { 444 | struct avl_node *parent = node->parent; 445 | 446 | if (parent == NULL) 447 | return; 448 | 449 | if (node == parent->left) { 450 | parent->balance--; 451 | 452 | if (parent->balance == 0) 453 | return; 454 | 455 | if (parent->balance == -1) { 456 | post_insert(tree, parent); 457 | return; 458 | } 459 | 460 | if (node->balance == -1) { 461 | avl_rotate_right(tree, parent); 462 | return; 463 | } 464 | 465 | avl_rotate_left(tree, node); 466 | avl_rotate_right(tree, node->parent->parent); 467 | return; 468 | } 469 | 470 | parent->balance++; 471 | 472 | if (parent->balance == 0) 473 | return; 474 | 475 | if (parent->balance == 1) { 476 | post_insert(tree, parent); 477 | return; 478 | } 479 | 480 | if (node->balance == 1) { 481 | avl_rotate_left(tree, parent); 482 | return; 483 | } 484 | 485 | avl_rotate_right(tree, node); 486 | avl_rotate_left(tree, node->parent->parent); 487 | } 488 | 489 | static void 490 | avl_insert_before(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node) 491 | { 492 | list_add_tail(&node->list, &pos_node->list); 493 | tree->count++; 494 | } 495 | 496 | static void 497 | avl_insert_after(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node) 498 | { 499 | list_add(&node->list, &pos_node->list); 500 | tree->count++; 501 | } 502 | 503 | static void 504 | avl_remove(struct avl_tree *tree, struct avl_node *node) 505 | { 506 | list_del(&node->list); 507 | tree->count--; 508 | } 509 | 510 | static void 511 | avl_post_delete(struct avl_tree *tree, struct avl_node *node) 512 | { 513 | struct avl_node *parent; 514 | 515 | if ((parent = node->parent) == NULL) 516 | return; 517 | 518 | if (node == parent->left) { 519 | parent->balance++; 520 | 521 | if (parent->balance == 0) { 522 | avl_post_delete(tree, parent); 523 | return; 524 | } 525 | 526 | if (parent->balance == 1) 527 | return; 528 | 529 | if (parent->right->balance == 0) { 530 | avl_rotate_left(tree, parent); 531 | return; 532 | } 533 | 534 | if (parent->right->balance == 1) { 535 | avl_rotate_left(tree, parent); 536 | avl_post_delete(tree, parent->parent); 537 | return; 538 | } 539 | 540 | avl_rotate_right(tree, parent->right); 541 | avl_rotate_left(tree, parent); 542 | avl_post_delete(tree, parent->parent); 543 | return; 544 | } 545 | 546 | parent->balance--; 547 | 548 | if (parent->balance == 0) { 549 | avl_post_delete(tree, parent); 550 | return; 551 | } 552 | 553 | if (parent->balance == -1) 554 | return; 555 | 556 | if (parent->left->balance == 0) { 557 | avl_rotate_right(tree, parent); 558 | return; 559 | } 560 | 561 | if (parent->left->balance == -1) { 562 | avl_rotate_right(tree, parent); 563 | avl_post_delete(tree, parent->parent); 564 | return; 565 | } 566 | 567 | avl_rotate_left(tree, parent->left); 568 | avl_rotate_right(tree, parent); 569 | avl_post_delete(tree, parent->parent); 570 | } 571 | 572 | static struct avl_node * 573 | avl_local_min(struct avl_node *node) 574 | { 575 | while (node->left != NULL) 576 | node = node->left; 577 | 578 | return node; 579 | } 580 | 581 | #if 0 582 | static struct avl_node * 583 | avl_local_max(struct avl_node *node) 584 | { 585 | while (node->right != NULL) 586 | node = node->right; 587 | 588 | return node; 589 | } 590 | #endif 591 | 592 | static void 593 | avl_delete_worker(struct avl_tree *tree, struct avl_node *node) 594 | { 595 | struct avl_node *parent, *min; 596 | 597 | parent = node->parent; 598 | 599 | if (node->left == NULL && node->right == NULL) { 600 | if (parent == NULL) { 601 | tree->root = NULL; 602 | return; 603 | } 604 | 605 | if (parent->left == node) { 606 | parent->left = NULL; 607 | parent->balance++; 608 | 609 | if (parent->balance == 1) 610 | return; 611 | 612 | if (parent->balance == 0) { 613 | avl_post_delete(tree, parent); 614 | return; 615 | } 616 | 617 | if (parent->right->balance == 0) { 618 | avl_rotate_left(tree, parent); 619 | return; 620 | } 621 | 622 | if (parent->right->balance == 1) { 623 | avl_rotate_left(tree, parent); 624 | avl_post_delete(tree, parent->parent); 625 | return; 626 | } 627 | 628 | avl_rotate_right(tree, parent->right); 629 | avl_rotate_left(tree, parent); 630 | avl_post_delete(tree, parent->parent); 631 | return; 632 | } 633 | 634 | if (parent->right == node) { 635 | parent->right = NULL; 636 | parent->balance--; 637 | 638 | if (parent->balance == -1) 639 | return; 640 | 641 | if (parent->balance == 0) { 642 | avl_post_delete(tree, parent); 643 | return; 644 | } 645 | 646 | if (parent->left->balance == 0) { 647 | avl_rotate_right(tree, parent); 648 | return; 649 | } 650 | 651 | if (parent->left->balance == -1) { 652 | avl_rotate_right(tree, parent); 653 | avl_post_delete(tree, parent->parent); 654 | return; 655 | } 656 | 657 | avl_rotate_left(tree, parent->left); 658 | avl_rotate_right(tree, parent); 659 | avl_post_delete(tree, parent->parent); 660 | return; 661 | } 662 | } 663 | 664 | if (node->left == NULL) { 665 | if (parent == NULL) { 666 | tree->root = node->right; 667 | node->right->parent = NULL; 668 | return; 669 | } 670 | 671 | node->right->parent = parent; 672 | 673 | if (parent->left == node) 674 | parent->left = node->right; 675 | 676 | else 677 | parent->right = node->right; 678 | 679 | avl_post_delete(tree, node->right); 680 | return; 681 | } 682 | 683 | if (node->right == NULL) { 684 | if (parent == NULL) { 685 | tree->root = node->left; 686 | node->left->parent = NULL; 687 | return; 688 | } 689 | 690 | node->left->parent = parent; 691 | 692 | if (parent->left == node) 693 | parent->left = node->left; 694 | 695 | else 696 | parent->right = node->left; 697 | 698 | avl_post_delete(tree, node->left); 699 | return; 700 | } 701 | 702 | min = avl_local_min(node->right); 703 | avl_delete_worker(tree, min); 704 | parent = node->parent; 705 | 706 | min->balance = node->balance; 707 | min->parent = parent; 708 | min->left = node->left; 709 | min->right = node->right; 710 | 711 | if (min->left != NULL) 712 | min->left->parent = min; 713 | 714 | if (min->right != NULL) 715 | min->right->parent = min; 716 | 717 | if (parent == NULL) { 718 | tree->root = min; 719 | return; 720 | } 721 | 722 | if (parent->left == node) { 723 | parent->left = min; 724 | return; 725 | } 726 | 727 | parent->right = min; 728 | } 729 | 730 | /* 731 | * Local Variables: 732 | * c-basic-offset: 2 733 | * indent-tabs-mode: nil 734 | * End: 735 | */ 736 | -------------------------------------------------------------------------------- /blob.c: -------------------------------------------------------------------------------- 1 | /* 2 | * blob - library for generating/parsing tagged binary data 3 | * 4 | * Copyright (C) 2010 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "blob.h" 20 | 21 | static bool 22 | blob_buffer_grow(struct blob_buf *buf, int minlen) 23 | { 24 | int delta = ((minlen / 256) + 1) * 256; 25 | buf->buflen += delta; 26 | buf->buf = realloc(buf->buf, buf->buflen); 27 | if (buf->buf) 28 | memset(buf->buf + buf->buflen - delta, 0, delta); 29 | return !!buf->buf; 30 | } 31 | 32 | static void 33 | blob_init(struct blob_attr *attr, int id, unsigned int len) 34 | { 35 | len &= BLOB_ATTR_LEN_MASK; 36 | len |= (id << BLOB_ATTR_ID_SHIFT) & BLOB_ATTR_ID_MASK; 37 | attr->id_len = cpu_to_be32(len); 38 | } 39 | 40 | static inline struct blob_attr * 41 | offset_to_attr(struct blob_buf *buf, int offset) 42 | { 43 | void *ptr = (char *)buf->buf + offset - BLOB_COOKIE; 44 | return ptr; 45 | } 46 | 47 | static inline int 48 | attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) 49 | { 50 | return (char *)attr - (char *) buf->buf + BLOB_COOKIE; 51 | } 52 | 53 | void 54 | blob_buf_grow(struct blob_buf *buf, int required) 55 | { 56 | int offset_head = attr_to_offset(buf, buf->head); 57 | 58 | if (!buf->grow || !buf->grow(buf, required)) 59 | return; 60 | 61 | buf->head = offset_to_attr(buf, offset_head); 62 | } 63 | 64 | static struct blob_attr * 65 | blob_add(struct blob_buf *buf, struct blob_attr *pos, int id, int payload) 66 | { 67 | int offset = attr_to_offset(buf, pos); 68 | int required = (offset - BLOB_COOKIE + sizeof(struct blob_attr) + payload) - buf->buflen; 69 | struct blob_attr *attr; 70 | 71 | if (required > 0) { 72 | blob_buf_grow(buf, required); 73 | attr = offset_to_attr(buf, offset); 74 | } else { 75 | attr = pos; 76 | } 77 | 78 | blob_init(attr, id, payload + sizeof(struct blob_attr)); 79 | blob_fill_pad(attr); 80 | return attr; 81 | } 82 | 83 | int 84 | blob_buf_init(struct blob_buf *buf, int id) 85 | { 86 | if (!buf->grow) 87 | buf->grow = blob_buffer_grow; 88 | 89 | buf->head = buf->buf; 90 | if (blob_add(buf, buf->buf, id, 0) == NULL) 91 | return -ENOMEM; 92 | 93 | return 0; 94 | } 95 | 96 | void 97 | blob_buf_free(struct blob_buf *buf) 98 | { 99 | free(buf->buf); 100 | buf->buf = NULL; 101 | buf->buflen = 0; 102 | } 103 | 104 | void 105 | blob_fill_pad(struct blob_attr *attr) 106 | { 107 | char *buf = (char *) attr; 108 | int len = blob_pad_len(attr); 109 | int delta = len - blob_raw_len(attr); 110 | 111 | if (delta > 0) 112 | memset(buf + len - delta, 0, delta); 113 | } 114 | 115 | void 116 | blob_set_raw_len(struct blob_attr *attr, unsigned int len) 117 | { 118 | len &= BLOB_ATTR_LEN_MASK; 119 | attr->id_len &= ~cpu_to_be32(BLOB_ATTR_LEN_MASK); 120 | attr->id_len |= cpu_to_be32(len); 121 | } 122 | 123 | struct blob_attr * 124 | blob_new(struct blob_buf *buf, int id, int payload) 125 | { 126 | struct blob_attr *attr; 127 | 128 | attr = blob_add(buf, blob_next(buf->head), id, payload); 129 | if (!attr) 130 | return NULL; 131 | 132 | blob_set_raw_len(buf->head, blob_pad_len(buf->head) + blob_pad_len(attr)); 133 | return attr; 134 | } 135 | 136 | struct blob_attr * 137 | blob_put_raw(struct blob_buf *buf, const void *ptr, int len) 138 | { 139 | struct blob_attr *attr; 140 | 141 | if (len < sizeof(struct blob_attr) || !ptr) 142 | return NULL; 143 | 144 | attr = blob_add(buf, blob_next(buf->head), 0, len - sizeof(struct blob_attr)); 145 | blob_set_raw_len(buf->head, blob_pad_len(buf->head) + len); 146 | memcpy(attr, ptr, len); 147 | return attr; 148 | } 149 | 150 | struct blob_attr * 151 | blob_put(struct blob_buf *buf, int id, const void *ptr, int len) 152 | { 153 | struct blob_attr *attr; 154 | 155 | attr = blob_new(buf, id, len); 156 | if (!attr) 157 | return NULL; 158 | 159 | if (ptr) 160 | memcpy(blob_data(attr), ptr, len); 161 | return attr; 162 | } 163 | 164 | void * 165 | blob_nest_start(struct blob_buf *buf, int id) 166 | { 167 | unsigned long offset = attr_to_offset(buf, buf->head); 168 | buf->head = blob_new(buf, id, 0); 169 | return (void *) offset; 170 | } 171 | 172 | void 173 | blob_nest_end(struct blob_buf *buf, void *cookie) 174 | { 175 | struct blob_attr *attr = offset_to_attr(buf, (unsigned long) cookie); 176 | blob_set_raw_len(attr, blob_pad_len(attr) + blob_len(buf->head)); 177 | buf->head = attr; 178 | } 179 | 180 | static const int blob_type_minlen[BLOB_ATTR_LAST] = { 181 | [BLOB_ATTR_STRING] = 1, 182 | [BLOB_ATTR_INT8] = sizeof(uint8_t), 183 | [BLOB_ATTR_INT16] = sizeof(uint16_t), 184 | [BLOB_ATTR_INT32] = sizeof(uint32_t), 185 | [BLOB_ATTR_INT64] = sizeof(uint64_t), 186 | }; 187 | 188 | bool 189 | blob_check_type(const void *ptr, int len, int type) 190 | { 191 | const char *data = ptr; 192 | 193 | if (type >= BLOB_ATTR_LAST) 194 | return false; 195 | 196 | if (type >= BLOB_ATTR_INT8 && type <= BLOB_ATTR_INT64) { 197 | if (len != blob_type_minlen[type]) 198 | return false; 199 | } else { 200 | if (len < blob_type_minlen[type]) 201 | return false; 202 | } 203 | 204 | if (type == BLOB_ATTR_STRING && data[len - 1] != 0) 205 | return false; 206 | 207 | return true; 208 | } 209 | 210 | int 211 | blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max) 212 | { 213 | struct blob_attr *pos; 214 | int found = 0; 215 | int rem; 216 | 217 | memset(data, 0, sizeof(struct blob_attr *) * max); 218 | blob_for_each_attr(pos, attr, rem) { 219 | int id = blob_id(pos); 220 | int len = blob_len(pos); 221 | 222 | if (id >= max) 223 | continue; 224 | 225 | if (info) { 226 | int type = info[id].type; 227 | 228 | if (type < BLOB_ATTR_LAST) { 229 | if (!blob_check_type(blob_data(pos), len, type)) 230 | continue; 231 | } 232 | 233 | if (info[id].minlen && len < info[id].minlen) 234 | continue; 235 | 236 | if (info[id].maxlen && len > info[id].maxlen) 237 | continue; 238 | 239 | if (info[id].validate && !info[id].validate(&info[id], attr)) 240 | continue; 241 | } 242 | 243 | if (!data[id]) 244 | found++; 245 | 246 | data[id] = pos; 247 | } 248 | return found; 249 | } 250 | 251 | bool 252 | blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2) 253 | { 254 | if (!a1 && !a2) 255 | return true; 256 | 257 | if (!a1 || !a2) 258 | return false; 259 | 260 | if (blob_pad_len(a1) != blob_pad_len(a2)) 261 | return false; 262 | 263 | return !memcmp(a1, a2, blob_pad_len(a1)); 264 | } 265 | 266 | struct blob_attr * 267 | blob_memdup(struct blob_attr *attr) 268 | { 269 | struct blob_attr *ret; 270 | int size = blob_pad_len(attr); 271 | 272 | ret = malloc(size); 273 | if (!ret) 274 | return NULL; 275 | 276 | memcpy(ret, attr, size); 277 | return ret; 278 | } 279 | -------------------------------------------------------------------------------- /blob.h: -------------------------------------------------------------------------------- 1 | /* 2 | * blob - library for generating/parsing tagged binary data 3 | * 4 | * Copyright (C) 2010 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef _BLOB_H__ 20 | #define _BLOB_H__ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "utils.h" 30 | 31 | #define BLOB_COOKIE 0x01234567 32 | 33 | enum { 34 | BLOB_ATTR_UNSPEC, 35 | BLOB_ATTR_NESTED, 36 | BLOB_ATTR_BINARY, 37 | BLOB_ATTR_STRING, 38 | BLOB_ATTR_INT8, 39 | BLOB_ATTR_INT16, 40 | BLOB_ATTR_INT32, 41 | BLOB_ATTR_INT64, 42 | BLOB_ATTR_LAST 43 | }; 44 | 45 | #define BLOB_ATTR_ID_MASK 0x7f000000 46 | #define BLOB_ATTR_ID_SHIFT 24 47 | #define BLOB_ATTR_LEN_MASK 0x00ffffff 48 | #define BLOB_ATTR_ALIGN 4 49 | #define BLOB_ATTR_EXTENDED 0x80000000 50 | 51 | struct blob_attr { 52 | uint32_t id_len; 53 | char data[]; 54 | } __packed; 55 | 56 | struct blob_attr_info { 57 | unsigned int type; 58 | unsigned int minlen; 59 | unsigned int maxlen; 60 | bool (*validate)(const struct blob_attr_info *, struct blob_attr *); 61 | }; 62 | 63 | struct blob_buf { 64 | struct blob_attr *head; 65 | bool (*grow)(struct blob_buf *buf, int minlen); 66 | int buflen; 67 | void *buf; 68 | }; 69 | 70 | /* 71 | * blob_data: returns the data pointer for an attribute 72 | */ 73 | static inline void * 74 | blob_data(const struct blob_attr *attr) 75 | { 76 | return (void *) attr->data; 77 | } 78 | 79 | /* 80 | * blob_id: returns the id of an attribute 81 | */ 82 | static inline unsigned int 83 | blob_id(const struct blob_attr *attr) 84 | { 85 | int id = (be32_to_cpu(attr->id_len) & BLOB_ATTR_ID_MASK) >> BLOB_ATTR_ID_SHIFT; 86 | return id; 87 | } 88 | 89 | static inline bool 90 | blob_is_extended(const struct blob_attr *attr) 91 | { 92 | return !!(attr->id_len & cpu_to_be32(BLOB_ATTR_EXTENDED)); 93 | } 94 | 95 | /* 96 | * blob_len: returns the length of the attribute's payload 97 | */ 98 | static inline unsigned int 99 | blob_len(const struct blob_attr *attr) 100 | { 101 | return (be32_to_cpu(attr->id_len) & BLOB_ATTR_LEN_MASK) - sizeof(struct blob_attr); 102 | } 103 | 104 | /* 105 | * blob_raw_len: returns the complete length of an attribute (including the header) 106 | */ 107 | static inline unsigned int 108 | blob_raw_len(const struct blob_attr *attr) 109 | { 110 | return blob_len(attr) + sizeof(struct blob_attr); 111 | } 112 | 113 | /* 114 | * blob_pad_len: returns the padded length of an attribute (including the header) 115 | */ 116 | static inline unsigned int 117 | blob_pad_len(const struct blob_attr *attr) 118 | { 119 | int len = blob_raw_len(attr); 120 | len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1); 121 | return len; 122 | } 123 | 124 | static inline uint8_t 125 | blob_get_u8(const struct blob_attr *attr) 126 | { 127 | return *((uint8_t *) attr->data); 128 | } 129 | 130 | static inline uint16_t 131 | blob_get_u16(const struct blob_attr *attr) 132 | { 133 | uint16_t *tmp = (uint16_t*)attr->data; 134 | return be16_to_cpu(*tmp); 135 | } 136 | 137 | static inline uint32_t 138 | blob_get_u32(const struct blob_attr *attr) 139 | { 140 | uint32_t *tmp = (uint32_t*)attr->data; 141 | return be32_to_cpu(*tmp); 142 | } 143 | 144 | static inline uint64_t 145 | blob_get_u64(const struct blob_attr *attr) 146 | { 147 | uint32_t *ptr = blob_data(attr); 148 | uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32; 149 | tmp |= be32_to_cpu(ptr[1]); 150 | return tmp; 151 | } 152 | 153 | static inline int8_t 154 | blob_get_int8(const struct blob_attr *attr) 155 | { 156 | return blob_get_u8(attr); 157 | } 158 | 159 | static inline int16_t 160 | blob_get_int16(const struct blob_attr *attr) 161 | { 162 | return blob_get_u16(attr); 163 | } 164 | 165 | static inline int32_t 166 | blob_get_int32(const struct blob_attr *attr) 167 | { 168 | return blob_get_u32(attr); 169 | } 170 | 171 | static inline int64_t 172 | blob_get_int64(const struct blob_attr *attr) 173 | { 174 | return blob_get_u64(attr); 175 | } 176 | 177 | static inline const char * 178 | blob_get_string(const struct blob_attr *attr) 179 | { 180 | return attr->data; 181 | } 182 | 183 | static inline struct blob_attr * 184 | blob_next(const struct blob_attr *attr) 185 | { 186 | return (struct blob_attr *) ((char *) attr + blob_pad_len(attr)); 187 | } 188 | 189 | extern void blob_fill_pad(struct blob_attr *attr); 190 | extern void blob_set_raw_len(struct blob_attr *attr, unsigned int len); 191 | extern bool blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2); 192 | extern int blob_buf_init(struct blob_buf *buf, int id); 193 | extern void blob_buf_free(struct blob_buf *buf); 194 | extern void blob_buf_grow(struct blob_buf *buf, int required); 195 | extern struct blob_attr *blob_new(struct blob_buf *buf, int id, int payload); 196 | extern void *blob_nest_start(struct blob_buf *buf, int id); 197 | extern void blob_nest_end(struct blob_buf *buf, void *cookie); 198 | extern struct blob_attr *blob_put(struct blob_buf *buf, int id, const void *ptr, int len); 199 | extern bool blob_check_type(const void *ptr, int len, int type); 200 | extern int blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max); 201 | extern struct blob_attr *blob_memdup(struct blob_attr *attr); 202 | extern struct blob_attr *blob_put_raw(struct blob_buf *buf, const void *ptr, int len); 203 | 204 | static inline struct blob_attr * 205 | blob_put_string(struct blob_buf *buf, int id, const char *str) 206 | { 207 | return blob_put(buf, id, str, strlen(str) + 1); 208 | } 209 | 210 | static inline struct blob_attr * 211 | blob_put_u8(struct blob_buf *buf, int id, uint8_t val) 212 | { 213 | return blob_put(buf, id, &val, sizeof(val)); 214 | } 215 | 216 | static inline struct blob_attr * 217 | blob_put_u16(struct blob_buf *buf, int id, uint16_t val) 218 | { 219 | val = cpu_to_be16(val); 220 | return blob_put(buf, id, &val, sizeof(val)); 221 | } 222 | 223 | static inline struct blob_attr * 224 | blob_put_u32(struct blob_buf *buf, int id, uint32_t val) 225 | { 226 | val = cpu_to_be32(val); 227 | return blob_put(buf, id, &val, sizeof(val)); 228 | } 229 | 230 | static inline struct blob_attr * 231 | blob_put_u64(struct blob_buf *buf, int id, uint64_t val) 232 | { 233 | val = cpu_to_be64(val); 234 | return blob_put(buf, id, &val, sizeof(val)); 235 | } 236 | 237 | #define blob_put_int8 blob_put_u8 238 | #define blob_put_int16 blob_put_u16 239 | #define blob_put_int32 blob_put_u32 240 | #define blob_put_int64 blob_put_u64 241 | 242 | #define __blob_for_each_attr(pos, attr, rem) \ 243 | for (pos = (void *) attr; \ 244 | rem > 0 && (blob_pad_len(pos) <= rem) && \ 245 | (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ 246 | rem -= blob_pad_len(pos), pos = blob_next(pos)) 247 | 248 | 249 | #define blob_for_each_attr(pos, attr, rem) \ 250 | for (rem = attr ? blob_len(attr) : 0, \ 251 | pos = attr ? blob_data(attr) : 0; \ 252 | rem > 0 && (blob_pad_len(pos) <= rem) && \ 253 | (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ 254 | rem -= blob_pad_len(pos), pos = blob_next(pos)) 255 | 256 | 257 | #endif 258 | -------------------------------------------------------------------------------- /blobmsg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include "blobmsg.h" 17 | 18 | static const int blob_type[__BLOBMSG_TYPE_LAST] = { 19 | [BLOBMSG_TYPE_INT8] = BLOB_ATTR_INT8, 20 | [BLOBMSG_TYPE_INT16] = BLOB_ATTR_INT16, 21 | [BLOBMSG_TYPE_INT32] = BLOB_ATTR_INT32, 22 | [BLOBMSG_TYPE_INT64] = BLOB_ATTR_INT64, 23 | [BLOBMSG_TYPE_STRING] = BLOB_ATTR_STRING, 24 | [BLOBMSG_TYPE_UNSPEC] = BLOB_ATTR_BINARY, 25 | }; 26 | 27 | static uint16_t 28 | blobmsg_namelen(const struct blobmsg_hdr *hdr) 29 | { 30 | return be16_to_cpu(hdr->namelen); 31 | } 32 | 33 | bool blobmsg_check_attr(const struct blob_attr *attr, bool name) 34 | { 35 | const struct blobmsg_hdr *hdr; 36 | const char *data; 37 | int id, len; 38 | 39 | if (blob_len(attr) < sizeof(struct blobmsg_hdr)) 40 | return false; 41 | 42 | hdr = (void *) attr->data; 43 | if (!hdr->namelen && name) 44 | return false; 45 | 46 | if (blobmsg_namelen(hdr) > blob_len(attr) - sizeof(struct blobmsg_hdr)) 47 | return false; 48 | 49 | if (hdr->name[blobmsg_namelen(hdr)] != 0) 50 | return false; 51 | 52 | id = blob_id(attr); 53 | len = blobmsg_data_len(attr); 54 | data = blobmsg_data(attr); 55 | 56 | if (id > BLOBMSG_TYPE_LAST) 57 | return false; 58 | 59 | if (!blob_type[id]) 60 | return true; 61 | 62 | return blob_check_type(data, len, blob_type[id]); 63 | } 64 | 65 | bool blobmsg_check_attr_list(const struct blob_attr *attr, int type) 66 | { 67 | struct blob_attr *cur; 68 | bool name; 69 | int rem; 70 | 71 | switch (blobmsg_type(attr)) { 72 | case BLOBMSG_TYPE_TABLE: 73 | name = true; 74 | break; 75 | case BLOBMSG_TYPE_ARRAY: 76 | name = false; 77 | break; 78 | default: 79 | return false; 80 | } 81 | 82 | blobmsg_for_each_attr(cur, attr, rem) { 83 | if (blobmsg_type(cur) != type) 84 | return false; 85 | 86 | if (!blobmsg_check_attr(cur, name)) 87 | return false; 88 | } 89 | 90 | return true; 91 | } 92 | 93 | int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len, 94 | struct blob_attr **tb, void *data, int len) 95 | { 96 | struct blob_attr *attr; 97 | int i = 0; 98 | 99 | memset(tb, 0, policy_len * sizeof(*tb)); 100 | __blob_for_each_attr(attr, data, len) { 101 | if (policy[i].type != BLOBMSG_TYPE_UNSPEC && 102 | blob_id(attr) != policy[i].type) 103 | continue; 104 | 105 | if (!blobmsg_check_attr(attr, false)) 106 | return -1; 107 | 108 | if (tb[i]) 109 | continue; 110 | 111 | tb[i++] = attr; 112 | if (i == policy_len) 113 | break; 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | 120 | int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, 121 | struct blob_attr **tb, void *data, int len) 122 | { 123 | struct blobmsg_hdr *hdr; 124 | struct blob_attr *attr; 125 | uint8_t *pslen; 126 | int i; 127 | 128 | memset(tb, 0, policy_len * sizeof(*tb)); 129 | pslen = alloca(policy_len); 130 | for (i = 0; i < policy_len; i++) { 131 | if (!policy[i].name) 132 | continue; 133 | 134 | pslen[i] = strlen(policy[i].name); 135 | } 136 | 137 | __blob_for_each_attr(attr, data, len) { 138 | hdr = blob_data(attr); 139 | for (i = 0; i < policy_len; i++) { 140 | if (!policy[i].name) 141 | continue; 142 | 143 | if (policy[i].type != BLOBMSG_TYPE_UNSPEC && 144 | blob_id(attr) != policy[i].type) 145 | continue; 146 | 147 | if (blobmsg_namelen(hdr) != pslen[i]) 148 | continue; 149 | 150 | if (!blobmsg_check_attr(attr, true)) 151 | return -1; 152 | 153 | if (tb[i]) 154 | continue; 155 | 156 | if (strcmp(policy[i].name, (char *) hdr->name) != 0) 157 | continue; 158 | 159 | tb[i] = attr; 160 | } 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | 167 | static struct blob_attr * 168 | blobmsg_new(struct blob_buf *buf, int type, const char *name, int payload_len, void **data) 169 | { 170 | struct blob_attr *attr; 171 | struct blobmsg_hdr *hdr; 172 | int attrlen, namelen; 173 | char *pad_start, *pad_end; 174 | 175 | if (!name) 176 | name = ""; 177 | 178 | namelen = strlen(name); 179 | attrlen = blobmsg_hdrlen(namelen) + payload_len; 180 | attr = blob_new(buf, type, attrlen); 181 | if (!attr) 182 | return NULL; 183 | 184 | attr->id_len |= be32_to_cpu(BLOB_ATTR_EXTENDED); 185 | hdr = blob_data(attr); 186 | hdr->namelen = cpu_to_be16(namelen); 187 | strcpy((char *) hdr->name, (const char *)name); 188 | pad_end = *data = blobmsg_data(attr); 189 | pad_start = (char *) &hdr->name[namelen]; 190 | if (pad_start < pad_end) 191 | memset(pad_start, 0, pad_end - pad_start); 192 | 193 | return attr; 194 | } 195 | 196 | static inline int 197 | attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) 198 | { 199 | return (char *)attr - (char *) buf->buf + BLOB_COOKIE; 200 | } 201 | 202 | 203 | void * 204 | blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array) 205 | { 206 | struct blob_attr *head = buf->head; 207 | int type = array ? BLOBMSG_TYPE_ARRAY : BLOBMSG_TYPE_TABLE; 208 | unsigned long offset = attr_to_offset(buf, buf->head); 209 | void *data; 210 | 211 | if (!name) 212 | name = ""; 213 | 214 | head = blobmsg_new(buf, type, name, 0, &data); 215 | blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blobmsg_hdrlen(strlen(name))); 216 | buf->head = head; 217 | return (void *)offset; 218 | } 219 | 220 | void 221 | blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg) 222 | { 223 | va_list arg2; 224 | char cbuf; 225 | int len; 226 | 227 | va_copy(arg2, arg); 228 | len = vsnprintf(&cbuf, sizeof(cbuf), format, arg2); 229 | va_end(arg2); 230 | 231 | vsprintf(blobmsg_alloc_string_buffer(buf, name, len + 1), format, arg); 232 | blobmsg_add_string_buffer(buf); 233 | } 234 | 235 | void 236 | blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...) 237 | { 238 | va_list ap; 239 | 240 | va_start(ap, format); 241 | blobmsg_vprintf(buf, name, format, ap); 242 | va_end(ap); 243 | } 244 | 245 | void * 246 | blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, int maxlen) 247 | { 248 | struct blob_attr *attr; 249 | void *data_dest; 250 | 251 | attr = blobmsg_new(buf, BLOBMSG_TYPE_STRING, name, maxlen, &data_dest); 252 | if (!attr) 253 | return NULL; 254 | 255 | data_dest = blobmsg_data(attr); 256 | blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blob_pad_len(attr)); 257 | blob_set_raw_len(attr, blob_raw_len(attr) - maxlen); 258 | 259 | return data_dest; 260 | } 261 | 262 | void * 263 | blobmsg_realloc_string_buffer(struct blob_buf *buf, int maxlen) 264 | { 265 | struct blob_attr *attr = blob_next(buf->head); 266 | int offset = attr_to_offset(buf, blob_next(buf->head)) + blob_pad_len(attr) - BLOB_COOKIE; 267 | int required = maxlen - (buf->buflen - offset); 268 | 269 | if (required <= 0) 270 | goto out; 271 | 272 | blob_buf_grow(buf, required); 273 | attr = blob_next(buf->head); 274 | 275 | out: 276 | return blobmsg_data(attr); 277 | } 278 | 279 | void 280 | blobmsg_add_string_buffer(struct blob_buf *buf) 281 | { 282 | struct blob_attr *attr; 283 | int len, attrlen; 284 | 285 | attr = blob_next(buf->head); 286 | len = strlen(blobmsg_data(attr)) + 1; 287 | 288 | attrlen = blob_raw_len(attr) + len; 289 | blob_set_raw_len(attr, attrlen); 290 | blob_fill_pad(attr); 291 | 292 | blob_set_raw_len(buf->head, blob_raw_len(buf->head) + blob_pad_len(attr)); 293 | } 294 | 295 | int 296 | blobmsg_add_field(struct blob_buf *buf, int type, const char *name, 297 | const void *data, int len) 298 | { 299 | struct blob_attr *attr; 300 | void *data_dest; 301 | 302 | attr = blobmsg_new(buf, type, name, len, &data_dest); 303 | if (!attr) 304 | return -1; 305 | 306 | if (len > 0) 307 | memcpy(data_dest, data, len); 308 | 309 | return 0; 310 | } 311 | -------------------------------------------------------------------------------- /blobmsg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef __BLOBMSG_H 17 | #define __BLOBMSG_H 18 | 19 | #include 20 | #include "blob.h" 21 | 22 | #define BLOBMSG_ALIGN 2 23 | #define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1)) 24 | 25 | enum blobmsg_type { 26 | BLOBMSG_TYPE_UNSPEC, 27 | BLOBMSG_TYPE_ARRAY, 28 | BLOBMSG_TYPE_TABLE, 29 | BLOBMSG_TYPE_STRING, 30 | BLOBMSG_TYPE_INT64, 31 | BLOBMSG_TYPE_INT32, 32 | BLOBMSG_TYPE_INT16, 33 | BLOBMSG_TYPE_INT8, 34 | __BLOBMSG_TYPE_LAST, 35 | BLOBMSG_TYPE_LAST = __BLOBMSG_TYPE_LAST - 1, 36 | BLOBMSG_TYPE_BOOL = BLOBMSG_TYPE_INT8, 37 | }; 38 | 39 | struct blobmsg_hdr { 40 | uint16_t namelen; 41 | uint8_t name[]; 42 | } __packed; 43 | 44 | struct blobmsg_policy { 45 | const char *name; 46 | enum blobmsg_type type; 47 | }; 48 | 49 | static inline int blobmsg_hdrlen(int namelen) 50 | { 51 | return BLOBMSG_PADDING(sizeof(struct blobmsg_hdr) + namelen + 1); 52 | } 53 | 54 | static inline const char *blobmsg_name(const struct blob_attr *attr) 55 | { 56 | struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); 57 | return (const char *) hdr->name; 58 | } 59 | 60 | static inline int blobmsg_type(const struct blob_attr *attr) 61 | { 62 | return blob_id(attr); 63 | } 64 | 65 | static inline void *blobmsg_data(const struct blob_attr *attr) 66 | { 67 | struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); 68 | char *data = blob_data(attr); 69 | 70 | if (blob_is_extended(attr)) 71 | data += blobmsg_hdrlen(be16_to_cpu(hdr->namelen)); 72 | 73 | return data; 74 | } 75 | 76 | static inline int blobmsg_data_len(const struct blob_attr *attr) 77 | { 78 | uint8_t *start, *end; 79 | 80 | start = (uint8_t *) blob_data(attr); 81 | end = (uint8_t *) blobmsg_data(attr); 82 | 83 | return blob_len(attr) - (end - start); 84 | } 85 | 86 | static inline int blobmsg_len(const struct blob_attr *attr) 87 | { 88 | return blobmsg_data_len(attr); 89 | } 90 | 91 | bool blobmsg_check_attr(const struct blob_attr *attr, bool name); 92 | bool blobmsg_check_attr_list(const struct blob_attr *attr, int type); 93 | int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, 94 | struct blob_attr **tb, void *data, int len); 95 | int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len, 96 | struct blob_attr **tb, void *data, int len); 97 | 98 | int blobmsg_add_field(struct blob_buf *buf, int type, const char *name, 99 | const void *data, int len); 100 | 101 | static inline int 102 | blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val) 103 | { 104 | return blobmsg_add_field(buf, BLOBMSG_TYPE_INT8, name, &val, 1); 105 | } 106 | 107 | static inline int 108 | blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val) 109 | { 110 | val = cpu_to_be16(val); 111 | return blobmsg_add_field(buf, BLOBMSG_TYPE_INT16, name, &val, 2); 112 | } 113 | 114 | static inline int 115 | blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val) 116 | { 117 | val = cpu_to_be32(val); 118 | return blobmsg_add_field(buf, BLOBMSG_TYPE_INT32, name, &val, 4); 119 | } 120 | 121 | static inline int 122 | blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val) 123 | { 124 | val = cpu_to_be64(val); 125 | return blobmsg_add_field(buf, BLOBMSG_TYPE_INT64, name, &val, 8); 126 | } 127 | 128 | static inline int 129 | blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string) 130 | { 131 | return blobmsg_add_field(buf, BLOBMSG_TYPE_STRING, name, string, strlen(string) + 1); 132 | } 133 | 134 | static inline int 135 | blobmsg_add_blob(struct blob_buf *buf, struct blob_attr *attr) 136 | { 137 | return blobmsg_add_field(buf, blobmsg_type(attr), blobmsg_name(attr), 138 | blobmsg_data(attr), blobmsg_data_len(attr)); 139 | } 140 | 141 | void *blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array); 142 | 143 | static inline void * 144 | blobmsg_open_array(struct blob_buf *buf, const char *name) 145 | { 146 | return blobmsg_open_nested(buf, name, true); 147 | } 148 | 149 | static inline void * 150 | blobmsg_open_table(struct blob_buf *buf, const char *name) 151 | { 152 | return blobmsg_open_nested(buf, name, false); 153 | } 154 | 155 | static inline void 156 | blobmsg_close_array(struct blob_buf *buf, void *cookie) 157 | { 158 | blob_nest_end(buf, cookie); 159 | } 160 | 161 | static inline void 162 | blobmsg_close_table(struct blob_buf *buf, void *cookie) 163 | { 164 | blob_nest_end(buf, cookie); 165 | } 166 | 167 | static inline int blobmsg_buf_init(struct blob_buf *buf) 168 | { 169 | return blob_buf_init(buf, BLOBMSG_TYPE_TABLE); 170 | } 171 | 172 | static inline uint8_t blobmsg_get_u8(struct blob_attr *attr) 173 | { 174 | return *(uint8_t *) blobmsg_data(attr); 175 | } 176 | 177 | static inline bool blobmsg_get_bool(struct blob_attr *attr) 178 | { 179 | return *(uint8_t *) blobmsg_data(attr); 180 | } 181 | 182 | static inline uint16_t blobmsg_get_u16(struct blob_attr *attr) 183 | { 184 | return be16_to_cpu(*(uint16_t *) blobmsg_data(attr)); 185 | } 186 | 187 | static inline uint32_t blobmsg_get_u32(struct blob_attr *attr) 188 | { 189 | return be32_to_cpu(*(uint32_t *) blobmsg_data(attr)); 190 | } 191 | 192 | static inline uint64_t blobmsg_get_u64(struct blob_attr *attr) 193 | { 194 | uint32_t *ptr = blobmsg_data(attr); 195 | uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32; 196 | tmp |= be32_to_cpu(ptr[1]); 197 | return tmp; 198 | } 199 | 200 | static inline char *blobmsg_get_string(struct blob_attr *attr) 201 | { 202 | return blobmsg_data(attr); 203 | } 204 | 205 | void *blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, int maxlen); 206 | void *blobmsg_realloc_string_buffer(struct blob_buf *buf, int maxlen); 207 | void blobmsg_add_string_buffer(struct blob_buf *buf); 208 | 209 | void blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg); 210 | void blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...) 211 | __attribute__((format(printf, 3, 4))); 212 | 213 | 214 | /* blobmsg to json formatting */ 215 | 216 | #define blobmsg_for_each_attr(pos, attr, rem) \ 217 | for (rem = attr ? blobmsg_data_len(attr) : 0, \ 218 | pos = attr ? blobmsg_data(attr) : 0; \ 219 | rem > 0 && (blob_pad_len(pos) <= rem) && \ 220 | (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ 221 | rem -= blob_pad_len(pos), pos = blob_next(pos)) 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /blobmsg_json.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include "blobmsg.h" 17 | #include "blobmsg_json.h" 18 | 19 | bool blobmsg_add_object(struct blob_buf *b, json_object *obj) 20 | { 21 | json_object_object_foreach(obj, key, val) { 22 | if (!blobmsg_add_json_element(b, key, val)) 23 | return false; 24 | } 25 | return true; 26 | } 27 | 28 | static bool blobmsg_add_array(struct blob_buf *b, struct array_list *a) 29 | { 30 | int i, len; 31 | 32 | for (i = 0, len = array_list_length(a); i < len; i++) { 33 | if (!blobmsg_add_json_element(b, NULL, array_list_get_idx(a, i))) 34 | return false; 35 | } 36 | 37 | return true; 38 | } 39 | 40 | bool blobmsg_add_json_element(struct blob_buf *b, const char *name, json_object *obj) 41 | { 42 | bool ret = true; 43 | void *c; 44 | 45 | if (!obj) 46 | return false; 47 | 48 | switch (json_object_get_type(obj)) { 49 | case json_type_object: 50 | c = blobmsg_open_table(b, name); 51 | ret = blobmsg_add_object(b, obj); 52 | blobmsg_close_table(b, c); 53 | break; 54 | case json_type_array: 55 | c = blobmsg_open_array(b, name); 56 | ret = blobmsg_add_array(b, json_object_get_array(obj)); 57 | blobmsg_close_array(b, c); 58 | break; 59 | case json_type_string: 60 | blobmsg_add_string(b, name, json_object_get_string(obj)); 61 | break; 62 | case json_type_boolean: 63 | blobmsg_add_u8(b, name, json_object_get_boolean(obj)); 64 | break; 65 | case json_type_int: 66 | blobmsg_add_u32(b, name, json_object_get_int(obj)); 67 | break; 68 | default: 69 | return false; 70 | } 71 | return ret; 72 | } 73 | 74 | static bool __blobmsg_add_json(struct blob_buf *b, json_object *obj) 75 | { 76 | bool ret = false; 77 | 78 | if (is_error(obj)) 79 | return false; 80 | 81 | if (json_object_get_type(obj) != json_type_object) 82 | goto out; 83 | 84 | ret = blobmsg_add_object(b, obj); 85 | 86 | out: 87 | json_object_put(obj); 88 | return ret; 89 | } 90 | 91 | bool blobmsg_add_json_from_file(struct blob_buf *b, const char *file) 92 | { 93 | return __blobmsg_add_json(b, json_object_from_file(file)); 94 | } 95 | 96 | bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str) 97 | { 98 | return __blobmsg_add_json(b, json_tokener_parse(str)); 99 | } 100 | 101 | 102 | struct strbuf { 103 | int len; 104 | int pos; 105 | char *buf; 106 | 107 | blobmsg_json_format_t custom_format; 108 | void *priv; 109 | bool indent; 110 | int indent_level; 111 | }; 112 | 113 | static bool blobmsg_puts(struct strbuf *s, const char *c, int len) 114 | { 115 | if (len <= 0) 116 | return true; 117 | 118 | if (s->pos + len >= s->len) { 119 | s->len += 16 + len; 120 | s->buf = realloc(s->buf, s->len); 121 | if (!s->buf) 122 | return false; 123 | } 124 | memcpy(s->buf + s->pos, c, len); 125 | s->pos += len; 126 | return true; 127 | } 128 | 129 | static void add_separator(struct strbuf *s) 130 | { 131 | static char indent_chars[17] = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; 132 | int indent; 133 | char *start; 134 | 135 | if (!s->indent) 136 | return; 137 | 138 | indent = s->indent_level; 139 | if (indent > 16) 140 | indent = 16; 141 | 142 | start = &indent_chars[sizeof(indent_chars) - indent - 1]; 143 | *start = '\n'; 144 | blobmsg_puts(s, start, indent + 1); 145 | *start = '\t'; 146 | } 147 | 148 | 149 | static void blobmsg_format_string(struct strbuf *s, const char *str) 150 | { 151 | const unsigned char *p, *last, *end; 152 | char buf[8] = "\\u00"; 153 | 154 | end = (unsigned char *) str + strlen(str); 155 | blobmsg_puts(s, "\"", 1); 156 | for (p = (unsigned char *) str, last = p; *p; p++) { 157 | char escape = '\0'; 158 | int len; 159 | 160 | switch(*p) { 161 | case '\b': 162 | escape = 'b'; 163 | break; 164 | case '\n': 165 | escape = 'n'; 166 | break; 167 | case '\t': 168 | escape = 't'; 169 | break; 170 | case '\r': 171 | escape = 'r'; 172 | break; 173 | case '"': 174 | case '\\': 175 | case '/': 176 | escape = *p; 177 | break; 178 | default: 179 | if (*p < ' ') 180 | escape = 'u'; 181 | break; 182 | } 183 | 184 | if (!escape) 185 | continue; 186 | 187 | if (p > last) 188 | blobmsg_puts(s, (char *) last, p - last); 189 | last = p + 1; 190 | buf[1] = escape; 191 | 192 | if (escape == 'u') { 193 | sprintf(buf + 4, "%02x", (unsigned char) *p); 194 | len = 6; 195 | } else { 196 | len = 2; 197 | } 198 | blobmsg_puts(s, buf, len); 199 | } 200 | 201 | blobmsg_puts(s, (char *) last, end - last); 202 | blobmsg_puts(s, "\"", 1); 203 | } 204 | 205 | static void blobmsg_format_json_list(struct strbuf *s, struct blob_attr *attr, int len, bool array); 206 | 207 | static void blobmsg_format_element(struct strbuf *s, struct blob_attr *attr, bool array, bool head) 208 | { 209 | const char *data_str; 210 | char buf[32]; 211 | void *data; 212 | int len; 213 | 214 | if (!blobmsg_check_attr(attr, false)) 215 | return; 216 | 217 | if (!array && blobmsg_name(attr)[0]) { 218 | blobmsg_format_string(s, blobmsg_name(attr)); 219 | blobmsg_puts(s, ": ", s->indent ? 2 : 1); 220 | } 221 | 222 | data = blobmsg_data(attr); 223 | len = blobmsg_data_len(attr); 224 | 225 | if (!head && s->custom_format) { 226 | data_str = s->custom_format(s->priv, attr); 227 | if (data_str) 228 | goto out; 229 | } 230 | 231 | data_str = buf; 232 | switch(blob_id(attr)) { 233 | case BLOBMSG_TYPE_UNSPEC: 234 | sprintf(buf, "null"); 235 | break; 236 | case BLOBMSG_TYPE_BOOL: 237 | sprintf(buf, "%s", *(uint8_t *)data ? "true" : "false"); 238 | break; 239 | case BLOBMSG_TYPE_INT16: 240 | sprintf(buf, "%d", be16_to_cpu(*(uint16_t *)data)); 241 | break; 242 | case BLOBMSG_TYPE_INT32: 243 | sprintf(buf, "%d", (int32_t) be32_to_cpu(*(uint32_t *)data)); 244 | break; 245 | case BLOBMSG_TYPE_INT64: 246 | sprintf(buf, "%lld", (long long int) be64_to_cpu(*(uint64_t *)data)); 247 | break; 248 | case BLOBMSG_TYPE_STRING: 249 | blobmsg_format_string(s, data); 250 | return; 251 | case BLOBMSG_TYPE_ARRAY: 252 | blobmsg_format_json_list(s, data, len, true); 253 | return; 254 | case BLOBMSG_TYPE_TABLE: 255 | blobmsg_format_json_list(s, data, len, false); 256 | return; 257 | } 258 | 259 | out: 260 | blobmsg_puts(s, data_str, strlen(data_str)); 261 | } 262 | 263 | static void blobmsg_format_json_list(struct strbuf *s, struct blob_attr *attr, int len, bool array) 264 | { 265 | struct blob_attr *pos; 266 | bool first = true; 267 | int rem = len; 268 | 269 | blobmsg_puts(s, (array ? "[" : "{" ), 1); 270 | s->indent_level++; 271 | add_separator(s); 272 | __blob_for_each_attr(pos, attr, rem) { 273 | if (!first) { 274 | blobmsg_puts(s, ",", 1); 275 | add_separator(s); 276 | } 277 | 278 | blobmsg_format_element(s, pos, array, false); 279 | first = false; 280 | } 281 | s->indent_level--; 282 | add_separator(s); 283 | blobmsg_puts(s, (array ? "]" : "}"), 1); 284 | } 285 | 286 | char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, blobmsg_json_format_t cb, void *priv, int indent) 287 | { 288 | struct strbuf s; 289 | 290 | s.len = blob_len(attr); 291 | s.buf = malloc(s.len); 292 | s.pos = 0; 293 | s.custom_format = cb; 294 | s.priv = priv; 295 | s.indent = false; 296 | 297 | if (indent >= 0) { 298 | s.indent = true; 299 | s.indent_level = indent; 300 | } 301 | 302 | if (list) 303 | blobmsg_format_json_list(&s, blobmsg_data(attr), blobmsg_data_len(attr), false); 304 | else 305 | blobmsg_format_element(&s, attr, false, false); 306 | 307 | if (!s.len) 308 | return NULL; 309 | 310 | s.buf = realloc(s.buf, s.pos + 1); 311 | s.buf[s.pos] = 0; 312 | 313 | return s.buf; 314 | } 315 | -------------------------------------------------------------------------------- /blobmsg_json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef __BLOBMSG_JSON_H 17 | #define __BLOBMSG_JSON_H 18 | 19 | #ifdef JSONC 20 | #include 21 | #else 22 | #include 23 | #endif 24 | 25 | #include 26 | #include "blobmsg.h" 27 | 28 | bool blobmsg_add_object(struct blob_buf *b, json_object *obj); 29 | bool blobmsg_add_json_element(struct blob_buf *b, const char *name, json_object *obj); 30 | bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str); 31 | bool blobmsg_add_json_from_file(struct blob_buf *b, const char *file); 32 | 33 | typedef const char *(*blobmsg_json_format_t)(void *priv, struct blob_attr *attr); 34 | 35 | char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, 36 | blobmsg_json_format_t cb, void *priv, 37 | int indent); 38 | 39 | static inline char *blobmsg_format_json(struct blob_attr *attr, bool list) 40 | { 41 | return blobmsg_format_json_with_cb(attr, list, NULL, NULL, -1); 42 | } 43 | 44 | static inline char *blobmsg_format_json_indent(struct blob_attr *attr, bool list, int indent) 45 | { 46 | return blobmsg_format_json_with_cb(attr, list, NULL, NULL, indent); 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | PROJECT(ubox-examples C) 4 | ADD_DEFINITIONS(-O1 -Wall -Werror --std=gnu99 -g3) 5 | 6 | IF(APPLE) 7 | INCLUDE_DIRECTORIES(/opt/local/include) 8 | LINK_DIRECTORIES(/opt/local/lib) 9 | ENDIF() 10 | 11 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 12 | LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 13 | 14 | ADD_EXECUTABLE(blobmsg-example blobmsg-example.c) 15 | TARGET_LINK_LIBRARIES(blobmsg-example ubox blobmsg_json json) 16 | 17 | ADD_EXECUTABLE(ustream-example ustream-example.c) 18 | TARGET_LINK_LIBRARIES(ustream-example ubox) 19 | 20 | ADD_EXECUTABLE(runqueue-example runqueue-example.c) 21 | TARGET_LINK_LIBRARIES(runqueue-example ubox) 22 | 23 | -------------------------------------------------------------------------------- /examples/blobmsg-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "blobmsg.h" 4 | #include "blobmsg_json.h" 5 | 6 | static const char *indent_str = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; 7 | 8 | #define indent_printf(indent, ...) do { \ 9 | if (indent > 0) \ 10 | fwrite(indent_str, indent, 1, stderr); \ 11 | fprintf(stderr, __VA_ARGS__); \ 12 | } while(0) 13 | 14 | static void dump_attr_data(void *data, int len, int type, int indent, int next_indent); 15 | 16 | static void 17 | dump_table(struct blob_attr *head, int len, int indent, bool array) 18 | { 19 | struct blob_attr *attr, *last_attr; 20 | struct blobmsg_hdr *hdr; 21 | 22 | indent_printf(indent, "{\n"); 23 | __blob_for_each_attr(attr, head, len) { 24 | hdr = blob_data(attr); 25 | if (!array) 26 | indent_printf(indent + 1, "%s : ", hdr->name); 27 | dump_attr_data(blobmsg_data(attr), blobmsg_data_len(attr), blob_id(attr), 0, indent + 1); 28 | last_attr = attr; 29 | } 30 | indent_printf(indent, "}\n"); 31 | } 32 | 33 | static void dump_attr_data(void *data, int len, int type, int indent, int next_indent) 34 | { 35 | switch(type) { 36 | case BLOBMSG_TYPE_STRING: 37 | indent_printf(indent, "%s\n", (char *) data); 38 | break; 39 | case BLOBMSG_TYPE_INT8: 40 | indent_printf(indent, "%d\n", *(uint8_t *)data); 41 | break; 42 | case BLOBMSG_TYPE_INT16: 43 | indent_printf(indent, "%d\n", *(uint16_t *)data); 44 | break; 45 | case BLOBMSG_TYPE_INT32: 46 | indent_printf(indent, "%d\n", *(uint32_t *)data); 47 | break; 48 | case BLOBMSG_TYPE_INT64: 49 | indent_printf(indent, "%lld\n", *(uint64_t *)data); 50 | break; 51 | case BLOBMSG_TYPE_TABLE: 52 | case BLOBMSG_TYPE_ARRAY: 53 | if (!indent) 54 | indent_printf(indent, "\n"); 55 | dump_table(data, len, next_indent, type == BLOBMSG_TYPE_ARRAY); 56 | break; 57 | } 58 | } 59 | 60 | enum { 61 | FOO_MESSAGE, 62 | FOO_LIST, 63 | FOO_TESTDATA 64 | }; 65 | 66 | static const struct blobmsg_policy pol[] = { 67 | [FOO_MESSAGE] = { 68 | .name = "message", 69 | .type = BLOBMSG_TYPE_STRING, 70 | }, 71 | [FOO_LIST] = { 72 | .name = "list", 73 | .type = BLOBMSG_TYPE_ARRAY, 74 | }, 75 | [FOO_TESTDATA] = { 76 | .name = "testdata", 77 | .type = BLOBMSG_TYPE_TABLE, 78 | }, 79 | }; 80 | 81 | #ifndef ARRAY_SIZE 82 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 83 | #endif 84 | 85 | static void dump_message(struct blob_buf *buf) 86 | { 87 | struct blob_attr *tb[ARRAY_SIZE(pol)]; 88 | 89 | if (blobmsg_parse(pol, ARRAY_SIZE(pol), tb, blob_data(buf->head), blob_len(buf->head)) != 0) { 90 | fprintf(stderr, "Parse failed\n"); 91 | return; 92 | } 93 | if (tb[FOO_MESSAGE]) 94 | fprintf(stderr, "Message: %s\n", (char *) blobmsg_data(tb[FOO_MESSAGE])); 95 | 96 | if (tb[FOO_LIST]) { 97 | fprintf(stderr, "List: "); 98 | dump_table(blobmsg_data(tb[FOO_LIST]), blobmsg_data_len(tb[FOO_LIST]), 0, true); 99 | } 100 | if (tb[FOO_TESTDATA]) { 101 | fprintf(stderr, "Testdata: "); 102 | dump_table(blobmsg_data(tb[FOO_TESTDATA]), blobmsg_data_len(tb[FOO_TESTDATA]), 0, false); 103 | } 104 | } 105 | 106 | static void 107 | fill_message(struct blob_buf *buf) 108 | { 109 | void *tbl; 110 | 111 | blobmsg_add_string(buf, "message", "Hello, world!"); 112 | 113 | tbl = blobmsg_open_table(buf, "testdata"); 114 | blobmsg_add_u32(buf, "hello", 1); 115 | blobmsg_add_string(buf, "world", "2"); 116 | blobmsg_close_table(buf, tbl); 117 | 118 | tbl = blobmsg_open_array(buf, "list"); 119 | blobmsg_add_u32(buf, NULL, 0); 120 | blobmsg_add_u32(buf, NULL, 1); 121 | blobmsg_add_u32(buf, NULL, 2); 122 | blobmsg_close_table(buf, tbl); 123 | } 124 | 125 | int main(int argc, char **argv) 126 | { 127 | static struct blob_buf buf; 128 | 129 | blobmsg_buf_init(&buf); 130 | fill_message(&buf); 131 | dump_message(&buf); 132 | fprintf(stderr, "json: %s\n", blobmsg_format_json(buf.head, false)); 133 | 134 | if (buf.buf) 135 | free(buf.buf); 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /examples/runqueue-example.c: -------------------------------------------------------------------------------- 1 | /* 2 | * runqueue-example.c 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "runqueue.h" 25 | 26 | static struct runqueue q; 27 | 28 | struct sleeper { 29 | struct runqueue_process proc; 30 | int val; 31 | }; 32 | 33 | static void q_empty(struct runqueue *q) 34 | { 35 | fprintf(stderr, "All done!\n"); 36 | uloop_end(); 37 | } 38 | 39 | static void q_sleep_run(struct runqueue *q, struct runqueue_task *t) 40 | { 41 | struct sleeper *s = container_of(t, struct sleeper, proc.task); 42 | char str[32]; 43 | pid_t pid; 44 | 45 | fprintf(stderr, "[%d/%d] start 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); 46 | 47 | pid = fork(); 48 | if (pid < 0) 49 | return; 50 | 51 | if (pid) { 52 | runqueue_process_add(q, &s->proc, pid); 53 | return; 54 | } 55 | 56 | sprintf(str, "%d", s->val); 57 | execlp("sleep", "sleep", str, NULL); 58 | exit(1); 59 | } 60 | 61 | static void q_sleep_cancel(struct runqueue *q, struct runqueue_task *t, int type) 62 | { 63 | struct sleeper *s = container_of(t, struct sleeper, proc.task); 64 | 65 | fprintf(stderr, "[%d/%d] cancel 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); 66 | runqueue_process_cancel_cb(q, t, type); 67 | } 68 | 69 | static void q_sleep_complete(struct runqueue *q, struct runqueue_task *p) 70 | { 71 | struct sleeper *s = container_of(p, struct sleeper, proc.task); 72 | 73 | fprintf(stderr, "[%d/%d] finish 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); 74 | free(s); 75 | } 76 | 77 | static void add_sleeper(int val) 78 | { 79 | static const struct runqueue_task_type sleeper_type = { 80 | .run = q_sleep_run, 81 | .cancel = q_sleep_cancel, 82 | .kill = runqueue_process_kill_cb, 83 | }; 84 | struct sleeper *s; 85 | 86 | s = calloc(1, sizeof(*s)); 87 | s->proc.task.type = &sleeper_type; 88 | s->proc.task.run_timeout = 500; 89 | s->proc.task.complete = q_sleep_complete; 90 | s->val = val; 91 | runqueue_task_add(&q, &s->proc.task, false); 92 | } 93 | 94 | int main(int argc, char **argv) 95 | { 96 | uloop_init(); 97 | 98 | runqueue_init(&q); 99 | q.empty_cb = q_empty; 100 | q.max_running_tasks = 1; 101 | 102 | if (argc > 1) 103 | q.max_running_tasks = atoi(argv[1]); 104 | 105 | add_sleeper(1); 106 | add_sleeper(1); 107 | add_sleeper(1); 108 | uloop_run(); 109 | uloop_done(); 110 | 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /examples/uloop-example.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local socket = require "socket" 4 | 5 | local uloop = require("uloop") 6 | uloop.init() 7 | 8 | local udp = socket.udp() 9 | udp:settimeout(0) 10 | udp:setsockname('*', 8080) 11 | 12 | -- timer example 1 13 | local timer 14 | function t() 15 | print("1000 ms timer run"); 16 | timer:set(1000) 17 | end 18 | timer = uloop.timer(t) 19 | timer:set(1000) 20 | 21 | -- timer example 2 22 | uloop.timer(function() print("2000 ms timer run"); end, 2000) 23 | 24 | -- timer example 3 25 | uloop.timer(function() print("3000 ms timer run"); end, 3000):cancel() 26 | 27 | -- process 28 | function p1(r) 29 | print("Process 1 completed") 30 | print(r) 31 | end 32 | 33 | function p2(r) 34 | print("Process 2 completed") 35 | print(r) 36 | end 37 | 38 | uloop.timer( 39 | function() 40 | uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=1"}, p1) 41 | end, 1000 42 | ) 43 | uloop.timer( 44 | function() 45 | uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=2"}, p2) 46 | end, 2000 47 | ) 48 | 49 | udp_ev = uloop.fd_add(udp, function(ufd, events) 50 | local words, msg_or_ip, port_or_nil = ufd:receivefrom() 51 | print('Recv UDP packet from '..msg_or_ip..':'..port_or_nil..' : '..words) 52 | if words == "Stop!" then 53 | udp_ev:cancel() 54 | end 55 | end, uloop.ULOOP_READ) 56 | 57 | udp_count = 0 58 | udp_send_timer = uloop.timer( 59 | function() 60 | local s = socket.udp() 61 | local words 62 | if udp_count > 3 then 63 | words = "Stop!" 64 | udp_send_timer:cancel() 65 | else 66 | words = 'Hello!' 67 | udp_send_timer:set(1000) 68 | end 69 | print('Send UDP packet to 127.0.0.1:8080 :'..words) 70 | s:sendto(words, '127.0.0.1', 8080) 71 | s:close() 72 | 73 | udp_count = udp_count + 1 74 | end, 3000 75 | ) 76 | 77 | uloop.run() 78 | 79 | -------------------------------------------------------------------------------- /examples/uloop_pid_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo $0 $* 4 | echo Environment: 5 | env 6 | 7 | sleep 2 8 | 9 | echo "stopping child" 10 | 11 | exit 5 12 | -------------------------------------------------------------------------------- /examples/ustream-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ustream.h" 10 | #include "uloop.h" 11 | #include "usock.h" 12 | 13 | static struct uloop_fd server; 14 | static const char *port = "10000"; 15 | struct client *next_client = NULL; 16 | 17 | struct client { 18 | struct sockaddr_in sin; 19 | 20 | struct ustream_fd s; 21 | int ctr; 22 | }; 23 | 24 | static void client_read_cb(struct ustream *s, int bytes) 25 | { 26 | struct client *cl = container_of(s, struct client, s.stream); 27 | struct ustream_buf *buf = s->r.head; 28 | char *newline, *str; 29 | 30 | do { 31 | str = ustream_get_read_buf(s, NULL); 32 | if (!str) 33 | break; 34 | 35 | newline = strchr(buf->data, '\n'); 36 | if (!newline) 37 | break; 38 | 39 | *newline = 0; 40 | ustream_printf(s, "%s\n", str); 41 | ustream_consume(s, newline + 1 - str); 42 | cl->ctr += newline + 1 - str; 43 | } while(1); 44 | 45 | if (s->w.data_bytes > 256 && !ustream_read_blocked(s)) { 46 | fprintf(stderr, "Block read, bytes: %d\n", s->w.data_bytes); 47 | ustream_set_read_blocked(s, true); 48 | } 49 | } 50 | 51 | static void client_close(struct ustream *s) 52 | { 53 | struct client *cl = container_of(s, struct client, s.stream); 54 | 55 | fprintf(stderr, "Connection closed\n"); 56 | ustream_free(s); 57 | close(cl->s.fd.fd); 58 | free(cl); 59 | } 60 | 61 | static void client_notify_write(struct ustream *s, int bytes) 62 | { 63 | fprintf(stderr, "Wrote %d bytes, pending: %d\n", bytes, s->w.data_bytes); 64 | 65 | if (s->w.data_bytes < 128 && ustream_read_blocked(s)) { 66 | fprintf(stderr, "Unblock read\n"); 67 | ustream_set_read_blocked(s, false); 68 | } 69 | } 70 | 71 | static void client_notify_state(struct ustream *s) 72 | { 73 | struct client *cl = container_of(s, struct client, s.stream); 74 | 75 | if (!s->eof) 76 | return; 77 | 78 | fprintf(stderr, "eof!, pending: %d, total: %d\n", s->w.data_bytes, cl->ctr); 79 | if (!s->w.data_bytes) 80 | return client_close(s); 81 | 82 | } 83 | 84 | static void server_cb(struct uloop_fd *fd, unsigned int events) 85 | { 86 | struct client *cl; 87 | unsigned int sl = sizeof(struct sockaddr_in); 88 | int sfd; 89 | 90 | if (!next_client) 91 | next_client = calloc(1, sizeof(*next_client)); 92 | 93 | cl = next_client; 94 | sfd = accept(server.fd, (struct sockaddr *) &cl->sin, &sl); 95 | if (sfd < 0) { 96 | fprintf(stderr, "Accept failed\n"); 97 | return; 98 | } 99 | 100 | cl->s.stream.string_data = true; 101 | cl->s.stream.notify_read = client_read_cb; 102 | cl->s.stream.notify_state = client_notify_state; 103 | cl->s.stream.notify_write = client_notify_write; 104 | ustream_fd_init(&cl->s, sfd); 105 | next_client = NULL; 106 | fprintf(stderr, "New connection\n"); 107 | } 108 | 109 | static int run_server(void) 110 | { 111 | 112 | server.cb = server_cb; 113 | server.fd = usock(USOCK_TCP | USOCK_SERVER | USOCK_IPV4ONLY | USOCK_NUMERIC, "127.0.0.1", port); 114 | if (server.fd < 0) { 115 | perror("usock"); 116 | return 1; 117 | } 118 | 119 | uloop_init(); 120 | uloop_fd_add(&server, ULOOP_READ); 121 | uloop_run(); 122 | 123 | return 0; 124 | } 125 | 126 | static int usage(const char *name) 127 | { 128 | fprintf(stderr, "Usage: %s -p \n", name); 129 | return 1; 130 | } 131 | 132 | int main(int argc, char **argv) 133 | { 134 | int ch; 135 | 136 | while ((ch = getopt(argc, argv, "p:")) != -1) { 137 | switch(ch) { 138 | case 'p': 139 | port = optarg; 140 | break; 141 | default: 142 | return usage(argv[0]); 143 | } 144 | } 145 | 146 | return run_server(); 147 | } 148 | -------------------------------------------------------------------------------- /jshn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011-2013 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifdef JSONC 17 | #include 18 | #else 19 | #include 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "list.h" 29 | 30 | #define MAX_VARLEN 256 31 | 32 | static const char *var_prefix = ""; 33 | static int var_prefix_len = 0; 34 | 35 | static int add_json_element(const char *key, json_object *obj); 36 | 37 | static int add_json_object(json_object *obj) 38 | { 39 | int ret = 0; 40 | 41 | json_object_object_foreach(obj, key, val) { 42 | ret = add_json_element(key, val); 43 | if (ret) 44 | break; 45 | } 46 | return ret; 47 | } 48 | 49 | static int add_json_array(struct array_list *a) 50 | { 51 | char seq[12]; 52 | int i, len; 53 | int ret; 54 | 55 | for (i = 0, len = array_list_length(a); i < len; i++) { 56 | sprintf(seq, "%d", i); 57 | ret = add_json_element(seq, array_list_get_idx(a, i)); 58 | if (ret) 59 | return ret; 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | static void add_json_string(const char *str) 66 | { 67 | char *ptr = (char *) str; 68 | int len; 69 | char *c; 70 | 71 | while ((c = strchr(ptr, '\'')) != NULL) { 72 | len = c - ptr; 73 | if (len > 0) 74 | fwrite(ptr, len, 1, stdout); 75 | ptr = c + 1; 76 | c = "'\\''"; 77 | fwrite(c, strlen(c), 1, stdout); 78 | } 79 | len = strlen(ptr); 80 | if (len > 0) 81 | fwrite(ptr, len, 1, stdout); 82 | } 83 | 84 | static void write_key_string(const char *key) 85 | { 86 | while (*key) { 87 | putc(isalnum(*key) ? *key : '_', stdout); 88 | key++; 89 | } 90 | } 91 | 92 | static int add_json_element(const char *key, json_object *obj) 93 | { 94 | char *type; 95 | 96 | if (!obj) 97 | return -1; 98 | 99 | switch (json_object_get_type(obj)) { 100 | case json_type_object: 101 | type = "object"; 102 | break; 103 | case json_type_array: 104 | type = "array"; 105 | break; 106 | case json_type_string: 107 | type = "string"; 108 | break; 109 | case json_type_boolean: 110 | type = "boolean"; 111 | break; 112 | case json_type_int: 113 | type = "int"; 114 | break; 115 | case json_type_double: 116 | type = "double"; 117 | break; 118 | default: 119 | return -1; 120 | } 121 | 122 | fprintf(stdout, "json_add_%s '", type); 123 | write_key_string(key); 124 | 125 | switch (json_object_get_type(obj)) { 126 | case json_type_object: 127 | fprintf(stdout, "';\n"); 128 | add_json_object(obj); 129 | fprintf(stdout, "json_close_object;\n"); 130 | break; 131 | case json_type_array: 132 | fprintf(stdout, "';\n"); 133 | add_json_array(json_object_get_array(obj)); 134 | fprintf(stdout, "json_close_array;\n"); 135 | break; 136 | case json_type_string: 137 | fprintf(stdout, "' '"); 138 | add_json_string(json_object_get_string(obj)); 139 | fprintf(stdout, "';\n"); 140 | break; 141 | case json_type_boolean: 142 | fprintf(stdout, "' %d;\n", json_object_get_boolean(obj)); 143 | break; 144 | case json_type_int: 145 | fprintf(stdout, "' %d;\n", json_object_get_int(obj)); 146 | break; 147 | case json_type_double: 148 | fprintf(stdout, "' %lf;\n", json_object_get_double(obj)); 149 | break; 150 | default: 151 | return -1; 152 | } 153 | 154 | return 0; 155 | } 156 | 157 | static int jshn_parse(const char *str) 158 | { 159 | json_object *obj; 160 | 161 | obj = json_tokener_parse(str); 162 | if (is_error(obj) || json_object_get_type(obj) != json_type_object) { 163 | fprintf(stderr, "Failed to parse message data\n"); 164 | return 1; 165 | } 166 | fprintf(stdout, "json_init;\n"); 167 | add_json_object(obj); 168 | fflush(stdout); 169 | 170 | return 0; 171 | } 172 | 173 | static char *get_keys(const char *prefix) 174 | { 175 | char *keys; 176 | 177 | keys = alloca(var_prefix_len + strlen(prefix) + sizeof("KEYS_") + 1); 178 | sprintf(keys, "%sKEYS_%s", var_prefix, prefix); 179 | return getenv(keys); 180 | } 181 | 182 | static void get_var(const char *prefix, const char **name, char **var, char **type) 183 | { 184 | char *tmpname, *varname; 185 | 186 | tmpname = alloca(var_prefix_len + strlen(prefix) + 1 + strlen(*name) + 1 + sizeof("TYPE_")); 187 | 188 | sprintf(tmpname, "%s%s_%s", var_prefix, prefix, *name); 189 | *var = getenv(tmpname); 190 | 191 | sprintf(tmpname, "%sTYPE_%s_%s", var_prefix, prefix, *name); 192 | *type = getenv(tmpname); 193 | 194 | sprintf(tmpname, "%sNAME_%s_%s", var_prefix, prefix, *name); 195 | varname = getenv(tmpname); 196 | if (varname) 197 | *name = varname; 198 | } 199 | 200 | static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array); 201 | 202 | static void jshn_add_object_var(json_object *obj, bool array, const char *prefix, const char *name) 203 | { 204 | json_object *new; 205 | char *var, *type; 206 | 207 | get_var(prefix, &name, &var, &type); 208 | if (!var || !type) 209 | return; 210 | 211 | if (!strcmp(type, "array")) { 212 | new = json_object_new_array(); 213 | jshn_add_objects(new, var, true); 214 | } else if (!strcmp(type, "object")) { 215 | new = json_object_new_object(); 216 | jshn_add_objects(new, var, false); 217 | } else if (!strcmp(type, "string")) { 218 | new = json_object_new_string(var); 219 | } else if (!strcmp(type, "int")) { 220 | new = json_object_new_int(atoi(var)); 221 | } else if (!strcmp(type, "double")) { 222 | new = json_object_new_double(strtod(var, NULL)); 223 | } else if (!strcmp(type, "boolean")) { 224 | new = json_object_new_boolean(!!atoi(var)); 225 | } else { 226 | return; 227 | } 228 | 229 | if (array) 230 | json_object_array_add(obj, new); 231 | else 232 | json_object_object_add(obj, name, new); 233 | } 234 | 235 | static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array) 236 | { 237 | char *keys, *key, *brk; 238 | 239 | keys = get_keys(prefix); 240 | if (!keys || !obj) 241 | goto out; 242 | 243 | for (key = strtok_r(keys, " ", &brk); key; 244 | key = strtok_r(NULL, " ", &brk)) { 245 | jshn_add_object_var(obj, array, prefix, key); 246 | } 247 | 248 | out: 249 | return obj; 250 | } 251 | 252 | static int jshn_format(bool no_newline) 253 | { 254 | json_object *obj; 255 | 256 | obj = json_object_new_object(); 257 | jshn_add_objects(obj, "JSON_VAR", false); 258 | fprintf(stdout, "%s%s", json_object_to_json_string(obj), 259 | no_newline ? "" : "\n"); 260 | json_object_put(obj); 261 | return 0; 262 | } 263 | 264 | static int usage(const char *progname) 265 | { 266 | fprintf(stderr, "Usage: %s [-n] -r |-w\n", progname); 267 | return 2; 268 | } 269 | 270 | int main(int argc, char **argv) 271 | { 272 | bool no_newline = false; 273 | int ch; 274 | 275 | while ((ch = getopt(argc, argv, "p:nr:w")) != -1) { 276 | switch(ch) { 277 | case 'p': 278 | var_prefix = optarg; 279 | var_prefix_len = strlen(var_prefix); 280 | break; 281 | case 'r': 282 | return jshn_parse(optarg); 283 | case 'w': 284 | return jshn_format(no_newline); 285 | case 'n': 286 | no_newline = true; 287 | break; 288 | default: 289 | return usage(argv[0]); 290 | } 291 | } 292 | return usage(argv[0]); 293 | } 294 | -------------------------------------------------------------------------------- /json_script.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include 17 | #include 18 | 19 | #include "avl-cmp.h" 20 | #include "json_script.h" 21 | 22 | struct json_call { 23 | struct json_script_ctx *ctx; 24 | struct blob_attr *vars; 25 | unsigned int seq; 26 | }; 27 | 28 | struct json_handler { 29 | const char *name; 30 | int (*cb)(struct json_call *call, struct blob_attr *cur); 31 | }; 32 | 33 | static int json_process_expr(struct json_call *call, struct blob_attr *cur); 34 | static int json_process_cmd(struct json_call *call, struct blob_attr *cur); 35 | 36 | struct json_script_file * 37 | json_script_file_from_blobmsg(const char *name, void *data, int len) 38 | { 39 | struct json_script_file *f; 40 | char *new_name; 41 | int name_len = 0; 42 | 43 | if (name) 44 | name_len = strlen(name) + 1; 45 | 46 | f = calloc_a(sizeof(*f) + len, &new_name, name_len); 47 | memcpy(f->data, data, len); 48 | if (name) 49 | f->avl.key = strcpy(new_name, name); 50 | 51 | return f; 52 | } 53 | 54 | static struct json_script_file * 55 | json_script_get_file(struct json_script_ctx *ctx, const char *filename) 56 | { 57 | struct json_script_file *f; 58 | 59 | f = avl_find_element(&ctx->files, filename, f, avl); 60 | if (f) 61 | return f; 62 | 63 | f = ctx->handle_file(ctx, filename); 64 | if (!f) 65 | return NULL; 66 | 67 | avl_insert(&ctx->files, &f->avl); 68 | return f; 69 | } 70 | 71 | static void __json_script_run(struct json_call *call, struct json_script_file *file, 72 | struct blob_attr *context) 73 | { 74 | struct json_script_ctx *ctx = call->ctx; 75 | 76 | if (file->seq == call->seq) { 77 | if (context) 78 | ctx->handle_error(ctx, "Recursive include", context); 79 | 80 | return; 81 | } 82 | 83 | file->seq = call->seq; 84 | while (file) { 85 | json_process_cmd(call, file->data); 86 | file = file->next; 87 | } 88 | } 89 | 90 | const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars, 91 | const char *name) 92 | { 93 | struct blob_attr *cur; 94 | int rem; 95 | 96 | blobmsg_for_each_attr(cur, vars, rem) { 97 | if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) 98 | continue; 99 | 100 | if (strcmp(blobmsg_name(cur), name) != 0) 101 | continue; 102 | 103 | return blobmsg_data(cur); 104 | } 105 | 106 | return ctx->handle_var(ctx, name, vars); 107 | } 108 | 109 | static const char * 110 | msg_find_var(struct json_call *call, const char *name) 111 | { 112 | return json_script_find_var(call->ctx, call->vars, name); 113 | } 114 | 115 | static void 116 | json_get_tuple(struct blob_attr *cur, struct blob_attr **tb, int t1, int t2) 117 | { 118 | static struct blobmsg_policy expr_tuple[3] = { 119 | { .type = BLOBMSG_TYPE_STRING }, 120 | {}, 121 | {}, 122 | }; 123 | 124 | expr_tuple[1].type = t1; 125 | expr_tuple[2].type = t2; 126 | blobmsg_parse_array(expr_tuple, 3, tb, blobmsg_data(cur), blobmsg_data_len(cur)); 127 | } 128 | 129 | static int handle_if(struct json_call *call, struct blob_attr *expr) 130 | { 131 | struct blob_attr *tb[4]; 132 | int ret; 133 | 134 | static const struct blobmsg_policy if_tuple[4] = { 135 | { .type = BLOBMSG_TYPE_STRING }, 136 | { .type = BLOBMSG_TYPE_ARRAY }, 137 | { .type = BLOBMSG_TYPE_ARRAY }, 138 | { .type = BLOBMSG_TYPE_ARRAY }, 139 | }; 140 | 141 | blobmsg_parse_array(if_tuple, 4, tb, blobmsg_data(expr), blobmsg_data_len(expr)); 142 | 143 | if (!tb[1] || !tb[2]) 144 | return 0; 145 | 146 | ret = json_process_expr(call, tb[1]); 147 | if (ret < 0) 148 | return 0; 149 | 150 | if (ret) 151 | return json_process_cmd(call, tb[2]); 152 | 153 | if (!tb[3]) 154 | return 0; 155 | 156 | return json_process_cmd(call, tb[3]); 157 | } 158 | 159 | static int handle_case(struct json_call *call, struct blob_attr *expr) 160 | { 161 | struct blob_attr *tb[3], *cur; 162 | const char *var; 163 | int rem; 164 | 165 | json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE); 166 | if (!tb[1] || !tb[2]) 167 | return 0; 168 | 169 | var = msg_find_var(call, blobmsg_data(tb[1])); 170 | if (!var) 171 | return 0; 172 | 173 | blobmsg_for_each_attr(cur, tb[2], rem) { 174 | if (!strcmp(var, blobmsg_name(cur))) 175 | return json_process_cmd(call, cur); 176 | } 177 | 178 | return 0; 179 | } 180 | 181 | static int handle_return(struct json_call *call, struct blob_attr *expr) 182 | { 183 | return -2; 184 | } 185 | 186 | static int handle_include(struct json_call *call, struct blob_attr *expr) 187 | { 188 | struct blob_attr *tb[3]; 189 | struct json_script_file *f; 190 | 191 | json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); 192 | if (!tb[1]) 193 | return 0; 194 | 195 | f = json_script_get_file(call->ctx, blobmsg_data(tb[1])); 196 | if (!f) 197 | return 0; 198 | 199 | __json_script_run(call, f, expr); 200 | return 0; 201 | } 202 | 203 | static const struct json_handler cmd[] = { 204 | { "if", handle_if }, 205 | { "case", handle_case }, 206 | { "return", handle_return }, 207 | { "include", handle_include }, 208 | }; 209 | 210 | static int eq_regex_cmp(const char *str, const char *pattern, bool regex) 211 | { 212 | regex_t reg; 213 | int ret; 214 | 215 | if (!regex) 216 | return !strcmp(str, pattern); 217 | 218 | if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB)) 219 | return 0; 220 | 221 | ret = !regexec(®, str, 0, NULL, 0); 222 | regfree(®); 223 | 224 | return ret; 225 | } 226 | 227 | static int expr_eq_regex(struct json_call *call, struct blob_attr *expr, bool regex) 228 | { 229 | struct json_script_ctx *ctx = call->ctx; 230 | struct blob_attr *tb[3], *cur; 231 | const char *var; 232 | int rem; 233 | 234 | json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); 235 | if (!tb[1] || !tb[2]) 236 | return -1; 237 | 238 | var = msg_find_var(call, blobmsg_data(tb[1])); 239 | if (!var) 240 | return 0; 241 | 242 | switch(blobmsg_type(tb[2])) { 243 | case BLOBMSG_TYPE_STRING: 244 | return eq_regex_cmp(var, blobmsg_data(tb[2]), regex); 245 | case BLOBMSG_TYPE_ARRAY: 246 | blobmsg_for_each_attr(cur, tb[2], rem) { 247 | if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { 248 | ctx->handle_error(ctx, "Unexpected element type", cur); 249 | return -1; 250 | } 251 | 252 | if (eq_regex_cmp(var, blobmsg_data(cur), regex)) 253 | return 1; 254 | } 255 | return 0; 256 | default: 257 | ctx->handle_error(ctx, "Unexpected element type", tb[2]); 258 | return -1; 259 | } 260 | } 261 | 262 | static int handle_expr_eq(struct json_call *call, struct blob_attr *expr) 263 | { 264 | return expr_eq_regex(call, expr, false); 265 | } 266 | 267 | static int handle_expr_regex(struct json_call *call, struct blob_attr *expr) 268 | { 269 | return expr_eq_regex(call, expr, true); 270 | } 271 | 272 | static int handle_expr_has(struct json_call *call, struct blob_attr *expr) 273 | { 274 | struct json_script_ctx *ctx = call->ctx; 275 | struct blob_attr *tb[3], *cur; 276 | int rem; 277 | 278 | json_get_tuple(expr, tb, 0, 0); 279 | if (!tb[1]) 280 | return -1; 281 | 282 | switch(blobmsg_type(tb[1])) { 283 | case BLOBMSG_TYPE_STRING: 284 | return !!msg_find_var(call, blobmsg_data(tb[1])); 285 | case BLOBMSG_TYPE_ARRAY: 286 | blobmsg_for_each_attr(cur, tb[1], rem) { 287 | if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { 288 | ctx->handle_error(ctx, "Unexpected element type", cur); 289 | return -1; 290 | } 291 | 292 | if (msg_find_var(call, blobmsg_data(cur))) 293 | return 1; 294 | } 295 | return 0; 296 | default: 297 | ctx->handle_error(ctx, "Unexpected element type", tb[1]); 298 | return -1; 299 | } 300 | } 301 | 302 | static int expr_and_or(struct json_call *call, struct blob_attr *expr, bool and) 303 | { 304 | struct blob_attr *cur; 305 | int ret, rem; 306 | int i = 0; 307 | 308 | blobmsg_for_each_attr(cur, expr, rem) { 309 | if (i++ < 1) 310 | continue; 311 | 312 | ret = json_process_expr(call, cur); 313 | if (ret < 0) 314 | return ret; 315 | 316 | if (ret != and) 317 | return ret; 318 | } 319 | 320 | return and; 321 | } 322 | 323 | static int handle_expr_and(struct json_call *call, struct blob_attr *expr) 324 | { 325 | return expr_and_or(call, expr, 1); 326 | } 327 | 328 | static int handle_expr_or(struct json_call *call, struct blob_attr *expr) 329 | { 330 | return expr_and_or(call, expr, 0); 331 | } 332 | 333 | static int handle_expr_not(struct json_call *call, struct blob_attr *expr) 334 | { 335 | struct blob_attr *tb[3]; 336 | 337 | json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0); 338 | if (!tb[1]) 339 | return -1; 340 | 341 | return json_process_expr(call, tb[1]); 342 | } 343 | 344 | static const struct json_handler expr[] = { 345 | { "eq", handle_expr_eq }, 346 | { "regex", handle_expr_regex }, 347 | { "has", handle_expr_has }, 348 | { "and", handle_expr_and }, 349 | { "or", handle_expr_or }, 350 | { "not", handle_expr_not }, 351 | }; 352 | 353 | static int 354 | __json_process_type(struct json_call *call, struct blob_attr *cur, 355 | const struct json_handler *h, int n, bool *found) 356 | { 357 | const char *name = blobmsg_data(blobmsg_data(cur)); 358 | int i; 359 | 360 | for (i = 0; i < n; i++) { 361 | if (strcmp(name, h[i].name) != 0) 362 | continue; 363 | 364 | *found = true; 365 | return h[i].cb(call, cur); 366 | } 367 | 368 | *found = false; 369 | return -1; 370 | } 371 | 372 | static int json_process_expr(struct json_call *call, struct blob_attr *cur) 373 | { 374 | struct json_script_ctx *ctx = call->ctx; 375 | bool found; 376 | int ret; 377 | 378 | if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || 379 | blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) { 380 | ctx->handle_error(ctx, "Unexpected element type", cur); 381 | return -1; 382 | } 383 | 384 | ret = __json_process_type(call, cur, expr, ARRAY_SIZE(expr), &found); 385 | if (!found) 386 | ctx->handle_error(ctx, "Unknown expression type", cur); 387 | 388 | return ret; 389 | } 390 | 391 | static int cmd_add_string(struct json_call *call, const char *pattern) 392 | { 393 | struct json_script_ctx *ctx = call->ctx; 394 | char *dest, *next, *str; 395 | int len = 0; 396 | bool var = false; 397 | char c = '%'; 398 | 399 | dest = blobmsg_alloc_string_buffer(&ctx->buf, NULL, 1); 400 | next = alloca(strlen(pattern) + 1); 401 | strcpy(next, pattern); 402 | 403 | for (str = next; str; str = next) { 404 | const char *cur; 405 | char *end; 406 | int cur_len = 0; 407 | bool cur_var = var; 408 | 409 | end = strchr(str, '%'); 410 | if (end) { 411 | *end = 0; 412 | next = end + 1; 413 | var = !var; 414 | } else { 415 | end = str + strlen(str); 416 | next = NULL; 417 | } 418 | 419 | if (cur_var) { 420 | if (next > str) { 421 | cur = msg_find_var(call, str); 422 | if (!cur) 423 | continue; 424 | 425 | cur_len = strlen(cur); 426 | } else { 427 | cur = &c; 428 | cur_len = 1; 429 | } 430 | } else { 431 | if (str == end) 432 | continue; 433 | 434 | cur = str; 435 | cur_len = end - str; 436 | } 437 | 438 | dest = blobmsg_realloc_string_buffer(&ctx->buf, cur_len + 1); 439 | memcpy(dest + len, cur, cur_len); 440 | len += cur_len; 441 | } 442 | 443 | if (var) 444 | return -1; 445 | 446 | dest[len] = 0; 447 | blobmsg_add_string_buffer(&ctx->buf); 448 | return 0; 449 | } 450 | 451 | static int cmd_process_strings(struct json_call *call, struct blob_attr *attr) 452 | { 453 | struct json_script_ctx *ctx = call->ctx; 454 | struct blob_attr *cur; 455 | int args = -1; 456 | int rem, ret; 457 | void *c; 458 | 459 | blob_buf_init(&ctx->buf, 0); 460 | c = blobmsg_open_array(&ctx->buf, NULL); 461 | blobmsg_for_each_attr(cur, attr, rem) { 462 | if (args++ < 0) 463 | continue; 464 | 465 | if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { 466 | ctx->handle_error(ctx, "Invalid argument in command", attr); 467 | return -1; 468 | } 469 | 470 | ret = cmd_add_string(call, blobmsg_data(cur)); 471 | if (ret) { 472 | ctx->handle_error(ctx, "Unterminated variable reference in string", attr); 473 | return ret; 474 | } 475 | } 476 | 477 | blobmsg_close_array(&ctx->buf, c); 478 | 479 | return 0; 480 | } 481 | 482 | static int __json_process_cmd(struct json_call *call, struct blob_attr *cur) 483 | { 484 | struct json_script_ctx *ctx = call->ctx; 485 | const char *name; 486 | bool found; 487 | int ret; 488 | 489 | if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || 490 | blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) { 491 | ctx->handle_error(ctx, "Unexpected element type", cur); 492 | return -1; 493 | } 494 | 495 | ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found); 496 | if (found) 497 | return ret; 498 | 499 | name = blobmsg_data(blobmsg_data(cur)); 500 | ret = cmd_process_strings(call, cur); 501 | if (ret) 502 | return ret; 503 | 504 | ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars); 505 | 506 | return 0; 507 | } 508 | 509 | static int json_process_cmd(struct json_call *call, struct blob_attr *block) 510 | { 511 | struct json_script_ctx *ctx = call->ctx; 512 | struct blob_attr *cur; 513 | int rem; 514 | int ret; 515 | int i = 0; 516 | 517 | if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) { 518 | ctx->handle_error(ctx, "Unexpected element type", block); 519 | return -1; 520 | } 521 | 522 | blobmsg_for_each_attr(cur, block, rem) { 523 | switch(blobmsg_type(cur)) { 524 | case BLOBMSG_TYPE_STRING: 525 | if (!i) 526 | return __json_process_cmd(call, block); 527 | default: 528 | ret = json_process_cmd(call, cur); 529 | if (ret < -1) 530 | return ret; 531 | break; 532 | } 533 | i++; 534 | } 535 | 536 | return 0; 537 | } 538 | 539 | void json_script_run(struct json_script_ctx *ctx, const char *name, 540 | struct blob_attr *vars) 541 | { 542 | struct json_script_file *file; 543 | static unsigned int _seq = 0; 544 | struct json_call call = { 545 | .ctx = ctx, 546 | .vars = vars, 547 | .seq = ++_seq, 548 | }; 549 | 550 | /* overflow */ 551 | if (!call.seq) 552 | call.seq = ++_seq; 553 | 554 | file = json_script_get_file(ctx, name); 555 | if (!file) 556 | return; 557 | 558 | __json_script_run(&call, file, NULL); 559 | } 560 | 561 | static void __json_script_file_free(struct json_script_ctx *ctx, struct json_script_file *f) 562 | { 563 | struct json_script_file *next; 564 | 565 | for (next = f->next; f; f = next, next = f->next) 566 | free(f); 567 | } 568 | 569 | void 570 | json_script_free(struct json_script_ctx *ctx) 571 | { 572 | struct json_script_file *f, *next; 573 | 574 | avl_remove_all_elements(&ctx->files, f, avl, next) 575 | __json_script_file_free(ctx, f); 576 | 577 | blob_buf_free(&ctx->buf); 578 | } 579 | 580 | static void 581 | __default_handle_error(struct json_script_ctx *ctx, const char *msg, 582 | struct blob_attr *context) 583 | { 584 | } 585 | 586 | static const char * 587 | __default_handle_var(struct json_script_ctx *ctx, const char *name, 588 | struct blob_attr *vars) 589 | { 590 | return NULL; 591 | } 592 | 593 | static int 594 | __default_handle_expr(struct json_script_ctx *ctx, const char *name, 595 | struct blob_attr *expr, struct blob_attr *vars) 596 | { 597 | return -1; 598 | } 599 | 600 | static struct json_script_file * 601 | __default_handle_file(struct json_script_ctx *ctx, const char *name) 602 | { 603 | return NULL; 604 | } 605 | 606 | void json_script_init(struct json_script_ctx *ctx) 607 | { 608 | avl_init(&ctx->files, avl_strcmp, false, NULL); 609 | 610 | if (!ctx->handle_error) 611 | ctx->handle_error = __default_handle_error; 612 | 613 | if (!ctx->handle_var) 614 | ctx->handle_var = __default_handle_var; 615 | 616 | if (!ctx->handle_expr) 617 | ctx->handle_expr = __default_handle_expr; 618 | 619 | if (!ctx->handle_file) 620 | ctx->handle_file = __default_handle_file; 621 | } 622 | -------------------------------------------------------------------------------- /json_script.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef __JSON_SCRIPT_H 17 | #define __JSON_SCRIPT_H 18 | 19 | #include "avl.h" 20 | #include "blob.h" 21 | #include "blobmsg.h" 22 | #include "utils.h" 23 | 24 | struct json_script_file; 25 | 26 | struct json_script_ctx { 27 | struct avl_tree files; 28 | struct blob_buf buf; 29 | 30 | uint32_t run_seq; 31 | 32 | /* 33 | * handle_command: handle a command that was not recognized by the 34 | * json_script core (required) 35 | * 36 | * @cmd: blobmsg container of the processed command 37 | * @vars: blobmsg container of current run variables 38 | */ 39 | void (*handle_command)(struct json_script_ctx *ctx, const char *name, 40 | struct blob_attr *cmd, struct blob_attr *vars); 41 | 42 | /* 43 | * handle_expr: handle an expression that was not recognized by the 44 | * json_script core (optional) 45 | * 46 | * @expr: blobmsg container of the processed expression 47 | * @vars: blobmsg container of current runtime variables 48 | */ 49 | int (*handle_expr)(struct json_script_ctx *ctx, const char *name, 50 | struct blob_attr *expr, struct blob_attr *vars); 51 | 52 | /* 53 | * handle_var - look up a variable that's not part of the runtime 54 | * variable set (optional) 55 | */ 56 | const char *(*handle_var)(struct json_script_ctx *ctx, const char *name, 57 | struct blob_attr *vars); 58 | 59 | /* 60 | * handle_file - load a file by filename (optional) 61 | * 62 | * in case of wildcards, it can return a chain of json_script files 63 | * linked via the ::next pointer. Only the first json_script file is 64 | * added to the tree. 65 | */ 66 | struct json_script_file *(*handle_file)(struct json_script_ctx *ctx, 67 | const char *name); 68 | 69 | /* 70 | * handle_error - handle a processing error in a command or expression 71 | * (optional) 72 | * 73 | * @msg: error message 74 | * @context: source file context of the error (blobmsg container) 75 | */ 76 | void (*handle_error)(struct json_script_ctx *ctx, const char *msg, 77 | struct blob_attr *context); 78 | }; 79 | 80 | struct json_script_file { 81 | struct avl_node avl; 82 | struct json_script_file *next; 83 | 84 | unsigned int seq; 85 | struct blob_attr data[]; 86 | }; 87 | 88 | void json_script_init(struct json_script_ctx *ctx); 89 | void json_script_free(struct json_script_ctx *ctx); 90 | 91 | /* 92 | * json_script_run - run a json script with a set of runtime variables 93 | * 94 | * @filename: initial filename to run 95 | * @vars: blobmsg container of the current runtime variables 96 | */ 97 | void json_script_run(struct json_script_ctx *ctx, const char *filename, 98 | struct blob_attr *vars); 99 | 100 | struct json_script_file * 101 | json_script_file_from_blobmsg(const char *name, void *data, int len); 102 | 103 | /* 104 | * json_script_find_var - helper function to find a runtime variable from 105 | * the list passed by json_script user. 106 | * It is intended to be used by the .handle_var callback 107 | */ 108 | const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars, 109 | const char *name); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2011 Felix Fietkau 3 | * Copyright (c) 2010 Isilon Systems, Inc. 4 | * Copyright (c) 2010 iX Systems, Inc. 5 | * Copyright (c) 2010 Panasas, Inc. 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice unmodified, this list of conditions, and the following 13 | * disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifndef _LINUX_LIST_H_ 30 | #define _LINUX_LIST_H_ 31 | 32 | #include 33 | #include 34 | 35 | #define prefetch(x) 36 | 37 | #ifndef container_of 38 | #define container_of(ptr, type, member) \ 39 | ({ \ 40 | const typeof(((type *) NULL)->member) *__mptr = (ptr); \ 41 | (type *) ((char *) __mptr - offsetof(type, member)); \ 42 | }) 43 | #endif 44 | 45 | struct list_head { 46 | struct list_head *next; 47 | struct list_head *prev; 48 | }; 49 | 50 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 51 | #undef LIST_HEAD 52 | #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) 53 | 54 | static inline void 55 | INIT_LIST_HEAD(struct list_head *list) 56 | { 57 | list->next = list->prev = list; 58 | } 59 | 60 | static inline bool 61 | list_empty(const struct list_head *head) 62 | { 63 | return (head->next == head); 64 | } 65 | 66 | static inline bool 67 | list_is_first(const struct list_head *list, 68 | const struct list_head *head) 69 | { 70 | return list->prev == head; 71 | } 72 | 73 | static inline bool 74 | list_is_last(const struct list_head *list, 75 | const struct list_head *head) 76 | { 77 | return list->next == head; 78 | } 79 | 80 | static inline void 81 | _list_del(struct list_head *entry) 82 | { 83 | entry->next->prev = entry->prev; 84 | entry->prev->next = entry->next; 85 | } 86 | 87 | static inline void 88 | list_del(struct list_head *entry) 89 | { 90 | _list_del(entry); 91 | entry->next = entry->prev = NULL; 92 | } 93 | 94 | static inline void 95 | _list_add(struct list_head *_new, struct list_head *prev, 96 | struct list_head *next) 97 | { 98 | 99 | next->prev = _new; 100 | _new->next = next; 101 | _new->prev = prev; 102 | prev->next = _new; 103 | } 104 | 105 | static inline void 106 | list_del_init(struct list_head *entry) 107 | { 108 | _list_del(entry); 109 | INIT_LIST_HEAD(entry); 110 | } 111 | 112 | #define list_entry(ptr, type, field) container_of(ptr, type, field) 113 | #define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field) 114 | #define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field) 115 | 116 | #define list_for_each(p, head) \ 117 | for (p = (head)->next; p != (head); p = p->next) 118 | 119 | #define list_for_each_safe(p, n, head) \ 120 | for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next) 121 | 122 | #define list_for_each_entry(p, h, field) \ 123 | for (p = list_first_entry(h, typeof(*p), field); &p->field != (h); \ 124 | p = list_entry(p->field.next, typeof(*p), field)) 125 | 126 | #define list_for_each_entry_safe(p, n, h, field) \ 127 | for (p = list_first_entry(h, typeof(*p), field), \ 128 | n = list_entry(p->field.next, typeof(*p), field); &p->field != (h);\ 129 | p = n, n = list_entry(n->field.next, typeof(*n), field)) 130 | 131 | #define list_for_each_entry_reverse(p, h, field) \ 132 | for (p = list_last_entry(h, typeof(*p), field); &p->field != (h); \ 133 | p = list_entry(p->field.prev, typeof(*p), field)) 134 | 135 | #define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev) 136 | #define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev) 137 | 138 | static inline void 139 | list_add(struct list_head *_new, struct list_head *head) 140 | { 141 | _list_add(_new, head, head->next); 142 | } 143 | 144 | static inline void 145 | list_add_tail(struct list_head *_new, struct list_head *head) 146 | { 147 | _list_add(_new, head->prev, head); 148 | } 149 | 150 | static inline void 151 | list_move(struct list_head *list, struct list_head *head) 152 | { 153 | _list_del(list); 154 | list_add(list, head); 155 | } 156 | 157 | static inline void 158 | list_move_tail(struct list_head *entry, struct list_head *head) 159 | { 160 | _list_del(entry); 161 | list_add_tail(entry, head); 162 | } 163 | 164 | static inline void 165 | _list_splice(const struct list_head *list, struct list_head *prev, 166 | struct list_head *next) 167 | { 168 | struct list_head *first; 169 | struct list_head *last; 170 | 171 | if (list_empty(list)) 172 | return; 173 | 174 | first = list->next; 175 | last = list->prev; 176 | first->prev = prev; 177 | prev->next = first; 178 | last->next = next; 179 | next->prev = last; 180 | } 181 | 182 | static inline void 183 | list_splice(const struct list_head *list, struct list_head *head) 184 | { 185 | _list_splice(list, head, head->next); 186 | } 187 | 188 | static inline void 189 | list_splice_tail(struct list_head *list, struct list_head *head) 190 | { 191 | _list_splice(list, head->prev, head); 192 | } 193 | 194 | static inline void 195 | list_splice_init(struct list_head *list, struct list_head *head) 196 | { 197 | _list_splice(list, head, head->next); 198 | INIT_LIST_HEAD(list); 199 | } 200 | 201 | static inline void 202 | list_splice_tail_init(struct list_head *list, struct list_head *head) 203 | { 204 | _list_splice(list, head->prev, head); 205 | INIT_LIST_HEAD(list); 206 | } 207 | 208 | #endif /* _LINUX_LIST_H_ */ 209 | -------------------------------------------------------------------------------- /lua/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | PROJECT(uloop C) 4 | 5 | SET(CMAKE_INSTALL_PREFIX /) 6 | 7 | IF(NOT LUA_CFLAGS) 8 | FIND_PROGRAM(PKG_CONFIG pkg-config) 9 | IF(PKG_CONFIG) 10 | EXECUTE_PROCESS( 11 | COMMAND pkg-config --silence-errors --cflags lua5.1 12 | OUTPUT_VARIABLE LUA_CFLAGS 13 | OUTPUT_STRIP_TRAILING_WHITESPACE 14 | ) 15 | ENDIF() 16 | ENDIF() 17 | 18 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I.. ${LUA_CFLAGS}) 19 | LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 20 | 21 | IF(APPLE) 22 | ADD_DEFINITIONS(-I/opt/local/include) 23 | SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") 24 | ENDIF(APPLE) 25 | 26 | IF(NOT LUAPATH) 27 | EXECUTE_PROCESS( 28 | COMMAND lua -e "for k in string.gmatch(package.cpath .. \";\", \"([^;]+)/..so;\") do if k:sub(1,1) == \"/\" then print(k) break end end" 29 | OUTPUT_VARIABLE LUAPATH 30 | RESULT_VARIABLE LUA_CHECK_RES 31 | OUTPUT_STRIP_TRAILING_WHITESPACE 32 | ) 33 | 34 | IF(BUILD_LUA) 35 | IF(NOT ${LUA_CHECK_RES} EQUAL 0 OR "${LUAPATH}" EQUAL "") 36 | MESSAGE(SEND_ERROR "Lua was not found on your system") 37 | ENDIF() 38 | ENDIF() 39 | ENDIF() 40 | 41 | IF(BUILD_LUA) 42 | ADD_LIBRARY(uloop_lua MODULE uloop.c) 43 | SET_TARGET_PROPERTIES(uloop_lua PROPERTIES 44 | OUTPUT_NAME uloop 45 | PREFIX "" 46 | ) 47 | TARGET_LINK_LIBRARIES(uloop_lua ubox) 48 | 49 | INSTALL(TARGETS uloop_lua 50 | LIBRARY DESTINATION ${LUAPATH} 51 | ) 52 | ENDIF() 53 | -------------------------------------------------------------------------------- /lua/uloop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 John Crispin 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "../uloop.h" 26 | #include "../list.h" 27 | 28 | struct lua_uloop_fd { 29 | struct uloop_fd fd; 30 | int r; 31 | int fd_r; 32 | }; 33 | 34 | struct lua_uloop_timeout { 35 | struct uloop_timeout t; 36 | int r; 37 | }; 38 | 39 | struct lua_uloop_process { 40 | struct uloop_process p; 41 | int r; 42 | }; 43 | 44 | static lua_State *state; 45 | 46 | static void ul_timer_cb(struct uloop_timeout *t) 47 | { 48 | struct lua_uloop_timeout *tout = container_of(t, struct lua_uloop_timeout, t); 49 | 50 | lua_getglobal(state, "__uloop_cb"); 51 | lua_rawgeti(state, -1, tout->r); 52 | lua_remove(state, -2); 53 | 54 | lua_call(state, 0, 0); 55 | 56 | } 57 | 58 | static int ul_timer_set(lua_State *L) 59 | { 60 | struct lua_uloop_timeout *tout; 61 | double set; 62 | 63 | if (!lua_isnumber(L, -1)) { 64 | lua_pushstring(L, "invalid arg list"); 65 | lua_error(L); 66 | 67 | return 0; 68 | } 69 | 70 | set = lua_tointeger(L, -1); 71 | tout = lua_touserdata(L, 1); 72 | uloop_timeout_set(&tout->t, set); 73 | 74 | return 1; 75 | } 76 | 77 | static int ul_timer_free(lua_State *L) 78 | { 79 | struct lua_uloop_timeout *tout = lua_touserdata(L, 1); 80 | 81 | uloop_timeout_cancel(&tout->t); 82 | 83 | /* obj.__index.__gc = nil , make sure executing only once*/ 84 | lua_getfield(L, -1, "__index"); 85 | lua_pushstring(L, "__gc"); 86 | lua_pushnil(L); 87 | lua_settable(L, -3); 88 | 89 | lua_getglobal(state, "__uloop_cb"); 90 | luaL_unref(state, -1, tout->r); 91 | 92 | return 1; 93 | } 94 | 95 | static const luaL_Reg timer_m[] = { 96 | { "set", ul_timer_set }, 97 | { "cancel", ul_timer_free }, 98 | { NULL, NULL } 99 | }; 100 | 101 | static int ul_timer(lua_State *L) 102 | { 103 | struct lua_uloop_timeout *tout; 104 | int set = 0; 105 | int ref; 106 | 107 | if (lua_isnumber(L, -1)) { 108 | set = lua_tointeger(L, -1); 109 | lua_pop(L, 1); 110 | } 111 | 112 | if (!lua_isfunction(L, -1)) { 113 | lua_pushstring(L, "invalid arg list"); 114 | lua_error(L); 115 | 116 | return 0; 117 | } 118 | 119 | lua_getglobal(L, "__uloop_cb"); 120 | lua_pushvalue(L, -2); 121 | ref = luaL_ref(L, -2); 122 | 123 | tout = lua_newuserdata(L, sizeof(*tout)); 124 | lua_createtable(L, 0, 2); 125 | lua_pushvalue(L, -1); 126 | lua_setfield(L, -2, "__index"); 127 | lua_pushcfunction(L, ul_timer_free); 128 | lua_setfield(L, -2, "__gc"); 129 | lua_pushvalue(L, -1); 130 | lua_setmetatable(L, -3); 131 | lua_pushvalue(L, -2); 132 | luaI_openlib(L, NULL, timer_m, 1); 133 | lua_pushvalue(L, -2); 134 | 135 | memset(tout, 0, sizeof(*tout)); 136 | 137 | tout->r = ref; 138 | tout->t.cb = ul_timer_cb; 139 | if (set) 140 | uloop_timeout_set(&tout->t, set); 141 | 142 | return 1; 143 | } 144 | 145 | static void ul_ufd_cb(struct uloop_fd *fd, unsigned int events) 146 | { 147 | struct lua_uloop_fd *ufd = container_of(fd, struct lua_uloop_fd, fd); 148 | 149 | lua_getglobal(state, "__uloop_cb"); 150 | lua_rawgeti(state, -1, ufd->r); 151 | lua_remove(state, -2); 152 | 153 | /* push fd object */ 154 | lua_getglobal(state, "__uloop_fds"); 155 | lua_rawgeti(state, -1, ufd->fd_r); 156 | lua_remove(state, -2); 157 | 158 | /* push events */ 159 | lua_pushinteger(state, events); 160 | lua_call(state, 2, 0); 161 | } 162 | 163 | 164 | static int get_sock_fd(lua_State* L, int idx) { 165 | int fd; 166 | if(lua_isnumber(L, idx)) { 167 | fd = lua_tonumber(L, idx); 168 | } else { 169 | luaL_checktype(L, idx, LUA_TUSERDATA); 170 | lua_getfield(L, idx, "getfd"); 171 | if(lua_isnil(L, -1)) 172 | return luaL_error(L, "socket type missing 'getfd' method"); 173 | lua_pushvalue(L, idx - 1); 174 | lua_call(L, 1, 1); 175 | fd = lua_tointeger(L, -1); 176 | lua_pop(L, 1); 177 | } 178 | return fd; 179 | } 180 | 181 | static int ul_ufd_delete(lua_State *L) 182 | { 183 | struct lua_uloop_fd *ufd = lua_touserdata(L, 1); 184 | 185 | uloop_fd_delete(&ufd->fd); 186 | 187 | /* obj.__index.__gc = nil , make sure executing only once*/ 188 | lua_getfield(L, -1, "__index"); 189 | lua_pushstring(L, "__gc"); 190 | lua_pushnil(L); 191 | lua_settable(L, -3); 192 | 193 | lua_getglobal(state, "__uloop_cb"); 194 | luaL_unref(state, -1, ufd->r); 195 | lua_remove(state, -1); 196 | 197 | lua_getglobal(state, "__uloop_fds"); 198 | luaL_unref(state, -1, ufd->fd_r); 199 | lua_remove(state, -1); 200 | 201 | return 1; 202 | } 203 | 204 | static const luaL_Reg ufd_m[] = { 205 | { "cancel", ul_ufd_delete }, 206 | { NULL, NULL } 207 | }; 208 | 209 | static int ul_ufd_add(lua_State *L) 210 | { 211 | struct lua_uloop_fd *ufd; 212 | int fd = 0; 213 | unsigned int flags = 0; 214 | int ref; 215 | int fd_ref; 216 | 217 | if (lua_isnumber(L, -1)) { 218 | flags = lua_tointeger(L, -1); 219 | lua_pop(L, 1); 220 | } 221 | 222 | if (!lua_isfunction(L, -1)) { 223 | lua_pushstring(L, "invalid arg list"); 224 | lua_error(L); 225 | 226 | return 0; 227 | } 228 | 229 | fd = get_sock_fd(L, -2); 230 | 231 | lua_getglobal(L, "__uloop_cb"); 232 | lua_pushvalue(L, -2); 233 | ref = luaL_ref(L, -2); 234 | lua_pop(L, 1); 235 | 236 | lua_getglobal(L, "__uloop_fds"); 237 | lua_pushvalue(L, -3); 238 | fd_ref = luaL_ref(L, -2); 239 | lua_pop(L, 1); 240 | 241 | ufd = lua_newuserdata(L, sizeof(*ufd)); 242 | 243 | lua_createtable(L, 0, 2); 244 | lua_pushvalue(L, -1); 245 | lua_setfield(L, -2, "__index"); 246 | lua_pushcfunction(L, ul_ufd_delete); 247 | lua_setfield(L, -2, "__gc"); 248 | lua_pushvalue(L, -1); 249 | lua_setmetatable(L, -3); 250 | lua_pushvalue(L, -2); 251 | luaI_openlib(L, NULL, ufd_m, 1); 252 | lua_pushvalue(L, -2); 253 | 254 | memset(ufd, 0, sizeof(*ufd)); 255 | 256 | ufd->r = ref; 257 | ufd->fd.fd = fd; 258 | ufd->fd_r = fd_ref; 259 | ufd->fd.cb = ul_ufd_cb; 260 | if (flags) 261 | uloop_fd_add(&ufd->fd, flags); 262 | 263 | return 1; 264 | } 265 | 266 | static void ul_process_cb(struct uloop_process *p, int ret) 267 | { 268 | struct lua_uloop_process *proc = container_of(p, struct lua_uloop_process, p); 269 | 270 | lua_getglobal(state, "__uloop_cb"); 271 | lua_rawgeti(state, -1, proc->r); 272 | 273 | luaL_unref(state, -2, proc->r); 274 | lua_remove(state, -2); 275 | lua_pushinteger(state, ret >> 8); 276 | lua_call(state, 1, 0); 277 | } 278 | 279 | static int ul_process(lua_State *L) 280 | { 281 | struct lua_uloop_process *proc; 282 | pid_t pid; 283 | int ref; 284 | 285 | if (!lua_isfunction(L, -1) || !lua_istable(L, -2) || 286 | !lua_istable(L, -3) || !lua_isstring(L, -4)) { 287 | lua_pushstring(L, "invalid arg list"); 288 | lua_error(L); 289 | 290 | return 0; 291 | } 292 | 293 | pid = fork(); 294 | 295 | if (pid == -1) { 296 | lua_pushstring(L, "failed to fork"); 297 | lua_error(L); 298 | 299 | return 0; 300 | } 301 | 302 | if (pid == 0) { 303 | /* child */ 304 | int argn = lua_objlen(L, -3); 305 | int envn = lua_objlen(L, -2); 306 | char** argp = malloc(sizeof(char*) * (argn + 2)); 307 | char** envp = malloc(sizeof(char*) * envn + 1); 308 | int i = 1; 309 | 310 | argp[0] = (char*) lua_tostring(L, -4); 311 | for (i = 1; i <= argn; i++) { 312 | lua_rawgeti(L, -3, i); 313 | argp[i] = (char*) lua_tostring(L, -1); 314 | lua_pop(L, 1); 315 | } 316 | argp[i] = NULL; 317 | 318 | for (i = 1; i <= envn; i++) { 319 | lua_rawgeti(L, -2, i); 320 | envp[i - 1] = (char*) lua_tostring(L, -1); 321 | lua_pop(L, 1); 322 | } 323 | envp[i - 1] = NULL; 324 | 325 | execve(*argp, argp, envp); 326 | exit(-1); 327 | } 328 | 329 | lua_getglobal(L, "__uloop_cb"); 330 | lua_pushvalue(L, -2); 331 | ref = luaL_ref(L, -2); 332 | 333 | proc = lua_newuserdata(L, sizeof(*proc)); 334 | memset(proc, 0, sizeof(*proc)); 335 | 336 | proc->r = ref; 337 | proc->p.pid = pid; 338 | proc->p.cb = ul_process_cb; 339 | uloop_process_add(&proc->p); 340 | 341 | return 1; 342 | } 343 | 344 | static int ul_init(lua_State *L) 345 | { 346 | uloop_init(); 347 | lua_pushboolean(L, 1); 348 | 349 | return 1; 350 | } 351 | 352 | static int ul_run(lua_State *L) 353 | { 354 | uloop_run(); 355 | lua_pushboolean(L, 1); 356 | 357 | return 1; 358 | } 359 | 360 | static int ul_cancel(lua_State *L) 361 | { 362 | uloop_end(); 363 | return 1; 364 | } 365 | 366 | static luaL_reg uloop_func[] = { 367 | {"init", ul_init}, 368 | {"run", ul_run}, 369 | {"timer", ul_timer}, 370 | {"process", ul_process}, 371 | {"fd_add", ul_ufd_add}, 372 | {"cancel", ul_cancel}, 373 | {NULL, NULL}, 374 | }; 375 | 376 | /* avoid warnings about missing declarations */ 377 | int luaopen_uloop(lua_State *L); 378 | int luaclose_uloop(lua_State *L); 379 | 380 | int luaopen_uloop(lua_State *L) 381 | { 382 | state = L; 383 | 384 | lua_createtable(L, 1, 0); 385 | lua_setglobal(L, "__uloop_cb"); 386 | 387 | lua_createtable(L, 1, 0); 388 | lua_setglobal(L, "__uloop_fds"); 389 | 390 | luaL_openlib(L, "uloop", uloop_func, 0); 391 | lua_pushstring(L, "_VERSION"); 392 | lua_pushstring(L, "1.0"); 393 | lua_rawset(L, -3); 394 | 395 | lua_pushstring(L, "ULOOP_READ"); 396 | lua_pushinteger(L, ULOOP_READ); 397 | lua_rawset(L, -3); 398 | 399 | lua_pushstring(L, "ULOOP_WRITE"); 400 | lua_pushinteger(L, ULOOP_WRITE); 401 | lua_rawset(L, -3); 402 | 403 | lua_pushstring(L, "ULOOP_EDGE_TRIGGER"); 404 | lua_pushinteger(L, ULOOP_EDGE_TRIGGER); 405 | lua_rawset(L, -3); 406 | 407 | lua_pushstring(L, "ULOOP_BLOCKING"); 408 | lua_pushinteger(L, ULOOP_BLOCKING); 409 | lua_rawset(L, -3); 410 | 411 | return 1; 412 | } 413 | 414 | int luaclose_uloop(lua_State *L) 415 | { 416 | lua_pushstring(L, "Called"); 417 | 418 | return 1; 419 | } 420 | -------------------------------------------------------------------------------- /md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * md5.c - Compute MD5 checksum of strings according to the 3 | * definition of MD5 in RFC 1321 from April 1992. 4 | * 5 | * Written by Ulrich Drepper , 1995. 6 | * 7 | * Copyright (C) 1995-1999 Free Software Foundation, Inc. 8 | * Copyright (C) 2001 Manuel Novoa III 9 | * Copyright (C) 2003 Glenn L. McGrath 10 | * Copyright (C) 2003 Erik Andersen 11 | * 12 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 13 | */ 14 | 15 | #include "blob.h" /* TODO: better include for bswap_32 compat */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "md5.h" 24 | 25 | #if __BYTE_ORDER == __LITTLE_ENDIAN 26 | #define SWAP_LE32(x) (x) 27 | #else 28 | #define SWAP_LE32(x) bswap_32(x) 29 | #endif 30 | 31 | /* Initialize structure containing state of computation. 32 | * (RFC 1321, 3.3: Step 3) 33 | */ 34 | void md5_begin(md5_ctx_t *ctx) 35 | { 36 | ctx->A = 0x67452301; 37 | ctx->B = 0xefcdab89; 38 | ctx->C = 0x98badcfe; 39 | ctx->D = 0x10325476; 40 | 41 | ctx->total = 0; 42 | ctx->buflen = 0; 43 | } 44 | 45 | /* These are the four functions used in the four steps of the MD5 algorithm 46 | * and defined in the RFC 1321. The first function is a little bit optimized 47 | * (as found in Colin Plumbs public domain implementation). 48 | * #define FF(b, c, d) ((b & c) | (~b & d)) 49 | */ 50 | # define FF(b, c, d) (d ^ (b & (c ^ d))) 51 | # define FG(b, c, d) FF (d, b, c) 52 | # define FH(b, c, d) (b ^ c ^ d) 53 | # define FI(b, c, d) (c ^ (b | ~d)) 54 | 55 | /* Hash a single block, 64 bytes long and 4-byte aligned. */ 56 | static void md5_hash_block(const void *buffer, md5_ctx_t *ctx) 57 | { 58 | uint32_t correct_words[16]; 59 | const uint32_t *words = buffer; 60 | 61 | static const uint32_t C_array[] = { 62 | /* round 1 */ 63 | 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 64 | 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 65 | 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 66 | 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 67 | /* round 2 */ 68 | 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 69 | 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, 70 | 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 71 | 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 72 | /* round 3 */ 73 | 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 74 | 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 75 | 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, 76 | 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 77 | /* round 4 */ 78 | 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 79 | 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 80 | 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 81 | 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 82 | }; 83 | 84 | static const char P_array[] = { 85 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ 86 | 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ 87 | 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ 88 | 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ 89 | }; 90 | 91 | static const char S_array[] = { 92 | 7, 12, 17, 22, 93 | 5, 9, 14, 20, 94 | 4, 11, 16, 23, 95 | 6, 10, 15, 21 96 | }; 97 | 98 | uint32_t A = ctx->A; 99 | uint32_t B = ctx->B; 100 | uint32_t C = ctx->C; 101 | uint32_t D = ctx->D; 102 | 103 | uint32_t *cwp = correct_words; 104 | 105 | # define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) 106 | 107 | const uint32_t *pc; 108 | const char *pp; 109 | const char *ps; 110 | int i; 111 | uint32_t temp; 112 | 113 | for (i = 0; i < 16; i++) { 114 | cwp[i] = SWAP_LE32(words[i]); 115 | } 116 | words += 16; 117 | 118 | pc = C_array; 119 | pp = P_array; 120 | ps = S_array; 121 | 122 | for (i = 0; i < 16; i++) { 123 | temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++; 124 | CYCLIC(temp, ps[i & 3]); 125 | temp += B; 126 | A = D; 127 | D = C; 128 | C = B; 129 | B = temp; 130 | } 131 | 132 | ps += 4; 133 | for (i = 0; i < 16; i++) { 134 | temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++; 135 | CYCLIC(temp, ps[i & 3]); 136 | temp += B; 137 | A = D; 138 | D = C; 139 | C = B; 140 | B = temp; 141 | } 142 | ps += 4; 143 | for (i = 0; i < 16; i++) { 144 | temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++; 145 | CYCLIC(temp, ps[i & 3]); 146 | temp += B; 147 | A = D; 148 | D = C; 149 | C = B; 150 | B = temp; 151 | } 152 | ps += 4; 153 | for (i = 0; i < 16; i++) { 154 | temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++; 155 | CYCLIC(temp, ps[i & 3]); 156 | temp += B; 157 | A = D; 158 | D = C; 159 | C = B; 160 | B = temp; 161 | } 162 | 163 | 164 | ctx->A += A; 165 | ctx->B += B; 166 | ctx->C += C; 167 | ctx->D += D; 168 | } 169 | 170 | /* Feed data through a temporary buffer to call md5_hash_aligned_block() 171 | * with chunks of data that are 4-byte aligned and a multiple of 64 bytes. 172 | * This function's internal buffer remembers previous data until it has 64 173 | * bytes worth to pass on. Call md5_end() to flush this buffer. */ 174 | 175 | void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx) 176 | { 177 | char *buf = (char *)buffer; 178 | 179 | /* RFC 1321 specifies the possible length of the file up to 2^64 bits, 180 | * Here we only track the number of bytes. */ 181 | 182 | ctx->total += len; 183 | 184 | // Process all input. 185 | 186 | while (len) { 187 | unsigned i = 64 - ctx->buflen; 188 | 189 | // Copy data into aligned buffer. 190 | 191 | if (i > len) 192 | i = len; 193 | memcpy(ctx->buffer + ctx->buflen, buf, i); 194 | len -= i; 195 | ctx->buflen += i; 196 | buf += i; 197 | 198 | // When buffer fills up, process it. 199 | 200 | if (ctx->buflen == 64) { 201 | md5_hash_block(ctx->buffer, ctx); 202 | ctx->buflen = 0; 203 | } 204 | } 205 | } 206 | 207 | /* Process the remaining bytes in the buffer and put result from CTX 208 | * in first 16 bytes following RESBUF. The result is always in little 209 | * endian byte order, so that a byte-wise output yields to the wanted 210 | * ASCII representation of the message digest. 211 | * 212 | * IMPORTANT: On some systems it is required that RESBUF is correctly 213 | * aligned for a 32 bits value. 214 | */ 215 | void md5_end(void *resbuf, md5_ctx_t *ctx) 216 | { 217 | char *buf = ctx->buffer; 218 | int i; 219 | 220 | /* Pad data to block size. */ 221 | 222 | buf[ctx->buflen++] = 0x80; 223 | memset(buf + ctx->buflen, 0, 128 - ctx->buflen); 224 | 225 | /* Put the 64-bit file length in *bits* at the end of the buffer. */ 226 | ctx->total <<= 3; 227 | if (ctx->buflen > 56) 228 | buf += 64; 229 | 230 | for (i = 0; i < 8; i++) 231 | buf[56 + i] = ctx->total >> (i*8); 232 | 233 | /* Process last bytes. */ 234 | if (buf != ctx->buffer) 235 | md5_hash_block(ctx->buffer, ctx); 236 | md5_hash_block(buf, ctx); 237 | 238 | /* Put result from CTX in first 16 bytes following RESBUF. The result is 239 | * always in little endian byte order, so that a byte-wise output yields 240 | * to the wanted ASCII representation of the message digest. 241 | * 242 | * IMPORTANT: On some systems it is required that RESBUF is correctly 243 | * aligned for a 32 bits value. 244 | */ 245 | ((uint32_t *) resbuf)[0] = SWAP_LE32(ctx->A); 246 | ((uint32_t *) resbuf)[1] = SWAP_LE32(ctx->B); 247 | ((uint32_t *) resbuf)[2] = SWAP_LE32(ctx->C); 248 | ((uint32_t *) resbuf)[3] = SWAP_LE32(ctx->D); 249 | } 250 | 251 | int md5sum(char *file, uint32_t *md5) 252 | { 253 | char buf[256]; 254 | md5_ctx_t ctx; 255 | int len, fd; 256 | int ret = 0; 257 | 258 | memset(md5, 0, sizeof(*md5) * 4); 259 | 260 | fd = open(file, O_RDONLY); 261 | if (fd < 0) 262 | return -1; 263 | 264 | md5_begin(&ctx); 265 | do { 266 | len = read(fd, buf, sizeof(buf)); 267 | if (len < 0) { 268 | if (errno == EINTR) 269 | continue; 270 | 271 | ret = -1; 272 | goto out; 273 | } 274 | if (!len) 275 | break; 276 | 277 | md5_hash(buf, len, &ctx); 278 | } while(1); 279 | 280 | md5_end(md5, &ctx); 281 | out: 282 | close(fd); 283 | 284 | return ret; 285 | } 286 | -------------------------------------------------------------------------------- /md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * Copyright (C) 2013 John Crispin 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 | #ifndef __PROCD_MD5_H 16 | #define __PROCD_MD5_H 17 | 18 | #include 19 | #include 20 | 21 | typedef struct md5_ctx { 22 | uint32_t A; 23 | uint32_t B; 24 | uint32_t C; 25 | uint32_t D; 26 | uint64_t total; 27 | uint32_t buflen; 28 | char buffer[128]; 29 | } md5_ctx_t; 30 | 31 | void md5_begin(md5_ctx_t *ctx); 32 | void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); 33 | void md5_end(void *resbuf, md5_ctx_t *ctx); 34 | int md5sum(char *file, uint32_t *md5); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /runqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * runqueue.c - a simple task queueing/completion tracking helper 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include "runqueue.h" 22 | 23 | static void 24 | __runqueue_empty_cb(struct uloop_timeout *timeout) 25 | { 26 | struct runqueue *q = container_of(timeout, struct runqueue, timeout); 27 | 28 | q->empty_cb(q); 29 | } 30 | 31 | void runqueue_init(struct runqueue *q) 32 | { 33 | INIT_SAFE_LIST(&q->tasks_active); 34 | INIT_SAFE_LIST(&q->tasks_inactive); 35 | } 36 | 37 | static void __runqueue_start_next(struct uloop_timeout *timeout) 38 | { 39 | struct runqueue *q = container_of(timeout, struct runqueue, timeout); 40 | struct runqueue_task *t; 41 | 42 | do { 43 | if (q->stopped) 44 | break; 45 | 46 | if (list_empty(&q->tasks_inactive.list)) 47 | break; 48 | 49 | if (q->max_running_tasks && q->running_tasks >= q->max_running_tasks) 50 | break; 51 | 52 | t = list_first_entry(&q->tasks_inactive.list, struct runqueue_task, list.list); 53 | safe_list_del(&t->list); 54 | safe_list_add(&t->list, &q->tasks_active); 55 | t->running = true; 56 | q->running_tasks++; 57 | if (t->run_timeout) 58 | uloop_timeout_set(&t->timeout, t->run_timeout); 59 | t->type->run(q, t); 60 | } while (1); 61 | 62 | if (!q->empty && 63 | list_empty(&q->tasks_active.list) && 64 | list_empty(&q->tasks_inactive.list)) { 65 | q->empty = true; 66 | if (q->empty_cb) { 67 | q->timeout.cb = __runqueue_empty_cb; 68 | uloop_timeout_set(&q->timeout, 1); 69 | } 70 | } 71 | } 72 | 73 | static void runqueue_start_next(struct runqueue *q) 74 | { 75 | if (q->empty) 76 | return; 77 | 78 | q->timeout.cb = __runqueue_start_next; 79 | uloop_timeout_set(&q->timeout, 1); 80 | } 81 | 82 | static int __runqueue_cancel(void *ctx, struct safe_list *list) 83 | { 84 | struct runqueue_task *t; 85 | 86 | t = container_of(list, struct runqueue_task, list); 87 | runqueue_task_cancel(t, 0); 88 | 89 | return 0; 90 | } 91 | 92 | void runqueue_cancel_active(struct runqueue *q) 93 | { 94 | safe_list_for_each(&q->tasks_active, __runqueue_cancel, NULL); 95 | } 96 | 97 | void runqueue_cancel_pending(struct runqueue *q) 98 | { 99 | safe_list_for_each(&q->tasks_inactive, __runqueue_cancel, NULL); 100 | } 101 | 102 | void runqueue_cancel(struct runqueue *q) 103 | { 104 | runqueue_cancel_pending(q); 105 | runqueue_cancel_active(q); 106 | } 107 | 108 | void runqueue_kill(struct runqueue *q) 109 | { 110 | struct runqueue_task *t; 111 | 112 | while (!list_empty(&q->tasks_active.list)) { 113 | t = list_first_entry(&q->tasks_active.list, struct runqueue_task, list.list); 114 | runqueue_task_kill(t); 115 | } 116 | runqueue_cancel_pending(q); 117 | uloop_timeout_cancel(&q->timeout); 118 | } 119 | 120 | void runqueue_task_cancel(struct runqueue_task *t, int type) 121 | { 122 | if (!t->queued) 123 | return; 124 | 125 | if (!t->running) { 126 | runqueue_task_complete(t); 127 | return; 128 | } 129 | 130 | t->cancelled = true; 131 | if (t->cancel_timeout) 132 | uloop_timeout_set(&t->timeout, t->cancel_timeout); 133 | if (t->type->cancel) 134 | t->type->cancel(t->q, t, type); 135 | } 136 | 137 | static void 138 | __runqueue_task_timeout(struct uloop_timeout *timeout) 139 | { 140 | struct runqueue_task *t = container_of(timeout, struct runqueue_task, timeout); 141 | 142 | if (t->cancelled) 143 | runqueue_task_kill(t); 144 | else 145 | runqueue_task_cancel(t, t->cancel_type); 146 | } 147 | 148 | static void _runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running, bool first) 149 | { 150 | struct safe_list *head; 151 | 152 | if (t->queued) 153 | return; 154 | 155 | if (!t->type->run && !running) { 156 | fprintf(stderr, "BUG: inactive task added without run() callback\n"); 157 | return; 158 | } 159 | 160 | if (running) { 161 | q->running_tasks++; 162 | head = &q->tasks_active; 163 | } else { 164 | head = &q->tasks_inactive; 165 | } 166 | 167 | t->timeout.cb = __runqueue_task_timeout; 168 | t->q = q; 169 | if (first) 170 | safe_list_add_first(&t->list, head); 171 | else 172 | safe_list_add(&t->list, head); 173 | t->cancelled = false; 174 | t->queued = true; 175 | t->running = running; 176 | q->empty = false; 177 | 178 | runqueue_start_next(q); 179 | } 180 | 181 | void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running) 182 | { 183 | _runqueue_task_add(q, t, running, 0); 184 | } 185 | 186 | void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running) 187 | { 188 | _runqueue_task_add(q, t, running, 1); 189 | } 190 | 191 | void runqueue_task_kill(struct runqueue_task *t) 192 | { 193 | struct runqueue *q = t->q; 194 | bool running = t->running; 195 | 196 | if (!t->queued) 197 | return; 198 | 199 | runqueue_task_complete(t); 200 | if (running && t->type->kill) 201 | t->type->kill(q, t); 202 | 203 | runqueue_start_next(q); 204 | } 205 | 206 | void runqueue_stop(struct runqueue *q) 207 | { 208 | q->stopped = true; 209 | } 210 | 211 | void runqueue_resume(struct runqueue *q) 212 | { 213 | q->stopped = false; 214 | runqueue_start_next(q); 215 | } 216 | 217 | void runqueue_task_complete(struct runqueue_task *t) 218 | { 219 | struct runqueue *q = t->q; 220 | 221 | if (!t->queued) 222 | return; 223 | 224 | if (t->running) 225 | t->q->running_tasks--; 226 | 227 | uloop_timeout_cancel(&t->timeout); 228 | 229 | safe_list_del(&t->list); 230 | t->queued = false; 231 | t->running = false; 232 | t->cancelled = false; 233 | if (t->complete) 234 | t->complete(q, t); 235 | runqueue_start_next(t->q); 236 | } 237 | 238 | static void 239 | __runqueue_proc_cb(struct uloop_process *p, int ret) 240 | { 241 | struct runqueue_process *t = container_of(p, struct runqueue_process, proc); 242 | 243 | runqueue_task_complete(&t->task); 244 | } 245 | 246 | void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type) 247 | { 248 | struct runqueue_process *p = container_of(t, struct runqueue_process, task); 249 | 250 | if (!type) 251 | type = SIGTERM; 252 | 253 | kill(p->proc.pid, type); 254 | } 255 | 256 | void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t) 257 | { 258 | struct runqueue_process *p = container_of(t, struct runqueue_process, task); 259 | 260 | uloop_process_delete(&p->proc); 261 | kill(p->proc.pid, SIGKILL); 262 | } 263 | 264 | static const struct runqueue_task_type runqueue_proc_type = { 265 | .name = "process", 266 | .cancel = runqueue_process_cancel_cb, 267 | .kill = runqueue_process_kill_cb, 268 | }; 269 | 270 | void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid) 271 | { 272 | if (p->proc.pending) 273 | return; 274 | 275 | p->proc.pid = pid; 276 | p->proc.cb = __runqueue_proc_cb; 277 | if (!p->task.type) 278 | p->task.type = &runqueue_proc_type; 279 | uloop_process_add(&p->proc); 280 | if (!p->task.running) 281 | runqueue_task_add(q, &p->task, true); 282 | } 283 | -------------------------------------------------------------------------------- /runqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * runqueue.c - a simple task queueing/completion tracking helper 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __LIBUBOX_RUNQUEUE_H 20 | #define __LIBUBOX_RUNQUEUE_H 21 | 22 | #include "list.h" 23 | #include "safe_list.h" 24 | #include "uloop.h" 25 | 26 | struct runqueue; 27 | struct runqueue_task; 28 | struct runqueue_task_type; 29 | 30 | struct runqueue { 31 | struct safe_list tasks_active; 32 | struct safe_list tasks_inactive; 33 | struct uloop_timeout timeout; 34 | 35 | int running_tasks; 36 | int max_running_tasks; 37 | bool stopped; 38 | bool empty; 39 | 40 | /* called when the runqueue is emptied */ 41 | void (*empty_cb)(struct runqueue *q); 42 | }; 43 | 44 | struct runqueue_task_type { 45 | const char *name; 46 | 47 | /* 48 | * called when a task is requested to run 49 | * 50 | * The task is removed from the list before this callback is run. It 51 | * can re-arm itself using runqueue_task_add. 52 | */ 53 | void (*run)(struct runqueue *q, struct runqueue_task *t); 54 | 55 | /* 56 | * called to request cancelling a task 57 | * 58 | * int type is used as an optional hint for the method to be used when 59 | * cancelling the task, e.g. a signal number for processes. Calls 60 | * runqueue_task_complete when done. 61 | */ 62 | void (*cancel)(struct runqueue *q, struct runqueue_task *t, int type); 63 | 64 | /* 65 | * called to kill a task. must not make any calls to runqueue_task_complete, 66 | * it has already been removed from the list. 67 | */ 68 | void (*kill)(struct runqueue *q, struct runqueue_task *t); 69 | }; 70 | 71 | struct runqueue_task { 72 | struct safe_list list; 73 | const struct runqueue_task_type *type; 74 | struct runqueue *q; 75 | 76 | void (*complete)(struct runqueue *q, struct runqueue_task *t); 77 | 78 | struct uloop_timeout timeout; 79 | int run_timeout; 80 | int cancel_timeout; 81 | int cancel_type; 82 | 83 | bool queued; 84 | bool running; 85 | bool cancelled; 86 | }; 87 | 88 | struct runqueue_process { 89 | struct runqueue_task task; 90 | struct uloop_process proc; 91 | }; 92 | 93 | void runqueue_init(struct runqueue *q); 94 | void runqueue_cancel(struct runqueue *q); 95 | void runqueue_cancel_active(struct runqueue *q); 96 | void runqueue_cancel_pending(struct runqueue *q); 97 | void runqueue_kill(struct runqueue *q); 98 | 99 | void runqueue_stop(struct runqueue *q); 100 | void runqueue_resume(struct runqueue *q); 101 | 102 | void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running); 103 | void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running); 104 | void runqueue_task_complete(struct runqueue_task *t); 105 | 106 | void runqueue_task_cancel(struct runqueue_task *t, int type); 107 | void runqueue_task_kill(struct runqueue_task *t); 108 | 109 | void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid); 110 | 111 | /* to be used only from runqueue_process callbacks */ 112 | void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type); 113 | void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t); 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /safe_list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * safe_list - linked list protected against recursive iteration with deletes 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "safe_list.h" 20 | 21 | struct safe_list_iterator { 22 | struct safe_list_iterator **head; 23 | struct safe_list_iterator *next_i; 24 | struct safe_list *next; 25 | }; 26 | 27 | static void 28 | __safe_list_set_iterator(struct safe_list *list, 29 | struct safe_list_iterator *i) 30 | { 31 | struct safe_list_iterator *next_i; 32 | struct safe_list *next; 33 | 34 | next = list_entry(list->list.next, struct safe_list, list); 35 | next_i = next->i; 36 | 37 | next->i = i; 38 | i->next = next; 39 | i->head = &next->i; 40 | 41 | i->next_i = next_i; 42 | if (next_i) 43 | next_i->head = &i->next_i; 44 | } 45 | 46 | static void 47 | __safe_list_del_iterator(struct safe_list_iterator *i) 48 | { 49 | *i->head = i->next_i; 50 | if (i->next_i) 51 | i->next_i->head = i->head; 52 | } 53 | 54 | static void 55 | __safe_list_move_iterator(struct safe_list *list, 56 | struct safe_list_iterator *i) 57 | { 58 | __safe_list_del_iterator(i); 59 | __safe_list_set_iterator(list, i); 60 | } 61 | 62 | int safe_list_for_each(struct safe_list *head, 63 | int (*cb)(void *ctx, struct safe_list *list), 64 | void *ctx) 65 | { 66 | struct safe_list_iterator i; 67 | struct safe_list *cur; 68 | int ret = 0; 69 | 70 | for (cur = list_entry(head->list.next, struct safe_list, list), 71 | __safe_list_set_iterator(cur, &i); 72 | cur != head; 73 | cur = i.next, __safe_list_move_iterator(cur, &i)) { 74 | ret = cb(ctx, cur); 75 | if (ret) 76 | break; 77 | } 78 | 79 | __safe_list_del_iterator(&i); 80 | return ret; 81 | } 82 | 83 | void safe_list_add(struct safe_list *list, struct safe_list *head) 84 | { 85 | list->i = NULL; 86 | list_add_tail(&list->list, &head->list); 87 | } 88 | 89 | void safe_list_add_first(struct safe_list *list, struct safe_list *head) 90 | { 91 | list->i = NULL; 92 | list_add(&list->list, &head->list); 93 | } 94 | 95 | void safe_list_del(struct safe_list *list) 96 | { 97 | struct safe_list_iterator *i, *next_i, **tail; 98 | struct safe_list *next; 99 | 100 | next = list_entry(list->list.next, struct safe_list, list); 101 | list_del(&list->list); 102 | 103 | if (!list->i) 104 | return; 105 | 106 | next_i = next->i; 107 | tail = &next->i; 108 | 109 | for (i = list->i; i; i = i->next_i) { 110 | tail = &i->next_i; 111 | i->next = next; 112 | } 113 | 114 | next->i = list->i; 115 | list->i->head = &next->i; 116 | *tail = next_i; 117 | if (next_i) 118 | next_i->head = tail; 119 | 120 | list->i = NULL; 121 | } 122 | -------------------------------------------------------------------------------- /safe_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * safe_list - linked list protected against recursive iteration with deletes 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Use this linked list implementation as a replacement for list.h if you 21 | * want to allow deleting arbitrary list entries from within one or more 22 | * recursive iterator calling context 23 | */ 24 | 25 | #ifndef __LIBUBOX_SAFE_LIST_H 26 | #define __LIBUBOX_SAFE_LIST_H 27 | 28 | #include 29 | #include "list.h" 30 | #include "utils.h" 31 | 32 | struct safe_list; 33 | struct safe_list_iterator; 34 | 35 | struct safe_list { 36 | struct list_head list; 37 | struct safe_list_iterator *i; 38 | }; 39 | 40 | int safe_list_for_each(struct safe_list *list, 41 | int (*cb)(void *ctx, struct safe_list *list), 42 | void *ctx); 43 | 44 | void safe_list_add(struct safe_list *list, struct safe_list *head); 45 | void safe_list_add_first(struct safe_list *list, struct safe_list *head); 46 | void safe_list_del(struct safe_list *list); 47 | 48 | #define INIT_SAFE_LIST(_head) \ 49 | do { \ 50 | INIT_LIST_HEAD(_head.list); \ 51 | (_head)->i = NULL; \ 52 | } while (0) 53 | 54 | #define SAFE_LIST_INIT(_name) { LIST_HEAD_INIT(_name.list), NULL } 55 | #define SAFE_LIST(_name) struct safe_list _name = SAFE_LIST_INIT(_name) 56 | 57 | static inline bool safe_list_empty(struct safe_list *head) 58 | { 59 | return list_empty(&head->list); 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /sh/jshn.sh: -------------------------------------------------------------------------------- 1 | # functions for parsing and generating json 2 | 3 | _json_get_var() { 4 | # dest=$1 5 | # var=$2 6 | eval "$1=\"\$${JSON_PREFIX}$2\"" 7 | } 8 | 9 | _json_set_var() { 10 | # var=$1 11 | local ___val="$2" 12 | eval "${JSON_PREFIX}$1=\"\$___val\"" 13 | } 14 | 15 | __jshn_raw_append() { 16 | # var=$1 17 | local value="$2" 18 | local sep="${3:- }" 19 | 20 | eval "export -- \"$1=\${$1:+\${$1}\${value:+\$sep}}\$value\"" 21 | } 22 | 23 | _jshn_append() { 24 | # var=$1 25 | local _a_value="$2" 26 | eval "${JSON_PREFIX}$1=\"\${${JSON_PREFIX}$1} \$_a_value\"" 27 | } 28 | 29 | _get_var() { 30 | # var=$1 31 | # value=$2 32 | eval "$1=\"\$$2\"" 33 | } 34 | 35 | _set_var() { 36 | # var=$1 37 | local __val="$2" 38 | eval "$1=\"\$__val\"" 39 | } 40 | 41 | _json_inc() { 42 | # var=$1 43 | # dest=$2 44 | 45 | eval "${JSON_PREFIX}$1=\$(( \${${JSON_PREFIX}$1:-0} + 1))${2:+; $2=\"\$${JSON_PREFIX}$1\"}" 46 | } 47 | 48 | _json_add_generic() { 49 | # type=$1 50 | # name=$2 51 | # value=$3 52 | # cur=$4 53 | 54 | local var 55 | if [ "${4%%[0-9]*}" = "JSON_ARRAY" ]; then 56 | _json_inc "SEQ_$4" var 57 | else 58 | local name="${2//[^a-zA-Z0-9_]/_}" 59 | [[ "$name" == "$2" ]] || export -- "${JSON_PREFIX}NAME_${4}_${name}=$2" 60 | var="$name" 61 | fi 62 | 63 | local cur_var= 64 | export -- \ 65 | "${JSON_PREFIX}${4}_$var=$3" \ 66 | "${JSON_PREFIX}TYPE_${4}_$var=$1" 67 | _jshn_append "JSON_UNSET" "${4}_$var" 68 | _jshn_append "KEYS_$4" "$var" 69 | } 70 | 71 | _json_add_table() { 72 | # name=$1 73 | # type=$2 74 | # itype=$3 75 | local cur seq 76 | 77 | _json_get_var cur JSON_CUR 78 | _json_inc JSON_SEQ seq 79 | 80 | local table="JSON_$3$seq" 81 | _json_set_var "UP_$table" "$cur" 82 | export -- "${JSON_PREFIX}KEYS_$table=" 83 | unset "${JSON_PREFIX}SEQ_$table" 84 | _json_set_var JSON_CUR "$table" 85 | _jshn_append "JSON_UNSET" "$table" 86 | 87 | _json_add_generic "$2" "$1" "$table" "$cur" 88 | } 89 | 90 | _json_close_table() { 91 | local _s_cur 92 | 93 | _json_get_var _s_cur JSON_CUR 94 | _json_get_var "${JSON_PREFIX}JSON_CUR" "UP_$_s_cur" 95 | } 96 | 97 | json_set_namespace() { 98 | local _new="$1" 99 | local _old="$2" 100 | 101 | [ -n "$_old" ] && _set_var "$_old" "$JSON_PREFIX" 102 | JSON_PREFIX="$_new" 103 | } 104 | 105 | json_cleanup() { 106 | local unset 107 | 108 | _json_get_var unset JSON_UNSET 109 | for tmp in $unset JSON_VAR; do 110 | unset \ 111 | ${JSON_PREFIX}UP_$tmp \ 112 | ${JSON_PREFIX}KEYS_$tmp \ 113 | ${JSON_PREFIX}SEQ_$tmp \ 114 | ${JSON_PREFIX}TYPE_$tmp \ 115 | ${JSON_PREFIX}NAME_$tmp \ 116 | ${JSON_PREFIX}$tmp 117 | done 118 | 119 | unset \ 120 | ${JSON_PREFIX}JSON_SEQ \ 121 | ${JSON_PREFIX}JSON_CUR \ 122 | ${JSON_PREFIX}JSON_UNSET 123 | } 124 | 125 | json_init() { 126 | json_cleanup 127 | export -- \ 128 | ${JSON_PREFIX}JSON_SEQ=0 \ 129 | ${JSON_PREFIX}JSON_CUR="JSON_VAR" \ 130 | ${JSON_PREFIX}KEYS_JSON_VAR= \ 131 | ${JSON_PREFIX}TYPE_JSON_VAR= 132 | } 133 | 134 | json_add_object() { 135 | _json_add_table "$1" object TABLE 136 | } 137 | 138 | json_close_object() { 139 | _json_close_table 140 | } 141 | 142 | json_add_array() { 143 | _json_add_table "$1" array ARRAY 144 | } 145 | 146 | json_close_array() { 147 | _json_close_table 148 | } 149 | 150 | json_add_string() { 151 | local cur 152 | _json_get_var cur JSON_CUR 153 | _json_add_generic string "$1" "$2" "$cur" 154 | } 155 | 156 | json_add_int() { 157 | local cur 158 | _json_get_var cur JSON_CUR 159 | _json_add_generic int "$1" "$2" "$cur" 160 | } 161 | 162 | json_add_boolean() { 163 | local cur 164 | _json_get_var cur JSON_CUR 165 | _json_add_generic boolean "$1" "$2" "$cur" 166 | } 167 | 168 | json_add_double() { 169 | local cur 170 | _json_get_var cur JSON_CUR 171 | _json_add_generic double "$1" "$2" "$cur" 172 | } 173 | 174 | # functions read access to json variables 175 | 176 | json_load() { 177 | eval `jshn -r "$1"` 178 | } 179 | 180 | json_dump() { 181 | jshn "$@" ${JSON_PREFIX:+-p "$JSON_PREFIX"} -w 182 | } 183 | 184 | json_get_type() { 185 | local __dest="$1" 186 | local __cur 187 | 188 | _json_get_var __cur JSON_CUR 189 | local __var="${JSON_PREFIX}TYPE_${__cur}_${2//[^a-zA-Z0-9_]/_}" 190 | eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" 191 | } 192 | 193 | json_get_keys() { 194 | local __dest="$1" 195 | local _tbl_cur 196 | 197 | if [ -n "$2" ]; then 198 | json_get_var _tbl_cur "$2" 199 | else 200 | _json_get_var _tbl_cur JSON_CUR 201 | fi 202 | local __var="${JSON_PREFIX}KEYS_${_tbl_cur}" 203 | eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" 204 | } 205 | 206 | json_get_values() { 207 | local _v_dest="$1" 208 | local _v_keys _v_val _select= 209 | local _json_no_warning=1 210 | 211 | unset "$_v_dest" 212 | [ -n "$2" ] && { 213 | json_select "$2" || return 1 214 | _select=1 215 | } 216 | 217 | json_get_keys _v_keys 218 | set -- $_v_keys 219 | while [ "$#" -gt 0 ]; do 220 | json_get_var _v_val "$1" 221 | __jshn_raw_append "$_v_dest" "$_v_val" 222 | shift 223 | done 224 | [ -n "$_select" ] && json_select .. 225 | 226 | return 0 227 | } 228 | 229 | json_get_var() { 230 | local __dest="$1" 231 | local __cur 232 | 233 | _json_get_var __cur JSON_CUR 234 | local __var="${JSON_PREFIX}${__cur}_${2//[^a-zA-Z0-9_]/_}" 235 | eval "export -- \"$__dest=\${$__var:-$3}\"; [ -n \"\${$__var+x}\${3+x}\" ]" 236 | } 237 | 238 | json_get_vars() { 239 | while [ "$#" -gt 0 ]; do 240 | local _var="$1"; shift 241 | if [ "$_var" != "${_var#*:}" ]; then 242 | json_get_var "${_var%%:*}" "${_var%%:*}" "${_var#*:}" 243 | else 244 | json_get_var "$_var" "$_var" 245 | fi 246 | done 247 | } 248 | 249 | json_select() { 250 | local target="$1" 251 | local type 252 | local cur 253 | 254 | [ -z "$1" ] && { 255 | _json_set_var JSON_CUR "JSON_VAR" 256 | return 0 257 | } 258 | [[ "$1" == ".." ]] && { 259 | _json_get_var cur JSON_CUR 260 | _json_get_var cur "UP_$cur" 261 | _json_set_var JSON_CUR "$cur" 262 | return 0 263 | } 264 | json_get_type type "$target" 265 | case "$type" in 266 | object|array) 267 | json_get_var cur "$target" 268 | _json_set_var JSON_CUR "$cur" 269 | ;; 270 | *) 271 | [ -n "$_json_no_warning" ] || \ 272 | echo "WARNING: Variable '$target' does not exist or is not an array/object" 273 | return 1 274 | ;; 275 | esac 276 | } 277 | 278 | json_is_a() { 279 | local type 280 | 281 | json_get_type type "$1" 282 | [ "$type" = "$2" ] 283 | } 284 | -------------------------------------------------------------------------------- /uloop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uloop - event loop implementation 3 | * 4 | * Copyright (C) 2010-2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "uloop.h" 31 | #include "utils.h" 32 | 33 | #ifdef USE_KQUEUE 34 | #include 35 | #endif 36 | #ifdef USE_EPOLL 37 | #include 38 | #endif 39 | #include 40 | 41 | struct uloop_fd_event { 42 | struct uloop_fd *fd; 43 | unsigned int events; 44 | }; 45 | 46 | struct uloop_fd_stack { 47 | struct uloop_fd_stack *next; 48 | struct uloop_fd *fd; 49 | unsigned int events; 50 | }; 51 | 52 | static struct uloop_fd_stack *fd_stack = NULL; 53 | 54 | #define ULOOP_MAX_EVENTS 10 55 | 56 | static struct list_head timeouts = LIST_HEAD_INIT(timeouts); 57 | static struct list_head processes = LIST_HEAD_INIT(processes); 58 | 59 | static int poll_fd = -1; 60 | bool uloop_cancelled = false; 61 | bool uloop_handle_sigchld = true; 62 | static bool do_sigchld = false; 63 | 64 | static struct uloop_fd_event cur_fds[ULOOP_MAX_EVENTS]; 65 | static int cur_fd, cur_nfds; 66 | 67 | #ifdef USE_KQUEUE 68 | 69 | int uloop_init(void) 70 | { 71 | struct timespec timeout = { 0, 0 }; 72 | struct kevent ev = {}; 73 | 74 | if (poll_fd >= 0) 75 | return 0; 76 | 77 | poll_fd = kqueue(); 78 | if (poll_fd < 0) 79 | return -1; 80 | 81 | EV_SET(&ev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); 82 | kevent(poll_fd, &ev, 1, NULL, 0, &timeout); 83 | 84 | return 0; 85 | } 86 | 87 | 88 | static uint16_t get_flags(unsigned int flags, unsigned int mask) 89 | { 90 | uint16_t kflags = 0; 91 | 92 | if (!(flags & mask)) 93 | return EV_DELETE; 94 | 95 | kflags = EV_ADD; 96 | if (flags & ULOOP_EDGE_TRIGGER) 97 | kflags |= EV_CLEAR; 98 | 99 | return kflags; 100 | } 101 | 102 | static struct kevent events[ULOOP_MAX_EVENTS]; 103 | 104 | static int register_kevent(struct uloop_fd *fd, unsigned int flags) 105 | { 106 | struct timespec timeout = { 0, 0 }; 107 | struct kevent ev[2]; 108 | int nev = 0; 109 | unsigned int fl = 0; 110 | unsigned int changed; 111 | uint16_t kflags; 112 | 113 | if (flags & ULOOP_EDGE_DEFER) 114 | flags &= ~ULOOP_EDGE_TRIGGER; 115 | 116 | changed = flags ^ fd->flags; 117 | if (changed & ULOOP_EDGE_TRIGGER) 118 | changed |= flags; 119 | 120 | if (changed & ULOOP_READ) { 121 | kflags = get_flags(flags, ULOOP_READ); 122 | EV_SET(&ev[nev++], fd->fd, EVFILT_READ, kflags, 0, 0, fd); 123 | } 124 | 125 | if (changed & ULOOP_WRITE) { 126 | kflags = get_flags(flags, ULOOP_WRITE); 127 | EV_SET(&ev[nev++], fd->fd, EVFILT_WRITE, kflags, 0, 0, fd); 128 | } 129 | 130 | if (!flags) 131 | fl |= EV_DELETE; 132 | 133 | fd->flags = flags; 134 | if (kevent(poll_fd, ev, nev, NULL, fl, &timeout) == -1) 135 | return -1; 136 | 137 | return 0; 138 | } 139 | 140 | static int register_poll(struct uloop_fd *fd, unsigned int flags) 141 | { 142 | if (flags & ULOOP_EDGE_TRIGGER) 143 | flags |= ULOOP_EDGE_DEFER; 144 | else 145 | flags &= ~ULOOP_EDGE_DEFER; 146 | 147 | return register_kevent(fd, flags); 148 | } 149 | 150 | static int __uloop_fd_delete(struct uloop_fd *fd) 151 | { 152 | return register_poll(fd, 0); 153 | } 154 | 155 | static int uloop_fetch_events(int timeout) 156 | { 157 | struct timespec ts; 158 | int nfds, n; 159 | 160 | if (timeout >= 0) { 161 | ts.tv_sec = timeout / 1000; 162 | ts.tv_nsec = (timeout % 1000) * 1000000; 163 | } 164 | 165 | nfds = kevent(poll_fd, NULL, 0, events, ARRAY_SIZE(events), timeout >= 0 ? &ts : NULL); 166 | for (n = 0; n < nfds; n++) { 167 | struct uloop_fd_event *cur = &cur_fds[n]; 168 | struct uloop_fd *u = events[n].udata; 169 | unsigned int ev = 0; 170 | 171 | cur->fd = u; 172 | if (!u) 173 | continue; 174 | 175 | if (events[n].flags & EV_ERROR) { 176 | u->error = true; 177 | if (!(u->flags & ULOOP_ERROR_CB)) 178 | uloop_fd_delete(u); 179 | } 180 | 181 | if(events[n].filter == EVFILT_READ) 182 | ev |= ULOOP_READ; 183 | else if (events[n].filter == EVFILT_WRITE) 184 | ev |= ULOOP_WRITE; 185 | 186 | if (events[n].flags & EV_EOF) 187 | u->eof = true; 188 | else if (!ev) 189 | cur->fd = NULL; 190 | 191 | cur->events = ev; 192 | if (u->flags & ULOOP_EDGE_DEFER) { 193 | u->flags &= ~ULOOP_EDGE_DEFER; 194 | u->flags |= ULOOP_EDGE_TRIGGER; 195 | register_kevent(u, u->flags); 196 | } 197 | } 198 | return nfds; 199 | } 200 | 201 | #endif 202 | 203 | #ifdef USE_EPOLL 204 | 205 | /** 206 | * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17 207 | */ 208 | #ifndef EPOLLRDHUP 209 | #define EPOLLRDHUP 0x2000 210 | #endif 211 | 212 | int uloop_init(void) 213 | { 214 | if (poll_fd >= 0) 215 | return 0; 216 | 217 | poll_fd = epoll_create(32); 218 | if (poll_fd < 0) 219 | return -1; 220 | 221 | fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC); 222 | return 0; 223 | } 224 | 225 | static int register_poll(struct uloop_fd *fd, unsigned int flags) 226 | { 227 | struct epoll_event ev; 228 | int op = fd->registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; 229 | 230 | memset(&ev, 0, sizeof(struct epoll_event)); 231 | 232 | if (flags & ULOOP_READ) 233 | ev.events |= EPOLLIN | EPOLLRDHUP; 234 | 235 | if (flags & ULOOP_WRITE) 236 | ev.events |= EPOLLOUT; 237 | 238 | if (flags & ULOOP_EDGE_TRIGGER) 239 | ev.events |= EPOLLET; 240 | 241 | ev.data.fd = fd->fd; 242 | ev.data.ptr = fd; 243 | fd->flags = flags; 244 | 245 | return epoll_ctl(poll_fd, op, fd->fd, &ev); 246 | } 247 | 248 | static struct epoll_event events[ULOOP_MAX_EVENTS]; 249 | 250 | static int __uloop_fd_delete(struct uloop_fd *sock) 251 | { 252 | sock->flags = 0; 253 | return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0); 254 | } 255 | 256 | static int uloop_fetch_events(int timeout) 257 | { 258 | int n, nfds; 259 | 260 | nfds = epoll_wait(poll_fd, events, ARRAY_SIZE(events), timeout); 261 | for (n = 0; n < nfds; ++n) { 262 | struct uloop_fd_event *cur = &cur_fds[n]; 263 | struct uloop_fd *u = events[n].data.ptr; 264 | unsigned int ev = 0; 265 | 266 | cur->fd = u; 267 | if (!u) 268 | continue; 269 | 270 | if (events[n].events & (EPOLLERR|EPOLLHUP)) { 271 | u->error = true; 272 | if (!(u->flags & ULOOP_ERROR_CB)) 273 | uloop_fd_delete(u); 274 | } 275 | 276 | if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) { 277 | cur->fd = NULL; 278 | continue; 279 | } 280 | 281 | if(events[n].events & EPOLLRDHUP) 282 | u->eof = true; 283 | 284 | if(events[n].events & EPOLLIN) 285 | ev |= ULOOP_READ; 286 | 287 | if(events[n].events & EPOLLOUT) 288 | ev |= ULOOP_WRITE; 289 | 290 | cur->events = ev; 291 | } 292 | 293 | return nfds; 294 | } 295 | 296 | #endif 297 | 298 | static bool uloop_fd_stack_event(struct uloop_fd *fd, int events) 299 | { 300 | struct uloop_fd_stack *cur; 301 | 302 | /* 303 | * Do not buffer events for level-triggered fds, they will keep firing. 304 | * Caller needs to take care of recursion issues. 305 | */ 306 | if (!(fd->flags & ULOOP_EDGE_TRIGGER)) 307 | return false; 308 | 309 | for (cur = fd_stack; cur; cur = cur->next) { 310 | if (cur->fd != fd) 311 | continue; 312 | 313 | if (events < 0) 314 | cur->fd = NULL; 315 | else 316 | cur->events |= events | ULOOP_EVENT_BUFFERED; 317 | 318 | return true; 319 | } 320 | 321 | return false; 322 | } 323 | 324 | static void uloop_run_events(int timeout) 325 | { 326 | struct uloop_fd_event *cur; 327 | struct uloop_fd *fd; 328 | 329 | if (!cur_nfds) { 330 | cur_fd = 0; 331 | cur_nfds = uloop_fetch_events(timeout); 332 | if (cur_nfds < 0) 333 | cur_nfds = 0; 334 | } 335 | 336 | while (cur_nfds > 0) { 337 | struct uloop_fd_stack stack_cur; 338 | unsigned int events; 339 | 340 | cur = &cur_fds[cur_fd++]; 341 | cur_nfds--; 342 | 343 | fd = cur->fd; 344 | events = cur->events; 345 | if (!fd) 346 | continue; 347 | 348 | if (!fd->cb) 349 | continue; 350 | 351 | if (uloop_fd_stack_event(fd, cur->events)) 352 | continue; 353 | 354 | stack_cur.next = fd_stack; 355 | stack_cur.fd = fd; 356 | fd_stack = &stack_cur; 357 | do { 358 | stack_cur.events = 0; 359 | fd->cb(fd, events); 360 | events = stack_cur.events & ULOOP_EVENT_MASK; 361 | } while (stack_cur.fd && events); 362 | fd_stack = stack_cur.next; 363 | 364 | return; 365 | } 366 | } 367 | 368 | int uloop_fd_add(struct uloop_fd *sock, unsigned int flags) 369 | { 370 | unsigned int fl; 371 | int ret; 372 | 373 | if (!(flags & (ULOOP_READ | ULOOP_WRITE))) 374 | return uloop_fd_delete(sock); 375 | 376 | if (!sock->registered && !(flags & ULOOP_BLOCKING)) { 377 | fl = fcntl(sock->fd, F_GETFL, 0); 378 | fl |= O_NONBLOCK; 379 | fcntl(sock->fd, F_SETFL, fl); 380 | } 381 | 382 | ret = register_poll(sock, flags); 383 | if (ret < 0) 384 | goto out; 385 | 386 | sock->registered = true; 387 | sock->eof = false; 388 | 389 | out: 390 | return ret; 391 | } 392 | 393 | int uloop_fd_delete(struct uloop_fd *fd) 394 | { 395 | int i; 396 | 397 | for (i = 0; i < cur_nfds; i++) { 398 | if (cur_fds[cur_fd + i].fd != fd) 399 | continue; 400 | 401 | cur_fds[cur_fd + i].fd = NULL; 402 | } 403 | 404 | if (!fd->registered) 405 | return 0; 406 | 407 | fd->registered = false; 408 | uloop_fd_stack_event(fd, -1); 409 | return __uloop_fd_delete(fd); 410 | } 411 | 412 | static int tv_diff(struct timeval *t1, struct timeval *t2) 413 | { 414 | return 415 | (t1->tv_sec - t2->tv_sec) * 1000 + 416 | (t1->tv_usec - t2->tv_usec) / 1000; 417 | } 418 | 419 | int uloop_timeout_add(struct uloop_timeout *timeout) 420 | { 421 | struct uloop_timeout *tmp; 422 | struct list_head *h = &timeouts; 423 | 424 | if (timeout->pending) 425 | return -1; 426 | 427 | list_for_each_entry(tmp, &timeouts, list) { 428 | if (tv_diff(&tmp->time, &timeout->time) > 0) { 429 | h = &tmp->list; 430 | break; 431 | } 432 | } 433 | 434 | list_add_tail(&timeout->list, h); 435 | timeout->pending = true; 436 | 437 | return 0; 438 | } 439 | 440 | static void uloop_gettime(struct timeval *tv) 441 | { 442 | struct timespec ts; 443 | 444 | clock_gettime(CLOCK_MONOTONIC, &ts); 445 | tv->tv_sec = ts.tv_sec; 446 | tv->tv_usec = ts.tv_nsec / 1000; 447 | } 448 | 449 | int uloop_timeout_set(struct uloop_timeout *timeout, int msecs) 450 | { 451 | struct timeval *time = &timeout->time; 452 | 453 | if (timeout->pending) 454 | uloop_timeout_cancel(timeout); 455 | 456 | uloop_gettime(&timeout->time); 457 | 458 | time->tv_sec += msecs / 1000; 459 | time->tv_usec += (msecs % 1000) * 1000; 460 | 461 | if (time->tv_usec > 1000000) { 462 | time->tv_sec++; 463 | time->tv_usec %= 1000000; 464 | } 465 | 466 | return uloop_timeout_add(timeout); 467 | } 468 | 469 | int uloop_timeout_cancel(struct uloop_timeout *timeout) 470 | { 471 | if (!timeout->pending) 472 | return -1; 473 | 474 | list_del(&timeout->list); 475 | timeout->pending = false; 476 | 477 | return 0; 478 | } 479 | 480 | int uloop_timeout_remaining(struct uloop_timeout *timeout) 481 | { 482 | struct timeval now; 483 | 484 | if (!timeout->pending) 485 | return -1; 486 | 487 | uloop_gettime(&now); 488 | 489 | return tv_diff(&timeout->time, &now); 490 | } 491 | 492 | int uloop_process_add(struct uloop_process *p) 493 | { 494 | struct uloop_process *tmp; 495 | struct list_head *h = &processes; 496 | 497 | if (p->pending) 498 | return -1; 499 | 500 | list_for_each_entry(tmp, &processes, list) { 501 | if (tmp->pid > p->pid) { 502 | h = &tmp->list; 503 | break; 504 | } 505 | } 506 | 507 | list_add_tail(&p->list, h); 508 | p->pending = true; 509 | 510 | return 0; 511 | } 512 | 513 | int uloop_process_delete(struct uloop_process *p) 514 | { 515 | if (!p->pending) 516 | return -1; 517 | 518 | list_del(&p->list); 519 | p->pending = false; 520 | 521 | return 0; 522 | } 523 | 524 | static void uloop_handle_processes(void) 525 | { 526 | struct uloop_process *p, *tmp; 527 | pid_t pid; 528 | int ret; 529 | 530 | do_sigchld = false; 531 | 532 | while (1) { 533 | pid = waitpid(-1, &ret, WNOHANG); 534 | if (pid <= 0) 535 | return; 536 | 537 | list_for_each_entry_safe(p, tmp, &processes, list) { 538 | if (p->pid < pid) 539 | continue; 540 | 541 | if (p->pid > pid) 542 | break; 543 | 544 | uloop_process_delete(p); 545 | p->cb(p, ret); 546 | } 547 | } 548 | 549 | } 550 | 551 | static void uloop_handle_sigint(int signo) 552 | { 553 | uloop_cancelled = true; 554 | } 555 | 556 | static void uloop_sigchld(int signo) 557 | { 558 | do_sigchld = true; 559 | } 560 | 561 | static void uloop_setup_signals(bool add) 562 | { 563 | static struct sigaction old_sigint, old_sigchld; 564 | struct sigaction s; 565 | 566 | memset(&s, 0, sizeof(struct sigaction)); 567 | 568 | if (add) { 569 | s.sa_handler = uloop_handle_sigint; 570 | s.sa_flags = 0; 571 | } else { 572 | s = old_sigint; 573 | } 574 | 575 | sigaction(SIGINT, &s, &old_sigint); 576 | 577 | if (!uloop_handle_sigchld) 578 | return; 579 | 580 | if (add) 581 | s.sa_handler = uloop_sigchld; 582 | else 583 | s = old_sigchld; 584 | 585 | sigaction(SIGCHLD, &s, &old_sigchld); 586 | } 587 | 588 | static int uloop_get_next_timeout(struct timeval *tv) 589 | { 590 | struct uloop_timeout *timeout; 591 | int diff; 592 | 593 | if (list_empty(&timeouts)) 594 | return -1; 595 | 596 | timeout = list_first_entry(&timeouts, struct uloop_timeout, list); 597 | diff = tv_diff(&timeout->time, tv); 598 | if (diff < 0) 599 | return 0; 600 | 601 | return diff; 602 | } 603 | 604 | static void uloop_process_timeouts(struct timeval *tv) 605 | { 606 | struct uloop_timeout *t; 607 | 608 | while (!list_empty(&timeouts)) { 609 | t = list_first_entry(&timeouts, struct uloop_timeout, list); 610 | 611 | if (tv_diff(&t->time, tv) > 0) 612 | break; 613 | 614 | uloop_timeout_cancel(t); 615 | if (t->cb) 616 | t->cb(t); 617 | } 618 | } 619 | 620 | static void uloop_clear_timeouts(void) 621 | { 622 | struct uloop_timeout *t, *tmp; 623 | 624 | list_for_each_entry_safe(t, tmp, &timeouts, list) 625 | uloop_timeout_cancel(t); 626 | } 627 | 628 | static void uloop_clear_processes(void) 629 | { 630 | struct uloop_process *p, *tmp; 631 | 632 | list_for_each_entry_safe(p, tmp, &processes, list) 633 | uloop_process_delete(p); 634 | } 635 | 636 | void uloop_run(void) 637 | { 638 | static int recursive_calls = 0; 639 | struct timeval tv; 640 | 641 | /* 642 | * Handlers are only updated for the first call to uloop_run() (and restored 643 | * when this call is done). 644 | */ 645 | if (!recursive_calls++) 646 | uloop_setup_signals(true); 647 | 648 | while(!uloop_cancelled) 649 | { 650 | uloop_gettime(&tv); 651 | uloop_process_timeouts(&tv); 652 | if (uloop_cancelled) 653 | break; 654 | 655 | if (do_sigchld) 656 | uloop_handle_processes(); 657 | uloop_gettime(&tv); 658 | uloop_run_events(uloop_get_next_timeout(&tv)); 659 | } 660 | 661 | if (!--recursive_calls) 662 | uloop_setup_signals(false); 663 | } 664 | 665 | void uloop_done(void) 666 | { 667 | if (poll_fd < 0) 668 | return; 669 | 670 | close(poll_fd); 671 | poll_fd = -1; 672 | 673 | uloop_clear_timeouts(); 674 | uloop_clear_processes(); 675 | } 676 | -------------------------------------------------------------------------------- /uloop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * uloop - event loop implementation 3 | * 4 | * Copyright (C) 2010-2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef _ULOOP_H__ 19 | #define _ULOOP_H__ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #if defined(__APPLE__) || defined(__FreeBSD__) 28 | #define USE_KQUEUE 29 | #else 30 | #define USE_EPOLL 31 | #endif 32 | 33 | #include "list.h" 34 | 35 | struct uloop_fd; 36 | struct uloop_timeout; 37 | struct uloop_process; 38 | 39 | typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events); 40 | typedef void (*uloop_timeout_handler)(struct uloop_timeout *t); 41 | typedef void (*uloop_process_handler)(struct uloop_process *c, int ret); 42 | 43 | #define ULOOP_READ (1 << 0) 44 | #define ULOOP_WRITE (1 << 1) 45 | #define ULOOP_EDGE_TRIGGER (1 << 2) 46 | #define ULOOP_BLOCKING (1 << 3) 47 | 48 | #define ULOOP_EVENT_MASK (ULOOP_READ | ULOOP_WRITE) 49 | 50 | /* internal flags */ 51 | #define ULOOP_EVENT_BUFFERED (1 << 4) 52 | #ifdef USE_KQUEUE 53 | #define ULOOP_EDGE_DEFER (1 << 5) 54 | #endif 55 | 56 | #define ULOOP_ERROR_CB (1 << 6) 57 | 58 | struct uloop_fd 59 | { 60 | uloop_fd_handler cb; 61 | int fd; 62 | bool eof; 63 | bool error; 64 | bool registered; 65 | uint8_t flags; 66 | }; 67 | 68 | struct uloop_timeout 69 | { 70 | struct list_head list; 71 | bool pending; 72 | 73 | uloop_timeout_handler cb; 74 | struct timeval time; 75 | }; 76 | 77 | struct uloop_process 78 | { 79 | struct list_head list; 80 | bool pending; 81 | 82 | uloop_process_handler cb; 83 | pid_t pid; 84 | }; 85 | 86 | extern bool uloop_cancelled; 87 | extern bool uloop_handle_sigchld; 88 | 89 | int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); 90 | int uloop_fd_delete(struct uloop_fd *sock); 91 | 92 | int uloop_timeout_add(struct uloop_timeout *timeout); 93 | int uloop_timeout_set(struct uloop_timeout *timeout, int msecs); 94 | int uloop_timeout_cancel(struct uloop_timeout *timeout); 95 | int uloop_timeout_remaining(struct uloop_timeout *timeout); 96 | 97 | int uloop_process_add(struct uloop_process *p); 98 | int uloop_process_delete(struct uloop_process *p); 99 | 100 | static inline void uloop_end(void) 101 | { 102 | uloop_cancelled = true; 103 | } 104 | 105 | int uloop_init(void); 106 | void uloop_run(void); 107 | void uloop_done(void); 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /usock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usock - socket helper functions 3 | * 4 | * Copyright (C) 2010 Steven Barth 5 | * Copyright (C) 2011-2012 Felix Fietkau 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "usock.h" 31 | 32 | static void usock_set_flags(int sock, unsigned int type) 33 | { 34 | if (!(type & USOCK_NOCLOEXEC)) 35 | fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); 36 | 37 | if (type & USOCK_NONBLOCK) 38 | fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); 39 | } 40 | 41 | static int usock_connect(struct sockaddr *sa, int sa_len, int family, int socktype, bool server) 42 | { 43 | int sock; 44 | 45 | sock = socket(family, socktype, 0); 46 | if (sock < 0) 47 | return -1; 48 | 49 | if (server) { 50 | const int one = 1; 51 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 52 | 53 | if (!bind(sock, sa, sa_len) && 54 | (socktype != SOCK_STREAM || !listen(sock, SOMAXCONN))) 55 | return sock; 56 | } else { 57 | if (!connect(sock, sa, sa_len) || errno == EINPROGRESS) 58 | return sock; 59 | } 60 | 61 | close(sock); 62 | return -1; 63 | } 64 | 65 | static int usock_unix(const char *host, int socktype, bool server) 66 | { 67 | struct sockaddr_un sun = {.sun_family = AF_UNIX}; 68 | 69 | if (strlen(host) >= sizeof(sun.sun_path)) { 70 | errno = EINVAL; 71 | return -1; 72 | } 73 | strcpy(sun.sun_path, host); 74 | 75 | return usock_connect((struct sockaddr*)&sun, sizeof(sun), AF_UNIX, socktype, server); 76 | } 77 | 78 | static int usock_inet(int type, const char *host, const char *service, int socktype, bool server) 79 | { 80 | struct addrinfo *result, *rp; 81 | struct addrinfo hints = { 82 | .ai_family = (type & USOCK_IPV6ONLY) ? AF_INET6 : 83 | (type & USOCK_IPV4ONLY) ? AF_INET : AF_UNSPEC, 84 | .ai_socktype = socktype, 85 | .ai_flags = AI_ADDRCONFIG 86 | | ((type & USOCK_SERVER) ? AI_PASSIVE : 0) 87 | | ((type & USOCK_NUMERIC) ? AI_NUMERICHOST : 0), 88 | }; 89 | int sock = -1; 90 | 91 | if (getaddrinfo(host, service, &hints, &result)) 92 | return -1; 93 | 94 | for (rp = result; rp != NULL; rp = rp->ai_next) { 95 | sock = usock_connect(rp->ai_addr, rp->ai_addrlen, rp->ai_family, socktype, server); 96 | if (sock >= 0) 97 | break; 98 | } 99 | 100 | freeaddrinfo(result); 101 | return sock; 102 | } 103 | 104 | int usock(int type, const char *host, const char *service) { 105 | int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 106 | bool server = !!(type & USOCK_SERVER); 107 | int sock; 108 | 109 | if (type & USOCK_UNIX) 110 | sock = usock_unix(host, socktype, server); 111 | else 112 | sock = usock_inet(type, host, service, socktype, server); 113 | 114 | if (sock < 0) 115 | return -1; 116 | 117 | usock_set_flags(sock, type); 118 | return sock; 119 | } 120 | -------------------------------------------------------------------------------- /usock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usock - socket helper functions 3 | * 4 | * Copyright (C) 2010 Steven Barth 5 | * Copyright (C) 2011-2012 Felix Fietkau 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | #ifndef USOCK_H_ 20 | #define USOCK_H_ 21 | 22 | #define USOCK_TCP 0 23 | #define USOCK_UDP 1 24 | 25 | #define USOCK_SERVER 0x0100 26 | #define USOCK_NOCLOEXEC 0x0200 27 | #define USOCK_NONBLOCK 0x0400 28 | #define USOCK_NUMERIC 0x0800 29 | #define USOCK_IPV6ONLY 0x2000 30 | #define USOCK_IPV4ONLY 0x4000 31 | #define USOCK_UNIX 0x8000 32 | 33 | int usock(int type, const char *host, const char *service); 34 | 35 | #endif /* USOCK_H_ */ 36 | -------------------------------------------------------------------------------- /ustream-fd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ustream - library for stream buffer management 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "ustream.h" 23 | 24 | static void ustream_fd_set_uloop(struct ustream *s, bool write) 25 | { 26 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 27 | struct ustream_buf *buf; 28 | unsigned int flags = ULOOP_EDGE_TRIGGER; 29 | 30 | if (!s->read_blocked && !s->eof) 31 | flags |= ULOOP_READ; 32 | 33 | buf = s->w.head; 34 | if (write || (buf && s->w.data_bytes && !s->write_error)) 35 | flags |= ULOOP_WRITE; 36 | 37 | uloop_fd_add(&sf->fd, flags); 38 | } 39 | 40 | static void ustream_fd_set_read_blocked(struct ustream *s) 41 | { 42 | ustream_fd_set_uloop(s, false); 43 | } 44 | 45 | static void ustream_fd_read_pending(struct ustream_fd *sf, bool *more) 46 | { 47 | struct ustream *s = &sf->stream; 48 | int buflen = 0; 49 | ssize_t len; 50 | char *buf; 51 | 52 | do { 53 | buf = ustream_reserve(s, 1, &buflen); 54 | if (!buf) 55 | break; 56 | 57 | len = read(sf->fd.fd, buf, buflen); 58 | if (len < 0) { 59 | if (errno == EINTR) 60 | continue; 61 | 62 | if (errno == EAGAIN) 63 | return; 64 | 65 | len = 0; 66 | } 67 | 68 | if (!len) { 69 | if (!s->eof) 70 | ustream_state_change(s); 71 | s->eof = true; 72 | ustream_fd_set_uloop(s, false); 73 | return; 74 | } 75 | 76 | ustream_fill_read(s, len); 77 | *more = true; 78 | } while (1); 79 | } 80 | 81 | static int ustream_fd_write(struct ustream *s, const char *buf, int buflen, bool more) 82 | { 83 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 84 | ssize_t ret = 0, len; 85 | 86 | if (!buflen) 87 | return 0; 88 | 89 | while (buflen) { 90 | len = write(sf->fd.fd, buf, buflen); 91 | 92 | if (len < 0) { 93 | if (errno == EINTR) 94 | continue; 95 | 96 | if (errno == EAGAIN || errno == EWOULDBLOCK) 97 | break; 98 | 99 | return -1; 100 | } 101 | 102 | ret += len; 103 | buf += len; 104 | buflen -= len; 105 | } 106 | 107 | if (buflen) 108 | ustream_fd_set_uloop(s, true); 109 | 110 | return ret; 111 | } 112 | 113 | static bool __ustream_fd_poll(struct ustream_fd *sf, unsigned int events) 114 | { 115 | struct ustream *s = &sf->stream; 116 | bool more = false; 117 | 118 | if (events & ULOOP_READ) 119 | ustream_fd_read_pending(sf, &more); 120 | 121 | if (events & ULOOP_WRITE) { 122 | if (!ustream_write_pending(s)) 123 | ustream_fd_set_uloop(s, false); 124 | } 125 | 126 | return more; 127 | } 128 | 129 | static bool ustream_fd_poll(struct ustream *s) 130 | { 131 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 132 | 133 | return __ustream_fd_poll(sf, ULOOP_READ | ULOOP_WRITE); 134 | } 135 | 136 | static void ustream_uloop_cb(struct uloop_fd *fd, unsigned int events) 137 | { 138 | struct ustream_fd *sf = container_of(fd, struct ustream_fd, fd); 139 | 140 | __ustream_fd_poll(sf, events); 141 | } 142 | 143 | static void ustream_fd_free(struct ustream *s) 144 | { 145 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 146 | 147 | uloop_fd_delete(&sf->fd); 148 | } 149 | 150 | void ustream_fd_init(struct ustream_fd *sf, int fd) 151 | { 152 | struct ustream *s = &sf->stream; 153 | 154 | ustream_init_defaults(s); 155 | 156 | sf->fd.fd = fd; 157 | sf->fd.cb = ustream_uloop_cb; 158 | s->set_read_blocked = ustream_fd_set_read_blocked; 159 | s->write = ustream_fd_write; 160 | s->free = ustream_fd_free; 161 | s->poll = ustream_fd_poll; 162 | ustream_fd_set_uloop(s, false); 163 | } 164 | -------------------------------------------------------------------------------- /ustream.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ustream - library for stream buffer management 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "ustream.h" 26 | 27 | static void ustream_init_buf(struct ustream_buf *buf, int len) 28 | { 29 | if (!len) 30 | abort(); 31 | 32 | memset(buf, 0, sizeof(*buf)); 33 | buf->data = buf->tail = buf->head; 34 | buf->end = buf->head + len; 35 | *buf->head = 0; 36 | } 37 | 38 | static void ustream_add_buf(struct ustream_buf_list *l, struct ustream_buf *buf) 39 | { 40 | l->buffers++; 41 | if (!l->tail) 42 | l->head = buf; 43 | else 44 | l->tail->next = buf; 45 | 46 | buf->next = NULL; 47 | l->tail = buf; 48 | if (!l->data_tail) 49 | l->data_tail = l->head; 50 | } 51 | 52 | static bool ustream_can_alloc(struct ustream_buf_list *l) 53 | { 54 | if (l->max_buffers <= 0) 55 | return true; 56 | 57 | return (l->buffers < l->max_buffers); 58 | } 59 | 60 | static int ustream_alloc_default(struct ustream *s, struct ustream_buf_list *l) 61 | { 62 | struct ustream_buf *buf; 63 | 64 | if (!ustream_can_alloc(l)) 65 | return -1; 66 | 67 | buf = malloc(sizeof(*buf) + l->buffer_len + s->string_data); 68 | ustream_init_buf(buf, l->buffer_len); 69 | ustream_add_buf(l, buf); 70 | 71 | return 0; 72 | } 73 | 74 | static void ustream_free_buffers(struct ustream_buf_list *l) 75 | { 76 | struct ustream_buf *buf = l->head; 77 | 78 | while (buf) { 79 | struct ustream_buf *next = buf->next; 80 | 81 | free(buf); 82 | buf = next; 83 | } 84 | l->head = NULL; 85 | l->tail = NULL; 86 | l->data_tail = NULL; 87 | } 88 | 89 | void ustream_free(struct ustream *s) 90 | { 91 | if (s->free) 92 | s->free(s); 93 | 94 | uloop_timeout_cancel(&s->state_change); 95 | ustream_free_buffers(&s->r); 96 | ustream_free_buffers(&s->w); 97 | } 98 | 99 | static void ustream_state_change_cb(struct uloop_timeout *t) 100 | { 101 | struct ustream *s = container_of(t, struct ustream, state_change); 102 | 103 | if (s->write_error) 104 | ustream_free_buffers(&s->w); 105 | if (s->notify_state) 106 | s->notify_state(s); 107 | } 108 | 109 | void ustream_init_defaults(struct ustream *s) 110 | { 111 | #define DEFAULT_SET(_f, _default) \ 112 | do { \ 113 | if (!_f) \ 114 | _f = _default; \ 115 | } while(0) 116 | 117 | DEFAULT_SET(s->r.alloc, ustream_alloc_default); 118 | DEFAULT_SET(s->w.alloc, ustream_alloc_default); 119 | 120 | DEFAULT_SET(s->r.min_buffers, 1); 121 | DEFAULT_SET(s->r.max_buffers, 1); 122 | DEFAULT_SET(s->r.buffer_len, 4096); 123 | 124 | DEFAULT_SET(s->w.min_buffers, 2); 125 | DEFAULT_SET(s->w.max_buffers, -1); 126 | DEFAULT_SET(s->w.buffer_len, 256); 127 | 128 | #undef DEFAULT_SET 129 | 130 | s->state_change.cb = ustream_state_change_cb; 131 | s->write_error = false; 132 | s->eof = false; 133 | s->eof_write_done = false; 134 | s->read_blocked = 0; 135 | 136 | s->r.buffers = 0; 137 | s->r.data_bytes = 0; 138 | 139 | s->w.buffers = 0; 140 | s->w.data_bytes = 0; 141 | } 142 | 143 | static bool ustream_should_move(struct ustream_buf_list *l, struct ustream_buf *buf, int len) 144 | { 145 | int maxlen; 146 | int offset; 147 | 148 | if (buf->data == buf->head) 149 | return false; 150 | 151 | maxlen = buf->end - buf->head; 152 | offset = buf->data - buf->head; 153 | 154 | if (offset > maxlen / 2) 155 | return true; 156 | 157 | if (buf->tail - buf->data < 32 && offset > maxlen / 4) 158 | return true; 159 | 160 | if (buf != l->tail || ustream_can_alloc(l)) 161 | return false; 162 | 163 | return (buf->end - buf->tail < len); 164 | } 165 | 166 | static void ustream_free_buf(struct ustream_buf_list *l, struct ustream_buf *buf) 167 | { 168 | if (buf == l->head) 169 | l->head = buf->next; 170 | 171 | if (buf == l->data_tail) 172 | l->data_tail = buf->next; 173 | 174 | if (buf == l->tail) 175 | l->tail = NULL; 176 | 177 | if (--l->buffers >= l->min_buffers) { 178 | free(buf); 179 | return; 180 | } 181 | 182 | /* recycle */ 183 | ustream_init_buf(buf, buf->end - buf->head); 184 | ustream_add_buf(l, buf); 185 | } 186 | 187 | static void __ustream_set_read_blocked(struct ustream *s, unsigned char val) 188 | { 189 | bool changed = !!s->read_blocked != !!val; 190 | 191 | s->read_blocked = val; 192 | if (changed) 193 | s->set_read_blocked(s); 194 | } 195 | 196 | void ustream_set_read_blocked(struct ustream *s, bool set) 197 | { 198 | unsigned char val = s->read_blocked & ~READ_BLOCKED_USER; 199 | 200 | if (set) 201 | val |= READ_BLOCKED_USER; 202 | 203 | __ustream_set_read_blocked(s, val); 204 | } 205 | 206 | void ustream_consume(struct ustream *s, int len) 207 | { 208 | struct ustream_buf *buf = s->r.head; 209 | 210 | if (!len) 211 | return; 212 | 213 | s->r.data_bytes -= len; 214 | if (s->r.data_bytes < 0) 215 | abort(); 216 | 217 | do { 218 | struct ustream_buf *next = buf->next; 219 | int buf_len = buf->tail - buf->data; 220 | 221 | if (len < buf_len) { 222 | buf->data += len; 223 | break; 224 | } 225 | 226 | len -= buf_len; 227 | ustream_free_buf(&s->r, buf); 228 | buf = next; 229 | } while(len); 230 | 231 | __ustream_set_read_blocked(s, s->read_blocked & ~READ_BLOCKED_FULL); 232 | } 233 | 234 | static void ustream_fixup_string(struct ustream *s, struct ustream_buf *buf) 235 | { 236 | if (!s->string_data) 237 | return; 238 | 239 | *buf->tail = 0; 240 | } 241 | 242 | static bool ustream_prepare_buf(struct ustream *s, struct ustream_buf_list *l, int len) 243 | { 244 | struct ustream_buf *buf; 245 | 246 | buf = l->data_tail; 247 | if (buf) { 248 | if (ustream_should_move(l, buf, len)) { 249 | int len = buf->tail - buf->data; 250 | 251 | memmove(buf->head, buf->data, len); 252 | buf->data = buf->head; 253 | buf->tail = buf->data + len; 254 | 255 | if (l == &s->r) 256 | ustream_fixup_string(s, buf); 257 | } 258 | if (buf->tail != buf->end) 259 | return true; 260 | } 261 | 262 | if (buf && buf->next) { 263 | l->data_tail = buf->next; 264 | return true; 265 | } 266 | 267 | if (!ustream_can_alloc(l)) 268 | return false; 269 | 270 | if (l->alloc(s, l) < 0) 271 | return false; 272 | 273 | l->data_tail = l->tail; 274 | return true; 275 | } 276 | 277 | char *ustream_reserve(struct ustream *s, int len, int *maxlen) 278 | { 279 | struct ustream_buf *buf = s->r.head; 280 | 281 | if (!ustream_prepare_buf(s, &s->r, len)) { 282 | __ustream_set_read_blocked(s, s->read_blocked | READ_BLOCKED_FULL); 283 | *maxlen = 0; 284 | return NULL; 285 | } 286 | 287 | buf = s->r.data_tail; 288 | *maxlen = buf->end - buf->tail; 289 | return buf->tail; 290 | } 291 | 292 | void ustream_fill_read(struct ustream *s, int len) 293 | { 294 | struct ustream_buf *buf = s->r.data_tail; 295 | int n = len; 296 | int maxlen; 297 | 298 | s->r.data_bytes += len; 299 | do { 300 | if (!buf) 301 | abort(); 302 | 303 | maxlen = buf->end - buf->tail; 304 | if (len < maxlen) 305 | maxlen = len; 306 | 307 | len -= maxlen; 308 | buf->tail += maxlen; 309 | ustream_fixup_string(s, buf); 310 | 311 | s->r.data_tail = buf; 312 | buf = buf->next; 313 | } while (len); 314 | 315 | if (s->notify_read) 316 | s->notify_read(s, n); 317 | } 318 | 319 | char *ustream_get_read_buf(struct ustream *s, int *buflen) 320 | { 321 | char *data = NULL; 322 | int len = 0; 323 | 324 | if (s->r.head) { 325 | len = s->r.head->tail - s->r.head->data; 326 | if (len > 0) 327 | data = s->r.head->data; 328 | } 329 | 330 | if (buflen) 331 | *buflen = len; 332 | 333 | return data; 334 | } 335 | 336 | static void ustream_write_error(struct ustream *s) 337 | { 338 | if (!s->write_error) 339 | ustream_state_change(s); 340 | s->write_error = true; 341 | } 342 | 343 | bool ustream_write_pending(struct ustream *s) 344 | { 345 | struct ustream_buf *buf = s->w.head; 346 | int wr = 0, len; 347 | 348 | if (s->write_error) 349 | return false; 350 | 351 | while (buf && s->w.data_bytes) { 352 | struct ustream_buf *next = buf->next; 353 | int maxlen = buf->tail - buf->data; 354 | 355 | len = s->write(s, buf->data, maxlen, !!buf->next); 356 | if (len < 0) { 357 | ustream_write_error(s); 358 | break; 359 | } 360 | 361 | if (len == 0) 362 | break; 363 | 364 | wr += len; 365 | s->w.data_bytes -= len; 366 | if (len < maxlen) { 367 | buf->data += len; 368 | break; 369 | } 370 | 371 | ustream_free_buf(&s->w, buf); 372 | buf = next; 373 | } 374 | 375 | if (s->notify_write) 376 | s->notify_write(s, wr); 377 | 378 | if (s->eof && wr && !s->w.data_bytes) 379 | ustream_state_change(s); 380 | 381 | return !s->w.data_bytes; 382 | } 383 | 384 | static int ustream_write_buffered(struct ustream *s, const char *data, int len, int wr) 385 | { 386 | struct ustream_buf_list *l = &s->w; 387 | struct ustream_buf *buf; 388 | int maxlen; 389 | 390 | while (len) { 391 | if (!ustream_prepare_buf(s, &s->w, len)) 392 | break; 393 | 394 | buf = l->data_tail; 395 | 396 | maxlen = buf->end - buf->tail; 397 | if (maxlen > len) 398 | maxlen = len; 399 | 400 | memcpy(buf->tail, data, maxlen); 401 | buf->tail += maxlen; 402 | data += maxlen; 403 | len -= maxlen; 404 | wr += maxlen; 405 | l->data_bytes += maxlen; 406 | } 407 | 408 | return wr; 409 | } 410 | 411 | int ustream_write(struct ustream *s, const char *data, int len, bool more) 412 | { 413 | struct ustream_buf_list *l = &s->w; 414 | int wr = 0; 415 | 416 | if (s->write_error) 417 | return 0; 418 | 419 | if (!l->data_bytes) { 420 | wr = s->write(s, data, len, more); 421 | if (wr == len) 422 | return wr; 423 | 424 | if (wr < 0) { 425 | ustream_write_error(s); 426 | return wr; 427 | } 428 | 429 | data += wr; 430 | len -= wr; 431 | } 432 | 433 | return ustream_write_buffered(s, data, len, wr); 434 | } 435 | 436 | #define MAX_STACK_BUFLEN 256 437 | 438 | int ustream_vprintf(struct ustream *s, const char *format, va_list arg) 439 | { 440 | struct ustream_buf_list *l = &s->w; 441 | char *buf; 442 | va_list arg2; 443 | int wr, maxlen, buflen; 444 | 445 | if (s->write_error) 446 | return 0; 447 | 448 | if (!l->data_bytes) { 449 | buf = alloca(MAX_STACK_BUFLEN); 450 | va_copy(arg2, arg); 451 | maxlen = vsnprintf(buf, MAX_STACK_BUFLEN, format, arg2); 452 | va_end(arg2); 453 | if (maxlen < MAX_STACK_BUFLEN) { 454 | wr = s->write(s, buf, maxlen, false); 455 | if (wr < 0) { 456 | ustream_write_error(s); 457 | return wr; 458 | } 459 | if (wr == maxlen) 460 | return wr; 461 | 462 | buf += wr; 463 | maxlen -= wr; 464 | return ustream_write_buffered(s, buf, maxlen, wr); 465 | } else { 466 | buf = malloc(maxlen + 1); 467 | wr = vsnprintf(buf, maxlen + 1, format, arg); 468 | wr = ustream_write(s, buf, wr, false); 469 | free(buf); 470 | return wr; 471 | } 472 | } 473 | 474 | if (!ustream_prepare_buf(s, l, 1)) 475 | return 0; 476 | 477 | buf = l->data_tail->tail; 478 | buflen = l->data_tail->end - buf; 479 | 480 | va_copy(arg2, arg); 481 | maxlen = vsnprintf(buf, buflen, format, arg2); 482 | va_end(arg2); 483 | 484 | wr = maxlen; 485 | if (wr >= buflen) 486 | wr = buflen - 1; 487 | 488 | l->data_tail->tail += wr; 489 | l->data_bytes += wr; 490 | if (maxlen < buflen) 491 | return wr; 492 | 493 | buf = malloc(maxlen + 1); 494 | maxlen = vsnprintf(buf, maxlen + 1, format, arg); 495 | wr = ustream_write_buffered(s, buf + wr, maxlen - wr, wr); 496 | free(buf); 497 | 498 | return wr; 499 | } 500 | 501 | int ustream_printf(struct ustream *s, const char *format, ...) 502 | { 503 | va_list arg; 504 | int ret; 505 | 506 | if (s->write_error) 507 | return 0; 508 | 509 | va_start(arg, format); 510 | ret = ustream_vprintf(s, format, arg); 511 | va_end(arg); 512 | 513 | return ret; 514 | } 515 | -------------------------------------------------------------------------------- /ustream.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ustream - library for stream buffer management 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __USTREAM_H 20 | #define __USTREAM_H 21 | 22 | #include 23 | #include "uloop.h" 24 | 25 | struct ustream; 26 | struct ustream_buf; 27 | 28 | enum read_blocked_reason { 29 | READ_BLOCKED_USER = (1 << 0), 30 | READ_BLOCKED_FULL = (1 << 1), 31 | }; 32 | 33 | struct ustream_buf_list { 34 | struct ustream_buf *head; 35 | struct ustream_buf *data_tail; 36 | struct ustream_buf *tail; 37 | 38 | int (*alloc)(struct ustream *s, struct ustream_buf_list *l); 39 | 40 | int data_bytes; 41 | 42 | int min_buffers; 43 | int max_buffers; 44 | int buffer_len; 45 | 46 | int buffers; 47 | }; 48 | 49 | struct ustream { 50 | struct ustream_buf_list r, w; 51 | struct uloop_timeout state_change; 52 | struct ustream *next; 53 | 54 | /* 55 | * notify_read: (optional) 56 | * called by the ustream core to notify that new data is available 57 | * for reading. 58 | * must not free the ustream from this callback 59 | */ 60 | void (*notify_read)(struct ustream *s, int bytes_new); 61 | 62 | /* 63 | * notify_write: (optional) 64 | * called by the ustream core to notify that some buffered data has 65 | * been written to the stream. 66 | * must not free the ustream from this callback 67 | */ 68 | void (*notify_write)(struct ustream *s, int bytes); 69 | 70 | /* 71 | * notify_state: (optional) 72 | * called by the ustream implementation to notify that the read 73 | * side of the stream is closed (eof is set) or there was a write 74 | * error (write_error is set). 75 | * will be called again after the write buffer has been emptied when 76 | * the read side has hit EOF. 77 | */ 78 | void (*notify_state)(struct ustream *s); 79 | 80 | /* 81 | * write: 82 | * must be defined by ustream implementation, accepts new write data. 83 | * 'more' is used to indicate that a subsequent call will provide more 84 | * data (useful for aggregating writes) 85 | * returns the number of bytes accepted, or -1 if no more writes can 86 | * be accepted (link error) 87 | */ 88 | int (*write)(struct ustream *s, const char *buf, int len, bool more); 89 | 90 | /* 91 | * free: (optional) 92 | * defined by ustream implementation, tears down the ustream and frees data 93 | */ 94 | void (*free)(struct ustream *s); 95 | 96 | /* 97 | * set_read_blocked: (optional) 98 | * defined by ustream implementation, called when the read_blocked flag 99 | * changes 100 | */ 101 | void (*set_read_blocked)(struct ustream *s); 102 | 103 | /* 104 | * poll: (optional) 105 | * defined by the upstream implementation, called to request polling for 106 | * available data. 107 | * returns true if data was fetched. 108 | */ 109 | bool (*poll)(struct ustream *s); 110 | 111 | /* 112 | * ustream user should set this if the input stream is expected 113 | * to contain string data. the core will keep all data 0-terminated. 114 | */ 115 | bool string_data; 116 | bool write_error; 117 | bool eof, eof_write_done; 118 | 119 | enum read_blocked_reason read_blocked; 120 | }; 121 | 122 | struct ustream_fd { 123 | struct ustream stream; 124 | struct uloop_fd fd; 125 | }; 126 | 127 | struct ustream_buf { 128 | struct ustream_buf *next; 129 | 130 | char *data; 131 | char *tail; 132 | char *end; 133 | 134 | char head[]; 135 | }; 136 | 137 | /* ustream_fd_init: create a file descriptor ustream (uses uloop) */ 138 | void ustream_fd_init(struct ustream_fd *s, int fd); 139 | 140 | /* ustream_free: free all buffers and data associated with a ustream */ 141 | void ustream_free(struct ustream *s); 142 | 143 | /* ustream_consume: remove data from the head of the read buffer */ 144 | void ustream_consume(struct ustream *s, int len); 145 | 146 | /* ustream_write: add data to the write buffer */ 147 | int ustream_write(struct ustream *s, const char *buf, int len, bool more); 148 | int ustream_printf(struct ustream *s, const char *format, ...); 149 | int ustream_vprintf(struct ustream *s, const char *format, va_list arg); 150 | 151 | /* ustream_get_read_buf: get a pointer to the next read buffer data */ 152 | char *ustream_get_read_buf(struct ustream *s, int *buflen); 153 | 154 | /* 155 | * ustream_set_read_blocked: set read blocked state 156 | * 157 | * if set, the ustream will no longer fetch pending data. 158 | */ 159 | void ustream_set_read_blocked(struct ustream *s, bool set); 160 | 161 | static inline bool ustream_read_blocked(struct ustream *s) 162 | { 163 | return !!(s->read_blocked & READ_BLOCKED_USER); 164 | } 165 | 166 | static inline int ustream_pending_data(struct ustream *s, bool write) 167 | { 168 | struct ustream_buf_list *b = write ? &s->w : &s->r; 169 | return b->data_bytes; 170 | } 171 | 172 | static inline bool ustream_read_buf_full(struct ustream *s) 173 | { 174 | struct ustream_buf *buf = s->r.data_tail; 175 | return buf && buf->data == buf->head && buf->tail == buf->end && 176 | s->r.buffers == s->r.max_buffers; 177 | } 178 | 179 | /*** --- functions only used by ustream implementations --- ***/ 180 | 181 | /* ustream_init_defaults: fill default callbacks and options */ 182 | void ustream_init_defaults(struct ustream *s); 183 | 184 | /* 185 | * ustream_reserve: allocate rx buffer space 186 | * 187 | * len: hint for how much space is needed (not guaranteed to be met) 188 | * maxlen: pointer to where the actual buffer size is going to be stored 189 | */ 190 | char *ustream_reserve(struct ustream *s, int len, int *maxlen); 191 | 192 | /* ustream_fill_read: mark rx buffer space as filled */ 193 | void ustream_fill_read(struct ustream *s, int len); 194 | 195 | /* 196 | * ustream_write_pending: attempt to write more data from write buffers 197 | * returns true if all write buffers have been emptied. 198 | */ 199 | bool ustream_write_pending(struct ustream *s); 200 | 201 | static inline void ustream_state_change(struct ustream *s) 202 | { 203 | uloop_timeout_set(&s->state_change, 0); 204 | } 205 | 206 | static inline bool ustream_poll(struct ustream *s) 207 | { 208 | if (!s->poll) 209 | return false; 210 | 211 | return s->poll(s); 212 | } 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * utils - misc libubox utility functions 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "utils.h" 20 | #include 21 | #include 22 | #include 23 | 24 | #define foreach_arg(_arg, _addr, _len, _first_addr, _first_len) \ 25 | for (_addr = (_first_addr), _len = (_first_len); \ 26 | _addr; \ 27 | _addr = va_arg(_arg, void **), _len = _addr ? va_arg(_arg, size_t) : 0) 28 | 29 | void *__calloc_a(size_t len, ...) 30 | { 31 | va_list ap, ap1; 32 | void *ret; 33 | void **cur_addr; 34 | size_t cur_len; 35 | int alloc_len = 0; 36 | char *ptr; 37 | 38 | va_start(ap, len); 39 | 40 | va_copy(ap1, ap); 41 | foreach_arg(ap1, cur_addr, cur_len, &ret, len) 42 | alloc_len += cur_len; 43 | va_end(ap1); 44 | 45 | ptr = calloc(1, alloc_len); 46 | alloc_len = 0; 47 | foreach_arg(ap, cur_addr, cur_len, &ret, len) { 48 | *cur_addr = &ptr[alloc_len]; 49 | alloc_len += cur_len; 50 | } 51 | va_end(ap); 52 | 53 | return ret; 54 | } 55 | 56 | #ifdef __APPLE__ 57 | #include 58 | 59 | static void clock_gettime_realtime(struct timespec *tv) 60 | { 61 | struct timeval _tv; 62 | 63 | gettimeofday(&_tv, NULL); 64 | tv->tv_sec = _tv.tv_sec; 65 | tv->tv_nsec = _tv.tv_usec * 1000; 66 | } 67 | 68 | static void clock_gettime_monotonic(struct timespec *tv) 69 | { 70 | mach_timebase_info_data_t info; 71 | float sec; 72 | uint64_t val; 73 | 74 | mach_timebase_info(&info); 75 | 76 | val = mach_absolute_time(); 77 | tv->tv_nsec = (val * info.numer / info.denom) % 1000000000; 78 | 79 | sec = val; 80 | sec *= info.numer; 81 | sec /= info.denom; 82 | sec /= 1000000000; 83 | tv->tv_sec = sec; 84 | } 85 | 86 | void clock_gettime(int type, struct timespec *tv) 87 | { 88 | if (type == CLOCK_REALTIME) 89 | return clock_gettime_realtime(tv); 90 | else 91 | return clock_gettime_monotonic(tv); 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * utils - misc libubox utility functions 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __LIBUBOX_UTILS_H 20 | #define __LIBUBOX_UTILS_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* 29 | * calloc_a(size_t len, [void **addr, size_t len,...], NULL) 30 | * 31 | * allocate a block of memory big enough to hold multiple aligned objects. 32 | * the pointer to the full object (starting with the first chunk) is returned, 33 | * all other pointers are stored in the locations behind extra addr arguments. 34 | * the last argument needs to be a NULL pointer 35 | */ 36 | 37 | #define calloc_a(len, ...) __calloc_a(len, ##__VA_ARGS__, NULL) 38 | 39 | void *__calloc_a(size_t len, ...); 40 | 41 | #ifndef ARRAY_SIZE 42 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 43 | #endif 44 | 45 | #define __BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) 46 | 47 | #ifdef __OPTIMIZE__ 48 | extern int __BUILD_BUG_ON_CONDITION_FAILED; 49 | #define BUILD_BUG_ON(condition) \ 50 | do { \ 51 | __BUILD_BUG_ON(condition); \ 52 | if (condition) \ 53 | __BUILD_BUG_ON_CONDITION_FAILED = 1; \ 54 | } while(0) 55 | #else 56 | #define BUILD_BUG_ON __BUILD_BUG_ON 57 | #endif 58 | 59 | #ifdef __APPLE__ 60 | 61 | #define CLOCK_REALTIME 0 62 | #define CLOCK_MONOTONIC 1 63 | 64 | void clock_gettime(int type, struct timespec *tv); 65 | 66 | #endif 67 | 68 | #ifdef __GNUC__ 69 | #define _GNUC_MIN_VER(maj, min) (((__GNUC__ << 8) + __GNUC_MINOR__) >= (((maj) << 8) + (min))) 70 | #else 71 | #define _GNUC_MIN_VER(maj, min) 0 72 | #endif 73 | 74 | #if defined(__linux__) || defined(__CYGWIN__) 75 | #include 76 | #include 77 | 78 | #elif defined(__APPLE__) 79 | #include 80 | #include 81 | #define bswap_32(x) OSSwapInt32(x) 82 | #define bswap_64(x) OSSwapInt64(x) 83 | #elif defined(__FreeBSD__) 84 | #include 85 | #define bswap_32(x) bswap32(x) 86 | #define bswap_64(x) bswap64(x) 87 | #else 88 | #include 89 | #define bswap_32(x) swap32(x) 90 | #define bswap_64(x) swap64(x) 91 | #endif 92 | 93 | #ifndef __BYTE_ORDER 94 | #define __BYTE_ORDER BYTE_ORDER 95 | #endif 96 | #ifndef __BIG_ENDIAN 97 | #define __BIG_ENDIAN BIG_ENDIAN 98 | #endif 99 | #ifndef __LITTLE_ENDIAN 100 | #define __LITTLE_ENDIAN LITTLE_ENDIAN 101 | #endif 102 | 103 | static inline uint16_t __u_bswap16(uint16_t val) 104 | { 105 | return ((val >> 8) & 0xffu) | ((val & 0xffu) << 8); 106 | } 107 | 108 | #if _GNUC_MIN_VER(4, 2) 109 | #define __u_bswap32(x) __builtin_bswap32(x) 110 | #define __u_bswap64(x) __builtin_bswap64(x) 111 | #else 112 | #define __u_bswap32(x) bswap_32(x) 113 | #define __u_bswap64(x) bswap_64(x) 114 | #endif 115 | 116 | #if __BYTE_ORDER == __LITTLE_ENDIAN 117 | 118 | #define cpu_to_be64(x) __u_bswap64(x) 119 | #define cpu_to_be32(x) __u_bswap32(x) 120 | #define cpu_to_be16(x) __u_bswap16((uint16_t) (x)) 121 | 122 | #define be64_to_cpu(x) __u_bswap64(x) 123 | #define be32_to_cpu(x) __u_bswap32(x) 124 | #define be16_to_cpu(x) __u_bswap16((uint16_t) (x)) 125 | 126 | #define cpu_to_le64(x) (x) 127 | #define cpu_to_le32(x) (x) 128 | #define cpu_to_le16(x) (x) 129 | 130 | #define le64_to_cpu(x) (x) 131 | #define le32_to_cpu(x) (x) 132 | #define le16_to_cpu(x) (x) 133 | 134 | #else /* __BYTE_ORDER == __LITTLE_ENDIAN */ 135 | 136 | #define cpu_to_le64(x) __u_bswap64(x) 137 | #define cpu_to_le32(x) __u_bswap32(x) 138 | #define cpu_to_le16(x) __u_bswap16((uint16_t) (x)) 139 | 140 | #define le64_to_cpu(x) __u_bswap64(x) 141 | #define le32_to_cpu(x) __u_bswap32(x) 142 | #define le16_to_cpu(x) __u_bswap16((uint16_t) (x)) 143 | 144 | #define cpu_to_be64(x) (x) 145 | #define cpu_to_be32(x) (x) 146 | #define cpu_to_be16(x) (x) 147 | 148 | #define be64_to_cpu(x) (x) 149 | #define be32_to_cpu(x) (x) 150 | #define be16_to_cpu(x) (x) 151 | 152 | #endif 153 | 154 | #ifndef __packed 155 | #define __packed __attribute__((packed)) 156 | #endif 157 | 158 | #ifndef __constructor 159 | #define __constructor __attribute__((constructor)) 160 | #endif 161 | 162 | #ifndef __hidden 163 | #define __hidden __attribute__((visibility("hidden"))) 164 | #endif 165 | 166 | #ifndef BITS_PER_LONG 167 | #define BITS_PER_LONG (8 * sizeof(unsigned long)) 168 | #endif 169 | 170 | static inline void bitfield_set(unsigned long *bits, int bit) 171 | { 172 | bits[bit / BITS_PER_LONG] |= (1UL << (bit % BITS_PER_LONG)); 173 | } 174 | 175 | static inline bool bitfield_test(unsigned long *bits, int bit) 176 | { 177 | return !!(bits[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))); 178 | } 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /vlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include "vlist.h" 17 | 18 | void 19 | vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update) 20 | { 21 | tree->update = update; 22 | tree->version = 1; 23 | 24 | avl_init(&tree->avl, cmp, 0, tree); 25 | } 26 | 27 | void 28 | vlist_delete(struct vlist_tree *tree, struct vlist_node *node) 29 | { 30 | if (!tree->no_delete) 31 | avl_delete(&tree->avl, &node->avl); 32 | tree->update(tree, NULL, node); 33 | } 34 | 35 | void 36 | vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key) 37 | { 38 | struct vlist_node *old_node = NULL; 39 | struct avl_node *anode; 40 | 41 | node->avl.key = key; 42 | node->version = tree->version; 43 | 44 | anode = avl_find(&tree->avl, key); 45 | if (anode) { 46 | old_node = container_of(anode, struct vlist_node, avl); 47 | if (tree->keep_old || tree->no_delete) { 48 | old_node->version = tree->version; 49 | goto update_only; 50 | } 51 | 52 | avl_delete(&tree->avl, anode); 53 | } 54 | 55 | avl_insert(&tree->avl, &node->avl); 56 | 57 | update_only: 58 | tree->update(tree, node, old_node); 59 | } 60 | 61 | void 62 | vlist_flush(struct vlist_tree *tree) 63 | { 64 | struct vlist_node *node, *tmp; 65 | 66 | avl_for_each_element_safe(&tree->avl, node, avl, tmp) { 67 | if ((node->version == tree->version || node->version == -1) && 68 | tree->version != -1) 69 | continue; 70 | 71 | vlist_delete(tree, node); 72 | } 73 | } 74 | 75 | void 76 | vlist_flush_all(struct vlist_tree *tree) 77 | { 78 | tree->version = -1; 79 | vlist_flush(tree); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /vlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __LIBUBOX_VLIST_H 18 | #define __LIBUBOX_VLIST_H 19 | 20 | #include "avl.h" 21 | 22 | struct vlist_tree; 23 | struct vlist_node; 24 | 25 | typedef void (*vlist_update_cb)(struct vlist_tree *tree, 26 | struct vlist_node *node_new, 27 | struct vlist_node *node_old); 28 | 29 | struct vlist_tree { 30 | struct avl_tree avl; 31 | 32 | vlist_update_cb update; 33 | bool keep_old; 34 | bool no_delete; 35 | 36 | int version; 37 | }; 38 | 39 | struct vlist_node { 40 | struct avl_node avl; 41 | int version; 42 | }; 43 | 44 | void vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update); 45 | 46 | #define vlist_find(tree, name, element, node_member) \ 47 | avl_find_element(&(tree)->avl, name, element, node_member.avl) 48 | 49 | static inline void vlist_update(struct vlist_tree *tree) 50 | { 51 | tree->version++; 52 | } 53 | 54 | void vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key); 55 | void vlist_delete(struct vlist_tree *tree, struct vlist_node *node); 56 | void vlist_flush(struct vlist_tree *tree); 57 | void vlist_flush_all(struct vlist_tree *tree); 58 | 59 | #define vlist_for_each_element(tree, element, node_member) \ 60 | avl_for_each_element(&(tree)->avl, element, node_member.avl) 61 | 62 | #endif 63 | --------------------------------------------------------------------------------