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