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