fix lintian issues
[debian/mtx] / tapeinfo.c
1 /* Copyright 2000 Enhanced Software Technologies Inc.
2  * Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
3  *   Released under terms of the GNU General Public License as
4  * required by the license on 'mtxl.c'.
5  * $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
6  * $Revision: 193 $
7  */
8
9 /*#define DEBUG_PARTITION */
10 /*#define DEBUG 1 */
11
12 /* What this does: This basically dumps out the contents of the following
13  * pages:
14  *
15  * Inquiry -- prints full inquiry info. If it's not a tape drive, this is
16  * the end of things.
17  *    DeviceType:
18  *    Manufacturer:
19  *    ProdID:  
20  *    ProdRevision:
21  *
22  * Log Sense: TapeAlert Page (if supported):
23  *    TapeAlert:[message#]<Message>  e.g. "TapeAlert:[22]Cleaning Cartridge Worn Out"
24  *  
25  * Mode Sense: 
26  *  Data Compression Page:
27  *    DataCompEnabled:<yes|no>
28  *    DataCompCapable:<yes|no>
29  *    DataDeCompEnabled:<yes|no>
30  *    CompType:<number>
31  *    DeCompType:<number>
32  *
33  *  Device Configuration Page:
34  *    ActivePartition:<#>
35  *    DevConfigComp:<#>    -- the compression byte in device config page.
36  *    EarlyWarningSize:<#> -- size of early warning buffer?
37  *
38  *  Medium Partition Page:
39  *    NumPartitions:<#>
40  *    MaxPartitions:<#>
41  *    Partition[0]:<size>
42  *    Partition[1]:<size>...
43  *
44  * Read Block Limits command:
45  *    MinBlock:<#>  -- Minimum block size.
46  *    MaxBlock:<#>  -- Maximum block size. 
47  */
48
49 #include <stdio.h>
50 #include <string.h>
51 #include "mtx.h"
52 #include "mtxl.h"
53
54 char    *argv0;
55
56 void usage(void)
57 {
58         FatalError("Usage: tapeinfo -f <generic-device>\n");
59 }
60
61 /* A table for printing out the peripheral device type as ASCII. */ 
62 static char *PeripheralDeviceType[32] =
63 {
64         "Disk Drive",
65         "Tape Drive",
66         "Printer",
67         "Processor",
68         "Write-once",
69         "CD-ROM",
70         "Scanner",
71         "Optical",
72         "Medium Changer",
73         "Communications",
74         "ASC IT8",
75         "ASC IT8",
76         "RAID Array",
77         "Enclosure Services",
78         "OCR/W",
79         "Bridging Expander",    /* 0x10 */
80         "Reserved",                             /* 0x11 */
81         "Reserved",                             /* 0x12 */
82         "Reserved",                             /* 0x13 */
83         "Reserved",                             /* 0x14 */
84         "Reserved",                             /* 0x15 */
85         "Reserved",                             /* 0x16 */
86         "Reserved",                             /* 0x17 */
87         "Reserved",                             /* 0x18 */
88         "Reserved",                             /* 0x19 */
89         "Reserved",                             /* 0x1a */
90         "Reserved",                             /* 0x1b */
91         "Reserved",                             /* 0x1c */
92         "Reserved",                             /* 0x1d */
93         "Reserved",                             /* 0x1e */
94         "Unknown"                               /* 0x1f */
95 };
96
97
98
99 /* we call it MediumChangerFD for history reasons, sigh. */
100
101 /* now to print inquiry information: Copied from other one.... */
102
103 static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
104 {
105         RequestSense_T RequestSense;
106         Inquiry_T *Inquiry;
107         int i;
108
109         Inquiry = RequestInquiry(MediumChangerFD, &RequestSense);
110         if (Inquiry == NULL)
111         {
112                 PrintRequestSense(&RequestSense);
113                 FatalError("INQUIRY Command Failed\n");
114         }
115
116         printf("Product Type: %s\n", PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
117
118         printf("Vendor ID: '");
119         for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
120                 printf("%c", Inquiry->VendorIdentification[i]);
121
122         printf("'\nProduct ID: '");
123         for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
124                 printf("%c", Inquiry->ProductIdentification[i]);
125
126         printf("'\nRevision: '");
127         for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
128                 printf("%c", Inquiry->ProductRevisionLevel[i]);
129         printf("'\n");
130
131         if (Inquiry->MChngr)
132         {
133                 /* check the attached-media-changer bit... */
134                 printf("Attached Changer API: Yes\n");
135         }
136         else
137         {
138                 printf("Attached Changer API: No\n");
139         }
140
141         free(Inquiry);          /* well, we're about to exit, but ... */
142 }
143
144
145
146
147 /* Okay, now for the Log Sense Tape Alert Page (if supported): */
148 #define TAPEALERT_SIZE 2048  /* max size of tapealert buffer. */ 
149 #define MAX_TAPE_ALERT 0x41
150
151 static char *tapealert_messages[] =
152 {
153         "Undefined", /* 0 */
154         "         Read: Having problems reading (slowing down)", /* 1 */
155         "        Write: Having problems writing (losing capacity)", /* 2 */
156         "   Hard Error: Uncorrectable read/write error", /* 3 */
157         "        Media: Media Performance Degraded, Data Is At Risk", /* 4 */
158         " Read Failure: Tape faulty or tape drive broken", /* 5 */
159         "Write Failure: Tape faulty or tape drive broken", /* 6 */
160         "   Media Life: The tape has reached the end of its useful life", /* 7 */
161         "Not Data Grade:Replace cartridge with one  containing data grade tape",/*8*/
162         "Write Protect: Attempted to write to a write-protected cartridge",/*9 */
163         "   No Removal: Cannot unload, initiator is preventing media removal", /*a*/
164         "Cleaning Media:Cannot back up or restore to a cleaning cartridge", /* b */
165         "   Bad Format: The loaded tape contains data in an unsupported format", /*c */
166         " Snapped Tape: The data cartridge contains a broken tape", /* d */
167         "Undefined", /* e */
168         "Undefined", /* f */
169         "Undefined", /* 10 */
170         "Undefined", /* 11 */
171         "Undefined", /* 12 */
172         "Undefined", /* 13 */
173         "    Clean Now: The tape drive neads cleaning NOW", /* 0x14 */
174         "Clean Periodic:The tape drive needs to be cleaned at next opportunity", /* 0x15 */
175         "Cleaning Media:Cannot clean because cleaning cartridge used up, insert new cleaning cartridge to clean the drive", /* 0x16 */
176         "Undefined", /* 0x17 */
177         "Undefined", /* 0x18 */
178         "Undefined", /* 0x19 */
179         "Undefined", /* 0x1a */
180         "Undefined", /* 0x1b */
181         "Undefined", /* 0x1c */
182         "Undefined", /* 0x1d */
183         "   Hardware A: Tape drive has a problem not read/write related", /* 0x1e */
184         "   Hardware B: Tape drive has a problem not read/write related", /* 0x1f */
185         "    Interface: Problem with SCSI interface between tape drive and initiator", /* 0x20 */
186         "  Eject Media: The current operation has failed. Eject and reload media", /* 0x21 */
187         "Download Fail: Attempt to download new firmware failed", /* 0x22 */
188         "Undefined", /* 0x23 */
189         "Undefined", /* 0x24 */
190         "Undefined", /* 0x25 */
191         "Undefined", /* 0x26 */
192         "Undefined", /* 0x27 */
193         "Loader Hardware A: Changer having problems communicating with tape drive", /* 0x28   40 */
194         "Loader Stray Tape: Stray tape left in drive from prior error", /* 0x29 41 */
195         "Loader Hardware B: Autoloader mechanism has a fault", /* 0x2a 42 */
196         "  Loader Door: Loader door is open, please close it", /* 0x2b 43 */
197         "Undefined", /* 0x2c */
198         "Undefined", /* 0x2d */
199         "Undefined", /* 0x2e */
200         "Undefined", /* 0x2f */
201         "Undefined", /* 0x30 */
202         "Undefined", /* 0x31 */
203         "Undefined", /* 0x32 */
204         "Undefined", /* 0x33 */
205         "Undefined", /* 0x34 */
206         "Undefined", /* 0x35 */
207         "Undefined", /* 0x36 */
208         "Undefined", /* 0x37 */
209         "Undefined", /* 0x38 */
210         "Undefined", /* 0x39 */
211         "Undefined", /* 0x3a */
212         "Undefined", /* 0x3b */
213         "Undefined", /* 0x3c */
214         "Undefined", /* 0x3d */
215         "Undefined", /* 0x3e */
216         "Undefined", /* 0x3f */
217         "Undefined" /* 0x40 */
218 };
219
220 typedef struct TapeCapacityStruct
221 {
222         unsigned int partition0_remaining;
223         unsigned int partition1_remaining;
224         unsigned int partition0_size;
225         unsigned int partition1_size;
226 }       TapeCapacity;
227
228 #if defined(DEBUG)
229 /* DEBUG */
230 static void dump_data(unsigned char *data, int len)
231 {
232         if (len != 0)
233         {
234                 fprintf(stderr,"DATA:");
235                 PrintHex(1, data, len);
236         }
237         else
238         {
239                 fprintf(stderr, "**NO DATA**\n");
240         }
241 }
242 #endif
243
244
245 /* Request the tape capacity page defined by some DAT autoloaders. */
246
247 static TapeCapacity *RequestTapeCapacity(DEVICE_TYPE fd, RequestSense_T *sense)
248 {
249         CDB_T CDB;
250         TapeCapacity *result;
251         int result_len;
252
253         unsigned char buffer[TAPEALERT_SIZE]; /* Overkill, but ... */
254
255         slow_bzero((char *)buffer,TAPEALERT_SIZE); /*zero it... */
256
257         /* now to create the CDB block: */
258         CDB[0] = 0x4d;   /* Log Sense */
259         CDB[1] = 0;   
260         CDB[2] = 0x31;   /* Tape Capacity Page. */
261         CDB[3] = 0;
262         CDB[4] = 0;
263         CDB[5] = 0;
264         CDB[6] = 0;
265         CDB[7] = TAPEALERT_SIZE >> 8 & 0xff;    /* hi byte, allocation size */
266         CDB[8] = TAPEALERT_SIZE & 0xff;                 /* lo byte, allocation size */
267         CDB[9] = 0;                                                             /* reserved */ 
268
269         if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, TAPEALERT_SIZE, sense) != 0)
270         {
271                 /*    fprintf(stderr,"RequestTapeCapacity: Command failed: Log Sense\n"); */
272                 return NULL;
273         }
274
275         /* dump_data(buffer,64); */
276
277         /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
278         * byte 0 should be 0x31, byte 1 == 0, bytes 2,3 tell how long the
279         * log page is. 
280         */
281         if ((buffer[0]&0x3f) != 0x31)
282         {
283                 /*    fprintf(stderr,"RequestTapeCapacity: Invalid header for page (not 0x31).\n"); */
284                 return NULL;
285         }
286
287         result_len = ((int)buffer[2] << 8) + buffer[3];
288
289         if (result_len != 32)
290         {
291                 /*   fprintf(stderr,"RequestTapeCapacity: Page was %d bytes long, not 32 bytes\n",result_len); */
292                 return NULL; /* This Is Not The Page You're Looking For */
293         }
294
295         result = xmalloc(sizeof(TapeCapacity));
296
297         /* okay, now allocate data and move the buffer over there: */
298
299                 /*      0  1  2  3  4  5  6  7  8  9
300         DATA:   31 00 00 20 00 01 4c 04 01 3a
301                         10 11 12 13 14 15 16 17 18 19
302         DATA:   81 0c 00 02 4c 04 00 00 00 00
303                         20 21 22 23 24 25 26 27 28 29
304         DATA:   00 03 4c 04 01 3f 4b 1f 00 04
305                         30 31 32 33 34 35
306         DATA:   4c 04 00 00 00 00 00 00 00 00
307         DATA:   00 00 00 00 00 00 00 00 00 00
308         DATA:   00 00 00 00 00 00 00 00 00 00
309         DATA:   00 00 00 00
310         */
311
312         result->partition0_remaining =
313                 ((unsigned int)buffer[8]  << 24) +
314                 ((unsigned int)buffer[9]  << 16) +
315                 ((unsigned int)buffer[10] <<  8) + 
316                 buffer[11];
317
318         result->partition1_remaining =
319                 ((unsigned int)buffer[16] << 24) +
320                 ((unsigned int)buffer[17] << 16) +
321                 ((unsigned int)buffer[18] <<  8) +
322                 buffer[19];
323
324         result->partition0_size =
325                 ((unsigned int)buffer[24] << 24) +
326                 ((unsigned int)buffer[25] << 16) +
327                 ((unsigned int)buffer[26] <<  8) +
328                 buffer[27];
329
330         result->partition1_size =
331                 ((unsigned int)buffer[32] << 24) +
332                 ((unsigned int)buffer[33] << 16) +
333                 ((unsigned int)buffer[34] <<  8) +
334                 buffer[35]; 
335
336         return result;
337 }
338
339
340
341 struct tapealert_struct
342 {
343         int length;
344         unsigned char *data;
345 };
346
347 static struct tapealert_struct *RequestTapeAlert(DEVICE_TYPE fd, RequestSense_T *sense)
348 {
349         CDB_T CDB;
350
351         struct tapealert_struct *result;
352         int i, tapealert_len, result_idx;
353
354         unsigned char buffer[TAPEALERT_SIZE];
355         unsigned char *walkptr;
356
357         slow_bzero((char *)buffer, TAPEALERT_SIZE); /*zero it... */
358
359         /* now to create the CDB block: */
360         CDB[0] = 0x4d;  /* Log Sense */
361         CDB[1] = 0;
362         CDB[2] = 0x2e;  /* Tape Alert Page. */
363         CDB[3] = 0;
364         CDB[4] = 0;
365         CDB[5] = 0;
366         CDB[6] = 0;
367         CDB[7] = TAPEALERT_SIZE >> 8 & 0xff;    /* hi byte, allocation size */
368         CDB[8] = TAPEALERT_SIZE & 0xff;                 /* lo byte, allocation size */
369         CDB[9] = 0;                                                             /* reserved */
370
371         if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,TAPEALERT_SIZE,sense)!=0)
372         {
373                 return NULL;
374         }
375
376         result = xmalloc(sizeof(struct tapealert_struct));
377
378         /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
379          * byte 0 should be 0x2e, byte 1 == 0, bytes 2,3 tell how long the
380          * tapealert page is. 
381          */
382         if ((buffer[0]&0x3f) != 0x2e)
383         {
384                 result->data = NULL;
385                 result->length = 0;
386                 return result;
387         }
388
389         tapealert_len = ((int)buffer[2] << 8) + buffer[3];
390
391         if (!tapealert_len)
392         {
393                 result->length = 0;
394                 result->data = NULL;
395                 return result;
396         }
397
398         /* okay, now allocate data and move the buffer over there: */
399         result->length = MAX_TAPE_ALERT;
400         result->data = xzmalloc(MAX_TAPE_ALERT); /* alloc & zero. */
401
402         walkptr = &buffer[4];
403         i = 0;
404
405         while (i < tapealert_len)
406         {
407                 result_idx=(((int)walkptr[0])<<8) + walkptr[1]; /* the parameter #. */
408                 if (result_idx > 0 && result_idx < MAX_TAPE_ALERT)
409                 {
410                         if (walkptr[4])
411                         {
412                                 result->data[result_idx] = 1; 
413                         }
414                         else
415                         {
416                                 result->data[result_idx] = 0;
417                         }
418 #ifdef DEBUGOLD1
419                         fprintf(stderr,"Alert[0x%x]=%d\n",result_idx,result->data[result_idx]);
420                         fflush(stderr);
421 #endif
422                 }
423                 else
424                 {
425                         FatalError("Invalid tapealert page: %d\n",result_idx);
426                 }
427
428                 i = i + 4 + walkptr[3]; /* length byte! */
429                 walkptr = walkptr + 4 + walkptr[3]; /* next! */
430         }
431         return result;
432 }
433
434 static void ReportTapeCapacity(DEVICE_TYPE fd)
435 {
436         /* we actually ignore a bad sense reading, like might happen if the 
437          * tape drive does not support the tape capacity page. 
438          */
439
440         RequestSense_T RequestSense;
441
442         TapeCapacity *result;
443
444         result=RequestTapeCapacity(fd,&RequestSense);
445
446         if (!result)
447                 return;
448
449         printf("Partition 0 Remaining Kbytes: %d\n", result->partition0_remaining);
450         printf("Partition 0 Size in Kbytes: %d\n", result->partition0_size);
451
452         if (result->partition1_size)
453         {
454                 printf("Partition 1 Remaining Kbytes: %d\n", result->partition1_remaining);
455                 printf("Partition 1 Size in Kbytes: %d\n", result->partition1_size);
456         }
457
458         free(result);
459 }
460
461
462
463 static void ReportTapeAlert(DEVICE_TYPE fd)
464 {
465         /* we actually ignore a bad sense reading, like might happen if the 
466          * tape drive does not support the tapealert page. 
467          */
468
469         RequestSense_T RequestSense;
470
471         struct tapealert_struct *result;
472         int i;
473
474         result=RequestTapeAlert(fd,&RequestSense);
475
476         if (!result)
477                 return; /* sorry. Don't print sense here. */
478
479         if (!result->length)
480                 return; /* sorry, no alerts valid. */
481
482         for (i = 0; i < result->length; i++)
483         {
484                 if (result->data[i])
485                 {
486                         printf("TapeAlert[%d]: %s.\n", i, tapealert_messages[i]);
487                 }
488         }
489
490         free(result->data);
491         free(result);
492 }
493
494 static unsigned char
495 *mode_sense(DEVICE_TYPE fd, char pagenum, int alloc_len,  RequestSense_T *RequestSense)
496 {
497         CDB_T CDB;
498
499         unsigned char *input_buffer;
500         unsigned char *tmp;
501         unsigned char *retval;
502         int i, pagelen;
503
504         if (alloc_len > 255)
505         {
506                 FatalError("mode_sense(6) can only read up to 255 characters!\n");
507         }
508
509         input_buffer = (unsigned char *)xzmalloc(256); /* overdo it, eh? */
510
511         /* clear the sense buffer: */
512         slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
513
514         /* returns an array of bytes in the page, or, if not possible, NULL. */
515         CDB[0] = 0x1a; /* Mode Sense(6) */
516         CDB[1] = 0; 
517         CDB[2] = pagenum; /* the page to read. */
518         CDB[3] = 0;
519         CDB[4] = 255; /* allocation length. This does max of 256 bytes! */
520         CDB[5] = 0;
521
522         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, input_buffer, 255, RequestSense) != 0)
523         {
524 #ifdef DEBUG_MODE_SENSE
525                 fprintf(stderr,"Could not execute mode sense...\n");
526                 fflush(stderr);
527 #endif
528                 return NULL; /* sorry, couldn't do it. */
529         }
530
531         /* Oh hell, write protect is the only thing I have: always print
532          * it if our mode page was 0x0fh, before skipping past buffer: 
533          * if the media is *NOT* write protected, just skip, sigh. 
534          *
535          * Oh poops, the blocksize is reported in the block descriptor header
536          * <   * too. Again, just print if our mode page was 0x0f...
537          */
538         if (pagenum == 0x0f)
539         {
540                 int blocklen;
541
542                 if (input_buffer[2] & 0x80)
543                 {
544                         printf("WriteProtect: yes\n");
545                 }
546
547                 if (input_buffer[2] & 0x70)
548                 {
549                         printf("BufferedMode: yes\n");
550                 }
551
552                 if (input_buffer[1] )
553                 {
554                         printf("Medium Type: 0x%x\n", input_buffer[1]);
555                 }
556                 else
557                 {
558                         printf("Medium Type: Not Loaded\n");
559                 }
560
561                 printf("Density Code: 0x%x\n", input_buffer[4]);
562                 /* Put out the block size: */
563
564                 blocklen =      ((int)input_buffer[9]  << 16)+
565                                         ((int)input_buffer[10] <<  8)+
566                                         input_buffer[11];
567
568                 printf("BlockSize: %d\n", blocklen);
569         }
570
571         /* First skip past any header.... */
572         tmp = input_buffer + 4 + input_buffer[3];
573
574         /* now find out real length of page... */
575         pagelen = tmp[1] + 2;
576         retval = xmalloc(pagelen);
577
578         /* and copy our data to the new page. */
579         for (i=0;i<pagelen;i++)
580         {
581                 retval[i]=tmp[i];
582         }
583
584         /* okay, free our input buffer: */
585         free(input_buffer);
586         return retval;
587 }
588
589
590 #define DCE_MASK 0x80
591 #define DCC_MASK 0x40
592 #define DDE_MASK 0x80
593
594 static void ReportCompressionPage(DEVICE_TYPE fd)
595 {
596         /* actually ignore a bad sense reading, like might happen if the tape
597         * drive does not support the mode sense compression page. 
598         */
599
600         RequestSense_T RequestSense;
601
602         unsigned char *compression_page;
603
604         compression_page=mode_sense(fd,0x0f,16,&RequestSense);
605
606         if (!compression_page)
607         {
608                 return;  /* sorry! */
609         }
610
611         /* Okay, we now have the compression page. Now print stuff from it: */
612         printf("DataCompEnabled: %s\n", (compression_page[2] & DCE_MASK)? "yes" : "no");
613         printf("DataCompCapable: %s\n", (compression_page[2] & DCC_MASK)? "yes" : "no");
614         printf("DataDeCompEnabled: %s\n", (compression_page[3] & DDE_MASK)? "yes" : "no");
615         printf("CompType: 0x%x\n",
616                 (compression_page[4] << 24) +
617                 (compression_page[5] << 16) +
618                 (compression_page[6] <<  8) +
619                  compression_page[7]);
620
621         printf("DeCompType: 0x%x\n",
622                 (compression_page[8]  << 24) +
623                 (compression_page[9]  << 16) +
624                 (compression_page[10] <<  8) +
625                  compression_page[11]);
626
627         free(compression_page);
628 }
629
630 /* Now for the device configuration mode page: */
631
632 static void ReportConfigPage(DEVICE_TYPE fd)
633 {
634         RequestSense_T RequestSense;
635         unsigned char *config_page;
636
637         config_page = mode_sense(fd, 0x10, 16, &RequestSense);
638         if (!config_page)
639                 return;
640
641         /* Now to print the stuff: */
642         printf("ActivePartition: %d\n", config_page[3]);
643
644         /* The following does NOT work accurately on any tape drive I know of... */
645         /*  printf("DevConfigComp: %s\n", config_page[14] ? "yes" : "no"); */
646         printf("EarlyWarningSize: %d\n",
647                 (config_page[11] << 16) +
648                 (config_page[12] <<  8) +
649                  config_page[13]);
650 }
651
652 /* ***************************************
653  * Medium Partition Page:
654  * 
655  * The problem here, as we oh so graphically demonstrated during debugging
656  * of the Linux 'st' driver :-), is that there are actually *TWO* formats for
657  * the Medium Partition Page: There is the "long" format, where there is a
658  * partition size word for each partition on the page, and there is a "short"
659  * format, beloved of DAT drives, which only has a partition size word for
660  * partition #1 (and no partition size word for partition #0, and no
661  * provisions for any more partitions). So we must look at the size and
662  * # of partitions defined to know what to report as what. 
663  *
664  ********************************************/
665
666 static void ReportPartitionPage(DEVICE_TYPE fd)
667 {
668         RequestSense_T RequestSense;
669         unsigned char *partition_page;
670
671         int num_parts,max_parts;
672         int i;
673
674         partition_page=mode_sense(fd,0x11,255,&RequestSense);
675         if (!partition_page)
676                 return;
677
678         /* Okay, now we have either old format or new format: */
679         num_parts = partition_page[3];
680         max_parts = partition_page[2];
681
682         printf("NumPartitions: %d\n", num_parts);
683         printf("MaxPartitions: %d\n", max_parts);
684
685         if (!num_parts)
686         {
687                 /* if no additional partitions, then ... */ 
688                 free(partition_page);
689                 return;
690         }
691
692         /* we know we have at least one partition if we got here. Check the
693          * page size field. If it is 8 or below, then we are the old format....
694          */
695
696 #ifdef DEBUG_PARTITION
697         fprintf(stderr,"partition_page[1]=%d\n",partition_page[1]);
698         fflush(stderr);
699 #endif
700         if (partition_page[1]==8)
701         {
702                 /* old-style! */
703                 printf("Partition1: %d\n",(partition_page[8]<<8)+partition_page[9]);
704         }
705         else
706         {
707                 /* new-style! */
708                 for (i=0;i<=max_parts;i++)
709                 {
710 #ifdef DEBUG_PARTITION
711                         fprintf(stderr,"partition%d:[%d]%d [%d]%d\n", i, 8 + i * 2,
712                         partition_page[8+i*2]<<8, 9+i*2,partition_page[9 + i * 2]);
713                         fflush(stderr);
714 #endif
715                         printf("Partition%d: %d\n", i,
716                                         (partition_page[8 + i * 2] << 8) + partition_page[9 + i * 2]);
717                 }
718         }
719         free(partition_page);
720 }
721
722 static void ReportSerialNumber(DEVICE_TYPE fd)
723 {
724         /*      Actually ignore a bad sense reading, like might happen if the
725                 tape drive does not support the inquiry page 0x80. 
726         */
727
728         RequestSense_T sense;
729         CDB_T CDB;
730
731 #define WILD_SER_SIZE 30
732 unsigned char buffer[WILD_SER_SIZE]; /* just wildly overestimate serial# length! */
733
734         int i, lim;
735         char *bufptr; 
736
737         CDB[0] = 0x12;                  /* INQUIRY */
738         CDB[1] = 1;                             /* EVPD = 1 */
739         CDB[2] = 0x80;                  /* The serial # page, hopefully. */
740         CDB[3] = 0;                             /* reserved */
741         CDB[4] = WILD_SER_SIZE;
742         CDB[5] = 0;
743
744         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, &buffer, sizeof(buffer), &sense) != 0)
745         {
746                 /* PrintRequestSense(&sense); */ /* zap debugging output :-) */
747                 /* printf("No Serial Number: None\n"); */
748                 return; 
749         }
750
751         /* okay, we have something in our buffer. Byte 3 should be the length of
752         the sernum field, and bytes 4 onward are the serial #. */
753
754         lim = (int)buffer[3];
755         bufptr = (char *)&(buffer[4]);
756
757         printf("SerialNumber: '");
758         for (i=0;i<lim;i++)
759         {
760                 putchar(*bufptr++);
761         }
762         printf("'\n");
763 }
764
765 /*  Read Block Limits! */
766
767 void ReportBlockLimits(DEVICE_TYPE fd)
768 {
769         RequestSense_T sense;
770         CDB_T CDB;
771         unsigned char buffer[6];
772
773         CDB[0] = 0x05;  /* READ_BLOCK_LIMITS */
774         CDB[1] = 0;
775         CDB[2] = 0;
776         CDB[3] = 0;             /* 1-5 all unused. */
777         CDB[4] = 0;
778         CDB[5] = 0; 
779
780         slow_bzero((char *)&sense,sizeof(RequestSense_T));
781         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 6, &sense) != 0)
782         {
783                 return;
784         }
785
786         /* okay, but if we did get a result, print it: */
787         printf("MinBlock: %d\n", (buffer[4] << 8) + buffer[5]);
788         printf("MaxBlock: %d\n", (buffer[1] << 16) + (buffer[2]<<8) + buffer[3]);
789 }
790
791 /* Do a READ_POSITION. This may not be always valid, but (shrug). */
792 void ReadPosition(DEVICE_TYPE fd)
793 {
794         RequestSense_T sense;
795         CDB_T CDB;
796         unsigned char buffer[20];
797         unsigned int position;
798
799         CDB[0] = 0x34;          /* READ_POSITION */
800         CDB[1] = 0;
801         CDB[2] = 0;
802         CDB[3] = 0;                     /* 1-9 all unused. */
803         CDB[4] = 0;
804         CDB[5] = 0;
805         CDB[6] = 0;
806         CDB[7] = 0;
807         CDB[8] = 0;
808         CDB[9] = 0;
809
810         slow_bzero((char *)&sense, sizeof(RequestSense_T));
811
812         SCSI_Set_Timeout(2); /* set timeout to 2 seconds! */
813
814         /* if we don't get a result (e.g. we issue this to a disk drive), punt. */
815         if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, 20, &sense) != 0)
816         {
817                 return;
818         }
819
820         SCSI_Default_Timeout(); /* reset it to 5 minutes, sigh! */
821         /* okay, but if we did get a result, print it: */
822
823 #define RBL_BOP 0x80
824 #define RBL_EOP 0x40
825 #define RBL_BCU 0x20
826 #define RBL_BYCU  0x10
827 #define RBL_R1 0x08
828 #define RBL_BPU 0x04
829 #define RBL_PERR 0x02
830
831         /* If we have BOP, go ahead and print that. */
832         if (buffer[0]&RBL_BOP)
833         {
834                 printf("BOP: yes\n");
835         }
836
837         /* if we have valid data, print it: */
838         if (buffer[0]&RBL_BPU)
839         {
840                 printf("Block Position: -1");
841         }
842         else
843         {
844                 position = (unsigned int)(((unsigned int)buffer[4] << 24) +
845                                                                   ((unsigned int)buffer[5] << 16) +
846                                                                   ((unsigned int)buffer[6] <<  8) +
847                                                                         buffer[7]);
848
849                 printf("Block Position: %d\n",position);
850         }
851 }
852
853 /* Test unit ready: This will tell us whether the tape drive
854  * is currently ready to read or write.
855  */
856
857 int TestUnitReady(DEVICE_TYPE fd)
858 {
859         RequestSense_T sense;
860         CDB_T CDB;
861         unsigned char buffer[6];
862
863         CDB[0] = 0x00;          /* TEST_UNIT_READY */
864         CDB[1] = 0;
865         CDB[2] = 0;
866         CDB[3] = 0;                     /* 1-5 all unused. */
867         CDB[4] = 0;
868         CDB[5] = 0;
869
870         slow_bzero((char *)&sense,sizeof(RequestSense_T));
871         if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0)
872         {
873                 printf("Ready: no\n");
874                 return 0;
875         }
876
877         printf("Ready: yes\n");
878         return 1;
879 }
880
881 /* We write a filemarks of 0 before going to grab position, in order
882  * to insure that data in the buffer is not a problem. 
883  */
884
885 int WriteFileMarks(DEVICE_TYPE fd,int count)
886 {
887         RequestSense_T sense;
888         CDB_T CDB;
889         unsigned char buffer[6];
890
891         CDB[0] = 0x10;  /* WRITE_FILE_MARKS */
892         CDB[1] = 0;
893         CDB[2] = (unsigned char)(count >> 16);
894         CDB[3] = (unsigned char)(count >> 8);
895         CDB[4] = (unsigned char)count;
896         CDB[5] = 0; 
897
898         /* we really don't care if this command works or not, sigh.  */
899         slow_bzero((char *)&sense, sizeof(RequestSense_T));
900         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 0, &sense) != 0)
901         {
902                 return 1;
903         }
904
905         return 0;
906 }
907
908
909 /* This will get the SCSI ID and LUN of the target device, if such
910  * is available from the OS. Currently only Linux supports this,
911  * but other drivers could, if someone wants to write a 
912  * SCSI_GetIDLun function for them. 
913  */
914 #ifdef HAVE_GET_ID_LUN
915
916 static void ReportIDLun(DEVICE_TYPE fd)
917 {
918         scsi_id_t *scsi_id;
919
920         scsi_id = SCSI_GetIDLun(fd);
921         printf("SCSI ID: %d\nSCSI LUN: %d\n", scsi_id->id, scsi_id->lun);
922 }
923
924 #endif
925
926 /* we only have one argument: "-f <device>". */
927 int main(int argc, char **argv)
928 {
929         DEVICE_TYPE fd;
930         char *filename;
931
932         argv0=argv[0];
933
934         if (argc != 3)
935         {
936                 fprintf(stderr,"argc=%d",argc);
937                 usage();
938         }
939
940         if (strcmp(argv[1],"-f")!=0)
941         {
942                 usage();
943         }
944         filename=argv[2];
945
946         fd=SCSI_OpenDevice(filename);
947
948         /* Now to call the various routines: */
949         ReportInquiry(fd);
950         ReportSerialNumber(fd);
951         ReportTapeAlert(fd);
952         ReportBlockLimits(fd); 
953
954 #ifdef HAVE_GET_ID_LUN
955         ReportIDLun(fd);
956 #endif
957
958         /* okay, we should only report position if the unit is ready :-(. */
959         if (TestUnitReady(fd))
960         {
961                 ReportCompressionPage(fd); 
962                 ReadPosition(fd); 
963                 ReportTapeCapacity(fd);         /* only if we have it */
964                 ReportConfigPage(fd);           /* only valid if unit is ready. */
965                 ReportPartitionPage(fd); 
966         }
967
968         exit(0);
969 }