Imported Upstream version 2.4.4p3
[debian/amanda] / changer-src / chg-scsi-chio.c
1 /*
2  *  $Id: chg-scsi-chio.c,v 1.1.2.3.6.3 2003/01/26 19:20:56 martinea 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   dbopen();
640   parse_args(argc,argv,&com);
641
642   if(read_conffile(CONFFILE_NAME)) {
643     fprintf(stderr, "%s: could not find config file \"%s\"",
644                     changer_dev, conffile);
645     exit(1);
646   }
647
648   changer_dev = getconf_str(CNF_CHNGRDEV);
649   changer_file = getconf_str(CNF_CHNGRFILE);
650   tape_device = getconf_str(CNF_TAPEDEV);
651
652   /* Get the configuration parameters */
653   if (strlen(tape_device)==1){
654     read_config(changer_file,&chg);
655     confnum=atoi(tape_device);
656     use_slots    = chg.conf[confnum].end-chg.conf[confnum].start+1;
657     slot_offset  = chg.conf[confnum].start;
658     drive_num    = chg.conf[confnum].drivenum;
659     need_eject   = chg.eject;
660     need_sleep   = chg.sleep;
661     clean_file   = stralloc(chg.conf[confnum].cleanfile);
662     clean_slot   = chg.conf[confnum].cleanslot;
663     maxclean     = chg.cleanmax;
664     if (NULL != chg.conf[confnum].timefile)
665       time_file = stralloc(chg.conf[confnum].timefile);
666     if (NULL != chg.conf[confnum].slotfile)
667       changer_file = stralloc(chg.conf[confnum].slotfile);
668     else
669       changer_file = NULL;
670     if (NULL != chg.conf[confnum].device)
671       tape_device  = stralloc(chg.conf[confnum].device);
672     if (NULL != chg.device)
673       changer_dev  = stralloc(chg.device); 
674     if (NULL != chg.conf[confnum].scsitapedev)
675       scsitapedevice = stralloc(chg.conf[confnum].scsitapedev);
676     if (NULL != chg.conf[confnum].tapestatfile)
677       tapestatfile = stralloc(chg.conf[confnum].tapestatfile);
678     dump_changer_struct(chg);
679     /* get info about the changer */
680     if (-1 == (fd = OpenDevice(changer_dev, "changer_dev"))) {
681       int localerr = errno;
682       fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
683               changer_dev, strerror(localerr));
684       printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
685       dbprintf(("%s: open: %s: %s\n", get_pname(),
686                 changer_dev, strerror(localerr)));
687       return 2;
688     }
689
690     if (tape_device == NULL)
691       {
692         tape_device = stralloc(changer_dev);
693       }
694
695     if (scsitapedevice == NULL)
696       {
697          scsitapedevice = stralloc(tape_device);
698       }
699
700     if ((chg.conf[confnum].end == -1) || (chg.conf[confnum].start == -1)){
701       slotcnt = get_slot_count(fd);
702       use_slots    = slotcnt;
703       slot_offset  = 0;
704     }
705     free_changer_struct(&chg);
706   } else {
707     /* get info about the changer */
708     if (-1 == (fd = OpenDevice(changer_dev))) {
709       int localerr = errno;
710       fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
711               changer_dev, strerror(localerr));
712       printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
713       dbprintf(("%s: open: %s: %s\n", get_pname(),
714                 changer_dev, strerror(localerr)));
715       return 2;
716     }
717     slotcnt = get_slot_count(fd);
718     use_slots    = slotcnt;
719     slot_offset  = 0;
720     drive_num    = 0;
721     need_eject   = 0;
722     need_sleep   = 0;
723   }
724
725   drivecnt = get_drive_count(fd);
726
727   if (drive_num > drivecnt) {
728     printf("%s drive number error (%d > %d)\n", "<none>", 
729            drive_num, drivecnt);
730     fprintf(stderr, "%s: requested drive number (%d) greater than "
731             "number of supported drives (%d)\n", get_pname(), 
732             drive_num, drivecnt);
733     dbprintf(("%s: requested drive number (%d) greater than "
734               "number of supported drives (%d)\n", get_pname(), 
735               drive_num, drivecnt));
736     CloseDevice("", fd);
737     return 2;
738   }
739
740   loaded = drive_loaded(fd, drive_num);
741
742   switch(com.command_code) {
743   case COM_SLOT:  /* slot changing command */
744     if (is_positive_number(com.parameter)) {
745       if ((target = atoi(com.parameter))>=use_slots) {
746         printf("<none> no slot `%d'\n",target);
747         close(fd);
748         endstatus = 2;
749         break;
750       } else {
751         target = target+slot_offset;
752       }
753     } else
754       target=get_relative_target(fd, use_slots,
755                                  com.parameter,
756                                  loaded, 
757                                  changer_file,slot_offset,slot_offset+use_slots);
758     if (loaded) {
759       if (changer_file != NULL)
760         {
761           oldtarget=get_current_slot(changer_file);
762         } else {
763           oldtarget = GetCurrentSlot(fd, drive_num);
764         }
765       if ((oldtarget)!=target) {
766         if (need_eject)
767           eject_tape(scsitapedevice, need_eject);
768         (void)unload(fd, drive_num, oldtarget);
769         if (ask_clean(scsitapedevice))
770           clean_tape(fd,tape_device,clean_file,drive_num,
771                      clean_slot,maxclean,time_file);
772         loaded=0;
773       }
774     }
775     if (changer_file != NULL)
776       {
777       put_current_slot(changer_file, target);
778     }
779     if (!loaded && isempty(fd, target)) {
780       printf("%d slot %d is empty\n",target-slot_offset,
781              target-slot_offset);
782       close(fd);
783       endstatus = 1;
784       break;
785     }
786     if (!loaded)
787       if (load(fd, drive_num, target) != 0) {
788         printf("%d slot %d move failed\n",target-slot_offset,
789                target-slot_offset);  
790         close(fd);
791         endstatus = 2;
792         break;
793       }
794     if (need_sleep)
795       Tape_Ready(scsitapedevice, need_sleep);
796     printf("%d %s\n", target-slot_offset, tape_device);
797     break;
798
799   case COM_INFO:
800     if (changer_file != NULL)
801       {
802         printf("%d ", get_current_slot(changer_file)-slot_offset);
803       } else {
804         printf("%d ", GetCurrentSlot(fd, drive_num)-slot_offset);
805       }
806     printf("%d 1\n", use_slots);
807     break;
808
809   case COM_RESET:
810     if (changer_file != NULL)
811       {
812         target=get_current_slot(changer_file);
813       } else {
814         target = GetCurrentSlot(fd, drive_num);
815       }
816     if (loaded) {
817       if (!isempty(fd, target))
818         target=find_empty(fd,0 ,0);
819       if (need_eject)
820         eject_tape(scsitapedevice, need_eject);
821       (void)unload(fd, drive_num, target);
822       if (ask_clean(scsitapedevice))
823         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
824                    maxclean,time_file);
825     }
826
827     if (isempty(fd, slot_offset)) {
828       printf("0 slot 0 is empty\n");
829       close(fd);
830       endstatus = 1;
831       break;
832     }
833
834     if (load(fd, drive_num, slot_offset) != 0) {
835       printf("%d slot %d move failed\n",slot_offset,
836              slot_offset);  
837       close(fd);
838       endstatus = 2;
839       break;
840     }
841     if (changer_file != NULL)
842     {
843       put_current_slot(changer_file, slot_offset);
844     }
845     if (need_sleep)
846       Tape_Ready(scsitapedevice, need_sleep);
847     if (changer_file != NULL)
848       {
849         printf("%d %s\n", get_current_slot(changer_file), tape_device);
850       } else {
851         printf("%d %s\n", GetCurrentSlot(fd, drive_num), tape_device);
852       }
853     break;
854
855   case COM_EJECT:
856     if (loaded) {
857       if (changer_file != NULL)
858         {
859           target=get_current_slot(changer_file);
860         } else {
861           target = GetCurrentSlot(fd, drive_num);
862         }
863       if (need_eject)
864         eject_tape(scsitapedevice, need_eject);
865       (void)unload(fd, drive_num, target);
866       if (ask_clean(scsitapedevice))
867         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
868                    maxclean,time_file);
869       printf("%d %s\n", target, tape_device);
870     } else {
871       printf("%d %s\n", target, "drive was not loaded");
872       endstatus = 1;
873     }
874     break;
875   case COM_CLEAN:
876     if (loaded) {
877       if (changer_file  != NULL)
878         {
879           target=get_current_slot(changer_file);
880         } else {
881           target = GetCurrentSlot(fd, drive_num);
882         }
883       if (need_eject)
884         eject_tape(scsitapedevice, need_eject);
885       (void)unload(fd, drive_num, target);
886     } 
887     clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
888                maxclean,time_file);
889     printf("%s cleaned\n", tape_device);
890     break;
891   };
892
893   CloseDevice("", 0);
894   dbclose();
895   return endstatus;
896 }
897 /*
898  * Local variables:
899  * indent-tabs-mode: nil
900  * tab-width: 4
901  * End:
902  */