/src/haproxy/src/stats-json.c
Line | Count | Source |
1 | | #include <haproxy/stats-json.h> |
2 | | |
3 | | #include <stdio.h> |
4 | | |
5 | | #include <haproxy/applet.h> |
6 | | #include <haproxy/buf.h> |
7 | | #include <haproxy/chunk.h> |
8 | | #include <haproxy/stats.h> |
9 | | |
10 | | /* Emits an encoding of the field type as JSON. |
11 | | * Returns non-zero on success, 0 if the buffer is full. |
12 | | */ |
13 | | static int stats_emit_json_field_tags(struct buffer *out, const struct field *f) |
14 | 0 | { |
15 | 0 | const char *origin, *nature, *scope; |
16 | 0 | int old_len; |
17 | |
|
18 | 0 | switch (field_origin(f, 0)) { |
19 | 0 | case FO_METRIC: origin = "Metric"; break; |
20 | 0 | case FO_STATUS: origin = "Status"; break; |
21 | 0 | case FO_KEY: origin = "Key"; break; |
22 | 0 | case FO_CONFIG: origin = "Config"; break; |
23 | 0 | case FO_PRODUCT: origin = "Product"; break; |
24 | 0 | default: origin = "Unknown"; break; |
25 | 0 | } |
26 | | |
27 | 0 | switch (field_nature(f, 0)) { |
28 | 0 | case FN_GAUGE: nature = "Gauge"; break; |
29 | 0 | case FN_LIMIT: nature = "Limit"; break; |
30 | 0 | case FN_MIN: nature = "Min"; break; |
31 | 0 | case FN_MAX: nature = "Max"; break; |
32 | 0 | case FN_RATE: nature = "Rate"; break; |
33 | 0 | case FN_COUNTER: nature = "Counter"; break; |
34 | 0 | case FN_DURATION: nature = "Duration"; break; |
35 | 0 | case FN_AGE: nature = "Age"; break; |
36 | 0 | case FN_TIME: nature = "Time"; break; |
37 | 0 | case FN_NAME: nature = "Name"; break; |
38 | 0 | case FN_OUTPUT: nature = "Output"; break; |
39 | 0 | case FN_AVG: nature = "Avg"; break; |
40 | 0 | default: nature = "Unknown"; break; |
41 | 0 | } |
42 | | |
43 | 0 | switch (field_scope(f, 0)) { |
44 | 0 | case FS_PROCESS: scope = "Process"; break; |
45 | 0 | case FS_SERVICE: scope = "Service"; break; |
46 | 0 | case FS_SYSTEM: scope = "System"; break; |
47 | 0 | case FS_CLUSTER: scope = "Cluster"; break; |
48 | 0 | default: scope = "Unknown"; break; |
49 | 0 | } |
50 | | |
51 | 0 | old_len = out->data; |
52 | 0 | chunk_appendf(out, "\"tags\":{" |
53 | 0 | "\"origin\":\"%s\"," |
54 | 0 | "\"nature\":\"%s\"," |
55 | 0 | "\"scope\":\"%s\"" |
56 | 0 | "}", origin, nature, scope); |
57 | 0 | return !(old_len == out->data); |
58 | 0 | } |
59 | | |
60 | | /* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per |
61 | | * the recommendation for interoperable integers in section 6 of RFC 7159. |
62 | | */ |
63 | 0 | #define JSON_INT_MAX ((1LL << 53) - 1) |
64 | 0 | #define JSON_INT_MIN (0 - JSON_INT_MAX) |
65 | | |
66 | | /* Emits a stats field value and its type in JSON. |
67 | | * Returns non-zero on success, 0 on error. |
68 | | */ |
69 | | static int stats_emit_json_data_field(struct buffer *out, const struct field *f) |
70 | 0 | { |
71 | 0 | int old_len; |
72 | 0 | char buf[20]; |
73 | 0 | const char *type, *value = buf, *quote = ""; |
74 | |
|
75 | 0 | switch (field_format(f, 0)) { |
76 | 0 | case FF_EMPTY: return 1; |
77 | 0 | case FF_S32: type = "\"s32\""; |
78 | 0 | snprintf(buf, sizeof(buf), "%d", f->u.s32); |
79 | 0 | break; |
80 | 0 | case FF_U32: type = "\"u32\""; |
81 | 0 | snprintf(buf, sizeof(buf), "%u", f->u.u32); |
82 | 0 | break; |
83 | 0 | case FF_S64: type = "\"s64\""; |
84 | 0 | if (f->u.s64 < JSON_INT_MIN || f->u.s64 > JSON_INT_MAX) |
85 | 0 | return 0; |
86 | 0 | type = "\"s64\""; |
87 | 0 | snprintf(buf, sizeof(buf), "%lld", (long long)f->u.s64); |
88 | 0 | break; |
89 | 0 | case FF_U64: if (f->u.u64 > JSON_INT_MAX) |
90 | 0 | return 0; |
91 | 0 | type = "\"u64\""; |
92 | 0 | snprintf(buf, sizeof(buf), "%llu", |
93 | 0 | (unsigned long long) f->u.u64); |
94 | 0 | break; |
95 | 0 | case FF_FLT: type = "\"flt\""; |
96 | 0 | flt_trim(buf, 0, snprintf(buf, sizeof(buf), "%f", f->u.flt)); |
97 | 0 | break; |
98 | 0 | case FF_STR: type = "\"str\""; |
99 | 0 | value = field_str(f, 0); |
100 | 0 | quote = "\""; |
101 | 0 | break; |
102 | 0 | default: snprintf(buf, sizeof(buf), "%u", f->type); |
103 | 0 | type = buf; |
104 | 0 | value = "unknown"; |
105 | 0 | quote = "\""; |
106 | 0 | break; |
107 | 0 | } |
108 | | |
109 | 0 | old_len = out->data; |
110 | 0 | chunk_appendf(out, ",\"value\":{\"type\":%s,\"value\":%s%s%s}", |
111 | 0 | type, quote, value, quote); |
112 | 0 | return !(old_len == out->data); |
113 | 0 | } |
114 | | |
115 | | static void stats_print_proxy_field_json(struct buffer *out, |
116 | | const struct field *stat, |
117 | | const char *name, |
118 | | int pos, |
119 | | uint32_t field_type, |
120 | | uint32_t iid, |
121 | | uint32_t sid, |
122 | | uint32_t pid) |
123 | 0 | { |
124 | 0 | const char *obj_type; |
125 | 0 | switch (field_type) { |
126 | 0 | case STATS_TYPE_FE: obj_type = "Frontend"; break; |
127 | 0 | case STATS_TYPE_BE: obj_type = "Backend"; break; |
128 | 0 | case STATS_TYPE_SO: obj_type = "Listener"; break; |
129 | 0 | case STATS_TYPE_SV: obj_type = "Server"; break; |
130 | 0 | default: obj_type = "Unknown"; break; |
131 | 0 | } |
132 | | |
133 | 0 | chunk_appendf(out, |
134 | 0 | "{" |
135 | 0 | "\"objType\":\"%s\"," |
136 | 0 | "\"proxyId\":%u," |
137 | 0 | "\"id\":%u," |
138 | 0 | "\"field\":{\"pos\":%d,\"name\":\"%s\"}," |
139 | 0 | "\"processNum\":%u,", |
140 | 0 | obj_type, iid, sid, pos, name, pid); |
141 | 0 | } |
142 | | |
143 | | static void stats_print_rslv_field_json(struct buffer *out, |
144 | | const struct field *stat, |
145 | | const char *name, |
146 | | int pos) |
147 | 0 | { |
148 | 0 | chunk_appendf(out, |
149 | 0 | "{" |
150 | 0 | "\"field\":{\"pos\":%d,\"name\":\"%s\"},", |
151 | 0 | pos, name); |
152 | 0 | } |
153 | | |
154 | | |
155 | | /* Dumps the stats JSON header to <out> buffer. The caller is responsible for |
156 | | * clearing it if needed. |
157 | | */ |
158 | | void stats_dump_json_header(struct buffer *out) |
159 | 0 | { |
160 | 0 | chunk_strcat(out, "["); |
161 | 0 | } |
162 | | |
163 | | /* Dump all fields from <line> into <out> using a typed "field:desc:type:value" format */ |
164 | | int stats_dump_fields_json(struct buffer *out, |
165 | | const struct field *line, size_t stats_count, |
166 | | struct show_stat_ctx *ctx) |
167 | 0 | { |
168 | 0 | int flags = ctx->flags; |
169 | 0 | int domain = ctx->domain; |
170 | 0 | int started = (ctx->field) ? 1 : 0; |
171 | 0 | int ready_data = 0; |
172 | |
|
173 | 0 | if (!started && (flags & STAT_F_STARTED) && !chunk_strcat(out, ",")) |
174 | 0 | return 0; |
175 | 0 | if (!started && !chunk_strcat(out, "[")) |
176 | 0 | return 0; |
177 | | |
178 | 0 | for (; ctx->field < stats_count; ctx->field++) { |
179 | 0 | int old_len; |
180 | 0 | int i = ctx->field; |
181 | |
|
182 | 0 | if (!line[i].type) |
183 | 0 | continue; |
184 | | |
185 | 0 | if (started && !chunk_strcat(out, ",")) |
186 | 0 | goto err; |
187 | 0 | started = 1; |
188 | |
|
189 | 0 | old_len = out->data; |
190 | 0 | if (domain == STATS_DOMAIN_PROXY) { |
191 | 0 | stats_print_proxy_field_json(out, &line[i], |
192 | 0 | stat_cols[domain][i].name, |
193 | 0 | i, |
194 | 0 | line[ST_I_PX_TYPE].u.u32, |
195 | 0 | line[ST_I_PX_IID].u.u32, |
196 | 0 | line[ST_I_PX_SID].u.u32, |
197 | 0 | line[ST_I_PX_PID].u.u32); |
198 | 0 | } else if (domain == STATS_DOMAIN_RESOLVERS) { |
199 | 0 | stats_print_rslv_field_json(out, &line[i], |
200 | 0 | stat_cols[domain][i].name, |
201 | 0 | i); |
202 | 0 | } |
203 | |
|
204 | 0 | if (old_len == out->data) |
205 | 0 | goto err; |
206 | | |
207 | 0 | if (!stats_emit_json_field_tags(out, &line[i])) |
208 | 0 | goto err; |
209 | | |
210 | 0 | if (!stats_emit_json_data_field(out, &line[i])) |
211 | 0 | goto err; |
212 | | |
213 | 0 | if (!chunk_strcat(out, "}")) |
214 | 0 | goto err; |
215 | 0 | ready_data = out->data; |
216 | 0 | } |
217 | | |
218 | 0 | if (!chunk_strcat(out, "]")) |
219 | 0 | goto err; |
220 | | |
221 | 0 | ctx->field = 0; /* we're done */ |
222 | 0 | return 1; |
223 | | |
224 | 0 | err: |
225 | 0 | if (!ready_data) { |
226 | | /* not enough buffer space for a single entry.. */ |
227 | 0 | chunk_reset(out); |
228 | 0 | if (ctx->flags & STAT_F_STARTED) |
229 | 0 | chunk_strcat(out, ","); |
230 | 0 | chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}"); |
231 | 0 | return 0; /* hard error */ |
232 | 0 | } |
233 | | /* push ready data and wait for a new buffer to complete the dump */ |
234 | 0 | out->data = ready_data; |
235 | 0 | return 1; |
236 | 0 | } |
237 | | |
238 | | /* Dumps the JSON stats trailer block to <out> buffer. The caller is |
239 | | * responsible for clearing it if needed. |
240 | | */ |
241 | | void stats_dump_json_end(struct buffer *out) |
242 | 0 | { |
243 | 0 | chunk_strcat(out, "]\n"); |
244 | 0 | } |
245 | | |
246 | | /* Dump all fields from <stats> into <out> using the "show info json" format */ |
247 | | int stats_dump_json_info_fields(struct buffer *out, |
248 | | const struct field *info, |
249 | | struct show_stat_ctx *ctx) |
250 | 0 | { |
251 | 0 | int started = (ctx->field) ? 1 : 0; |
252 | 0 | int ready_data = 0; |
253 | |
|
254 | 0 | if (!started && !chunk_strcat(out, "[")) |
255 | 0 | return 0; |
256 | | |
257 | 0 | for (; ctx->field < ST_I_INF_MAX; ctx->field++) { |
258 | 0 | int old_len; |
259 | 0 | int i = ctx->field; |
260 | |
|
261 | 0 | if (!field_format(info, i)) |
262 | 0 | continue; |
263 | | |
264 | 0 | if (started && !chunk_strcat(out, ",")) |
265 | 0 | goto err; |
266 | 0 | started = 1; |
267 | |
|
268 | 0 | old_len = out->data; |
269 | 0 | chunk_appendf(out, |
270 | 0 | "{\"field\":{\"pos\":%d,\"name\":\"%s\"}," |
271 | 0 | "\"processNum\":%u,", |
272 | 0 | i, stat_cols_info[i].name, |
273 | 0 | info[ST_I_INF_PROCESS_NUM].u.u32); |
274 | 0 | if (old_len == out->data) |
275 | 0 | goto err; |
276 | | |
277 | 0 | if (!stats_emit_json_field_tags(out, &info[i])) |
278 | 0 | goto err; |
279 | | |
280 | 0 | if (!stats_emit_json_data_field(out, &info[i])) |
281 | 0 | goto err; |
282 | | |
283 | 0 | if (!chunk_strcat(out, "}")) |
284 | 0 | goto err; |
285 | 0 | ready_data = out->data; |
286 | 0 | } |
287 | | |
288 | 0 | if (!chunk_strcat(out, "]\n")) |
289 | 0 | goto err; |
290 | 0 | ctx->field = 0; /* we're done */ |
291 | 0 | return 1; |
292 | | |
293 | 0 | err: |
294 | 0 | if (!ready_data) { |
295 | | /* not enough buffer space for a single entry.. */ |
296 | 0 | chunk_reset(out); |
297 | 0 | chunk_appendf(out, "{\"errorStr\":\"output buffer too short\"}\n"); |
298 | 0 | return 0; /* hard error */ |
299 | 0 | } |
300 | | /* push ready data and wait for a new buffer to complete the dump */ |
301 | 0 | out->data = ready_data; |
302 | 0 | return 1; |
303 | 0 | } |
304 | | |
305 | | /* This function dumps the schema onto the stream connector's read buffer. |
306 | | * It returns 0 as long as it does not complete, non-zero upon completion. |
307 | | * No state is used. |
308 | | * |
309 | | * Integer values bounded to the range [-(2**53)+1, (2**53)-1] as |
310 | | * per the recommendation for interoperable integers in section 6 of RFC 7159. |
311 | | */ |
312 | | void stats_dump_json_schema(struct buffer *out) |
313 | 0 | { |
314 | |
|
315 | 0 | int old_len = out->data; |
316 | |
|
317 | 0 | chunk_strcat(out, |
318 | 0 | "{" |
319 | 0 | "\"$schema\":\"http://json-schema.org/draft-04/schema#\"," |
320 | 0 | "\"oneOf\":[" |
321 | 0 | "{" |
322 | 0 | "\"title\":\"Info\"," |
323 | 0 | "\"type\":\"array\"," |
324 | 0 | "\"items\":{" |
325 | 0 | "\"title\":\"InfoItem\"," |
326 | 0 | "\"type\":\"object\"," |
327 | 0 | "\"properties\":{" |
328 | 0 | "\"field\":{\"$ref\":\"#/definitions/field\"}," |
329 | 0 | "\"processNum\":{\"$ref\":\"#/definitions/processNum\"}," |
330 | 0 | "\"tags\":{\"$ref\":\"#/definitions/tags\"}," |
331 | 0 | "\"value\":{\"$ref\":\"#/definitions/typedValue\"}" |
332 | 0 | "}," |
333 | 0 | "\"required\":[\"field\",\"processNum\",\"tags\"," |
334 | 0 | "\"value\"]" |
335 | 0 | "}" |
336 | 0 | "}," |
337 | 0 | "{" |
338 | 0 | "\"title\":\"Stat\"," |
339 | 0 | "\"type\":\"array\"," |
340 | 0 | "\"items\":{" |
341 | 0 | "\"title\":\"InfoItem\"," |
342 | 0 | "\"type\":\"object\"," |
343 | 0 | "\"properties\":{" |
344 | 0 | "\"objType\":{" |
345 | 0 | "\"enum\":[\"Frontend\",\"Backend\",\"Listener\"," |
346 | 0 | "\"Server\",\"Unknown\"]" |
347 | 0 | "}," |
348 | 0 | "\"proxyId\":{" |
349 | 0 | "\"type\":\"integer\"," |
350 | 0 | "\"minimum\":0" |
351 | 0 | "}," |
352 | 0 | "\"id\":{" |
353 | 0 | "\"type\":\"integer\"," |
354 | 0 | "\"minimum\":0" |
355 | 0 | "}," |
356 | 0 | "\"field\":{\"$ref\":\"#/definitions/field\"}," |
357 | 0 | "\"processNum\":{\"$ref\":\"#/definitions/processNum\"}," |
358 | 0 | "\"tags\":{\"$ref\":\"#/definitions/tags\"}," |
359 | 0 | "\"typedValue\":{\"$ref\":\"#/definitions/typedValue\"}" |
360 | 0 | "}," |
361 | 0 | "\"required\":[\"objType\",\"proxyId\",\"id\"," |
362 | 0 | "\"field\",\"processNum\",\"tags\"," |
363 | 0 | "\"value\"]" |
364 | 0 | "}" |
365 | 0 | "}," |
366 | 0 | "{" |
367 | 0 | "\"title\":\"Error\"," |
368 | 0 | "\"type\":\"object\"," |
369 | 0 | "\"properties\":{" |
370 | 0 | "\"errorStr\":{" |
371 | 0 | "\"type\":\"string\"" |
372 | 0 | "}" |
373 | 0 | "}," |
374 | 0 | "\"required\":[\"errorStr\"]" |
375 | 0 | "}" |
376 | 0 | "]," |
377 | 0 | "\"definitions\":{" |
378 | 0 | "\"field\":{" |
379 | 0 | "\"type\":\"object\"," |
380 | 0 | "\"pos\":{" |
381 | 0 | "\"type\":\"integer\"," |
382 | 0 | "\"minimum\":0" |
383 | 0 | "}," |
384 | 0 | "\"name\":{" |
385 | 0 | "\"type\":\"string\"" |
386 | 0 | "}," |
387 | 0 | "\"required\":[\"pos\",\"name\"]" |
388 | 0 | "}," |
389 | 0 | "\"processNum\":{" |
390 | 0 | "\"type\":\"integer\"," |
391 | 0 | "\"minimum\":1" |
392 | 0 | "}," |
393 | 0 | "\"tags\":{" |
394 | 0 | "\"type\":\"object\"," |
395 | 0 | "\"origin\":{" |
396 | 0 | "\"type\":\"string\"," |
397 | 0 | "\"enum\":[\"Metric\",\"Status\",\"Key\"," |
398 | 0 | "\"Config\",\"Product\",\"Unknown\"]" |
399 | 0 | "}," |
400 | 0 | "\"nature\":{" |
401 | 0 | "\"type\":\"string\"," |
402 | 0 | "\"enum\":[\"Gauge\",\"Limit\",\"Min\",\"Max\"," |
403 | 0 | "\"Rate\",\"Counter\",\"Duration\"," |
404 | 0 | "\"Age\",\"Time\",\"Name\",\"Output\"," |
405 | 0 | "\"Avg\", \"Unknown\"]" |
406 | 0 | "}," |
407 | 0 | "\"scope\":{" |
408 | 0 | "\"type\":\"string\"," |
409 | 0 | "\"enum\":[\"Cluster\",\"Process\",\"Service\"," |
410 | 0 | "\"System\",\"Unknown\"]" |
411 | 0 | "}," |
412 | 0 | "\"required\":[\"origin\",\"nature\",\"scope\"]" |
413 | 0 | "}," |
414 | 0 | "\"typedValue\":{" |
415 | 0 | "\"type\":\"object\"," |
416 | 0 | "\"oneOf\":[" |
417 | 0 | "{\"$ref\":\"#/definitions/typedValue/definitions/s32Value\"}," |
418 | 0 | "{\"$ref\":\"#/definitions/typedValue/definitions/s64Value\"}," |
419 | 0 | "{\"$ref\":\"#/definitions/typedValue/definitions/u32Value\"}," |
420 | 0 | "{\"$ref\":\"#/definitions/typedValue/definitions/u64Value\"}," |
421 | 0 | "{\"$ref\":\"#/definitions/typedValue/definitions/strValue\"}" |
422 | 0 | "]," |
423 | 0 | "\"definitions\":{" |
424 | 0 | "\"s32Value\":{" |
425 | 0 | "\"properties\":{" |
426 | 0 | "\"type\":{" |
427 | 0 | "\"type\":\"string\"," |
428 | 0 | "\"enum\":[\"s32\"]" |
429 | 0 | "}," |
430 | 0 | "\"value\":{" |
431 | 0 | "\"type\":\"integer\"," |
432 | 0 | "\"minimum\":-2147483648," |
433 | 0 | "\"maximum\":2147483647" |
434 | 0 | "}" |
435 | 0 | "}," |
436 | 0 | "\"required\":[\"type\",\"value\"]" |
437 | 0 | "}," |
438 | 0 | "\"s64Value\":{" |
439 | 0 | "\"properties\":{" |
440 | 0 | "\"type\":{" |
441 | 0 | "\"type\":\"string\"," |
442 | 0 | "\"enum\":[\"s64\"]" |
443 | 0 | "}," |
444 | 0 | "\"value\":{" |
445 | 0 | "\"type\":\"integer\"," |
446 | 0 | "\"minimum\":-9007199254740991," |
447 | 0 | "\"maximum\":9007199254740991" |
448 | 0 | "}" |
449 | 0 | "}," |
450 | 0 | "\"required\":[\"type\",\"value\"]" |
451 | 0 | "}," |
452 | 0 | "\"u32Value\":{" |
453 | 0 | "\"properties\":{" |
454 | 0 | "\"type\":{" |
455 | 0 | "\"type\":\"string\"," |
456 | 0 | "\"enum\":[\"u32\"]" |
457 | 0 | "}," |
458 | 0 | "\"value\":{" |
459 | 0 | "\"type\":\"integer\"," |
460 | 0 | "\"minimum\":0," |
461 | 0 | "\"maximum\":4294967295" |
462 | 0 | "}" |
463 | 0 | "}," |
464 | 0 | "\"required\":[\"type\",\"value\"]" |
465 | 0 | "}," |
466 | 0 | "\"u64Value\":{" |
467 | 0 | "\"properties\":{" |
468 | 0 | "\"type\":{" |
469 | 0 | "\"type\":\"string\"," |
470 | 0 | "\"enum\":[\"u64\"]" |
471 | 0 | "}," |
472 | 0 | "\"value\":{" |
473 | 0 | "\"type\":\"integer\"," |
474 | 0 | "\"minimum\":0," |
475 | 0 | "\"maximum\":9007199254740991" |
476 | 0 | "}" |
477 | 0 | "}," |
478 | 0 | "\"required\":[\"type\",\"value\"]" |
479 | 0 | "}," |
480 | 0 | "\"strValue\":{" |
481 | 0 | "\"properties\":{" |
482 | 0 | "\"type\":{" |
483 | 0 | "\"type\":\"string\"," |
484 | 0 | "\"enum\":[\"str\"]" |
485 | 0 | "}," |
486 | 0 | "\"value\":{\"type\":\"string\"}" |
487 | 0 | "}," |
488 | 0 | "\"required\":[\"type\",\"value\"]" |
489 | 0 | "}," |
490 | 0 | "\"unknownValue\":{" |
491 | 0 | "\"properties\":{" |
492 | 0 | "\"type\":{" |
493 | 0 | "\"type\":\"integer\"," |
494 | 0 | "\"minimum\":0" |
495 | 0 | "}," |
496 | 0 | "\"value\":{" |
497 | 0 | "\"type\":\"string\"," |
498 | 0 | "\"enum\":[\"unknown\"]" |
499 | 0 | "}" |
500 | 0 | "}," |
501 | 0 | "\"required\":[\"type\",\"value\"]" |
502 | 0 | "}" |
503 | 0 | "}" |
504 | 0 | "}" |
505 | 0 | "}" |
506 | 0 | "}"); |
507 | |
|
508 | 0 | if (old_len == out->data) { |
509 | 0 | chunk_reset(out); |
510 | 0 | chunk_appendf(out, |
511 | 0 | "{\"errorStr\":\"output buffer too short\"}"); |
512 | 0 | } |
513 | 0 | chunk_appendf(out, "\n"); |
514 | 0 | } |
515 | | |
516 | | /* This function dumps the schema onto the stream connector's read buffer. |
517 | | * It returns 0 as long as it does not complete, non-zero upon completion. |
518 | | * No state is used. |
519 | | */ |
520 | | int stats_dump_json_schema_to_buffer(struct appctx *appctx) |
521 | 0 | { |
522 | 0 | struct show_stat_ctx *ctx = appctx->svcctx; |
523 | 0 | struct buffer *chk = &ctx->chunk; |
524 | |
|
525 | 0 | chunk_reset(chk); |
526 | |
|
527 | 0 | stats_dump_json_schema(chk); |
528 | |
|
529 | 0 | if (applet_putchk(appctx, chk) == -1) |
530 | 0 | return 0; |
531 | | |
532 | 0 | return 1; |
533 | 0 | } |