Imported Debian patch 1.2.16rel-4
[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> ] [invert] load <storage-element-number> [<drive#>]\n\
161   mtx [ -f <loader-dev> ] [invert] unload [<storage-element-number>][<drive#>]\n\
162   mtx [ -f <loader-dev> ] [eepos eepos-number] transfer <storage-element-number> <storage-element-number>\n\
163   mtx [ -f <device> ] eject\n");
164
165 #ifndef VMS
166   exit(1);
167 #else
168   sys$exit(VMS_ExitCode);
169 #endif
170 }
171
172
173
174 static void Version(void) {
175   fprintf(stderr,"mtx version %s\n\n",VERSION);
176   Usage(); 
177 }
178
179 static void NoAttach(void) {
180   SCSI_Flags.no_attached=1;
181 }
182
183 static void InvertCommand(void) {
184   SCSI_Flags.invert=1;
185   /* invert_bit=1;*/
186 }
187
188 static void NoBarCode(void) {
189   SCSI_Flags.no_barcodes=1;  /* don't request barcodes, sigh! */
190
191
192 /* First and Last are easy. Next is the bitch. */
193 static void First(void){
194   int driveno;
195   /* okay, first see if we have a drive#: */
196   if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
197     driveno=arg1;
198   } else {
199     driveno = 0;
200   }
201   /* now see if there's anything *IN* that drive: */
202   if (ElementStatus->DataTransferElementFull[driveno]) {
203     /* if so, then unload it... */
204     arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
205     if (arg1==1) {
206       printf("loading...done.\n");  /* it already has tape #1 in it! */ 
207       return;
208     }
209     arg2=driveno;
210     Unload();
211   }
212   /* and now to actually do the Load(): */
213   arg2=driveno;
214   arg1=1; /* first! */
215   Load(); /* and voila! */
216   
217 }
218
219 static void Last(void) {
220   int driveno;
221   /* okay, first see if we have a drive#: */
222   if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
223     driveno=arg1;
224   } else {
225     driveno = 0;
226   }
227
228   /* now see if there's anything *IN* that drive: */
229   if (ElementStatus->DataTransferElementFull[driveno]) {
230     /* if so, then unload it... */
231     arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
232     if (arg1==ElementStatus->StorageElementCount) {
233       printf("loading...done.\n");  /* it already has last tape in it! */ 
234       return;
235     }
236     arg2=driveno;
237     Unload();
238   }
239
240   arg1 = ElementStatus->StorageElementCount; /* the last slot... */
241   arg2=driveno;
242   Load(); /* and load it, sigh! */
243
244 }
245
246
247 static void Next(void) {
248   int driveno;
249   int current=0; 
250   /* okay, first see if we have a drive#: */
251   if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
252     driveno=arg1;
253   } else {
254     driveno = 0;
255   }
256
257   /* Now to see if there's anything in that drive! */
258   if (ElementStatus->DataTransferElementFull[driveno]) {
259     /* if so, unload it! */
260     arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
261     current=arg1;
262     arg2=driveno;
263     Unload();
264   }
265   
266   /* okay, now to load, if we can... */
267   current++;
268   if (current > ElementStatus->StorageElementCount) { /* the last slot... */
269     FatalError("No More Tapes\n");
270   }
271   arg1=current;
272   arg2=driveno;
273   Load();
274 }
275
276 static void do_Inventory(void) 
277 {
278   int i;
279   i=Inventory(MediumChangerFD);
280   if (i < 0) {
281     fprintf(stderr,"mtx:inventory failed\n");
282     fflush(stderr);
283     exit(1); /*  exit with an error status. */
284   }
285   return;  /* if it failed, well, sigh.... */
286 }
287
288 /* For Linux, this allows us to do a short erase on a tape (sigh!).
289  * Note that you'll need to do a 'mt status' on the tape afterwards in
290  * order to get the tape driver in sync with the tape drive again. Also
291  * note that on other OS's, this might do other evil things to the tape
292  * driver. Note that to do an erase, you must first rewind using the OS's
293  * native tools!
294  */
295 static void do_Erase(void) {
296   RequestSense_T *RequestSense;
297   RequestSense=Erase(MediumChangerFD);
298   if (RequestSense) {
299     PrintRequestSense(RequestSense);
300     exit(1);  /* exit with an error status. */
301   }
302   return;
303 }
304   
305
306 /* This should eject a tape or magazine, depending upon the device sent
307  * to.
308  */
309 static void do_Unload(void)
310 {
311   int i;
312   i=Eject(MediumChangerFD);
313   if (i<0) {
314     fprintf(stderr,"mtx:eject failed\n");
315     fflush(stderr);
316   }
317   return;  /* if it failed, well, sigh.... */
318 }
319
320 static void ReportInquiry(void)
321 {
322   RequestSense_T RequestSense;
323   Inquiry_T *Inquiry;
324   int i;
325
326   Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
327   if (Inquiry == NULL) 
328     {
329       PrintRequestSense(&RequestSense);
330       FatalError("INQUIRY Command Failed\n");
331     }
332   
333   printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
334   printf("Vendor ID: '");
335   for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
336     printf("%c", Inquiry->VendorIdentification[i]);
337   printf("'\nProduct ID: '");
338   for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
339     printf("%c", Inquiry->ProductIdentification[i]);
340   printf("'\nRevision: '");
341   for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
342     printf("%c", Inquiry->ProductRevisionLevel[i]);
343   printf("'\n");\
344   if (Inquiry->MChngr) {  /* check the attached-media-changer bit... */
345     printf("Attached Changer: Yes\n");
346   } else {
347     printf("Attached Changer: No\n");
348   }
349   free(Inquiry);  /* well, we're about to exit, but ... */
350 }
351
352 static void Status(void)
353 {
354   int StorageElementNumber;
355   int TransferElementNumber;
356   printf("  Storage Changer %s:%d Drives, %d Slots ( %d Import/Export )\n",
357          device,
358          ElementStatus->DataTransferElementCount,
359          ElementStatus->StorageElementCount,
360          ElementStatus->ImportExportCount);
361
362
363   for (TransferElementNumber=0;TransferElementNumber <
364          ElementStatus->DataTransferElementCount;TransferElementNumber++) {
365     
366     printf("Data Transfer Element %d:",TransferElementNumber);
367     if (ElementStatus->DataTransferElementFull[TransferElementNumber])
368       {
369         if (ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber] > -1) 
370           printf("Full (Storage Element %d Loaded)",
371                  ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber]+1);
372         else printf("Full (Unknown Storage Element Loaded)");
373         if
374           (ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber][0])
375           printf(":VolumeTag = %s",ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber]);
376         if
377           (ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber][0])
378           
379           printf(":AlternateVolumeTag = %s",ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber]); 
380         putchar('\n');
381       }
382     else printf("Empty\n");
383   }
384
385   for (StorageElementNumber = 0;
386        StorageElementNumber < ElementStatus->StorageElementCount;
387        StorageElementNumber++) {
388     printf("      Storage Element %d%s:%s", StorageElementNumber+1, 
389            (ElementStatus->StorageElementIsImportExport[StorageElementNumber]) ? " IMPORT/EXPORT" : "",
390            (ElementStatus->StorageElementFull[StorageElementNumber] ? "Full " : "Empty"));
391     if (ElementStatus->PrimaryVolumeTag[StorageElementNumber][0]) {
392       printf(":VolumeTag=%s",ElementStatus->PrimaryVolumeTag[StorageElementNumber]);
393     }
394     if (ElementStatus->AlternateVolumeTag[StorageElementNumber][0]) {
395             printf(":AlternateVolumeTag=%s",ElementStatus->AlternateVolumeTag[StorageElementNumber]);
396     }
397     putchar('\n');
398   }
399   
400 #ifdef VMS
401   VMS_DefineStatusSymbols();
402 #endif
403 }
404
405 void Move(int src, int dest) {
406   RequestSense_T *result; /* from MoveMedium */
407   
408   result=MoveMedium(MediumChangerFD,src,dest,ElementStatus,inquiry_info,&SCSI_Flags);
409   if (result)   /* we have an error! */
410      {
411       if (result->AdditionalSenseCode == 0x3B &&
412           result->AdditionalSenseCodeQualifier == 0x0E)
413         FatalError("source Element Address %d is Empty\n", src);
414       if (result->AdditionalSenseCode == 0x3B &&
415           result->AdditionalSenseCodeQualifier == 0x0D)
416         FatalError("destination Element Address %d is Already Full\n",
417                    dest);
418       if (result->AdditionalSenseCode == 0x30 &&
419           result->AdditionalSenseCodeQualifier == 0x03)
420         FatalError("Cleaning Cartridge Installed and Ejected\n");
421       PrintRequestSense(result);
422       FatalError("MOVE MEDIUM from Element Address %d to %d Failed\n",
423                  src,dest);
424     }
425 }   
426
427 /* okay, now for the Load, Unload, etc. logic: */
428
429 static void Load(void) {
430   int src, dest;
431   /* okay, check src, dest: arg1=src, arg2=dest */
432   if (arg1 < 1) {
433     FatalError("No source specified\n");
434   }
435   if (arg2 < 0) {
436     arg2 = 0; /* default to 1st drive :-( */
437   }
438   arg1--;  /* we use zero-based arrays, sigh, not 1 base like some lusers */
439   /* check for filehandle: */
440   if (!device_opened) {
441     FatalError("No Media Changer Device Specified\n");
442   } 
443   /* okay, we should be there: */
444   if (arg1 < 0 || arg1 >= ElementStatus->StorageElementCount) {
445         FatalError(
446           "illegal <storage-element-number> argument '%s' to 'load' command\n",
447           arg1+1);
448   }
449   if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
450     FatalError(
451                "illegal <drive-number> argument '%s' to 'load' command\n",
452                arg2);
453   }
454   if (ElementStatus->DataTransferElementFull[arg2])  {
455     FatalError("Drive %d Full (Storage Element %d loaded)\n",arg2,
456                ElementStatus->DataTransferElementSourceStorageElementNumber[arg2]+1);
457   }
458   /* Now look up the actual devices: */
459   dest=ElementStatus->DataTransferElementAddress[arg2];
460   src=ElementStatus->StorageElementAddress[arg1];
461   Move(src,dest);  /* load it into the particular slot, if possible! */
462   /* now set the status for further command son this line... */
463   ElementStatus->StorageElementFull[arg1] = false;
464   ElementStatus->DataTransferElementFull[arg2] = true;
465
466   return; /* and done! */
467 }
468
469 static void Transfer(void) {
470   int src,dest;
471   if (arg1 < 1 ) {
472     FatalError("No source specified\n");
473   } 
474   if (arg2 < 1) {
475     FatalError("No destination specified\n");
476   }
477   if (arg1 > ElementStatus->StorageElementCount) {
478     FatalError("Invalid source\n");
479   }
480   if (arg2 > ElementStatus->StorageElementCount) {
481     FatalError("Invalid destination\n");
482   }
483   /* okay, that's done... */
484   src=ElementStatus->StorageElementAddress[arg1-1];
485   dest=ElementStatus->StorageElementAddress[arg2-1];
486   Move(src,dest);
487 }
488
489 static void Eepos(void) {
490   if (arg1 < 0 || arg1 > 3) {
491     FatalError("eepos equires argument between 0 and 3.\n");
492   }
493   SCSI_Flags.eepos=arg1;
494 }
495
496
497 static void Unload(void) {
498   int src,dest;  /* the actual SCSI-level numbers */
499   if (arg2 < 0) {
500     arg2 = 0; /* default to 1st drive :-( */
501   }
502   /* check for filehandle: */
503   if (!device_opened) {
504     FatalError("No Media Changer Device Specified\n");
505   } 
506   /* okay, we should be there: */
507   if (arg1 < 0) {
508     arg1 = ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
509     if (arg1 < 0) {
510       FatalError("No Source for tape in drive %d!\n");
511     }
512   } else {
513     arg1--; /* go from bogus 1-base to zero-base */
514   }
515
516   if (arg1 >= ElementStatus->StorageElementCount) {
517         FatalError(
518           "illegal <storage-element-number> argument '%s' to 'unload' command\n",
519           arg1+1);
520   }
521     
522   if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
523     FatalError(
524                "illegal <drive-number> argument '%s' to 'unload' command\n",
525                arg2);
526   }
527   if (ElementStatus->DataTransferElementFull[arg2] < 0 ) {
528     FatalError("Data Transfer Element %d is Empty\n", arg2);
529   }    
530   /* Now see if something already lives where  we wanna go... */
531   if (ElementStatus->StorageElementFull[arg1]) {
532     FatalError("Storage Element %d is Already Full\n", arg1+1);
533   }
534   /* okay, now to get src, dest: */
535   src=ElementStatus->DataTransferElementAddress[arg2];
536   if (arg1 >=0) {
537     dest=ElementStatus->StorageElementAddress[arg1];
538   } else {
539     dest=ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
540   }
541   if (dest < 0) { /* we STILL don't know, sigh... */
542     FatalError("Do not know which slot to unload tape into!\n");
543   }
544   fprintf(stderr, "Unloading Data Transfer Element into Storage Element %d...", arg1+1);
545   fflush(stderr); /* make it real-time :-( */ 
546   Move(src,dest);
547   fprintf(stderr, "done\n");
548   ElementStatus->StorageElementFull[arg1] = true;
549   ElementStatus->DataTransferElementFull[arg2] = false;
550 }
551
552 /*****************************************************************
553  ** ARGUMENT PARSING SUBROUTINES: Parse arguments, dispatch. 
554  *****************************************************************/
555
556 /* ***
557  * int get_arg(idx):
558  *
559  * If we have an actual argument at the index position indicated (i.e. we
560  * have not gone off the edge of the world), we return
561  * its number. If we don't, or it's not a numeric argument, 
562  * we return -1. Note that 'get_arg' is kind of misleading, we only accept 
563  * numeric arguments, not any other kind. 
564  */
565 int get_arg(int idx) {
566   char *arg;
567   int retval=-1;
568
569   if (idx >= argc) return -1;  /* sorry! */
570   arg=argv[idx];
571   if (*arg < '0' || *arg > '9') {
572     return -1;  /* sorry! */
573   }
574
575   retval=atoi(arg);
576   return retval;
577 }
578
579 /* open_device() -- set the 'fh' variable.... */
580 void open_device(void) {
581
582
583   if (device_opened) {
584     SCSI_CloseDevice("Unknown",MediumChangerFD);  /* close it, sigh...  new device now! */
585   }
586
587   MediumChangerFD = SCSI_OpenDevice(device);
588   device_opened=1; /* SCSI_OpenDevice does an exit() if not. */
589 }
590   
591
592 /* we see if we've got a file open. If not, we open one :-(. Then
593  * we execute the actual command. Or not :-(. 
594  */ 
595 void execute_command(struct command_table_struct *command) {
596   RequestSense_T RequestSense;
597
598
599   if ((device==NULL) && command->need_device) {
600    /* try to get it from TAPE environment variable... */
601     device=getenv("CHANGER");
602     if (device==NULL) {
603       device=getenv("TAPE");
604       if (device==NULL) {
605         device="/dev/changer"; /* Usage(); */
606       }
607     }
608     open_device();
609   }
610   if (!ElementStatus && command->need_status) {
611     inquiry_info=RequestInquiry(MediumChangerFD,&RequestSense);
612     if (!inquiry_info) {
613       PrintRequestSense(&RequestSense);
614       FatalError("INQUIRY command Failed\n");
615     }
616     ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense,inquiry_info,&SCSI_Flags);
617     if (!ElementStatus) {
618       PrintRequestSense(&RequestSense);                   
619       FatalError("READ ELEMENT STATUS Command Failed\n"); 
620     }
621   }
622
623   /* okay, now to execute the command... */
624   command->command();
625 }
626
627 /* parse_args():
628  *   Basically, we are parsing argv/argc. We can have multiple commands
629  * on a line now, such as "unload 3 0 load 4 0" to unload one tape and
630  * load in another tape into drive 0, and we execute these commands one
631  * at a time as we come to them. If we don't have a -f at the start, we
632  * barf. If we leave out a drive #, we default to drive 0 (the first drive
633  * in the cabinet). 
634  */ 
635
636 int parse_args(void) {
637   int i,cmd_tbl_idx;
638   struct command_table_struct *command;
639
640   i=1;
641   while (i<argc) {
642     if (strcmp(argv[i],"-f") == 0) {
643       i++;
644       if (i>=argc) {
645         Usage();
646       }
647       device=argv[i++];
648       open_device(); /* open the device and do a status scan on it... */
649     } else {
650       cmd_tbl_idx=0;
651       command=&command_table[0]; /* default to the first command... */
652       command=&command_table[cmd_tbl_idx];
653       while (command->name) {
654         if (!strcmp(command->name,argv[i])) {
655           /* we have a match... */
656           break;
657         }
658         /* otherwise we don't have a match... */
659         cmd_tbl_idx++;
660         command=&command_table[cmd_tbl_idx];
661       }
662       /* if it's not a command, exit.... */
663       if (!command->name) {
664         Usage();
665       }
666       i++;  /* go to the next argument, if possible... */
667       /* see if we need to gather arguments, though! */
668       if (command->num_args == 0) {
669         execute_command(command);  /* execute_command handles 'stuff' */
670       }
671       else {
672         arg1=get_arg(i);  /* checks i... */
673         if (arg1 != -1) {
674           i++;  /* next! */
675         }
676         if (command->num_args==2 && arg1 != -1) {
677           arg2=get_arg(i); 
678           if (arg2 != -1) {
679             i++;
680           }
681         }
682         execute_command(command);
683       }
684       arg1=-1;
685       arg2=-1;
686     }
687   }
688   return 0; /* should never get here. */
689 }
690
691
692
693 int main(int ArgCount,
694          char *ArgVector[],
695          char *Environment[])
696 {
697
698
699 #ifdef VMS
700   RequestSense_T RequestSense;
701 #endif
702    
703  /* save these where we can get at them elsewhere... */
704   argc=ArgCount;
705   argv=ArgVector;
706
707   argv0=argv[0];
708
709    
710
711
712   parse_args();  /* also executes them as it sees them, sigh. */
713
714 #ifndef VMS
715   if (device) 
716     SCSI_CloseDevice(device, MediumChangerFD);
717   return 0;
718 #else
719   if (device) {
720     ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense);
721     if (!ElementStatus) {
722       PrintRequestSense(&RequestSense);                   
723       FatalError("READ ELEMENT STATUS Command Failed\n"); 
724     }
725     VMS_DefineStatusSymbols();
726     SCSI_CloseDevice(device, MediumChangerFD);
727   }
728   return SS$_NORMAL;
729 #endif
730
731
732 }
733 /*
734  *$Log: mtx.c,v $
735  *Revision 1.2.2.1  2001/11/06 21:20:40  elgreen
736  *Hopefully a fix to the problem with the 0 return for open in crontabs
737  *
738  *Revision 1.2  2001/06/09 17:26:26  elgreen
739  *Added 'nobarcode' command to mtx (to skip the initial request asking for
740  *barcodes for mtx status purposes).
741  *
742  *Revision 1.1.1.1  2001/06/05 17:10:22  elgreen
743  *Initial import into SourceForge
744  *
745  *Revision 1.15  2001/04/18 16:32:59  eric
746  *Cleaned up all -Wall messages.
747  *
748  *Revision 1.14  2001/04/18 16:08:08  eric
749  *after re-arranging & fixing bugs
750  *
751  *Revision 1.13  2001/03/06 01:37:05  eric
752  *Solaris changes
753  *
754  *Revision 1.12  2001/01/24 22:04:08  eric
755  *added /dev/changer as default file name.
756  *
757  *Revision 1.11  2001/01/15 22:21:06  eric
758  *added autoconf stuff.
759  *
760  *Revision 1.10  2000/11/30 20:37:17  eric
761  *Added quicky 'erase' command because I needed blank tapes and Linux
762  *only does long erases. (Not documented on purpose, because of rather
763  *bizarre interactions with the native tape driver if you don't know
764  *what you are doing).
765  *
766  *Revision 1.9  2000/11/28 01:10:50  eric
767  *Fixed syntax error in mtx.c...
768  *
769  *Revision 1.8  2000/10/28 00:10:35  eric
770  *Fixed stupid typo
771  *
772  *Revision 1.7  2000/10/27 23:36:57  eric
773  *make mtx inventory return an error code if the inventory fails, so that
774  *we can wait for inventory to be completed at system startup
775  *
776  *
777  */