7bf190fb310b693644fcf0ebf77437ddeaeed1e5
[debian/amanda] / changer-src / chg-scsi.c
1 #ifndef lint
2 static char rcsid[] = "$Id: chg-scsi.c,v 1.44 2006/03/09 20:06:10 johnfranks Exp $";
3 #endif
4 /*
5  * 
6  *
7  *  chg-scsi.c -- generic SCSI changer driver
8  *
9  *  This program provides the framework to control
10  *  SCSI changers. It is based on the original chg-scsi
11  *  from Eric Schnoebelen <eric@cirr.com> (Original copyright below)
12  *  The device dependent part is handled by scsi-changer-driver.c
13  *  The SCSI OS interface is handled by scsi-ostype.c
14  *
15  *  Original copyrigths:
16  *
17  *  This program provides a driver to control generic
18  *  SCSI changers, no matter what platform.  The host/OS
19  *  specific portions of the interface are implemented
20  *  in libscsi.a, which contains a module for each host/OS.
21  *  The actual interface for HP/UX is in scsi-hpux.c;
22  *  chio is in scsi-chio.c, etc..  A prototype system
23  *  dependent scsi interface file is in scsi-proto.c.
24  *
25  *  Copyright 1997, 1998 Eric Schnoebelen <eric@cirr.com>
26  *
27  * This module based upon seagate-changer, by Larry Pyeatt
28  *                  <pyeatt@cs.colostate.edu>
29  *
30  * The original introductory comments follow:
31  *
32  * This program was written to control the Seagate/Conner/Archive
33  * autoloading DAT drive.  This drive normally has 4 tape capacity
34  * but can be expanded to 12 tapes with an optional tape cartridge.
35  * This program may also work on onther drives.  Try it and let me
36  * know of successes/failures.
37  *
38  * I have attempted to conform to the requirements for Amanda tape
39  * changer interface.  There could be some bugs.  
40  *
41  * This program works for me under Linux with Gerd Knorr's 
42  * <kraxel@cs.tu-berlin.de> SCSI media changer driver installed 
43  * as a kernel module.  The kernel module is available at 
44  * http://sunsite.unc.edu/pub/Linux/kernel/patches/scsi/scsi-changer*
45  * Since the Linux media changer is based on NetBSD, this program
46  * should also work for NetBSD, although I have not tried it.
47  * It may be necessary to change the IOCTL calls to work on other
48  * OS's.  
49  *
50  * (c) 1897 Larry Pyeatt,  pyeatt@cs.colostate.edu 
51  * All Rights Reserved.
52  * 
53  * Permission to use, copy, modify, distribute, and sell this software and its
54  * documentation for any purpose is hereby granted without fee, provided that
55  * the above copyright notice appear in all copies and that both that
56  * copyright notice and this permission notice appear in supporting
57  * documentation.  The author makes no representations about the
58  * suitability of this software for any purpose.   It is provided "as is"
59  * without express or implied warranty.
60  *
61  * Michael C. Povel 03.06.98 added ejetct_tape and sleep for external tape
62  * devices, and changed some code to allow multiple drives to use their
63  * own slots. Also added support for reserverd slots.
64  * At the moment these parameters are hard coded and only tested under Linux
65  * 
66  */
67
68
69 #include "config.h"
70
71
72 #include "amanda.h"
73
74 #ifdef HAVE_DMALLOC_H
75 #include <dmalloc.h>
76 #endif
77
78
79 #include "conffile.h"
80 #include "libscsi.h"
81 #include "scsi-defs.h"
82 #include "tapeio.h"
83
84 char *tapestatfile = NULL;
85 FILE *debug_file = NULL;
86
87 /* 
88  * So we have 3 devices, here will all the infos be stored after an
89  * successfull open 
90  */
91
92 OpenFiles_T *pDev = NULL;
93
94 /* Defined in scsi-changer-driver.c
95  */
96 extern int ElementStatusValid;
97 extern ElementInfo_T *pMTE; /*Medium Transport Element */
98 extern ElementInfo_T *pSTE; /*Storage Element */
99 extern ElementInfo_T *pIEE; /*Import Export Element */
100 extern ElementInfo_T *pDTE; /*Data Transfer Element */
101 extern int MTE;             /*Counter for the above element types */
102 extern int STE;
103 extern int IEE;
104 extern int DTE;
105
106 changer_t chg;
107
108 int do_inventory = 0;     /* Set if load/unload functions thinks an inventory should be done */
109 int clean_slot = -1;
110
111 typedef enum{
112   NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM,
113     CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE, LABELFILE, CHANGERIDENT,
114     TAPEIDENT, EMUBARCODE, HAVEBARCODE, DEBUGLEVEL, AUTOINV
115     } token_t;
116
117 typedef struct {
118   char *word;
119   int token;
120 } tokentable_t;
121
122 tokentable_t t_table[]={
123   { "number_configs",NUMDRIVE},
124   { "autoinv", AUTOINV},
125   { "eject",EJECT},
126   { "sleep",SLEEP},
127   { "cleanmax",CLEANMAX},
128   { "config",DRIVE},
129   { "startuse",START},
130   { "enduse",END},
131   { "cleancart",CLEAN},
132   { "dev",DEVICE},
133   { "statfile",STATFILE},
134   { "cleanfile",CLEANFILE},
135   { "drivenum",DRIVENUM},
136   { "changerdev",CHANGERDEV},
137   { "usagecount",USAGECOUNT},
138   { "scsitapedev", SCSITAPEDEV},
139   { "tapestatus", TAPESTATFILE},
140   { "labelfile", LABELFILE},
141   { "changerident" , CHANGERIDENT},
142   { "tapeident", TAPEIDENT},
143   { "emubarcode", EMUBARCODE},
144   { "havebarcode", HAVEBARCODE},
145   { "debuglevel", DEBUGLEVEL},
146   { NULL,-1 }
147 };
148
149 void init_changer_struct(changer_t *chg,int number_of_config)
150      /* Initialize datasructures with default values */
151 {
152   int i;
153  
154   chg->number_of_configs = number_of_config;
155   chg->eject = 1;
156   chg->sleep = 0;
157   chg->autoinv = 0;
158   chg->cleanmax = 0;
159   chg->havebarcode = 0;
160   chg->emubarcode = 0;  
161   chg->device = NULL;
162   chg->labelfile = NULL;
163   chg->debuglevel = NULL;
164   chg->conf = malloc(sizeof(config_t)*number_of_config);
165   if (chg->conf != NULL){
166     for (i=0; i < number_of_config; i++){
167       chg->conf[i].drivenum   = 0;
168       chg->conf[i].start      = -1;
169       chg->conf[i].end        = -1;
170       chg->conf[i].cleanslot  = -1;
171       chg->conf[i].device     = NULL;
172       chg->conf[i].slotfile   = NULL;
173       chg->conf[i].cleanfile  = NULL;
174       chg->conf[i].timefile  = NULL;
175       chg->conf[i].scsitapedev = NULL;
176       chg->conf[i].tapestatfile = NULL;
177       chg->conf[i].changerident = NULL;
178       chg->conf[i].tapeident = NULL;
179     }
180   } else {
181    fprintf(stderr,"init_changer_struct malloc failed\n");
182   }
183 }
184
185 void dump_changer_struct(changer_t chg)
186      /* Dump of information for debug */
187 {
188   int i;
189
190   dbprintf(("Number of configurations: %d\n",chg.number_of_configs));
191   dbprintf(("Tapes need eject: %s\n",(chg.eject>0?"Yes":"No")));
192         dbprintf (("\traw: %d\n",chg.eject));
193   dbprintf(("Inv. auto update: %s\n",(chg.autoinv>0?"Yes":"No")));
194   dbprintf (("\traw: %d\n",chg.autoinv));
195   dbprintf(("barcode reader  : %s\n",(chg.havebarcode>0?"Yes":"No")));
196   dbprintf (("\traw: %d\n",chg.havebarcode));
197   dbprintf(("Emulate Barcode : %s\n",(chg.emubarcode>0?"Yes":"No")));
198   dbprintf (("\traw: %d\n",chg.emubarcode));
199   if (chg.debuglevel != NULL)
200      dbprintf(("debug level     : %s\n", chg.debuglevel));
201   dbprintf(("Tapes need sleep: %d seconds\n",chg.sleep));
202   dbprintf(("Cleancycles     : %d\n",chg.cleanmax));
203   dbprintf(("Changerdevice   : %s\n",chg.device));
204   if (chg.labelfile != NULL)
205     dbprintf(("Labelfile       : %s\n", chg.labelfile));
206   for (i=0; i<chg.number_of_configs; i++){
207     dbprintf(("Tapeconfig Nr: %d\n",i));
208     dbprintf(("  Drivenumber   : %d\n",chg.conf[i].drivenum));
209     dbprintf(("  Startslot     : %d\n",chg.conf[i].start));
210     dbprintf(("  Endslot       : %d\n",chg.conf[i].end));
211     dbprintf(("  Cleanslot     : %d\n",chg.conf[i].cleanslot));
212
213     if (chg.conf[i].device != NULL)
214       dbprintf(("  Devicename    : %s\n",chg.conf[i].device));
215     else
216       dbprintf(("  Devicename    : none\n"));
217
218     if (chg.conf[i].changerident != NULL)
219       dbprintf(("  changerident  : %s\n",chg.conf[i].changerident));
220     else
221       dbprintf(("  changerident  : none\n"));
222
223     if (chg.conf[i].scsitapedev != NULL)
224       dbprintf(("  SCSITapedev   : %s\n",chg.conf[i].scsitapedev));
225     else
226       dbprintf(("  SCSITapedev   : none\n"));
227
228     if (chg.conf[i].tapeident != NULL)
229       dbprintf(("  tapeident     : %s\n",chg.conf[i].tapeident));
230     else
231       dbprintf(("  tapeident     : none\n"));
232
233     if (chg.conf[i].tapestatfile != NULL)
234       dbprintf(("  statfile      : %s\n", chg.conf[i].tapestatfile));
235     else
236       dbprintf(("  statfile      : none\n"));
237
238     if (chg.conf[i].slotfile != NULL)
239       dbprintf(("  Slotfile      : %s\n",chg.conf[i].slotfile));
240     else
241       dbprintf(("  Slotfile      : none\n"));
242
243     if (chg.conf[i].cleanfile != NULL)
244       dbprintf(("  Cleanfile     : %s\n",chg.conf[i].cleanfile));
245     else
246       dbprintf(("  Cleanfile     : none\n"));
247
248     if (chg.conf[i].timefile != NULL)
249       dbprintf(("  Usagecount    : %s\n",chg.conf[i].timefile));
250     else
251       dbprintf(("  Usagecount    : none\n"));
252   }
253 }
254
255 void free_changer_struct(changer_t *chg)
256      /* Free all allocated memory */
257 {
258   int i;
259
260   if (chg->device != NULL)
261     free(chg->device);
262   for (i=0; i<chg->number_of_configs; i++){
263     if (chg->conf[i].device != NULL)
264       free(chg->conf[i].device);
265     if (chg->conf[i].slotfile != NULL)
266       free(chg->conf[i].slotfile);
267     if (chg->conf[i].cleanfile != NULL)
268       free(chg->conf[i].cleanfile);
269     if (chg->conf[i].timefile != NULL)
270       free(chg->conf[i].timefile);
271   }
272   if (chg->conf != NULL)
273     free(chg->conf);
274   chg->conf = NULL;
275   chg->device = NULL;
276 }
277
278 void parse_line(char *linebuffer,int *token,char **value)
279      /* This function parses a line, and returns a token and value */
280 {
281   char *tok;
282   int i;
283   int ready = 0;
284   *token = -1;  /* No Token found */
285   tok=strtok(linebuffer," \t\n");
286
287   while ((tok != NULL) && (tok[0]!='#')&&(ready==0)){
288     if (*token != -1){
289       *value=tok;
290       ready=1;
291     } else {
292       i=0;
293       while ((t_table[i].word != NULL)&&(*token==-1)){
294         if (0==strcasecmp(t_table[i].word,tok)){
295           *token=t_table[i].token;
296         }
297         i++;
298       }
299     }
300     tok=strtok(NULL," \t\n");
301   }
302   return;
303 }
304
305 int read_config(char *configfile, changer_t *chg)
306      /* This function reads the specified configfile and fills the structure */
307 {
308   int numconf;
309   FILE *file;
310   int init_flag = 0;
311   int drivenum=0;
312   char *linebuffer = NULL;
313   int token;
314   char *value;
315   char *p;
316
317   numconf = 1;  /* At least one configuration is assumed */
318   /* If there are more, it should be the first entry in the configurationfile */
319
320
321   if (NULL==(file=fopen(configfile,"r"))){
322     return (-1);
323   }
324
325   amfree(linebuffer);
326   while ((NULL!=(linebuffer=agets(file)))) {
327       parse_line(linebuffer,&token,&value);
328       if (token != -1){
329         if (0==init_flag) {
330           if (token != NUMDRIVE){
331             init_changer_struct(chg,numconf);
332           } else {
333             numconf = atoi(value);
334             init_changer_struct(chg,numconf);
335           }
336           init_flag=1;
337         }
338         switch (token){
339         case NUMDRIVE: if (atoi(value) != numconf)
340           fprintf(stderr,"Error: number_drives at wrong place, should be "\
341                   "first in file\n");
342         break;
343         case AUTOINV:
344           chg->autoinv = 1;
345           break;
346         case EMUBARCODE:
347           chg->emubarcode = 1;
348           break;
349         case DEBUGLEVEL:
350           chg->debuglevel = stralloc(value);
351           break;
352         case EJECT:
353           chg->eject = atoi(value);
354           break;
355         case HAVEBARCODE:
356           chg->havebarcode = atoi(value);
357           break;
358        case SLEEP:
359           chg->sleep = atoi(value);
360           break;
361         case LABELFILE:
362           chg->labelfile = stralloc(value);
363           break;
364         case CHANGERDEV:
365           chg->device = stralloc(value);
366           break;
367         case SCSITAPEDEV:
368           chg->conf[drivenum].scsitapedev = stralloc(value);
369           break;
370         case TAPESTATFILE:
371           chg->conf[drivenum].tapestatfile = stralloc(value);
372           break;
373         case CHANGERIDENT:
374           chg->conf[drivenum].changerident = stralloc(value);
375           p = chg->conf[drivenum].changerident;
376           while (*p != '\0')
377           {
378             if (*p == '_')
379             {
380               *p=' ';
381             }
382             p++;
383           }
384           break;
385         case TAPEIDENT:
386           chg->conf[drivenum].tapeident = stralloc(value);
387           break;
388         case CLEANMAX:
389           chg->cleanmax = atoi(value);
390           break;
391         case DRIVE:
392           drivenum = atoi(value);
393           if(drivenum >= numconf){
394             fprintf(stderr,"Error: drive must be less than number_drives\n");
395           }
396           break;
397         case DRIVENUM:
398           if (drivenum < numconf){
399             chg->conf[drivenum].drivenum = atoi(value);
400           } else {
401             fprintf(stderr,"Error: drive is not less than number_drives"\
402                     " drivenum ignored\n");
403           }
404           break;
405         case START:
406           if (drivenum < numconf){
407             chg->conf[drivenum].start = atoi(value);
408           } else {
409             fprintf(stderr,"Error: drive is not less than number_drives"\
410                     " startuse ignored\n");
411           }
412           break;
413         case END:
414           if (drivenum < numconf){
415             chg->conf[drivenum].end = atoi(value);
416           } else {
417             fprintf(stderr,"Error: drive is not less than number_drives"\
418                     " enduse ignored\n");
419           }
420           break;
421         case CLEAN:
422           if (drivenum < numconf){
423             chg->conf[drivenum].cleanslot = atoi(value);
424           } else {
425             fprintf(stderr,"Error: drive is not less than number_drives"\
426                     " cleanslot ignored\n");
427           }
428           break;
429         case DEVICE:
430           if (drivenum < numconf){
431             chg->conf[drivenum].device = stralloc(value);
432           } else {
433             fprintf(stderr,"Error: drive is not less than number_drives"\
434                     " device ignored\n");
435           }
436           break;
437         case STATFILE:
438           if (drivenum < numconf){
439             chg->conf[drivenum].slotfile = stralloc(value);
440           } else {
441             fprintf(stderr,"Error: drive is not less than number_drives"\
442                     " slotfile ignored\n");
443           }
444           break;
445         case CLEANFILE:
446           if (drivenum < numconf){
447             chg->conf[drivenum].cleanfile = stralloc(value);
448           } else {
449             fprintf(stderr,"Error: drive is not less than number_drives"\
450                     " cleanfile ignored\n");
451           }
452           break;
453         case USAGECOUNT:
454           if (drivenum < numconf){
455             chg->conf[drivenum].timefile = stralloc(value);
456           } else {
457             fprintf(stderr,"Error: drive is not less than number_drives"\
458                     " usagecount ignored\n");
459           }
460           break;
461         default:
462           fprintf(stderr,"Error: Unknown token\n");
463           break;
464         }
465       }
466     amfree(linebuffer);
467   }
468   amfree(linebuffer);
469
470   fclose(file);
471   return 0;
472 }
473
474 /*----------------------------------------------------------------------------*/
475
476 /*  The tape drive does not have an idea of current slot so
477  *  we use a file to store the current slot.  It is not ideal
478  *  but it gets the job done  
479  */
480 int get_current_slot(char *count_file)
481 {
482   FILE *inf;
483   int retval;
484   int ret;          /* return value for the fscanf function */
485   if ((inf=fopen(count_file,"r")) == NULL) {
486     fprintf(stderr, "%s: unable to open (%s)\n",
487             get_pname(), count_file);
488     exit(2);
489   }
490   ret = fscanf(inf,"%d",&retval);
491   fclose(inf);
492   
493   /*
494    * check if we got an result
495    * if no set retval to -1 
496   */
497   if (ret == 0 || ret == EOF)
498     {
499       retval = -1;
500     }
501
502   return retval;
503 }
504
505 void put_current_slot(char *count_file,int slot)
506 {
507   FILE *inf;
508
509   if ((inf=fopen(count_file,"w")) == NULL) {
510     fprintf(stderr, "%s: unable to open current slot file (%s)\n",
511             get_pname(), count_file);
512     exit(2);
513   }
514   fprintf(inf, "%d\n", slot);
515   fclose(inf);
516 }
517
518 /* 
519  * Here we handle the inventory DB
520  * With this it should be possible to do an mapping
521  * Barcode      -> Volume label
522  * Volume Label -> Barcode
523  * Volume label -> Slot number
524  * Return Values:
525  * 1 -> Action was ok
526  * 0 -> Action failed
527  *
528  * The passed struct MBC_T will hold the found entry in the DB
529  */
530
531 int MapBarCode(char *labelfile, MBC_T *result)
532 {
533   FILE *fp;
534   int version;
535   LabelV2_T *plabelv2;
536   int unusedpos = 0;
537   int unusedrec = 0;
538   int pos     = 0;
539   int record  = 0;
540   int volseen = 0;
541   int loop    = 1;
542   int rsize   = 0;
543
544   DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : Parameter\n");
545   DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"labelfile -> %s, vol -> %s, barcode -> %s, action -> %c, slot -> %d, from -> %d\n",
546              labelfile,
547              result->data.voltag,
548              result->data.barcode,
549              result->action,
550              result->data.slot,
551              result->data.from);
552   
553   if (labelfile == NULL)
554     {
555       DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"Got empty labelfile (NULL)\n");
556       ChgExit("MapBarCode", "MapBarCode name of labelfile is not set\n",FATAL);
557     }
558   if (access(labelfile, F_OK) == -1)
559     {
560       DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE, "MapBarCode : creating %s", labelfile);
561       if ((fp = fopen(labelfile, "w+")) == NULL)
562         {
563           DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE," failed\n");
564           ChgExit("MapBarCode", "MapBarCode, creating labelfile failed\n", FATAL);
565         }
566       fprintf(fp,":%d:", LABEL_DB_VERSION);
567       fclose(fp);
568     }
569   
570   if ((fp = fopen(labelfile, "r+")) == NULL)
571     {
572        DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"MapBarCode : failed to open %s\n", labelfile);
573        ChgExit("MapBarCode", "MapBarCode, opening labelfile for read/write failed\n", FATAL);
574     }
575   
576   fscanf(fp,":%d:", &version);
577   DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : DB version %d\n", version);
578   
579   pos = ftell(fp);
580   if (version != LABEL_DB_VERSION)
581     {
582       ChgExit("MapBarCode", "MapBarCode, DB Version does not match\n", FATAL);
583     }
584
585   if (( plabelv2 = (LabelV2_T *)malloc(sizeof(LabelV2_T))) == NULL)
586     {
587       DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"MapBarCode : malloc failed\n");
588       ChgExit("MapBarCode", "MapBarCode malloc failed\n", FATAL);
589     }
590   
591   memset(plabelv2, 0, sizeof(LabelV2_T));
592
593   while(feof(fp) == 0 && loop == 1)
594     {
595       rsize = fread(plabelv2, 1, sizeof(LabelV2_T), fp);
596       if (rsize == sizeof(LabelV2_T))
597       {
598       record++;
599       DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : (%d) VolTag \"%s\", BarCode %s, inuse %d, slot %d, from %d, loadcount %d\n",record,
600                  plabelv2->voltag,
601                  plabelv2->barcode,
602                  plabelv2->valid,
603                  plabelv2->slot,
604                  plabelv2->from,
605                  plabelv2->LoadCount);
606       switch (result->action)
607         {
608           /*
609            * Only dump the info
610            */ 
611         case BARCODE_DUMP:
612           printf("Slot -> %d, from -> %d, valid -> %d, Tag -> %s, Barcode -> %s, Loadcount %d\n",
613                  plabelv2->slot,
614                  plabelv2->from,
615                  plabelv2->valid,
616                  plabelv2->voltag,
617                  plabelv2->barcode,
618                  plabelv2->LoadCount
619                  );
620           break;
621           /*
622            * Set all the record to invalid, used by the Inventory function
623            */
624         case RESET_VALID:
625           fseek(fp, pos, SEEK_SET);
626           plabelv2->valid = 0;
627           fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
628           break;
629           /*
630            * Add an entry
631            */
632         case BARCODE_PUT:
633           /*
634            * If it is an invalid record we can use it,
635            * so save the record number.
636            * This value is used at the end if no other
637            * record/action matches.
638            */
639           if (plabelv2->valid == 0)
640             {
641                  unusedpos = pos;
642                  unusedrec = record;
643             }
644
645           /*
646            * Hmm whats that ?
647            */
648           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
649             {
650               volseen = record;
651             }
652           
653           /*
654            * OK this record matches the barcode label
655            * so use/update it
656            */
657           if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
658             {
659               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : update entry\n");
660               fseek(fp, pos, SEEK_SET);
661               plabelv2->valid = 1;
662               plabelv2->from = result->data.from;
663               plabelv2->slot = result->data.slot;
664               plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
665               strcpy(plabelv2->voltag, result->data.voltag);
666               strcpy(plabelv2->barcode, result->data.barcode);
667               fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
668               fclose(fp);
669               free(plabelv2);
670               return(1);
671             }
672           break;
673           /*
674            * Look for an entry an return the entry
675            * if the voltag (the tape name) matches
676            */
677         case FIND_SLOT:
678           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
679             {
680               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode FIND_SLOT : \n");
681               memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
682               free(plabelv2);
683               return(1);
684            }
685           break;
686           /*
687            * Update the entry,
688            * reason can be an load, incr the LoadCount
689            * or an new tape
690            */
691         case UPDATE_SLOT:
692           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
693             {
694               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode UPDATE_SLOT : update entry\n");
695               fseek(fp, pos, SEEK_SET);
696               strcpy(plabelv2->voltag, result->data.voltag);
697               strcpy(plabelv2->barcode, result->data.barcode);
698               plabelv2->valid = 1;
699               plabelv2->slot = result->data.slot;
700               plabelv2->from = result->data.from;
701               plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
702               fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
703               fclose(fp);
704               free(plabelv2);
705               return(1);
706             }
707           break;
708           /*
709            * Look for the barcode label of an given volume label
710            * return the slot number and the barcode label.
711            * If the entry is not valid return -1 as slot number
712            */
713         case BARCODE_VOL:
714           /*
715            * DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode: (%d) inside BARCODE_VOL\n", record);
716           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"file value: %s, searched for value: %s\n", plabelv2->voltag, result->data.voltag);
717           */
718           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
719             {
720               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : VOL %s match\n", result->data.voltag);
721               fclose(fp);
722               
723               memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
724               free(plabelv2);
725               return(1);
726             }
727           break;
728           /*
729            * Look for an entry which matches the passed
730            * barcode label
731            */
732         case BARCODE_BARCODE:
733           if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
734             {
735               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : BARCODE %s match\n", result->data.barcode);
736               fclose(fp);
737               
738               memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
739               free(plabelv2);
740               return(1);
741             }
742           break;
743
744         default:
745           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : unknown action\n");
746           break;
747         }
748       pos = ftell(fp);
749       } else {
750          DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : feof (%d)\n", feof(fp));
751          DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : error in read record expect %d, got %d\n",sizeof(LabelV2_T), rsize);
752         loop=0;
753       }
754     }
755
756   /*
757    * OK, if we come here and the action is either
758    * PUT or update it seems that we have to create a new
759    * record, becuae none of the exsisting records matches
760    */
761   if (result->action == BARCODE_PUT || result->action == UPDATE_SLOT )
762     {
763       /*
764        * If we have an entry where the valid flag was set to 0
765        * we can use this record, so seek to this position
766        * If we have no record for reuse the new record will be written to the end.
767        */
768       if (unusedpos != 0)
769         {
770           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : reuse record %d\n", unusedrec);
771           fseek(fp, unusedpos, SEEK_SET);
772         }
773       /*
774        * Set all values to zero
775        */
776       memset(plabelv2, 0, sizeof(LabelV2_T));     
777
778       strcpy(plabelv2->voltag, result->data.voltag);
779       strncpy(plabelv2->barcode, result->data.barcode, TAG_SIZE);
780       plabelv2->valid = 1;
781       plabelv2->from = result->data.from;
782       plabelv2->slot = result->data.slot;
783       fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
784       fclose(fp);
785       free(plabelv2);
786       return(1);
787     }
788
789   /*
790    * If we hit this point nothing was 
791    * found, so return an 0
792    */
793   fclose(fp);
794   free(plabelv2);
795   return(0);
796 }
797
798 /* ---------------------------------------------------------------------- 
799    This stuff deals with parsing the command line */
800
801 typedef struct com_arg
802 {
803   char *str;
804   int command_code;
805   int takesparam;
806 } argument;
807
808
809 typedef struct com_stru
810 {
811   int command_code;
812   char *parameter;
813 } command;
814
815
816 /* major command line args */
817 #define COMCOUNT 13
818 #define COM_SLOT 0
819 #define COM_INFO 1
820 #define COM_RESET 2
821 #define COM_EJECT 3
822 #define COM_CLEAN 4
823 #define COM_LABEL 5
824 #define COM_SEARCH 6
825 #define COM_STATUS 7
826 #define COM_TRACE 8
827 #define COM_INVENTORY 9
828 #define COM_DUMPDB 10
829 #define COM_SCAN 11
830 #define COM_GEN_CONF 12
831 argument argdefs[]={{"-slot",COM_SLOT,1},
832                     {"-info",COM_INFO,0},
833                     {"-reset",COM_RESET,0},
834                     {"-eject",COM_EJECT,0},
835                     {"-clean",COM_CLEAN,0},
836                     {"-label",COM_LABEL,1},
837                     {"-search",COM_SEARCH,1},
838                     {"-status",COM_STATUS,1},
839                     {"-trace",COM_TRACE,1},
840                     {"-inventory", COM_INVENTORY,0},
841                     {"-dumpdb", COM_DUMPDB,0},
842                     {"-scan", COM_SCAN,0},
843                     {"-genconf", COM_GEN_CONF,0}
844         };
845
846
847 /* minor command line args */
848 #define SLOT_CUR 0
849 #define SLOT_NEXT 1
850 #define SLOT_PREV 2
851 #define SLOT_FIRST 3
852 #define SLOT_LAST 4
853 #define SLOT_ADVANCE 5
854 argument slotdefs[]={{"current",SLOT_CUR,0},
855                      {"next",SLOT_NEXT,0},
856                      {"prev",SLOT_PREV,0},
857                      {"first",SLOT_FIRST,0},
858                      {"last",SLOT_LAST,0},
859                      {"advance",SLOT_ADVANCE,0},
860         };
861 #define SLOTCOUNT (sizeof(slotdefs) / sizeof(slotdefs[0]))
862
863 int is_positive_number(char *tmp) /* is the string a valid positive int? */
864 {
865   int i=0;
866   if ((tmp==NULL)||(tmp[0]==0))
867     return 0;
868   while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
869     i++;
870   if (tmp[i]==0)
871     return 1;
872   else
873     return 0;
874 }
875
876 void usage(char *argv[])
877 {
878   int cnt;
879   printf("%s: Usage error.\n", argv[0]);
880   for (cnt=0; cnt < COMCOUNT; cnt++){
881     printf("      %s    %s",argv[0],argdefs[cnt].str);
882     if (argdefs[cnt].takesparam)
883       printf(" <param>\n");
884     else
885       printf("\n");
886   }
887   exit(2);
888 }
889
890
891 void parse_args(int argc, char *argv[],command *rval)
892 {
893   int i=0;
894   for (i=0; i < argc; i++)
895     dbprintf(("ARG [%d] : %s\n", i, argv[i]));
896   i = 0;
897   if ((argc<2)||(argc>3))
898     usage(argv);
899   while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
900     i++;
901   if (i==COMCOUNT)
902     usage(argv);
903   rval->command_code = argdefs[i].command_code;
904   if (argdefs[i].takesparam) {
905     if (argc<3)
906       usage(argv);
907     rval->parameter=argv[2];      
908   }
909   else {
910     if (argc>2)
911       usage(argv);
912     rval->parameter=0;
913   }
914 }
915
916 /* used to find actual slot number from keywords next, prev, first, etc */
917 int get_relative_target(int fd,int nslots,char *parameter,int param_index,
918                         int loaded,char *slot_file,
919                         int slot_offset,int maxslot)
920 {
921   int current_slot;
922   
923   current_slot = get_current_slot(slot_file);
924
925   if (current_slot > maxslot){
926     current_slot = slot_offset;
927   }
928   if (current_slot < slot_offset){
929     current_slot = slot_offset;
930   }
931
932   switch(param_index) {
933   case SLOT_CUR:
934     return current_slot;
935     break;
936   case SLOT_NEXT:
937   case SLOT_ADVANCE:
938     if (++current_slot==nslots+slot_offset)
939       return slot_offset;
940     else
941       return current_slot;
942     break;
943   case SLOT_PREV:
944     if (--current_slot<slot_offset)
945       return maxslot;
946     else
947       return current_slot;
948     break;
949   case SLOT_FIRST:
950     return slot_offset;
951     break;
952   case SLOT_LAST:
953     return maxslot;
954     break;
955   default: 
956     printf("<none> no slot `%s'\n",parameter);
957     close(fd);
958     exit(2);
959     break;
960   };
961   return(-1); /* never executed */
962 }
963
964 int ask_clean(char *tapedev)
965      /* This function should ask the drive if it wants to be cleaned */
966 {
967   int ret;
968
969   ret = get_clean_state(tapedev);
970
971   if (ret < 0) /* < 0 means query does not work ... */
972   {
973     return(0);
974   }
975   return ret;
976 }
977
978 void clean_tape(int fd,char *tapedev,char *cnt_file, int drivenum, 
979                 int cleancart, int maxclean,char *usagetime)
980      /* This function should move the cleaning cartridge into the drive */
981 {
982   int counter=-1;
983   if (cleancart == -1 ){
984     return;
985   }
986   /* Now we should increment the counter */
987   if (cnt_file != NULL){
988     counter = get_current_slot(cnt_file);
989     counter++;
990     if (counter>=maxclean){
991       /* Now we should inform the administrator */
992       char *mail_cmd;
993       FILE *mailf;
994       mail_cmd = vstralloc(MAILER,
995                            " -s", " \"", "AMANDA PROBLEM: PLEASE FIX", "\"",
996                            " ", getconf_str(CNF_MAILTO),
997                            NULL);
998       if((mailf = popen(mail_cmd, "w")) == NULL){
999         error("could not open pipe to \"%s\": %s",
1000               mail_cmd, strerror(errno));
1001         printf("Mail failed\n");
1002         return;
1003       }
1004       fprintf(mailf,"\nThe usage count of your cleaning tape in slot %d",
1005               cleancart);
1006       fprintf(mailf,"\nis more than %d. (cleanmax)",maxclean);
1007       fprintf(mailf,"\nTapedrive %s needs to be cleaned",tapedev);
1008       fprintf(mailf,"\nPlease insert a new cleaning tape and reset");
1009       fprintf(mailf,"\nthe countingfile %s",cnt_file);
1010
1011       if(pclose(mailf) != 0)
1012         error("mail command failed: %s", mail_cmd);
1013
1014       return;
1015     }
1016     put_current_slot(cnt_file,counter);
1017   }
1018   load(fd,drivenum,cleancart);
1019   /*
1020    * Hack, sleep for some time
1021    */
1022
1023   sleep(60);
1024
1025   if (drive_loaded(fd, drivenum))
1026     unload(fd,drivenum,cleancart);  
1027   unlink(usagetime);
1028 }
1029 /* ----------------------------------------------------------------------*/
1030
1031 int main(int argc, char *argv[])
1032 {
1033   int loaded,target,oldtarget;
1034   command com;   /* a little DOS joke */
1035   int x;
1036
1037   MBC_T *pbarcoderes = malloc(sizeof(MBC_T));
1038   /*
1039    * drive_num really should be something from the config file, but..
1040    * for now, it is set to zero, since most of the common changers
1041    * used by amanda only have one drive ( until someone wants to 
1042    * use an EXB60/120, or a Breece Hill Q45.. )
1043    */
1044   unsigned char emubarcode = 0;
1045   int drive_num = 0;
1046   int need_eject = 0; /* Does the drive need an eject command ? */
1047   int need_sleep = 0; /* How many seconds to wait for the drive to get ready */
1048
1049   int maxclean = 0;
1050   char *clean_file=NULL;
1051   char *time_file=NULL;
1052
1053   /*
1054    * For the emubarcode stuff
1055    */
1056   int use_slots;
1057   int slot_offset;
1058   int confnum;
1059
1060   int fd, slotcnt, drivecnt;
1061   int endstatus = 0;
1062
1063   char *changer_dev    = NULL;
1064   char *tape_device    = NULL;
1065   char *chg_scsi_conf  = NULL;          /* The config file for us */
1066   char *slot_file      = NULL;          /* Where we will place the info which
1067                                          * slot is loaded
1068                                          */
1069   char *scsitapedevice = NULL;
1070
1071   int param_index = 0;
1072
1073   memset(pbarcoderes, 0 , sizeof(MBC_T));
1074   chg.number_of_configs = 0;
1075   chg.eject = 0;
1076   chg.sleep = 0;
1077   chg.cleanmax = 0;
1078   chg.device = NULL;
1079   chg.labelfile = NULL;
1080   chg.conf = NULL;
1081 #ifdef CHG_SCSI_STANDALONE
1082   printf("Ups standalone\n");
1083 #else
1084   set_pname("chg-scsi");
1085
1086   /* Don't die when child closes pipe */
1087   signal(SIGPIPE, SIG_IGN);
1088
1089   dbopen();
1090
1091   dbprintf(("chg-scsi: %s\n",rcsid));
1092   ChangerDriverVersion();
1093
1094   if (debug_file == NULL)
1095     {
1096         debug_file = dbfp();
1097     }
1098   
1099   parse_args(argc,argv,&com);
1100
1101   pDev = (OpenFiles_T *)malloc(sizeof(OpenFiles_T) * CHG_MAXDEV);
1102   memset(pDev, 0, sizeof(OpenFiles_T) * CHG_MAXDEV );
1103
1104
1105   switch(com.command_code) 
1106     {
1107     case COM_SCAN:
1108       ScanBus(1);
1109       return(0);
1110       break;
1111     case COM_GEN_CONF:
1112       ScanBus(0);
1113       PrintConf();
1114       return(0);
1115       break;
1116     default:
1117       break;
1118     }
1119
1120   if(read_conffile(CONFFILE_NAME)) {
1121     perror(CONFFILE_NAME);
1122     exit(1);
1123   }
1124
1125   chg_scsi_conf = getconf_str(CNF_CHNGRFILE);
1126   tape_device = getconf_str(CNF_TAPEDEV);
1127
1128   /* Get the configuration parameters */
1129   /* Attention, this will not support more than 10 tape devices 0-9 */
1130   /* */
1131   if (strlen(tape_device)==1){
1132     if (read_config(chg_scsi_conf,&chg) == -1)
1133     {
1134       fprintf(stderr, "%s open: of %s failed\n", get_pname(), chg_scsi_conf);
1135       return 2;
1136     }
1137     confnum=atoi(tape_device);
1138     if (chg.number_of_configs == 0)
1139     {
1140        fprintf(stderr,"%s: chg.conf[%d] == NULL\n",get_pname(), confnum);
1141        return(2);
1142     }
1143     use_slots    = chg.conf[confnum].end-chg.conf[confnum].start+1;
1144     slot_offset  = chg.conf[confnum].start;
1145     drive_num    = chg.conf[confnum].drivenum;
1146     need_eject   = chg.eject;
1147     need_sleep   = chg.sleep;
1148
1149     if ( NULL != chg.conf[confnum].cleanfile)
1150       clean_file   = stralloc(chg.conf[confnum].cleanfile);
1151     else
1152       clean_file = NULL;
1153
1154     clean_slot   = chg.conf[confnum].cleanslot;
1155     maxclean     = chg.cleanmax;
1156     emubarcode   = chg.emubarcode;
1157     if (NULL != chg.conf[confnum].timefile)
1158       time_file = stralloc(chg.conf[confnum].timefile);
1159
1160     if (NULL != chg.conf[confnum].slotfile)
1161       slot_file = stralloc(chg.conf[confnum].slotfile);
1162     else
1163       slot_file = NULL;
1164
1165     if (NULL != chg.conf[confnum].device)
1166       tape_device  = stralloc(chg.conf[confnum].device);
1167
1168     if (NULL != chg.device)
1169       changer_dev  = stralloc(chg.device); 
1170
1171     if (NULL != chg.conf[confnum].scsitapedev)
1172       scsitapedevice = stralloc(chg.conf[confnum].scsitapedev);
1173
1174     if (NULL != chg.conf[confnum].tapestatfile)
1175       tapestatfile = stralloc(chg.conf[confnum].tapestatfile);
1176     dump_changer_struct(chg);
1177
1178
1179
1180     /* 
1181      * The changer device.
1182      * If we can't open it fail with a message
1183      */
1184
1185     if (OpenDevice(INDEX_CHANGER , changer_dev, "changer_dev", chg.conf[confnum].changerident) == 0)
1186       {
1187         int localerr = errno;
1188         fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
1189                 changer_dev, strerror(localerr));
1190         printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
1191         dbprintf(("%s: open: %s: %s\n", get_pname(),
1192                   changer_dev, strerror(localerr)));
1193         return 2;
1194       }
1195
1196     fd = INDEX_CHANGER;
1197
1198     /*
1199      * The tape device.
1200      * We need it for:
1201      * eject if eject is set
1202      * inventory (reading of the labels) if emubarcode (not yet)
1203      */
1204     if (tape_device != NULL)
1205       {
1206         if (OpenDevice(INDEX_TAPE, tape_device, "tape_device", chg.conf[confnum].tapeident) == 0)
1207           {
1208             dbprintf(("warning open of %s: failed\n",  tape_device));
1209           }
1210       }
1211
1212     /*
1213      * This is for the status pages of the SCSI tape, nice to have but no must....
1214      */
1215     if (scsitapedevice != NULL)
1216       {
1217         if (OpenDevice(INDEX_TAPECTL, scsitapedevice, "scsitapedevice", chg.conf[confnum].tapeident) == 0)
1218           {
1219             dbprintf(("warning open of %s: failed\n", scsitapedevice));
1220           }
1221       }
1222     
1223
1224     /*
1225      * So if we need eject we need either an raw device to eject with an ioctl,
1226      * or an SCSI device to send the SCSI eject.
1227      */
1228
1229     if (need_eject != 0 )
1230       {
1231         if (pDev[INDEX_TAPE].avail == 0 && pDev[INDEX_TAPECTL].avail == 0)
1232           {
1233             printf("No device found for tape eject");
1234             return(2);
1235           }
1236       }
1237
1238         
1239     if ((chg.conf[confnum].end == -1) || (chg.conf[confnum].start == -1)){
1240       slotcnt = get_slot_count(fd);
1241       use_slots    = slotcnt;
1242       slot_offset  = 0;
1243     }
1244
1245     /*
1246      * Now check if we have all what we need
1247      * If either emubarcode is set or the changer support barcode
1248      * we need an label file
1249      */
1250     
1251     if ( chg.emubarcode == 1 || BarCode(INDEX_CHANGER) == 1) 
1252       {
1253         if (chg.labelfile == NULL)
1254           {
1255             printf("labelfile param not set in your config\n");
1256             return(2);
1257           }
1258       }
1259     
1260     if (slot_file == NULL)
1261       {
1262         printf("slotfile param. not set in your config\n");
1263         return(2);
1264       }
1265     
1266     if (access(slot_file,R_OK|W_OK) != 0)
1267       {
1268         printf("slotfile %s does not exsist or is not read/write\n", slot_file);
1269         return(2);
1270       }
1271
1272   } else { /* if (strlen(tape_device)==1) */
1273         printf("please check your config and use a config file for chg-scsi\n");
1274         return(2);
1275   }
1276
1277   drivecnt = get_drive_count(fd);
1278
1279   if (drive_num > drivecnt) {
1280     printf("%s drive number error (%d > %d)\n", "<none>", 
1281            drive_num, drivecnt);
1282     fprintf(stderr, "%s: requested drive number (%d) greater than "
1283             "number of supported drives (%d)\n", get_pname(), 
1284             drive_num, drivecnt);
1285     dbprintf(("%s: requested drive number (%d) greater than "
1286               "number of supported drives (%d)\n", get_pname(), 
1287               drive_num, drivecnt));
1288     return 2;
1289   }
1290
1291   loaded = drive_loaded(fd, drive_num);
1292   target = -1;
1293
1294   switch(com.command_code) {
1295 /* This is only for the experts ;-) */
1296   case COM_TRACE:
1297         ChangerReplay(com.parameter);
1298   break;
1299 /*
1300 */
1301   case COM_DUMPDB:
1302     pbarcoderes->action = BARCODE_DUMP;
1303     MapBarCode(chg.labelfile, pbarcoderes);
1304     break;
1305   case COM_STATUS:
1306     ChangerStatus(com.parameter, chg.labelfile, BarCode(fd),slot_file, changer_dev, tape_device);
1307     break;
1308   case COM_LABEL: /* Update BarCode/Label mapping file */
1309     pbarcoderes->action = BARCODE_PUT;
1310     strcpy(pbarcoderes->data.voltag, com.parameter);
1311     strcpy( pbarcoderes->data.barcode, pDTE[drive_num].VolTag);
1312     MapBarCode(chg.labelfile, pbarcoderes);
1313     printf("0 0 0\n");
1314     break;
1315
1316     /*
1317      * Inventory does an scan of the library and updates the mapping in the label DB
1318      */
1319   case COM_INVENTORY:
1320     do_inventory = 1;                                     /* Tell the label check not to exit on label errors */
1321     if (loaded)
1322       {
1323         oldtarget = get_current_slot(slot_file);
1324         if (oldtarget < 0)
1325           {
1326             dbprintf(("COM_INVENTORY: get_current_slot %d\n", oldtarget));
1327             oldtarget = find_empty(fd, slot_offset, use_slots);
1328             dbprintf(("COM_INVENTORY: find_empty %d\n", oldtarget));
1329           }
1330
1331         if (need_eject)
1332           {
1333             eject_tape(scsitapedevice, need_eject);
1334           } else {
1335             if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1336               {
1337                 LogSense(INDEX_TAPE);
1338               }
1339           }
1340
1341         (void)unload(fd, drive_num, oldtarget);
1342         if (ask_clean(scsitapedevice))
1343           clean_tape(fd,tape_device,clean_file,drive_num,
1344                      clean_slot,maxclean,time_file);
1345       }
1346     Inventory(chg.labelfile, drive_num, need_eject, 0, 0, clean_slot);
1347     do_inventory = 0;                        /* If set on exit the labeldb will be set to invalid ..... */
1348     break;
1349  
1350    /*
1351      * Search for the tape, the index is the volume label
1352      */
1353   case COM_SEARCH:
1354     
1355     /*
1356      * If we have an barcode reader use
1357      * this way
1358      */
1359     if (BarCode(fd) == 1 && emubarcode != 1)
1360       {
1361         dbprintf(("search : look for %s\n", com.parameter));
1362         pbarcoderes->action = BARCODE_VOL;
1363         pbarcoderes->data.slot = -1;
1364         strcpy(pbarcoderes->data.voltag, com.parameter);
1365         if (MapBarCode(chg.labelfile, pbarcoderes) == 1)
1366           {
1367             /*
1368              * If both values are unset we have an problem
1369              * so leave the program
1370              */
1371             if (pbarcoderes->data.slot == -1 && pbarcoderes->data.barcode == NULL)
1372               {
1373                 printf("Label %s not found (1)\n",com.parameter);
1374                 endstatus = 2;
1375                 close(fd);
1376                 break;
1377               }
1378             
1379
1380             /*
1381              * Let's see, if we got an barcode check if it is
1382              * in the current inventory
1383              */
1384             if (pbarcoderes->data.barcode != NULL)
1385               {
1386  
1387                 for (x = 0; x < STE; x++)
1388                   {
1389                     if (strcmp(pSTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1390                       {
1391                         dbprintf(("search : found slot %d\n", x));
1392                         target = x;
1393                       }
1394                   }
1395                 /*
1396                  * Not found in the STE slots
1397                  * my be in the DTE (tape)
1398                  * If we find it check if it is in the right drive
1399                  * if we have more than one drive.
1400                  */
1401                 for (x = 0; x < DTE; x++)
1402                   {
1403                     if (strcmp(pDTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1404                       {
1405                         dbprintf(("search : found in tape %d\n", x));
1406                         /*
1407                          */
1408                         if (x == drive_num) {
1409                           oldtarget = get_current_slot(slot_file);
1410                           printf("%d %s\n", oldtarget- slot_offset, tape_device);
1411                           return(0);
1412                         } else {
1413                           printf("LABEL in wrong tape Unit\n");
1414                           return(2);
1415                         }
1416                       }
1417                   }
1418                 /*
1419                  * not found, so do an exit...
1420                  */               
1421                 if (target == -1)
1422                   {
1423                     printf("Label %s not found (2) \n",com.parameter);
1424                     close(fd);
1425                     endstatus = 2;
1426                     break;
1427                   }
1428               }  /* if barcode[0] != 0 */
1429
1430             /*
1431              * If we didn't find anything we will try the info
1432              * from the DB. A reason for not finding anything in the inventory
1433              * might be an unreadable barcode label
1434              */
1435             if (target == -1 && pbarcoderes->data.slot != -1)
1436               {
1437                 target = pbarcoderes->data.slot;
1438               }
1439
1440             /*
1441              * OK, if target is still -1 do the exit
1442              */
1443             if (target == -1)
1444               {
1445                 printf("Label %s not found (3)\n",com.parameter);
1446                 close(fd);
1447                 endstatus = 2;
1448                 break;
1449               }
1450           }
1451                
1452       }
1453
1454     /*
1455      * And now if we have emubarcode set and no barcode reader
1456      * use this one
1457      */
1458     if (emubarcode == 1 && BarCode(fd) != 1)
1459       {
1460         dbprintf(("search : look for %s\n", com.parameter));
1461         pbarcoderes->action = FIND_SLOT;
1462         pbarcoderes->data.slot = -1;
1463         strcpy(pbarcoderes->data.voltag, com.parameter);
1464
1465         if (MapBarCode(chg.labelfile, pbarcoderes) == 1)
1466           {
1467             if (pbarcoderes->data.valid == 1)
1468               {
1469                 target = pbarcoderes->data.slot;
1470               } else {
1471                 printf("Barcode DB out of sync \n");
1472                 close(fd);
1473                 endstatus=2;
1474                 break;
1475               }
1476           } else {
1477             printf("Label %s not found \n",com.parameter);
1478             close(fd);
1479             endstatus = 2;
1480             break;
1481           }
1482       } 
1483
1484     /*
1485      * The slot changing command
1486      */
1487   case COM_SLOT: 
1488     if (target == -1)
1489       {
1490         if (is_positive_number(com.parameter)) {
1491           if ((target = atoi(com.parameter))>=use_slots) {
1492             printf("<none> no slot `%d'\n",target);
1493             close(fd);
1494             endstatus = 2;
1495             break;
1496           } else {
1497             target = target+slot_offset;
1498           }
1499         } else {
1500           param_index=0;
1501           while((param_index<SLOTCOUNT)
1502                 &&(strcmp(slotdefs[param_index].str,com.parameter))) {
1503             param_index++;
1504           }
1505           target=get_relative_target(fd, use_slots,
1506                                      com.parameter,param_index,
1507                                      loaded, 
1508                                      slot_file,
1509                                      slot_offset,slot_offset+use_slots-1);
1510         }
1511       }
1512
1513     if (loaded) {
1514       oldtarget = get_current_slot(slot_file);
1515       if (oldtarget < 0)
1516         {
1517           dbprintf(("COM_SLOT: get_current_slot %d\n", oldtarget));
1518           oldtarget = find_empty(fd, slot_offset, use_slots);
1519           dbprintf(("COM_SLOT: find_empty %d\n", oldtarget));
1520         }
1521       
1522       /*
1523        * TODO check if the request slot for the unload is empty
1524        */
1525
1526        
1527       if ((oldtarget)!=target) {
1528         if (need_eject)
1529           {
1530             eject_tape(scsitapedevice, need_eject);
1531           } else {
1532             /*
1533              * If we have an SCSI path to the tape and an raw io path
1534              * try to read the Error Counter and the label
1535              */
1536             if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1537               {
1538                 LogSense(INDEX_TAPE);
1539               }
1540           }
1541
1542         (void)unload(fd, drive_num, oldtarget);
1543         if (ask_clean(scsitapedevice))
1544           clean_tape(fd,tape_device,clean_file,drive_num,
1545                      clean_slot,maxclean,time_file);
1546         loaded=0;
1547       }
1548     }
1549     
1550     put_current_slot(slot_file, target);
1551     
1552     if (!loaded && isempty(fd, target)) {
1553       printf("%d slot %d is empty\n",target-slot_offset,
1554              target-slot_offset);
1555       close(fd);
1556       endstatus = 1;
1557       break;
1558     }
1559
1560     if (!loaded && param_index != SLOT_ADVANCE)
1561       {
1562         if (ask_clean(scsitapedevice))
1563           clean_tape(fd,tape_device,clean_file,drive_num,
1564                      clean_slot,maxclean,time_file);
1565         if (load(fd, drive_num, target) != 0) {
1566           printf("%d slot %d move failed\n",target-slot_offset,
1567                  target-slot_offset);  
1568           close(fd);
1569           endstatus = 2;
1570           break;
1571         }
1572       }
1573
1574     if (need_sleep)
1575       {
1576         if (pDev[INDEX_TAPECTL].inqdone == 1)
1577           {
1578             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1579               {
1580                 printf("tape not ready\n");
1581                 endstatus = 2;
1582                 break;
1583               }
1584           } else {
1585             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1586               {
1587                 printf("tape not ready\n");
1588                 endstatus = 2;
1589                 break;
1590               }          
1591         }
1592       }
1593
1594     printf("%d %s\n", target-slot_offset, tape_device);
1595     break;
1596
1597   case COM_INFO:
1598     loaded = get_current_slot(slot_file);
1599
1600     if (loaded < 0)
1601       {
1602         loaded = find_empty(fd, slot_offset, use_slots);
1603       }
1604     loaded = loaded - slot_offset;
1605       
1606     printf("%d %d 1", loaded, use_slots);
1607
1608     if (BarCode(fd) == 1 || emubarcode == 1)
1609       {
1610         printf(" 1\n");
1611       } else {
1612         printf(" 0\n");
1613       }
1614     break;
1615
1616   case COM_RESET:
1617     target=get_current_slot(slot_file);
1618
1619     if (target < 0)
1620     {
1621       dbprintf(("COM_RESET: get_current_slot %d\n", target));
1622       target=find_empty(fd, slot_offset, use_slots);
1623       dbprintf(("COM_RESET: find_empty %d\n", target));
1624     }
1625
1626     if (loaded) {
1627       
1628       if (!isempty(fd, target))
1629         target=find_empty(fd, slot_offset, use_slots);
1630       
1631       if (need_eject)
1632         {
1633           eject_tape(scsitapedevice, need_eject);
1634         } else {
1635           /*
1636            * If we have an SCSI path to the tape and an raw io path
1637            * try to read the Error Counter and the label
1638            */
1639           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1640             {
1641               LogSense(INDEX_TAPE);
1642             }
1643         }
1644       
1645       (void)unload(fd, drive_num, target);
1646       if (ask_clean(scsitapedevice))
1647         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1648                    maxclean,time_file);
1649     }
1650     
1651     if (isempty(fd, slot_offset)) {
1652       printf("0 slot 0 is empty\n");
1653       close(fd);
1654       endstatus = 1;
1655       break;
1656     }
1657     
1658     if (load(fd, drive_num, slot_offset) != 0) {
1659       printf("%d slot %d move failed\n",drive_num,
1660              slot_offset);  
1661       close(fd);
1662       put_current_slot(slot_file, slot_offset);
1663       endstatus = 2;
1664       break;
1665     }
1666     
1667     put_current_slot(slot_file, slot_offset);
1668     
1669     if (need_sleep)
1670       {
1671         if (pDev[INDEX_TAPECTL].inqdone == 1)
1672           {
1673             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1674               {
1675                 printf("tape not ready\n");
1676                 endstatus = 2;
1677                 break;
1678               }
1679           } else {
1680             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1681               {
1682                 printf("tape not ready\n");
1683                 endstatus = 2;
1684                 break;
1685               }          
1686           }
1687       }
1688     
1689     printf("%d %s\n", slot_offset, tape_device);
1690     break;
1691
1692   case COM_EJECT:
1693     if (loaded) {
1694       target=get_current_slot(slot_file);
1695       if (target < 0)
1696         {
1697           dbprintf(("COM_EJECT: get_current_slot %d\n", target));
1698           target = find_empty(fd, slot_offset, use_slots);
1699           dbprintf(("COM_EJECT: find_empty %d\n", target));
1700         }
1701       
1702       if (need_eject)
1703         {
1704           eject_tape(scsitapedevice, need_eject);
1705         } else {
1706           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1707             {
1708               LogSense(INDEX_TAPE);
1709             }
1710         }
1711
1712
1713       (void)unload(fd, drive_num, target);
1714       if (ask_clean(scsitapedevice))
1715         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1716                    maxclean,time_file);
1717       printf("%d %s\n", target, tape_device);
1718     } else {
1719       printf("%d %s\n", target, "drive was not loaded");
1720       endstatus = 1;
1721     }
1722     break;
1723   case COM_CLEAN:
1724     if (loaded) {
1725       target=get_current_slot(slot_file);
1726       if (target < 0)
1727         {
1728           dbprintf(("COM_CLEAN: get_current_slot %d\n", target));
1729           target = find_empty(fd, slot_offset, use_slots);
1730           dbprintf(("COM_CLEAN: find_empty %d\n",target));
1731         }
1732
1733       if (need_eject)
1734         {
1735           eject_tape(scsitapedevice, need_eject);
1736         } else {
1737           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1738             {
1739               LogSense(INDEX_TAPE);
1740             }
1741         }
1742
1743       (void)unload(fd, drive_num, target);
1744     } 
1745
1746     clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1747                maxclean,time_file);
1748     printf("%s cleaned\n", tape_device);
1749     break;
1750   };
1751
1752   /* FIX ME, should be an function to close the device */  
1753 /*   if (pChangerDev != NULL) */
1754 /*     close(pChangerDev->fd); */
1755  
1756 /*   if (pTapeDev != NULL) */
1757 /*     close(pTapeDev->fd); */
1758
1759 /*   if (pTapeDevCtl != NULL) */
1760 /*     close(pTapeDevCtl->fd); */
1761
1762
1763 #endif
1764   if (do_inventory == 1 && endstatus == 0 && chg.labelfile != NULL)
1765     {
1766       if ( chg.autoinv == 1)
1767         {
1768           DebugPrint(DEBUG_INFO,SECTION_INFO, "Do an inventory \n");
1769           Inventory(chg.labelfile, drive_num , chg.eject, 0, 0, clean_slot);
1770         } else {
1771           DebugPrint(DEBUG_INFO,SECTION_INFO, "Set all entrys in DB to invalid\n");
1772           memset(pbarcoderes, 0 , sizeof(MBC_T));
1773           pbarcoderes->action = RESET_VALID;
1774           MapBarCode(chg.labelfile,pbarcoderes);
1775         }
1776     }
1777
1778   DebugPrint(DEBUG_INFO,SECTION_INFO,"Exit status -> %d\n", endstatus);
1779   dbclose();
1780   return endstatus;
1781 }
1782 /*
1783  * Local variables:
1784  * indent-tabs-mode: nil
1785  * tab-width: 4
1786  * End:
1787  */
1788