-
Notifications
You must be signed in to change notification settings - Fork 20
/
sg_logs.c
10330 lines (9955 loc) · 381 KB
/
sg_logs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* A utility program originally written for the Linux OS SCSI subsystem.
* Copyright (C) 2000-2023 D. Gilbert
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program outputs information provided by a SCSI LOG SENSE command
* and in some cases issues a LOG SELECT command.
*
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#include <errno.h>
#include <sys/stat.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sg_lib.h"
#include "sg_lib_names.h"
#include "sg_cmds_basic.h"
#ifdef SG_LIB_WIN32
#include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
#endif
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
#include "sg_logs.h"
static const char * version_str = "2.37 20231026"; /* spc6r10 + sbc5r05 */
#define MY_NAME "sg_logs"
const char * const in_hex = "in_hex";
const char * const param_c = "Parameter code";
const char * const param_c_sn = "parameter_code";
const char * const rsv_s = "reserved";
const char * const unkn_s = "unknown";
const char * const vend_spec = "vendor specific";
static uint8_t * rsp_buff;
static uint8_t * free_rsp_buff;
static int rsp_buff_sz = MX_ALLOC_LEN + 4;
static const int parr_sz = 4096;
static const char * const as_s_s = "as_string";
static const char * const lba_sn = "logical_block_address";
static const char * const not_avail = "not available";
static const char * const not_rep = "not reported";
static const char * const pg_c_sn = "page_code";
static const char * const spg_c_sn = "subpage_code";
static const char * const param_s = "parameter";
static const char * const rstrict_s = "restricted";
static const char * const sns_key_s = "sense key";
static const char * const sns_key_sn = "sense_key";
static const char * const asc_s = "Additional sense code";
static const char * const asc_sn = "additional_sense_code";
static const char * const ass_sn = "additional_sense_string";
static const char * const qual_s = "qualifier";
static const char * const acsq_sn = "additional_sense_code_qualifier";
static const char * const lp_sn = "log_page";
static const char * const tims_s = "Timestamp";
static const char * const tims_sn = "timestamp";
static const char * const percent_s = "[percentage]";
static const struct option long_options[] = {
{"All", no_argument, 0, 'A'}, /* equivalent to '-aa' */
{"ALL", no_argument, 0, 'A'}, /* equivalent to '-aa' */
{"all", no_argument, 0, 'a'},
{"brief", no_argument, 0, 'b'},
{"control", required_argument, 0, 'c'},
{"enumerate", no_argument, 0, 'e'},
{"exclude", no_argument, 0, 'E'},
{"filter", required_argument, 0, 'f'},
{"full", no_argument, 0, 'F'},
{"help", no_argument, 0, 'h'},
{"hex", no_argument, 0, 'H'},
{"in", required_argument, 0, 'i'},
{"inhex", required_argument, 0, 'i'},
{"json", optional_argument, 0, '^'}, /* short option is '-j' */
{"js_file", required_argument, 0, 'J'},
{"js-file", required_argument, 0, 'J'},
{"list", no_argument, 0, 'l'},
{"maxlen", required_argument, 0, 'm'},
{"name", no_argument, 0, 'n'},
{"new", no_argument, 0, 'N'},
{"no_inq", no_argument, 0, 'x'},
{"no-inq", no_argument, 0, 'x'},
{"old", no_argument, 0, 'O'},
{"page", required_argument, 0, 'p'},
{"paramp", required_argument, 0, 'P'},
{"pcb", no_argument, 0, 'q'},
{"ppc", no_argument, 0, 'Q'},
{"pdt", required_argument, 0, 'D'},
{"raw", no_argument, 0, 'r'},
{"readonly", no_argument, 0, 'X'},
{"reset", no_argument, 0, 'R'},
{"sp", no_argument, 0, 's'},
{"select", no_argument, 0, 'S'},
{"temperature", no_argument, 0, 't'},
{"transport", no_argument, 0, 'T'},
{"undefined", no_argument, 0, 'u'},
{"vendor", required_argument, 0, 'M'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0},
};
static const char * ls_s = "log_sense: ";
/* Following show_* functions need to match the signature of the
* function pointer: log_elem:show_pagep */
static bool show_supported_pgs_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_supported_pgs_sub_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_buffer_over_under_run_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_error_counter_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_non_medium_error_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_last_n_error_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_format_status_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_last_n_deferred_error_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_last_n_inq_data_ch_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_last_n_mode_pg_data_ch_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_lb_provisioning_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_sequential_access_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_temperature_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_start_stop_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_utilization_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_app_client_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_self_test_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_solid_state_media_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_device_stats_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_media_stats_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_dt_device_status_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_tapealert_response_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_requested_recovery_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_background_scan_results_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_zoned_block_dev_stats(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_pending_defects_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_background_op_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_lps_misalignment_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_element_stats_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_service_buffers_info_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_ata_pt_results_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_tape_diag_data_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_mchanger_diag_data_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_non_volatile_cache_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_volume_stats_pages(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_protocol_specific_port_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_stats_perform_pages(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_cache_stats_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_power_condition_transitions_page(const uint8_t * resp,
int len, struct opts_t * op,
sgj_opaque_p jop);
static bool show_environmental_reporting_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_environmental_limits_page(const uint8_t * resp, int len,
struct opts_t * op,
sgj_opaque_p jop);
static bool show_cmd_dur_limits_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_tape_alert_ssc_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
static bool show_ie_page(const uint8_t * resp, int len,
struct opts_t * op, sgj_opaque_p jop);
/* elements in page_number/subpage_number order */
static const struct log_elem log_arr[] = {
{SUPP_PAGES_LPAGE, 0, 0, -1, MVP_STD, "Supported log pages", "sp",
show_supported_pgs_page}, /* 0, 0 */
{SUPP_PAGES_LPAGE, SUPP_SPGS_SUBPG, 0, -1, MVP_STD, "Supported log pages "
"and subpages", "ssp", show_supported_pgs_sub_page}, /* 0, 0xff */
{BUFF_OVER_UNDER_LPAGE, 0, 0, -1, MVP_STD, "Buffer over-run/under-run",
"bou", show_buffer_over_under_run_page}, /* 0x1, 0x0 */
{WRITE_ERR_LPAGE, 0, 0, -1, MVP_STD, "Write error counters", "we",
show_error_counter_page}, /* 0x2, 0x0 */
{READ_ERR_LPAGE, 0, 0, -1, MVP_STD, "Read error counters", "re",
show_error_counter_page}, /* 0x3, 0x0 */
{READ_REV_ERR_LPAGE, 0, 0, -1, MVP_STD, "Read reverse error counters",
"rre", show_error_counter_page}, /* 0x4, 0x0 */
{VERIFY_ERR_LPAGE, 0, 0, -1, MVP_STD, "Verify error counters", "ve",
show_error_counter_page}, /* 0x5, 0x0 */
{NON_MEDIUM_LPAGE, 0, 0, -1, MVP_STD, "Non medium", "nm",
show_non_medium_error_page}, /* 0x6, 0x0 */
{LAST_N_ERR_LPAGE, 0, 0, -1, MVP_STD, "Last n error", "lne",
show_last_n_error_page}, /* 0x7, 0x0 */
{FORMAT_STATUS_LPAGE, 0, 0, 0, MVP_STD, "Format status", "fs",
show_format_status_page}, /* 0x8, 0x0 SBC */
{LAST_N_DEFERRED_LPAGE, 0, 0, -1, MVP_STD, "Last n deferred error", "lnd",
show_last_n_deferred_error_page}, /* 0xb, 0x0 */
{LAST_N_DEFERRED_LPAGE, LAST_N_INQUIRY_DATA_CH_SUBPG, 0, -1, MVP_STD,
"Last n inquiry data changed", "lnic",
show_last_n_inq_data_ch_page}, /* 0xb, 0x1 */
{LAST_N_DEFERRED_LPAGE, LAST_N_MODE_PG_DATA_CH_SUBPG, 0, -1, MVP_STD,
"Last n mode page data changed", "lnmc",
show_last_n_mode_pg_data_ch_page}, /* 0xb, 0x2 */
{LB_PROV_LPAGE, 0, 0, 0, MVP_STD, "Logical block provisioning", "lbp",
show_lb_provisioning_page}, /* 0xc, 0x0 SBC */
{0xc, 0, 0, PDT_TAPE, MVP_STD, "Sequential access device", "sad",
show_sequential_access_page}, /* 0xc, 0x0 SSC */
{TEMPERATURE_LPAGE, 0, 0, -1, MVP_STD, "Temperature", "temp",
show_temperature_page}, /* 0xd, 0x0 */
{TEMPERATURE_LPAGE, ENV_REPORTING_SUBPG, 0, -1, MVP_STD, /* 0xd, 0x1 */
"Environmental reporting", "enr", show_environmental_reporting_page},
{TEMPERATURE_LPAGE, ENV_LIMITS_SUBPG, 0, -1, MVP_STD, /* 0xd, 0x2 */
"Environmental limits", "enl", show_environmental_limits_page},
{START_STOP_LPAGE, 0, 0, -1, MVP_STD, "Start-stop cycle counter", "sscc",
show_start_stop_page}, /* 0xe, 0x0 */
{START_STOP_LPAGE, UTILIZATION_SUBPG, 0, 0, MVP_STD, "Utilization",
"util", show_utilization_page}, /* 0xe, 0x1 SBC */ /* sbc4r04 */
{APP_CLIENT_LPAGE, 0, 0, -1, MVP_STD, "Application client", "ac",
show_app_client_page}, /* 0xf, 0x0 */
{SELF_TEST_LPAGE, 0, 0, -1, MVP_STD, "Self test results", "str",
show_self_test_page}, /* 0x10, 0x0 */
{SOLID_STATE_MEDIA_LPAGE, 0, 0, 0, MVP_STD, "Solid state media", "ssm",
show_solid_state_media_page}, /* 0x11, 0x0 SBC */
{0x11, 0, 0, PDT_TAPE, MVP_STD, "DT Device status", "dtds",
show_dt_device_status_page}, /* 0x11, 0x0 SSC,ADC */
{0x12, 0, 0, PDT_TAPE, MVP_STD, "Tape alert response", "tar",
show_tapealert_response_page}, /* 0x12, 0x0 ADC,SSC */
{REQ_RECOVERY_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Requested recovery", "rr",
show_requested_recovery_page}, /* 0x13, 0x0 SSC,ADC */
{DEVICE_STATS_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Device statistics", "ds",
show_device_stats_page}, /* 0x14, 0x0 SSC,ADC */
{DEVICE_STATS_LPAGE, 0, 0, PDT_MCHANGER, MVP_STD, /* 0x14, 0x0 SMC */
"Media changer statistics", "mcs", show_media_stats_page},
{DEVICE_STATS_LPAGE, ZONED_BLOCK_DEV_STATS_SUBPG, /* 0x14,0x1 zbc2r01 */
0, 0, MVP_STD, "Zoned block device statistics", "zbds",
show_zoned_block_dev_stats},
{BACKGROUND_SCAN_LPAGE, 0, 0, 0, MVP_STD, "Background scan results",
"bsr", show_background_scan_results_page}, /* 0x15, 0x0 SBC */
{BACKGROUND_SCAN_LPAGE, BACKGROUND_OP_SUBPG, 0, 0, MVP_STD,
"Background operation", "bop", show_background_op_page},
/* 0x15, 0x2 SBC */
{BACKGROUND_SCAN_LPAGE, LPS_MISALIGNMENT_SUBPG, 0, 0, MVP_STD,
"LPS misalignment", "lps", show_lps_misalignment_page},
/* 0x15, 0x3 SBC-4 */
{0x15, 0, 0, PDT_MCHANGER, MVP_STD, "Element statistics", "els",
show_element_stats_page}, /* 0x15, 0x0 SMC */
{0x15, 0, 0, PDT_ADC, MVP_STD, "Service buffers information", "sbi",
show_service_buffers_info_page}, /* 0x15, 0x0 ADC */
{BACKGROUND_SCAN_LPAGE, PENDING_DEFECTS_SUBPG, 0, 0, MVP_STD,
"Pending defects", "pd", show_pending_defects_page}, /* 0x15, 0x1 SBC */
{SAT_ATA_RESULTS_LPAGE, 0, 0, 0, MVP_STD, "ATA pass-through results",
"aptr", show_ata_pt_results_page}, /* 0x16, 0x0 SAT */
{0x16, 0, 0, PDT_TAPE, MVP_STD, "Tape diagnostic data", "tdd",
show_tape_diag_data_page}, /* 0x16, 0x0 SSC */
{0x16, 0, 0, PDT_MCHANGER, MVP_STD, "Media changer diagnostic data",
"mcdd", show_mchanger_diag_data_page}, /* 0x16, 0x0 SMC */
{0x17, 0, 0, 0, MVP_STD, "Non volatile cache", "nvc",
show_non_volatile_cache_page}, /* 0x17, 0x0 SBC */
{0x17, 0, 0xf, PDT_TAPE, MVP_STD, "Volume statistics", "vs",
show_volume_stats_pages}, /* 0x17, 0x0...0xf SSC */
{PROTO_SPECIFIC_LPAGE, 0, 0, -1, MVP_STD, "Protocol specific port",
"psp", show_protocol_specific_port_page}, /* 0x18, 0x0 */
{STATS_LPAGE, 0, 0, -1, MVP_STD, "General Statistics and Performance",
"gsp", show_stats_perform_pages}, /* 0x19, 0x0 */
{STATS_LPAGE, 0x1, 0x1f, -1, MVP_STD, "Group Statistics and Performance",
"grsp", show_stats_perform_pages}, /* 0x19, 0x1...0x1f */
{STATS_LPAGE, CACHE_STATS_SUBPG, 0, -1, MVP_STD, /* 0x19, 0x20 */
"Cache memory statistics", "cms", show_cache_stats_page},
{STATS_LPAGE, CMD_DUR_LIMITS_SUBPG, 0, -1, MVP_STD, /* 0x19, 0x21 */
"Command duration limits statistics", "cdl",
show_cmd_dur_limits_page /* spc6r01 */ },
{PCT_LPAGE, 0, 0, -1, MVP_STD, "Power condition transitions", "pct",
show_power_condition_transitions_page}, /* 0x1a, 0 */
{0x1b, 0, 0, PDT_TAPE, MVP_STD, "Data compression", "dc",
show_data_compression_page}, /* 0x1b, 0 SSC */
{0x2d, 0, 0, PDT_TAPE, MVP_STD, "Current service information", "csi",
NULL}, /* 0x2d, 0 SSC */
{TAPE_ALERT_LPAGE, 0, 0, PDT_TAPE, MVP_STD, "Tape alert", "ta",
show_tape_alert_ssc_page}, /* 0x2e, 0 SSC */
{IE_LPAGE, 0, 0, -1, (MVP_STD | MVP_HITA),
"Informational exceptions", "ie", show_ie_page}, /* 0x2f, 0 */
/* vendor specific */
{0x30, 0, 0, PDT_DISK, MVP_HITA, "Performance counters (Hitachi)",
"pc_hi", show_hgst_perf_page}, /* 0x30, 0 SBC */
{0x30, 0, 0, PDT_TAPE, OVP_LTO, "Tape usage (lto-5, 6)", "tu_",
show_tape_usage_page}, /* 0x30, 0 SSC */
{0x31, 0, 0, PDT_TAPE, OVP_LTO, "Tape capacity (lto-5, 6)",
"tc_", show_tape_capacity_page}, /* 0x31, 0 SSC */
{0x32, 0, 0, PDT_TAPE, MVP_LTO5, "Data compression (lto-5)",
"dc_", show_data_compression_page}, /* 0x32, 0 SSC; redirect to 0x1b */
{0x33, 0, 0, PDT_TAPE, MVP_LTO5, "Write errors (lto-5)", "we_",
NULL}, /* 0x33, 0 SSC */
{0x34, 0, 0, PDT_TAPE, MVP_LTO5, "Read forward errors (lto-5)",
"rfe_", NULL}, /* 0x34, 0 SSC */
{0x35, 0, 0, PDT_TAPE, OVP_LTO, "DT Device Error (lto-5, 6)",
"dtde_", NULL}, /* 0x35, 0 SSC */
{0x37, 0, 0, PDT_DISK, MVP_SEAG, "Cache (seagate)", "c_se",
show_seagate_cache_page}, /* 0x37, 0 SBC */
{0x37, 0, 0, PDT_DISK, MVP_HITA, "Miscellaneous (hitachi)", "mi_hi",
show_hgst_misc_page}, /* 0x37, 0 SBC */
{0x37, 0, 0, PDT_TAPE, MVP_LTO5, "Performance characteristics "
"(lto-5)", "pc_", NULL}, /* 0x37, 0 SSC */
{0x38, 0, 0, PDT_TAPE, MVP_LTO5, "Blocks/bytes transferred "
"(lto-5)", "bbt_", NULL}, /* 0x38, 0 SSC */
{0x39, 0, 0, PDT_TAPE, MVP_LTO5, "Host port 0 interface errors "
"(lto-5)", "hp0_", NULL}, /* 0x39, 0 SSC */
{0x3a, 0, 0, PDT_TAPE, MVP_LTO5, "Drive control verification "
"(lto-5)", "dcv_", NULL}, /* 0x3a, 0 SSC */
{0x3b, 0, 0, PDT_TAPE, MVP_LTO5, "Host port 1 interface errors "
"(lto-5)", "hp1_", NULL}, /* 0x3b, 0 SSC */
{0x3c, 0, 0, PDT_TAPE, MVP_LTO5, "Drive usage information "
"(lto-5)", "dui_", NULL}, /* 0x3c, 0 SSC */
{0x3d, 0, 0, PDT_TAPE, MVP_LTO5, "Subsystem statistics (lto-5)",
"ss_", NULL}, /* 0x3d, 0 SSC */
{0x3d, 0x3, 0, PDT_DISK, MVP_SEAG, "Field access reliability metrics "
"(seagate)", "farm_se", show_seagate_farm_page}, /* 0x3d, 3 SBC */
{0x3e, 0, 0, PDT_DISK, MVP_SEAG, "Factory (seagate)", "f_se",
show_seagate_factory_page}, /* 0x3e, 0 SBC */
{0x3e, 0, 0, PDT_DISK, MVP_HITA, "Factory (hitachi)", "f_hi",
NULL}, /* 0x3e, 0 SBC */
{0x3e, 0, 0, PDT_TAPE, OVP_LTO, "Device Status (lto-5, 6)",
"ds_", NULL}, /* 0x3e, 0 SSC */
{-1, -1, -1, -1, 0, NULL, "zzzzz", NULL}, /* end sentinel */
};
/* Supported vendor product codes */
/* Arrange in alphabetical order by acronym */
static const struct vp_name_t vp_arr[] = {
{VP_SEAG, "sea", "Seagate", "SEAGATE", NULL},
{VP_HITA, "hit", "Hitachi", "HGST", NULL},
{VP_HITA, "wdc", "WDC/Hitachi", "WDC", NULL},
{VP_TOSH, "tos", "Toshiba", "TOSHIBA", NULL},
{VP_LTO5, "lto5", "LTO-5 (tape drive consortium)", NULL, NULL},
{VP_LTO6, "lto6", "LTO-6 (tape drive consortium)", NULL, NULL},
{VP_ALL, "all", "enumerate all vendor specific", NULL, NULL},
{0, NULL, NULL, NULL, NULL},
};
static char t10_vendor_str[10];
static char t10_product_str[18];
#ifdef SG_LIB_WIN32
static bool win32_spt_init_state = false;
static bool win32_spt_curr_state = false;
#endif
static void
usage(int hval)
{
if (1 == hval) {
pr2serr(
"Usage: sg_logs [-ALL] [--all] [--brief] [--control=PC] "
"[--enumerate]\n"
" [--exclude] [--filter=FL] [--full] [--help] "
"[--hex]\n"
" [--inhex=FN] [--json[=JO]] [--js_file=JFN] "
"[--list]\n"
" [--maxlen=LEN] [--name] [--no_inq] "
"[--page=PG]\n"
" [--paramp=PP] [--pcb] [--ppc] [--pdt=DT] "
"[--raw]\n"
" [--readonly] [--reset] [--select] [--sp] "
"[--temperature]\n"
" [--transport] [--undefined] [--vendor=VP] "
"[--verbose]\n"
" [--version] DEVICE\n"
" where the main options are:\n"
" --ALL|-A fetch and decode all log pages and "
"subpages\n"
" --all|-a fetch and decode all log pages, but not "
"subpages; use\n"
" twice to fetch and decode all log pages "
"and subpages\n"
" --brief|-b shorten the output of some log pages\n"
" --enumerate|-e enumerate known pages, ignore DEVICE. "
"Sort order,\n"
" '-e': all by acronym; '-ee': non-vendor "
"by acronym;\n"
" '-eee': all numerically; '-eeee': "
"non-v numerically\n"
" --filter=FL|-f FL FL is parameter code to display (def: "
"all);\n"
" with '-e' then FL>=0 enumerate that "
"pdt + spc\n"
" FL=-1 all (default), FL=-2 spc only\n"
" --full|-F drill down in application client log page\n"
" --help|-h print usage message then exit. Use twice "
"for more help\n"
" --hex|-H output response in hex (default: decode if "
"known)\n"
" --inhex=FN|-i FN FN is a filename containing a log page "
"in ASCII hex\n"
" or binary if --raw also given. --inhex=FN "
"also accepted\n"
" --in=FN|-i FN same as --inhex=FN\n"
" --json[=JO]|-j[=JO] output in JSON instead of plain "
"text\n"
" Use --json=? for JSON help\n"
" --js-file=JFN|-J JFN JFN is a filename to which JSON "
"output is\n"
" written (def: stdout); truncates "
"then writes\n"
" --list|-l list supported log pages; twice: list "
"supported log\n"
" pages and subpages page; thrice: merge of "
"both pages\n"
" --page=PG|-p PG PG is either log page acronym, PGN or "
"PGN,SPGN\n"
" where (S)PGN is a (sub) page number\n");
pr2serr(
" --temperature|-t decode temperature (log page 0xd or "
"0x2f)\n"
" --transport|-T decode transport (protocol specific port "
"0x18) page\n"
" --vendor=VP|-M VP vendor/product abbreviation [or "
"number]\n"
" --verbose|-v increase verbosity\n\n"
"Performs a SCSI LOG SENSE (or LOG SELECT) command and decodes "
"the response.\nIf only DEVICE is given then '-p sp' (supported "
"pages) is assumed. Use\n'-e' to see known pages and their "
"acronyms. For more help use '-hh'.\n");
} else if (hval > 1) {
pr2serr(
" where sg_logs' lesser used options are:\n"
" --control=PC|-c PC page control(PC) (default: 1)\n"
" 0: current threshold, 1: current "
"cumulative\n"
" 2: default threshold, 3: default "
"cumulative\n"
" --exclude|-E exclude vendor specific pages and "
"parameters\n"
" --list|-l list supported log page names (equivalent to "
"'-p sp')\n"
" use twice to list supported log page and "
"subpage names\n"
" --maxlen=LEN|-m LEN max response length (def: 0 "
"-> everything)\n"
" when > 1 will request LEN bytes\n"
" --name|-n decode some pages into multiple name=value "
"lines\n"
" --no_inq|-x no initial INQUIRY output (twice: and no "
"INQUIRY call)\n"
" --old|-O use old interface (use as first option)\n"
" --paramp=PP|-P PP place PP in parameter pointer field in "
"cdb (def: 0)\n"
" --pcb|-q show parameter control bytes in decoded "
"output\n"
" --ppc|-Q set the Parameter Pointer Control (PPC) bit "
"(def: 0)\n"
" --pdt=DT|-D DT DT is peripheral device type to use with "
"'--in=FN',\n"
" when '--no_inq' is used, or with "
"'--enumerate'\n"
" --raw|-r either output response in binary to stdout "
"or, if\n"
" '--in=FN' is given, FN is decoded as "
"binary\n"
" --readonly|-X open DEVICE read-only (def: first "
"read-write then if\n"
" fails try open again read-only)\n"
" --reset|-R reset log parameters (takes PC and SP into "
"account)\n"
" (uses PCR bit in LOG SELECT)\n"
" --select|-S perform LOG SELECT (def: LOG SENSE)\n"
" --sp|-s set the Saving Parameters (SP) bit (def: "
"0)\n"
" --undefined|-u hex format for undefined/unrecognized "
"fields,\n"
" use one or more times; format as per "
"--hex\n"
" --version|-V output version string then exit\n\n"
"If DEVICE and --select are given, a LOG SELECT command will be "
"issued.\nIf DEVICE is not given and '--in=FN' is given then FN "
"will decoded as if\nit were a log page. The contents of FN is "
"generated by either a prior\n'sg_logs -HHH ...' invocation or "
"by a text editor. Log pages defined in\nSPC are common to all "
"device types.\n");
}
}
static void
usage_old()
{
printf("Usage: sg_logs [-a] [-A] [-b] [-c=PC] [-D=DT] [-e] [-E] [-f=FL] "
"[-F]\n"
" [-h] [-H] [-i=FN] [-j] [-l] [-L] [-m=LEN] [-M=VP] "
"[-n]\n"
" [-p=PG] [-paramp=PP] [-pcb] [-ppc] [-r] [-select] "
"[-sp]i\n"
" [-t] [-T] [-u] [-v] [-V] [-x] [-X] [-?] DEVICE\n"
" where:\n"
" -a fetch and decode all log pages\n"
" -A fetch and decode all log pages and subpages\n"
" -b shorten the output of some log pages\n"
" -c=PC page control(PC) (default: 1)\n"
" 0: current threshold, 1: current cumulative\n"
" 2: default threshold, 3: default cumulative\n"
" -e enumerate known log pages\n"
" -D=DT DT is peripheral device type to use with "
"'--in=FN'\n"
" -E exclude vendor specific pages and parameters\n"
" -f=FL filter match parameter code or pdt\n"
" -F drill down in application client log page\n"
" -h output in hex (default: decode if known)\n"
" -H output in hex (same as '-h')\n"
" -i=FN FN is a filename containing a log page "
"in ASCII hex.\n"
" -j produce JSON output instead of plain text "
"form\n"
" -l list supported log page names (equivalent to "
"'-p=0')\n"
" -L list supported log page and subpages names "
"(equivalent to\n"
" '-p=0,ff')\n"
" -m=LEN max response length (decimal) (def: 0 "
"-> everything)\n"
" -M=VP vendor/product abbreviation [or number]\n"
" -n decode some pages into multiple name=value "
"lines\n"
" -N|--new use new interface\n"
" -p=PG PG is an acronym (def: 'sp')\n"
" -p=PGN page code in hex (def: 0)\n"
" -p=PGN,SPGN page and subpage codes in hex, (defs: 0,0)\n"
" -paramp=PP (in hex) (def: 0)\n"
" -pcb show parameter control bytes in decoded "
"output\n");
printf(" -ppc set the Parameter Pointer Control (PPC) bit "
"(def: 0)\n"
" -r reset log parameters (takes PC and SP into "
"account)\n"
" (uses PCR bit in LOG SELECT)\n"
" -select perform LOG SELECT (def: LOG SENSE)\n"
" -sp set the Saving Parameters (SP) bit (def: 0)\n"
" -t outputs temperature log page (0xd)\n"
" -T outputs transport (protocol specific port) log "
"page (0x18)\n"
" -u hex format for undefined/unrecognized fields\n"
" -v increase verbosity\n"
" -V output version string\n"
" -x no initial INQUIRY output (twice: no INQUIRY call)\n"
" -X open DEVICE read-only (def: first read-write then "
"if fails\n"
" try open again with read-only)\n"
" -? output this usage message\n\n"
"Performs a SCSI LOG SENSE (or LOG SELECT) command\n");
}
/* Return vendor product mask given vendor product number */
static int
get_vp_mask(int vpn)
{
if (vpn < 0)
return 0;
else
return (vpn >= (32 - MVP_OFFSET)) ? OVP_ALL :
(1 << (vpn + MVP_OFFSET));
}
static int
asort_comp(const void * lp, const void * rp)
{
const struct log_elem * const * lepp =
(const struct log_elem * const *)lp;
const struct log_elem * const * repp =
(const struct log_elem * const *)rp;
return strcmp((*lepp)->acron, (*repp)->acron);
}
static void
enumerate_helper(const struct log_elem * lep, bool first, struct opts_t * op)
{
bool vendor_lpage = ! (MVP_STD & lep->flags);
const char * cp;
sgj_state * jsp = &op->json_st;
char b[80];
char bb[80];
static const int blen = sizeof(b);
if (first) {
if (1 == op->verbose) {
sgj_pr_hr(jsp, "acronym pg[,spg] name\n");
sgj_pr_hr(jsp,
"===============================================\n");
} else if (2 == op->verbose) {
sgj_pr_hr(jsp, "acronym pg[,spg] pdt name\n");
sgj_pr_hr(jsp,
"===================================================\n");
}
}
if ((0 == (op->do_enumerate % 2)) && vendor_lpage)
return; /* if do_enumerate is even then skip vendor pages */
else if ((! op->filter_given) || (-1 == op->filter))
; /* otherwise enumerate all lpages if no --filter= */
else if (-2 == op->filter) { /* skip non-SPC pages */
if (lep->pdt >= 0)
return;
} else if (-10 == op->filter) { /* skip non-disk like pages */
if (sg_lib_pdt_decay(lep->pdt) != 0)
return;
} else if (-11 == op->filter) { /* skip tape like device pages */
if (sg_lib_pdt_decay(lep->pdt) != 1)
return;
} else if ((op->filter >= 0) && (op->filter <= 0x1f)) {
if ((lep->pdt >= 0) && (lep->pdt != op->filter) &&
(lep->pdt != sg_lib_pdt_decay(op->filter)))
return;
}
if (op->vend_prod_num >= 0) {
if (! (lep->flags & get_vp_mask(op->vend_prod_num)))
return;
}
if (op->deduced_vpn >= 0) {
if (! (lep->flags & get_vp_mask(op->deduced_vpn)))
return;
}
if (lep->subpg_high > 0)
snprintf(b, blen, "0x%x,0x%x->0x%x", lep->pg_code, lep->subpg_code,
lep->subpg_high);
else if (lep->subpg_code > 0)
snprintf(b, blen, "0x%x,0x%x", lep->pg_code, lep->subpg_code);
else
snprintf(b, blen, "0x%x", lep->pg_code);
snprintf(bb, sizeof(bb), "%-16s", b);
cp = (op->verbose && (! lep->show_pagep)) ? " [hex only]" : "";
if (op->verbose > 1) {
if (lep->pdt < 0)
sgj_pr_hr(jsp, " %-8s%s- %s%s\n", lep->acron, bb, lep->name,
cp);
else
sgj_pr_hr(jsp, " %-8s%s0x%02x %s%s\n", lep->acron, bb, lep->pdt,
lep->name, cp);
} else
sgj_pr_hr(jsp, " %-8s%s%s%s\n", lep->acron, bb, lep->name, cp);
}
static void
enumerate_pages(struct opts_t * op)
{
int j;
const struct log_elem * lep;
const struct log_elem ** lep_arr;
sgj_state * jsp = &op->json_st;
char b[128];
static const int blen = sizeof(b);
static const char * lpi = "log pages in";
if (0 == (op->do_enumerate % 2))
snprintf(b, blen, "T10 (but no vendor) %s", lpi);
else
snprintf(b, blen, "All %s", lpi);
if (op->do_enumerate < 3) { /* -e, -ee: sort by acronym */
int k;
const struct log_elem ** lepp;
for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k)
;
++k;
lep_arr = (const struct log_elem **)
calloc(k, sizeof(struct log_elem *));
if (NULL == lep_arr) {
pr2serr("%s: out of memory\n", __func__);
return;
}
for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k)
lep_arr[k] = lep;
lep_arr[k++] = lep; /* put sentinel on end */
qsort(lep_arr, k, sizeof(struct log_elem *), asort_comp);
sgj_pr_hr(jsp, "%s acronym order:\n", b);
for (lepp = lep_arr, j = 0; (*lepp)->pg_code >=0; ++lepp, ++j)
enumerate_helper(*lepp, (0 == j), op);
free(lep_arr);
} else { /* -eee, -eeee numeric sort (as per table) */
sgj_pr_hr(jsp, "%s numerical order:\n", b);
for (lep = log_arr, j = 0; lep->pg_code >=0; ++lep, ++j)
enumerate_helper(lep, (0 == j), op);
}
}
static const struct log_elem *
acron_search(const char * acron)
{
const struct log_elem * lep;
for (lep = log_arr; lep->pg_code >=0; ++lep) {
if (0 == strcmp(acron, lep->acron))
return lep;
}
return NULL;
}
static int
find_vpn_by_acron(const char * vp_ap)
{
const struct vp_name_t * vpp;
for (vpp = vp_arr; vpp->acron; ++vpp) {
size_t k;
size_t len = strlen(vpp->acron);
for (k = 0; k < len; ++k) {
if (tolower((uint8_t)vp_ap[k]) != (uint8_t)vpp->acron[k])
break;
}
if (k < len)
continue;
return vpp->vend_prod_num;
}
return VP_NONE;
}
/* Find vendor product number using T10 VENDOR and PRODUCT ID fields in a
INQUIRY response. */
static int
find_vpn_by_inquiry(void)
{
size_t len;
size_t t10_v_len = strlen(t10_vendor_str);
size_t t10_p_len = strlen(t10_product_str);
const struct vp_name_t * vpp;
if ((0 == t10_v_len) && (0 == t10_p_len))
return VP_NONE;
for (vpp = vp_arr; vpp->acron; ++vpp) {
bool matched = false;
if (vpp->t10_vendorp && (t10_v_len > 0)) {
len = strlen(vpp->t10_vendorp);
len = (len > t10_v_len) ? t10_v_len : len;
if (strncmp(vpp->t10_vendorp, t10_vendor_str, len))
continue;
matched = true;
}
if (vpp->t10_productp && (t10_p_len > 0)) {
len = strlen(vpp->t10_productp);
len = (len > t10_p_len) ? t10_p_len : len;
if (strncmp(vpp->t10_productp, t10_product_str, len))
continue;
matched = true;
}
if (matched)
return vpp->vend_prod_num;
}
return VP_NONE;
}
static void
enumerate_vp(struct opts_t * op)
{
bool seen = false;
const struct vp_name_t * vpp;
sgj_state * jsp = &op->json_st;
for (vpp = vp_arr; vpp->acron; ++vpp) {
if (vpp->name) {
if (! seen) {
sgj_pr_hr(jsp, "\nVendor/product identifiers:\n");
seen = true;
}
sgj_pr_hr(jsp, " %-10s %d %s\n", vpp->acron,
vpp->vend_prod_num, vpp->name);
}
}
}
static const struct log_elem *
pg_subpg_pdt_search(int pg_code, int subpg_code, int pdt, int vpn)
{
const struct log_elem * lep;
int d_pdt;
int vp_mask = get_vp_mask(vpn);
d_pdt = sg_lib_pdt_decay(pdt);
for (lep = log_arr; lep->pg_code >=0; ++lep) {
if (pg_code == lep->pg_code) {
if (subpg_code == lep->subpg_code) {
if ((MVP_STD & lep->flags) || (0 == vp_mask) ||
(vp_mask & lep->flags))
;
else
continue;
if ((lep->pdt < 0) || (pdt == lep->pdt) || (pdt < 0))
return lep;
else if (d_pdt == lep->pdt)
return lep;
else if (pdt == sg_lib_pdt_decay(lep->pdt))
return lep;
} else if ((lep->subpg_high > 0) &&
(subpg_code > lep->subpg_code) &&
(subpg_code <= lep->subpg_high))
return lep;
}
}
return NULL;
}
static void
js_snakenv_ihexstr_nex(sgj_state * jsp, sgj_opaque_p jop,
const char * conv2sname, int64_t val_i,
bool hex_as_well, const char * str_name,
const char * val_s, const char * nex_s)
{
if ((NULL == jsp) || (NULL == jop))
return;
if (sgj_is_snake_name(conv2sname))
sgj_js_nv_ihexstr_nex(jsp, jop, conv2sname, val_i, hex_as_well,
str_name, val_s, nex_s);
else {
char b[128];
sgj_convert2snake(conv2sname, b, sizeof(b));
sgj_js_nv_ihexstr_nex(jsp, jop, b, val_i, hex_as_well, str_name,
val_s, nex_s);
}
}
static void
usage_for(int hval, const struct opts_t * op)
{
if (op->opt_new)
usage(hval);
else
usage_old();
}
/* Handles short options after '-j' including a sequence of short options
* that include one 'j' (for JSON). Want optional argument to '-j' to be
* prefixed by '='. Return 0 for good, SG_LIB_SYNTAX_ERROR for syntax error
* and SG_LIB_OK_FALSE for exit with no error. */
static int
chk_short_opts(const char sopt_ch, struct opts_t * op)
{
/* only need to process short, non-argument options */
switch (sopt_ch) {
case 'a':
++op->do_all;
break;
case 'A':
op->do_all += 2;
break;
case 'b':
++op->do_brief;
break;
case 'e':
++op->do_enumerate;
break;
case 'E':
op->exclude_vendor = true;
break;
case 'F':
op->do_full = true;
break;
case 'h':
case '?':
++op->do_help;
break;
case 'H':
++op->do_hex;
break;
case 'j':
break; /* simply ignore second 'j' (e.g. '-jxj') */
case 'l':
++op->do_list;
break;
case 'L':
op->do_list += 2;
break;
case 'n':
op->do_name = true;
break;
case 'N':
break; /* ignore */
case 'O':
op->opt_new = false;
break;
case 'q':
op->do_pcb = true;
break;
case 'Q': /* N.B. PPC bit obsoleted in SPC-4 rev 18 */
op->do_ppc = true;
break;
case 'r':
++op->do_raw;
break;
case 'R':
op->do_pcreset = true;
op->do_select = true;
break;
case 's':
op->do_sp = true;
break;
case 'S':
op->do_select = true;
break;
case 't':
op->do_temperature = true;
break;
case 'T':
op->do_transport = true;
break;
case 'u':
++op->undefined_hex;
break;
case 'v':
op->verbose_given = true;
++op->verbose;
break;
case 'V':
op->version_given = true;
break;
case 'x':
++op->no_inq;
break;
case 'X':
op->o_readonly = true;
break;
default:
pr2serr("unrecognised option code %c [0x%x]\n", sopt_ch, sopt_ch);
return SG_LIB_SYNTAX_ERROR;
}
return 0;
}
/* Processes command line options according to new option format. Returns
* 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
static int
new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
{
while (1) {
int c, n;
int option_index = 0;
c = getopt_long(argc, argv, "^aAbc:D:eEf:FhHi:j::J:lLm:M:nNOp:P:qQrR"
"sStTuvVxX", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'a':
++op->do_all;
break;
case 'A':