1 /*
2 * Copyright (c) 2022 Douglas Gilbert.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <ctype.h>
17
18 #include "sg_pr2serr.h"
19 #include "sg_json_builder.h"
20
21 /*
22 * Some users of sg_pr2serr may not need fixed and descriptor sense decoded
23 * for JSON output. If the following define is commented out the effective
24 * compile size of this file is reduced by 800 lines plus dependencies on
25 * other large components of the sg3_utils library.
26 * Comment out the next line to remove dependency on sg_lib.h and its code.
27 */
28 #define SG_PRSE_SENSE_DECODE 1
29
30 #ifdef SG_PRSE_SENSE_DECODE
31 #include "sg_lib.h"
32 #include "sg_lib_data.h"
33 #include "sg_unaligned.h"
34 #endif
35
36
37 #define sgj_opts_ev "SG3_UTILS_JSON_OPTS"
38
39 /*
40 * #define json_serialize_mode_multiline 0
41 * #define json_serialize_mode_single_line 1
42 * #define json_serialize_mode_packed 2
43 *
44 * #define json_serialize_opt_CRLF (1 << 1)
45 * #define json_serialize_opt_pack_brackets (1 << 2)
46 * #define json_serialize_opt_no_space_after_comma (1 << 3)
47 * #define json_serialize_opt_no_space_after_colon (1 << 4)
48 * #define json_serialize_opt_use_tabs (1 << 5)
49 */
50
51
52 static const json_serialize_opts def_out_settings = {
53 json_serialize_mode_multiline, /* one of serialize_mode_* */
54 0, /* serialize_opt_* OR-ed together */
55 4 /* indent size */
56 };
57
58 static int sgj_name_to_snake(const char * in, char * out, int maxlen_out);
59
60
61 /* Users of the sg_pr2serr.h header need this function definition */
62 int
pr2serr(const char * fmt,...)63 pr2serr(const char * fmt, ...)
64 {
65 va_list args;
66 int n;
67
68 va_start(args, fmt);
69 n = vfprintf(stderr, fmt, args);
70 va_end(args);
71 return n;
72 }
73
74 #ifndef SG_PRSE_SENSE_DECODE
75
76 /* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of
77 * functions. Returns number of chars placed in cp excluding the
78 * trailing null char. So for cp_max_len > 0 the return value is always
79 * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars are
80 * written to cp. Note this means that when cp_max_len = 1, this function
81 * assumes that cp[0] is the null character and does nothing (and returns
82 * 0). Linux kernel has a similar function called scnprintf(). Public
83 * declaration in sg_pr2serr.h header */
84 int
sg_scnpr(char * cp,int cp_max_len,const char * fmt,...)85 sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...)
86 {
87 va_list args;
88 int n;
89
90 if (cp_max_len < 2)
91 return 0;
92 va_start(args, fmt);
93 n = vsnprintf(cp, cp_max_len, fmt, args);
94 va_end(args);
95 return (n < cp_max_len) ? n : (cp_max_len - 1);
96 }
97
98 int
pr2ws(const char * fmt,...)99 pr2ws(const char * fmt, ...)
100 {
101 va_list args;
102 int n;
103
104 va_start(args, fmt);
105 n = vfprintf(stderr, fmt, args);
106 va_end(args);
107 return n;
108 }
109
110 #endif
111
112 static bool
sgj_parse_opts(sgj_state * jsp,const char * j_optarg)113 sgj_parse_opts(sgj_state * jsp, const char * j_optarg)
114 {
115 bool bad_arg = false;
116 bool prev_negate = false;
117 bool negate;
118 int k, c;
119
120 for (k = 0; j_optarg[k]; ++k) { /* step over leading whitespace */
121 if (! isspace(j_optarg[k]))
122 break;
123 }
124 for ( ; j_optarg[k]; ++k) {
125 c = j_optarg[k];
126 negate = false;
127 switch (c) {
128 case '=':
129 if (0 == k)
130 break; /* allow and ignore leading '=' */
131 bad_arg = true;
132 if (0 == jsp->first_bad_char)
133 jsp->first_bad_char = c;
134 break;
135 case '!':
136 case '~':
137 case '-': /* '-' is probably most practical negation symbol */
138 negate = true;
139 break;
140 case '0':
141 case '2':
142 jsp->pr_indent_size = 2;
143 break;
144 case '3':
145 jsp->pr_indent_size = 3;
146 break;
147 case '4':
148 jsp->pr_indent_size = 4;
149 break;
150 case '8':
151 jsp->pr_indent_size = 8;
152 break;
153 case 'e':
154 jsp->pr_exit_status = ! prev_negate;
155 break;
156 case 'g':
157 jsp->pr_format = 'g';
158 break;
159 case 'h':
160 jsp->pr_hex = ! prev_negate;
161 break;
162 case 'k':
163 jsp->pr_packed = ! prev_negate;
164 break;
165 case 'l':
166 jsp->pr_leadin = ! prev_negate;
167 break;
168 case 'n':
169 jsp->pr_name_ex = ! prev_negate;
170 break;
171 case 'o':
172 jsp->pr_out_hr = ! prev_negate;
173 break;
174 case 'p':
175 jsp->pr_pretty = ! prev_negate;
176 break;
177 case 's':
178 jsp->pr_string = ! prev_negate;
179 break;
180 case 'v':
181 ++jsp->verbose;
182 break;
183 case 'y':
184 jsp->pr_format = 'g';
185 break;
186 case '?':
187 bad_arg = true;
188 jsp->first_bad_char = '\0';
189 break;
190 default:
191 bad_arg = true;
192 if (0 == jsp->first_bad_char)
193 jsp->first_bad_char = c;
194 break;
195 }
196 prev_negate = negate ? ! prev_negate : false;
197 }
198 return ! bad_arg;
199 }
200
201 char *
sg_json_usage(int char_if_not_j,char * b,int blen)202 sg_json_usage(int char_if_not_j, char * b, int blen)
203 {
204 int n = 0;
205 char short_opt = char_if_not_j ? char_if_not_j : 'j';
206
207 if ((NULL == b) || (blen < 1))
208 goto fini;
209 n += sg_scnpr(b + n, blen - n, "JSON option usage:\n");
210 n += sg_scnpr(b + n, blen - n,
211 " --json[-JO] | -%c[JO]\n\n", short_opt);
212 n += sg_scnpr(b + n, blen - n, " where JO is a string of one or more "
213 "of:\n");
214 n += sg_scnpr(b + n, blen - n,
215 " 0 | 2 tab pretty output to 2 spaces\n");
216 n += sg_scnpr(b + n, blen - n,
217 " 4 tab pretty output to 4 spaces\n");
218 n += sg_scnpr(b + n, blen - n,
219 " 8 tab pretty output to 8 spaces\n");
220 if (n >= (blen - 1))
221 goto fini;
222 n += sg_scnpr(b + n, blen - n,
223 " e show 'exit_status' field\n");
224 n += sg_scnpr(b + n, blen - n,
225 " h show 'hex' fields\n");
226 n += sg_scnpr(b + n, blen - n,
227 " k packed, only non-pretty printed output\n");
228 n += sg_scnpr(b + n, blen - n,
229 " l show lead-in fields (invocation "
230 "information)\n");
231 n += sg_scnpr(b + n, blen - n,
232 " n show 'name_extra' information fields\n");
233 n += sg_scnpr(b + n, blen - n,
234 " o non-JSON output placed in 'output' array in "
235 "lead-in\n");
236 if (n >= (blen - 1))
237 goto fini;
238 n += sg_scnpr(b + n, blen - n,
239 " p pretty print the JSON output\n");
240 n += sg_scnpr(b + n, blen - n,
241 " s show string output (usually fields named "
242 "'meaning')\n");
243 n += sg_scnpr(b + n, blen - n,
244 " v make JSON output more verbose\n");
245 n += sg_scnpr(b + n, blen - n,
246 " = ignored if first character, else it's an "
247 "error\n");
248 n += sg_scnpr(b + n, blen - n,
249 " - | ~ | ! toggle next letter setting\n");
250
251 n += sg_scnpr(b + n, blen - n, "\nIn the absence of the optional JO "
252 "argument, the following are set\non: 'elps' while the "
253 "others are set off, and tabs are set to 4.\nBefore "
254 "command line JO options are applied, the environment\n"
255 "variable: %s is applied (if present). Note that\nno "
256 "space is permitted between the short option ('-%c') "
257 "and its\nargument ('JO').\n", sgj_opts_ev, short_opt);
258 fini:
259 return b;
260 }
261
262 char *
sg_json_settings(sgj_state * jsp,char * b,int blen)263 sg_json_settings(sgj_state * jsp, char * b, int blen)
264 {
265 snprintf(b, blen, "%d%se%sh%sk%sl%sn%so%sp%ss%sv", jsp->pr_indent_size,
266 jsp->pr_exit_status ? "" : "-", jsp->pr_hex ? "" : "-",
267 jsp->pr_packed ? "" : "-", jsp->pr_leadin ? "" : "-",
268 jsp->pr_name_ex ? "" : "-", jsp->pr_out_hr ? "" : "-",
269 jsp->pr_pretty ? "" : "-", jsp->pr_string ? "" : "-",
270 jsp->verbose ? "" : "-");
271 return b;
272 }
273
274 static void
sgj_def_opts(sgj_state * jsp)275 sgj_def_opts(sgj_state * jsp)
276 {
277 jsp->pr_as_json = true;
278 jsp->pr_exit_status = true;
279 jsp->pr_hex = false;
280 jsp->pr_leadin = true;
281 jsp->pr_out_hr = false;
282 jsp->pr_name_ex = false;
283 jsp->pr_packed = false; /* 'k' control character, needs '-p' */
284 jsp->pr_pretty = true;
285 jsp->pr_string = true;
286 jsp->pr_format = 0;
287 jsp->first_bad_char = 0;
288 jsp->verbose = 0;
289 jsp->pr_indent_size = 4;
290 }
291
292 bool
sgj_init_state(sgj_state * jsp,const char * j_optarg)293 sgj_init_state(sgj_state * jsp, const char * j_optarg)
294 {
295 const char * cp;
296
297 sgj_def_opts(jsp);
298 jsp->basep = NULL;
299 jsp->out_hrp = NULL;
300 jsp->userp = NULL;
301
302 cp = getenv(sgj_opts_ev);
303 if (cp) {
304 if (! sgj_parse_opts(jsp, cp)) {
305 pr2ws("error parsing %s environment variable, ignore\n",
306 sgj_opts_ev);
307 sgj_def_opts(jsp);
308 }
309 }
310 return j_optarg ? sgj_parse_opts(jsp, j_optarg) : true;
311 }
312
313 sgj_opaque_p
sgj_start_r(const char * util_name,const char * ver_str,int argc,char * argv[],sgj_state * jsp)314 sgj_start_r(const char * util_name, const char * ver_str, int argc,
315 char *argv[], sgj_state * jsp)
316 {
317 int k;
318 json_value * jvp = json_object_new(0);
319 json_value * jv2p = NULL;
320 json_value * jap = NULL;
321
322 if (NULL == jvp)
323 return NULL;
324 if (NULL == jsp)
325 return jvp;
326
327 jsp->basep = jvp;
328 if (jsp->pr_leadin) {
329 jap = json_array_new(0);
330 if (NULL == jap) {
331 json_builder_free((json_value *)jvp);
332 return NULL;
333 }
334 /* assume rest of json_*_new() calls succeed */
335 json_array_push((json_value *)jap, json_integer_new(1));
336 json_array_push((json_value *)jap, json_integer_new(0));
337 json_object_push((json_value *)jvp, "json_format_version",
338 (json_value *)jap);
339 if (util_name) {
340 jap = json_array_new(0);
341 if (argv) {
342 for (k = 0; k < argc; ++k)
343 json_array_push((json_value *)jap,
344 json_string_new(argv[k]));
345 }
346 jv2p = json_object_push((json_value *)jvp, "utility_invoked",
347 json_object_new(0));
348 json_object_push((json_value *)jv2p, "name",
349 json_string_new(util_name));
350 if (ver_str)
351 json_object_push((json_value *)jv2p, "version_date",
352 json_string_new(ver_str));
353 else
354 json_object_push((json_value *)jv2p, "version_date",
355 json_string_new("0.0"));
356 json_object_push((json_value *)jv2p, "argv", jap);
357 }
358 if (jsp->verbose) {
359 const char * cp = getenv(sgj_opts_ev);
360 char b[32];
361
362 json_object_push((json_value *)jv2p, "environment_variable_name",
363 json_string_new(sgj_opts_ev));
364 json_object_push((json_value *)jv2p, "environment_variable_value",
365 json_string_new(cp ? cp : "no available"));
366 sg_json_settings(jsp, b, sizeof(b));
367 json_object_push((json_value *)jv2p, "json_options",
368 json_string_new(b));
369 }
370 } else {
371 if (jsp->pr_out_hr && util_name)
372 jv2p = json_object_push((json_value *)jvp, "utility_invoked",
373 json_object_new(0));
374 }
375 if (jsp->pr_out_hr && jv2p) {
376 jsp->out_hrp = json_object_push((json_value *)jv2p, "output",
377 json_array_new(0));
378 if (jsp->pr_leadin && (jsp->verbose > 3)) {
379 char * bp = (char *)calloc(4096, 1);
380
381 if (bp) {
382 sg_json_usage(0, bp, 4096);
383 sgj_js_str_out(jsp, bp, strlen(bp));
384 free(bp);
385 }
386 }
387 }
388 return jvp;
389 }
390
391 void
sgj_js2file(sgj_state * jsp,sgj_opaque_p jop,int exit_status,FILE * fp)392 sgj_js2file(sgj_state * jsp, sgj_opaque_p jop, int exit_status, FILE * fp)
393 {
394 size_t len;
395 char * b;
396 json_value * jvp = (json_value *)(jop ? jop : jsp->basep);
397 json_serialize_opts out_settings;
398
399 if (NULL == jvp) {
400 fprintf(fp, "%s: all NULL pointers ??\n", __func__);
401 return;
402 }
403 if ((NULL == jop) && jsp->pr_exit_status) {
404 char d[80];
405
406 #ifdef SG_PRSE_SENSE_DECODE
407 if (sg_exit2str(exit_status, jsp->verbose, sizeof(d), d)) {
408 if (0 == strlen(d))
409 strncpy(d, "no errors", sizeof(d) - 1);
410 } else
411 strncpy(d, "not available", sizeof(d) - 1);
412 #else
413 if (0 == exit_status)
414 strncpy(d, "no errors", sizeof(d) - 1);
415 else
416 snprintf(d, sizeof(d), "exit_status=%d", exit_status);
417 #endif
418 sgj_js_nv_istr(jsp, jop, "exit_status", exit_status, NULL, d);
419 }
420 memcpy(&out_settings, &def_out_settings, sizeof(out_settings));
421 if (jsp->pr_indent_size != def_out_settings.indent_size)
422 out_settings.indent_size = jsp->pr_indent_size;
423 if (! jsp->pr_pretty)
424 out_settings.mode = jsp->pr_packed ? json_serialize_mode_packed :
425 json_serialize_mode_single_line;
426
427 len = json_measure_ex(jvp, out_settings);
428 if (len < 1)
429 return;
430 if (jsp->verbose > 3)
431 fprintf(fp, "%s: serialization length: %zu bytes\n", __func__, len);
432 b = (char *)calloc(len, 1);
433 if (NULL == b) {
434 if (jsp->verbose > 3)
435 pr2serr("%s: unable to get %zu bytes on heap\n", __func__, len);
436 return;
437 }
438
439 json_serialize_ex(b, jvp, out_settings);
440 if (jsp->verbose > 3)
441 fprintf(fp, "json serialized:\n");
442 fprintf(fp, "%s\n", b);
443 }
444
445 void
sgj_finish(sgj_state * jsp)446 sgj_finish(sgj_state * jsp)
447 {
448 if (jsp && jsp->basep) {
449 json_builder_free((json_value *)jsp->basep);
450 jsp->basep = NULL;
451 jsp->out_hrp = NULL;
452 jsp->userp = NULL;
453 }
454 }
455
456 void
sgj_free_unattached(sgj_opaque_p jop)457 sgj_free_unattached(sgj_opaque_p jop)
458 {
459 if (jop)
460 json_builder_free((json_value *)jop);
461 }
462
463 void
sgj_pr_hr(sgj_state * jsp,const char * fmt,...)464 sgj_pr_hr(sgj_state * jsp, const char * fmt, ...)
465 {
466 va_list args;
467
468 if (jsp->pr_as_json && jsp->pr_out_hr) {
469 size_t len;
470 char b[256];
471
472 va_start(args, fmt);
473 len = vsnprintf(b, sizeof(b), fmt, args);
474 if ((len > 0) && (len < sizeof(b))) {
475 const char * cp = b;
476
477 /* remove up to two trailing linefeeds */
478 if (b[len - 1] == '\n') {
479 --len;
480 if (b[len - 1] == '\n')
481 --len;
482 b[len] = '\0';
483 }
484 /* remove leading linefeed, if present */
485 if ((len > 0) && ('\n' == b[0]))
486 ++cp;
487 json_array_push((json_value *)jsp->out_hrp, json_string_new(cp));
488 }
489 va_end(args);
490 } else if (jsp->pr_as_json) {
491 va_start(args, fmt);
492 va_end(args);
493 } else {
494 va_start(args, fmt);
495 vfprintf(stdout, fmt, args);
496 va_end(args);
497 }
498 }
499
500 /* jop will 'own' returned value (if non-NULL) */
501 sgj_opaque_p
sgj_named_subobject_r(sgj_state * jsp,sgj_opaque_p jop,const char * name)502 sgj_named_subobject_r(sgj_state * jsp, sgj_opaque_p jop, const char * name)
503 {
504 sgj_opaque_p resp = NULL;
505
506 if (jsp && jsp->pr_as_json && name)
507 resp = json_object_push((json_value *)(jop ? jop : jsp->basep), name,
508 json_object_new(0));
509 return resp;
510 }
511
512 sgj_opaque_p
sgj_snake_named_subobject_r(sgj_state * jsp,sgj_opaque_p jop,const char * conv2sname)513 sgj_snake_named_subobject_r(sgj_state * jsp, sgj_opaque_p jop,
514 const char * conv2sname)
515 {
516 if (jsp && jsp->pr_as_json && conv2sname) {
517 int olen = strlen(conv2sname);
518 char * sname = (char *)malloc(olen + 8);
519 int nlen = sgj_name_to_snake(conv2sname, sname, olen + 8);
520
521 if (nlen > 0)
522 return json_object_push((json_value *)(jop ? jop : jsp->basep),
523 sname, json_object_new(0));
524 }
525 return NULL;
526 }
527
528 /* jop will 'own' returned value (if non-NULL) */
529 sgj_opaque_p
sgj_named_subarray_r(sgj_state * jsp,sgj_opaque_p jop,const char * name)530 sgj_named_subarray_r(sgj_state * jsp, sgj_opaque_p jop, const char * name)
531 {
532 sgj_opaque_p resp = NULL;
533
534 if (jsp && jsp->pr_as_json && name)
535 resp = json_object_push((json_value *)(jop ? jop : jsp->basep), name,
536 json_array_new(0));
537 return resp;
538 }
539
540 sgj_opaque_p
sgj_snake_named_subarray_r(sgj_state * jsp,sgj_opaque_p jop,const char * conv2sname)541 sgj_snake_named_subarray_r(sgj_state * jsp, sgj_opaque_p jop,
542 const char * conv2sname)
543 {
544 if (jsp && jsp->pr_as_json && conv2sname) {
545 int olen = strlen(conv2sname);
546 char * sname = (char *)malloc(olen + 8);
547 int nlen = sgj_name_to_snake(conv2sname, sname, olen + 8);
548
549 if (nlen > 0)
550 return json_object_push((json_value *)(jop ? jop : jsp->basep),
551 sname, json_array_new(0));
552 }
553 return NULL;
554 }
555
556 /* Newly created object is un-attached to jsp->basep tree */
557 sgj_opaque_p
sgj_new_unattached_object_r(sgj_state * jsp)558 sgj_new_unattached_object_r(sgj_state * jsp)
559 {
560 return (jsp && jsp->pr_as_json) ? json_object_new(0) : NULL;
561 }
562
563 /* Newly created array is un-attached to jsp->basep tree */
564 sgj_opaque_p
sgj_new_unattached_array_r(sgj_state * jsp)565 sgj_new_unattached_array_r(sgj_state * jsp)
566 {
567 return (jsp && jsp->pr_as_json) ? json_array_new(0) : NULL;
568 }
569
570 sgj_opaque_p
sgj_js_nv_s(sgj_state * jsp,sgj_opaque_p jop,const char * name,const char * value)571 sgj_js_nv_s(sgj_state * jsp, sgj_opaque_p jop, const char * name,
572 const char * value)
573 {
574 if (jsp && jsp->pr_as_json && value) {
575 if (name)
576 return json_object_push((json_value *)(jop ? jop : jsp->basep),
577 name, json_string_new(value));
578 else
579 return json_array_push((json_value *)(jop ? jop : jsp->basep),
580 json_string_new(value));
581 } else
582 return NULL;
583 }
584
585 sgj_opaque_p
sgj_js_nv_s_len(sgj_state * jsp,sgj_opaque_p jop,const char * name,const char * value,int slen)586 sgj_js_nv_s_len(sgj_state * jsp, sgj_opaque_p jop, const char * name,
587 const char * value, int slen)
588 {
589 int k;
590
591 if (jsp && jsp->pr_as_json && value && (slen >= 0)) {
592 for (k = 0; k < slen; ++k) { /* don't want '\0' in value string */
593 if (0 == value[k])
594 break;
595 }
596 if (name)
597 return json_object_push((json_value *)(jop ? jop : jsp->basep),
598 name, json_string_new_length(k, value));
599 else
600 return json_array_push((json_value *)(jop ? jop : jsp->basep),
601 json_string_new_length(k, value));
602 } else
603 return NULL;
604 }
605
606 sgj_opaque_p
sgj_js_nv_i(sgj_state * jsp,sgj_opaque_p jop,const char * name,int64_t value)607 sgj_js_nv_i(sgj_state * jsp, sgj_opaque_p jop, const char * name,
608 int64_t value)
609 {
610 if (jsp && jsp->pr_as_json) {
611 if (name)
612 return json_object_push((json_value *)(jop ? jop : jsp->basep),
613 name, json_integer_new(value));
614 else
615 return json_array_push((json_value *)(jop ? jop : jsp->basep),
616 json_integer_new(value));
617 }
618 else
619 return NULL;
620 }
621
622 sgj_opaque_p
sgj_js_nv_b(sgj_state * jsp,sgj_opaque_p jop,const char * name,bool value)623 sgj_js_nv_b(sgj_state * jsp, sgj_opaque_p jop, const char * name, bool value)
624 {
625 if (jsp && jsp->pr_as_json) {
626 if (name)
627 return json_object_push((json_value *)(jop ? jop : jsp->basep),
628 name, json_boolean_new(value));
629 else
630 return json_array_push((json_value *)(jop ? jop : jsp->basep),
631 json_boolean_new(value));
632 } else
633 return NULL;
634 }
635
636 /* jop will 'own' ua_jop (if returned value is non-NULL) */
637 sgj_opaque_p
sgj_js_nv_o(sgj_state * jsp,sgj_opaque_p jop,const char * name,sgj_opaque_p ua_jop)638 sgj_js_nv_o(sgj_state * jsp, sgj_opaque_p jop, const char * name,
639 sgj_opaque_p ua_jop)
640 {
641 if (jsp && jsp->pr_as_json && ua_jop) {
642 if (name)
643 return json_object_push((json_value *)(jop ? jop : jsp->basep),
644 name, (json_value *)ua_jop);
645 else
646 return json_array_push((json_value *)(jop ? jop : jsp->basep),
647 (json_value *)ua_jop);
648 } else
649 return NULL;
650 }
651
652 void
sgj_js_nv_ihex(sgj_state * jsp,sgj_opaque_p jop,const char * name,uint64_t value)653 sgj_js_nv_ihex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
654 uint64_t value)
655 {
656 if ((NULL == jsp) || (NULL == name) || (! jsp->pr_as_json))
657 return;
658 else if (jsp->pr_hex) {
659 sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
660 char b[64];
661
662 if (NULL == jo2p)
663 return;
664 sgj_js_nv_i(jsp, jo2p, "i", (int64_t)value);
665 snprintf(b, sizeof(b), "%" PRIx64, value);
666 sgj_js_nv_s(jsp, jo2p, "hex", b);
667 } else
668 sgj_js_nv_i(jsp, jop, name, (int64_t)value);
669 }
670
671 static const char * sc_mn_s = "meaning";
672
673 void
sgj_js_nv_istr(sgj_state * jsp,sgj_opaque_p jop,const char * name,int64_t val_i,const char * str_name,const char * val_s)674 sgj_js_nv_istr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
675 int64_t val_i, const char * str_name, const char * val_s)
676 {
677 if ((NULL == jsp) || (! jsp->pr_as_json))
678 return;
679 else if (val_s && jsp->pr_string) {
680 sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
681
682 if (NULL == jo2p)
683 return;
684 sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
685 sgj_js_nv_s(jsp, jo2p, str_name ? str_name : sc_mn_s, val_s);
686 } else
687 sgj_js_nv_i(jsp, jop, name, val_i);
688 }
689
690 void
sgj_js_nv_ihexstr(sgj_state * jsp,sgj_opaque_p jop,const char * name,int64_t val_i,const char * str_name,const char * val_s)691 sgj_js_nv_ihexstr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
692 int64_t val_i, const char * str_name, const char * val_s)
693 {
694 bool as_str;
695
696 if ((NULL == jsp) || (! jsp->pr_as_json))
697 return;
698 as_str = jsp->pr_string && val_s;
699 if ((! jsp->pr_hex) && (! as_str))
700 sgj_js_nv_i(jsp, jop, name, val_i);
701 else {
702 char b[64];
703 sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
704
705 if (NULL == jo2p)
706 return;
707 sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
708 if (jsp->pr_hex) {
709 snprintf(b, sizeof(b), "%" PRIx64, val_i);
710 sgj_js_nv_s(jsp, jo2p, "hex", b);
711 }
712 if (as_str)
713 sgj_js_nv_s(jsp, jo2p, str_name ? str_name : sc_mn_s, val_s);
714 }
715 }
716
717 static const char * sc_nex_s = "name_extra";
718
719 void
sgj_js_nv_ihex_nex(sgj_state * jsp,sgj_opaque_p jop,const char * name,int64_t val_i,bool hex_as_well,const char * nex_s)720 sgj_js_nv_ihex_nex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
721 int64_t val_i, bool hex_as_well, const char * nex_s)
722 {
723 bool as_hex, as_nex;
724
725 if ((NULL == jsp) || (! jsp->pr_as_json))
726 return;
727 as_hex = jsp->pr_hex && hex_as_well;
728 as_nex = jsp->pr_name_ex && nex_s;
729 if (! (as_hex || as_nex))
730 sgj_js_nv_i(jsp, jop, name, val_i);
731 else {
732 char b[64];
733 sgj_opaque_p jo2p =
734 sgj_named_subobject_r(jsp, jop, name);
735
736 if (NULL == jo2p)
737 return;
738 sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
739 if (as_hex) {
740 snprintf(b, sizeof(b), "%" PRIx64, val_i);
741 sgj_js_nv_s(jsp, jo2p, "hex", b);
742 }
743 if (as_nex)
744 sgj_js_nv_s(jsp, jo2p, sc_nex_s, nex_s);
745 }
746 }
747
748 #ifndef SG_PRSE_SENSE_DECODE
749 static void
h2str(const uint8_t * byte_arr,int num_bytes,char * bp,int blen)750 h2str(const uint8_t * byte_arr, int num_bytes, char * bp, int blen)
751 {
752 int j, k, n;
753
754 for (k = 0, n = 0; (k < num_bytes) && (n < blen); ) {
755 j = sg_scnpr(bp + n, blen - n, "%02x ", byte_arr[k]);
756 if (j < 2)
757 break;
758 n += j;
759 ++k;
760 if ((0 == (k % 8)) && (k < num_bytes) && (n < blen)) {
761 bp[n++] = ' ';
762 }
763 }
764 j = strlen(bp);
765 if ((j > 0) && (' ' == bp[j - 1]))
766 bp[j - 1] = '\0'; /* chop off trailing space */
767 }
768 #endif
769
770 /* Add hex byte strings irrespective of jsp->pr_hex setting. */
771 void
sgj_js_nv_hex_bytes(sgj_state * jsp,sgj_opaque_p jop,const char * name,const uint8_t * byte_arr,int num_bytes)772 sgj_js_nv_hex_bytes(sgj_state * jsp, sgj_opaque_p jop, const char * name,
773 const uint8_t * byte_arr, int num_bytes)
774 {
775 int blen = num_bytes * 4;
776 char * bp;
777
778 if ((NULL == jsp) || (! jsp->pr_as_json))
779 return;
780 bp = (char *)calloc(blen + 4, 1);
781 if (bp) {
782 #ifdef SG_PRSE_SENSE_DECODE
783 hex2str(byte_arr, num_bytes, NULL, 2, blen, bp);
784 #else
785 h2str(byte_arr, num_bytes, bp, blen);
786 #endif
787 sgj_js_nv_s(jsp, jop, name, bp);
788 free(bp);
789 }
790 }
791
792 void
sgj_js_nv_ihexstr_nex(sgj_state * jsp,sgj_opaque_p jop,const char * name,int64_t val_i,bool hex_as_well,const char * str_name,const char * val_s,const char * nex_s)793 sgj_js_nv_ihexstr_nex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
794 int64_t val_i, bool hex_as_well, const char * str_name,
795 const char * val_s, const char * nex_s)
796 {
797 bool as_hex = jsp->pr_hex && hex_as_well;
798 bool as_str = jsp->pr_string && val_s;
799 bool as_nex = jsp->pr_name_ex && nex_s;
800 const char * sname = str_name ? str_name : sc_mn_s;
801
802 if ((NULL == jsp) || (! jsp->pr_as_json))
803 return;
804 if (! (as_hex || as_nex || as_str))
805 sgj_js_nv_i(jsp, jop, name, val_i);
806 else {
807 char b[64];
808 sgj_opaque_p jo2p =
809 sgj_named_subobject_r(jsp, jop, name);
810
811 if (NULL == jo2p)
812 return;
813 sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
814 if (as_nex) {
815 if (as_hex) {
816 snprintf(b, sizeof(b), "%" PRIx64, val_i);
817 sgj_js_nv_s(jsp, jo2p, "hex", b);
818 }
819 if (as_str) {
820 sgj_js_nv_s(jsp, jo2p, sname, val_s);
821 }
822 sgj_js_nv_s(jsp, jo2p, sc_nex_s, nex_s);
823 } else if (as_hex) {
824 snprintf(b, sizeof(b), "%" PRIx64, val_i);
825 sgj_js_nv_s(jsp, jo2p, "hex", b);
826 if (as_str)
827 sgj_js_nv_s(jsp, jo2p, sname, val_s);
828 } else if (as_str)
829 sgj_js_nv_s(jsp, jo2p, sname, val_s);
830 }
831 }
832
833 /* Treat '\n' in sp as line breaks. Consumes characters from sp until either
834 * a '\0' is found or slen is exhausted. Add each line to jsp->out_hrp JSON
835 * array (if conditions met). */
836 void
sgj_js_str_out(sgj_state * jsp,const char * sp,int slen)837 sgj_js_str_out(sgj_state * jsp, const char * sp, int slen)
838 {
839 char c;
840 int k, n;
841 const char * prev_sp = sp;
842 const char * cur_sp = sp;
843
844 if ((NULL == jsp) || (NULL == jsp->out_hrp) || (! jsp->pr_as_json) ||
845 (! jsp->pr_out_hr))
846 return;
847 for (k = 0; k < slen; ++k, ++cur_sp) {
848 c = *cur_sp;
849 if ('\0' == c)
850 break;
851 else if ('\n' == c) {
852 n = cur_sp - prev_sp;
853 /* when name is NULL, add to array (jsp->out_hrp) */
854 sgj_js_nv_s_len(jsp, jsp->out_hrp, NULL, prev_sp, n);
855 prev_sp = cur_sp + 1;
856 }
857 }
858 if (prev_sp < cur_sp) {
859 n = cur_sp - prev_sp;
860 sgj_js_nv_s_len(jsp, jsp->out_hrp, NULL, prev_sp, n);
861 }
862 }
863
864 char *
sgj_convert_to_snake_name(const char * in_name,char * sname,int max_sname_len)865 sgj_convert_to_snake_name(const char * in_name, char * sname,
866 int max_sname_len)
867 {
868 sgj_name_to_snake(in_name, sname, max_sname_len);
869 return sname;
870 }
871
872 bool
sgj_is_snake_name(const char * in_name)873 sgj_is_snake_name(const char * in_name)
874 {
875 size_t k;
876 size_t ln = strlen(in_name);
877 char c;
878
879 for (k = 0; k < ln; ++k) {
880 c = in_name[k];
881 if (((c >= '0') && (c <= '9')) ||
882 ((c >= 'a') && (c <= 'z')) ||
883 (c == '_'))
884 continue;
885 else
886 return false;
887 }
888 return true;
889 }
890
891 /* This function tries to convert the 'in' C string to "snake_case"
892 * convention so the output 'out' only contains lower case ASCII letters,
893 * numerals and "_" as a separator. Any leading or trailing underscores
894 * are removed as are repeated underscores (e.g. "_Snake __ case" becomes
895 * "snake_case"). Parentheses and the characters between them are removed.
896 * Returns number of characters placed in 'out' excluding the trailing
897 * NULL */
898 static int
sgj_name_to_snake(const char * in,char * out,int maxlen_out)899 sgj_name_to_snake(const char * in, char * out, int maxlen_out)
900 {
901 bool prev_underscore = false;
902 bool within_paren = false;
903 int c, k, j, inlen;
904
905 if (maxlen_out < 2) {
906 if (maxlen_out == 1)
907 out[0] = '\0';
908 return 0;
909 }
910 inlen = strlen(in);
911 for (k = 0, j = 0; (k < inlen) && (j < maxlen_out); ++k) {
912 c = in[k];
913 if (within_paren) {
914 if (')' == c)
915 within_paren = false;
916 continue;
917 }
918 if (isalnum(c)) {
919 out[j++] = isupper(c) ? tolower(c) : c;
920 prev_underscore = false;
921 } else if ('(' == c)
922 within_paren = true;
923 else if ((j > 0) && (! prev_underscore)) {
924 out[j++] = '_';
925 prev_underscore = true;
926 }
927 /* else we are skipping character 'c' */
928 }
929 if (j == maxlen_out)
930 out[--j] = '\0';
931 /* trim of trailing underscores (might have been spaces) */
932 for (k = j - 1; k >= 0; --k) {
933 if (out[k] != '_')
934 break;
935 }
936 if (k < 0)
937 k = 0;
938 else
939 ++k;
940 out[k] = '\0';
941 return k;
942 }
943
944 static int
sgj_jtype_to_s(char * b,int blen_max,json_value * jvp)945 sgj_jtype_to_s(char * b, int blen_max, json_value * jvp)
946 {
947 json_type jtype = jvp ? jvp->type : json_none;
948
949 switch (jtype) {
950 case json_string:
951 return sg_scnpr(b, blen_max, "%s", jvp->u.string.ptr);
952 case json_integer:
953 return sg_scnpr(b, blen_max, "%" PRIi64, jvp->u.integer);
954 case json_boolean:
955 return sg_scnpr(b, blen_max, "%s", jvp->u.boolean ? "true" : "false");
956 case json_none:
957 default:
958 if ((blen_max > 0) && ('\0' != b[0]))
959 b[0] = '\0';
960 break;
961 }
962 return 0;
963 }
964
965 static int
sgj_haj_helper(char * b,int blen_max,const char * name,enum sgj_separator_t sep,bool use_jvp,json_value * jvp,int64_t val_instead)966 sgj_haj_helper(char * b, int blen_max, const char * name,
967 enum sgj_separator_t sep, bool use_jvp,
968 json_value * jvp, int64_t val_instead)
969 {
970 int n = 0;
971
972 if (name) {
973 n += sg_scnpr(b + n, blen_max - n, "%s", name);
974 switch (sep) {
975 case SGJ_SEP_NONE:
976 break;
977 case SGJ_SEP_SPACE_1:
978 n += sg_scnpr(b + n, blen_max - n, " ");
979 break;
980 case SGJ_SEP_SPACE_2:
981 n += sg_scnpr(b + n, blen_max - n, " ");
982 break;
983 case SGJ_SEP_SPACE_3:
984 n += sg_scnpr(b + n, blen_max - n, " ");
985 break;
986 case SGJ_SEP_SPACE_4:
987 n += sg_scnpr(b + n, blen_max - n, " ");
988 break;
989 case SGJ_SEP_EQUAL_NO_SPACE:
990 n += sg_scnpr(b + n, blen_max - n, "=");
991 break;
992 case SGJ_SEP_EQUAL_1_SPACE:
993 n += sg_scnpr(b + n, blen_max - n, "= ");
994 break;
995 case SGJ_SEP_COLON_NO_SPACE:
996 n += sg_scnpr(b + n, blen_max - n, ":");
997 break;
998 case SGJ_SEP_COLON_1_SPACE:
999 n += sg_scnpr(b + n, blen_max - n, ": ");
1000 break;
1001 default:
1002 break;
1003 }
1004 }
1005 if (use_jvp)
1006 n += sgj_jtype_to_s(b + n, blen_max - n, jvp);
1007 else
1008 n += sg_scnpr(b + n, blen_max - n, "%" PRIi64, val_instead);
1009 return n;
1010 }
1011
1012 static void
sgj_haj_xx(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,json_value * jvp,bool hex_as_well,const char * val_s,const char * nex_s)1013 sgj_haj_xx(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1014 const char * name, enum sgj_separator_t sep, json_value * jvp,
1015 bool hex_as_well, const char * val_s, const char * nex_s)
1016 {
1017 bool eaten = false;
1018 bool as_json = (jsp && jsp->pr_as_json);
1019 bool done;
1020 int n;
1021 json_type jtype = jvp ? jvp->type : json_none;
1022 char b[256];
1023 char jname[96];
1024 static const int blen = sizeof(b);
1025
1026 if (leadin_sp > 128)
1027 leadin_sp = 128;
1028 for (n = 0; n < leadin_sp; ++n)
1029 b[n] = ' ';
1030 b[n] = '\0';
1031 if (NULL == name) {
1032 if ((! as_json) || (jsp && jsp->pr_out_hr)) {
1033 n += sgj_jtype_to_s(b + n, blen - n, jvp);
1034 printf("%s\n", b);
1035 }
1036 if (NULL == jop) {
1037 if (as_json && jsp->pr_out_hr) {
1038 eaten = true;
1039 json_array_push((json_value *)jsp->out_hrp,
1040 jvp ? jvp : json_null_new());
1041 }
1042 } else { /* assume jop points to named array */
1043 if (as_json) {
1044 eaten = true;
1045 json_array_push((json_value *)jop,
1046 jvp ? jvp : json_null_new());
1047 }
1048 }
1049 goto fini;
1050 }
1051 if (as_json) {
1052 int k;
1053
1054 if (NULL == jop)
1055 jop = jsp->basep;
1056 k = sgj_name_to_snake(name, jname, sizeof(jname));
1057 if (k > 0) {
1058 done = false;
1059 if (nex_s && (strlen(nex_s) > 0)) {
1060 switch (jtype) {
1061 case json_string:
1062 break;
1063 case json_integer:
1064 sgj_js_nv_ihexstr_nex(jsp, jop, jname, jvp->u.integer,
1065 hex_as_well, sc_mn_s, val_s, nex_s);
1066 done = true;
1067 break;
1068 case json_boolean:
1069 sgj_js_nv_ihexstr_nex(jsp, jop, jname, jvp->u.boolean,
1070 false, sc_mn_s, val_s, nex_s);
1071 done = true;
1072 break;
1073 case json_none:
1074 default:
1075 break;
1076 }
1077 } else {
1078 switch (jtype) {
1079 case json_string:
1080 break;
1081 case json_integer:
1082 if (hex_as_well) {
1083 sgj_js_nv_ihexstr(jsp, jop, jname, jvp->u.integer,
1084 sc_mn_s, val_s);
1085 done = true;
1086 }
1087 break;
1088 case json_none:
1089 default:
1090 break;
1091 }
1092 }
1093 if (! done) {
1094 eaten = true;
1095 json_object_push((json_value *)jop, jname,
1096 jvp ? jvp : json_null_new());
1097 }
1098 }
1099 }
1100 if (jvp && ((as_json && jsp->pr_out_hr) || (! as_json)))
1101 n += sgj_haj_helper(b + n, blen - n, name, sep, true, jvp, 0);
1102
1103 if (as_json && jsp->pr_out_hr)
1104 json_array_push((json_value *)jsp->out_hrp, json_string_new(b));
1105 if (! as_json)
1106 printf("%s\n", b);
1107 fini:
1108 if (jvp && (! eaten))
1109 json_builder_free((json_value *)jvp);
1110 }
1111
1112 void
sgj_haj_vs(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,const char * value)1113 sgj_haj_vs(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1114 const char * name, enum sgj_separator_t sep, const char * value)
1115 {
1116 json_value * jvp;
1117
1118 /* make json_value even if jsp->pr_as_json is false */
1119 jvp = value ? json_string_new(value) : NULL;
1120 sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, false, NULL, NULL);
1121 }
1122
1123 void
sgj_haj_vi(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,int64_t value,bool hex_as_well)1124 sgj_haj_vi(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1125 const char * name, enum sgj_separator_t sep, int64_t value,
1126 bool hex_as_well)
1127 {
1128 json_value * jvp;
1129
1130 jvp = json_integer_new(value);
1131 sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, NULL, NULL);
1132 }
1133
1134 void
sgj_haj_vistr(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,int64_t value,bool hex_as_well,const char * val_s)1135 sgj_haj_vistr(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1136 const char * name, enum sgj_separator_t sep, int64_t value,
1137 bool hex_as_well, const char * val_s)
1138 {
1139 json_value * jvp;
1140
1141 jvp = json_integer_new(value);
1142 sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, val_s,
1143 NULL);
1144 }
1145
1146 void
sgj_haj_vi_nex(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,int64_t value,bool hex_as_well,const char * nex_s)1147 sgj_haj_vi_nex(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1148 const char * name, enum sgj_separator_t sep,
1149 int64_t value, bool hex_as_well, const char * nex_s)
1150 {
1151 json_value * jvp;
1152
1153 jvp = json_integer_new(value);
1154 sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, NULL, nex_s);
1155 }
1156
1157 void
sgj_haj_vistr_nex(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,int64_t value,bool hex_as_well,const char * val_s,const char * nex_s)1158 sgj_haj_vistr_nex(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1159 const char * name, enum sgj_separator_t sep,
1160 int64_t value, bool hex_as_well,
1161 const char * val_s, const char * nex_s)
1162 {
1163 json_value * jvp;
1164
1165 jvp = json_integer_new(value);
1166 sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, val_s,
1167 nex_s);
1168 }
1169
1170 void
sgj_haj_vb(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,bool value)1171 sgj_haj_vb(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1172 const char * name, enum sgj_separator_t sep, bool value)
1173 {
1174 json_value * jvp;
1175
1176 jvp = json_boolean_new(value);
1177 sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, false, NULL, NULL);
1178 }
1179
1180 sgj_opaque_p
sgj_haj_subo_r(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,int64_t value,bool hex_as_well)1181 sgj_haj_subo_r(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1182 const char * name, enum sgj_separator_t sep, int64_t value,
1183 bool hex_as_well)
1184 {
1185 bool as_json = (jsp && jsp->pr_as_json);
1186 int n = 0;
1187 sgj_opaque_p jo2p;
1188 char b[256];
1189 static const int blen = sizeof(b);
1190
1191 if (NULL == name)
1192 return NULL;
1193 for (n = 0; n < leadin_sp; ++n)
1194 b[n] = ' ';
1195 b[n] = '\0';
1196 if ((! as_json) || (jsp && jsp->pr_out_hr))
1197 n += sgj_haj_helper(b + n, blen - n, name, sep, false, NULL, value);
1198
1199 if (as_json && jsp->pr_out_hr)
1200 json_array_push((json_value *)jsp->out_hrp, json_string_new(b));
1201 if (! as_json)
1202 printf("%s\n", b);
1203
1204 if (as_json) {
1205 sgj_name_to_snake(name, b, blen);
1206 jo2p = sgj_named_subobject_r(jsp, jop, b);
1207 if (jo2p) {
1208 sgj_js_nv_i(jsp, jo2p, "i", value);
1209 if (hex_as_well && jsp->pr_hex) {
1210 snprintf(b, blen, "%" PRIx64, value);
1211 sgj_js_nv_s(jsp, jo2p, "hex", b);
1212 }
1213 }
1214 return jo2p;
1215 }
1216 return NULL;
1217 }
1218
1219 #ifdef SG_PRSE_SENSE_DECODE
1220
1221 static const char * dtsp = "descriptor too short";
1222 static const char * sksvp = "sense-key specific valid";
1223 static const char * ddep = "designation_descriptor_error";
1224 static const char * naa_exp = "Network Address Authority";
1225 static const char * aoi_exp = "IEEE-Administered Organizational Identifier";
1226
1227 bool
sgj_js_designation_descriptor(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * ddp,int dd_len)1228 sgj_js_designation_descriptor(sgj_state * jsp, sgj_opaque_p jop,
1229 const uint8_t * ddp, int dd_len)
1230 {
1231 int p_id, piv, c_set, assoc, desig_type, d_id, naa;
1232 int n, aoi, vsi, dlen;
1233 uint64_t ull;
1234 const uint8_t * ip;
1235 char e[80];
1236 char b[256];
1237 const char * cp;
1238 const char * naa_sp;
1239 sgj_opaque_p jo2p;
1240 static const int blen = sizeof(b);
1241 static const int elen = sizeof(e);
1242
1243 if (dd_len < 4) {
1244 sgj_js_nv_s(jsp, jop, ddep, "too short");
1245 return false;
1246 }
1247 dlen = ddp[3];
1248 if (dlen > (dd_len - 4)) {
1249 snprintf(e, elen, "too long: says it is %d bytes, but given %d "
1250 "bytes\n", dlen, dd_len - 4);
1251 sgj_js_nv_s(jsp, jop, ddep, e);
1252 return false;
1253 }
1254 ip = ddp + 4;
1255 p_id = ((ddp[0] >> 4) & 0xf);
1256 c_set = (ddp[0] & 0xf);
1257 piv = ((ddp[1] & 0x80) ? 1 : 0);
1258 assoc = ((ddp[1] >> 4) & 0x3);
1259 desig_type = (ddp[1] & 0xf);
1260 cp = sg_get_desig_assoc_str(assoc);
1261 if (assoc == 3)
1262 cp = "Reserved [0x3]"; /* should not happen */
1263 sgj_js_nv_ihexstr(jsp, jop, "association", assoc, NULL, cp);
1264 cp = sg_get_desig_type_str(desig_type);
1265 if (NULL == cp)
1266 cp = "unknown";
1267 sgj_js_nv_ihexstr(jsp, jop, "designator_type", desig_type,
1268 NULL, cp);
1269 cp = sg_get_desig_code_set_str(c_set);
1270 if (NULL == cp)
1271 cp = "unknown";
1272 sgj_js_nv_ihexstr(jsp, jop, "code_set", desig_type,
1273 NULL, cp);
1274 sgj_js_nv_ihex_nex(jsp, jop, "piv", piv, false,
1275 "Protocol Identifier Valid");
1276 sg_get_trans_proto_str(p_id, elen, e);
1277 sgj_js_nv_ihexstr(jsp, jop, "protocol_identifier", p_id, NULL, e);
1278 switch (desig_type) {
1279 case 0: /* vendor specific */
1280 sgj_js_nv_hex_bytes(jsp, jop, "vendor_specific_hexbytes", ip, dlen);
1281 break;
1282 case 1: /* T10 vendor identification */
1283 n = (dlen < 8) ? dlen : 8;
1284 snprintf(b, blen, "%.*s", n, ip);
1285 sgj_js_nv_s(jsp, jop, "t10_vendor_identification", b);
1286 b[0] = '\0';
1287 if (dlen > 8)
1288 snprintf(b, blen, "%.*s", dlen - 8, ip + 8);
1289 sgj_js_nv_s(jsp, jop, "vendor_specific_identifier", b);
1290 break;
1291 case 2: /* EUI-64 based */
1292 sgj_js_nv_i(jsp, jop, "eui_64_based_designator_length", dlen);
1293 ull = sg_get_unaligned_be64(ip);
1294 switch (dlen) {
1295 case 8:
1296 sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ull);
1297 break;
1298 case 12:
1299 sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ull);
1300 sgj_js_nv_ihex(jsp, jop, "directory_id",
1301 sg_get_unaligned_be32(ip + 8));
1302 break;
1303 case 16:
1304 sgj_js_nv_ihex(jsp, jop, "identifier_extension", ull);
1305 sgj_js_nv_ihex(jsp, jop, "ieee_identifier",
1306 sg_get_unaligned_be64(ip + 8));
1307 break;
1308 default:
1309 sgj_js_nv_s(jsp, jop, "eui_64", "decoding failed");
1310 break;
1311 }
1312 break;
1313 case 3: /* NAA <n> */
1314 if (jsp->pr_hex)
1315 sgj_js_nv_hex_bytes(jsp, jop, "full_naa_hexbytes", ip, dlen);
1316 naa = (ip[0] >> 4) & 0xff;
1317 switch (naa) {
1318 case 2:
1319 naa_sp = "IEEE Extended";
1320 sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1321 naa_exp);
1322 d_id = (((ip[0] & 0xf) << 8) | ip[1]);
1323 sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_a", d_id);
1324 aoi = sg_get_unaligned_be24(ip + 2);
1325 sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
1326 vsi = sg_get_unaligned_be24(ip + 5);
1327 sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_b", vsi);
1328 break;
1329 case 3:
1330 naa_sp = "Locally Assigned";
1331 sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1332 naa_exp);
1333 ull = sg_get_unaligned_be64(ip + 0) & 0xfffffffffffffffULL;
1334 sgj_js_nv_ihex(jsp, jop, "locally_administered_value", ull);
1335 break;
1336 case 5:
1337 naa_sp = "IEEE Registered";
1338 sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1339 naa_exp);
1340 aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff;
1341 sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
1342 ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL;
1343 sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier", ull);
1344 break;
1345 case 6:
1346 naa_sp = "IEEE Registered Extended";
1347 sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1348 naa_exp);
1349 aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff;
1350 sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
1351 ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL;
1352 sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier", ull);
1353 ull = sg_get_unaligned_be64(ip + 8);
1354 sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_extension",
1355 ull);
1356 break;
1357 default:
1358 snprintf(b, blen, "unknown NAA value=0x%x", naa);
1359 sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, true, NULL, b,
1360 naa_exp);
1361 sgj_js_nv_hex_bytes(jsp, jop, "full_naa_hexbytes", ip, dlen);
1362 break;
1363 }
1364 break;
1365 case 4: /* Relative target port */
1366 if (jsp->pr_hex)
1367 sgj_js_nv_hex_bytes(jsp, jop, "relative_target_port_hexbytes",
1368 ip, dlen);
1369 sgj_js_nv_ihex(jsp, jop, "relative_target_port_identifier",
1370 sg_get_unaligned_be16(ip + 2));
1371 break;
1372 case 5: /* (primary) Target port group */
1373 if (jsp->pr_hex)
1374 sgj_js_nv_hex_bytes(jsp, jop, "target_port_group_hexbytes",
1375 ip, dlen);
1376 sgj_js_nv_ihex(jsp, jop, "target_port_group",
1377 sg_get_unaligned_be16(ip + 2));
1378 break;
1379 case 6: /* Logical unit group */
1380 if (jsp->pr_hex)
1381 sgj_js_nv_hex_bytes(jsp, jop, "logical_unit_group_hexbytes",
1382 ip, dlen);
1383 sgj_js_nv_ihex(jsp, jop, "logical_unit_group",
1384 sg_get_unaligned_be16(ip + 2));
1385 break;
1386 case 7: /* MD5 logical unit identifier */
1387 sgj_js_nv_hex_bytes(jsp, jop, "md5_logical_unit_hexbytes",
1388 ip, dlen);
1389 break;
1390 case 8: /* SCSI name string */
1391 if (jsp->pr_hex)
1392 sgj_js_nv_hex_bytes(jsp, jop, "scsi_name_string_hexbytes",
1393 ip, dlen);
1394 snprintf(b, blen, "%.*s", dlen, ip);
1395 sgj_js_nv_s(jsp, jop, "scsi_name_string", b);
1396 break;
1397 case 9: /* Protocol specific port identifier */
1398 if (jsp->pr_hex)
1399 sgj_js_nv_hex_bytes(jsp, jop,
1400 "protocol_specific_port_identifier_hexbytes",
1401 ip, dlen);
1402 if (TPROTO_UAS == p_id) {
1403 jo2p = sgj_named_subobject_r(jsp, jop,
1404 "usb_target_port_identifier");
1405 sgj_js_nv_ihex(jsp, jo2p, "device_address", 0x7f & ip[0]);
1406 sgj_js_nv_ihex(jsp, jo2p, "interface_number", ip[2]);
1407 } else if (TPROTO_SOP == p_id) {
1408 jo2p = sgj_named_subobject_r(jsp, jop, "pci_express_routing_id");
1409 sgj_js_nv_ihex(jsp, jo2p, "routing_id",
1410 sg_get_unaligned_be16(ip + 0));
1411 } else
1412 sgj_js_nv_s(jsp, jop, "protocol_specific_port_identifier",
1413 "decoding failure");
1414
1415 break;
1416 case 0xa: /* UUID identifier */
1417 if (jsp->pr_hex)
1418 sgj_js_nv_hex_bytes(jsp, jop, "uuid_hexbytes", ip, dlen);
1419 sg_t10_uuid_desig2str(ip, dlen, c_set, false, true, NULL, blen, b);
1420 n = strlen(b);
1421 if ((n > 0) && ('\n' == b[n - 1]))
1422 b[n - 1] = '\0';
1423 sgj_js_nv_s(jsp, jop, "uuid", b);
1424 break;
1425 default: /* reserved */
1426 sgj_js_nv_hex_bytes(jsp, jop, "reserved_designator_hexbytes",
1427 ip, dlen);
1428 break;
1429 }
1430 return true;
1431 }
1432
1433 static void
sgj_progress_indication(sgj_state * jsp,sgj_opaque_p jop,uint16_t prog_indic,bool is_another)1434 sgj_progress_indication(sgj_state * jsp, sgj_opaque_p jop,
1435 uint16_t prog_indic, bool is_another)
1436 {
1437 uint32_t progress, pr, rem;
1438 sgj_opaque_p jo2p;
1439 char b[64];
1440
1441 if (is_another)
1442 jo2p = sgj_named_subobject_r(jsp, jop, "another_progress_indication");
1443 else
1444 jo2p = sgj_named_subobject_r(jsp, jop, "progress_indication");
1445 if (NULL == jo2p)
1446 return;
1447 progress = prog_indic;
1448 sgj_js_nv_i(jsp, jo2p, "i", progress);
1449 snprintf(b, sizeof(b), "%x", progress);
1450 sgj_js_nv_s(jsp, jo2p, "hex", b);
1451 progress *= 100;
1452 pr = progress / 65536;
1453 rem = (progress % 65536) / 656;
1454 snprintf(b, sizeof(b), "%d.02%d%%\n", pr, rem);
1455 sgj_js_nv_s(jsp, jo2p, "percentage", b);
1456 }
1457
1458 static bool
sgj_decode_sks(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * dp,int dlen,int sense_key)1459 sgj_decode_sks(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * dp, int dlen,
1460 int sense_key)
1461 {
1462 switch (sense_key) {
1463 case SPC_SK_ILLEGAL_REQUEST:
1464 if (dlen < 3) {
1465 sgj_js_nv_s(jsp, jop, "illegal_request_sks", dtsp);
1466 return false;
1467 }
1468 sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1469 sksvp);
1470 sgj_js_nv_ihex_nex(jsp, jop, "c_d", !! (dp[0] & 0x40), false,
1471 "c: cdb; d: data-out");
1472 sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false,
1473 "bit pointer (index) valid");
1474 sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7);
1475 sgj_js_nv_ihex(jsp, jop, "field_pointer",
1476 sg_get_unaligned_be16(dp + 1));
1477 break;
1478 case SPC_SK_HARDWARE_ERROR:
1479 case SPC_SK_MEDIUM_ERROR:
1480 case SPC_SK_RECOVERED_ERROR:
1481 if (dlen < 3) {
1482 sgj_js_nv_s(jsp, jop, "actual_retry_count_sks", dtsp);
1483 return false;
1484 }
1485 sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1486 sksvp);
1487 sgj_js_nv_ihex(jsp, jop, "actual_retry_count",
1488 sg_get_unaligned_be16(dp + 1));
1489 break;
1490 case SPC_SK_NO_SENSE:
1491 case SPC_SK_NOT_READY:
1492 if (dlen < 7) {
1493 sgj_js_nv_s(jsp, jop, "progress_indication_sks", dtsp);
1494 return false;
1495 }
1496 sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1497 sksvp);
1498 sgj_progress_indication(jsp, jop, sg_get_unaligned_be16(dp + 1),
1499 false);
1500 break;
1501 case SPC_SK_COPY_ABORTED:
1502 if (dlen < 7) {
1503 sgj_js_nv_s(jsp, jop, "segment_indication_sks", dtsp);
1504 return false;
1505 }
1506 sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1507 sksvp);
1508 sgj_js_nv_ihex_nex(jsp, jop, "sd", !! (dp[0] & 0x20), false,
1509 "field pointer relative to: 1->segment "
1510 "descriptor, 0->parameter list");
1511 sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false,
1512 "bit pointer (index) valid");
1513 sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7);
1514 sgj_js_nv_ihex(jsp, jop, "field_pointer",
1515 sg_get_unaligned_be16(dp + 1));
1516 break;
1517 case SPC_SK_UNIT_ATTENTION:
1518 if (dlen < 7) {
1519 sgj_js_nv_s(jsp, jop, "segment_indication_sks", dtsp);
1520 return false;
1521 }
1522 sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1523 sksvp);
1524 sgj_js_nv_i(jsp, jop, "overflow", !! (dp[0] & 0x80));
1525 break;
1526 default:
1527 sgj_js_nv_ihex(jsp, jop, "unexpected_sense_key", sense_key);
1528 return false;
1529 }
1530 return true;
1531 }
1532
1533 #define TPGS_STATE_OPTIMIZED 0x0
1534 #define TPGS_STATE_NONOPTIMIZED 0x1
1535 #define TPGS_STATE_STANDBY 0x2
1536 #define TPGS_STATE_UNAVAILABLE 0x3
1537 #define TPGS_STATE_OFFLINE 0xe
1538 #define TPGS_STATE_TRANSITIONING 0xf
1539
1540 static int
decode_tpgs_state(int st,char * b,int blen)1541 decode_tpgs_state(int st, char * b, int blen)
1542 {
1543 switch (st) {
1544 case TPGS_STATE_OPTIMIZED:
1545 return sg_scnpr(b, blen, "active/optimized");
1546 case TPGS_STATE_NONOPTIMIZED:
1547 return sg_scnpr(b, blen, "active/non optimized");
1548 case TPGS_STATE_STANDBY:
1549 return sg_scnpr(b, blen, "standby");
1550 case TPGS_STATE_UNAVAILABLE:
1551 return sg_scnpr(b, blen, "unavailable");
1552 case TPGS_STATE_OFFLINE:
1553 return sg_scnpr(b, blen, "offline");
1554 case TPGS_STATE_TRANSITIONING:
1555 return sg_scnpr(b, blen, "transitioning between states");
1556 default:
1557 return sg_scnpr(b, blen, "unknown: 0x%x", st);
1558 }
1559 }
1560
1561 static bool
sgj_uds_referral_descriptor(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * dp,int alen)1562 sgj_uds_referral_descriptor(sgj_state * jsp, sgj_opaque_p jop,
1563 const uint8_t * dp, int alen)
1564 {
1565 int dlen = alen - 2;
1566 int k, j, g, f, aas;
1567 uint64_t ull;
1568 const uint8_t * tp;
1569 sgj_opaque_p jap, jo2p, ja2p, jo3p;
1570 char c[40];
1571
1572 sgj_js_nv_ihex_nex(jsp, jop, "not_all_r", (dp[2] & 0x1), false,
1573 "Not all referrals");
1574 dp += 4;
1575 jap = sgj_named_subarray_r(jsp, jop,
1576 "user_data_segment_referral_descriptor_list");
1577 for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) {
1578 int ntpgd = dp[3];
1579
1580 jo2p = sgj_new_unattached_object_r(jsp);
1581 g = (ntpgd * 4) + 20;
1582 sgj_js_nv_ihex(jsp, jo2p, "number_of_target_port_group_descriptors",
1583 ntpgd);
1584 if ((k + g) > dlen) {
1585 sgj_js_nv_i(jsp, jo2p, "truncated_descriptor_dlen", dlen);
1586 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1587 return false;
1588 }
1589 ull = sg_get_unaligned_be64(dp + 4);
1590 sgj_js_nv_ihex(jsp, jo2p, "first_user_date_sgment_lba", ull);
1591 ull = sg_get_unaligned_be64(dp + 12);
1592 sgj_js_nv_ihex(jsp, jo2p, "last_user_date_sgment_lba", ull);
1593 ja2p = sgj_named_subarray_r(jsp, jo2p,
1594 "target_port_group_descriptor_list");
1595 for (j = 0; j < ntpgd; ++j) {
1596 jo3p = sgj_new_unattached_object_r(jsp);
1597 tp = dp + 20 + (j * 4);
1598 aas = tp[0] & 0xf;
1599 decode_tpgs_state(aas, c, sizeof(c));
1600 sgj_js_nv_ihexstr(jsp, jo3p, "asymmetric_access_state", aas,
1601 NULL, c);
1602 sgj_js_nv_ihex(jsp, jo3p, "target_port_group",
1603 sg_get_unaligned_be16(tp + 2));
1604 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
1605 }
1606 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1607 }
1608 return true;
1609 }
1610
1611 /* Copy of static array in sg_lib.c */
1612 static const char * dd_usage_reason_str_arr[] = {
1613 "Unknown",
1614 "resend this and further commands to:",
1615 "resend this command to:",
1616 "new subsidiary lu added to this administrative lu:",
1617 "administrative lu associated with a preferred binding:",
1618 };
1619
1620 static bool
sgj_js_sense_descriptors(sgj_state * jsp,sgj_opaque_p jop,const struct sg_scsi_sense_hdr * sshp,const uint8_t * sbp,int sb_len)1621 sgj_js_sense_descriptors(sgj_state * jsp, sgj_opaque_p jop,
1622 const struct sg_scsi_sense_hdr * sshp,
1623 const uint8_t * sbp, int sb_len)
1624 {
1625 bool processed = true;
1626 int add_sb_len, desc_len, k, dt, sense_key, n, sds;
1627 uint16_t sct_sc;
1628 uint64_t ull;
1629 const uint8_t * descp;
1630 const char * cp;
1631 sgj_opaque_p jap, jo2p, jo3p;
1632 char b[80];
1633 static const int blen = sizeof(b);
1634 static const char * parsing = "parsing_error";
1635 #if 0
1636 static const char * eccp = "Extended copy command";
1637 static const char * ddp = "destination device";
1638 #endif
1639
1640 add_sb_len = sshp->additional_length;
1641 add_sb_len = (add_sb_len < sb_len) ? add_sb_len : sb_len;
1642 sense_key = sshp->sense_key;
1643 jap = sgj_named_subarray_r(jsp, jop, "sense_data_descriptor_list");
1644
1645 for (descp = sbp, k = 0; (k < add_sb_len);
1646 k += desc_len, descp += desc_len) {
1647 int add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1;
1648
1649 jo2p = sgj_new_unattached_object_r(jsp);
1650 if ((k + add_d_len + 2) > add_sb_len)
1651 add_d_len = add_sb_len - k - 2;
1652 desc_len = add_d_len + 2;
1653 processed = true;
1654 dt = descp[0];
1655 switch (dt) {
1656 case 0:
1657 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt,
1658 NULL, "Information");
1659 if (add_d_len >= 10) {
1660 int valid = !! (0x80 & descp[2]);
1661 sgj_js_nv_ihexstr(jsp, jo2p, "valid", valid, NULL,
1662 valid ? "as per T10" : "Vendor specific");
1663 sgj_js_nv_ihex(jsp, jo2p, "information",
1664 sg_get_unaligned_be64(descp + 4));
1665 } else {
1666 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1667 processed = false;
1668 }
1669 break;
1670 case 1:
1671 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt,
1672 NULL, "Command specific");
1673 if (add_d_len >= 10) {
1674 sgj_js_nv_ihex(jsp, jo2p, "command_specific_information",
1675 sg_get_unaligned_be64(descp + 4));
1676 } else {
1677 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1678 processed = false;
1679 }
1680 break;
1681 case 2: /* Sense Key Specific */
1682 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1683 "Sense key specific");
1684 processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4,
1685 sense_key);
1686 break;
1687 case 3:
1688 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1689 "Field replaceable unit code");
1690 if (add_d_len >= 2)
1691 sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code",
1692 descp[3]);
1693 else {
1694 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1695 processed = false;
1696 }
1697 break;
1698 case 4:
1699 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1700 "Stream commands");
1701 if (add_d_len >= 2) {
1702 sgj_js_nv_i(jsp, jo2p, "filemark", !! (descp[3] & 0x80));
1703 sgj_js_nv_ihex_nex(jsp, jo2p, "eom", !! (descp[3] & 0x40),
1704 false, "End Of Medium");
1705 sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20),
1706 false, "Incorrect Length Indicator");
1707 } else {
1708 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1709 processed = false;
1710 }
1711 break;
1712 case 5:
1713 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1714 "Block commands");
1715 if (add_d_len >= 2)
1716 sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20),
1717 false, "Incorrect Length Indicator");
1718 else {
1719 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1720 processed = false;
1721 }
1722 break;
1723 case 6:
1724 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1725 "OSD object identification");
1726 sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
1727 processed = false;
1728 break;
1729 case 7:
1730 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1731 "OSD response integrity check value");
1732 sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
1733 break;
1734 case 8:
1735 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1736 "OSD attribute identification");
1737 sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
1738 processed = false;
1739 break;
1740 case 9: /* this is defined in SAT (SAT-2) */
1741 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1742 "ATA status return");
1743 if (add_d_len >= 12) {
1744 sgj_js_nv_i(jsp, jo2p, "extend", !! (descp[2] & 1));
1745 sgj_js_nv_ihex(jsp, jo2p, "error", descp[3]);
1746 sgj_js_nv_ihex(jsp, jo2p, "count",
1747 sg_get_unaligned_be16(descp + 4));
1748 ull = ((uint64_t)descp[10] << 40) |
1749 ((uint64_t)descp[8] << 32) |
1750 (descp[6] << 24) |
1751 (descp[11] << 16) |
1752 (descp[9] << 8) |
1753 descp[7];
1754 sgj_js_nv_ihex(jsp, jo2p, "lba", ull);
1755 sgj_js_nv_ihex(jsp, jo2p, "device", descp[12]);
1756 sgj_js_nv_ihex(jsp, jo2p, "status", descp[13]);
1757 } else {
1758 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1759 processed = false;
1760 }
1761 break;
1762 case 0xa:
1763 /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */
1764 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1765 "Another progress indication");
1766 if (add_d_len < 6) {
1767 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1768 processed = false;
1769 break;
1770 }
1771 sgj_js_nv_ihex(jsp, jo2p, "another_sense_key", descp[2]);
1772 sgj_js_nv_ihex(jsp, jo2p, "another_additional_sense_code",
1773 descp[3]);
1774 sgj_js_nv_ihex(jsp, jo2p,
1775 "another_additional_sense_code_qualifier",
1776 descp[4]);
1777 sgj_progress_indication(jsp, jo2p,
1778 sg_get_unaligned_be16(descp + 6), true);
1779 break;
1780 case 0xb: /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */
1781 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1782 "User data segment referral");
1783 if (add_d_len < 2) {
1784 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1785 processed = false;
1786 break;
1787 }
1788 if (! sgj_uds_referral_descriptor(jsp, jo2p, descp, add_d_len)) {
1789 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1790 processed = false;
1791 }
1792 break;
1793 case 0xc: /* Added in SPC-4 rev 28 */
1794 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1795 "Forwarded sense data");
1796 if (add_d_len < 2) {
1797 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1798 processed = false;
1799 break;
1800 }
1801 sgj_js_nv_ihex_nex(jsp, jo2p, "fsdt", !! (0x80 & descp[2]),
1802 false, "Forwarded Sense Data Truncated");
1803 sds = (0x7 & descp[2]);
1804 if (sds < 1)
1805 snprintf(b, blen, "%s [%d]", "Unknown", sds);
1806 else if (sds > 9)
1807 snprintf(b, blen, "%s [%d]", "Reserved", sds);
1808 else {
1809 n = 0;
1810 n += sg_scnpr(b + n, blen - n, "EXTENDED COPY command copy %s",
1811 (sds == 1) ? "source" : "destination");
1812 if (sds > 1)
1813 n += sg_scnpr(b + n, blen - n, " %d", sds - 1);
1814 }
1815 sgj_js_nv_ihexstr(jsp, jo2p, "sense_data_source",
1816 (0x7 & descp[2]), NULL, b);
1817 jo3p = sgj_named_subobject_r(jsp, jo2p, "forwarded_sense_data");
1818 sgj_js_sense(jsp, jo3p, descp + 4, desc_len - 4);
1819 break;
1820 case 0xd: /* Added in SBC-3 rev 36d */
1821 /* this descriptor combines descriptors 0, 1, 2 and 3 */
1822 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1823 "Direct-access block device");
1824 if (add_d_len < 28) {
1825 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1826 processed = false;
1827 break;
1828 }
1829 sgj_js_nv_i(jsp, jo2p, "valid", (0x80 & descp[2]));
1830 sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (0x20 & descp[2]),
1831 false, "Incorrect Length Indicator");
1832 processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4,
1833 sense_key);
1834 sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code",
1835 descp[7]);
1836 sgj_js_nv_ihex(jsp, jo2p, "information",
1837 sg_get_unaligned_be64(descp + 8));
1838 sgj_js_nv_ihex(jsp, jo2p, "command_specific_information",
1839 sg_get_unaligned_be64(descp + 16));
1840 break;
1841 case 0xe: /* Added in SPC-5 rev 6 (for Bind/Unbind) */
1842 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1843 "Device designation");
1844 n = descp[3];
1845 cp = (n < (int)SG_ARRAY_SIZE(dd_usage_reason_str_arr)) ?
1846 dd_usage_reason_str_arr[n] : "Unknown (reserved)";
1847 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_usage_reason",
1848 n, NULL, cp);
1849 jo3p = sgj_named_subobject_r(jsp, jo2p,
1850 "device_designation_descriptor");
1851 sgj_js_designation_descriptor(jsp, jo3p, descp + 4, desc_len - 4);
1852 break;
1853 case 0xf: /* Added in SPC-5 rev 10 (for Write buffer) */
1854 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1855 "Microcode activation");
1856 if (add_d_len < 6) {
1857 sgj_js_nv_s(jsp, jop, parsing, dtsp);
1858 processed = false;
1859 break;
1860 }
1861 sgj_js_nv_ihex(jsp, jo2p, "microcode_activation_time",
1862 sg_get_unaligned_be16(descp + 6));
1863 break;
1864 case 0xde: /* NVME Status Field; vendor (sg3_utils) specific */
1865 sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1866 "NVME status (sg3_utils)");
1867 if (add_d_len < 6) {
1868 sgj_js_nv_s(jsp, jop, parsing, dtsp);
1869 processed = false;
1870 break;
1871 }
1872 sgj_js_nv_ihex_nex(jsp, jo2p, "dnr", !! (0x80 & descp[5]),
1873 false, "Do not retry");
1874 sgj_js_nv_ihex_nex(jsp, jo2p, "m", !! (0x40 & descp[5]),
1875 false, "More");
1876 sct_sc = sg_get_unaligned_be16(descp + 6);
1877 sgj_js_nv_ihexstr_nex
1878 (jsp, jo2p, "sct_sc", sct_sc, true, NULL,
1879 sg_get_nvme_cmd_status_str(sct_sc, blen, b),
1880 "Status Code Type (upper 8 bits) and Status Code");
1881 break;
1882 default:
1883 if (dt >= 0x80)
1884 sgj_js_nv_ihex(jsp, jo2p, "vendor_specific_descriptor_type",
1885 dt);
1886 else
1887 sgj_js_nv_ihex(jsp, jo2p, "unknown_descriptor_type", dt);
1888 sgj_js_nv_hex_bytes(jsp, jo2p, "descriptor_hexbytes",
1889 descp, desc_len);
1890 processed = false;
1891 break;
1892 }
1893 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1894 }
1895 return processed;
1896 }
1897
1898 #define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d /* corresponding ASC is 0 */
1899
1900 /* Fetch sense information */
1901 bool
sgj_js_sense(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * sbp,int sb_len)1902 sgj_js_sense(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * sbp,
1903 int sb_len)
1904 {
1905 bool descriptor_format = false;
1906 bool sdat_ovfl = false;
1907 bool ret = true;
1908 bool valid_info_fld;
1909 int len, n;
1910 uint32_t info;
1911 uint8_t resp_code;
1912 const char * ebp = NULL;
1913 char ebuff[64];
1914 char b[256];
1915 struct sg_scsi_sense_hdr ssh;
1916 static int blen = sizeof(b);
1917 static int elen = sizeof(ebuff);
1918
1919 if ((NULL == sbp) || (sb_len < 1)) {
1920 snprintf(ebuff, elen, "sense buffer empty\n");
1921 ebp = ebuff;
1922 ret = false;
1923 goto fini;
1924 }
1925 resp_code = 0x7f & sbp[0];
1926 valid_info_fld = !!(sbp[0] & 0x80);
1927 len = sb_len;
1928 if (! sg_scsi_normalize_sense(sbp, sb_len, &ssh)) {
1929 ebp = "unable to normalize sense buffer";
1930 ret = false;
1931 goto fini;
1932 }
1933 /* We have been able to normalize the sense buffer */
1934 switch (resp_code) {
1935 case 0x70: /* fixed, current */
1936 ebp = "Fixed format, current";
1937 len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
1938 len = (len > sb_len) ? sb_len : len;
1939 sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
1940 break;
1941 case 0x71: /* fixed, deferred */
1942 /* error related to a previous command */
1943 ebp = "Fixed format, <<<deferred>>>";
1944 len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
1945 len = (len > sb_len) ? sb_len : len;
1946 sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
1947 break;
1948 case 0x72: /* descriptor, current */
1949 descriptor_format = true;
1950 ebp = "Descriptor format, current";
1951 sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
1952 break;
1953 case 0x73: /* descriptor, deferred */
1954 descriptor_format = true;
1955 ebp = "Descriptor format, <<<deferred>>>";
1956 sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
1957 break;
1958 default:
1959 sg_scnpr(ebuff, elen, "Unknown code: 0x%x", resp_code);
1960 ebp = ebuff;
1961 break;
1962 }
1963 sgj_js_nv_ihexstr(jsp, jop, "response_code", resp_code, NULL, ebp);
1964 sgj_js_nv_b(jsp, jop, "descriptor_format", descriptor_format);
1965 sgj_js_nv_ihex_nex(jsp, jop, "sdat_ovfl", sdat_ovfl, false,
1966 "Sense data overflow");
1967 sgj_js_nv_ihexstr(jsp, jop, "sense_key", ssh.sense_key, NULL,
1968 sg_lib_sense_key_desc[ssh.sense_key]);
1969 sgj_js_nv_ihex(jsp, jop, "additional_sense_code", ssh.asc);
1970 sgj_js_nv_ihex(jsp, jop, "additional_sense_code_qualifier", ssh.ascq);
1971 sgj_js_nv_s(jsp, jop, "additional_sense_str",
1972 sg_get_additional_sense_str(ssh.asc, ssh.ascq, false,
1973 blen, b));
1974 if (descriptor_format) {
1975 if (len > 8) {
1976 ret = sgj_js_sense_descriptors(jsp, jop, &ssh, sbp + 8, len - 8);
1977 if (ret == false) {
1978 ebp = "unable to decode sense descriptor";
1979 goto fini;
1980 }
1981 }
1982 } else if ((len > 12) && (0 == ssh.asc) &&
1983 (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
1984 /* SAT ATA PASS-THROUGH fixed format */
1985 sgj_js_nv_ihex(jsp, jop, "error", sbp[3]);
1986 sgj_js_nv_ihex(jsp, jop, "status", sbp[4]);
1987 sgj_js_nv_ihex(jsp, jop, "device", sbp[5]);
1988 sgj_js_nv_i(jsp, jop, "extend", !! (0x80 & sbp[8]));
1989 sgj_js_nv_i(jsp, jop, "count_upper_nonzero", !! (0x40 & sbp[8]));
1990 sgj_js_nv_i(jsp, jop, "lba_upper_nonzero", !! (0x20 & sbp[8]));
1991 sgj_js_nv_i(jsp, jop, "log_index", (0xf & sbp[8]));
1992 sgj_js_nv_i(jsp, jop, "lba", sg_get_unaligned_le24(sbp + 9));
1993 } else if (len > 2) { /* fixed format */
1994 sgj_js_nv_i(jsp, jop, "valid", valid_info_fld);
1995 sgj_js_nv_i(jsp, jop, "filemark", !! (sbp[2] & 0x80));
1996 sgj_js_nv_ihex_nex(jsp, jop, "eom", !! (sbp[2] & 0x40),
1997 false, "End Of Medium");
1998 sgj_js_nv_ihex_nex(jsp, jop, "ili", !! (sbp[2] & 0x20),
1999 false, "Incorrect Length Indicator");
2000 info = sg_get_unaligned_be32(sbp + 3);
2001 sgj_js_nv_ihex(jsp, jop, "information", info);
2002 sgj_js_nv_ihex(jsp, jop, "additional_sense_length", sbp[7]);
2003 if (sb_len > 11) {
2004 info = sg_get_unaligned_be32(sbp + 8);
2005 sgj_js_nv_ihex(jsp, jop, "command_specific_information", info);
2006 }
2007 if (sb_len > 14)
2008 sgj_js_nv_ihex(jsp, jop, "field_replaceable_unit_code", sbp[14]);
2009 if (sb_len > 17)
2010 sgj_decode_sks(jsp, jop, sbp + 15, sb_len - 15, ssh.sense_key);
2011 n = sbp[7];
2012 n = (sb_len > n) ? n : sb_len;
2013 sgj_js_nv_ihex(jsp, jop, "number_of_bytes_beyond_18",
2014 (n > 18) ? n - 18 : 0);
2015 } else {
2016 snprintf(ebuff, sizeof(ebuff), "sb_len=%d too short", sb_len);
2017 ebp = ebuff;
2018 ret = false;
2019 }
2020 fini:
2021 if ((! ret) && ebp)
2022 sgj_js_nv_s(jsp, jop, "sense_decode_error", ebp);
2023 return ret;
2024 }
2025
2026 #endif
2027