b86074de3fbb4fb4509e90c08a92a7db4c3d583e
[debian/amanda] / changer-src / chg-scsi-chio.c
1 /*
2  *  $Id: chg-scsi-chio.c,v 1.8 2006/01/14 04:37:18 paddy_s Exp $
3  *
4  *  chg-scsi.c -- generic SCSI changer driver
5  *
6  *  This program provides a driver to control generic
7  *  SCSI changers, no matter what platform.  The host/OS
8  *  specific portions of the interface are implemented
9  *  in libscsi.a, which contains a module for each host/OS.
10  *  The actual interface for HP/UX is in scsi-hpux.c;
11  *  chio is in scsi-chio.c, etc..  A prototype system
12  *  dependent scsi interface file is in scsi-proto.c.
13  *
14  *  Copyright 1997, 1998 Eric Schnoebelen <eric@cirr.com>
15  *
16  * This module based upon seagate-changer, by Larry Pyeatt
17  *                  <pyeatt@cs.colostate.edu>
18  *
19  * The original introductory comments follow:
20  *
21  * This program was written to control the Seagate/Conner/Archive
22  * autoloading DAT drive.  This drive normally has 4 tape capacity
23  * but can be expanded to 12 tapes with an optional tape cartridge.
24  * This program may also work on onther drives.  Try it and let me
25  * know of successes/failures.
26  *
27  * I have attempted to conform to the requirements for Amanda tape
28  * changer interface.  There could be some bugs.  
29  *
30  * This program works for me under Linux with Gerd Knorr's 
31  * <kraxel@cs.tu-berlin.de> SCSI media changer driver installed 
32  * as a kernel module.  The kernel module is available at 
33  * http://sunsite.unc.edu/pub/Linux/kernel/patches/scsi/scsi-changer*
34  * Since the Linux media changer is based on NetBSD, this program
35  * should also work for NetBSD, although I have not tried it.
36  * It may be necessary to change the IOCTL calls to work on other
37  * OS's.  
38  *
39  * (c) 1897 Larry Pyeatt,  pyeatt@cs.colostate.edu 
40  * All Rights Reserved.
41  * 
42  * Permission to use, copy, modify, distribute, and sell this software and its
43  * documentation for any purpose is hereby granted without fee, provided that
44  * the above copyright notice appear in all copies and that both that
45  * copyright notice and this permission notice appear in supporting
46  * documentation.  The author makes no representations about the
47  * suitability of this software for any purpose.   It is provided "as is"
48  * without express or implied warranty.
49  *
50  * Michael C. Povel 03.06.98 added ejetct_tape and sleep for external tape
51  * devices, and changed some code to allow multiple drives to use their
52  * own slots. Also added support for reserverd slots.
53  * At the moment these parameters are hard coded and only tested under Linux
54  * 
55  */
56
57 #include "config.h"
58 #include "amanda.h"
59 #include "conffile.h"
60 #include "libscsi.h"
61
62 char *tapestatfile = NULL;
63
64 /*----------------------------------------------------------------------------*/
65 /* Some stuff for our own configurationfile */
66 typedef struct {  /* The information we can get for any drive (configuration) */
67   int drivenum;      /* Which drive to use in the library */
68   int start;         /* Which is the first slot we may use */
69   int end;           /* The last slot we are allowed to use */
70   int cleanslot;     /* Where the cleaningcartridge stays */
71   char *scsitapedev; /* Where can we send raw SCSI commands to the tape */
72   char *device;      /* Which device is associated to the drivenum */
73   char *slotfile;    /* Where we should have our memory */   
74   char *cleanfile;   /* Where we count how many cleanings we did */
75   char *timefile;    /* Where we count the time the tape was used*/
76   char *tapestatfile;/* Where can we place some drive stats */
77 }config_t; 
78
79 typedef struct {
80   int number_of_configs; /* How many different configurations are used */
81   int eject;             /* Do the drives need an eject-command */
82   int sleep;             /* How many seconds to wait for the drive to get ready */
83   int cleanmax;          /* How many runs could be done with one cleaning tape */
84   char *device;          /* Which device is our changer */
85   config_t *conf;
86 }changer_t;
87
88 typedef enum{
89   NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM,
90     CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE
91     } token_t;
92
93 typedef struct {
94   char *word;
95   int token;
96 } tokentable_t;
97
98 tokentable_t t_table[]={
99   { "number_configs",NUMDRIVE},
100   { "eject",EJECT},
101   { "sleep",SLEEP},
102   { "cleanmax",CLEANMAX},
103   { "config",DRIVE},
104   { "startuse",START},
105   { "enduse",END},
106   { "cleancart",CLEAN},
107   { "dev",DEVICE},
108   { "statfile",STATFILE},
109   { "cleanfile",CLEANFILE},
110   { "drivenum",DRIVENUM},
111   { "changerdev",CHANGERDEV},
112   { "usagecount",USAGECOUNT},
113   { "scsitapedev", SCSITAPEDEV},
114   { "tapestatus", TAPESTATFILE},
115   { NULL,-1 }
116 };
117
118 void init_changer_struct(changer_t *chg,int number_of_config)
119      /* Initialize datasructures with default values */
120 {
121   int i;
122   
123   chg->number_of_configs = number_of_config;
124   chg->eject = 1;
125   chg->sleep = 0;
126   chg->cleanmax = 0;
127   chg->device = NULL;
128   chg->conf = malloc(sizeof(config_t)*number_of_config);
129   if (chg->conf != NULL){
130     for (i=0; i < number_of_config; i++){
131       chg->conf[i].drivenum   = 0;
132       chg->conf[i].start      = -1;
133       chg->conf[i].end        = -1;
134       chg->conf[i].cleanslot  = -1;
135       chg->conf[i].device     = NULL;
136       chg->conf[i].slotfile   = NULL;
137       chg->conf[i].cleanfile  = NULL;
138       chg->conf[i].timefile  = NULL;
139       chg->conf[i].scsitapedev = NULL;
140       chg->conf[i].tapestatfile = NULL;
141     }
142   }
143 }
144
145 void dump_changer_struct(changer_t chg)
146      /* Dump of information for debug */
147 {
148   int i;
149
150   dbprintf(("Number of configurations: %d\n",chg.number_of_configs));
151   dbprintf(("Tapes need eject: %s\n",(chg.eject>0?"Yes":"No")));
152   dbprintf(("Tapes need sleep: %d seconds\n",chg.sleep));
153   dbprintf(("Cleancycles     : %d\n",chg.cleanmax));
154   dbprintf(("Changerdevice   : %s\n",chg.device));
155   for (i=0; i<chg.number_of_configs; i++){
156     dbprintf(("Tapeconfig Nr: %d\n",i));
157     dbprintf(("  Drivenumber   : %d\n",chg.conf[i].drivenum));
158     dbprintf(("  Startslot     : %d\n",chg.conf[i].start));
159     dbprintf(("  Endslot       : %d\n",chg.conf[i].end));
160     dbprintf(("  Cleanslot     : %d\n",chg.conf[i].cleanslot));
161     if (chg.conf[i].device != NULL)
162       dbprintf(("  Devicename    : %s\n",chg.conf[i].device));
163     else
164       dbprintf(("  Devicename    : none\n"));
165     if (chg.conf[i].scsitapedev != NULL)
166       dbprintf(("  SCSITapedev   : %s\n",chg.conf[i].scsitapedev));
167     else
168       dbprintf(("  SCSITapedev   : none\n"));
169     if (chg.conf[i].tapestatfile != NULL)
170       dbprintf(("  statfile      : %s\n", chg.conf[i].tapestatfile));
171     else
172       dbprintf(("  statfile      : none\n"));
173     if (chg.conf[i].slotfile != NULL)
174       dbprintf(("  Slotfile      : %s\n",chg.conf[i].slotfile));
175     else
176       dbprintf(("  Slotfile      : none\n"));
177     if (chg.conf[i].cleanfile != NULL)
178       dbprintf(("  Cleanfile     : %s\n",chg.conf[i].cleanfile));
179     else
180       dbprintf(("  Cleanfile     : none\n"));
181     if (chg.conf[i].timefile != NULL)
182       dbprintf(("  Usagecount    : %s\n",chg.conf[i].timefile));
183     else
184       dbprintf(("  Usagecount    : none\n"));
185   }
186 }
187
188 void free_changer_struct(changer_t *chg)
189      /* Free all allocated memory */
190 {
191   int i;
192
193   if (chg->device != NULL)
194     free(chg->device);
195   for (i=0; i<chg->number_of_configs; i++){
196     if (chg->conf[i].device != NULL)
197       free(chg->conf[i].device);
198     if (chg->conf[i].slotfile != NULL)
199       free(chg->conf[i].slotfile);
200     if (chg->conf[i].cleanfile != NULL)
201       free(chg->conf[i].cleanfile);
202     if (chg->conf[i].timefile != NULL)
203       free(chg->conf[i].timefile);
204   }
205   if (chg->conf != NULL)
206     free(chg->conf);
207   chg->conf = NULL;
208   chg->device = NULL;
209 }
210
211 void parse_line(char *linebuffer,int *token,char **value)
212      /* This function parses a line, and returns the token an value */
213 {
214   char *tok;
215   int i;
216   int ready = 0;
217   *token = -1;  /* No Token found */
218   tok=strtok(linebuffer," \t\n");
219
220   while ((tok != NULL) && (tok[0]!='#')&&(ready==0)){
221     if (*token != -1){
222       *value=tok;
223       ready=1;
224     } else {
225       i=0;
226       while ((t_table[i].word != NULL)&&(*token==-1)){
227         if (0==strcasecmp(t_table[i].word,tok)){
228           *token=t_table[i].token;
229         }
230         i++;
231       }
232     }
233     tok=strtok(NULL," \t\n");
234   }
235   return;
236 }
237
238 int read_config(char *configfile, changer_t *chg)
239      /* This function reads the specified configfile and fills the structure */
240 {
241   int numconf;
242   FILE *file;
243   int init_flag = 0;
244   int drivenum=0;
245   char *linebuffer = NULL;
246   int token;
247   char *value;
248
249   numconf = 1;  /* At least one configuration is assumed */
250   /* If there are more, it should be the first entry in the configurationfile */
251
252   if (NULL==(file=fopen(configfile,"r"))){
253     return (-1);
254   }
255
256   amfree(linebuffer);
257   while (NULL!=(linebuffer=agets(file))){
258       parse_line(linebuffer,&token,&value);
259       if (token != -1){
260         if (0==init_flag) {
261           if (token != NUMDRIVE){
262             init_changer_struct(chg,numconf);
263           } else {
264             numconf = atoi(value);
265             init_changer_struct(chg,numconf);
266           }
267           init_flag=1;
268         }
269         switch (token){
270         case NUMDRIVE: if (atoi(value) != numconf)
271           fprintf(stderr,"Error: number_drives at wrong place, should be "\
272                   "first in file\n");
273         break;
274         case EJECT:
275           chg->eject = atoi(value);
276           break;
277         case SLEEP:
278           chg->sleep = atoi(value);
279           break;
280         case CHANGERDEV:
281           chg->device = stralloc(value);
282           break;
283         case SCSITAPEDEV:
284           chg->conf[drivenum].scsitapedev = stralloc(value);
285           break;
286         case TAPESTATFILE:
287           chg->conf[drivenum].tapestatfile = stralloc(value);
288           break;
289         case CLEANMAX:
290           chg->cleanmax = atoi(value);
291           break;
292         case DRIVE:
293           drivenum = atoi(value);
294           if(drivenum >= numconf){
295             fprintf(stderr,"Error: drive must be less than number_drives\n");
296           }
297           break;
298         case DRIVENUM:
299           if (drivenum < numconf){
300             chg->conf[drivenum].drivenum = atoi(value);
301           } else {
302             fprintf(stderr,"Error: drive is not less than number_drives"\
303                     " drivenum ignored\n");
304           }
305           break;
306         case START:
307           if (drivenum < numconf){
308             chg->conf[drivenum].start = atoi(value);
309           } else {
310             fprintf(stderr,"Error: drive is not less than number_drives"\
311                     " startuse ignored\n");
312           }
313           break;
314         case END:
315           if (drivenum < numconf){
316             chg->conf[drivenum].end = atoi(value);
317           } else {
318             fprintf(stderr,"Error: drive is not less than number_drives"\
319                     " enduse ignored\n");
320           }
321           break;
322         case CLEAN:
323           if (drivenum < numconf){
324             chg->conf[drivenum].cleanslot = atoi(value);
325           } else {
326             fprintf(stderr,"Error: drive is not less than number_drives"\
327                     " cleanslot ignored\n");
328           }
329           break;
330         case DEVICE:
331           if (drivenum < numconf){
332             chg->conf[drivenum].device = stralloc(value);
333           } else {
334             fprintf(stderr,"Error: drive is not less than number_drives"\
335                     " device ignored\n");
336           }
337           break;
338         case STATFILE:
339           if (drivenum < numconf){
340             chg->conf[drivenum].slotfile = stralloc(value);
341           } else {
342             fprintf(stderr,"Error: drive is not less than number_drives"\
343                     " slotfile ignored\n");
344           }
345           break;
346         case CLEANFILE:
347           if (drivenum < numconf){
348             chg->conf[drivenum].cleanfile = stralloc(value);
349           } else {
350             fprintf(stderr,"Error: drive is not less than number_drives"\
351                     " cleanfile ignored\n");
352           }
353           break;
354         case USAGECOUNT:
355           if (drivenum < numconf){
356             chg->conf[drivenum].timefile = stralloc(value);
357           } else {
358             fprintf(stderr,"Error: drive is not less than number_drives"\
359                     " usagecount ignored\n");
360           }
361           break;
362         default:
363           fprintf(stderr,"Error: Unknown token\n");
364           break;
365         }
366       }
367     amfree(linebuffer);
368   }
369   amfree(linebuffer);
370
371   fclose(file);
372   return 0;
373 }
374
375 /*----------------------------------------------------------------------------*/
376
377 /*  The tape drive does not have an idea of current slot so
378  *  we use a file to store the current slot.  It is not ideal
379  *  but it gets the job done  
380  */
381 int get_current_slot(char *count_file)
382 {
383   FILE *inf;
384   int retval;
385   if ((inf=fopen(count_file,"r")) == NULL) {
386     fprintf(stderr, "%s: unable to open current slot file (%s)\n",
387             get_pname(), count_file);
388     return 0;
389   }
390   fscanf(inf,"%d",&retval);
391   fclose(inf);
392   return retval;
393 }
394
395 void put_current_slot(char *count_file,int slot)
396 {
397   FILE *inf;
398
399   if ((inf=fopen(count_file,"w")) == NULL) {
400     fprintf(stderr, "%s: unable to open current slot file (%s)\n",
401             get_pname(), count_file);
402     exit(2);
403   }
404   fprintf(inf, "%d\n", slot);
405   fclose(inf);
406 }
407
408 /* ---------------------------------------------------------------------- 
409    This stuff deals with parsing the command line */
410
411 typedef struct com_arg
412 {
413   char *str;
414   int command_code;
415   int takesparam;
416 } argument;
417
418
419 typedef struct com_stru
420 {
421   int command_code;
422   char *parameter;
423 } command;
424
425
426 /* major command line args */
427 #define COMCOUNT 5
428 #define COM_SLOT 0
429 #define COM_INFO 1
430 #define COM_RESET 2
431 #define COM_EJECT 3
432 #define COM_CLEAN 4
433 argument argdefs[]={{"-slot",COM_SLOT,1},
434                     {"-info",COM_INFO,0},
435                     {"-reset",COM_RESET,0},
436                     {"-eject",COM_EJECT,0},
437                     {"-clean",COM_CLEAN,0}};
438
439
440 /* minor command line args */
441 #define SLOTCOUNT 5
442 #define SLOT_CUR 0
443 #define SLOT_NEXT 1
444 #define SLOT_PREV 2
445 #define SLOT_FIRST 3
446 #define SLOT_LAST 4
447 argument slotdefs[]={{"current",SLOT_CUR,0},
448                      {"next",SLOT_NEXT,0},
449                      {"prev",SLOT_PREV,0},
450                      {"first",SLOT_FIRST,0},
451                      {"last",SLOT_LAST,0}};
452
453 int is_positive_number(char *tmp) /* is the string a valid positive int? */
454 {
455   int i=0;
456   if ((tmp==NULL)||(tmp[0]==0))
457     return 0;
458   while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
459     i++;
460   if (tmp[i]==0)
461     return 1;
462   else
463     return 0;
464 }
465
466 void usage(char *argv[])
467 {
468   int cnt;
469   printf("%s: Usage error.\n", argv[0]);
470   for (cnt=0; cnt < COMCOUNT; cnt++){
471     printf("      %s    %s",argv[0],argdefs[cnt].str);
472     if (argdefs[cnt].takesparam)
473       printf(" <param>\n");
474     else
475       printf("\n");
476   }
477   exit(2);
478 }
479
480
481 void parse_args(int argc, char *argv[],command *rval)
482 {
483   int i=0;
484   if ((argc<2)||(argc>3))
485     usage(argv);
486   while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
487     i++;
488   if (i==COMCOUNT)
489     usage(argv);
490   rval->command_code = argdefs[i].command_code;
491   if (argdefs[i].takesparam) {
492     if (argc<3)
493       usage(argv);
494     rval->parameter=argv[2];      
495   }
496   else {
497     if (argc>2)
498       usage(argv);
499     rval->parameter=0;
500   }
501 }
502
503 /* used to find actual slot number from keywords next, prev, first, etc */
504 int get_relative_target(int fd,int nslots,char *parameter,int loaded, 
505                         char *changer_file,int slot_offset,int maxslot)
506 {
507   int current_slot,i;
508   if (changer_file != NULL)
509     {
510       current_slot=get_current_slot(changer_file);
511     } else {
512       current_slot =   GetCurrentSlot(fd, 0);
513     }
514   if (current_slot > maxslot){
515     current_slot = slot_offset;
516   }
517   if (current_slot < slot_offset){
518     current_slot = slot_offset;
519   }
520
521   i=0;
522   while((i<SLOTCOUNT)&&(strcmp(slotdefs[i].str,parameter)))
523     i++;
524
525   switch(i) {
526   case SLOT_CUR:
527     return current_slot;
528     break;
529   case SLOT_NEXT:
530     if (++current_slot==nslots+slot_offset)
531       return slot_offset;
532     else
533       return current_slot;
534     break;
535   case SLOT_PREV:
536     if (--current_slot<slot_offset)
537       return maxslot;
538     else
539       return current_slot;
540     break;
541   case SLOT_FIRST:
542     return slot_offset;
543     break;
544   case SLOT_LAST:
545     return maxslot;
546     break;
547   default: 
548     printf("<none> no slot `%s'\n",parameter);
549     close(fd);
550     exit(2);
551   };
552 }
553
554 int ask_clean(char *tapedev)
555      /* This function should ask the drive if it wants to be cleaned */
556 {
557   return get_clean_state(tapedev);
558 }
559
560 void clean_tape(int fd,char *tapedev,char *cnt_file, int drivenum, 
561                 int cleancart, int maxclean,char *usagetime)
562      /* This function should move the cleaning cartridge into the drive */
563 {
564   int counter=-1;
565   if (cleancart == -1 ){
566     return;
567   }
568   /* Now we should increment the counter */
569   if (cnt_file != NULL){
570     counter = get_current_slot(cnt_file);
571     counter++;
572     if (counter>=maxclean){
573       /* Now we should inform the administrator */
574       char *mail_cmd;
575       FILE *mailf;
576       mail_cmd = vstralloc(MAILER,
577                            " -s", " \"", "AMANDA PROBLEM: PLEASE FIX", "\"",
578                            " ", getconf_str(CNF_MAILTO),
579                            NULL);
580       if((mailf = popen(mail_cmd, "w")) == NULL){
581         error("could not open pipe to \"%s\": %s",
582               mail_cmd, strerror(errno));
583         printf("Mail failed\n");
584         return;
585       }
586       fprintf(mailf,"\nThe usage count of your cleaning tape in slot %d",
587               cleancart);
588       fprintf(mailf,"\nis more than %d. (cleanmax)",maxclean);
589       fprintf(mailf,"\nTapedrive %s needs to be cleaned",tapedev);
590       fprintf(mailf,"\nPlease insert a new cleaning tape and reset");
591       fprintf(mailf,"\nthe countingfile %s",cnt_file);
592
593       if(pclose(mailf) != 0)
594         error("mail command failed: %s", mail_cmd);
595
596       return;
597     }
598     put_current_slot(cnt_file,counter);
599   }
600   load(fd,drivenum,cleancart);
601   
602   if (drive_loaded(fd, drivenum))
603     unload(fd,drivenum,cleancart);  
604   unlink(usagetime);
605 }
606 /* ----------------------------------------------------------------------*/
607
608 int main(int argc, char *argv[])
609 {
610   int loaded,target,oldtarget;
611   command com;   /* a little DOS joke */
612   changer_t chg;
613   
614   /*
615    * drive_num really should be something from the config file, but..
616    * for now, it is set to zero, since most of the common changers
617    * used by amanda only have one drive ( until someone wants to 
618    * use an EXB60/120, or a Breece Hill Q45.. )
619    */
620   int    drive_num = 0;
621   int need_eject = 0; /* Does the drive need an eject command ? */
622   int need_sleep = 0; /* How many seconds to wait for the drive to get ready */
623   int clean_slot = -1;
624   int maxclean = 0;
625   char *clean_file=NULL;
626   char *time_file=NULL;
627
628   int use_slots;
629   int slot_offset;
630   int confnum;
631
632   int fd, rc, slotcnt, drivecnt;
633   int endstatus = 0;
634   char *changer_dev, *tape_device;
635   char *changer_file = NULL;
636   char *scsitapedevice = NULL;
637
638   set_pname("chg-scsi");
639
640   /* Don't die when child closes pipe */
641   signal(SIGPIPE, SIG_IGN);
642
643   dbopen();
644   parse_args(argc,argv,&com);
645
646   if(read_conffile(CONFFILE_NAME)) {
647     fprintf(stderr, "%s: could not find config file \"%s\"",
648                     changer_dev, conffile);
649     exit(1);
650   }
651
652   changer_dev = getconf_str(CNF_CHNGRDEV);
653   changer_file = getconf_str(CNF_CHNGRFILE);
654   tape_device = getconf_str(CNF_TAPEDEV);
655
656   /* Get the configuration parameters */
657   if (strlen(tape_device)==1){
658     read_config(changer_file,&chg);
659     confnum=atoi(tape_device);
660     use_slots    = chg.conf[confnum].end-chg.conf[confnum].start+1;
661     slot_offset  = chg.conf[confnum].start;
662     drive_num    = chg.conf[confnum].drivenum;
663     need_eject   = chg.eject;
664     need_sleep   = chg.sleep;
665     clean_file   = stralloc(chg.conf[confnum].cleanfile);
666     clean_slot   = chg.conf[confnum].cleanslot;
667     maxclean     = chg.cleanmax;
668     if (NULL != chg.conf[confnum].timefile)
669       time_file = stralloc(chg.conf[confnum].timefile);
670     if (NULL != chg.conf[confnum].slotfile)
671       changer_file = stralloc(chg.conf[confnum].slotfile);
672     else
673       changer_file = NULL;
674     if (NULL != chg.conf[confnum].device)
675       tape_device  = stralloc(chg.conf[confnum].device);
676     if (NULL != chg.device)
677       changer_dev  = stralloc(chg.device); 
678     if (NULL != chg.conf[confnum].scsitapedev)
679       scsitapedevice = stralloc(chg.conf[confnum].scsitapedev);
680     if (NULL != chg.conf[confnum].tapestatfile)
681       tapestatfile = stralloc(chg.conf[confnum].tapestatfile);
682     dump_changer_struct(chg);
683     /* get info about the changer */
684     if (-1 == (fd = OpenDevice(changer_dev, "changer_dev"))) {
685       int localerr = errno;
686       fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
687               changer_dev, strerror(localerr));
688       printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
689       dbprintf(("%s: open: %s: %s\n", get_pname(),
690                 changer_dev, strerror(localerr)));
691       return 2;
692     }
693
694     if (tape_device == NULL)
695       {
696         tape_device = stralloc(changer_dev);
697       }
698
699     if (scsitapedevice == NULL)
700       {
701          scsitapedevice = stralloc(tape_device);
702       }
703
704     if ((chg.conf[confnum].end == -1) || (chg.conf[confnum].start == -1)){
705       slotcnt = get_slot_count(fd);
706       use_slots    = slotcnt;
707       slot_offset  = 0;
708     }
709     free_changer_struct(&chg);
710   } else {
711     /* get info about the changer */
712     if (-1 == (fd = OpenDevice(changer_dev))) {
713       int localerr = errno;
714       fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
715               changer_dev, strerror(localerr));
716       printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
717       dbprintf(("%s: open: %s: %s\n", get_pname(),
718                 changer_dev, strerror(localerr)));
719       return 2;
720     }
721     slotcnt = get_slot_count(fd);
722     use_slots    = slotcnt;
723     slot_offset  = 0;
724     drive_num    = 0;
725     need_eject   = 0;
726     need_sleep   = 0;
727   }
728
729   drivecnt = get_drive_count(fd);
730
731   if (drive_num > drivecnt) {
732     printf("%s drive number error (%d > %d)\n", "<none>", 
733            drive_num, drivecnt);
734     fprintf(stderr, "%s: requested drive number (%d) greater than "
735             "number of supported drives (%d)\n", get_pname(), 
736             drive_num, drivecnt);
737     dbprintf(("%s: requested drive number (%d) greater than "
738               "number of supported drives (%d)\n", get_pname(), 
739               drive_num, drivecnt));
740     CloseDevice("", fd);
741     return 2;
742   }
743
744   loaded = drive_loaded(fd, drive_num);
745
746   switch(com.command_code) {
747   case COM_SLOT:  /* slot changing command */
748     if (is_positive_number(com.parameter)) {
749       if ((target = atoi(com.parameter))>=use_slots) {
750         printf("<none> no slot `%d'\n",target);
751         close(fd);
752         endstatus = 2;
753         break;
754       } else {
755         target = target+slot_offset;
756       }
757     } else
758       target=get_relative_target(fd, use_slots,
759                                  com.parameter,
760                                  loaded, 
761                                  changer_file,slot_offset,slot_offset+use_slots);
762     if (loaded) {
763       if (changer_file != NULL)
764         {
765           oldtarget=get_current_slot(changer_file);
766         } else {
767           oldtarget = GetCurrentSlot(fd, drive_num);
768         }
769       if ((oldtarget)!=target) {
770         if (need_eject)
771           eject_tape(scsitapedevice, need_eject);
772         (void)unload(fd, drive_num, oldtarget);
773         if (ask_clean(scsitapedevice))
774           clean_tape(fd,tape_device,clean_file,drive_num,
775                      clean_slot,maxclean,time_file);
776         loaded=0;
777       }
778     }
779     if (changer_file != NULL)
780       {
781       put_current_slot(changer_file, target);
782     }
783     if (!loaded && isempty(fd, target)) {
784       printf("%d slot %d is empty\n",target-slot_offset,
785              target-slot_offset);
786       close(fd);
787       endstatus = 1;
788       break;
789     }
790     if (!loaded)
791       if (load(fd, drive_num, target) != 0) {
792         printf("%d slot %d move failed\n",target-slot_offset,
793                target-slot_offset);  
794         close(fd);
795         endstatus = 2;
796         break;
797       }
798     if (need_sleep)
799       Tape_Ready(scsitapedevice, need_sleep);
800     printf("%d %s\n", target-slot_offset, tape_device);
801     break;
802
803   case COM_INFO:
804     if (changer_file != NULL)
805       {
806         printf("%d ", get_current_slot(changer_file)-slot_offset);
807       } else {
808         printf("%d ", GetCurrentSlot(fd, drive_num)-slot_offset);
809       }
810     printf("%d 1\n", use_slots);
811     break;
812
813   case COM_RESET:
814     if (changer_file != NULL)
815       {
816         target=get_current_slot(changer_file);
817       } else {
818         target = GetCurrentSlot(fd, drive_num);
819       }
820     if (loaded) {
821       if (!isempty(fd, target))
822         target=find_empty(fd,0 ,0);
823       if (need_eject)
824         eject_tape(scsitapedevice, need_eject);
825       (void)unload(fd, drive_num, target);
826       if (ask_clean(scsitapedevice))
827         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
828                    maxclean,time_file);
829     }
830
831     if (isempty(fd, slot_offset)) {
832       printf("0 slot 0 is empty\n");
833       close(fd);
834       endstatus = 1;
835       break;
836     }
837
838     if (load(fd, drive_num, slot_offset) != 0) {
839       printf("%d slot %d move failed\n",slot_offset,
840              slot_offset);  
841       close(fd);
842       endstatus = 2;
843       break;
844     }
845     if (changer_file != NULL)
846     {
847       put_current_slot(changer_file, slot_offset);
848     }
849     if (need_sleep)
850       Tape_Ready(scsitapedevice, need_sleep);
851     if (changer_file != NULL)
852       {
853         printf("%d %s\n", get_current_slot(changer_file), tape_device);
854       } else {
855         printf("%d %s\n", GetCurrentSlot(fd, drive_num), tape_device);
856       }
857     break;
858
859   case COM_EJECT:
860     if (loaded) {
861       if (changer_file != NULL)
862         {
863           target=get_current_slot(changer_file);
864         } else {
865           target = GetCurrentSlot(fd, drive_num);
866         }
867       if (need_eject)
868         eject_tape(scsitapedevice, need_eject);
869       (void)unload(fd, drive_num, target);
870       if (ask_clean(scsitapedevice))
871         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
872                    maxclean,time_file);
873       printf("%d %s\n", target, tape_device);
874     } else {
875       printf("%d %s\n", target, "drive was not loaded");
876       endstatus = 1;
877     }
878     break;
879   case COM_CLEAN:
880     if (loaded) {
881       if (changer_file  != NULL)
882         {
883           target=get_current_slot(changer_file);
884         } else {
885           target = GetCurrentSlot(fd, drive_num);
886         }
887       if (need_eject)
888         eject_tape(scsitapedevice, need_eject);
889       (void)unload(fd, drive_num, target);
890     } 
891     clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
892                maxclean,time_file);
893     printf("%s cleaned\n", tape_device);
894     break;
895   };
896
897   CloseDevice("", 0);
898   dbclose();
899   return endstatus;
900 }
901 /*
902  * Local variables:
903  * indent-tabs-mode: nil
904  * tab-width: 4
905  * End:
906  */