46d4801621da68e7c6a0a6cc0b9f25680f3741a5
[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: %ld seconds\n"), (long int)chg->sleep);
204   dbprintf(_("Clean cycles    : %d\n"), chg->cleanmax);
205   dbprintf(_("Changer device  : %s\n"), chg->device);
206   if (chg->labelfile != NULL)
207     dbprintf(_("Label file      : %s\n"), chg->labelfile);
208   for (i=0; i<chg->number_of_configs; i++){
209     dbprintf(_("Tape config Nr: %d\n"), i);
210     dbprintf(_("  Drive number  : %d\n"), chg->conf[i].drivenum);
211     dbprintf(_("  Start slot    : %d\n"), chg->conf[i].start);
212     dbprintf(_("  End slot      : %d\n"), chg->conf[i].end);
213     dbprintf(_("  Clean slot    : %d\n"), chg->conf[i].cleanslot);
214
215     if (chg->conf[i].device != NULL)
216       dbprintf(_("  Device name   : %s\n"), chg->conf[i].device);
217     else
218       dbprintf(_("  Device name   : none\n"));
219
220     if (chg->conf[i].changerident != NULL)
221       dbprintf(_("  changer ident : %s\n"), chg->conf[i].changerident);
222     else
223       dbprintf(_("  changer ident : none\n"));
224
225     if (chg->conf[i].scsitapedev != NULL)
226       dbprintf(_("  SCSI Tape dev : %s\n"), chg->conf[i].scsitapedev);
227     else
228       dbprintf(_("  SCSI Tape dev : none\n"));
229
230     if (chg->conf[i].tapeident != NULL)
231       dbprintf(_("  tape ident    : %s\n"), chg->conf[i].tapeident);
232     else
233       dbprintf(_("  tape ident    : none\n"));
234
235     if (chg->conf[i].tapestatfile != NULL)
236       dbprintf(_("  stat file     : %s\n"), chg->conf[i].tapestatfile);
237     else
238       dbprintf(_("  stat file     : none\n"));
239
240     if (chg->conf[i].slotfile != NULL)
241       dbprintf(_("  Slot file     : %s\n"), chg->conf[i].slotfile);
242     else
243       dbprintf(_("  Slot file     : none\n"));
244
245     if (chg->conf[i].cleanfile != NULL)
246       dbprintf(_("  Clean file    : %s\n"), chg->conf[i].cleanfile);
247     else
248       dbprintf(_("  Clean file    : none\n"));
249
250     if (chg->conf[i].timefile != NULL)
251       dbprintf(_("  Usage count   : %s\n"), chg->conf[i].timefile);
252     else
253       dbprintf(_("  Usage count   : 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                 g_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           g_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             g_fprintf(stderr,_("drivenum %d is bad\n"), drivenum);
400             drivenum = 0;
401           }
402           if (strcmp(chg->conf[drivenum].changerident,"generic_changer") != 0) {
403             p = chg->conf[drivenum].changerident;
404             while (*p != '\0')
405             {
406               if (*p == '_')
407               {
408                 *p=' ';
409               }
410               p++;
411             }
412           }
413           break;
414         case TAPEIDENT:
415           chg->conf[drivenum].tapeident = stralloc(value);
416           break;
417         case CLEANMAX:
418           chg->cleanmax = atoi(value);
419           break;
420         case DRIVE:
421           drivenum = atoi(value);
422           if (drivenum < 0) {
423             g_fprintf(stderr,_("Error: drive must be >= 0\n"));
424             drivenum = 0;
425           } else if (drivenum >= numconf) {
426             g_fprintf(stderr,_("Error: drive must be less than number_drives\n"));
427             drivenum = numconf;
428           }
429           break;
430         case DRIVENUM:
431           if (drivenum < numconf){
432             chg->conf[drivenum].drivenum = atoi(value);
433           } else {
434             g_fprintf(stderr,_("Error: drive is not less than number_drives"
435                     " drivenum ignored\n"));
436           }
437           break;
438         case START:
439           if (drivenum < numconf){
440             chg->conf[drivenum].start = atoi(value);
441           } else {
442             g_fprintf(stderr,_("Error: drive is not less than number_drives"
443                     " startuse ignored\n"));
444           }
445           break;
446         case END:
447           if (drivenum < numconf){
448             chg->conf[drivenum].end = atoi(value);
449           } else {
450             g_fprintf(stderr,_("Error: drive is not less than number_drives"
451                     " enduse ignored\n"));
452           }
453           break;
454         case CLEAN:
455           if (drivenum < numconf){
456             chg->conf[drivenum].cleanslot = atoi(value);
457           } else {
458             g_fprintf(stderr,_("Error: drive is not less than number_drives"
459                     " cleanslot ignored\n"));
460           }
461           break;
462         case DEVICE:
463           if (drivenum < numconf){
464             chg->conf[drivenum].device = stralloc(value);
465           } else {
466             g_fprintf(stderr,_("Error: drive is not less than number_drives"
467                     " device ignored\n"));
468           }
469           break;
470         case STATFILE:
471           if (drivenum < numconf){
472             chg->conf[drivenum].slotfile = stralloc(value);
473           } else {
474             g_fprintf(stderr,_("Error: drive is not less than number_drives"
475                     " slotfile ignored\n"));
476           }
477           break;
478         case CLEANFILE:
479           if (drivenum < numconf){
480             chg->conf[drivenum].cleanfile = stralloc(value);
481           } else {
482             g_fprintf(stderr,_("Error: drive is not less than number_drives"
483                     " cleanfile ignored\n"));
484           }
485           break;
486         case USAGECOUNT:
487           if (drivenum < numconf){
488             chg->conf[drivenum].timefile = stralloc(value);
489           } else {
490             g_fprintf(stderr,_("Error: drive is not less than number_drives"
491                     " usagecount ignored\n"));
492           }
493           break;
494         default:
495           g_fprintf(stderr,_("Error: Unknown token\n"));
496           break;
497         }
498       }
499     amfree(linebuffer);
500   }
501   amfree(linebuffer);
502
503   fclose(file);
504   return 0;
505 }
506
507 /*----------------------------------------------------------------------------*/
508
509 /*  The tape drive does not have an idea of current slot so
510  *  we use a file to store the current slot.  It is not ideal
511  *  but it gets the job done  
512  */
513 int
514 get_current_slot(
515     char *count_file)
516 {
517   FILE *inf;
518   int retval = -1;
519   int ret;          /* return value for the fscanf function */
520   if ((inf=fopen(count_file,"r")) == NULL) {
521     g_fprintf(stderr, _("%s: unable to open (%s)\n"),
522             get_pname(), count_file);
523     exit(2);
524   }
525
526   ret = fscanf(inf,"%d",&retval);
527   fclose(inf);
528   
529   /*
530    * check if we got an result
531    * if no set retval to -1 
532   */
533   if (ret == 0 || ret == EOF)
534     {
535       retval = -1;
536     }
537
538   if (retval < 0 || retval > 10000) {
539     retval = -1;
540   }
541   return retval;
542 }
543
544 void
545 put_current_slot(
546     char *count_file,
547     int slot)
548 {
549   FILE *inf;
550
551   if (!count_file)
552     return;
553
554   if ((inf=fopen(count_file,"w")) == NULL) {
555     g_fprintf(stderr, _("%s: unable to open current slot file (%s)\n"),
556             get_pname(), count_file);
557     exit(2);
558   }
559   g_fprintf(inf, "%d\n", slot);
560   fclose(inf);
561 }
562
563 /* 
564  * Here we handle the inventory DB
565  * With this it should be possible to do an mapping
566  * Barcode      -> Volume label
567  * Volume Label -> Barcode
568  * Volume label -> Slot number
569  * Return Values:
570  * 1 -> Action was ok
571  * 0 -> Action failed
572  *
573  * The passed struct MBC_T will hold the found entry in the DB
574  */
575
576 int
577 MapBarCode(
578     char *labelfile,
579     MBC_T *result)
580 {
581   FILE *fp;
582   int version;
583   LabelV2_T *plabelv2;
584   long unusedpos= 0;
585   int unusedrec = 0;
586   int record    = 0;
587   int loop      = 1;
588   size_t rsize;
589   long pos;
590   int rc;
591
592   DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : Parameter\n"));
593   DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("labelfile -> %s, vol -> %s, barcode -> %s, action -> %c, slot -> %d, from -> %d\n"),
594              labelfile,
595              result->data.voltag,
596              result->data.barcode,
597              result->action,
598              result->data.slot,
599              result->data.from);
600   
601   if (labelfile == NULL)
602     {
603       DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_("Got empty labelfile (NULL)\n"));
604       ChgExit("MapBarCode", _("MapBarCode name of labelfile is not set\n"),FATAL);
605       /*NOTREACHED*/
606     }
607   if (access(labelfile, F_OK) == -1)
608     {
609       DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE, _("MapBarCode : creating %s"), labelfile);
610       if ((fp = fopen(labelfile, "w+")) == NULL)
611         {
612           DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_(" failed\n"));
613           ChgExit("MapBarCode", _("MapBarCode, creating labelfile failed\n"), FATAL);
614           /*NOTREACHED*/
615         }
616       g_fprintf(fp,":%d:", LABEL_DB_VERSION);
617       fclose(fp);
618     }
619   
620   if ((fp = fopen(labelfile, "r+")) == NULL)
621     {
622        DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_("MapBarCode : failed to open %s\n"), labelfile);
623        ChgExit("MapBarCode", _("MapBarCode, opening labelfile for read/write failed\n"), FATAL);
624        /*NOTREACHED*/
625     }
626   
627   if (fscanf(fp,":%d:", &version) != 1) {
628      ChgExit("MapBarCode", _("MapBarCode, DB Version unreadable.\n"), FATAL);
629      /*NOTREACHED*/
630   }
631   DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : DB version %d\n"), version);
632   
633   pos = ftell(fp);
634   if (version != LABEL_DB_VERSION)
635     {
636       ChgExit("MapBarCode", _("MapBarCode, DB Version does not match\n"), FATAL);
637       /*NOTREACHED*/
638     }
639
640   if (( plabelv2 = (LabelV2_T *)alloc(SIZEOF(LabelV2_T))) == NULL)
641     {
642       DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_("MapBarCode : alloc failed\n"));
643       ChgExit("MapBarCode", _("MapBarCode alloc failed\n"), FATAL);
644       /*NOTREACHED*/
645     }
646   
647   memset(plabelv2, 0, SIZEOF(LabelV2_T));
648
649   while(feof(fp) == 0 && loop == 1)
650     {
651       rsize = fread(plabelv2, 1, SIZEOF(LabelV2_T), fp);
652       if (rsize == SIZEOF(LabelV2_T))
653       {
654       record++;
655       DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : (%d) VolTag \"%s\", BarCode %s, inuse %d, slot %d, from %d, loadcount %d\n"),record,
656                  plabelv2->voltag,
657                  plabelv2->barcode,
658                  plabelv2->valid,
659                  plabelv2->slot,
660                  plabelv2->from,
661                  plabelv2->LoadCount);
662       switch (result->action)
663         {
664           /*
665            * Only dump the info
666            */ 
667         case BARCODE_DUMP:
668           g_printf(_("Slot -> %d, from -> %d, valid -> %d, Tag -> %s, Barcode -> %s, Loadcount %u\n"),
669                  plabelv2->slot,
670                  plabelv2->from,
671                  plabelv2->valid,
672                  plabelv2->voltag,
673                  plabelv2->barcode,
674                  plabelv2->LoadCount
675                  );
676           break;
677           /*
678            * Set all the record to invalid, used by the Inventory function
679            */
680         case RESET_VALID:
681           plabelv2->valid = 0;
682           if (fseek(fp, pos, SEEK_SET) == -1) {
683             fclose(fp);
684             amfree(plabelv2);
685             return 0; /* Fail */
686           }
687           if (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T)) {
688             fclose(fp);
689             amfree(plabelv2);
690             return 0; /* Fail */
691           }
692           break;
693           /*
694            * Add an entry
695            */
696         case BARCODE_PUT:
697           /*
698            * If it is an invalid record we can use it,
699            * so save the record number.
700            * This value is used at the end if no other
701            * record/action matches.
702            */
703           if (plabelv2->valid == 0)
704             {
705                  unusedpos = pos;
706                  unusedrec = record;
707             }
708
709           /*
710            * OK this record matches the barcode label
711            * so use/update it
712            */
713           if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
714             {
715
716               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : update entry\n"));
717               if (fseek(fp, pos, SEEK_SET) == -1) {
718                 fclose(fp);
719                 amfree(plabelv2);
720                 return 0; /* Fail */
721               }
722               plabelv2->valid = 1;
723               plabelv2->from = result->data.from;
724               plabelv2->slot = result->data.slot;
725               plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
726               strncpy(plabelv2->voltag, result->data.voltag,
727                       SIZEOF(plabelv2->voltag));
728               strncpy(plabelv2->barcode, result->data.barcode,
729                       SIZEOF(plabelv2->barcode));
730               rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
731               fclose(fp);
732               amfree(plabelv2);
733               return(rc);
734             }
735           break;
736           /*
737            * Look for an entry an return the entry
738            * if the voltag (the tape name) matches
739            */
740         case FIND_SLOT:
741           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
742             {
743               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode FIND_SLOT : \n"));
744               memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
745               amfree(plabelv2);
746               return(1);
747            }
748           break;
749           /*
750            * Update the entry,
751            * reason can be an load, incr the LoadCount
752            * or an new tape
753            */
754         case UPDATE_SLOT:
755           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
756             {
757               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode UPDATE_SLOT : update entry\n"));
758               if (fseek(fp, pos, SEEK_SET) == -1) {
759                 fclose(fp);
760                 amfree(plabelv2);
761                 return 0; /* Fail */
762               }
763               strncpy(plabelv2->voltag, result->data.voltag,
764                      SIZEOF(plabelv2->voltag));
765               strncpy(plabelv2->barcode, result->data.barcode,
766                      SIZEOF(plabelv2->barcode));
767               plabelv2->valid = 1;
768               plabelv2->slot = result->data.slot;
769               plabelv2->from = result->data.from;
770               plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
771               rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
772               fclose(fp);
773               amfree(plabelv2);
774               return(rc);
775             }
776           break;
777           /*
778            * Look for the barcode label of an given volume label
779            * return the slot number and the barcode label.
780            * If the entry is not valid return -1 as slot number
781            */
782         case BARCODE_VOL:
783           /*
784            * DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode: (%d) inside BARCODE_VOL\n"), record);
785           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("file value: %s, searched for value: %s\n"), plabelv2->voltag, result->data.voltag);
786           */
787           if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
788             {
789               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : VOL %s match\n"), result->data.voltag);
790               fclose(fp);
791               
792               memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
793               amfree(plabelv2);
794               return(1);
795             }
796           break;
797           /*
798            * Look for an entry which matches the passed
799            * barcode label
800            */
801         case BARCODE_BARCODE:
802           if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
803             {
804               DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : BARCODE %s match\n"), result->data.barcode);
805               fclose(fp);
806               
807               memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
808               amfree(plabelv2);
809               return(1);
810             }
811           break;
812
813         default:
814           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : unknown action\n"));
815           break;
816         }
817       pos = ftell(fp);
818       } else {
819          DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : feof (%d)\n"), feof(fp));
820          DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : error in read record expect %d, got %d\n"),SIZEOF(LabelV2_T), rsize);
821         loop=0;
822       }
823     }
824
825   /*
826    * OK, if we come here and the action is either
827    * PUT or update it seems that we have to create a new
828    * record, becuae none of the exsisting records matches
829    */
830   if (result->action == BARCODE_PUT || result->action == UPDATE_SLOT )
831     {
832       /*
833        * If we have an entry where the valid flag was set to 0
834        * we can use this record, so seek to this position
835        * If we have no record for reuse the new record will be written to the end.
836        */
837       if (unusedpos != 0)
838         {
839           DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : reuse record %d\n"), unusedrec);
840           if (fseek(fp, unusedpos, SEEK_SET) == -1) {
841             fclose(fp);
842             amfree(plabelv2);
843             return 0; /* Fail */
844           }
845         }
846       /*
847        * Set all values to zero
848        */
849       memset(plabelv2, 0, SIZEOF(LabelV2_T));     
850
851       strncpy(plabelv2->voltag, result->data.voltag,
852               SIZEOF(plabelv2->voltag));
853       strncpy(plabelv2->barcode, result->data.barcode,
854               SIZEOF(plabelv2->barcode));
855       plabelv2->valid = 1;
856       plabelv2->from = result->data.from;
857       plabelv2->slot = result->data.slot;
858       rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
859       fclose(fp);
860       amfree(plabelv2);
861       return(rc);
862     }
863
864   /*
865    * If we hit this point nothing was 
866    * found, so return an 0
867    */
868   fclose(fp);
869   amfree(plabelv2);
870   return(0);
871 }
872
873 /* ---------------------------------------------------------------------- 
874    This stuff deals with parsing the command line */
875
876 typedef struct com_arg
877 {
878   char *str;
879   int command_code;
880   int takesparam;
881 } argument;
882
883
884 typedef struct com_stru
885 {
886   int command_code;
887   char *parameter;
888 } command;
889
890 void parse_args(int argc, char *argv[],command *rval);
891
892 /* major command line args */
893 #define COMCOUNT 13
894 #define COM_SLOT 0
895 #define COM_INFO 1
896 #define COM_RESET 2
897 #define COM_EJECT 3
898 #define COM_CLEAN 4
899 #define COM_LABEL 5
900 #define COM_SEARCH 6
901 #define COM_STATUS 7
902 #define COM_TRACE 8
903 #define COM_INVENTORY 9
904 #define COM_DUMPDB 10
905 #define COM_SCAN 11
906 #define COM_GEN_CONF 12
907 argument argdefs[]={{"-slot",COM_SLOT,1},
908                     {"-info",COM_INFO,0},
909                     {"-reset",COM_RESET,0},
910                     {"-eject",COM_EJECT,0},
911                     {"-clean",COM_CLEAN,0},
912                     {"-label",COM_LABEL,1},
913                     {"-search",COM_SEARCH,1},
914                     {"-status",COM_STATUS,1},
915                     {"-trace",COM_TRACE,1},
916                     {"-inventory", COM_INVENTORY,0},
917                     {"-dumpdb", COM_DUMPDB,0},
918                     {"-scan", COM_SCAN,0},
919                     {"-genconf", COM_GEN_CONF,0}
920         };
921
922
923 /* minor command line args */
924 #define SLOT_CUR 0
925 #define SLOT_NEXT 1
926 #define SLOT_PREV 2
927 #define SLOT_FIRST 3
928 #define SLOT_LAST 4
929 #define SLOT_ADVANCE 5
930 argument slotdefs[]={{"current",SLOT_CUR,0},
931                      {"next",SLOT_NEXT,0},
932                      {"prev",SLOT_PREV,0},
933                      {"first",SLOT_FIRST,0},
934                      {"last",SLOT_LAST,0},
935                      {"advance",SLOT_ADVANCE,0},
936         };
937 #define SLOTCOUNT (int)(sizeof(slotdefs) / sizeof(slotdefs[0]))
938
939 /* is the string a valid positive int? */
940 int
941 is_positive_number(
942     char *tmp)
943 {
944   int i=0;
945   if ((tmp==NULL)||(tmp[0]==0))
946     return 0;
947   while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
948     i++;
949   if (tmp[i]==0)
950     return 1;
951   else
952     return 0;
953 }
954
955 void
956 usage(
957     char *argv[])
958 {
959   int cnt;
960   g_printf(_("%s: Usage error.\n"), argv[0]);
961   for (cnt=0; cnt < COMCOUNT; cnt++){
962     g_printf("      %s    %s",argv[0],argdefs[cnt].str);
963     if (argdefs[cnt].takesparam)
964       g_printf(" <param>\n");
965     else
966       g_printf("\n");
967   }
968   exit(2);
969 }
970
971
972 void
973 parse_args(
974     int argc,
975     char *argv[],
976     command *rval)
977 {
978   int i;
979
980   for (i=0; i < argc; i++)
981     dbprintf(_("ARG [%d] : %s\n"), i, argv[i]);
982   i = 0;
983   if ((argc<2)||(argc>3))
984     usage(argv);
985   while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
986     i++;
987   if (i==COMCOUNT)
988     usage(argv);
989   rval->command_code = argdefs[i].command_code;
990   if (argdefs[i].takesparam) {
991     if (argc<3)
992       usage(argv);
993     rval->parameter=argv[2];      
994   }
995   else {
996     if (argc>2)
997       usage(argv);
998     rval->parameter=0;
999   }
1000 }
1001
1002 /* used to find actual slot number from keywords next, prev, first, etc */
1003 int
1004 get_relative_target(
1005     int fd,
1006     int nslots,
1007     char *parameter,
1008     int param_index,
1009     int loaded,
1010     char *slot_file,
1011     int slot_offset,
1012     int maxslot)
1013 {
1014   int current_slot;
1015   
1016   (void)loaded; /* Quiet unused parameter warning */
1017
1018   current_slot = get_current_slot(slot_file);
1019
1020   if (current_slot > maxslot) {
1021     current_slot = slot_offset;
1022   }
1023   if (current_slot < slot_offset) {
1024     current_slot = slot_offset;
1025   }
1026
1027   switch(param_index) {
1028   case SLOT_CUR:
1029     return current_slot;
1030
1031   case SLOT_NEXT:
1032   case SLOT_ADVANCE:
1033     if (++current_slot==nslots+slot_offset)
1034       return slot_offset;
1035     return current_slot;
1036
1037   case SLOT_PREV:
1038     if (--current_slot<slot_offset)
1039       return maxslot;
1040     return current_slot;
1041
1042   case SLOT_FIRST:
1043     return slot_offset;
1044
1045   case SLOT_LAST:
1046     return maxslot;
1047
1048   default: 
1049     break;
1050   }
1051   g_printf(_("<none> no slot `%s'\n"),parameter);
1052   close(fd);
1053   exit(2);
1054   /*NOTREACHED*/
1055 }
1056
1057 /* This function should ask the drive if it wants to be cleaned */
1058 int
1059 ask_clean(
1060     char *tapedev)
1061 {
1062   int ret;
1063
1064   ret = get_clean_state(tapedev);
1065
1066   if (ret < 0) /* < 0 means query does not work ... */
1067   {
1068     return(0);
1069   }
1070   return ret;
1071 }
1072
1073 /* This function should move the cleaning cartridge into the drive */
1074 void
1075 clean_tape(
1076     int fd,
1077     char *tapedev,
1078     char *cnt_file,
1079     int drivenum, 
1080     int cleancart,
1081     int maxclean,
1082     char *usagetime)
1083 {
1084   int counter;
1085
1086   if (cleancart == -1 ){
1087     return;
1088   }
1089
1090   /* Now we should increment the counter */
1091   if (cnt_file != NULL){
1092     counter = get_current_slot(cnt_file);
1093     counter++;
1094     if (counter>=maxclean){
1095       /* Now we should inform the administrator */
1096       char *mail_cmd = NULL;
1097       FILE *mailf = NULL;
1098       int mail_pipe_opened = 1;
1099 #ifdef MAILER
1100       if(getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0 &&
1101          validate_mailto(getconf_str(CNF_MAILTO))) {
1102          mail_cmd = vstralloc(MAILER,
1103                            " -s", " \"", _("AMANDA PROBLEM: PLEASE FIX"), "\"",
1104                            " ", getconf_str(CNF_MAILTO),
1105                            NULL);
1106          if((mailf = popen(mail_cmd, "w")) == NULL){
1107                 g_printf(_("Mail failed\n"));
1108                 error(_("could not open pipe to \"%s\": %s"),
1109                         mail_cmd, strerror(errno));
1110                 /*NOTREACHED*/
1111         }
1112       } else {
1113         mail_pipe_opened = 0;
1114         mailf = stderr;
1115         g_fprintf(mailf, _("\nNo mail recipient specified, output redirected to stderr"));
1116       }   
1117 #else
1118       mail_pipe_opened = 0;
1119       mailf = stderr;
1120       g_fprintf(mailf, _("\nNo mailer specified; output redirected to stderr"));
1121 #endif
1122       g_fprintf(mailf,_("\nThe usage count of your cleaning tape in slot %d"),
1123               cleancart);
1124       g_fprintf(mailf,_("\nis more than %d. (cleanmax)"),maxclean);
1125       g_fprintf(mailf,_("\nTapedrive %s needs to be cleaned"),tapedev);
1126       g_fprintf(mailf,_("\nPlease insert a new cleaning tape and reset"));
1127       g_fprintf(mailf,_("\nthe countingfile %s"),cnt_file);
1128
1129       if(mail_pipe_opened == 1 && pclose(mailf) != 0) {
1130         error(_("mail command failed: %s"), mail_cmd);
1131         /*NOTREACHED*/
1132       }
1133       return;
1134     }
1135     put_current_slot(cnt_file, counter);
1136   }
1137   load(fd,drivenum,cleancart);
1138   /*
1139    * Hack, sleep for some time
1140    */
1141
1142   sleep(60);
1143
1144   if (drive_loaded(fd, drivenum))
1145     unload(fd, drivenum, cleancart);  
1146   if (usagetime)
1147     unlink(usagetime);
1148 }
1149 /* ----------------------------------------------------------------------*/
1150
1151 int
1152 main(
1153     int         argc,
1154     char **     argv)
1155 {
1156   int loaded;
1157   int target, oldtarget;
1158   command com;   /* a little DOS joke */
1159   int x;
1160   MBC_T *pbarcoderes;
1161   /*
1162    * drive_num really should be something from the config file, but..
1163    * for now, it is set to zero, since most of the common changers
1164    * used by amanda only have one drive ( until someone wants to 
1165    * use an EXB60/120, or a Breece Hill Q45.. )
1166    */
1167   unsigned char emubarcode;
1168   int drive_num;
1169   int need_eject; /* Does the drive need an eject command ? */
1170   time_t need_sleep; /* How many seconds to wait for the drive to get ready */
1171
1172   int maxclean;
1173   char *clean_file;
1174   char *time_file;
1175
1176   /*
1177    * For the emubarcode stuff
1178    */
1179   int use_slots;
1180   int slot_offset;
1181   int confnum;
1182
1183   int fd;
1184   int slotcnt;
1185   int drivecnt;
1186   int endstatus = 0;
1187
1188   char *changer_dev;
1189   char *tape_device;
1190   char *chg_scsi_conf;          /* The config file for us */
1191   char *slot_file;              /* Where we will place the info which
1192                                          * slot is loaded
1193                                          */
1194   char *scsitapedevice;
1195
1196   int param_index = 0;
1197
1198   /*
1199    * Configure program for internationalization:
1200    *   1) Only set the message locale for now.
1201    *   2) Set textdomain for all amanda related programs to "amanda"
1202    *      We don't want to be forced to support dozens of message catalogs.
1203    */  
1204   setlocale(LC_MESSAGES, "C");
1205   textdomain("amanda"); 
1206
1207   changer = alloc(SIZEOF(changer_t));
1208   pbarcoderes = alloc(SIZEOF(MBC_T));
1209
1210   memset(pbarcoderes, 0 , SIZEOF(MBC_T));
1211   changer->number_of_configs = 0;
1212   changer->eject = 0;
1213   changer->sleep = 0;
1214   changer->cleanmax = 0;
1215   changer->device = NULL;
1216   changer->labelfile = NULL;
1217   changer->conf = NULL;
1218 #ifdef CHG_SCSI_STANDALONE
1219   g_printf(_("Ups standalone\n"));
1220 #else
1221   set_pname("chg-scsi");
1222
1223   /* Don't die when child closes pipe */
1224   signal(SIGPIPE, SIG_IGN);
1225
1226   dbopen(DBG_SUBDIR_SERVER);
1227
1228   dbprintf("chg-scsi: %s\n", rcsid);
1229   ChangerDriverVersion();
1230
1231   if (debug_file == NULL)
1232     {
1233         debug_file = dbfp();
1234     }
1235   
1236   parse_args(argc,argv,&com);
1237
1238   pDev = (OpenFiles_T *)alloc(SIZEOF(OpenFiles_T) * CHG_MAXDEV);
1239   memset(pDev, 0, SIZEOF(OpenFiles_T) * CHG_MAXDEV );
1240
1241
1242   switch(com.command_code) 
1243     {
1244     case COM_SCAN:
1245       ScanBus(1);
1246       return(0);
1247
1248     case COM_GEN_CONF:
1249       ScanBus(0);
1250       PrintConf();
1251       return(0);
1252
1253     default:
1254       break;
1255     }
1256
1257   config_init(CONFIG_INIT_USE_CWD | CONFIG_INIT_FATAL, NULL);
1258
1259   chg_scsi_conf = getconf_str(CNF_CHANGERFILE);
1260   tape_device = getconf_str(CNF_TAPEDEV);
1261
1262   /* Get the configuration parameters */
1263   /* Attention, this will not support more than 10 tape devices 0-9 */
1264   /* */
1265   if (strlen(tape_device)==1){
1266     if (read_config(chg_scsi_conf, changer) == -1)
1267     {
1268       g_fprintf(stderr, _("%s open: of %s failed\n"), get_pname(), chg_scsi_conf);
1269       return (2);
1270     }
1271     confnum=atoi(tape_device);
1272     if (changer->number_of_configs == 0)
1273     {
1274        g_fprintf(stderr,_("%s: changer->conf[%d] == NULL\n"),
1275                 get_pname(), confnum);
1276        return (2);
1277     }
1278     if (confnum >= changer->number_of_configs) {
1279        g_fprintf(stderr,_("%s: Configuration %s config # out of range (%d >= %d)\n"),
1280                 get_pname(), chg_scsi_conf,
1281                 confnum, 
1282                 changer->number_of_configs);
1283        return (2);
1284     }
1285
1286     use_slots    = changer->conf[confnum].end-changer->conf[confnum].start+1;
1287     slot_offset  = changer->conf[confnum].start;
1288     drive_num    = changer->conf[confnum].drivenum;
1289     need_eject   = changer->eject;
1290     need_sleep   = changer->sleep;
1291
1292     if ( NULL != changer->conf[confnum].cleanfile)
1293       clean_file   = stralloc(changer->conf[confnum].cleanfile);
1294     else
1295       clean_file = NULL;
1296
1297     clean_slot   = changer->conf[confnum].cleanslot;
1298     maxclean     = changer->cleanmax;
1299     emubarcode   = changer->emubarcode;
1300     if (NULL != changer->conf[confnum].timefile)
1301       time_file = stralloc(changer->conf[confnum].timefile);
1302     else
1303       time_file = NULL;
1304
1305     if (NULL != changer->conf[confnum].slotfile)
1306       slot_file = stralloc(changer->conf[confnum].slotfile);
1307     else
1308       slot_file = NULL;
1309
1310     if (NULL != changer->conf[confnum].device)
1311       tape_device  = stralloc(changer->conf[confnum].device);
1312     else
1313       tape_device = NULL;
1314
1315     if (NULL != changer->device)
1316       changer_dev  = stralloc(changer->device); 
1317     else
1318       changer_dev = NULL;
1319
1320     if (NULL != changer->conf[confnum].scsitapedev)
1321       scsitapedevice = stralloc(changer->conf[confnum].scsitapedev);
1322     else
1323       scsitapedevice = NULL;
1324
1325     if (NULL != changer->conf[confnum].tapestatfile)
1326       tapestatfile = stralloc(changer->conf[confnum].tapestatfile);
1327     else
1328       tapestatfile = NULL;
1329     dump_changer_struct(changer);
1330
1331
1332
1333     /* 
1334      * The changer device.
1335      * If we can't open it fail with a message
1336      */
1337
1338     if (OpenDevice(INDEX_CHANGER , changer_dev, "changer_dev", changer->conf[confnum].changerident) == 0)
1339       {
1340         int localerr = errno;
1341         g_fprintf(stderr, _("%s: open: %s: %s\n"), get_pname(), 
1342                 changer_dev, strerror(localerr));
1343         g_printf(_("%s open: %s: %s\n"), _("<none>"), changer_dev, strerror(localerr));
1344         dbprintf(_("open: %s: %s\n"), changer_dev, strerror(localerr));
1345         return 2;
1346       }
1347
1348     fd = INDEX_CHANGER;
1349
1350     /*
1351      * The tape device.
1352      * We need it for:
1353      * eject if eject is set
1354      * inventory (reading of the labels) if emubarcode (not yet)
1355      */
1356     if (tape_device != NULL)
1357       {
1358         if (OpenDevice(INDEX_TAPE, tape_device, "tape_device", changer->conf[confnum].tapeident) == 0)
1359           {
1360             dbprintf(_("warning open of %s: failed\n"),  tape_device);
1361           }
1362       }
1363
1364     /*
1365      * This is for the status pages of the SCSI tape, nice to have but no must....
1366      */
1367     if (scsitapedevice != NULL)
1368       {
1369         if (OpenDevice(INDEX_TAPECTL, scsitapedevice, "scsitapedevice", changer->conf[confnum].tapeident) == 0)
1370           {
1371             dbprintf(_("warning open of %s: failed\n"), scsitapedevice);
1372           }
1373       }
1374     
1375
1376     /*
1377      * So if we need eject we need either an raw device to eject with an ioctl,
1378      * or an SCSI device to send the SCSI eject.
1379      */
1380
1381     if (need_eject != 0 )
1382       {
1383         if (pDev[INDEX_TAPE].avail == 0 && pDev[INDEX_TAPECTL].avail == 0)
1384           {
1385             g_printf(_("No device found for tape eject"));
1386             return(2);
1387           }
1388       }
1389
1390         
1391     if ((changer->conf[confnum].end == -1) || (changer->conf[confnum].start == -1)){
1392       slotcnt = get_slot_count(fd);
1393       use_slots    = slotcnt;
1394       slot_offset  = 0;
1395     }
1396
1397     /*
1398      * Now check if we have all what we need
1399      * If either emubarcode is set or the changer support barcode
1400      * we need an label file
1401      */
1402     
1403     if ( changer->emubarcode == 1 || BarCode(INDEX_CHANGER) == 1) 
1404       {
1405         if (changer->labelfile == NULL)
1406           {
1407             g_printf(_("labelfile param not set in your config\n"));
1408             return(2);
1409           }
1410       }
1411     
1412     if (slot_file == NULL)
1413       {
1414         g_printf(_("slotfile param. not set in your config\n"));
1415         return(2);
1416       }
1417     
1418     if (access(slot_file,R_OK|W_OK) != 0)
1419       {
1420         g_printf(_("slotfile %s does not exsist or is not read/write\n"), slot_file);
1421         return(2);
1422       }
1423
1424   } else { /* if (strlen(tape_device)==1) */
1425         g_printf(_("please check your config and use a config file for chg-scsi\n"));
1426         return(2);
1427   }
1428
1429   drivecnt = get_drive_count(fd);
1430
1431   if (drive_num > drivecnt) {
1432     g_printf(_("%s drive number error (%d > %d)\n"), _("<none>"), 
1433            drive_num, drivecnt);
1434     g_fprintf(stderr, _("%s: requested drive number (%d) greater than "
1435             "number of supported drives (%d)\n"), get_pname(), 
1436             drive_num, drivecnt);
1437     dbprintf(_("requested drive number (%d) is greater than "
1438               "number of supported drives (%d)\n"), drive_num, drivecnt);
1439     return 2;
1440   }
1441
1442   loaded = (int)drive_loaded(fd, drive_num);
1443   target = -1;
1444
1445   switch(com.command_code) {
1446 /* This is only for the experts ;-) */
1447   case COM_TRACE:
1448         ChangerReplay(com.parameter);
1449   break;
1450 /*
1451 */
1452   case COM_DUMPDB:
1453     pbarcoderes->action = BARCODE_DUMP;
1454     MapBarCode(changer->labelfile, pbarcoderes);
1455     break;
1456   case COM_STATUS:
1457     ChangerStatus(com.parameter, changer->labelfile,
1458                 BarCode(fd), slot_file, changer_dev, tape_device);
1459     break;
1460   case COM_LABEL: /* Update BarCode/Label mapping file */
1461     pbarcoderes->action = BARCODE_PUT;
1462     strncpy(pbarcoderes->data.voltag, com.parameter,
1463            SIZEOF(pbarcoderes->data.voltag));
1464     strncpy(pbarcoderes->data.barcode, pDTE[drive_num].VolTag,
1465            SIZEOF(pbarcoderes->data.barcode));
1466     MapBarCode(changer->labelfile, pbarcoderes);
1467     g_printf("0 0 0\n");
1468     break;
1469
1470     /*
1471      * Inventory does an scan of the library and updates the mapping in the label DB
1472      */
1473   case COM_INVENTORY:
1474     do_inventory = 1;                                     /* Tell the label check not to exit on label errors */
1475     if (loaded)
1476       {
1477         oldtarget = get_current_slot(slot_file);
1478         if (oldtarget < 0)
1479           {
1480             dbprintf(_("COM_INVENTORY: get_current_slot %d\n"), oldtarget);
1481             oldtarget = find_empty(fd, slot_offset, use_slots);
1482             dbprintf(_("COM_INVENTORY: find_empty %d\n"), oldtarget);
1483           }
1484
1485         if (need_eject)
1486           {
1487             eject_tape(scsitapedevice, need_eject);
1488           } else {
1489             if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1490               {
1491                 LogSense(INDEX_TAPE);
1492               }
1493           }
1494
1495         (void)unload(fd, drive_num, oldtarget);
1496         if (ask_clean(scsitapedevice))
1497           clean_tape(fd,tape_device,clean_file,drive_num,
1498                      clean_slot, maxclean, time_file);
1499       }
1500     Inventory(changer->labelfile, drive_num, need_eject, 0, 0, clean_slot);
1501     do_inventory = 0;                        /* If set on exit the labeldb will be set to invalid ..... */
1502     break;
1503  
1504    /*
1505      * Search for the tape, the index is the volume label
1506      */
1507   case COM_SEARCH:
1508     
1509     /*
1510      * If we have an barcode reader use
1511      * this way
1512      */
1513     if (BarCode(fd) == 1 && emubarcode != 1)
1514       {
1515         dbprintf(_("search : look for %s\n"), com.parameter);
1516         pbarcoderes->action = BARCODE_VOL;
1517         pbarcoderes->data.slot = -1;
1518         strncpy(pbarcoderes->data.voltag, com.parameter,
1519                SIZEOF(pbarcoderes->data.voltag));
1520         if (MapBarCode(changer->labelfile, pbarcoderes) == 1)
1521           {
1522             /*
1523              * If both values are unset we have an problem
1524              * so leave the program
1525              */
1526             if (pbarcoderes->data.slot == -1 && pbarcoderes->data.barcode == NULL)
1527               {
1528                 g_printf(_("Label %s not found (1)\n"),com.parameter);
1529                 endstatus = 2;
1530                 close(fd);
1531                 break;
1532               }
1533             
1534
1535             /*
1536              * Let's see, if we got an barcode check if it is
1537              * in the current inventory
1538              */
1539             if (pbarcoderes->data.barcode != NULL)
1540               {
1541  
1542                 for (x = 0; x < (int)STE; x++)
1543                   {
1544                     if (strcmp(pSTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1545                       {
1546                         dbprintf(_("search : found slot %d\n"), x);
1547                         target = x;
1548                       }
1549                   }
1550                 /*
1551                  * Not found in the STE slots
1552                  * my be in the DTE (tape)
1553                  * If we find it check if it is in the right drive
1554                  * if we have more than one drive.
1555                  */
1556                 for (x = 0; x < (int)DTE; x++)
1557                   {
1558                     if (strcmp(pDTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1559                       {
1560                         dbprintf(_("search : found in tape %d\n"), x);
1561                         /*
1562                          */
1563                         if (x == drive_num) {
1564                           oldtarget = get_current_slot(slot_file);
1565                           g_printf("%d %s\n", oldtarget - slot_offset, tape_device);
1566                           return(0);
1567                         } else {
1568                           g_printf(_("LABEL in wrong tape Unit\n"));
1569                           return(2);
1570                         }
1571                       }
1572                   }
1573                 /*
1574                  * not found, so do an exit...
1575                  */               
1576                 if (target == -1)
1577                   {
1578                     g_printf(_("Label %s not found (2) \n"),com.parameter);
1579                     close(fd);
1580                     endstatus = 2;
1581                     break;
1582                   }
1583               }  /* if barcode[0] != 0 */
1584
1585             /*
1586              * If we didn't find anything we will try the info
1587              * from the DB. A reason for not finding anything in the inventory
1588              * might be an unreadable barcode label
1589              */
1590             if (target == -1 && pbarcoderes->data.slot != -1)
1591               {
1592                 target = pbarcoderes->data.slot;
1593               }
1594
1595             /*
1596              * OK, if target is still -1 do the exit
1597              */
1598             if (target == -1)
1599               {
1600                 g_printf(_("Label %s not found (3)\n"),com.parameter);
1601                 close(fd);
1602                 endstatus = 2;
1603                 break;
1604               }
1605           }
1606                
1607       }
1608
1609     /*
1610      * And now if we have emubarcode set and no barcode reader
1611      * use this one
1612      */
1613     if (emubarcode == 1 && BarCode(fd) != 1)
1614       {
1615         dbprintf(_("search : look for %s\n"), com.parameter);
1616         pbarcoderes->action = FIND_SLOT;
1617         pbarcoderes->data.slot = -1;
1618         strncpy(pbarcoderes->data.voltag, com.parameter,
1619                SIZEOF(pbarcoderes->data.voltag));
1620
1621         if (MapBarCode(changer->labelfile, pbarcoderes) == 1)
1622           {
1623             if (pbarcoderes->data.valid == 1)
1624               {
1625                 target = pbarcoderes->data.slot;
1626               } else {
1627                 g_printf(_("Barcode DB out of sync \n"));
1628                 close(fd);
1629                 endstatus=2;
1630                 break;
1631               }
1632           } else {
1633             g_printf(_("Label %s not found \n"),com.parameter);
1634             close(fd);
1635             endstatus = 2;
1636             break;
1637           }
1638       } 
1639
1640     /*
1641      * The slot changing command
1642      */
1643   case COM_SLOT: 
1644     if (target == -1)
1645       {
1646         if (is_positive_number(com.parameter)) {
1647           if ((target = atoi(com.parameter))>=use_slots) {
1648             g_printf(_("<none> no slot `%d'\n"),target);
1649             close(fd);
1650             endstatus = 2;
1651             break;
1652           } else {
1653             target = target + slot_offset;
1654           }
1655         } else {
1656           param_index=0;
1657           while((param_index < SLOTCOUNT) &&
1658                 (strcmp(slotdefs[param_index].str,com.parameter))) {
1659             param_index++;
1660           }
1661           target=get_relative_target(fd, use_slots,
1662                                      com.parameter,param_index,
1663                                      loaded, 
1664                                      slot_file,
1665                                      slot_offset,
1666                                      (slot_offset + use_slots - 1));
1667         }
1668       }
1669
1670     if (loaded) {
1671       oldtarget = get_current_slot(slot_file);
1672       if (oldtarget < 0)
1673         {
1674           dbprintf(_("COM_SLOT: get_current_slot %d\n"), oldtarget);
1675           oldtarget = find_empty(fd, slot_offset, use_slots);
1676           dbprintf(_("COM_SLOT: find_empty %d\n"), oldtarget);
1677         }
1678       
1679       /*
1680        * TODO check if the request slot for the unload is empty
1681        */
1682
1683        
1684       if ((oldtarget)!=target) {
1685         if (need_eject)
1686           {
1687             eject_tape(scsitapedevice, need_eject);
1688           } else {
1689             /*
1690              * If we have an SCSI path to the tape and an raw io path
1691              * try to read the Error Counter and the label
1692              */
1693             if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1694               {
1695                 LogSense(INDEX_TAPE);
1696               }
1697           }
1698
1699         (void)unload(fd, drive_num, oldtarget);
1700         if (ask_clean(scsitapedevice))
1701           clean_tape(fd, tape_device, clean_file, drive_num,
1702                      clean_slot, maxclean, time_file);
1703         loaded=0;
1704       }
1705     }
1706     
1707     put_current_slot(slot_file, target);
1708     
1709     if (!loaded && isempty(fd, target)) {
1710       g_printf(_("%d slot %d is empty\n"),target - slot_offset,
1711              target - slot_offset);
1712       close(fd);
1713       endstatus = 1;
1714       break;
1715     }
1716
1717     if (!loaded && param_index != SLOT_ADVANCE)
1718       {
1719         if (ask_clean(scsitapedevice))
1720           clean_tape(fd, tape_device, clean_file, drive_num,
1721                      clean_slot, maxclean, time_file);
1722         if (load(fd, drive_num, target) != 0) {
1723           g_printf(_("%d slot %d move failed\n"),target - slot_offset,
1724                  target - slot_offset);  
1725           close(fd);
1726           endstatus = 2;
1727           break;
1728         }
1729       }
1730
1731     if (need_sleep)
1732       {
1733         if (pDev[INDEX_TAPECTL].inqdone == 1)
1734           {
1735             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1736               {
1737                 g_printf(_("tape not ready\n"));
1738                 endstatus = 2;
1739                 break;
1740               }
1741           } else {
1742             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1743               {
1744                 g_printf(_("tape not ready\n"));
1745                 endstatus = 2;
1746                 break;
1747               }          
1748         }
1749       }
1750
1751     g_printf("%d %s\n", target - slot_offset, tape_device);
1752     break;
1753
1754   case COM_INFO:
1755     loaded = (int)get_current_slot(slot_file);
1756
1757     if (loaded < 0)
1758       {
1759         loaded = find_empty(fd, slot_offset, use_slots);
1760       }
1761     loaded = loaded - (int)slot_offset;
1762       
1763     g_printf("%d %d 1", loaded, use_slots);
1764
1765     if (BarCode(fd) == 1 || emubarcode == 1)
1766       {
1767         g_printf(" 1\n");
1768       } else {
1769         g_printf(" 0\n");
1770       }
1771     break;
1772
1773   case COM_RESET:
1774     target=get_current_slot(slot_file);
1775
1776     if (target < 0)
1777     {
1778       dbprintf(_("COM_RESET: get_current_slot %d\n"), target);
1779       target = find_empty(fd, slot_offset, use_slots);
1780       dbprintf(_("COM_RESET: find_empty %d\n"), target);
1781     }
1782
1783     if (loaded) {
1784       
1785       if (!isempty(fd, target))
1786         target = find_empty(fd, slot_offset, use_slots);
1787       
1788       if (need_eject)
1789         {
1790           eject_tape(scsitapedevice, need_eject);
1791         } else {
1792           /*
1793            * If we have an SCSI path to the tape and an raw io path
1794            * try to read the Error Counter and the label
1795            */
1796           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1797             {
1798               LogSense(INDEX_TAPE);
1799             }
1800         }
1801       
1802       (void)unload(fd, drive_num, target);
1803       if (ask_clean(scsitapedevice))
1804         clean_tape(fd,tape_device, clean_file, drive_num, clean_slot,
1805                    maxclean, time_file);
1806     }
1807     
1808     if (isempty(fd, slot_offset)) {
1809       g_printf(_("0 slot 0 is empty\n"));
1810       close(fd);
1811       endstatus = 1;
1812       break;
1813     }
1814     
1815     if (load(fd, drive_num, slot_offset) != 0) {
1816       g_printf(_("%d slot %d move failed\n"),
1817              drive_num, slot_offset);  
1818       close(fd);
1819       put_current_slot(slot_file, slot_offset);
1820       endstatus = 2;
1821       break;
1822     }
1823     
1824     put_current_slot(slot_file, slot_offset);
1825     
1826     if (need_sleep)
1827       {
1828         if (pDev[INDEX_TAPECTL].inqdone == 1)
1829           {
1830             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1831               {
1832                 g_printf(_("tape not ready\n"));
1833                 endstatus = 2;
1834                 break;
1835               }
1836           } else {
1837             if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1838               {
1839                 g_printf(_("tape not ready\n"));
1840                 endstatus = 2;
1841                 break;
1842               }          
1843           }
1844       }
1845     
1846     g_printf(_("%d %s\n"), slot_offset, tape_device);
1847     break;
1848
1849   case COM_EJECT:
1850     if (loaded) {
1851       target = get_current_slot(slot_file);
1852       if (target < 0)
1853         {
1854           dbprintf(_("COM_EJECT: get_current_slot %d\n"), target);
1855           target = find_empty(fd, slot_offset, use_slots);
1856           dbprintf(_("COM_EJECT: find_empty %d\n"), target);
1857         }
1858       
1859       if (need_eject)
1860         {
1861           eject_tape(scsitapedevice, need_eject);
1862         } else {
1863           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1864             {
1865               LogSense(INDEX_TAPE);
1866             }
1867         }
1868
1869
1870       (void)unload(fd, drive_num, target);
1871       if (ask_clean(scsitapedevice))
1872         clean_tape(fd, tape_device, clean_file, drive_num, clean_slot,
1873                    maxclean, time_file);
1874       g_printf("%d %s\n", target, tape_device);
1875     } else {
1876       g_printf(_("%d drive was not loaded\n"), target);
1877       endstatus = 1;
1878     }
1879     break;
1880   case COM_CLEAN:
1881     if (loaded) {
1882       target = get_current_slot(slot_file);
1883       if (target < 0)
1884         {
1885           dbprintf(_("COM_CLEAN: get_current_slot %d\n"), target);
1886           target = find_empty(fd, slot_offset, use_slots);
1887           dbprintf(_("COM_CLEAN: find_empty %d\n"),target);
1888         }
1889
1890       if (need_eject)
1891         {
1892           eject_tape(scsitapedevice, need_eject);
1893         } else {
1894           if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1895             {
1896               LogSense(INDEX_TAPE);
1897             }
1898         }
1899
1900       (void)unload(fd, drive_num, target);
1901     } 
1902
1903     clean_tape(fd, tape_device, clean_file, drive_num, clean_slot,
1904                maxclean, time_file);
1905     g_printf(_("%s cleaned\n"), tape_device);
1906     break;
1907   };
1908
1909 /* FIX ME, should be an function to close the device */  
1910 /*   if (pChangerDev != NULL) */
1911 /*     close(pChangerDev->fd); */
1912  
1913 /*   if (pTapeDev != NULL) */
1914 /*     close(pTapeDev->fd); */
1915
1916 /*   if (pTapeDevCtl != NULL) */
1917 /*     close(pTapeDevCtl->fd); */
1918
1919
1920 #endif
1921   if (do_inventory == 1 && endstatus == 0 && changer->labelfile != NULL)
1922     {
1923       if (changer->autoinv == 1)
1924         {
1925           DebugPrint(DEBUG_INFO,SECTION_INFO, _("Do an inventory \n"));
1926           Inventory(changer->labelfile, drive_num, changer->eject,
1927                 0, 0, clean_slot);
1928         } else {
1929           DebugPrint(DEBUG_INFO,SECTION_INFO, _("Set all entrys in DB to invalid\n"));
1930           memset(pbarcoderes, 0 , SIZEOF(MBC_T));
1931           pbarcoderes->action = RESET_VALID;
1932           MapBarCode(changer->labelfile,pbarcoderes);
1933         }
1934     }
1935
1936   DebugPrint(DEBUG_INFO,SECTION_INFO,_("Exit status -> %d\n"), endstatus);
1937   dbclose();
1938   return endstatus;
1939 }
1940 /*
1941  * Local variables:
1942  * indent-tabs-mode: nil
1943  * tab-width: 4
1944  * End:
1945  */