-
Notifications
You must be signed in to change notification settings - Fork 1
/
vol_ser_sniff.pl
480 lines (437 loc) · 14.7 KB
/
vol_ser_sniff.pl
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
#!/usr/local/bin/perl -w
# Sniff the currently mounted tape volume header label and
# format them nicely
# See the following link for IBM doco on label formats
# http://publibz.boulder.ibm.com/cgi-bin/bookmgr_OS390/BOOKS/IGG3M300/CONTENTS?SHELF=&DT=19911220181358#2.13
# http://publibz.boulder.ibm.com/cgi-bin/bookmgr_OS390/BOOKS/IGG3M300/CONTENTS?SHELF=&DT=19911220181358#3.16
#
# 2023-12-07 Wish I could get in my Delorian and go back to around the year 2008 AD and save off the contents of those URLs.
# 2023-12-08 Google saved the day, yet again.
# https://www.google.com/search?q=ibm+tape+volume+label+and+header+format
# https://www.ibm.com/docs/en/zos/2.1.0?topic=format-standard-data-set-label-1-hdr1eov1eof1
# Use nocheck to allow easy use of day of year in mday field w/o hitting range
# validations. IE: Ask for day of month 66 for the 67th day of year
use Time::Local 'timegm_nocheck';
$USAGE='vol_ser_catalog.pl';
$CLIENT_BASE=$ENV{'CLIENT_BASE'};
if ( ! -f "${CLIENT_BASE}/common/log/mt_swap_tape_info.txt" )
{
if ( -f "/export/home/cots/common/log/mt_swap_tape_info.txt" )
{
$CLIENT_BASE="/export/home/cots";
}
}
$STATUS_FILE="${CLIENT_BASE}/common/log/mt_swap_tape_info.txt";
$VOL_SER_DB_FILE="${CLIENT_BASE}/common/log/vol_ser_db.dat";
$TAPE_OPTIONS_LABEL="conv=unblock,ascii cbs=80 ";
$info_from_tape = 0;
if ($#ARGV == -1)
{
$info_from_tape = 1;
$tape_slot_num=`cat $STATUS_FILE`;
chomp $tape_slot_num;
print STDERR "Going to sniff info from currently loaded tape # ",
"==>$tape_slot_num<==\n";
# Ensure "file no= 0" from mt status, to ensure tape has been rewound.
# prompt> mt status
#Vendor 'DEC ' Product 'TKZ62CL ' tape drive:
# sense key(0x0)= No Additional Sense residual= 0 retries= 0
# file no= 0 block no= 0
`mt status | grep "file no= 0" > /dev/null`;
$RC_GREP=$?;
if ( $RC_GREP != 0 )
{
die "Tape not rewound: Do a mt status for details\n";
}
# Read header file
$file_hdr="/tmp/vol_ser_sniff_${$}_hdr";
$hdr_output=`/usr/bin/dd if=/dev/rmt/0n of=$file_hdr $TAPE_OPTIONS_LABEL `;
# Now shove the header file onto ARGV, and rewind the tape.
push @ARGV, $file_hdr;
$rew_output=`mt rewind`;
print STDERR "dd hdr output: $hdr_output\n";
print STDERR "mt rewind output: $rew_output\n";
# Now grep for "VOL1" to ensure label was converted from EBCDIC.
# If not found, tape is in ASCII format, and we need to read it again.
`grep '^VOL1' $file_hdr > /dev/null`;
$RC_GREP=$?;
if ( $RC_GREP == 0 )
{
$TAPE_CHAR_SET='EBCDIC';
}
else
{
$TAPE_OPTIONS_LABEL="conv=unblock cbs=80 ";
$hdr_output=`/usr/bin/dd if=/dev/rmt/0n of=$file_hdr $TAPE_OPTIONS_LABEL `;
`grep '^VOL1' $file_hdr > /dev/null`;
$RC_GREP=$?;
if ( $RC_GREP != 0 )
{
$rew_output=`mt rewind`;
die "Unable to determine tape character set. See $file_hdr";
}
$TAPE_CHAR_SET='ASCII';
# Now rewind the tape.
$rew_output=`mt rewind`;
print STDERR "dd hdr output: $hdr_output\n";
print STDERR "mt rewind output: $rew_output\n";
}
print STDERR "tape character set is $TAPE_CHAR_SET.\n";
}
else
{
# OK to pass filename containing label data
#die "Invalid number of parameters. Should have none";
}
# 0 = one parm
$dump_filename = ($#ARGV > 0);
foreach $file (@ARGV)
{
if (-r $file)
{
$open_rc = open (CURR_FILE, $file);
if ($open_rc == 0)
{
print STDERR "Unable to open file $file ->$!<- (rc=$open_rc) skipping\n";
}
else
{
# Process
if ($dump_filename)
{
print STDOUT "#############\n", $file, "\n#############\n";
}
# $VOL_SER=substr($rest_rec, 0, 6);
open (VOL_SER_DB,
">>$VOL_SER_DB_FILE")
|| die "Unable to append $VOL_SER_DB_FILE: $?";
if ($info_from_tape && $file eq $file_hdr)
{
# Create a template HTML file for this tape, that will look
# like this:
# <td>
# <pre>
# <a href="/env_support/tapes/t_XA7088_info.html">X</a>
# A
# 7
# 0
# 8
# 8
# </pre>
# </td>
#
open (HTML_FILE,
">/usr/local/apache2/htdocs/env_support/tapes/t_${tape_slot_num}.tpl")
|| die "Unable to open /usr/local/apache2/htdocs/env_support/tapes/t_${tape_slot_num}.tpl: $?";
print HTML_FILE " <td>\n";
print HTML_FILE " <pre>\n";
}
while ( (eof CURR_FILE) != 1)
{
$curr_rec = <CURR_FILE>;
$rec_type = substr($curr_rec, 0, 4);
$rest_rec = substr($curr_rec, 4);
SWITCH:
{
if ( $rec_type =~ /^VOL1/ )
{
$VOL_SER=substr($rest_rec, 0, 6);
print "Vol Ser: $VOL_SER";
if (length($rest_rec) > 37 &&
substr($rest_rec, 37, 1) ne ' ' )
{
print ". Owner: " .
substr($rest_rec, 37, 10);
}
print "\n";
last SWITCH;
}
if ( $rec_type =~ /^HDR1|EO[VF]1/ )
{
$FILENAME=substr($rest_rec, 0, 17);
#print "Filename: " . substr($rest_rec, 0, 17) . "\n";
print "Filename: $FILENAME\n";
print "1st Vol : " . substr($rest_rec, 17, 6) . "\n";
print "Tape# (section#): " . substr($rest_rec, 23, 4) . "\n";
print "(sequenc#):" . substr($rest_rec, 27, 4) . "\n";
print "(Gen #): " . substr($rest_rec, 31, 4) . "\n";
print "(Ver #): " . substr($rest_rec, 35, 2) . "\n";
$CREATE_DATE_RAW = substr($rest_rec, 37, 6);
print "Create dt: $CREATE_DATE_RAW";
# create dt: cyyddd c=century, yy= last two digits
# of year, ddd=day of year
# c=' ' = 1900, c=0 = 2000, c=1 = 2100, etc.
# Create date of Jan 1st, then add day of year later
if (substr($CREATE_DATE_RAW, 0, 1) eq " ")
{
#print "(substr(\$CREATE_DATE_RAW, 1, 2)) =",
# (substr($CREATE_DATE_RAW, 1, 2)),
# "\n";
$cr_date = timegm_nocheck(0, 0, 0, # Time
# mday
(substr($CREATE_DATE_RAW, 3, 3)),
0, # month
# year
(substr($CREATE_DATE_RAW, 1, 2) + 0)
);
}
else
{
#print "(substr(\$CREATE_DATE_RAW, 0, 3)) =",
# (substr($CREATE_DATE_RAW, 0, 3)),
# "\n";
$cr_date = timegm_nocheck(0, 0, 0, # Time
# mday
(substr($CREATE_DATE_RAW, 3, 3)),
0, # month
# year
(substr($CREATE_DATE_RAW, 0, 3) + 100)
);
}
# Take day of year and multiply by number of
# seconds in a day to increment $cr_date.
# Subtract 1 from day of year since perl says
# day of year is zero-based
#$cr_date += (
# (substr($CREATE_DATE_RAW, 3, 3) -1 ) *
# 86400
# ); # 86400 seconds in a day
($sec, $min, $hour, $mday, $mon, $year,
$wday, $yday, $isdst) = gmtime($cr_date);
printf " mm/dd/yyyy: %02d/%02d/%04d\n",
$mon+1, $mday, $year + 1900;
$EXPIRE_DATE_RAW = substr($rest_rec, 43, 6);
print "Expire dt: $EXPIRE_DATE_RAW";
if (substr($EXPIRE_DATE_RAW, 0, 1) eq " ")
{
#print "(substr(\$EXPIRE_DATE_RAW, 1, 2)) =",
# (substr($EXPIRE_DATE_RAW, 1, 2)),
# "\n";
$ex_date = timegm_nocheck(0, 0, 0, # Time
# mday
(substr($EXPIRE_DATE_RAW, 3, 3)),
0, # month
# year
(substr($EXPIRE_DATE_RAW, 1, 2) + 0)
);
}
else
{
#print "(substr(\$EXPIRE_DATE_RAW, 0, 3)) =",
# (substr($EXPIRE_DATE_RAW, 0, 3)),
# "\n";
$ex_date = timegm_nocheck(0, 0, 0, # Time
# mday
(substr($EXPIRE_DATE_RAW, 3, 3)),
0, # month
# year
(substr($EXPIRE_DATE_RAW, 0, 3) + 100)
);
}
#$ex_date += (
# (substr($EXPIRE_DATE_RAW, 3, 3) -1 ) *
# 86400
# ); # 86400 seconds in a day
($sec, $min, $hour, $mday, $mon, $year,
$wday, $yday, $isdst) = gmtime($ex_date);
printf " mm/dd/yyyy: %02d/%02d/%04d\n",
$mon+1, $mday, $year + 1900;
if ( $rec_type =~ /^EO/ )
{
# Dump block count
print "BlkCount: " . substr($rest_rec, 49, 7) . "\n";
if ( $rec_type =~ /^EOF/ )
{
print "(Last volume of file)\n";
}
else
{
print "(file continues on another volume)\n";
}
}
print "Creator : " . substr($rest_rec, 56, 13) . "\n";
last SWITCH;
}
if ( $rec_type =~ /^HDR2|EO[VF]2/ )
{
$FMT=substr($rest_rec, 0, 1);
print "Fixed/Var(V|D)/Spanned/Undefined: $FMT\n";
#print "Fixed/V: " . substr($rest_rec, 0, 1) . "\n";
$BLKSIZE=substr($rest_rec, 1, 5);
print "Blksize: $BLKSIZE\n";
#print "Blksize: " . substr($rest_rec, 1, 5) . "\n";
$LRECL=substr($rest_rec, 6, 5);
print "LRECL : $LRECL\n";
#print "LRECL : " . substr($rest_rec, 6, 5) . "\n";
$reserved=substr($rest_rec, 11, 35);
#print "Reserved for O/S: " .
# substr($rest_rec, 11, 35) . "\n";
#print " 1 2 3\n";
#print " 12345678901234567890123456789012345\n";
#
#print "Rec Dens: " . substr($rest_rec, 11, 1) . "\n";
print "Rec Dens: " . substr($reserved, 0, 1) . " (";
$RecDens = substr($reserved, 0, 1);
# Decode per http://publibz.boulder.ibm.com/cgi-bin/bookmgr_OS390/BOOKS/IGG3M300/1.4.2?SHELF=&DT=19911220181358&CASE=
if ($RecDens eq ' ')
{
print "N/A (cartridge)";
}
elsif ($RecDens == 0 || $RecDens == 1)
{
print "7-track 556 bpi";
}
elsif ($RecDens == 2)
{
print "7/9-track 800 bpi";
}
elsif ($RecDens == 3)
{
print "9-track 1600 bpi";
}
elsif ($RecDens == 4)
{
print "9-track 6250 bpi";
}
else
{
print "unknown";
}
print ")\n";
print "Job/JobStep: " . substr($reserved, 2, 17) .
"\n";
print "Tape Recording Technique: " .
substr($reserved, 19, 2) .
(substr($reserved, 19, 1) eq "P" ?
"(Compressed Data)\n" :
"\n"
);
print "Control Character: " .
substr($reserved, 21, 1) .
"(A = ISO/ANSI/FIPS, M = machine, blank= none)\n";
# one byte reserved for Buffer Alignment Block
print "Block Attribute: " .
substr($reserved, 23, 1) .
" (B = blocked, S = spanned, R= B & ",
"S, blank= none)\n";
if (length($rest_rec) > 43 &&
substr($rest_rec, 38, 4) ne ' ' )
{
print "Serial #: " .
substr($rest_rec, 38, 4) .
", Device Addr #: " .
substr($rest_rec, 42, 1) . "\n";
}
# See vol_ser_catalog_both.pl for notes on the
# following.
#print "Blocking Attr: " . substr($rest_rec, 34, 1) . "\n";
# Data Set Position 0 - no volume switch occurred
# Data Set Position 1 - volume switch occurred prev.
if ( substr($rest_rec, 12, 1) eq '0' )
{
if ( $rec_type =~ /^EOF2/ )
{
print "(First and Last volume of file)\n";
}
else
{
$VOL_FILE_INFO="(First volume of file)";
print "$VOL_FILE_INFO\n";
#print "(First volume of file)\n";
}
}
else
{
if ( $rec_type =~ /^EOF2/ )
{
print "(subsequent and Last volume of file)\n";
}
else
{
$VOL_FILE_INFO="(subsequent volume of file)";
print "$VOL_FILE_INFO\n";
#print "(subsequent volume of file)\n";
}
}
last SWITCH;
}
# Default
{
print "Unexpected record type: $rec_type\n";
}
}
# The following will not work if only reading trailer recs
if (defined($VOL_SER))
{
print VOL_SER_DB "$VOL_SER\t$curr_rec";
}
} # end while ( (eof CURR_FILE) != 1)
close (CURR_FILE);
close (VOL_SER_DB);
#if ($info_from_tape)
if ($info_from_tape && $file eq $file_hdr)
{
@VOL_SER_ARRAY = split(/ */, $VOL_SER);
# Yank first character for fancy stuff
# <a href="tapes/t_XA7088_info.html">X</a>
$first_char = shift @VOL_SER_ARRAY;
print HTML_FILE "<a href=\"/env_support/tapes/t_${VOL_SER}_info.html\">",
"$first_char</a>\n";
# For rest of vol ser, one char per line
foreach $vol_ser_char (@VOL_SER_ARRAY)
{
print HTML_FILE "$vol_ser_char\n";
}
print HTML_FILE " </pre>\n";
print HTML_FILE " </td>\n";
close (HTML_FILE);
# Copy the info for archive purposes
`cp /usr/local/apache2/htdocs/env_support/tapes/t_${tape_slot_num}.tpl /usr/local/apache2/htdocs/env_support/tapes/t_${VOL_SER}.tpl`;
#!! To Do: Also create a t_${VOL_SER}_info.html file
# that would contain details on the tape.
open (HTML_FILE_INFO,
">/usr/local/apache2/htdocs/env_support/tapes/t_${VOL_SER}_info.html")
|| die "Unable to open /usr/local/apache2/htdocs/env_support/tapes/t_${VOL_SER}_info.html: $?";
print HTML_FILE_INFO "<html>\n<head>\n";
print HTML_FILE_INFO "<title>Tape Volume $VOL_SER Info</title>\n";
print HTML_FILE_INFO "</head>\n<body>\n";
print HTML_FILE_INFO "<h1>Tape Volume $VOL_SER Info</h1>\n";
print HTML_FILE_INFO "<p>$VOL_FILE_INFO Char Set $TAPE_CHAR_SET\n";
print HTML_FILE_INFO "<p>\n<table border=1>\n";
print HTML_FILE_INFO "<tr><td>Filename</td>";
print HTML_FILE_INFO "<td>$FILENAME</td></tr>\n";
print HTML_FILE_INFO "<tr><td>Fixed Variable</td>";
print HTML_FILE_INFO "<td>$FMT</td></tr>\n";
print HTML_FILE_INFO "<tr><td>Blksize</td>";
print HTML_FILE_INFO "<td>$BLKSIZE</td></tr>\n";
print HTML_FILE_INFO "<tr><td>LRECL</td>";
print HTML_FILE_INFO "<td>$LRECL</td></tr>\n";
print HTML_FILE_INFO "<tr><td>Tape Slot #</td>";
print HTML_FILE_INFO "<td>$tape_slot_num</td></tr>\n";
print HTML_FILE_INFO "<p>\n</table>\n";
print HTML_FILE_INFO "</body>\n</html>\n";
close (HTML_FILE_INFO);
}
}
}
else
{
print STDERR "Unable to read file $file - skipping\n";
}
}
if (defined $file_hdr)
{
if (defined $file_tlr)
{
$file_info=`more $file_hdr $file_tlr`;
print STDERR "Info from tape header and trailer:\n$file_info";
unlink $file_hdr;
unlink $file_tlr;
}
else
{
$file_info=`more $file_hdr`;
print STDERR "Info from tape header:\n$file_info";
unlink $file_hdr;
}
}