Imported Debian patch 2.4.5p1-2
[debian/amanda] / changer-src / chg-scsi.c
1 #ifndef lint
2 static char rcsid[] = "$Id: chg-scsi.c,v 1.6.2.22.2.7.2.9 2003/07/05 16:59:01 ant 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               return(1);
670             }
671           break;
672           /*
673            * Look for an entry an return the entry
674            * if the voltag (the tape name) matches
675            */
676         case FIND_SLOT:
677           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
678             {
679               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode FIND_SLOT : \n");
680               memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
681               return(1);
682            }
683           break;
684           /*
685            * Update the entry,
686            * reason can be an load, incr the LoadCount
687            * or an new tape
688            */
689         case UPDATE_SLOT:
690           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
691             {
692               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode UPDATE_SLOT : update entry\n");
693               fseek(fp, pos, SEEK_SET);
694               strcpy(plabelv2->voltag, result->data.voltag);
695               strcpy(plabelv2->barcode, result->data.barcode);
696               plabelv2->valid = 1;
697               plabelv2->slot = result->data.slot;
698               plabelv2->from = result->data.from;
699               plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
700               fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
701               fclose(fp);
702               return(1);
703             }
704           break;
705           /*
706            * Look for the barcode label of an given volume label
707            * return the slot number and the barcode label.
708            * If the entry is not valid return -1 as slot number
709            */
710         case BARCODE_VOL:
711           /*
712            * DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode: (%d) inside BARCODE_VOL\n", record);
713           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"file value: %s, searched for value: %s\n", plabelv2->voltag, result->data.voltag);
714           */
715           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
716             {
717               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : VOL %s match\n", result->data.voltag);
718               fclose(fp);
719               
720               memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
721               return(1);
722             }
723           break;
724           /*
725            * Look for an entry which matches the passed
726            * barcode label
727            */
728         case BARCODE_BARCODE:
729           if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
730             {
731               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : BARCODE %s match\n", result->data.barcode);
732               fclose(fp);
733               
734               memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
735               return(1);
736             }
737           break;
738
739         default:
740           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : unknown action\n");
741           break;
742         }
743       pos = ftell(fp);
744       } else {
745          DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : feof (%d)\n", feof(fp));
746          DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : error in read record expect %d, got %d\n",sizeof(LabelV2_T), rsize);
747         loop=0;
748       }
749     }
750
751   /*
752    * OK, if we come here and the action is either
753    * PUT or update it seems that we have to create a new
754    * record, becuae none of the exsisting records matches
755    */
756   if (result->action == BARCODE_PUT || result->action == UPDATE_SLOT )
757     {
758       /*
759        * If we have an entry where the valid flag was set to 0
760        * we can use this record, so seek to this position
761        * If we have no record for reuse the new record will be written to the end.
762        */
763       if (unusedpos != 0)
764         {
765           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : reuse record %d\n", unusedrec);
766           fseek(fp, unusedpos, SEEK_SET);
767         }
768       /*
769        * Set all values to zero
770        */
771       memset(plabelv2, 0, sizeof(LabelV2_T));     
772
773       strcpy(plabelv2->voltag, result->data.voltag);
774       strncpy(plabelv2->barcode, result->data.barcode, TAG_SIZE);
775       plabelv2->valid = 1;
776       plabelv2->from = result->data.from;
777       plabelv2->slot = result->data.slot;
778       fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
779       fclose(fp);
780       return(1);
781     }                                                                           
782
783   /*
784    * If we hit this point nothing was 
785    * found, so return an 0
786    */
787   fclose(fp);
788   return(0);
789 }
790
791 /* ---------------------------------------------------------------------- 
792    This stuff deals with parsing the command line */
793
794 typedef struct com_arg
795 {
796   char *str;
797   int command_code;
798   int takesparam;
799 } argument;
800
801
802 typedef struct com_stru
803 {
804   int command_code;
805   char *parameter;
806 } command;
807
808
809 /* major command line args */
810 #define COMCOUNT 13
811 #define COM_SLOT 0
812 #define COM_INFO 1
813 #define COM_RESET 2
814 #define COM_EJECT 3
815 #define COM_CLEAN 4
816 #define COM_LABEL 5
817 #define COM_SEARCH 6
818 #define COM_STATUS 7
819 #define COM_TRACE 8
820 #define COM_INVENTORY 9
821 #define COM_DUMPDB 10
822 #define COM_SCAN 11
823 #define COM_GEN_CONF 12
824 argument argdefs[]={{"-slot",COM_SLOT,1},
825                     {"-info",COM_INFO,0},
826                     {"-reset",COM_RESET,0},
827                     {"-eject",COM_EJECT,0},
828                     {"-clean",COM_CLEAN,0},
829                     {"-label",COM_LABEL,1},
830                     {"-search",COM_SEARCH,1},
831                     {"-status",COM_STATUS,1},
832                     {"-trace",COM_TRACE,1},
833                     {"-inventory", COM_INVENTORY,0},
834                     {"-dumpdb", COM_DUMPDB,0},
835                     {"-scan", COM_SCAN,0},
836                     {"-genconf", COM_GEN_CONF,0}
837         };
838
839
840 /* minor command line args */
841 #define SLOT_CUR 0
842 #define SLOT_NEXT 1
843 #define SLOT_PREV 2
844 #define SLOT_FIRST 3
845 #define SLOT_LAST 4
846 #define SLOT_ADVANCE 5
847 argument slotdefs[]={{"current",SLOT_CUR,0},
848                      {"next",SLOT_NEXT,0},
849                      {"prev",SLOT_PREV,0},
850                      {"first",SLOT_FIRST,0},
851                      {"last",SLOT_LAST,0},
852                      {"advance",SLOT_ADVANCE,0},
853         };
854 #define SLOTCOUNT (sizeof(slotdefs) / sizeof(slotdefs[0]))
855
856 int is_positive_number(char *tmp) /* is the string a valid positive int? */
857 {
858   int i=0;
859   if ((tmp==NULL)||(tmp[0]==0))
860     return 0;
861   while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
862     i++;
863   if (tmp[i]==0)
864     return 1;
865   else
866     return 0;
867 }
868
869 void usage(char *argv[])
870 {
871   int cnt;
872   printf("%s: Usage error.\n", argv[0]);
873   for (cnt=0; cnt < COMCOUNT; cnt++){
874     printf("      %s    %s",argv[0],argdefs[cnt].str);
875     if (argdefs[cnt].takesparam)
876       printf(" <param>\n");
877     else
878       printf("\n");
879   }
880   exit(2);
881 }
882
883
884 void parse_args(int argc, char *argv[],command *rval)
885 {
886   int i=0;
887   for (i=0; i < argc; i++)
888     dbprintf(("ARG [%d] : %s\n", i, argv[i]));
889   i = 0;
890   if ((argc<2)||(argc>3))
891     usage(argv);
892   while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
893     i++;
894   if (i==COMCOUNT)
895     usage(argv);
896   rval->command_code = argdefs[i].command_code;
897   if (argdefs[i].takesparam) {
898     if (argc<3)
899       usage(argv);
900     rval->parameter=argv[2];      
901   }
902   else {
903     if (argc>2)
904       usage(argv);
905     rval->parameter=0;
906   }
907 }
908
909 /* used to find actual slot number from keywords next, prev, first, etc */
910 int get_relative_target(int fd,int nslots,char *parameter,int param_index,
911                         int loaded,char *slot_file,
912                         int slot_offset,int maxslot)
913 {
914   int current_slot;
915   
916   current_slot = get_current_slot(slot_file);
917
918   if (current_slot > maxslot){
919     current_slot = slot_offset;
920   }
921   if (current_slot < slot_offset){
922     current_slot = slot_offset;
923   }
924
925   switch(param_index) {
926   case SLOT_CUR:
927     return current_slot;
928     break;
929   case SLOT_NEXT:
930   case SLOT_ADVANCE:
931     if (++current_slot==nslots+slot_offset)
932       return slot_offset;
933     else
934       return current_slot;
935     break;
936   case SLOT_PREV:
937     if (--current_slot<slot_offset)
938       return maxslot;
939     else
940       return current_slot;
941     break;
942   case SLOT_FIRST:
943     return slot_offset;
944     break;
945   case SLOT_LAST:
946     return maxslot;
947     break;
948   default: 
949     printf("<none> no slot `%s'\n",parameter);
950     close(fd);
951     exit(2);
952     break;
953   };
954   return(-1); /* never executed */
955 }
956
957 int ask_clean(char *tapedev)
958      /* This function should ask the drive if it wants to be cleaned */
959 {
960   int ret;
961
962   ret = get_clean_state(tapedev);
963
964   if (ret < 0) /* < 0 means query does not work ... */
965   {
966     return(0);
967   }
968   return ret;
969 }
970
971 void clean_tape(int fd,char *tapedev,char *cnt_file, int drivenum, 
972                 int cleancart, int maxclean,char *usagetime)
973      /* This function should move the cleaning cartridge into the drive */
974 {
975   int counter=-1;
976   if (cleancart == -1 ){
977     return;
978   }
979   /* Now we should increment the counter */
980   if (cnt_file != NULL){
981     counter = get_current_slot(cnt_file);
982     counter++;
983     if (counter>=maxclean){
984       /* Now we should inform the administrator */
985       char *mail_cmd;
986       FILE *mailf;
987       mail_cmd = vstralloc(MAILER,
988                            " -s", " \"", "AMANDA PROBLEM: PLEASE FIX", "\"",
989                            " ", getconf_str(CNF_MAILTO),
990                            NULL);
991       if((mailf = popen(mail_cmd, "w")) == NULL){
992         error("could not open pipe to \"%s\": %s",
993               mail_cmd, strerror(errno));
994         printf("Mail failed\n");
995         return;
996       }
997       fprintf(mailf,"\nThe usage count of your cleaning tape in slot %d",
998               cleancart);
999       fprintf(mailf,"\nis more than %d. (cleanmax)",maxclean);
1000       fprintf(mailf,"\nTapedrive %s needs to be cleaned",tapedev);
1001       fprintf(mailf,"\nPlease insert a new cleaning tape and reset");
1002       fprintf(mailf,"\nthe countingfile %s",cnt_file);
1003
1004       if(pclose(mailf) != 0)
1005         error("mail command failed: %s", mail_cmd);
1006
1007       return;
1008     }
1009     put_current_slot(cnt_file,counter);
1010   }
1011   load(fd,drivenum,cleancart);
1012   /*
1013    * Hack, sleep for some time
1014    */
1015
1016   sleep(60);
1017
1018   if (drive_loaded(fd, drivenum))
1019     unload(fd,drivenum,cleancart);  
1020   unlink(usagetime);
1021 }
1022 /* ----------------------------------------------------------------------*/
1023
1024 int main(int argc, char *argv[])
1025 {
1026   int loaded,target,oldtarget;
1027   command com;   /* a little DOS joke */
1028   int x;
1029
1030   MBC_T *pbarcoderes = malloc(sizeof(MBC_T));
1031   /*
1032    * drive_num really should be something from the config file, but..
1033    * for now, it is set to zero, since most of the common changers
1034    * used by amanda only have one drive ( until someone wants to 
1035    * use an EXB60/120, or a Breece Hill Q45.. )
1036    */
1037   unsigned char emubarcode = 0;
1038   int drive_num = 0;
1039   int need_eject = 0; /* Does the drive need an eject command ? */
1040   int need_sleep = 0; /* How many seconds to wait for the drive to get ready */
1041
1042   int maxclean = 0;
1043   char *clean_file=NULL;
1044   char *time_file=NULL;
1045
1046   char *ptr;         /* a public pointer .... */
1047   /*
1048    * For the emubarcode stuff
1049    */
1050   int use_slots;
1051   int slot_offset;
1052   int confnum;
1053
1054   int fd, slotcnt, drivecnt;
1055   int endstatus = 0;
1056
1057   char *changer_dev    = NULL;
1058   char *tape_device    = NULL;
1059   char *chg_scsi_conf  = NULL;          /* The config file for us */
1060   char *slot_file      = NULL;          /* Where we will place the info which
1061                                          * slot is loaded
1062                                          */
1063   char *scsitapedevice = NULL;
1064
1065   int param_index = 0;
1066
1067   memset(pbarcoderes, 0 , sizeof(MBC_T));
1068   chg.number_of_configs = 0;
1069   chg.eject = 0;
1070   chg.sleep = 0;
1071   chg.cleanmax = 0;
1072   chg.device = NULL;
1073   chg.labelfile = NULL;
1074   chg.conf = NULL;
1075   chg.debuglevel = NULL;
1076 #ifdef CHG_SCSI_STANDALONE
1077   printf("Ups standalone\n");
1078 #else
1079   set_pname("chg-scsi");
1080   dbopen();
1081
1082   dbprintf(("chg-scsi: %s\n",rcsid));
1083   ChangerDriverVersion();
1084
1085   if (debug_file == NULL)
1086     {
1087         debug_file = dbfp();
1088     }
1089   
1090   parse_args(argc,argv,&com);
1091
1092   pDev = (OpenFiles_T *)malloc(sizeof(OpenFiles_T) * CHG_MAXDEV);
1093   memset(pDev, 0, sizeof(OpenFiles_T) * CHG_MAXDEV );
1094
1095
1096   if ((ptr=getenv("CHG_DEBUG")) != NULL)
1097     {
1098       chg.debuglevel = strdup(ptr);
1099     }
1100
1101   switch(com.command_code) 
1102     {
1103     case COM_SCAN:
1104       ScanBus(1);
1105       return(0);
1106       break;
1107     case COM_GEN_CONF:
1108       ScanBus(0);
1109       PrintConf();
1110       return(0);
1111       break;
1112     default:
1113       break;
1114     }
1115
1116   if(read_conffile(CONFFILE_NAME)) {
1117     perror(CONFFILE_NAME);
1118     exit(1);
1119   }
1120
1121   chg_scsi_conf = getconf_str(CNF_CHNGRFILE);
1122   tape_device = getconf_str(CNF_TAPEDEV);
1123
1124   /* Get the configuration parameters */
1125   /* Attention, this will not support more than 10 tape devices 0-9 */
1126   /* */
1127   if (strlen(tape_device)==1){
1128     if (read_config(chg_scsi_conf,&chg) == -1)
1129     {
1130       fprintf(stderr, "%s open: of %s failed\n", get_pname(), chg_scsi_conf);
1131       return 2;
1132     }
1133     confnum=atoi(tape_device);
1134     if (chg.number_of_configs == 0)
1135     {
1136        fprintf(stderr,"%s: chg.conf[%d] == NULL\n",get_pname(), confnum);
1137        return(2);
1138     }
1139     use_slots    = chg.conf[confnum].end-chg.conf[confnum].start+1;
1140     slot_offset  = chg.conf[confnum].start;
1141     drive_num    = chg.conf[confnum].drivenum;
1142     need_eject   = chg.eject;
1143     need_sleep   = chg.sleep;
1144
1145     if ( NULL != chg.conf[confnum].cleanfile)
1146       clean_file   = stralloc(chg.conf[confnum].cleanfile);
1147     else
1148       clean_file = NULL;
1149
1150     clean_slot   = chg.conf[confnum].cleanslot;
1151     maxclean     = chg.cleanmax;
1152     emubarcode   = chg.emubarcode;
1153     if (NULL != chg.conf[confnum].timefile)
1154       time_file = stralloc(chg.conf[confnum].timefile);
1155
1156     if (NULL != chg.conf[confnum].slotfile)
1157       slot_file = stralloc(chg.conf[confnum].slotfile);
1158     else
1159       slot_file = NULL;
1160
1161     if (NULL != chg.conf[confnum].device)
1162       tape_device  = stralloc(chg.conf[confnum].device);
1163
1164     if (NULL != chg.device)
1165       changer_dev  = stralloc(chg.device); 
1166
1167     if (NULL != chg.conf[confnum].scsitapedev)
1168       scsitapedevice = stralloc(chg.conf[confnum].scsitapedev);
1169
1170     if (NULL != chg.conf[confnum].tapestatfile)
1171       tapestatfile = stralloc(chg.conf[confnum].tapestatfile);
1172     dump_changer_struct(chg);
1173
1174
1175
1176     /* 
1177      * The changer device.
1178      * If we can't open it fail with a message
1179      */
1180
1181     if (OpenDevice(INDEX_CHANGER , changer_dev, "changer_dev", chg.conf[confnum].changerident) == 0)
1182       {
1183         int localerr = errno;
1184         fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
1185                 changer_dev, strerror(localerr));
1186         printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
1187         dbprintf(("%s: open: %s: %s\n", get_pname(),
1188                   changer_dev, strerror(localerr)));
1189         return 2;
1190       }
1191
1192     fd = INDEX_CHANGER;
1193
1194     /*
1195      * The tape device.
1196      * We need it for:
1197      * eject if eject is set
1198      * inventory (reading of the labels) if emubarcode (not yet)
1199      */
1200     if (tape_device != NULL)
1201       {
1202         if (OpenDevice(INDEX_TAPE, tape_device, "tape_device", chg.conf[confnum].tapeident) == 0)
1203           {
1204             dbprintf(("warning open of %s: failed\n",  tape_device));
1205           }
1206       }
1207
1208     /*
1209      * This is for the status pages of the SCSI tape, nice to have but no must....
1210      */
1211     if (scsitapedevice != NULL)
1212       {
1213         if (OpenDevice(INDEX_TAPECTL, scsitapedevice, "scsitapedevice", chg.conf[confnum].tapeident) == 0)
1214           {
1215             dbprintf(("warning open of %s: failed\n", scsitapedevice));
1216           }
1217       }
1218     
1219
1220     /*
1221      * So if we need eject we need either an raw device to eject with an ioctl,
1222      * or an SCSI device to send the SCSI eject.
1223      */
1224
1225     if (need_eject != 0 )
1226       {
1227         if (pDev[INDEX_TAPE].avail == 0 && pDev[INDEX_TAPECTL].avail == 0)
1228           {
1229             printf("No device found for tape eject");
1230             return(2);
1231           }
1232       }
1233
1234         
1235     if ((chg.conf[confnum].end == -1) || (chg.conf[confnum].start == -1)){
1236       slotcnt = get_slot_count(fd);
1237       use_slots    = slotcnt;
1238       slot_offset  = 0;
1239     }
1240
1241     /*
1242      * Now check if we have all what we need
1243      * If either emubarcode is set or the changer support barcode
1244      * we need an label file
1245      */
1246     
1247     if ( chg.emubarcode == 1 || BarCode(INDEX_CHANGER) == 1) 
1248       {
1249         if (chg.labelfile == NULL)
1250           {
1251             printf("labelfile param not set in your config\n");
1252             return(2);
1253           }
1254       }
1255     
1256     if (slot_file == NULL)
1257       {
1258         printf("slotfile param. not set in your config\n");
1259         return(2);
1260       }
1261     
1262     if (access(slot_file,R_OK|W_OK) != 0)
1263       {
1264         printf("slotfile %s does not exsist or is not read/write\n", slot_file);
1265         return(2);
1266       }
1267
1268   } else { /* if (strlen(tape_device)==1) */
1269         printf("please check your config and use a config file for chg-scsi\n");
1270         return(2);
1271   }
1272
1273   drivecnt = get_drive_count(fd);
1274
1275   if (drive_num > drivecnt) {
1276     printf("%s drive number error (%d > %d)\n", "<none>", 
1277            drive_num, drivecnt);
1278     fprintf(stderr, "%s: requested drive number (%d) greater than "
1279             "number of supported drives (%d)\n", get_pname(), 
1280             drive_num, drivecnt);
1281     dbprintf(("%s: requested drive number (%d) greater than "
1282               "number of supported drives (%d)\n", get_pname(), 
1283               drive_num, drivecnt));
1284     return 2;
1285   }
1286
1287   loaded = drive_loaded(fd, drive_num);
1288   target = -1;
1289
1290   switch(com.command_code) {
1291 /* This is only for the experts ;-) */
1292   case COM_TRACE:
1293         ChangerReplay(com.parameter);
1294   break;
1295 /*
1296 */
1297   case COM_DUMPDB:
1298     pbarcoderes->action = BARCODE_DUMP;
1299     MapBarCode(chg.labelfile, pbarcoderes);
1300     break;
1301   case COM_STATUS:
1302     ChangerStatus(com.parameter, chg.labelfile, BarCode(fd),slot_file, changer_dev, tape_device);
1303     break;
1304   case COM_LABEL: /* Update BarCode/Label mapping file */
1305     pbarcoderes->action = BARCODE_PUT;
1306     pbarcoderes->data.from = pbarcoderes->data.slot = get_current_slot(slot_file);
1307     strcpy(pbarcoderes->data.voltag, com.parameter);
1308     if (BarCode(fd) == 1 && emubarcode != 1)
1309     {
1310        strcpy( pbarcoderes->data.barcode, pDTE[drive_num].VolTag);
1311     } else {
1312        strcpy( pbarcoderes->data.barcode, com.parameter);
1313     }
1314     MapBarCode(chg.labelfile, pbarcoderes);
1315     printf("0 0 0\n");
1316     break;
1317
1318     /*
1319      * Inventory does an scan of the library and updates the mapping in the label DB
1320      */
1321   case COM_INVENTORY:
1322     do_inventory = 1;                                     /* Tell the label check not to exit on label errors */
1323     if (loaded)
1324       {
1325         oldtarget = get_current_slot(slot_file);
1326         if (oldtarget < 0)
1327           {
1328             dbprintf(("COM_INVENTORY: get_current_slot %d\n", oldtarget));
1329             oldtarget = find_empty(fd, slot_offset, use_slots);
1330             dbprintf(("COM_INVENTORY: find_empty %d\n", oldtarget));
1331           }
1332
1333         if (need_eject)
1334           {
1335             eject_tape(scsitapedevice, need_eject);
1336           } else {
1337             if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1338               {
1339                 LogSense(INDEX_TAPE);
1340               }
1341           }
1342
1343         (void)unload(fd, drive_num, oldtarget);
1344         if (ask_clean(scsitapedevice))
1345           clean_tape(fd,tape_device,clean_file,drive_num,
1346                      clean_slot,maxclean,time_file);
1347       }
1348     Inventory(chg.labelfile, drive_num, need_eject, 0, 0, clean_slot);
1349     do_inventory = 0;                        /* If set on exit the labeldb will be set to invalid ..... */
1350     break;
1351  
1352    /*
1353      * Search for the tape, the index is the volume label
1354      */
1355   case COM_SEARCH:
1356     
1357     /*
1358      * If we have an barcode reader use
1359      * this way
1360      */
1361     if (BarCode(fd) == 1 && emubarcode != 1)
1362       {
1363         dbprintf(("search : look for %s\n", com.parameter));
1364         pbarcoderes->action = BARCODE_VOL;
1365         pbarcoderes->data.slot = -1;
1366         strcpy(pbarcoderes->data.voltag, com.parameter);
1367         if (MapBarCode(chg.labelfile, pbarcoderes) == 1)
1368           {
1369             /*
1370              * If both values are unset we have an problem
1371              * so leave the program
1372              */
1373             if (pbarcoderes->data.slot == -1 && pbarcoderes->data.barcode == NULL)
1374               {
1375                 printf("Label %s not found (1)\n",com.parameter);
1376                 endstatus = 2;
1377                 close(fd);
1378                 break;
1379               }
1380             
1381
1382             /*
1383              * Let's see, if we got an barcode check if it is
1384              * in the current inventory
1385              */
1386             if (pbarcoderes->data.barcode != NULL)
1387               {
1388  
1389                 for (x = 0; x < STE; x++)
1390                   {
1391                     if (strcmp(pSTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1392                       {
1393                         dbprintf(("search : found slot %d\n", x));
1394                         target = x;
1395                       }
1396                   }
1397                 /*
1398                  * Not found in the STE slots
1399                  * my be in the DTE (tape)
1400                  * If we find it check if it is in the right drive
1401                  * if we have more than one drive.
1402                  */
1403                 for (x = 0; x < DTE; x++)
1404                   {
1405                     if (strcmp(pDTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1406                       {
1407                         dbprintf(("search : found in tape %d\n", x));
1408                         /*
1409                          */
1410                         if (x == drive_num) {
1411                           oldtarget = get_current_slot(slot_file);
1412                           printf("%d %s\n", oldtarget- slot_offset, tape_device);
1413                           return(0);
1414                         } else {
1415                           printf("LABEL in wrong tape Unit\n");
1416                           return(2);
1417                         }
1418                       }
1419                   }
1420                 /*
1421                  * not found, so do an exit...
1422                  */               
1423                 if (target == -1)
1424                   {
1425                     printf("Label %s not found (2) \n",com.parameter);
1426                     close(fd);
1427                     endstatus = 2;
1428                     break;
1429                   }
1430               }  /* if barcode[0] != 0 */
1431
1432             /*
1433              * If we didn't find anything we will try the info
1434              * from the DB. A reason for not finding anything in the inventory
1435              * might be an unreadable barcode label
1436              */
1437             if (target == -1 && pbarcoderes->data.slot != -1)
1438               {
1439                 target = pbarcoderes->data.slot;
1440               }
1441
1442             /*
1443              * OK, if target is still -1 do the exit
1444              */
1445             if (target == -1)
1446               {
1447                 printf("Label %s not found (3)\n",com.parameter);
1448                 close(fd);
1449                 endstatus = 2;
1450                 break;
1451               }
1452           }
1453                
1454       }
1455
1456     /*
1457      * And now if we have emubarcode set and no barcode reader
1458      * use this one
1459      */
1460     if (emubarcode == 1 && BarCode(fd) != 1)
1461       {
1462         dbprintf(("search : look for %s\n", com.parameter));
1463         pbarcoderes->action = FIND_SLOT;
1464         pbarcoderes->data.slot = -1;
1465         strcpy(pbarcoderes->data.voltag, com.parameter);
1466
1467         if (MapBarCode(chg.labelfile, pbarcoderes) == 1)
1468           {
1469             if (pbarcoderes->data.valid == 1)
1470               {
1471                 target = pbarcoderes->data.slot;
1472               } else {
1473                 printf("Barcode DB out of sync \n");
1474                 close(fd);
1475                 endstatus=2;
1476                 break;
1477               }
1478           } else {
1479             printf("Label %s not found \n",com.parameter);
1480             close(fd);
1481             endstatus = 2;
1482             break;
1483           }
1484       } 
1485
1486     /*
1487      * The slot changing command
1488      */
1489   case COM_SLOT: 
1490     if (target == -1)
1491       {
1492         if (is_positive_number(com.parameter)) {
1493           if ((target = atoi(com.parameter))>=use_slots) {
1494             printf("<none> no slot `%d'\n",target);
1495             close(fd);
1496             endstatus = 2;
1497             break;
1498           } else {
1499             target = target+slot_offset;
1500           }
1501         } else {
1502           param_index=0;
1503           while((param_index<SLOTCOUNT)
1504                 &&(strcmp(slotdefs[param_index].str,com.parameter))) {
1505             param_index++;
1506           }
1507           target=get_relative_target(fd, use_slots,
1508                                      com.parameter,param_index,
1509                                      loaded, 
1510                                      slot_file,
1511                                      slot_offset,slot_offset+use_slots-1);
1512         }
1513       }
1514
1515     if (loaded) {
1516       oldtarget = get_current_slot(slot_file);
1517       if (oldtarget < 0)
1518         {
1519           dbprintf(("COM_SLOT: get_current_slot %d\n", oldtarget));
1520           oldtarget = find_empty(fd, slot_offset, use_slots);
1521           dbprintf(("COM_SLOT: find_empty %d\n", oldtarget));
1522         }
1523       
1524       /*
1525        * TODO check if the request slot for the unload is empty
1526        */
1527
1528        
1529       if ((oldtarget)!=target) {
1530         if (need_eject)
1531           {
1532             eject_tape(scsitapedevice, need_eject);
1533           } else {
1534             /*
1535              * If we have an SCSI path to the tape and an raw io path
1536              * try to read the Error Counter and the label
1537              */
1538             if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1539               {
1540                 LogSense(INDEX_TAPE);
1541               }
1542           }
1543
1544         (void)unload(fd, drive_num, oldtarget);
1545         if (ask_clean(scsitapedevice))
1546           clean_tape(fd,tape_device,clean_file,drive_num,
1547                      clean_slot,maxclean,time_file);
1548         loaded=0;
1549       }
1550     }
1551     
1552     put_current_slot(slot_file, target);
1553     
1554     if (!loaded && isempty(fd, target)) {
1555       printf("%d slot %d is empty\n",target-slot_offset,
1556              target-slot_offset);
1557       close(fd);
1558       endstatus = 1;
1559       break;
1560     }
1561
1562     if (!loaded && param_index != SLOT_ADVANCE)
1563       {
1564         if (ask_clean(scsitapedevice))
1565           clean_tape(fd,tape_device,clean_file,drive_num,
1566                      clean_slot,maxclean,time_file);
1567         if (load(fd, drive_num, target) != 0) {
1568           printf("%d slot %d move failed\n",target-slot_offset,
1569                  target-slot_offset);  
1570           close(fd);
1571           endstatus = 2;
1572           break;
1573         }
1574       }
1575
1576     if (need_sleep)
1577       {
1578         if (pDev[INDEX_TAPECTL].inqdone == 1)
1579           {
1580             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1581               {
1582                 printf("tape not ready\n");
1583                 endstatus = 2;
1584                 break;
1585               }
1586           } else {
1587             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1588               {
1589                 printf("tape not ready\n");
1590                 endstatus = 2;
1591                 break;
1592               }          
1593         }
1594       }
1595
1596     printf("%d %s\n", target-slot_offset, tape_device);
1597     break;
1598
1599   case COM_INFO:
1600     loaded = get_current_slot(slot_file);
1601
1602     if (loaded < 0)
1603       {
1604         loaded = find_empty(fd, slot_offset, use_slots);
1605       }
1606     loaded = loaded - slot_offset;
1607       
1608     printf("%d %d 1", loaded, use_slots);
1609
1610     if (BarCode(fd) == 1 || emubarcode == 1 || chg.havebarcode == 1)
1611       {
1612         printf(" 1\n");
1613       } else {
1614         printf(" 0\n");
1615       }
1616     break;
1617
1618   case COM_RESET:
1619     target=get_current_slot(slot_file);
1620
1621     if (target < 0)
1622     {
1623       dbprintf(("COM_RESET: get_current_slot %d\n", target));
1624       target=find_empty(fd, slot_offset, use_slots);
1625       dbprintf(("COM_RESET: find_empty %d\n", target));
1626     }
1627
1628     if (loaded) {
1629       
1630       if (!isempty(fd, target))
1631         target=find_empty(fd, slot_offset, use_slots);
1632       
1633       if (need_eject)
1634         {
1635           eject_tape(scsitapedevice, need_eject);
1636         } else {
1637           /*
1638            * If we have an SCSI path to the tape and an raw io path
1639            * try to read the Error Counter and the label
1640            */
1641           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1642             {
1643               LogSense(INDEX_TAPE);
1644             }
1645         }
1646       
1647       (void)unload(fd, drive_num, target);
1648       if (ask_clean(scsitapedevice))
1649         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1650                    maxclean,time_file);
1651     }
1652     
1653     if (isempty(fd, slot_offset)) {
1654       printf("0 slot 0 is empty\n");
1655       close(fd);
1656       endstatus = 1;
1657       break;
1658     }
1659     
1660     if (load(fd, drive_num, slot_offset) != 0) {
1661       printf("%d slot %d move failed\n",drive_num,
1662              slot_offset);  
1663       close(fd);
1664       put_current_slot(slot_file, slot_offset);
1665       endstatus = 2;
1666       break;
1667     }
1668     
1669     put_current_slot(slot_file, slot_offset);
1670     
1671     if (need_sleep)
1672       {
1673         if (pDev[INDEX_TAPECTL].inqdone == 1)
1674           {
1675             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1676               {
1677                 printf("tape not ready\n");
1678                 endstatus = 2;
1679                 break;
1680               }
1681           } else {
1682             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1683               {
1684                 printf("tape not ready\n");
1685                 endstatus = 2;
1686                 break;
1687               }          
1688           }
1689       }
1690     
1691     printf("%d %s\n", slot_offset, tape_device);
1692     break;
1693
1694   case COM_EJECT:
1695     if (loaded) {
1696       target=get_current_slot(slot_file);
1697       if (target < 0)
1698         {
1699           dbprintf(("COM_EJECT: get_current_slot %d\n", target));
1700           target = find_empty(fd, slot_offset, use_slots);
1701           dbprintf(("COM_EJECT: find_empty %d\n", target));
1702         }
1703       
1704       if (need_eject)
1705         {
1706           eject_tape(scsitapedevice, need_eject);
1707         } else {
1708           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1709             {
1710               LogSense(INDEX_TAPE);
1711             }
1712         }
1713
1714
1715       (void)unload(fd, drive_num, target);
1716       if (ask_clean(scsitapedevice))
1717         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1718                    maxclean,time_file);
1719       printf("%d %s\n", target, tape_device);
1720     } else {
1721       printf("%d %s\n", target, "drive was not loaded");
1722       endstatus = 1;
1723     }
1724     break;
1725   case COM_CLEAN:
1726     if (loaded) {
1727       target=get_current_slot(slot_file);
1728       if (target < 0)
1729         {
1730           dbprintf(("COM_CLEAN: get_current_slot %d\n", target));
1731           target = find_empty(fd, slot_offset, use_slots);
1732           dbprintf(("COM_CLEAN: find_empty %d\n",target));
1733         }
1734
1735       if (need_eject)
1736         {
1737           eject_tape(scsitapedevice, need_eject);
1738         } else {
1739           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1740             {
1741               LogSense(INDEX_TAPE);
1742             }
1743         }
1744
1745       (void)unload(fd, drive_num, target);
1746     } 
1747
1748     clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1749                maxclean,time_file);
1750     printf("%s cleaned\n", tape_device);
1751     break;
1752   };
1753
1754   /* FIX ME, should be an function to close the device */  
1755 /*   if (pChangerDev != NULL) */
1756 /*     close(pChangerDev->fd); */
1757  
1758 /*   if (pTapeDev != NULL) */
1759 /*     close(pTapeDev->fd); */
1760
1761 /*   if (pTapeDevCtl != NULL) */
1762 /*     close(pTapeDevCtl->fd); */
1763
1764
1765 #endif
1766   if (do_inventory == 1 && endstatus == 0 && chg.labelfile != NULL)
1767     {
1768       if ( chg.autoinv == 1)
1769         {
1770           DebugPrint(DEBUG_INFO,SECTION_INFO, "Do an inventory \n");
1771           Inventory(chg.labelfile, drive_num , chg.eject, 0, 0, clean_slot);
1772         } else {
1773           DebugPrint(DEBUG_INFO,SECTION_INFO, "Set all entrys in DB to invalid\n");
1774           memset(pbarcoderes, 0 , sizeof(MBC_T));
1775           pbarcoderes->action = RESET_VALID;
1776           MapBarCode(chg.labelfile,pbarcoderes);
1777         }
1778     }
1779
1780   DebugPrint(DEBUG_INFO,SECTION_INFO,"Exit status -> %d\n", endstatus);
1781   dbclose();
1782   return endstatus;
1783 }
1784 /*
1785  * Local variables:
1786  * indent-tabs-mode: nil
1787  * tab-width: 4
1788  * End:
1789  */
1790