Imported Debian patch 1.2.16rel-3
[debian/mtx] / mtx.c
1 /*
2
3   MTX -- SCSI Tape Attached Medium Changer Control Program
4   $Date: 2001/11/06 21:20:40 $
5   $Revision: 1.2.2.1 $
6
7   Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
8   Now maintained by Eric Lee Green <eric@estinc.com>
9
10   This program is free software; you may redistribute and/or modify it under
11   the terms of the GNU General Public License Version 2 as published by the
12   Free Software Foundation.
13
14   This program is distributed in the hope that it will be useful, but
15   WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
16   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17   for complete details.
18
19   The author respectfully requests that any modifications to this software be
20   sent directly to him for evaluation and testing.
21
22   Thanks to Philip A. Prindeville <philipp@enteka.com> of Enteka Enterprise
23   Technology Service for porting MTX to Solaris/SPARC.
24
25   Thanks to Carsten Koch <Carsten.Koch@icem.de> for porting MTX to SGI IRIX.
26
27   Thanks to TECSys Development, Inc. for porting MTX to Digital Unix and
28   OpenVMS.
29
30   Changes Feb 2000 Eric Lee Green <eric@estinc.com> to add support for
31   multi-drive tape changers, extract out library stuff into mtxl.c,
32   and otherwise bring things up to date for dealing with LARGE tape jukeboxes
33   and other such enterprise-class storage subsystems. 
34
35 */
36
37 char *argv0;
38
39 #include "mtx.h"   /* various defines for bit order etc. */
40 #include "mtxl.h"
41
42 /* A table for printing out the peripheral device type as ASCII. */ 
43 static char *PeripheralDeviceType[32] = {
44   "Disk Drive",  /* 0 */
45   "Tape Drive",   /* 1 */
46   "Printer",   /* 2 */
47   "Processor",   /* 3 */
48   "Write-once",   /* 4 */
49   "CD-ROM",       /* 5 */
50   "Scanner",      /* 6 */
51   "Optical",      /* 7 */ 
52   "Medium Changer",  /* 8 */
53   "Communications",  /* 9 */
54   "ASC IT8",           /* a */ 
55   "ASC IT8",            /* b */
56   "RAID Array",         /* c */
57   "Enclosure Services",  /* d */
58   "RBC Simplified Disk", /* e */
59   "OCR/W",           /* f */
60   "Bridging Expander", /* 0x10 */
61   "Reserved",  /* 0x11 */
62   "Reserved", /* 0x12 */
63   "Reserved",  /* 0x13 */
64   "Reserved",  /* 0x14 */
65   "Reserved",  /* 0x15 */
66   "Reserved",  /* 0x16 */
67   "Reserved",  /* 0x17 */
68   "Reserved",  /* 0x18 */
69   "Reserved",  /* 0x19 */
70   "Reserved",  /* 0x1a */
71   "Reserved",  /* 0x1b */
72   "Reserved",  /* 0x1c */
73   "Reserved",  /* 0x1d */
74   "Reserved",  /* 0x1e */
75   "Unknown"    /* 0x1f */
76 };
77   
78 static int argc;
79 static char **argv;
80
81
82
83
84 static char *device=NULL; /* the device name passed as argument */
85
86 /* Unfortunately this must be true for SGI, because SGI does not
87    use an int :-(. 
88 */
89
90 static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) 0;
91 static int device_opened = 0;  /* okay, replace check here. */
92
93 /* was: static int MediumChangerFD=-1; *//* open filehandle to that device */
94 static int arg1=-1;       /* first arg to command */
95 static int arg2=-1;       /* second arg to command */
96
97 static SCSI_Flags_T SCSI_Flags = { 0, 0, 0,0 };
98   
99 /* static int invert_bit=0;*/  /* we by default do not invert... */
100 /* static int eepos=0;     */  /* the extend thingy for import/export. */
101 static Inquiry_T *inquiry_info;  /* needed by MoveMedium etc... */
102 static ElementStatus_T *ElementStatus = NULL;
103
104
105 /* pre-defined commands: */
106 static void ReportInquiry(void);
107 static void Status(void);
108 static void Load(void);
109 static void Unload(void);
110 static void First(void);
111 static void Last(void);
112 static void Next(void);
113 /* static void Previous(void); */
114 static void InvertCommand(void);
115 static void Transfer(void);
116 static void Eepos(void);
117 static void NoAttach(void);
118 static void Version(void);
119 static void do_Inventory(void); 
120 static void do_Unload(void);
121 static void do_Erase(void);
122 static void NoBarCode(void);
123
124 struct command_table_struct {
125   int num_args;
126   char *name;
127   void (*command)(void);
128   int need_device;
129   int need_status;
130 } command_table[] = {
131   { 0, "inquiry",ReportInquiry, 1,0},
132   { 0, "status", Status, 1,1 },
133   { 0, "invert", InvertCommand, 0,0},
134   { 0, "noattach",NoAttach,0,0},
135   { 1, "eepos", Eepos, 0,0},
136   { 2, "load", Load, 1,1 },
137   { 2, "unload", Unload, 1,1 },
138   { 2, "transfer", Transfer, 1,1 },
139   { 1, "first", First, 1,1 },
140   { 1, "last", Last, 1,1 },
141   { 1, "next", Next, 1,1 },
142   { 0, "--version", Version, 0,0 },
143   { 0, "inventory", do_Inventory, 1,0},
144   { 0, "eject", do_Unload, 1, 0},
145   { 0, "erase", do_Erase, 1, 0},
146   { 0, "nobarcode", NoBarCode, 0,0},
147   { 0, NULL, NULL }
148 };
149
150 static void Usage()
151 {
152   fprintf(stderr, "Usage:\n\
153   mtx --version\n\
154   mtx [ -f <loader-dev> ] noattach <more commands>\n\
155   mtx [ -f <loader-dev> ] inquiry | inventory \n\
156   mtx [ -f <loader-dev> ] [nobarcode] status\n\
157   mtx [ -f <loader-dev> ] first [<drive#>]\n\
158   mtx [ -f <loader-dev> ] last [<drive#>]\n\
159   mtx [ -f <loader-dev> ] next [<drive#>]\n\
160   mtx [ -f <loader-dev> ] previous [<drive#>]\n\
161   mtx [ -f <loader-dev> ] [invert] load <storage-element-number> [<drive#>]\n\
162   mtx [ -f <loader-dev> ] [invert] unload [<storage-element-number>][<drive#>]\n\
163   mtx [ -f <loader-dev> ] [eepos eepos-number] transfer <storage-element-number> <storage-element-number>\n\
164   mtx [ -f <device> ] eject\n");
165
166 #ifndef VMS
167   exit(1);
168 #else
169   sys$exit(VMS_ExitCode);
170 #endif
171 }
172
173
174
175 static void Version(void) {
176   fprintf(stderr,"mtx version %s\n\n",VERSION);
177   Usage(); 
178 }
179
180 static void NoAttach(void) {
181   SCSI_Flags.no_attached=1;
182 }
183
184 static void InvertCommand(void) {
185   SCSI_Flags.invert=1;
186   /* invert_bit=1;*/
187 }
188
189 static void NoBarCode(void) {
190   SCSI_Flags.no_barcodes=1;  /* don't request barcodes, sigh! */
191
192
193 /* First and Last are easy. Next is the bitch. */
194 static void First(void){
195   int driveno;
196   /* okay, first see if we have a drive#: */
197   if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
198     driveno=arg1;
199   } else {
200     driveno = 0;
201   }
202   /* now see if there's anything *IN* that drive: */
203   if (ElementStatus->DataTransferElementFull[driveno]) {
204     /* if so, then unload it... */
205     arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
206     if (arg1==1) {
207       printf("loading...done.\n");  /* it already has tape #1 in it! */ 
208       return;
209     }
210     arg2=driveno;
211     Unload();
212   }
213   /* and now to actually do the Load(): */
214   arg2=driveno;
215   arg1=1; /* first! */
216   Load(); /* and voila! */
217   
218 }
219
220 static void Last(void) {
221   int driveno;
222   /* okay, first see if we have a drive#: */
223   if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
224     driveno=arg1;
225   } else {
226     driveno = 0;
227   }
228
229   /* now see if there's anything *IN* that drive: */
230   if (ElementStatus->DataTransferElementFull[driveno]) {
231     /* if so, then unload it... */
232     arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
233     if (arg1==ElementStatus->StorageElementCount) {
234       printf("loading...done.\n");  /* it already has last tape in it! */ 
235       return;
236     }
237     arg2=driveno;
238     Unload();
239   }
240
241   arg1 = ElementStatus->StorageElementCount; /* the last slot... */
242   arg2=driveno;
243   Load(); /* and load it, sigh! */
244
245 }
246
247
248 static void Next(void) {
249   int driveno;
250   int current=0; 
251   /* okay, first see if we have a drive#: */
252   if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
253     driveno=arg1;
254   } else {
255     driveno = 0;
256   }
257
258   /* Now to see if there's anything in that drive! */
259   if (ElementStatus->DataTransferElementFull[driveno]) {
260     /* if so, unload it! */
261     arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
262     current=arg1;
263     arg2=driveno;
264     Unload();
265   }
266   
267   /* okay, now to load, if we can... */
268   current++;
269   if (current > ElementStatus->StorageElementCount) { /* the last slot... */
270     FatalError("No More Tapes\n");
271   }
272   arg1=current;
273   arg2=driveno;
274   Load();
275 }
276
277 static void do_Inventory(void) 
278 {
279   int i;
280   i=Inventory(MediumChangerFD);
281   if (i < 0) {
282     fprintf(stderr,"mtx:inventory failed\n");
283     fflush(stderr);
284     exit(1); /*  exit with an error status. */
285   }
286   return;  /* if it failed, well, sigh.... */
287 }
288
289 /* For Linux, this allows us to do a short erase on a tape (sigh!).
290  * Note that you'll need to do a 'mt status' on the tape afterwards in
291  * order to get the tape driver in sync with the tape drive again. Also
292  * note that on other OS's, this might do other evil things to the tape
293  * driver. Note that to do an erase, you must first rewind using the OS's
294  * native tools!
295  */
296 static void do_Erase(void) {
297   RequestSense_T *RequestSense;
298   RequestSense=Erase(MediumChangerFD);
299   if (RequestSense) {
300     PrintRequestSense(RequestSense);
301     exit(1);  /* exit with an error status. */
302   }
303   return;
304 }
305   
306
307 /* This should eject a tape or magazine, depending upon the device sent
308  * to.
309  */
310 static void do_Unload(void)
311 {
312   int i;
313   i=Eject(MediumChangerFD);
314   if (i<0) {
315     fprintf(stderr,"mtx:eject failed\n");
316     fflush(stderr);
317   }
318   return;  /* if it failed, well, sigh.... */
319 }
320
321 static void ReportInquiry(void)
322 {
323   RequestSense_T RequestSense;
324   Inquiry_T *Inquiry;
325   int i;
326
327   Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
328   if (Inquiry == NULL) 
329     {
330       PrintRequestSense(&RequestSense);
331       FatalError("INQUIRY Command Failed\n");
332     }
333   
334   printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
335   printf("Vendor ID: '");
336   for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
337     printf("%c", Inquiry->VendorIdentification[i]);
338   printf("'\nProduct ID: '");
339   for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
340     printf("%c", Inquiry->ProductIdentification[i]);
341   printf("'\nRevision: '");
342   for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
343     printf("%c", Inquiry->ProductRevisionLevel[i]);
344   printf("'\n");\
345   if (Inquiry->MChngr) {  /* check the attached-media-changer bit... */
346     printf("Attached Changer: Yes\n");
347   } else {
348     printf("Attached Changer: No\n");
349   }
350   free(Inquiry);  /* well, we're about to exit, but ... */
351 }
352
353 static void Status(void)
354 {
355   int StorageElementNumber;
356   int TransferElementNumber;
357   printf("  Storage Changer %s:%d Drives, %d Slots ( %d Import/Export )\n",
358          device,
359          ElementStatus->DataTransferElementCount,
360          ElementStatus->StorageElementCount,
361          ElementStatus->ImportExportCount);
362
363
364   for (TransferElementNumber=0;TransferElementNumber <
365          ElementStatus->DataTransferElementCount;TransferElementNumber++) {
366     
367     printf("Data Transfer Element %d:",TransferElementNumber);
368     if (ElementStatus->DataTransferElementFull[TransferElementNumber])
369       {
370         if (ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber] > -1) 
371           printf("Full (Storage Element %d Loaded)",
372                  ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber]+1);
373         else printf("Full (Unknown Storage Element Loaded)");
374         if
375           (ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber][0])
376           printf(":VolumeTag = %s",ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber]);
377         if
378           (ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber][0])
379           
380           printf(":AlternateVolumeTag = %s",ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber]); 
381         putchar('\n');
382       }
383     else printf("Empty\n");
384   }
385
386   for (StorageElementNumber = 0;
387        StorageElementNumber < ElementStatus->StorageElementCount;
388        StorageElementNumber++) {
389     printf("      Storage Element %d%s:%s", StorageElementNumber+1, 
390            (ElementStatus->StorageElementIsImportExport[StorageElementNumber]) ? " IMPORT/EXPORT" : "",
391            (ElementStatus->StorageElementFull[StorageElementNumber] ? "Full " : "Empty"));
392     if (ElementStatus->PrimaryVolumeTag[StorageElementNumber][0]) {
393       printf(":VolumeTag=%s",ElementStatus->PrimaryVolumeTag[StorageElementNumber]);
394     }
395     if (ElementStatus->AlternateVolumeTag[StorageElementNumber][0]) {
396             printf(":AlternateVolumeTag=%s",ElementStatus->AlternateVolumeTag[StorageElementNumber]);
397     }
398     putchar('\n');
399   }
400   
401 #ifdef VMS
402   VMS_DefineStatusSymbols();
403 #endif
404 }
405
406 void Move(int src, int dest) {
407   RequestSense_T *result; /* from MoveMedium */
408   
409   result=MoveMedium(MediumChangerFD,src,dest,ElementStatus,inquiry_info,&SCSI_Flags);
410   if (result)   /* we have an error! */
411      {
412       if (result->AdditionalSenseCode == 0x3B &&
413           result->AdditionalSenseCodeQualifier == 0x0E)
414         FatalError("source Element Address %d is Empty\n", src);
415       if (result->AdditionalSenseCode == 0x3B &&
416           result->AdditionalSenseCodeQualifier == 0x0D)
417         FatalError("destination Element Address %d is Already Full\n",
418                    dest);
419       if (result->AdditionalSenseCode == 0x30 &&
420           result->AdditionalSenseCodeQualifier == 0x03)
421         FatalError("Cleaning Cartridge Installed and Ejected\n");
422       PrintRequestSense(result);
423       FatalError("MOVE MEDIUM from Element Address %d to %d Failed\n",
424                  src,dest);
425     }
426 }   
427
428 /* okay, now for the Load, Unload, etc. logic: */
429
430 static void Load(void) {
431   int src, dest;
432   /* okay, check src, dest: arg1=src, arg2=dest */
433   if (arg1 < 1) {
434     FatalError("No source specified\n");
435   }
436   if (arg2 < 0) {
437     arg2 = 0; /* default to 1st drive :-( */
438   }
439   arg1--;  /* we use zero-based arrays, sigh, not 1 base like some lusers */
440   /* check for filehandle: */
441   if (!device_opened) {
442     FatalError("No Media Changer Device Specified\n");
443   } 
444   /* okay, we should be there: */
445   if (arg1 < 0 || arg1 >= ElementStatus->StorageElementCount) {
446         FatalError(
447           "illegal <storage-element-number> argument '%s' to 'load' command\n",
448           arg1+1);
449   }
450   if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
451     FatalError(
452                "illegal <drive-number> argument '%s' to 'load' command\n",
453                arg2);
454   }
455   if (ElementStatus->DataTransferElementFull[arg2])  {
456     FatalError("Drive %d Full (Storage Element %d loaded)\n",arg2,
457                ElementStatus->DataTransferElementSourceStorageElementNumber[arg2]+1);
458   }
459   /* Now look up the actual devices: */
460   dest=ElementStatus->DataTransferElementAddress[arg2];
461   src=ElementStatus->StorageElementAddress[arg1];
462   Move(src,dest);  /* load it into the particular slot, if possible! */
463   /* now set the status for further command son this line... */
464   ElementStatus->StorageElementFull[arg1] = false;
465   ElementStatus->DataTransferElementFull[arg2] = true;
466
467   return; /* and done! */
468 }
469
470 static void Transfer(void) {
471   int src,dest;
472   if (arg1 < 1 ) {
473     FatalError("No source specified\n");
474   } 
475   if (arg2 < 1) {
476     FatalError("No destination specified\n");
477   }
478   if (arg1 > ElementStatus->StorageElementCount) {
479     FatalError("Invalid source\n");
480   }
481   if (arg2 > ElementStatus->StorageElementCount) {
482     FatalError("Invalid destination\n");
483   }
484   /* okay, that's done... */
485   src=ElementStatus->StorageElementAddress[arg1-1];
486   dest=ElementStatus->StorageElementAddress[arg2-1];
487   Move(src,dest);
488 }
489
490 static void Eepos(void) {
491   if (arg1 < 0 || arg1 > 3) {
492     FatalError("eepos equires argument between 0 and 3.\n");
493   }
494   SCSI_Flags.eepos=arg1;
495 }
496
497
498 static void Unload(void) {
499   int src,dest;  /* the actual SCSI-level numbers */
500   if (arg2 < 0) {
501     arg2 = 0; /* default to 1st drive :-( */
502   }
503   /* check for filehandle: */
504   if (!device_opened) {
505     FatalError("No Media Changer Device Specified\n");
506   } 
507   /* okay, we should be there: */
508   if (arg1 < 0) {
509     arg1 = ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
510     if (arg1 < 0) {
511       FatalError("No Source for tape in drive %d!\n");
512     }
513   } else {
514     arg1--; /* go from bogus 1-base to zero-base */
515   }
516
517   if (arg1 >= ElementStatus->StorageElementCount) {
518         FatalError(
519           "illegal <storage-element-number> argument '%s' to 'unload' command\n",
520           arg1+1);
521   }
522     
523   if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
524     FatalError(
525                "illegal <drive-number> argument '%s' to 'unload' command\n",
526                arg2);
527   }
528   if (ElementStatus->DataTransferElementFull[arg2] < 0 ) {
529     FatalError("Data Transfer Element %d is Empty\n", arg2);
530   }    
531   /* Now see if something already lives where  we wanna go... */
532   if (ElementStatus->StorageElementFull[arg1]) {
533     FatalError("Storage Element %d is Already Full\n", arg1+1);
534   }
535   /* okay, now to get src, dest: */
536   src=ElementStatus->DataTransferElementAddress[arg2];
537   if (arg1 >=0) {
538     dest=ElementStatus->StorageElementAddress[arg1];
539   } else {
540     dest=ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
541   }
542   if (dest < 0) { /* we STILL don't know, sigh... */
543     FatalError("Do not know which slot to unload tape into!\n");
544   }
545   fprintf(stderr, "Unloading Data Transfer Element into Storage Element %d...", arg1+1);
546   fflush(stderr); /* make it real-time :-( */ 
547   Move(src,dest);
548   fprintf(stderr, "done\n");
549   ElementStatus->StorageElementFull[arg1] = true;
550   ElementStatus->DataTransferElementFull[arg2] = false;
551 }
552
553 /*****************************************************************
554  ** ARGUMENT PARSING SUBROUTINES: Parse arguments, dispatch. 
555  *****************************************************************/
556
557 /* ***
558  * int get_arg(idx):
559  *
560  * If we have an actual argument at the index position indicated (i.e. we
561  * have not gone off the edge of the world), we return
562  * its number. If we don't, or it's not a numeric argument, 
563  * we return -1. Note that 'get_arg' is kind of misleading, we only accept 
564  * numeric arguments, not any other kind. 
565  */
566 int get_arg(int idx) {
567   char *arg;
568   int retval=-1;
569
570   if (idx >= argc) return -1;  /* sorry! */
571   arg=argv[idx];
572   if (*arg < '0' || *arg > '9') {
573     return -1;  /* sorry! */
574   }
575
576   retval=atoi(arg);
577   return retval;
578 }
579
580 /* open_device() -- set the 'fh' variable.... */
581 void open_device(void) {
582
583
584   if (device_opened) {
585     SCSI_CloseDevice("Unknown",MediumChangerFD);  /* close it, sigh...  new device now! */
586   }
587
588   MediumChangerFD = SCSI_OpenDevice(device);
589   device_opened=1; /* SCSI_OpenDevice does an exit() if not. */
590 }
591   
592
593 /* we see if we've got a file open. If not, we open one :-(. Then
594  * we execute the actual command. Or not :-(. 
595  */ 
596 void execute_command(struct command_table_struct *command) {
597   RequestSense_T RequestSense;
598
599
600   if ((device==NULL) && command->need_device) {
601    /* try to get it from TAPE environment variable... */
602     device=getenv("CHANGER");
603     if (device==NULL) {
604       device=getenv("TAPE");
605       if (device==NULL) {
606         device="/dev/changer"; /* Usage(); */
607       }
608     }
609     open_device();
610   }
611   if (!ElementStatus && command->need_status) {
612     inquiry_info=RequestInquiry(MediumChangerFD,&RequestSense);
613     if (!inquiry_info) {
614       PrintRequestSense(&RequestSense);
615       FatalError("INQUIRY command Failed\n");
616     }
617     ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense,inquiry_info,&SCSI_Flags);
618     if (!ElementStatus) {
619       PrintRequestSense(&RequestSense);                   
620       FatalError("READ ELEMENT STATUS Command Failed\n"); 
621     }
622   }
623
624   /* okay, now to execute the command... */
625   command->command();
626 }
627
628 /* parse_args():
629  *   Basically, we are parsing argv/argc. We can have multiple commands
630  * on a line now, such as "unload 3 0 load 4 0" to unload one tape and
631  * load in another tape into drive 0, and we execute these commands one
632  * at a time as we come to them. If we don't have a -f at the start, we
633  * barf. If we leave out a drive #, we default to drive 0 (the first drive
634  * in the cabinet). 
635  */ 
636
637 int parse_args(void) {
638   int i,cmd_tbl_idx;
639   struct command_table_struct *command;
640
641   i=1;
642   while (i<argc) {
643     if (strcmp(argv[i],"-f") == 0) {
644       i++;
645       if (i>=argc) {
646         Usage();
647       }
648       device=argv[i++];
649       open_device(); /* open the device and do a status scan on it... */
650     } else {
651       cmd_tbl_idx=0;
652       command=&command_table[0]; /* default to the first command... */
653       command=&command_table[cmd_tbl_idx];
654       while (command->name) {
655         if (!strcmp(command->name,argv[i])) {
656           /* we have a match... */
657           break;
658         }
659         /* otherwise we don't have a match... */
660         cmd_tbl_idx++;
661         command=&command_table[cmd_tbl_idx];
662       }
663       /* if it's not a command, exit.... */
664       if (!command->name) {
665         Usage();
666       }
667       i++;  /* go to the next argument, if possible... */
668       /* see if we need to gather arguments, though! */
669       if (command->num_args == 0) {
670         execute_command(command);  /* execute_command handles 'stuff' */
671       }
672       else {
673         arg1=get_arg(i);  /* checks i... */
674         if (arg1 != -1) {
675           i++;  /* next! */
676         }
677         if (command->num_args==2 && arg1 != -1) {
678           arg2=get_arg(i); 
679           if (arg2 != -1) {
680             i++;
681           }
682         }
683         execute_command(command);
684       }
685       arg1=-1;
686       arg2=-1;
687     }
688   }
689   return 0; /* should never get here. */
690 }
691
692
693
694 int main(int ArgCount,
695          char *ArgVector[],
696          char *Environment[])
697 {
698
699
700 #ifdef VMS
701   RequestSense_T RequestSense;
702 #endif
703    
704  /* save these where we can get at them elsewhere... */
705   argc=ArgCount;
706   argv=ArgVector;
707
708   argv0=argv[0];
709
710    
711
712
713   parse_args();  /* also executes them as it sees them, sigh. */
714
715 #ifndef VMS
716   if (device) 
717     SCSI_CloseDevice(device, MediumChangerFD);
718   return 0;
719 #else
720   if (device) {
721     ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense);
722     if (!ElementStatus) {
723       PrintRequestSense(&RequestSense);                   
724       FatalError("READ ELEMENT STATUS Command Failed\n"); 
725     }
726     VMS_DefineStatusSymbols();
727     SCSI_CloseDevice(device, MediumChangerFD);
728   }
729   return SS$_NORMAL;
730 #endif
731
732
733 }
734 /*
735  *$Log: mtx.c,v $
736  *Revision 1.2.2.1  2001/11/06 21:20:40  elgreen
737  *Hopefully a fix to the problem with the 0 return for open in crontabs
738  *
739  *Revision 1.2  2001/06/09 17:26:26  elgreen
740  *Added 'nobarcode' command to mtx (to skip the initial request asking for
741  *barcodes for mtx status purposes).
742  *
743  *Revision 1.1.1.1  2001/06/05 17:10:22  elgreen
744  *Initial import into SourceForge
745  *
746  *Revision 1.15  2001/04/18 16:32:59  eric
747  *Cleaned up all -Wall messages.
748  *
749  *Revision 1.14  2001/04/18 16:08:08  eric
750  *after re-arranging & fixing bugs
751  *
752  *Revision 1.13  2001/03/06 01:37:05  eric
753  *Solaris changes
754  *
755  *Revision 1.12  2001/01/24 22:04:08  eric
756  *added /dev/changer as default file name.
757  *
758  *Revision 1.11  2001/01/15 22:21:06  eric
759  *added autoconf stuff.
760  *
761  *Revision 1.10  2000/11/30 20:37:17  eric
762  *Added quicky 'erase' command because I needed blank tapes and Linux
763  *only does long erases. (Not documented on purpose, because of rather
764  *bizarre interactions with the native tape driver if you don't know
765  *what you are doing).
766  *
767  *Revision 1.9  2000/11/28 01:10:50  eric
768  *Fixed syntax error in mtx.c...
769  *
770  *Revision 1.8  2000/10/28 00:10:35  eric
771  *Fixed stupid typo
772  *
773  *Revision 1.7  2000/10/27 23:36:57  eric
774  *make mtx inventory return an error code if the inventory fails, so that
775  *we can wait for inventory to be completed at system startup
776  *
777  *
778  */