├── .gitignore ├── LICENSE ├── Makefile.am ├── README.md ├── configure.ac ├── src ├── Makefile.am ├── cmd │ ├── add-ip.c │ ├── add-ip.h │ ├── add-node.c │ ├── add-node.h │ ├── ping.c │ ├── ping.h │ ├── remove-ip.c │ ├── remove-ip.h │ ├── remove-node.c │ ├── remove-node.h │ ├── show-ip.c │ ├── show-ip.h │ ├── show-node.c │ ├── show-node.h │ ├── status.c │ └── status.h ├── ip.c ├── ip.h ├── node.c ├── node.h ├── red.c ├── red.h ├── redd.c ├── redd.h └── util │ ├── adlist.c │ ├── adlist.h │ ├── sds.c │ └── sds.h └── test └── script.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | *.o 4 | *.a 5 | /config.* 6 | /src/vxadm 7 | /autom4te.cache 8 | /aclocal.m4 9 | /compile 10 | /depcomp 11 | /install-sh 12 | /missing 13 | .deps 14 | .dirstamp 15 | /tmp 16 | configure -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nanobox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # -*- mode: Makefile; tab-width: 8; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | # vim: ts=8 sw=8 ft=Makefile noet 3 | # 4 | # Copyright (c) 2015 Pagoda Box Inc 5 | # 6 | # This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | # 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | # at http://mozilla.org/MPL/2.0/. 9 | # 10 | 11 | SUBDIRS = src 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # red 2 | A fast, in-kernel, ad-hoc point-to-point vxlan network. This project interacts with [redd](https://github.com/nanopack/redd) to build virtual networks. The redd project is the daemon that interacts with the kernel's vxlan module. This project is the command-line utility used to interact with redd. 3 | 4 | ## How to use red: 5 | 6 | ### Usage 7 | Usage: red [OPTIONS] ... 8 | -h red hostname (default: 127.0.0.1) 9 | -p red port (default: 4470) 10 | --help Output this help and exit 11 | --version Output version and exit 12 | --yaml Format output in YAML 13 | 14 | add-ip 15 | remove-ip 16 | show-ip 17 | 18 | add-node 19 | remove-node 20 | show-node 21 | 22 | ping 23 | 24 | status 25 | 26 | ### Options 27 | #### -h 28 | Specify the host to connect to 29 | #### -p 30 | Specify the port to connect to 31 | #### --help 32 | Show the help for red 33 | #### --version 34 | Show the version number of red 35 | #### --yaml 36 | Format the output of subcommands to be yaml 37 | ### Subcommands 38 | #### add-ip 39 | Add ip to the vxlan interface. Must specify with the cidr (192.168.0.1/24). 40 | #### remove-ip 41 | Remove ip from the vxlan interface. Must specify with the cidr (192.168.0.1/24). 42 | #### show-ip 43 | Show a list of IPs attached to the vxlan interface. 44 | #### add-node 45 | Add host node to peer list. 46 | #### remove-node 47 | Remove host node from peer list. 48 | #### show-node 49 | Show a list of nodes in the peer list. 50 | #### ping 51 | Sends a simple request to the redd api to see if it is listening. 52 | #### status 53 | Shows information about the redd server, including ips and nodes. 54 | 55 | [![open source](http://nano-assets.gopagoda.io/open-src/nanobox-open-src.png)](http://nanobox.io/open-source) 56 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # rebuild: autoreconf -fvi 2 | # 3 | # Copyright (c) 2015 Pagoda Box Inc 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public License, v. 6 | # 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 7 | # at http://mozilla.org/MPL/2.0/. 8 | # 9 | 10 | AC_PREREQ(2.61) 11 | 12 | AC_INIT([red], [0.0.2]) 13 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 14 | AC_CONFIG_SRCDIR([src/red.c]) 15 | 16 | AC_PROG_CC 17 | AC_PROG_CC_STDC 18 | 19 | AC_CHECK_LIB(pthread, pthread_mutexattr_init, 20 | [], 21 | [AC_MSG_ERROR([pthread libraries not found.])] 22 | ) 23 | 24 | AC_CHECK_HEADERS(uv.h, 25 | [], 26 | [AC_MSG_ERROR([libuv header files not found.]); break] 27 | ) 28 | 29 | AC_CHECK_LIB(uv, uv_run, 30 | [], 31 | [AC_MSG_ERROR([libuv libraries not found.])] 32 | ) 33 | 34 | AC_CHECK_HEADERS(msgpack.h, 35 | [], 36 | [AC_MSG_ERROR([msgpack header files not found.]); break] 37 | ) 38 | 39 | AC_CHECK_LIB(msgpack, msgpack_object_print, 40 | [], 41 | [AC_MSG_ERROR([msgpack libraries not found.])] 42 | ) 43 | 44 | AC_CHECK_HEADERS(msgxchng.h, 45 | [], 46 | [AC_MSG_ERROR([msgxchng header files not found.]); break] 47 | ) 48 | 49 | AC_CHECK_LIB(msgxchng, pack_msgxchng_request, 50 | [], 51 | [AC_MSG_ERROR([msgxchng libraries not found.])] 52 | ) 53 | 54 | AC_CHECK_HEADERS(bframe.h, 55 | [], 56 | [AC_MSG_ERROR([bframe header files not found.]); break] 57 | ) 58 | 59 | AC_CHECK_LIB(bframe, new_bframe, 60 | [], 61 | [AC_MSG_ERROR([bframe libraries not found.])] 62 | ) 63 | 64 | AC_OUTPUT(Makefile src/Makefile) 65 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # -*- mode: Makefile; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | # vim: ts=8 sw=8 ft=Makefile noet 3 | # 4 | # Copyright (c) 2015 Pagoda Box Inc 5 | # 6 | # This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | # 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | # at http://mozilla.org/MPL/2.0/. 9 | # 10 | 11 | bin_PROGRAMS = red 12 | 13 | red_SOURCES = \ 14 | cmd/add-ip.c cmd/add-ip.h \ 15 | cmd/remove-ip.c cmd/remove-ip.h \ 16 | cmd/show-ip.c cmd/show-ip.h \ 17 | cmd/add-node.c cmd/add-node.h \ 18 | cmd/remove-node.c cmd/remove-node.h \ 19 | cmd/show-node.c cmd/show-node.h \ 20 | cmd/ping.c cmd/ping.h \ 21 | cmd/status.c cmd/status.h \ 22 | ip.c ip.h \ 23 | node.c node.h \ 24 | util/adlist.c util/adlist.h \ 25 | util/sds.c util/sds.h \ 26 | redd.c redd.h \ 27 | red.c red.h 28 | -------------------------------------------------------------------------------- /src/cmd/add-ip.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "redd.h" 18 | #include "red.h" 19 | #include "cmd/add-ip.h" 20 | #include "ip.h" 21 | 22 | static red_ip_t ip; 23 | 24 | static void 25 | usage(void) 26 | { 27 | fprintf(stderr, 28 | "Usage: add-ip \n"); 29 | 30 | exit(1); 31 | } 32 | 33 | static void 34 | parse_options(int argc, char **argv) 35 | { 36 | int i; 37 | ip.ip_address = NULL; 38 | 39 | for (i = 0; i < argc; i++) { 40 | int lastarg = i==argc-1; 41 | 42 | if (!strcmp(argv[i], "-h") && lastarg) { 43 | usage(); 44 | } else if (!strcmp(argv[i], "--help")) { 45 | usage(); 46 | } else { 47 | if (argv[i][0] == '-') { 48 | fprintf(stderr, 49 | "Unrecognized option: '%s'\n", 50 | argv[i]); 51 | usage(); 52 | } else { 53 | ip.ip_address = sdsnew(argv[i]); 54 | } 55 | } 56 | } 57 | } 58 | 59 | static char * 60 | pack_data(int *size) 61 | { 62 | msgpack_sbuffer *buffer = NULL; 63 | msgpack_packer *packer = NULL; 64 | char *data; 65 | 66 | buffer = msgpack_sbuffer_new(); 67 | msgpack_sbuffer_init(buffer); 68 | packer = msgpack_packer_new((void *)buffer, msgpack_sbuffer_write); 69 | 70 | pack_ip(packer, &ip); 71 | 72 | data = (char *)malloc(buffer->size + 1); 73 | memcpy(data, &buffer->data[0], buffer->size); 74 | data[buffer->size] = '\0'; 75 | *size = buffer->size; 76 | 77 | msgpack_packer_free(packer); 78 | msgpack_sbuffer_free(buffer); 79 | return data; 80 | } 81 | 82 | static void 83 | on_response(msgxchng_response_t *res, int status) 84 | { 85 | if (status == RED_ERR) 86 | exit(1); 87 | 88 | msgpack_zone mempool; 89 | msgpack_zone_init(&mempool, 4096); 90 | 91 | msgpack_object deserialized; 92 | msgpack_unpack(res->data, res->data_len, NULL, &mempool, &deserialized); 93 | 94 | int success = 0; 95 | 96 | if (deserialized.type == MSGPACK_OBJECT_MAP) { 97 | msgpack_object_kv* p = deserialized.via.map.ptr; 98 | msgpack_object_kv* const pend = deserialized.via.map.ptr + deserialized.via.map.size; 99 | 100 | for (; p < pend; ++p) { 101 | if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_RAW) { 102 | if (!strncmp(p->key.via.raw.ptr, "return", p->key.via.raw.size)) { 103 | if (!strncmp(p->val.via.raw.ptr, "success", p->val.via.raw.size)) 104 | success = 1; 105 | else 106 | success = 0; 107 | } else if (!strncmp(p->key.via.raw.ptr, "error", p->key.via.raw.size)) { 108 | if (!config.no_output) fprintf(stderr, "red: %s\n", p->val.via.raw.ptr); 109 | } 110 | } 111 | } 112 | } 113 | 114 | clean_msgxchng_response(res); 115 | free(res); 116 | 117 | if (!success) 118 | exit(1); 119 | } 120 | 121 | void 122 | handle_add_ip(int argc, char **argv) 123 | { 124 | char *data; 125 | int size; 126 | init_ip(&ip); 127 | parse_options(argc, argv); 128 | if (ip.ip_address == NULL) 129 | usage(); 130 | 131 | msgxchng_request_t *req; 132 | data = pack_data(&size); 133 | req = new_msgxchng_request("1", 1, "ip.add", 6, data, size); 134 | 135 | free(data); 136 | redd_request(req, on_response); 137 | } -------------------------------------------------------------------------------- /src/cmd/add-ip.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_ADD_IP_H 12 | #define RED_ADD_IP_H 13 | 14 | void handle_add_ip(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/cmd/add-node.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "redd.h" 18 | #include "red.h" 19 | #include "cmd/add-node.h" 20 | #include "node.h" 21 | 22 | static red_node_t node; 23 | 24 | static void 25 | usage(void) 26 | { 27 | fprintf(stderr, 28 | "Usage: add-node \n"); 29 | 30 | exit(1); 31 | } 32 | 33 | static void 34 | parse_options(int argc, char **argv) 35 | { 36 | int i; 37 | node.hostname = NULL; 38 | 39 | for (i = 0; i < argc; i++) { 40 | int lastarg = i==argc-1; 41 | 42 | if (!strcmp(argv[i], "-h") && lastarg) { 43 | usage(); 44 | } else if (!strcmp(argv[i], "--help")) { 45 | usage(); 46 | } else { 47 | if (argv[i][0] == '-') { 48 | fprintf(stderr, 49 | "Unrecognized option: '%s'\n", 50 | argv[i]); 51 | usage(); 52 | } else { 53 | node.hostname = sdsnew(argv[i]); 54 | } 55 | } 56 | } 57 | } 58 | 59 | static char * 60 | pack_data(int *size) 61 | { 62 | msgpack_sbuffer *buffer = NULL; 63 | msgpack_packer *packer = NULL; 64 | char *data; 65 | 66 | buffer = msgpack_sbuffer_new(); 67 | msgpack_sbuffer_init(buffer); 68 | packer = msgpack_packer_new((void *)buffer, msgpack_sbuffer_write); 69 | 70 | pack_node(packer, &node); 71 | 72 | data = (char *)malloc(buffer->size + 1); 73 | memcpy(data, &buffer->data[0], buffer->size); 74 | data[buffer->size] = '\0'; 75 | *size = buffer->size; 76 | 77 | msgpack_packer_free(packer); 78 | msgpack_sbuffer_free(buffer); 79 | return data; 80 | } 81 | 82 | static void 83 | on_response(msgxchng_response_t *res, int status) 84 | { 85 | if (status == RED_ERR) 86 | exit(1); 87 | 88 | msgpack_zone mempool; 89 | msgpack_zone_init(&mempool, 4096); 90 | 91 | msgpack_object deserialized; 92 | msgpack_unpack(res->data, res->data_len, NULL, &mempool, &deserialized); 93 | 94 | int success = 0; 95 | 96 | if (deserialized.type == MSGPACK_OBJECT_MAP) { 97 | msgpack_object_kv* p = deserialized.via.map.ptr; 98 | msgpack_object_kv* const pend = deserialized.via.map.ptr + deserialized.via.map.size; 99 | 100 | for (; p < pend; ++p) { 101 | if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_RAW) { 102 | if (!strncmp(p->key.via.raw.ptr, "return", p->key.via.raw.size)) { 103 | if (!strncmp(p->val.via.raw.ptr, "success", p->val.via.raw.size)) 104 | success = 1; 105 | else 106 | success = 0; 107 | } else if (!strncmp(p->key.via.raw.ptr, "error", p->key.via.raw.size)) { 108 | if (!config.no_output) fprintf(stderr, "red: %s\n", p->val.via.raw.ptr); 109 | } 110 | } 111 | } 112 | } 113 | 114 | clean_msgxchng_response(res); 115 | free(res); 116 | 117 | if (!success) 118 | exit(1); 119 | } 120 | 121 | void 122 | handle_add_node(int argc, char **argv) 123 | { 124 | char *data; 125 | int size; 126 | init_node(&node); 127 | parse_options(argc, argv); 128 | if (node.hostname == NULL) 129 | usage(); 130 | 131 | msgxchng_request_t *req; 132 | data = pack_data(&size); 133 | req = new_msgxchng_request("1", 1, "node.add", 8, data, size); 134 | 135 | free(data); 136 | redd_request(req, on_response); 137 | } -------------------------------------------------------------------------------- /src/cmd/add-node.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_ADD_NODE_H 12 | #define RED_ADD_NODE_H 13 | 14 | void handle_add_node(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/cmd/ping.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "redd.h" 18 | #include "red.h" 19 | #include "cmd/ping.h" 20 | 21 | static void 22 | usage(void) 23 | { 24 | fprintf(stderr, 25 | "Usage: ping\n"); 26 | 27 | exit(1); 28 | } 29 | 30 | static void 31 | parse_options(int argc, char **argv) 32 | { 33 | int i; 34 | 35 | for (i = 0; i < argc; i++) { 36 | int lastarg = i==argc-1; 37 | 38 | if (!strcmp(argv[i], "-h") && lastarg) { 39 | usage(); 40 | } else if (!strcmp(argv[i], "--help")) { 41 | usage(); 42 | } else { 43 | fprintf(stderr, 44 | "Unrecognized option: '%s'\n", 45 | argv[i]); 46 | usage(); 47 | } 48 | } 49 | } 50 | 51 | static void 52 | on_response(msgxchng_response_t *res, int status) 53 | { 54 | if (status == RED_ERR) 55 | exit(1); 56 | 57 | if (!config.no_output) printf("pong!\n"); 58 | 59 | clean_msgxchng_response(res); 60 | free(res); 61 | } 62 | 63 | void 64 | handle_ping(int argc, char **argv) 65 | { 66 | parse_options(argc, argv); 67 | 68 | msgxchng_request_t *req; 69 | req = new_msgxchng_request("1", 1, "ping", 4, "", 0); 70 | 71 | redd_request(req, on_response); 72 | } 73 | -------------------------------------------------------------------------------- /src/cmd/ping.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_PING_H 12 | #define RED_PING_H 13 | 14 | void handle_ping(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/cmd/remove-ip.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "redd.h" 18 | #include "red.h" 19 | #include "cmd/remove-ip.h" 20 | #include "ip.h" 21 | 22 | static red_ip_t ip; 23 | 24 | static void 25 | usage(void) 26 | { 27 | fprintf(stderr, 28 | "Usage: remove-ip \n"); 29 | 30 | exit(1); 31 | } 32 | 33 | static void 34 | parse_options(int argc, char **argv) 35 | { 36 | int i; 37 | ip.ip_address = NULL; 38 | 39 | for (i = 0; i < argc; i++) { 40 | int lastarg = i==argc-1; 41 | 42 | if (!strcmp(argv[i], "-h") && lastarg) { 43 | usage(); 44 | } else if (!strcmp(argv[i], "--help")) { 45 | usage(); 46 | } else { 47 | if (argv[i][0] == '-') { 48 | fprintf(stderr, 49 | "Unrecognized option: '%s'\n", 50 | argv[i]); 51 | usage(); 52 | } else { 53 | ip.ip_address = sdsnew(argv[i]); 54 | } 55 | } 56 | } 57 | } 58 | 59 | static char * 60 | pack_data(int *size) 61 | { 62 | msgpack_sbuffer *buffer = NULL; 63 | msgpack_packer *packer = NULL; 64 | char *data; 65 | 66 | buffer = msgpack_sbuffer_new(); 67 | msgpack_sbuffer_init(buffer); 68 | packer = msgpack_packer_new((void *)buffer, msgpack_sbuffer_write); 69 | 70 | pack_ip(packer, &ip); 71 | 72 | data = (char *)malloc(buffer->size + 1); 73 | memcpy(data, &buffer->data[0], buffer->size); 74 | data[buffer->size] = '\0'; 75 | *size = buffer->size; 76 | 77 | msgpack_packer_free(packer); 78 | msgpack_sbuffer_free(buffer); 79 | return data; 80 | } 81 | 82 | static void 83 | on_response(msgxchng_response_t *res, int status) 84 | { 85 | if (status == RED_ERR) 86 | exit(1); 87 | 88 | msgpack_zone mempool; 89 | msgpack_zone_init(&mempool, 4096); 90 | 91 | msgpack_object deserialized; 92 | msgpack_unpack(res->data, res->data_len, NULL, &mempool, &deserialized); 93 | 94 | int success = 0; 95 | 96 | if (deserialized.type == MSGPACK_OBJECT_MAP) { 97 | msgpack_object_kv* p = deserialized.via.map.ptr; 98 | msgpack_object_kv* const pend = deserialized.via.map.ptr + deserialized.via.map.size; 99 | 100 | for (; p < pend; ++p) { 101 | if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_RAW) { 102 | if (!strncmp(p->key.via.raw.ptr, "return", p->key.via.raw.size)) { 103 | if (!strncmp(p->val.via.raw.ptr, "success", p->val.via.raw.size)) 104 | success = 1; 105 | else 106 | success = 0; 107 | } else if (!strncmp(p->key.via.raw.ptr, "error", p->key.via.raw.size)) { 108 | if (!config.no_output) fprintf(stderr, "red: %s\n", p->val.via.raw.ptr); 109 | } 110 | } 111 | } 112 | } 113 | 114 | clean_msgxchng_response(res); 115 | free(res); 116 | 117 | if (!success) 118 | exit(1); 119 | } 120 | 121 | void 122 | handle_remove_ip(int argc, char **argv) 123 | { 124 | char *data; 125 | int size; 126 | init_ip(&ip); 127 | parse_options(argc, argv); 128 | if (ip.ip_address == NULL) 129 | usage(); 130 | 131 | msgxchng_request_t *req; 132 | data = pack_data(&size); 133 | req = new_msgxchng_request("1", 1, "ip.remove", 9, data, size); 134 | 135 | free(data); 136 | redd_request(req, on_response); 137 | } -------------------------------------------------------------------------------- /src/cmd/remove-ip.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_REMOVE_IP_H 12 | #define RED_REMOVE_IP_H 13 | 14 | void handle_remove_ip(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/cmd/remove-node.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "redd.h" 18 | #include "red.h" 19 | #include "cmd/remove-node.h" 20 | #include "node.h" 21 | 22 | static red_node_t node; 23 | 24 | static void 25 | usage(void) 26 | { 27 | fprintf(stderr, 28 | "Usage: remove-node \n"); 29 | 30 | exit(1); 31 | } 32 | 33 | static void 34 | parse_options(int argc, char **argv) 35 | { 36 | int i; 37 | node.hostname = NULL; 38 | 39 | for (i = 0; i < argc; i++) { 40 | int lastarg = i==argc-1; 41 | 42 | if (!strcmp(argv[i], "-h") && lastarg) { 43 | usage(); 44 | } else if (!strcmp(argv[i], "--help")) { 45 | usage(); 46 | } else { 47 | if (argv[i][0] == '-') { 48 | fprintf(stderr, 49 | "Unrecognized option: '%s'\n", 50 | argv[i]); 51 | usage(); 52 | } else { 53 | node.hostname = sdsnew(argv[i]); 54 | } 55 | } 56 | } 57 | } 58 | 59 | static char * 60 | pack_data(int *size) 61 | { 62 | msgpack_sbuffer *buffer = NULL; 63 | msgpack_packer *packer = NULL; 64 | char *data; 65 | 66 | buffer = msgpack_sbuffer_new(); 67 | msgpack_sbuffer_init(buffer); 68 | packer = msgpack_packer_new((void *)buffer, msgpack_sbuffer_write); 69 | 70 | pack_node(packer, &node); 71 | 72 | data = (char *)malloc(buffer->size + 1); 73 | memcpy(data, &buffer->data[0], buffer->size); 74 | data[buffer->size] = '\0'; 75 | *size = buffer->size; 76 | 77 | msgpack_packer_free(packer); 78 | msgpack_sbuffer_free(buffer); 79 | return data; 80 | } 81 | 82 | static void 83 | on_response(msgxchng_response_t *res, int status) 84 | { 85 | if (status == RED_ERR) 86 | exit(1); 87 | 88 | msgpack_zone mempool; 89 | msgpack_zone_init(&mempool, 4096); 90 | 91 | msgpack_object deserialized; 92 | msgpack_unpack(res->data, res->data_len, NULL, &mempool, &deserialized); 93 | 94 | int success = 0; 95 | 96 | if (deserialized.type == MSGPACK_OBJECT_MAP) { 97 | msgpack_object_kv* p = deserialized.via.map.ptr; 98 | msgpack_object_kv* const pend = deserialized.via.map.ptr + deserialized.via.map.size; 99 | 100 | for (; p < pend; ++p) { 101 | if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_RAW) { 102 | if (!strncmp(p->key.via.raw.ptr, "return", p->key.via.raw.size)) { 103 | if (!strncmp(p->val.via.raw.ptr, "success", p->val.via.raw.size)) 104 | success = 1; 105 | else 106 | success = 0; 107 | } else if (!strncmp(p->key.via.raw.ptr, "error", p->key.via.raw.size)) { 108 | if (!config.no_output) fprintf(stderr, "red: %s\n", p->val.via.raw.ptr); 109 | } 110 | } 111 | } 112 | } 113 | 114 | clean_msgxchng_response(res); 115 | free(res); 116 | 117 | if (!success) 118 | exit(1); 119 | } 120 | 121 | void 122 | handle_remove_node(int argc, char **argv) 123 | { 124 | char *data; 125 | int size; 126 | init_node(&node); 127 | parse_options(argc, argv); 128 | if (node.hostname == NULL) 129 | usage(); 130 | 131 | msgxchng_request_t *req; 132 | data = pack_data(&size); 133 | req = new_msgxchng_request("1", 1, "node.remove", 11, data, size); 134 | 135 | free(data); 136 | redd_request(req, on_response); 137 | } -------------------------------------------------------------------------------- /src/cmd/remove-node.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_REMOVE_NODE_H 12 | #define RED_REMOVE_NODE_H 13 | 14 | void handle_remove_node(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/cmd/show-ip.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "redd.h" 18 | #include "red.h" 19 | #include "cmd/show-ip.h" 20 | #include "ip.h" 21 | 22 | static list *ips; 23 | 24 | static void 25 | usage(void) 26 | { 27 | fprintf(stderr, 28 | "Usage: show-ip\n"); 29 | 30 | exit(1); 31 | } 32 | 33 | static void 34 | parse_options(int argc, char **argv) 35 | { 36 | int i; 37 | 38 | for (i = 0; i < argc; i++) { 39 | int lastarg = i==argc-1; 40 | 41 | if (!strcmp(argv[i], "-h") && lastarg) { 42 | usage(); 43 | } else if (!strcmp(argv[i], "--help")) { 44 | usage(); 45 | } else { 46 | fprintf(stderr, 47 | "Unrecognized option: '%s'\n", 48 | argv[i]); 49 | usage(); 50 | } 51 | } 52 | } 53 | 54 | static void 55 | unpack_data(char *data, int len) 56 | { 57 | msgpack_zone mempool; 58 | msgpack_zone_init(&mempool, 4096); 59 | 60 | msgpack_object deserialized; 61 | msgpack_unpack(data, len, NULL, &mempool, &deserialized); 62 | 63 | if (deserialized.type == MSGPACK_OBJECT_MAP) { 64 | msgpack_object_kv* p = deserialized.via.map.ptr; 65 | msgpack_object_kv* const pend = deserialized.via.map.ptr + deserialized.via.map.size; 66 | 67 | for (; p < pend; ++p) { 68 | if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_ARRAY) { 69 | if (!strncmp(p->key.via.raw.ptr, "ip_addresses", p->key.via.raw.size)) { 70 | ips = unpack_ips(p->val); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | static void 78 | print_data() 79 | { 80 | red_ip_t *ip; 81 | listNode *list_node; 82 | listIter *itr = listGetIterator(ips, AL_START_HEAD); 83 | printf("IP ADDRESSES:\n"); 84 | while ((list_node = listNext(itr)) != NULL) { 85 | ip = (red_ip_t *)list_node->value; 86 | printf("%s\n", ip->ip_address); 87 | } 88 | listReleaseIterator(itr); 89 | listRelease(ips); 90 | } 91 | 92 | static void 93 | print_yaml_data() 94 | { 95 | red_ip_t *ip; 96 | listNode *list_node; 97 | listIter *itr = listGetIterator(ips, AL_START_HEAD); 98 | printf("ips:\n"); 99 | while ((list_node = listNext(itr)) != NULL) { 100 | ip = (red_ip_t *)list_node->value; 101 | printf(" - %s\n", ip->ip_address); 102 | } 103 | listReleaseIterator(itr); 104 | listRelease(ips); 105 | } 106 | 107 | static void 108 | on_response(msgxchng_response_t *res, int status) 109 | { 110 | if (status == RED_ERR) 111 | exit(1); 112 | 113 | unpack_data(res->data, res->data_len); 114 | if (config.yaml_out) { 115 | print_yaml_data(); 116 | } else { 117 | print_data(); 118 | } 119 | 120 | clean_msgxchng_response(res); 121 | free(res); 122 | } 123 | 124 | void 125 | handle_show_ip(int argc, char **argv) 126 | { 127 | parse_options(argc, argv); 128 | 129 | msgxchng_request_t *req; 130 | req = new_msgxchng_request("1", 1, "ip.list", 7, "", 0); 131 | 132 | redd_request(req, on_response); 133 | } -------------------------------------------------------------------------------- /src/cmd/show-ip.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_SHOW_IP_H 12 | #define RED_SHOW_IP_H 13 | 14 | void handle_show_ip(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/cmd/show-node.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "redd.h" 18 | #include "red.h" 19 | #include "cmd/show-node.h" 20 | #include "node.h" 21 | 22 | static list *nodes; 23 | 24 | static void 25 | usage(void) 26 | { 27 | fprintf(stderr, 28 | "Usage: show-node\n"); 29 | 30 | exit(1); 31 | } 32 | 33 | static void 34 | parse_options(int argc, char **argv) 35 | { 36 | int i; 37 | 38 | for (i = 0; i < argc; i++) { 39 | int lastarg = i==argc-1; 40 | 41 | if (!strcmp(argv[i], "-h") && lastarg) { 42 | usage(); 43 | } else if (!strcmp(argv[i], "--help")) { 44 | usage(); 45 | } else { 46 | fprintf(stderr, 47 | "Unrecognized option: '%s'\n", 48 | argv[i]); 49 | usage(); 50 | } 51 | } 52 | } 53 | 54 | static void 55 | unpack_data(char *data, int len) 56 | { 57 | msgpack_zone mempool; 58 | msgpack_zone_init(&mempool, 4096); 59 | 60 | msgpack_object deserialized; 61 | msgpack_unpack(data, len, NULL, &mempool, &deserialized); 62 | 63 | if (deserialized.type == MSGPACK_OBJECT_MAP) { 64 | msgpack_object_kv* p = deserialized.via.map.ptr; 65 | msgpack_object_kv* const pend = deserialized.via.map.ptr + deserialized.via.map.size; 66 | 67 | for (; p < pend; ++p) { 68 | if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_ARRAY) { 69 | if (!strncmp(p->key.via.raw.ptr, "nodes", p->key.via.raw.size)) { 70 | nodes = unpack_nodes(p->val); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | static void 78 | print_data() 79 | { 80 | red_node_t *node; 81 | listNode *list_node; 82 | listIter *itr = listGetIterator(nodes, AL_START_HEAD); 83 | printf("NODES:\n"); 84 | while ((list_node = listNext(itr)) != NULL) { 85 | node = (red_node_t *)list_node->value; 86 | printf("%s\n", node->hostname); 87 | } 88 | listReleaseIterator(itr); 89 | listRelease(nodes); 90 | } 91 | 92 | static void 93 | print_yaml_data() 94 | { 95 | red_node_t *node; 96 | listNode *list_node; 97 | listIter *itr = listGetIterator(nodes, AL_START_HEAD); 98 | printf("nodes:\n"); 99 | while ((list_node = listNext(itr)) != NULL) { 100 | node = (red_node_t *)list_node->value; 101 | printf(" - %s\n", node->hostname); 102 | } 103 | listReleaseIterator(itr); 104 | listRelease(nodes); 105 | } 106 | 107 | static void 108 | on_response(msgxchng_response_t *res, int status) 109 | { 110 | if (status == RED_ERR) 111 | exit(1); 112 | 113 | unpack_data(res->data, res->data_len); 114 | if (config.yaml_out) { 115 | print_yaml_data(); 116 | } else { 117 | print_data(); 118 | } 119 | 120 | clean_msgxchng_response(res); 121 | free(res); 122 | } 123 | 124 | void 125 | handle_show_node(int argc, char **argv) 126 | { 127 | parse_options(argc, argv); 128 | 129 | msgxchng_request_t *req; 130 | req = new_msgxchng_request("1", 1, "node.list", 9, "", 0); 131 | 132 | redd_request(req, on_response); 133 | } -------------------------------------------------------------------------------- /src/cmd/show-node.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_SHOW_NODE_H 12 | #define RED_SHOW_NODE_H 13 | 14 | void handle_show_node(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/cmd/status.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "ip.h" 17 | #include "node.h" 18 | #include "util/sds.h" 19 | #include "redd.h" 20 | #include "red.h" 21 | #include "cmd/status.h" 22 | #include "util/adlist.h" 23 | 24 | typedef struct red_status_s { 25 | list *ips; 26 | list *nodes; 27 | char *tun_dev; 28 | char *vxlan_dev; 29 | char *vxlan_vni; 30 | char *vxlan_group; 31 | char *vxlan_port; 32 | char *vxlan_interface; 33 | } red_status_t; 34 | 35 | static red_status_t red_status; 36 | 37 | static void 38 | usage(void) 39 | { 40 | fprintf(stderr, 41 | "Usage: status\n"); 42 | 43 | exit(1); 44 | } 45 | 46 | static void 47 | parse_options(int argc, char **argv) 48 | { 49 | int i; 50 | 51 | for (i = 0; i < argc; i++) { 52 | int lastarg = i==argc-1; 53 | 54 | if (!strcmp(argv[i], "-h") && lastarg) { 55 | usage(); 56 | } else if (!strcmp(argv[i], "--help")) { 57 | usage(); 58 | } else { 59 | fprintf(stderr, 60 | "Unrecognized option: '%s'\n", 61 | argv[i]); 62 | usage(); 63 | } 64 | } 65 | } 66 | 67 | static void 68 | initialize_data() 69 | { 70 | red_status.ips = listCreate(); 71 | red_status.nodes = listCreate(); 72 | red_status.tun_dev = strdup(""); 73 | red_status.vxlan_dev = strdup(""); 74 | red_status.vxlan_vni = strdup(""); 75 | red_status.vxlan_group = strdup(""); 76 | red_status.vxlan_port = strdup(""); 77 | red_status.vxlan_interface = strdup(""); 78 | } 79 | 80 | static void 81 | unpack_data(char *data, int len) 82 | { 83 | msgpack_zone mempool; 84 | msgpack_zone_init(&mempool, 4096); 85 | 86 | msgpack_object deserialized; 87 | msgpack_unpack(data, len, NULL, &mempool, &deserialized); 88 | 89 | if (deserialized.type == MSGPACK_OBJECT_MAP) { 90 | msgpack_object_kv* p = deserialized.via.map.ptr; 91 | msgpack_object_kv* const pend = deserialized.via.map.ptr + deserialized.via.map.size; 92 | 93 | for (; p < pend; ++p) { 94 | if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_ARRAY) { 95 | if (!strncmp(p->key.via.raw.ptr, "ip_addresses", p->key.via.raw.size)) { 96 | listRelease(red_status.ips); 97 | red_status.ips = unpack_ips(p->val); 98 | } else if (!strncmp(p->key.via.raw.ptr, "nodes", p->key.via.raw.size)) { 99 | listRelease(red_status.nodes); 100 | red_status.nodes = unpack_nodes(p->val); 101 | } 102 | } else if (p->key.type == MSGPACK_OBJECT_RAW && p->val.type == MSGPACK_OBJECT_RAW) { 103 | if (!strncmp(p->key.via.raw.ptr, "tun_dev", p->key.via.raw.size)) { 104 | free(red_status.tun_dev); 105 | red_status.tun_dev = strndup(p->val.via.raw.ptr, p->val.via.raw.size); 106 | } else if (!strncmp(p->key.via.raw.ptr, "vxlan_dev", p->key.via.raw.size)) { 107 | free(red_status.vxlan_dev); 108 | red_status.vxlan_dev = strndup(p->val.via.raw.ptr, p->val.via.raw.size); 109 | } else if (!strncmp(p->key.via.raw.ptr, "vxlan_vni", p->key.via.raw.size)) { 110 | free(red_status.vxlan_vni); 111 | red_status.vxlan_vni = strndup(p->val.via.raw.ptr, p->val.via.raw.size); 112 | } else if (!strncmp(p->key.via.raw.ptr, "vxlan_group", p->key.via.raw.size)) { 113 | free(red_status.vxlan_group); 114 | red_status.vxlan_group = strndup(p->val.via.raw.ptr, p->val.via.raw.size); 115 | } else if (!strncmp(p->key.via.raw.ptr, "vxlan_port", p->key.via.raw.size)) { 116 | free(red_status.vxlan_port); 117 | red_status.vxlan_port = strndup(p->val.via.raw.ptr, p->val.via.raw.size); 118 | } else if (!strncmp(p->key.via.raw.ptr, "vxlan_interface", p->key.via.raw.size)) { 119 | free(red_status.vxlan_interface); 120 | red_status.vxlan_interface = strndup(p->val.via.raw.ptr, p->val.via.raw.size); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | static void print_status() 128 | { 129 | listIter *iterator = listGetIterator(red_status.ips, AL_START_HEAD); 130 | listNode *list_node = NULL; 131 | printf("Tunnel Device:\t%s\n", red_status.tun_dev); 132 | printf("VxLAN Device:\t%s\n", red_status.vxlan_dev); 133 | printf("VxLAN VNI:\t%s\n", red_status.vxlan_vni); 134 | printf("Multicast Group:\t%s\n", red_status.vxlan_group); 135 | printf("VxLan Port:\t%s\n", red_status.vxlan_port); 136 | printf("Real Interface:\t%s\n", red_status.vxlan_interface); 137 | iterator = listGetIterator(red_status.ips, AL_START_HEAD); 138 | printf("IP ADDRESSES:\n"); 139 | while ((list_node = listNext(iterator)) != NULL) { 140 | red_ip_t *ip = (red_ip_t *)list_node->value; 141 | printf("\t%s\n", ip->ip_address); 142 | } 143 | listReleaseIterator(iterator); 144 | iterator = listGetIterator(red_status.nodes, AL_START_HEAD); 145 | printf("NODES:\n"); 146 | while ((list_node = listNext(iterator)) != NULL) { 147 | red_node_t *node = (red_node_t *)list_node->value; 148 | printf("\t%s\n", node->hostname); 149 | } 150 | listReleaseIterator(iterator); 151 | listRelease(red_status.ips); 152 | listRelease(red_status.nodes); 153 | free(red_status.tun_dev); 154 | free(red_status.vxlan_dev); 155 | free(red_status.vxlan_vni); 156 | free(red_status.vxlan_group); 157 | free(red_status.vxlan_port); 158 | free(red_status.vxlan_interface); 159 | } 160 | 161 | static void print_yaml_status() 162 | { 163 | listNode *list_node = NULL; 164 | listIter *iterator = NULL; 165 | printf("tunnel_device: %s\n", red_status.tun_dev); 166 | printf("vxlan_device: %s\n", red_status.vxlan_dev); 167 | printf("vxlan_vni: %s\n", red_status.vxlan_vni); 168 | printf("multicast_group: %s\n", red_status.vxlan_group); 169 | printf("vxlan_port: %s\n", red_status.vxlan_port); 170 | printf("real_interface: %s\n", red_status.vxlan_interface); 171 | if (listLength(red_status.ips) == 0) { 172 | printf("ips: []\n"); 173 | } else { 174 | iterator = listGetIterator(red_status.ips, AL_START_HEAD); 175 | printf("ips:\n"); 176 | while ((list_node = listNext(iterator)) != NULL) { 177 | red_ip_t *ip = (red_ip_t *)list_node->value; 178 | printf(" - %s\n", ip->ip_address); 179 | } 180 | listReleaseIterator(iterator); 181 | } 182 | if (listLength(red_status.nodes) == 0) { 183 | printf("nodes: []\n"); 184 | } else { 185 | iterator = listGetIterator(red_status.nodes, AL_START_HEAD); 186 | printf("nodes:\n"); 187 | while ((list_node = listNext(iterator)) != NULL) { 188 | red_node_t *node = (red_node_t *)list_node->value; 189 | printf(" - %s\n", node->hostname); 190 | } 191 | } 192 | listReleaseIterator(iterator); 193 | listRelease(red_status.ips); 194 | listRelease(red_status.nodes); 195 | free(red_status.tun_dev); 196 | free(red_status.vxlan_dev); 197 | free(red_status.vxlan_vni); 198 | free(red_status.vxlan_group); 199 | free(red_status.vxlan_port); 200 | free(red_status.vxlan_interface); 201 | } 202 | 203 | static void 204 | on_response(msgxchng_response_t *res, int status) 205 | { 206 | if (status == RED_ERR) 207 | exit(1); 208 | 209 | initialize_data(); 210 | unpack_data(res->data, res->data_len); 211 | if (config.yaml_out) { 212 | print_yaml_status(); 213 | } else { 214 | print_status(); 215 | } 216 | 217 | clean_msgxchng_response(res); 218 | free(res); 219 | } 220 | 221 | void 222 | handle_status(int argc, char **argv) 223 | { 224 | parse_options(argc, argv); 225 | 226 | msgxchng_request_t *req; 227 | req = new_msgxchng_request("1", 1, "status", 6, "", 0); 228 | 229 | redd_request(req, on_response); 230 | } 231 | -------------------------------------------------------------------------------- /src/cmd/status.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_STATUS_H 12 | #define RED_STATUS_H 13 | 14 | void handle_status(int argc, char **argv); 15 | 16 | #endif -------------------------------------------------------------------------------- /src/ip.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | 12 | #include /* standard buffered input/output */ 13 | #include /* standard library definitions */ 14 | #include /* string operations */ 15 | #include 16 | 17 | #include "ip.h" 18 | 19 | red_ip_t 20 | *new_ip() 21 | { 22 | red_ip_t *ip = malloc(sizeof(red_ip_t)); 23 | init_ip(ip); 24 | return ip; 25 | } 26 | 27 | void 28 | init_ip(red_ip_t *ip) 29 | { 30 | ip->ip_address = NULL; 31 | } 32 | 33 | void 34 | free_ip(red_ip_t *ip) 35 | { 36 | sdsfree(ip->ip_address); 37 | } 38 | 39 | static void 40 | pack_key_value(msgpack_packer *packer, char *key, 41 | int key_len, char *value, int value_len) 42 | { 43 | msgpack_pack_raw(packer, key_len); 44 | msgpack_pack_raw_body(packer, key, key_len); 45 | msgpack_pack_raw(packer, value_len); 46 | msgpack_pack_raw_body(packer, value, value_len); 47 | } 48 | 49 | void 50 | pack_ip(msgpack_packer *packer, red_ip_t *ip) 51 | { 52 | msgpack_pack_map(packer, 1); 53 | pack_key_value(packer, "ip_address", 10, (char *)ip->ip_address, (int)sdslen(ip->ip_address)); 54 | } 55 | 56 | static void 57 | *list_dup_ip(void *ptr) 58 | { 59 | red_ip_t *ip = (red_ip_t *)ptr; 60 | red_ip_t *dup_ip = malloc(sizeof(red_ip_t)); 61 | dup_ip->ip_address = sdsdup(ip->ip_address); 62 | return (void *)dup_ip; 63 | } 64 | 65 | static void 66 | list_free_ip(void *ptr) 67 | { 68 | free_ip((red_ip_t *)ptr); 69 | } 70 | 71 | static int 72 | list_match_ip(void *ptr, void *key) 73 | { 74 | red_ip_t *ip = (red_ip_t *)ptr; 75 | if (sdscmp(ip->ip_address,(sds)key) == 0) 76 | return 1; 77 | else 78 | return 0; 79 | } 80 | 81 | red_ip_t 82 | *unpack_ip(msgpack_object object) 83 | { 84 | if (object.type != MSGPACK_OBJECT_MAP) 85 | return NULL; 86 | 87 | red_ip_t *ip = malloc(sizeof(red_ip_t)); 88 | init_ip(ip); 89 | 90 | msgpack_object_kv* p = object.via.map.ptr; 91 | msgpack_object_kv* pend = object.via.map.ptr + object.via.map.size; 92 | 93 | for (; p < pend; ++p) { 94 | if (p->key.type != MSGPACK_OBJECT_RAW || p->val.type != MSGPACK_OBJECT_RAW) 95 | continue; 96 | 97 | msgpack_object_raw *key = &(p->key.via.raw); 98 | msgpack_object_raw *val = &(p->val.via.raw); 99 | 100 | if (!strncmp(key->ptr, "ip_address", key->size)) { 101 | ip->ip_address = sdsnewlen(val->ptr, val->size); 102 | } 103 | } 104 | 105 | return ip; 106 | } 107 | 108 | list 109 | *unpack_ips(msgpack_object object) 110 | { 111 | list *ip_list = listCreate(); 112 | listSetDupMethod(ip_list, list_dup_ip); 113 | listSetFreeMethod(ip_list, list_free_ip); 114 | listSetMatchMethod(ip_list, list_match_ip); 115 | if (object.type != MSGPACK_OBJECT_ARRAY) 116 | return ip_list; 117 | red_ip_t *ip; 118 | 119 | for (int i = 0; i < object.via.array.size; i++) { 120 | ip = unpack_ip(object.via.array.ptr[i]); 121 | if (ip) { 122 | listAddNodeTail(ip_list, (void *)ip); 123 | } 124 | } 125 | return ip_list; 126 | } -------------------------------------------------------------------------------- /src/ip.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef IP_H 12 | #define IP_H 13 | 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "util/adlist.h" 18 | 19 | typedef struct red_ip_s { 20 | sds ip_address; 21 | } red_ip_t; 22 | 23 | red_ip_t *new_ip(); 24 | void init_ip(red_ip_t *ip); 25 | void free_ip(red_ip_t *ip); 26 | 27 | void pack_ip(msgpack_packer *packer, red_ip_t *ip); 28 | red_ip_t *unpack_ip(msgpack_object object); 29 | list *unpack_ips(msgpack_object object); 30 | 31 | #endif -------------------------------------------------------------------------------- /src/node.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "node.h" 18 | 19 | red_node_t 20 | *new_node() 21 | { 22 | red_node_t *node = malloc(sizeof(red_node_t)); 23 | init_node(node); 24 | return node; 25 | } 26 | 27 | void 28 | init_node(red_node_t *node) 29 | { 30 | node->hostname = NULL; 31 | } 32 | 33 | void 34 | free_node(red_node_t *node) 35 | { 36 | sdsfree(node->hostname); 37 | } 38 | 39 | static void 40 | pack_key_value(msgpack_packer *packer, char *key, 41 | int key_len, char *value, int value_len) 42 | { 43 | msgpack_pack_raw(packer, key_len); 44 | msgpack_pack_raw_body(packer, key, key_len); 45 | msgpack_pack_raw(packer, value_len); 46 | msgpack_pack_raw_body(packer, value, value_len); 47 | } 48 | 49 | void 50 | pack_node(msgpack_packer *packer, red_node_t *node) 51 | { 52 | msgpack_pack_map(packer, 1); 53 | pack_key_value(packer, "node", 4, (char *)node->hostname, (int)sdslen(node->hostname)); 54 | } 55 | 56 | static void 57 | *list_dup_node(void *ptr) 58 | { 59 | red_node_t *node = (red_node_t *)ptr; 60 | red_node_t *dup_node = malloc(sizeof(red_node_t)); 61 | dup_node->hostname = sdsdup(node->hostname); 62 | return (void *)dup_node; 63 | } 64 | 65 | static void 66 | list_free_node(void *ptr) 67 | { 68 | free_node((red_node_t *)ptr); 69 | } 70 | 71 | static int 72 | list_match_node(void *ptr, void *key) 73 | { 74 | red_node_t *node = (red_node_t *)ptr; 75 | if (sdscmp(node->hostname,(sds)key) == 0) 76 | return 1; 77 | else 78 | return 0; 79 | } 80 | 81 | red_node_t 82 | *unpack_node(msgpack_object object) 83 | { 84 | if (object.type != MSGPACK_OBJECT_MAP) 85 | return NULL; 86 | 87 | red_node_t *node = malloc(sizeof(red_node_t)); 88 | init_node(node); 89 | 90 | msgpack_object_kv* p = object.via.map.ptr; 91 | msgpack_object_kv* pend = object.via.map.ptr + object.via.map.size; 92 | 93 | for (; p < pend; ++p) { 94 | if (p->key.type != MSGPACK_OBJECT_RAW || p->val.type != MSGPACK_OBJECT_RAW) 95 | continue; 96 | 97 | msgpack_object_raw *key = &(p->key.via.raw); 98 | msgpack_object_raw *val = &(p->val.via.raw); 99 | 100 | if (!strncmp(key->ptr, "node", key->size)) { 101 | node->hostname = sdsnewlen(val->ptr, val->size); 102 | } 103 | } 104 | 105 | return node; 106 | } 107 | 108 | list 109 | *unpack_nodes(msgpack_object object) 110 | { 111 | list *node_list = listCreate(); 112 | listSetDupMethod(node_list, list_dup_node); 113 | listSetFreeMethod(node_list, list_free_node); 114 | listSetMatchMethod(node_list, list_match_node); 115 | if (object.type != MSGPACK_OBJECT_ARRAY) 116 | return node_list; 117 | red_node_t *node; 118 | 119 | for (int i = 0; i < object.via.array.size; i++) { 120 | node = unpack_node(object.via.array.ptr[i]); 121 | if (node) { 122 | listAddNodeTail(node_list, (void *)node); 123 | } 124 | } 125 | return node_list; 126 | } -------------------------------------------------------------------------------- /src/node.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef NODE_H 12 | #define NODE_H 13 | 14 | #include 15 | 16 | #include "util/sds.h" 17 | #include "util/adlist.h" 18 | 19 | typedef struct red_node_s { 20 | sds hostname; 21 | } red_node_t; 22 | 23 | red_node_t *new_node(); 24 | void init_node(red_node_t *node); 25 | void free_node(red_node_t *node); 26 | 27 | void pack_node(msgpack_packer *packer, red_node_t *node); 28 | red_node_t *unpack_node(msgpack_object object); 29 | list *unpack_nodes(msgpack_object object); 30 | 31 | #endif -------------------------------------------------------------------------------- /src/red.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include /* standard buffered input/output */ 12 | #include /* standard library definitions */ 13 | #include /* string operations */ 14 | 15 | #include "red.h" 16 | #include "util/sds.h" 17 | #include "cmd/add-ip.h" 18 | #include "cmd/add-node.h" 19 | #include "cmd/remove-ip.h" 20 | #include "cmd/remove-node.h" 21 | #include "cmd/show-ip.h" 22 | #include "cmd/show-node.h" 23 | #include "cmd/ping.h" 24 | #include "cmd/status.h" 25 | 26 | config_t config; 27 | int exit_code; 28 | 29 | static void 30 | usage(void) 31 | { 32 | fprintf(stderr, 33 | "Usage: red [OPTIONS] ...\n" 34 | " -h RED hostname (default: %s)\n" 35 | " -p RED port (default: %i)\n" 36 | " --help Output this help and exit\n" 37 | " --version Output version and exit\n" 38 | " --yaml Format output in YAML\n" 39 | " --nooutput Quiet the output for scripting\n" 40 | "\n" 41 | " add-ip \n" 42 | " remove-ip \n" 43 | " show-ip\n" 44 | "\n" 45 | " add-node \n" 46 | " remove-node \n" 47 | " show-node\n" 48 | "\n" 49 | " ping\n" 50 | "\n" 51 | " status\n", 52 | REDD_DEFAULT_ADDR, 53 | REDD_DEFAULT_PORT); 54 | 55 | exit(1); 56 | } 57 | 58 | static int 59 | parse_options(int argc, char **argv) 60 | { 61 | int i; 62 | 63 | if (argc == 1) 64 | usage(); 65 | 66 | for (i = 1; i < argc; i++) { 67 | int lastarg = i==argc-1; 68 | 69 | if (!strcmp(argv[i],"-h") && !lastarg) { 70 | sdsfree(config.redd_ip); 71 | config.redd_ip = sdsnew(argv[++i]); 72 | } else if (!strcmp(argv[i],"-h") && lastarg) { 73 | usage(); 74 | } else if (!strcmp(argv[i],"--help")) { 75 | usage(); 76 | } else if (!strcmp(argv[i],"-p") && !lastarg) { 77 | config.redd_port = atoi(argv[++i]); 78 | } else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) { 79 | printf("red %s\n", RED_VERSION); 80 | exit(0); 81 | } else if (!strcmp(argv[i],"-y") || !strcmp(argv[i], "--yaml")) { 82 | config.yaml_out = 1; 83 | } else if (!strcmp(argv[i],"-n") || !strcmp(argv[i], "--nooutput")) { 84 | config.no_output = 1; 85 | } else { 86 | if (argv[i][0] == '-') { 87 | fprintf(stderr, 88 | "Unrecognized option or bad number of args for: '%s'\n", 89 | argv[i]); 90 | exit(1); 91 | } else { 92 | /* Likely the command name, stop here. */ 93 | break; 94 | } 95 | } 96 | } 97 | 98 | return i; 99 | } 100 | 101 | static void 102 | init_config(void) 103 | { 104 | config.redd_ip = strdup(REDD_DEFAULT_ADDR); 105 | config.redd_port = REDD_DEFAULT_PORT; 106 | config.yaml_out = 0; 107 | config.no_output = 0; 108 | } 109 | 110 | static void 111 | handle_command(int argc, char **argv) 112 | { 113 | char *command = argv[0]; 114 | 115 | argc -= 1; 116 | argv += 1; 117 | 118 | if (!strcmp(command, "ping")) 119 | handle_ping(argc, argv); 120 | 121 | else if (!strcmp(command, "add-ip")) 122 | handle_add_ip(argc, argv); 123 | 124 | else if (!strcmp(command, "add-node")) 125 | handle_add_node(argc, argv); 126 | 127 | else if (!strcmp(command, "remove-ip")) 128 | handle_remove_ip(argc, argv); 129 | 130 | else if (!strcmp(command, "remove-node")) 131 | handle_remove_node(argc, argv); 132 | 133 | else if (!strcmp(command, "show-ip")) 134 | handle_show_ip(argc, argv); 135 | 136 | else if (!strcmp(command, "show-node")) 137 | handle_show_node(argc, argv); 138 | 139 | else if (!strcmp(command, "status")) 140 | handle_status(argc, argv); 141 | 142 | else 143 | usage(); 144 | } 145 | 146 | int 147 | main(int argc, char **argv) 148 | { 149 | exit_code = 0; 150 | int firstarg; 151 | 152 | init_config(); 153 | 154 | firstarg = parse_options(argc, argv); 155 | argc -= firstarg; 156 | argv += firstarg; 157 | 158 | handle_command(argc, argv); 159 | 160 | return exit_code; 161 | } 162 | -------------------------------------------------------------------------------- /src/red.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef RED_H 12 | #define RED_H 13 | 14 | #include "util/sds.h" 15 | 16 | // todo: move this to autobuild 17 | #define RED_VERSION "0.0.2" 18 | 19 | /* Error codes */ 20 | #define RED_OK 0 21 | #define RED_ERR -1 22 | 23 | /* Sensible defaults */ 24 | #define REDD_DEFAULT_ADDR "127.0.0.1" 25 | #define REDD_DEFAULT_PORT 4470 26 | 27 | typedef struct config_s { 28 | sds redd_ip; /* host/ip of red instance */ 29 | int redd_port; /* port of red instance */ 30 | int yaml_out; 31 | int no_output; 32 | } config_t; 33 | 34 | extern config_t config; 35 | extern int exit_code; 36 | 37 | #endif -------------------------------------------------------------------------------- /src/redd.c: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "redd.h" 17 | #include "red.h" 18 | 19 | typedef struct redd_session_s { 20 | msgxchng_request_t *req; /* msgxchng request */ 21 | redd_callback cb; /* callback function to handle response */ 22 | uv_tcp_t *socket; /* tcp socket */ 23 | uv_stream_t *stream; /* connection stream */ 24 | bframe_buffer_t *buf; /* buffer between packets */ 25 | void *data; /* data storage */ 26 | } redd_session_t; 27 | 28 | static redd_session_t * 29 | new_redd_session(msgxchng_request_t *req, redd_callback cb) 30 | { 31 | redd_session_t *session = (redd_session_t *)malloc(sizeof(redd_session_t)); 32 | 33 | session->req = req; 34 | session->cb = cb; 35 | session->socket = NULL; 36 | session->stream = NULL; 37 | session->buf = new_bframe_buffer(); 38 | session->data = NULL; 39 | 40 | return session; 41 | } 42 | 43 | static void 44 | clean_redd_session(redd_session_t *session) 45 | { 46 | clean_msgxchng_request(session->req); 47 | free(session->req); 48 | session->req = NULL; 49 | 50 | clean_bframe_buffer(session->buf); 51 | free(session->buf); 52 | session->buf = NULL; 53 | 54 | free(session); 55 | session = NULL; 56 | } 57 | 58 | static void 59 | parse_response(redd_session_t *session, bframe_t *frame) 60 | { 61 | msgxchng_response_t *res; 62 | 63 | res = unpack_msgxchng_response(frame->data, frame->len.int_len); 64 | 65 | if (!strcmp(res->status, "complete")) { 66 | uv_close((uv_handle_t *)session->stream, (uv_close_cb) free); 67 | uv_stop(uv_default_loop()); 68 | clean_redd_session(session); 69 | } 70 | 71 | session->cb(res, RED_OK); 72 | 73 | /* cleanup */ 74 | clean_bframe(frame); 75 | free(frame); 76 | frame = NULL; 77 | } 78 | 79 | static uv_buf_t 80 | read_alloc_buffer(uv_handle_t *handle, size_t suggested_size) 81 | { 82 | return uv_buf_init((char *)malloc(suggested_size), suggested_size); 83 | } 84 | 85 | static void 86 | on_read(uv_stream_t *proto, ssize_t nread, uv_buf_t buf) 87 | { 88 | redd_session_t *session = (redd_session_t *)proto->data; 89 | 90 | if (nread < 0) { 91 | if (buf.base) { 92 | free(buf.base); 93 | buf.base = NULL; 94 | } 95 | printf("Error: Connection closed prematurely\n"); 96 | uv_close((uv_handle_t *)proto, (uv_close_cb) free); 97 | session->cb(NULL, RED_ERR); 98 | uv_stop(uv_default_loop()); 99 | clean_redd_session(session); 100 | return; 101 | } 102 | 103 | if (nread == 0) { 104 | free(buf.base); 105 | buf.base = NULL; 106 | return; 107 | } 108 | 109 | int framec; 110 | bframe_t **frames; 111 | 112 | frames = parse_char_to_bframes(buf.base, nread, session->buf, &framec); 113 | 114 | int i; 115 | for (i=0; i < framec ; i++) 116 | parse_response(session, frames[i]); 117 | 118 | /* cleanup */ 119 | free(buf.base); 120 | buf.base = NULL; 121 | 122 | free(frames); 123 | frames = NULL; 124 | } 125 | 126 | static void 127 | redd_recv_response(redd_session_t *session) 128 | { 129 | if (uv_read_start(session->stream, read_alloc_buffer, on_read) == UV_OK) 130 | session->stream->data = session; 131 | } 132 | 133 | static void 134 | on_write(uv_write_t* req, int status) 135 | { 136 | uv_buf_t *buf = (uv_buf_t *)req->data; 137 | free(buf->base); 138 | buf->base = NULL; 139 | free(req->data); 140 | req->data = NULL; 141 | free(req); 142 | req = NULL; 143 | } 144 | 145 | static void 146 | redd_send_request(redd_session_t *session) 147 | { 148 | int p_len; 149 | char *payload = pack_msgxchng_request(session->req, &p_len); 150 | 151 | bframe_t *frame; 152 | frame = new_bframe(payload, p_len); 153 | 154 | uv_buf_t *buf; 155 | uv_write_t *writer; 156 | 157 | buf = (uv_buf_t *)malloc(sizeof(uv_buf_t)); 158 | writer = (uv_write_t *)malloc(sizeof(uv_write_t)); 159 | 160 | int size; 161 | buf->base = bframe_to_char(frame, &size); 162 | buf->len = size; 163 | 164 | if (uv_write(writer, session->stream, buf, 1, on_write) == UV_OK) 165 | writer->data = (void *)buf; 166 | 167 | clean_bframe(frame); 168 | free(frame); 169 | frame = NULL; 170 | } 171 | 172 | static void 173 | on_connect(uv_connect_t* connection, int status) 174 | { 175 | redd_session_t *session = (redd_session_t *)connection->data; 176 | 177 | if (status < 0) { 178 | printf("Error: Unable to connect to RED\n"); 179 | session->cb(NULL, RED_ERR); 180 | uv_stop(uv_default_loop()); 181 | clean_redd_session(session); 182 | free(session->socket); 183 | } else { 184 | session->stream = (uv_stream_t *)connection->handle; 185 | 186 | redd_send_request(session); 187 | redd_recv_response(session); 188 | } 189 | 190 | free(connection); 191 | } 192 | 193 | static void 194 | redd_connect(char *ip, redd_session_t *session) 195 | { 196 | uv_tcp_t *socket = (uv_tcp_t *)malloc(sizeof(uv_tcp_t)); 197 | 198 | uv_tcp_init(uv_default_loop(), socket); 199 | uv_tcp_keepalive(socket, 1, 60); 200 | 201 | struct sockaddr_in dest = uv_ip4_addr(ip, config.redd_port); 202 | 203 | uv_connect_t *connect = malloc(sizeof(uv_connect_t)); 204 | if(uv_tcp_connect(connect, socket, dest, on_connect) == UV_OK) { 205 | session->socket = socket; 206 | connect->data = (void *)session; 207 | } 208 | } 209 | 210 | static void 211 | on_resolve(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) 212 | { 213 | redd_session_t *session = (redd_session_t *)resolver->data; 214 | 215 | if (status < 0) { 216 | printf("Error: Unable to resolve %s\n", config.redd_ip); 217 | session->cb(NULL, RED_ERR); 218 | uv_stop(uv_default_loop()); 219 | clean_redd_session(session); 220 | } else { 221 | char addr[17] = {'\0'}; 222 | uv_ip4_name((struct sockaddr_in*) res->ai_addr, addr, 16); 223 | 224 | redd_connect(addr, session); 225 | } 226 | 227 | /* cleanup */ 228 | free(session->data); 229 | free(resolver); 230 | uv_freeaddrinfo(res); 231 | } 232 | 233 | static void 234 | redd_resolve(redd_session_t *session) 235 | { 236 | uv_getaddrinfo_t *resolver = (uv_getaddrinfo_t *)malloc(sizeof(uv_getaddrinfo_t)); 237 | struct addrinfo *hints = (struct addrinfo *)malloc(sizeof(struct addrinfo)); 238 | 239 | hints->ai_family = PF_INET; 240 | hints->ai_socktype = SOCK_STREAM; 241 | hints->ai_protocol = IPPROTO_TCP; 242 | hints->ai_flags = 0; 243 | 244 | if (uv_getaddrinfo(uv_default_loop(), resolver, on_resolve, config.redd_ip, NULL, hints) == UV_OK) { 245 | session->data = hints; 246 | resolver->data = session; 247 | } 248 | } 249 | 250 | static void 251 | redd_run(redd_session_t *session) 252 | { 253 | redd_resolve(session); 254 | 255 | uv_run(uv_default_loop(), UV_RUN_DEFAULT); 256 | } 257 | 258 | void 259 | redd_request(msgxchng_request_t *req, redd_callback cb) 260 | { 261 | redd_session_t *session = new_redd_session(req, cb); 262 | 263 | redd_run(session); 264 | } 265 | -------------------------------------------------------------------------------- /src/redd.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*- 2 | // vim: ts=8 sw=8 ft=c noet 3 | /* 4 | * Copyright (c) 2015 Pagoda Box Inc 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, v. 7 | * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 8 | * at http://mozilla.org/MPL/2.0/. 9 | */ 10 | 11 | #ifndef REDD_H 12 | #define REDD_H 13 | 14 | #include 15 | 16 | typedef void (*redd_callback)(msgxchng_response_t *res, int status); 17 | 18 | void redd_request(msgxchng_request_t *req, redd_callback cb); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/util/adlist.c: -------------------------------------------------------------------------------- 1 | /* adlist.c - A generic doubly linked list implementation 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | 32 | #include 33 | #include "adlist.h" 34 | 35 | /* Create a new list. The created list can be freed with 36 | * AlFreeList(), but private value of every node need to be freed 37 | * by the user before to call AlFreeList(). 38 | * 39 | * On error, NULL is returned. Otherwise the pointer to the new list. */ 40 | list *listCreate(void) 41 | { 42 | struct list *list; 43 | 44 | if ((list = malloc(sizeof(*list))) == NULL) 45 | return NULL; 46 | list->head = list->tail = NULL; 47 | list->len = 0; 48 | list->dup = NULL; 49 | list->free = NULL; 50 | list->match = NULL; 51 | return list; 52 | } 53 | 54 | /* Free the whole list. 55 | * 56 | * This function can't fail. */ 57 | void listRelease(list *list) 58 | { 59 | unsigned long len; 60 | listNode *current, *next; 61 | 62 | current = list->head; 63 | len = list->len; 64 | while(len--) { 65 | next = current->next; 66 | if (list->free) list->free(current->value); 67 | free(current); 68 | current = next; 69 | } 70 | free(list); 71 | } 72 | 73 | /* Add a new node to the list, to head, contaning the specified 'value' 74 | * pointer as value. 75 | * 76 | * On error, NULL is returned and no operation is performed (i.e. the 77 | * list remains unaltered). 78 | * On success the 'list' pointer you pass to the function is returned. */ 79 | list *listAddNodeHead(list *list, void *value) 80 | { 81 | listNode *node; 82 | 83 | if ((node = malloc(sizeof(*node))) == NULL) 84 | return NULL; 85 | node->value = value; 86 | if (list->len == 0) { 87 | list->head = list->tail = node; 88 | node->prev = node->next = NULL; 89 | } else { 90 | node->prev = NULL; 91 | node->next = list->head; 92 | list->head->prev = node; 93 | list->head = node; 94 | } 95 | list->len++; 96 | return list; 97 | } 98 | 99 | /* Add a new node to the list, to tail, containing the specified 'value' 100 | * pointer as value. 101 | * 102 | * On error, NULL is returned and no operation is performed (i.e. the 103 | * list remains unaltered). 104 | * On success the 'list' pointer you pass to the function is returned. */ 105 | list *listAddNodeTail(list *list, void *value) 106 | { 107 | listNode *node; 108 | 109 | if ((node = malloc(sizeof(*node))) == NULL) 110 | return NULL; 111 | node->value = value; 112 | if (list->len == 0) { 113 | list->head = list->tail = node; 114 | node->prev = node->next = NULL; 115 | } else { 116 | node->prev = list->tail; 117 | node->next = NULL; 118 | list->tail->next = node; 119 | list->tail = node; 120 | } 121 | list->len++; 122 | return list; 123 | } 124 | 125 | list *listInsertNode(list *list, listNode *old_node, void *value, int after) { 126 | listNode *node; 127 | 128 | if ((node = malloc(sizeof(*node))) == NULL) 129 | return NULL; 130 | node->value = value; 131 | if (after) { 132 | node->prev = old_node; 133 | node->next = old_node->next; 134 | if (list->tail == old_node) { 135 | list->tail = node; 136 | } 137 | } else { 138 | node->next = old_node; 139 | node->prev = old_node->prev; 140 | if (list->head == old_node) { 141 | list->head = node; 142 | } 143 | } 144 | if (node->prev != NULL) { 145 | node->prev->next = node; 146 | } 147 | if (node->next != NULL) { 148 | node->next->prev = node; 149 | } 150 | list->len++; 151 | return list; 152 | } 153 | 154 | /* Remove the specified node from the specified list. 155 | * It's up to the caller to free the private value of the node. 156 | * 157 | * This function can't fail. */ 158 | void listDelNode(list *list, listNode *node) 159 | { 160 | if (node->prev) 161 | node->prev->next = node->next; 162 | else 163 | list->head = node->next; 164 | if (node->next) 165 | node->next->prev = node->prev; 166 | else 167 | list->tail = node->prev; 168 | if (list->free) list->free(node->value); 169 | free(node); 170 | list->len--; 171 | } 172 | 173 | /* Returns a list iterator 'iter'. After the initialization every 174 | * call to listNext() will return the next element of the list. 175 | * 176 | * This function can't fail. */ 177 | listIter *listGetIterator(list *list, int direction) 178 | { 179 | listIter *iter; 180 | 181 | if ((iter = malloc(sizeof(*iter))) == NULL) return NULL; 182 | if (direction == AL_START_HEAD) 183 | iter->next = list->head; 184 | else 185 | iter->next = list->tail; 186 | iter->direction = direction; 187 | return iter; 188 | } 189 | 190 | /* Release the iterator memory */ 191 | void listReleaseIterator(listIter *iter) { 192 | free(iter); 193 | } 194 | 195 | /* Create an iterator in the list private iterator structure */ 196 | void listRewind(list *list, listIter *li) { 197 | li->next = list->head; 198 | li->direction = AL_START_HEAD; 199 | } 200 | 201 | void listRewindTail(list *list, listIter *li) { 202 | li->next = list->tail; 203 | li->direction = AL_START_TAIL; 204 | } 205 | 206 | /* Return the next element of an iterator. 207 | * It's valid to remove the currently returned element using 208 | * listDelNode(), but not to remove other elements. 209 | * 210 | * The function returns a pointer to the next element of the list, 211 | * or NULL if there are no more elements, so the classical usage patter 212 | * is: 213 | * 214 | * iter = listGetIterator(list,); 215 | * while ((node = listNext(iter)) != NULL) { 216 | * doSomethingWith(listNodeValue(node)); 217 | * } 218 | * 219 | * */ 220 | listNode *listNext(listIter *iter) 221 | { 222 | listNode *current = iter->next; 223 | 224 | if (current != NULL) { 225 | if (iter->direction == AL_START_HEAD) 226 | iter->next = current->next; 227 | else 228 | iter->next = current->prev; 229 | } 230 | return current; 231 | } 232 | 233 | /* Duplicate the whole list. On out of memory NULL is returned. 234 | * On success a copy of the original list is returned. 235 | * 236 | * The 'Dup' method set with listSetDupMethod() function is used 237 | * to copy the node value. Otherwise the same pointer value of 238 | * the original node is used as value of the copied node. 239 | * 240 | * The original list both on success or error is never modified. */ 241 | list *listDup(list *orig) 242 | { 243 | list *copy; 244 | listIter *iter; 245 | listNode *node; 246 | 247 | if ((copy = listCreate()) == NULL) 248 | return NULL; 249 | copy->dup = orig->dup; 250 | copy->free = orig->free; 251 | copy->match = orig->match; 252 | iter = listGetIterator(orig, AL_START_HEAD); 253 | while((node = listNext(iter)) != NULL) { 254 | void *value; 255 | 256 | if (copy->dup) { 257 | value = copy->dup(node->value); 258 | if (value == NULL) { 259 | listRelease(copy); 260 | listReleaseIterator(iter); 261 | return NULL; 262 | } 263 | } else 264 | value = node->value; 265 | if (listAddNodeTail(copy, value) == NULL) { 266 | listRelease(copy); 267 | listReleaseIterator(iter); 268 | return NULL; 269 | } 270 | } 271 | listReleaseIterator(iter); 272 | return copy; 273 | } 274 | 275 | /* Search the list for a node matching a given key. 276 | * The match is performed using the 'match' method 277 | * set with listSetMatchMethod(). If no 'match' method 278 | * is set, the 'value' pointer of every node is directly 279 | * compared with the 'key' pointer. 280 | * 281 | * On success the first matching node pointer is returned 282 | * (search starts from head). If no matching node exists 283 | * NULL is returned. */ 284 | listNode *listSearchKey(list *list, void *key) 285 | { 286 | listIter *iter; 287 | listNode *node; 288 | 289 | iter = listGetIterator(list, AL_START_HEAD); 290 | while((node = listNext(iter)) != NULL) { 291 | if (list->match) { 292 | if (list->match(node->value, key)) { 293 | listReleaseIterator(iter); 294 | return node; 295 | } 296 | } else { 297 | if (key == node->value) { 298 | listReleaseIterator(iter); 299 | return node; 300 | } 301 | } 302 | } 303 | listReleaseIterator(iter); 304 | return NULL; 305 | } 306 | 307 | /* Return the element at the specified zero-based index 308 | * where 0 is the head, 1 is the element next to head 309 | * and so on. Negative integers are used in order to count 310 | * from the tail, -1 is the last element, -2 the penultimate 311 | * and so on. If the index is out of range NULL is returned. */ 312 | listNode *listIndex(list *list, long index) { 313 | listNode *n; 314 | 315 | if (index < 0) { 316 | index = (-index)-1; 317 | n = list->tail; 318 | while(index-- && n) n = n->prev; 319 | } else { 320 | n = list->head; 321 | while(index-- && n) n = n->next; 322 | } 323 | return n; 324 | } 325 | 326 | /* Rotate the list removing the tail node and inserting it to the head. */ 327 | void listRotate(list *list) { 328 | listNode *tail = list->tail; 329 | 330 | if (listLength(list) <= 1) return; 331 | 332 | /* Detach current tail */ 333 | list->tail = tail->prev; 334 | list->tail->next = NULL; 335 | /* Move it as head */ 336 | list->head->prev = tail; 337 | tail->prev = NULL; 338 | tail->next = list->head; 339 | list->head = tail; 340 | } 341 | -------------------------------------------------------------------------------- /src/util/adlist.h: -------------------------------------------------------------------------------- 1 | /* adlist.h - A generic doubly linked list implementation 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ADLIST_H__ 32 | #define __ADLIST_H__ 33 | 34 | /* Node, List, and Iterator are the only data structures used currently. */ 35 | 36 | typedef struct listNode { 37 | struct listNode *prev; 38 | struct listNode *next; 39 | void *value; 40 | } listNode; 41 | 42 | typedef struct listIter { 43 | listNode *next; 44 | int direction; 45 | } listIter; 46 | 47 | typedef struct list { 48 | listNode *head; 49 | listNode *tail; 50 | void *(*dup)(void *ptr); 51 | void (*free)(void *ptr); 52 | int (*match)(void *ptr, void *key); 53 | unsigned long len; 54 | } list; 55 | 56 | /* Functions implemented as macros */ 57 | #define listLength(l) ((l)->len) 58 | #define listFirst(l) ((l)->head) 59 | #define listLast(l) ((l)->tail) 60 | #define listPrevNode(n) ((n)->prev) 61 | #define listNextNode(n) ((n)->next) 62 | #define listNodeValue(n) ((n)->value) 63 | 64 | #define listSetDupMethod(l,m) ((l)->dup = (m)) 65 | #define listSetFreeMethod(l,m) ((l)->free = (m)) 66 | #define listSetMatchMethod(l,m) ((l)->match = (m)) 67 | 68 | #define listGetDupMethod(l) ((l)->dup) 69 | #define listGetFree(l) ((l)->free) 70 | #define listGetMatchMethod(l) ((l)->match) 71 | 72 | /* Prototypes */ 73 | list *listCreate(void); 74 | void listRelease(list *list); 75 | list *listAddNodeHead(list *list, void *value); 76 | list *listAddNodeTail(list *list, void *value); 77 | list *listInsertNode(list *list, listNode *old_node, void *value, int after); 78 | void listDelNode(list *list, listNode *node); 79 | listIter *listGetIterator(list *list, int direction); 80 | listNode *listNext(listIter *iter); 81 | void listReleaseIterator(listIter *iter); 82 | list *listDup(list *orig); 83 | listNode *listSearchKey(list *list, void *key); 84 | listNode *listIndex(list *list, long index); 85 | void listRewind(list *list, listIter *li); 86 | void listRewindTail(list *list, listIter *li); 87 | void listRotate(list *list); 88 | 89 | /* Directions for iterators */ 90 | #define AL_START_HEAD 0 91 | #define AL_START_TAIL 1 92 | 93 | #endif /* __ADLIST_H__ */ 94 | -------------------------------------------------------------------------------- /src/util/sds.c: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "sds.h" 38 | 39 | /* Create a new sds string with the content specified by the 'init' pointer 40 | * and 'initlen'. 41 | * If NULL is used for 'init' the string is initialized with zero bytes. 42 | * 43 | * The string is always null-termined (all the sds strings are, always) so 44 | * even if you create an sds string with: 45 | * 46 | * mystring = sdsnewlen("abc",3"); 47 | * 48 | * You can print the string with printf() as there is an implicit \0 at the 49 | * end of the string. However the string is binary safe and can contain 50 | * \0 characters in the middle, as the length is stored in the sds header. */ 51 | sds sdsnewlen(const void *init, size_t initlen) { 52 | struct sdshdr *sh; 53 | 54 | if (init) { 55 | sh = malloc(sizeof(struct sdshdr)+initlen+1); 56 | } else { 57 | sh = calloc(1, sizeof(struct sdshdr)+initlen+1); 58 | } 59 | if (sh == NULL) return NULL; 60 | sh->len = initlen; 61 | sh->free = 0; 62 | if (initlen && init) 63 | memcpy(sh->buf, init, initlen); 64 | sh->buf[initlen] = '\0'; 65 | return (char*)sh->buf; 66 | } 67 | 68 | /* Create an empty (zero length) sds string. Even in this case the string 69 | * always has an implicit null term. */ 70 | sds sdsempty(void) { 71 | return sdsnewlen("",0); 72 | } 73 | 74 | /* Create a new sds string starting from a null termined C string. */ 75 | sds sdsnew(const char *init) { 76 | size_t initlen = (init == NULL) ? 0 : strlen(init); 77 | return sdsnewlen(init, initlen); 78 | } 79 | 80 | /* Duplicate an sds string. */ 81 | sds sdsdup(const sds s) { 82 | return sdsnewlen(s, sdslen(s)); 83 | } 84 | 85 | /* Free an sds string. No operation is performed if 's' is NULL. */ 86 | void sdsfree(sds s) { 87 | if (s == NULL) return; 88 | free(s-sizeof(struct sdshdr)); 89 | } 90 | 91 | /* Set the sds string length to the length as obtained with strlen(), so 92 | * considering as content only up to the first null term character. 93 | * 94 | * This function is useful when the sds string is hacked manually in some 95 | * way, like in the following example: 96 | * 97 | * s = sdsnew("foobar"); 98 | * s[2] = '\0'; 99 | * sdsupdatelen(s); 100 | * printf("%d\n", sdslen(s)); 101 | * 102 | * The output will be "2", but if we comment out the call to sdsupdatelen() 103 | * the output will be "6" as the string was modified but the logical length 104 | * remains 6 bytes. */ 105 | void sdsupdatelen(sds s) { 106 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 107 | int reallen = strlen(s); 108 | sh->free += (sh->len-reallen); 109 | sh->len = reallen; 110 | } 111 | 112 | /* Modify an sds string on-place to make it empty (zero length). 113 | * However all the existing buffer is not discarded but set as free space 114 | * so that next append operations will not require allocations up to the 115 | * number of bytes previously available. */ 116 | void sdsclear(sds s) { 117 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 118 | sh->free += sh->len; 119 | sh->len = 0; 120 | sh->buf[0] = '\0'; 121 | } 122 | 123 | /* Enlarge the free space at the end of the sds string so that the caller 124 | * is sure that after calling this function can overwrite up to addlen 125 | * bytes after the end of the string, plus one more byte for nul term. 126 | * 127 | * Note: this does not change the *length* of the sds string as returned 128 | * by sdslen(), but only the free buffer space we have. */ 129 | sds sdsMakeRoomFor(sds s, size_t addlen) { 130 | struct sdshdr *sh, *newsh; 131 | size_t free = sdsavail(s); 132 | size_t len, newlen; 133 | 134 | if (free >= addlen) return s; 135 | len = sdslen(s); 136 | sh = (void*) (s-(sizeof(struct sdshdr))); 137 | newlen = (len+addlen); 138 | if (newlen < SDS_MAX_PREALLOC) 139 | newlen *= 2; 140 | else 141 | newlen += SDS_MAX_PREALLOC; 142 | newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1); 143 | if (newsh == NULL) return NULL; 144 | 145 | newsh->free = newlen - len; 146 | return newsh->buf; 147 | } 148 | 149 | /* Reallocate the sds string so that it has no free space at the end. The 150 | * contained string remains not altered, but next concatenation operations 151 | * will require a reallocation. 152 | * 153 | * After the call, the passed sds string is no longer valid and all the 154 | * references must be substituted with the new pointer returned by the call. */ 155 | sds sdsRemoveFreeSpace(sds s) { 156 | struct sdshdr *sh; 157 | 158 | sh = (void*) (s-(sizeof(struct sdshdr))); 159 | sh = realloc(sh, sizeof(struct sdshdr)+sh->len+1); 160 | sh->free = 0; 161 | return sh->buf; 162 | } 163 | 164 | /* Return the total size of the allocation of the specifed sds string, 165 | * including: 166 | * 1) The sds header before the pointer. 167 | * 2) The string. 168 | * 3) The free buffer at the end if any. 169 | * 4) The implicit null term. 170 | */ 171 | size_t sdsAllocSize(sds s) { 172 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 173 | 174 | return sizeof(*sh)+sh->len+sh->free+1; 175 | } 176 | 177 | /* Increment the sds length and decrements the left free space at the 178 | * end of the string according to 'incr'. Also set the null term 179 | * in the new end of the string. 180 | * 181 | * This function is used in order to fix the string length after the 182 | * user calls sdsMakeRoomFor(), writes something after the end of 183 | * the current string, and finally needs to set the new length. 184 | * 185 | * Note: it is possible to use a negative increment in order to 186 | * right-trim the string. 187 | * 188 | * Usage example: 189 | * 190 | * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the 191 | * following schema, to cat bytes coming from the kernel to the end of an 192 | * sds string without copying into an intermediate buffer: 193 | * 194 | * oldlen = sdslen(s); 195 | * s = sdsMakeRoomFor(s, BUFFER_SIZE); 196 | * nread = read(fd, s+oldlen, BUFFER_SIZE); 197 | * ... check for nread <= 0 and handle it ... 198 | * sdsIncrLen(s, nread); 199 | */ 200 | void sdsIncrLen(sds s, int incr) { 201 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 202 | 203 | assert(sh->free >= incr); 204 | sh->len += incr; 205 | sh->free -= incr; 206 | assert(sh->free >= 0); 207 | s[sh->len] = '\0'; 208 | } 209 | 210 | /* Grow the sds to have the specified length. Bytes that were not part of 211 | * the original length of the sds will be set to zero. 212 | * 213 | * if the specified length is smaller than the current length, no operation 214 | * is performed. */ 215 | sds sdsgrowzero(sds s, size_t len) { 216 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 217 | size_t totlen, curlen = sh->len; 218 | 219 | if (len <= curlen) return s; 220 | s = sdsMakeRoomFor(s,len-curlen); 221 | if (s == NULL) return NULL; 222 | 223 | /* Make sure added region doesn't contain garbage */ 224 | sh = (void*)(s-(sizeof(struct sdshdr))); 225 | memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ 226 | totlen = sh->len+sh->free; 227 | sh->len = len; 228 | sh->free = totlen-sh->len; 229 | return s; 230 | } 231 | 232 | /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the 233 | * end of the specified sds string 's'. 234 | * 235 | * After the call, the passed sds string is no longer valid and all the 236 | * references must be substituted with the new pointer returned by the call. */ 237 | sds sdscatlen(sds s, const void *t, size_t len) { 238 | struct sdshdr *sh; 239 | size_t curlen = sdslen(s); 240 | 241 | s = sdsMakeRoomFor(s,len); 242 | if (s == NULL) return NULL; 243 | sh = (void*) (s-(sizeof(struct sdshdr))); 244 | memcpy(s+curlen, t, len); 245 | sh->len = curlen+len; 246 | sh->free = sh->free-len; 247 | s[curlen+len] = '\0'; 248 | return s; 249 | } 250 | 251 | /* Append the specified null termianted C string to the sds string 's'. 252 | * 253 | * After the call, the passed sds string is no longer valid and all the 254 | * references must be substituted with the new pointer returned by the call. */ 255 | sds sdscat(sds s, const char *t) { 256 | return sdscatlen(s, t, strlen(t)); 257 | } 258 | 259 | /* Append the specified sds 't' to the existing sds 's'. 260 | * 261 | * After the call, the modified sds string is no longer valid and all the 262 | * references must be substituted with the new pointer returned by the call. */ 263 | sds sdscatsds(sds s, const sds t) { 264 | return sdscatlen(s, t, sdslen(t)); 265 | } 266 | 267 | /* Destructively modify the sds string 's' to hold the specified binary 268 | * safe string pointed by 't' of length 'len' bytes. */ 269 | sds sdscpylen(sds s, const char *t, size_t len) { 270 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 271 | size_t totlen = sh->free+sh->len; 272 | 273 | if (totlen < len) { 274 | s = sdsMakeRoomFor(s,len-sh->len); 275 | if (s == NULL) return NULL; 276 | sh = (void*) (s-(sizeof(struct sdshdr))); 277 | totlen = sh->free+sh->len; 278 | } 279 | memcpy(s, t, len); 280 | s[len] = '\0'; 281 | sh->len = len; 282 | sh->free = totlen-len; 283 | return s; 284 | } 285 | 286 | /* Like sdscpylen() but 't' must be a null-termined string so that the length 287 | * of the string is obtained with strlen(). */ 288 | sds sdscpy(sds s, const char *t) { 289 | return sdscpylen(s, t, strlen(t)); 290 | } 291 | 292 | /* Like sdscatpritf() but gets va_list instead of being variadic. */ 293 | sds sdscatvprintf(sds s, const char *fmt, va_list ap) { 294 | va_list cpy; 295 | char *buf, *t; 296 | size_t buflen = 16; 297 | 298 | while(1) { 299 | buf = malloc(buflen); 300 | if (buf == NULL) return NULL; 301 | buf[buflen-2] = '\0'; 302 | va_copy(cpy,ap); 303 | vsnprintf(buf, buflen, fmt, cpy); 304 | if (buf[buflen-2] != '\0') { 305 | free(buf); 306 | buflen *= 2; 307 | continue; 308 | } 309 | break; 310 | } 311 | t = sdscat(s, buf); 312 | free(buf); 313 | return t; 314 | } 315 | 316 | /* Append to the sds string 's' a string obtained using printf-alike format 317 | * specifier. 318 | * 319 | * After the call, the modified sds string is no longer valid and all the 320 | * references must be substituted with the new pointer returned by the call. 321 | * 322 | * Example: 323 | * 324 | * s = sdsempty("Sum is: "); 325 | * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). 326 | * 327 | * Often you need to create a string from scratch with the printf-alike 328 | * format. When this is the need, just use sdsempty() as the target string: 329 | * 330 | * s = sdscatprintf(sdsempty(), "... your format ...", args); 331 | */ 332 | sds sdscatprintf(sds s, const char *fmt, ...) { 333 | va_list ap; 334 | char *t; 335 | va_start(ap, fmt); 336 | t = sdscatvprintf(s,fmt,ap); 337 | va_end(ap); 338 | return t; 339 | } 340 | 341 | /* Remove the part of the string from left and from right composed just of 342 | * contiguous characters found in 'cset', that is a null terminted C string. 343 | * 344 | * After the call, the modified sds string is no longer valid and all the 345 | * references must be substituted with the new pointer returned by the call. 346 | * 347 | * Example: 348 | * 349 | * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); 350 | * s = sdstrim(s,"A. :"); 351 | * printf("%s\n", s); 352 | * 353 | * Output will be just "Hello World". 354 | */ 355 | sds sdstrim(sds s, const char *cset) { 356 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 357 | char *start, *end, *sp, *ep; 358 | size_t len; 359 | 360 | sp = start = s; 361 | ep = end = s+sdslen(s)-1; 362 | while(sp <= end && strchr(cset, *sp)) sp++; 363 | while(ep > start && strchr(cset, *ep)) ep--; 364 | len = (sp > ep) ? 0 : ((ep-sp)+1); 365 | if (sh->buf != sp) memmove(sh->buf, sp, len); 366 | sh->buf[len] = '\0'; 367 | sh->free = sh->free+(sh->len-len); 368 | sh->len = len; 369 | return s; 370 | } 371 | 372 | /* Turn the string into a smaller (or equal) string containing only the 373 | * substring specified by the 'start' and 'end' indexes. 374 | * 375 | * start and end can be negative, where -1 means the last character of the 376 | * string, -2 the penultimate character, and so forth. 377 | * 378 | * The interval is inclusive, so the start and end characters will be part 379 | * of the resulting string. 380 | * 381 | * The string is modified in-place. 382 | * 383 | * Example: 384 | * 385 | * s = sdsnew("Hello World"); 386 | * sdstrim(s,1,-1); => "ello Worl" 387 | */ 388 | void sdsrange(sds s, int start, int end) { 389 | struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); 390 | size_t newlen, len = sdslen(s); 391 | 392 | if (len == 0) return; 393 | if (start < 0) { 394 | start = len+start; 395 | if (start < 0) start = 0; 396 | } 397 | if (end < 0) { 398 | end = len+end; 399 | if (end < 0) end = 0; 400 | } 401 | newlen = (start > end) ? 0 : (end-start)+1; 402 | if (newlen != 0) { 403 | if (start >= (signed)len) { 404 | newlen = 0; 405 | } else if (end >= (signed)len) { 406 | end = len-1; 407 | newlen = (start > end) ? 0 : (end-start)+1; 408 | } 409 | } else { 410 | start = 0; 411 | } 412 | if (start && newlen) memmove(sh->buf, sh->buf+start, newlen); 413 | sh->buf[newlen] = 0; 414 | sh->free = sh->free+(sh->len-newlen); 415 | sh->len = newlen; 416 | } 417 | 418 | /* Apply tolower() to every character of the sds string 's'. */ 419 | void sdstolower(sds s) { 420 | int len = sdslen(s), j; 421 | 422 | for (j = 0; j < len; j++) s[j] = tolower(s[j]); 423 | } 424 | 425 | /* Apply toupper() to every character of the sds string 's'. */ 426 | void sdstoupper(sds s) { 427 | int len = sdslen(s), j; 428 | 429 | for (j = 0; j < len; j++) s[j] = toupper(s[j]); 430 | } 431 | 432 | /* Compare two sds strings s1 and s2 with memcmp(). 433 | * 434 | * Return value: 435 | * 436 | * 1 if s1 > s2. 437 | * -1 if s1 < s2. 438 | * 0 if s1 and s2 are exactly the same binary string. 439 | * 440 | * If two strings share exactly the same prefix, but one of the two has 441 | * additional characters, the longer string is considered to be greater than 442 | * the smaller one. */ 443 | int sdscmp(const sds s1, const sds s2) { 444 | size_t l1, l2, minlen; 445 | int cmp; 446 | 447 | l1 = sdslen(s1); 448 | l2 = sdslen(s2); 449 | minlen = (l1 < l2) ? l1 : l2; 450 | cmp = memcmp(s1,s2,minlen); 451 | if (cmp == 0) return l1-l2; 452 | return cmp; 453 | } 454 | 455 | /* Split 's' with separator in 'sep'. An array 456 | * of sds strings is returned. *count will be set 457 | * by reference to the number of tokens returned. 458 | * 459 | * On out of memory, zero length string, zero length 460 | * separator, NULL is returned. 461 | * 462 | * Note that 'sep' is able to split a string using 463 | * a multi-character separator. For example 464 | * sdssplit("foo_-_bar","_-_"); will return two 465 | * elements "foo" and "bar". 466 | * 467 | * This version of the function is binary-safe but 468 | * requires length arguments. sdssplit() is just the 469 | * same function but for zero-terminated strings. 470 | */ 471 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { 472 | int elements = 0, slots = 5, start = 0, j; 473 | sds *tokens; 474 | 475 | if (seplen < 1 || len < 0) return NULL; 476 | 477 | tokens = malloc(sizeof(sds)*slots); 478 | if (tokens == NULL) return NULL; 479 | 480 | if (len == 0) { 481 | *count = 0; 482 | return tokens; 483 | } 484 | for (j = 0; j < (len-(seplen-1)); j++) { 485 | /* make sure there is room for the next element and the final one */ 486 | if (slots < elements+2) { 487 | sds *newtokens; 488 | 489 | slots *= 2; 490 | newtokens = realloc(tokens,sizeof(sds)*slots); 491 | if (newtokens == NULL) goto cleanup; 492 | tokens = newtokens; 493 | } 494 | /* search the separator */ 495 | if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { 496 | tokens[elements] = sdsnewlen(s+start,j-start); 497 | if (tokens[elements] == NULL) goto cleanup; 498 | elements++; 499 | start = j+seplen; 500 | j = j+seplen-1; /* skip the separator */ 501 | } 502 | } 503 | /* Add the final element. We are sure there is room in the tokens array. */ 504 | tokens[elements] = sdsnewlen(s+start,len-start); 505 | if (tokens[elements] == NULL) goto cleanup; 506 | elements++; 507 | *count = elements; 508 | return tokens; 509 | 510 | cleanup: 511 | { 512 | int i; 513 | for (i = 0; i < elements; i++) sdsfree(tokens[i]); 514 | free(tokens); 515 | *count = 0; 516 | return NULL; 517 | } 518 | } 519 | 520 | /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ 521 | void sdsfreesplitres(sds *tokens, int count) { 522 | if (!tokens) return; 523 | while(count--) 524 | sdsfree(tokens[count]); 525 | free(tokens); 526 | } 527 | 528 | /* Create an sds string from a long long value. It is much faster than: 529 | * 530 | * sdscatprintf(sdsempty(),"%lld\n", value); 531 | */ 532 | sds sdsfromlonglong(long long value) { 533 | char buf[32], *p; 534 | unsigned long long v; 535 | 536 | v = (value < 0) ? -value : value; 537 | p = buf+31; /* point to the last character */ 538 | do { 539 | *p-- = '0'+(v%10); 540 | v /= 10; 541 | } while(v); 542 | if (value < 0) *p-- = '-'; 543 | p++; 544 | return sdsnewlen(p,32-(p-buf)); 545 | } 546 | 547 | /* Append to the sds string "s" an escaped string representation where 548 | * all the non-printable characters (tested with isprint()) are turned into 549 | * escapes in the form "\n\r\a...." or "\x". 550 | * 551 | * After the call, the modified sds string is no longer valid and all the 552 | * references must be substituted with the new pointer returned by the call. */ 553 | sds sdscatrepr(sds s, const char *p, size_t len) { 554 | s = sdscatlen(s,"\"",1); 555 | while(len--) { 556 | switch(*p) { 557 | case '\\': 558 | case '"': 559 | s = sdscatprintf(s,"\\%c",*p); 560 | break; 561 | case '\n': s = sdscatlen(s,"\\n",2); break; 562 | case '\r': s = sdscatlen(s,"\\r",2); break; 563 | case '\t': s = sdscatlen(s,"\\t",2); break; 564 | case '\a': s = sdscatlen(s,"\\a",2); break; 565 | case '\b': s = sdscatlen(s,"\\b",2); break; 566 | default: 567 | if (isprint(*p)) 568 | s = sdscatprintf(s,"%c",*p); 569 | else 570 | s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); 571 | break; 572 | } 573 | p++; 574 | } 575 | return sdscatlen(s,"\"",1); 576 | } 577 | 578 | /* Helper function for sdssplitargs() that returns non zero if 'c' 579 | * is a valid hex digit. */ 580 | int is_hex_digit(char c) { 581 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 582 | (c >= 'A' && c <= 'F'); 583 | } 584 | 585 | /* Helper function for sdssplitargs() that converts an hex digit into an 586 | * integer from 0 to 15 */ 587 | int hex_digit_to_int(char c) { 588 | switch(c) { 589 | case '0': return 0; 590 | case '1': return 1; 591 | case '2': return 2; 592 | case '3': return 3; 593 | case '4': return 4; 594 | case '5': return 5; 595 | case '6': return 6; 596 | case '7': return 7; 597 | case '8': return 8; 598 | case '9': return 9; 599 | case 'a': case 'A': return 10; 600 | case 'b': case 'B': return 11; 601 | case 'c': case 'C': return 12; 602 | case 'd': case 'D': return 13; 603 | case 'e': case 'E': return 14; 604 | case 'f': case 'F': return 15; 605 | default: return 0; 606 | } 607 | } 608 | 609 | /* Split a line into arguments, where every argument can be in the 610 | * following programming-language REPL-alike form: 611 | * 612 | * foo bar "newline are supported\n" and "\xff\x00otherstuff" 613 | * 614 | * The number of arguments is stored into *argc, and an array 615 | * of sds is returned. 616 | * 617 | * The caller should free the resulting array of sds strings with 618 | * sdsfreesplitres(). 619 | * 620 | * Note that sdscatrepr() is able to convert back a string into 621 | * a quoted string in the same format sdssplitargs() is able to parse. 622 | * 623 | * The function returns the allocated tokens on success, even when the 624 | * input string is empty, or NULL if the input contains unbalanced 625 | * quotes or closed quotes followed by non space characters 626 | * as in: "foo"bar or "foo' 627 | */ 628 | sds *sdssplitargs(const char *line, int *argc) { 629 | const char *p = line; 630 | char *current = NULL; 631 | char **vector = NULL; 632 | 633 | *argc = 0; 634 | while(1) { 635 | /* skip blanks */ 636 | while(*p && isspace(*p)) p++; 637 | if (*p) { 638 | /* get a token */ 639 | int inq=0; /* set to 1 if we are in "quotes" */ 640 | int insq=0; /* set to 1 if we are in 'single quotes' */ 641 | int done=0; 642 | 643 | if (current == NULL) current = sdsempty(); 644 | while(!done) { 645 | if (inq) { 646 | if (*p == '\\' && *(p+1) == 'x' && 647 | is_hex_digit(*(p+2)) && 648 | is_hex_digit(*(p+3))) 649 | { 650 | unsigned char byte; 651 | 652 | byte = (hex_digit_to_int(*(p+2))*16)+ 653 | hex_digit_to_int(*(p+3)); 654 | current = sdscatlen(current,(char*)&byte,1); 655 | p += 3; 656 | } else if (*p == '\\' && *(p+1)) { 657 | char c; 658 | 659 | p++; 660 | switch(*p) { 661 | case 'n': c = '\n'; break; 662 | case 'r': c = '\r'; break; 663 | case 't': c = '\t'; break; 664 | case 'b': c = '\b'; break; 665 | case 'a': c = '\a'; break; 666 | default: c = *p; break; 667 | } 668 | current = sdscatlen(current,&c,1); 669 | } else if (*p == '"') { 670 | /* closing quote must be followed by a space or 671 | * nothing at all. */ 672 | if (*(p+1) && !isspace(*(p+1))) goto err; 673 | done=1; 674 | } else if (!*p) { 675 | /* unterminated quotes */ 676 | goto err; 677 | } else { 678 | current = sdscatlen(current,p,1); 679 | } 680 | } else if (insq) { 681 | if (*p == '\\' && *(p+1) == '\'') { 682 | p++; 683 | current = sdscatlen(current,"'",1); 684 | } else if (*p == '\'') { 685 | /* closing quote must be followed by a space or 686 | * nothing at all. */ 687 | if (*(p+1) && !isspace(*(p+1))) goto err; 688 | done=1; 689 | } else if (!*p) { 690 | /* unterminated quotes */ 691 | goto err; 692 | } else { 693 | current = sdscatlen(current,p,1); 694 | } 695 | } else { 696 | switch(*p) { 697 | case ' ': 698 | case '\n': 699 | case '\r': 700 | case '\t': 701 | case '\0': 702 | done=1; 703 | break; 704 | case '"': 705 | inq=1; 706 | break; 707 | case '\'': 708 | insq=1; 709 | break; 710 | default: 711 | current = sdscatlen(current,p,1); 712 | break; 713 | } 714 | } 715 | if (*p) p++; 716 | } 717 | /* add the token to the vector */ 718 | vector = realloc(vector,((*argc)+1)*sizeof(char*)); 719 | vector[*argc] = current; 720 | (*argc)++; 721 | current = NULL; 722 | } else { 723 | /* Even on empty input string return something not NULL. */ 724 | if (vector == NULL) vector = malloc(sizeof(void*)); 725 | return vector; 726 | } 727 | } 728 | 729 | err: 730 | while((*argc)--) 731 | sdsfree(vector[*argc]); 732 | free(vector); 733 | if (current) sdsfree(current); 734 | *argc = 0; 735 | return NULL; 736 | } 737 | 738 | /* Modify the string substituting all the occurrences of the set of 739 | * characters specified in the 'from' string to the corresponding character 740 | * in the 'to' array. 741 | * 742 | * For instance: sdsmapchars(mystring, "ho", "01", 2) 743 | * will have the effect of turning the string "hello" into "0ell1". 744 | * 745 | * The function returns the sds string pointer, that is always the same 746 | * as the input pointer since no resize is needed. */ 747 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { 748 | size_t j, i, l = sdslen(s); 749 | 750 | for (j = 0; j < l; j++) { 751 | for (i = 0; i < setlen; i++) { 752 | if (s[j] == from[i]) { 753 | s[j] = to[i]; 754 | break; 755 | } 756 | } 757 | } 758 | return s; 759 | } 760 | 761 | /* Join an array of C strings using the specified separator (also a C string). 762 | * Returns the result as an sds string. */ 763 | sds sdsjoin(char **argv, int argc, char *sep) { 764 | sds join = sdsempty(); 765 | int j; 766 | 767 | for (j = 0; j < argc; j++) { 768 | join = sdscat(join, argv[j]); 769 | if (j != argc-1) join = sdscat(join,sep); 770 | } 771 | return join; 772 | } 773 | 774 | #ifdef SDS_TEST_MAIN 775 | #include 776 | #include "testhelp.h" 777 | 778 | int main(void) { 779 | { 780 | struct sdshdr *sh; 781 | sds x = sdsnew("foo"), y; 782 | 783 | test_cond("Create a string and obtain the length", 784 | sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) 785 | 786 | sdsfree(x); 787 | x = sdsnewlen("foo",2); 788 | test_cond("Create a string with specified length", 789 | sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) 790 | 791 | x = sdscat(x,"bar"); 792 | test_cond("Strings concatenation", 793 | sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); 794 | 795 | x = sdscpy(x,"a"); 796 | test_cond("sdscpy() against an originally longer string", 797 | sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) 798 | 799 | x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); 800 | test_cond("sdscpy() against an originally shorter string", 801 | sdslen(x) == 33 && 802 | memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) 803 | 804 | sdsfree(x); 805 | x = sdscatprintf(sdsempty(),"%d",123); 806 | test_cond("sdscatprintf() seems working in the base case", 807 | sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) 808 | 809 | sdsfree(x); 810 | x = sdstrim(sdsnew("xxciaoyyy"),"xy"); 811 | test_cond("sdstrim() correctly trims characters", 812 | sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) 813 | 814 | y = sdsrange(sdsdup(x),1,1); 815 | test_cond("sdsrange(...,1,1)", 816 | sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) 817 | 818 | sdsfree(y); 819 | y = sdsrange(sdsdup(x),1,-1); 820 | test_cond("sdsrange(...,1,-1)", 821 | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 822 | 823 | sdsfree(y); 824 | y = sdsrange(sdsdup(x),-2,-1); 825 | test_cond("sdsrange(...,-2,-1)", 826 | sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) 827 | 828 | sdsfree(y); 829 | y = sdsrange(sdsdup(x),2,1); 830 | test_cond("sdsrange(...,2,1)", 831 | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 832 | 833 | sdsfree(y); 834 | y = sdsrange(sdsdup(x),1,100); 835 | test_cond("sdsrange(...,1,100)", 836 | sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) 837 | 838 | sdsfree(y); 839 | y = sdsrange(sdsdup(x),100,100); 840 | test_cond("sdsrange(...,100,100)", 841 | sdslen(y) == 0 && memcmp(y,"\0",1) == 0) 842 | 843 | sdsfree(y); 844 | sdsfree(x); 845 | x = sdsnew("foo"); 846 | y = sdsnew("foa"); 847 | test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) 848 | 849 | sdsfree(y); 850 | sdsfree(x); 851 | x = sdsnew("bar"); 852 | y = sdsnew("bar"); 853 | test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) 854 | 855 | sdsfree(y); 856 | sdsfree(x); 857 | x = sdsnew("aar"); 858 | y = sdsnew("bar"); 859 | test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) 860 | 861 | { 862 | int oldfree; 863 | 864 | sdsfree(x); 865 | x = sdsnew("0"); 866 | sh = (void*) (x-(sizeof(struct sdshdr))); 867 | test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0); 868 | x = sdsMakeRoomFor(x,1); 869 | sh = (void*) (x-(sizeof(struct sdshdr))); 870 | test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0); 871 | oldfree = sh->free; 872 | x[1] = '1'; 873 | sdsIncrLen(x,1); 874 | test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1'); 875 | test_cond("sdsIncrLen() -- len", sh->len == 2); 876 | test_cond("sdsIncrLen() -- free", sh->free == oldfree-1); 877 | } 878 | } 879 | test_report() 880 | return 0; 881 | } 882 | #endif 883 | -------------------------------------------------------------------------------- /src/util/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __SDS_H 32 | #define __SDS_H 33 | 34 | #define SDS_MAX_PREALLOC (1024*1024) 35 | 36 | #include 37 | #include 38 | 39 | typedef char *sds; 40 | 41 | struct sdshdr { 42 | int len; 43 | int free; 44 | char buf[]; 45 | }; 46 | 47 | static inline size_t sdslen(const sds s) { 48 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 49 | return sh->len; 50 | } 51 | 52 | static inline size_t sdsavail(const sds s) { 53 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 54 | return sh->free; 55 | } 56 | 57 | sds sdsnewlen(const void *init, size_t initlen); 58 | sds sdsnew(const char *init); 59 | sds sdsempty(void); 60 | size_t sdslen(const sds s); 61 | sds sdsdup(const sds s); 62 | void sdsfree(sds s); 63 | size_t sdsavail(const sds s); 64 | sds sdsgrowzero(sds s, size_t len); 65 | sds sdscatlen(sds s, const void *t, size_t len); 66 | sds sdscat(sds s, const char *t); 67 | sds sdscatsds(sds s, const sds t); 68 | sds sdscpylen(sds s, const char *t, size_t len); 69 | sds sdscpy(sds s, const char *t); 70 | 71 | sds sdscatvprintf(sds s, const char *fmt, va_list ap); 72 | #ifdef __GNUC__ 73 | sds sdscatprintf(sds s, const char *fmt, ...) 74 | __attribute__((format(printf, 2, 3))); 75 | #else 76 | sds sdscatprintf(sds s, const char *fmt, ...); 77 | #endif 78 | 79 | sds sdstrim(sds s, const char *cset); 80 | void sdsrange(sds s, int start, int end); 81 | void sdsupdatelen(sds s); 82 | void sdsclear(sds s); 83 | int sdscmp(const sds s1, const sds s2); 84 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); 85 | void sdsfreesplitres(sds *tokens, int count); 86 | void sdstolower(sds s); 87 | void sdstoupper(sds s); 88 | sds sdsfromlonglong(long long value); 89 | sds sdscatrepr(sds s, const char *p, size_t len); 90 | sds *sdssplitargs(const char *line, int *argc); 91 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); 92 | sds sdsjoin(char **argv, int argc, char *sep); 93 | 94 | /* Low level functions exposed to the user API */ 95 | sds sdsMakeRoomFor(sds s, size_t addlen); 96 | void sdsIncrLen(sds s, int incr); 97 | sds sdsRemoveFreeSpace(sds s); 98 | size_t sdsAllocSize(sds s); 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /test/script.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Copyright (c) 2015 Pagoda Box Inc 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public License, v. 6 | # 2.0. If a copy of the MPL was not distributed with this file, You can obtain one 7 | # at http://mozilla.org/MPL/2.0/. 8 | # 9 | 10 | host = "red.local" 11 | port = 4470 12 | verbose = false 13 | 14 | max_vxlans = 100 15 | simnets = 1000 16 | members = 1000 17 | 18 | def create_random_mac 19 | (1..6).map{"%0.2x"%rand(256)}.join(":") 20 | end 21 | 22 | def create_random_host 23 | (1..4).map{"%d"%(rand(254) + 1)}.join(".") 24 | end 25 | 26 | vxlans = {} 27 | 28 | simnets.times do |sim_num| 29 | vxlan = rand(max_vxlans) + 1 30 | simnet = "simnet#{sim_num}" 31 | mac = create_random_mac 32 | if vxlans[vxlan].nil? 33 | vxlans[vxlan] = {devices: [], members: []} 34 | end 35 | vxlans[vxlan][:devices] << {mac: mac, simnet: simnet} 36 | output = `src/vxadm -h #{host} -p #{port} add-device -v #{vxlan} -m #{mac} #{simnet}` 37 | puts "src/vxadm -h #{host} -p #{port} add-device -v #{vxlan} -m #{mac} #{simnet}" if verbose 38 | puts output if verbose 39 | end 40 | 41 | members.times do |num| 42 | vxlan = 0 43 | begin 44 | vxlan = rand(max_vxlans) + 1 45 | end while vxlans[vxlan].nil? 46 | vxlans[vxlan] 47 | mac = create_random_mac 48 | mem_host = create_random_host 49 | output = `src/vxadm -h #{host} -p #{port} add-member -v #{vxlan} -h #{mem_host} #{mac}` 50 | puts "src/vxadm -h #{host} -p #{port} add-member -v #{vxlan} -h #{mem_host} #{mac}" if verbose 51 | puts output if verbose 52 | end 53 | 54 | # simnets.times do |sim_num| 55 | # simnet = "simnet#{sim_num}" 56 | # output = `src/vxadm -h #{host} -p #{port} remove-device #{simnet}` 57 | # puts "src/vxadm -h #{host} -p #{port} remove-device #{simnet}" if verbose 58 | # puts output if verbose 59 | # end --------------------------------------------------------------------------------