1 /* 2 * Copyright (C) 2026 Jo-Philipp Wich <jo@mein.io> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ANY IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 #include <errno.h> 21 22 #include "ucode/util.h" 23 #include "debug_proto.h" 24 25 #define DEBUG_PROTO_READ_CHUNK 1024 26 27 /* Append a NUL-terminated string; like printbuf_strappend() but for 28 * non-literal strings (printbuf_memappend_fast() compares its size 29 * argument against the buffer's int fields, hence the cast). */ 30 static void 31 printbuf_strcat(uc_stringbuf_t *sb, const char *s) 32 { 33 printbuf_memappend_fast(sb, s, (int)strlen(s)); 34 } 35 36 void 37 debug_proto_buf_init(debug_proto_buf_t *buf) 38 { 39 buf->data = NULL; 40 buf->len = 0; 41 buf->cap = 0; 42 } 43 44 void 45 debug_proto_buf_free(debug_proto_buf_t *buf) 46 { 47 free(buf->data); 48 buf->data = NULL; 49 buf->len = 0; 50 buf->cap = 0; 51 } 52 53 void 54 debug_proto_write(int fd, uc_vm_t *vm, const char *verb, uc_value_t *payload) 55 { 56 uc_stringbuf_t *sb = xprintbuf_new(); 57 const char *p; 58 size_t remaining; 59 ssize_t n; 60 char *json; 61 62 printbuf_strcat(sb, verb); 63 64 if (payload) { 65 json = ucv_to_jsonstring(vm, payload); 66 67 if (json) { 68 printbuf_memappend_fast(sb, " ", 1); 69 printbuf_strcat(sb, json); 70 free(json); 71 } 72 } 73 74 printbuf_memappend_fast(sb, "\n", 1); 75 76 p = sb->buf; 77 remaining = (size_t)printbuf_length(sb); 78 79 while (remaining > 0) { 80 n = write(fd, p, remaining); 81 82 if (n < 0) { 83 if (errno == EINTR) 84 continue; 85 86 break; 87 } 88 89 p += n; 90 remaining -= (size_t)n; 91 } 92 93 printbuf_free(sb); 94 } 95 96 /* Append `len` bytes to the tail of buf, growing its backing storage as 97 * needed. Returns false on allocation failure (buf is left unchanged). */ 98 static bool 99 buf_append(debug_proto_buf_t *buf, const char *data, size_t len) 100 { 101 char *newdata; 102 size_t newcap; 103 104 if (buf->len + len > buf->cap) { 105 newcap = buf->cap ? buf->cap : DEBUG_PROTO_READ_CHUNK; 106 107 while (newcap < buf->len + len) 108 newcap *= 2; 109 110 newdata = realloc(buf->data, newcap); 111 112 if (!newdata) 113 return false; 114 115 buf->data = newdata; 116 buf->cap = newcap; 117 } 118 119 memcpy(buf->data + buf->len, data, len); 120 buf->len += len; 121 122 return true; 123 } 124 125 /* Drop the first `n` bytes already consumed as a message from the front of 126 * buf, shifting any remaining buffered bytes down. */ 127 static void 128 buf_consume(debug_proto_buf_t *buf, size_t n) 129 { 130 memmove(buf->data, buf->data + n, buf->len - n); 131 buf->len -= n; 132 } 133 134 int 135 debug_proto_read(int fd, debug_proto_buf_t *buf, uc_vm_t *vm, 136 char **verb_out, uc_value_t **payload_out) 137 { 138 char chunk[DEBUG_PROTO_READ_CHUNK]; 139 char *line, *sp, *verb; 140 struct json_tokener *tok; 141 json_object *jso; 142 size_t line_len, verb_len, json_len; 143 ssize_t n; 144 145 *verb_out = NULL; 146 *payload_out = NULL; 147 148 for (;;) { 149 char *nl = memchr(buf->data, '\n', buf->len); 150 151 if (nl) { 152 line_len = (size_t)(nl - buf->data); 153 break; 154 } 155 156 n = read(fd, chunk, sizeof(chunk)); 157 158 if (n < 0) { 159 if (errno == EINTR) 160 continue; 161 162 return -1; 163 } 164 165 if (n == 0) 166 return 0; 167 168 if (!buf_append(buf, chunk, (size_t)n)) 169 return -1; 170 } 171 172 line = malloc(line_len + 1); 173 174 if (!line) 175 return -1; 176 177 memcpy(line, buf->data, line_len); 178 line[line_len] = '\0'; 179 buf_consume(buf, line_len + 1); 180 181 if (line_len > 0 && line[line_len - 1] == '\r') 182 line[--line_len] = '\0'; 183 184 sp = memchr(line, ' ', line_len); 185 verb_len = sp ? (size_t)(sp - line) : line_len; 186 187 verb = malloc(verb_len + 1); 188 189 if (!verb) { 190 free(line); 191 192 return -1; 193 } 194 195 memcpy(verb, line, verb_len); 196 verb[verb_len] = '\0'; 197 198 json_len = sp ? line_len - verb_len - 1 : 0; 199 200 if (json_len > 0) { 201 tok = xjs_new_tokener(); 202 203 /* len + 1 to include the trailing NUL: works around json-c 204 * treating a lone atomic value (e.g. `true`) as incomplete 205 * without a following delimiter, see json-c issue #681. */ 206 jso = json_tokener_parse_ex(tok, sp + 1, (int)json_len + 1); 207 208 if (json_tokener_get_error(tok) != json_tokener_success) { 209 json_tokener_free(tok); 210 json_object_put(jso); 211 free(verb); 212 free(line); 213 214 return -2; 215 } 216 217 *payload_out = ucv_from_json(vm, jso); 218 219 json_tokener_free(tok); 220 json_object_put(jso); 221 } 222 223 free(line); 224 *verb_out = verb; 225 226 return 1; 227 } 228
This page was automatically generated by LXR 0.3.1. • OpenWrt