• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/debug_proto.c

  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