Imported Upstream version 2.5.1
[debian/amanda] / changer-src / chg-scsi-chio.c
1 /*
2  *  $Id: chg-scsi-chio.c,v 1.12 2006/07/25 18:18:46 martinea Exp $
3  *
4  *  chg-scsi-chio.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 #include "scsi-defs.h"
62
63 char *tapestatfile = NULL;
64
65 /*----------------------------------------------------------------------------*/
66
67 typedef enum{
68   NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM,
69     CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE
70     } token_t;
71
72 typedef struct {
73   char *word;
74   token_t token;
75 } tokentable_t;
76
77 tokentable_t t_table[]={
78   { "number_configs",   NUMDRIVE},
79   { "eject",            EJECT},
80   { "sleep",            SLEEP},
81   { "cleanmax",         CLEANMAX},
82   { "config",           DRIVE},
83   { "startuse",         START},
84   { "enduse",           END},
85   { "cleancart",        CLEAN},
86   { "dev",              DEVICE},
87   { "statfile",         STATFILE},
88   { "cleanfile",        CLEANFILE},
89   { "drivenum",         DRIVENUM},
90   { "changerdev",       CHANGERDEV},
91   { "usagecount",       USAGECOUNT},
92   { "scsitapedev",      SCSITAPEDEV},
93   { "tapestatus",       TAPESTATFILE},
94   { NULL,               -1 }
95 };
96
97 changer_t *changer;
98
99 void    init_changer_struct(changer_t *chg, size_t number_of_config);
100 void    dump_changer_struct(changer_t *chg);
101 void    free_changer_struct(changer_t **changer);
102 void    parse_line(char *linebuffer,int *token,char **value);
103 int     read_config(char *configfile, changer_t *chg);
104 int     get_current_slot(char *count_file);
105 void    put_current_slot(char *count_file,int slot);
106 void    usage(char *argv[]);
107 void    parse_args(int argc, char *argv[],command *rval);
108 int     get_relative_target(int fd,int nslots,char *parameter,int loaded, 
109                         char *changer_file,int slot_offset,int maxslot);
110 int     is_positive_number(char *tmp);
111 int     ask_clean(char *tapedev);
112 void    clean_tape(int fd,char *tapedev,char *cnt_file, int drivenum, 
113                 int cleancart, int maxclean,char *usagetime);
114 int     main(int argc, char *argv[]);
115
116
117 /*
118  * Initialize data structures with default values
119 */
120 void
121 init_changer_struct(
122     changer_t * chg,
123     size_t      number_of_config)
124 {
125   int i;
126  
127   memset(chg, 0, SIZEOF(*chg));
128   chg->number_of_configs = number_of_config;
129   chg->eject = 1;
130   chg->sleep = 0;
131   chg->cleanmax = 0;
132   chg->device = NULL
133   chg->conf = alloc(SIZEOF(config_t) * number_of_config);
134   for (i=0; i < number_of_config; i++){
135     chg->conf[i].drivenum     = 0;
136     chg->conf[i].start        = -1;
137     chg->conf[i].end          = -1;
138     chg->conf[i].cleanslot    = -1;
139     chg->conf[i].device       = NULL;
140     chg->conf[i].slotfile     = NULL;
141     chg->conf[i].cleanfile    = NULL;
142     chg->conf[i].timefile     = NULL;
143     chg->conf[i].scsitapedev  = NULL;
144     chg->conf[i].tapestatfile = NULL;
145     chg->conf[i].changerident = NULL;
146     chg->conf[i].tapeident    = NULL;
147   }
148 }
149
150 /*
151  * Dump of information for debug
152 */
153 void
154 dump_changer_struct(
155     changer_t * chg)
156 {
157   int i;
158
159   dbprintf(("Number of configurations: %d\n", chg->number_of_configs));
160   dbprintf(("Tapes need eject: %s\n", (chg->eject>0 ? "Yes" : "No")));
161   dbprintf(("Tapes need sleep: %d seconds\n", chg->sleep));
162   dbprintf(("Cleancycles     : %d\n", chg->cleanmax));
163   dbprintf(("Changerdevice   : %s\n", chg->device));
164   for (i = 0; i < chg->number_of_configs; i++){
165     dbprintf(("Tapeconfig Nr: %d\n", i));
166     dbprintf(("  Drivenumber   : %d\n", chg->conf[i].drivenum));
167     dbprintf(("  Startslot     : %d\n", chg->conf[i].start));
168     dbprintf(("  Endslot       : %d\n", chg->conf[i].end));
169     dbprintf(("  Cleanslot     : %d\n", chg->conf[i].cleanslot));
170     if (chg->conf[i].device != NULL)
171       dbprintf(("  Devicename    : %s\n", chg->conf[i].device));
172     else
173       dbprintf(("  Devicename    : none\n"));
174     if (chg->conf[i].scsitapedev != NULL)
175       dbprintf(("  SCSITapedev   : %s\n", chg->conf[i].scsitapedev));
176     else
177       dbprintf(("  SCSITapedev   : none\n"));
178     if (chg->conf[i].tapestatfile != NULL)
179       dbprintf(("  statfile      : %s\n", chg->conf[i].tapestatfile));
180     else
181       dbprintf(("  statfile      : none\n"));
182     if (chg->conf[i].slotfile != NULL)
183       dbprintf(("  Slotfile      : %s\n", chg->conf[i].slotfile));
184     else
185       dbprintf(("  Slotfile      : none\n"));
186     if (chg->conf[i].cleanfile != NULL)
187       dbprintf(("  Cleanfile     : %s\n", chg->conf[i].cleanfile));
188     else
189       dbprintf(("  Cleanfile     : none\n"));
190     if (chg->conf[i].timefile != NULL)
191       dbprintf(("  Usagecount    : %s\n", chg->conf[i].timefile));
192     else
193       dbprintf(("  Usagecount    : none\n"));
194   }
195 }
196
197 /*
198  * Free all allocated memory
199  */
200 void
201 free_changer_struct(
202 changer_t **changer)
203 {
204   changer_t *chg;
205   int i;
206
207   assert(changer != NULL);
208   assert(*changer != NULL);
209
210   chg = *changer;
211   if (chg->device != NULL)
212     amfree(chg->device);
213   for (i=0; i<chg->number_of_configs; i++){
214     if (chg->conf[i].device != NULL)
215       amfree(chg->conf[i].device);
216     if (chg->conf[i].slotfile != NULL)
217       amfree(chg->conf[i].slotfile);
218     if (chg->conf[i].cleanfile != NULL)
219       amfree(chg->conf[i].cleanfile);
220     if (chg->conf[i].timefile != NULL)
221       amfree(chg->conf[i].timefile);
222   }
223   if (chg->conf != NULL)
224     amfree(chg->conf);
225   chg->conf = NULL;
226   chg->device = NULL;
227   amfree(*changer);
228 }
229
230 /*
231  * This function parses a line, and returns a token and value
232  */
233 void
234 parse_line(
235     char *      linebuffer,
236     int *       token,
237     char **     value)
238 {
239   char *tok;
240   int i;
241   int ready = 0;
242   *token = -1;  /* No Token found */
243   tok=strtok(linebuffer," \t\n");
244
245   while ((tok != NULL) && (tok[0]!='#')&&(ready==0)){
246     if (*token != -1){
247       *value=tok;
248       ready=1;
249     } else {
250       i=0;
251       while ((t_table[i].word != NULL)&&(*token==-1)){
252         if (0==strcasecmp(t_table[i].word,tok)){
253           *token=t_table[i].token;
254         }
255         i++;
256       }
257     }
258     tok=strtok(NULL," \t\n");
259   }
260   return;
261 }
262
263 /*
264  * This function reads the specified configfile and fills the structure
265 */
266 int
267 read_config(
268     char *      configfile,
269     changer_t * chg)
270 {
271   size_t numconf;
272   FILE *file;
273   int init_flag = 0;
274   int drivenum=0;
275   char *linebuffer;
276   int token;
277   char *value;
278
279   numconf = 1;  /* At least one configuration is assumed */
280   /* If there are more, it should be the first entry in the configurationfile */
281
282   if (NULL==(file=fopen(configfile,"r"))){
283     return (-1);
284   }
285
286   while (NULL != (linebuffer = agets(file))) {
287       if (linebuffer[0] == '\0') {
288         amfree(linebuffer);
289         continue;
290       }
291       parse_line(linebuffer,&token,&value);
292       if (token != -1){
293         if (0==init_flag) {
294           if (token != NUMDRIVE){
295             init_changer_struct(chg, numconf);
296           } else {
297             numconf = atoi(value);
298             init_changer_struct(chg, numconf);
299           }
300           init_flag=1;
301         }
302         switch (token){
303         case NUMDRIVE: if (atoi(value) != numconf)
304           fprintf(stderr,"Error: number_drives at wrong place, should be "\
305                   "first in file\n");
306         break;
307         case EJECT:
308           chg->eject = atoi(value);
309           break;
310         case SLEEP:
311           chg->sleep = atoi(value);
312           break;
313         case CHANGERDEV:
314           chg->device = stralloc(value);
315           break;
316         case SCSITAPEDEV:
317           chg->conf[drivenum].scsitapedev = stralloc(value);
318           break;
319         case TAPESTATFILE:
320           chg->conf[drivenum].tapestatfile = stralloc(value);
321           break;
322         case CLEANMAX:
323           chg->cleanmax = atoi(value);
324           break;
325         case DRIVE:
326           drivenum = atoi(value);
327           if(drivenum >= numconf){
328             fprintf(stderr,"Error: drive must be less than number_drives\n");
329           }
330           break;
331         case DRIVENUM:
332           if (drivenum < numconf){
333             chg->conf[drivenum].drivenum = atoi(value);
334           } else {
335             fprintf(stderr,"Error: drive is not less than number_drives"\
336                     " drivenum ignored\n");
337           }
338           break;
339         case START:
340           if (drivenum < numconf){
341             chg->conf[drivenum].start = atoi(value);
342           } else {
343             fprintf(stderr,"Error: drive is not less than number_drives"\
344                     " startuse ignored\n");
345           }
346           break;
347         case END:
348           if (drivenum < numconf){
349             chg->conf[drivenum].end = atoi(value);
350           } else {
351             fprintf(stderr,"Error: drive is not less than number_drives"\
352                     " enduse ignored\n");
353           }
354           break;
355         case CLEAN:
356           if (drivenum < numconf){
357             chg->conf[drivenum].cleanslot = atoi(value);
358           } else {
359             fprintf(stderr,"Error: drive is not less than number_drives"\
360                     " cleanslot ignored\n");
361           }
362           break;
363         case DEVICE:
364           if (drivenum < numconf){
365             chg->conf[drivenum].device = stralloc(value);
366           } else {
367             fprintf(stderr,"Error: drive is not less than number_drives"\
368                     " device ignored\n");
369           }
370           break;
371         case STATFILE:
372           if (drivenum < numconf){
373             chg->conf[drivenum].slotfile = stralloc(value);
374           } else {
375             fprintf(stderr,"Error: drive is not less than number_drives"\
376                     " slotfile ignored\n");
377           }
378           break;
379         case CLEANFILE:
380           if (drivenum < numconf){
381             chg->conf[drivenum].cleanfile = stralloc(value);
382           } else {
383             fprintf(stderr,"Error: drive is not less than number_drives"\
384                     " cleanfile ignored\n");
385           }
386           break;
387         case USAGECOUNT:
388           if (drivenum < numconf){
389             chg->conf[drivenum].timefile = stralloc(value);
390           } else {
391             fprintf(stderr,"Error: drive is not less than number_drives"\
392                     " usagecount ignored\n");
393           }
394           break;
395         default:
396           fprintf(stderr,"Error: Unknown token\n");
397           break;
398         }
399       }
400     amfree(linebuffer);
401   }
402   amfree(linebuffer);
403
404   fclose(file);
405   return 0;
406 }
407
408 /*----------------------------------------------------------------------------*/
409
410 /*  The tape drive does not have an idea of current slot so
411  *  we use a file to store the current slot.  It is not ideal
412  *  but it gets the job done  
413  */
414 int get_current_slot(char *count_file)
415 {
416   FILE *inf;
417   int retval;
418   if ((inf=fopen(count_file,"r")) == NULL) {
419     fprintf(stderr, "%s: unable to open current slot file (%s)\n",
420             get_pname(), count_file);
421     return 0;
422   }
423
424   if (fscanf(inf, "%d", &retval) != 1) {
425     fprintf(stderr, "%s: unable to read current slot file (%s)\n",
426             get_pname(), count_file);
427     retval = 0;
428   }
429
430   fclose(inf);
431   return retval;
432 }
433
434 void put_current_slot(char *count_file,int slot)
435 {
436   FILE *inf;
437
438   if ((inf=fopen(count_file,"w")) == NULL) {
439     fprintf(stderr, "%s: unable to open current slot file (%s)\n",
440             get_pname(), count_file);
441     exit(2);
442   }
443   fprintf(inf, "%d\n", slot);
444   fclose(inf);
445 }
446
447 /* ---------------------------------------------------------------------- 
448    This stuff deals with parsing the command line */
449
450 typedef struct com_arg
451 {
452   char *str;
453   int command_code;
454   int takesparam;
455 } argument;
456
457
458 typedef struct com_stru
459 {
460   int command_code;
461   char *parameter;
462 } command;
463
464
465 /* major command line args */
466 #define COMCOUNT 5
467 #define COM_SLOT 0
468 #define COM_INFO 1
469 #define COM_RESET 2
470 #define COM_EJECT 3
471 #define COM_CLEAN 4
472 argument argdefs[]={{"-slot",COM_SLOT,1},
473                     {"-info",COM_INFO,0},
474                     {"-reset",COM_RESET,0},
475                     {"-eject",COM_EJECT,0},
476                     {"-clean",COM_CLEAN,0}};
477
478
479 /* minor command line args */
480 #define SLOTCOUNT 5
481 #define SLOT_CUR 0
482 #define SLOT_NEXT 1
483 #define SLOT_PREV 2
484 #define SLOT_FIRST 3
485 #define SLOT_LAST 4
486 argument slotdefs[]={{"current",SLOT_CUR,0},
487                      {"next",SLOT_NEXT,0},
488                      {"prev",SLOT_PREV,0},
489                      {"first",SLOT_FIRST,0},
490                      {"last",SLOT_LAST,0}};
491
492 int is_positive_number(char *tmp) /* is the string a valid positive int? */
493 {
494   int i=0;
495   if ((tmp==NULL)||(tmp[0]==0))
496     return 0;
497   while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
498     i++;
499   if (tmp[i]==0)
500     return 1;
501   else
502     return 0;
503 }
504
505 void usage(char *argv[])
506 {
507   int cnt;
508   printf("%s: Usage error.\n", argv[0]);
509   for (cnt=0; cnt < COMCOUNT; cnt++){
510     printf("      %s    %s",argv[0],argdefs[cnt].str);
511     if (argdefs[cnt].takesparam)
512       printf(" <param>\n");
513     else
514       printf("\n");
515   }
516   exit(2);
517 }
518
519
520 void parse_args(int argc, char *argv[],command *rval)
521 {
522   int i=0;
523   if ((argc < 2) || (argc > 3)) {
524     usage(argv);
525     /*NOTREACHED*/
526   }
527
528   while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
529     i++;
530   if (i == COMCOUNT) {
531     usage(argv);
532     /*NOTREACHED*/
533   }
534   rval->command_code = argdefs[i].command_code;
535   if (argdefs[i].takesparam) {
536     if (argc < 3) {
537       usage(argv);
538       /*NOTREACHED*/
539     }
540     rval->parameter=argv[2];      
541   }
542   else {
543     if (argc > 2) {
544       usage(argv);
545       /*NOTREACHED*/
546     }
547     rval->parameter=0;
548   }
549 }
550
551 /* used to find actual slot number from keywords next, prev, first, etc */
552 int
553 get_relative_target(
554     int         fd,
555     int         nslots,
556     char *      parameter,
557     int         loaded, 
558     char *      changer_file,
559     int         slot_offset,
560     int         maxslot)
561 {
562   int current_slot,i;
563
564   (void)loaded;         /* Quiet unused warning */
565   if (changer_file != NULL)
566     {
567       current_slot=get_current_slot(changer_file);
568     } else {
569       current_slot =   GetCurrentSlot(fd, 0);
570     }
571   if (current_slot > maxslot){
572     current_slot = slot_offset;
573   }
574   if (current_slot < slot_offset){
575     current_slot = slot_offset;
576   }
577
578   i=0;
579   while((i < SLOTCOUNT) && (strcmp(slotdefs[i].str,parameter)))
580     i++;
581
582   switch(i) {
583   case SLOT_CUR:
584     break;
585
586   case SLOT_NEXT:
587     if (++current_slot==nslots+slot_offset)
588       return slot_offset;
589     break;
590
591   case SLOT_PREV:
592     if (--current_slot<slot_offset)
593       return maxslot;
594     break;
595
596   case SLOT_FIRST:
597     return slot_offset;
598
599   case SLOT_LAST:
600     return maxslot;
601
602   default: 
603     printf("<none> no slot `%s'\n",parameter);
604     close(fd);
605     exit(2);
606     /*NOTREACHED*/
607   }
608   return current_slot;
609 }
610
611 /*
612  * This function should ask the drive if it wants to be cleaned
613  */
614 int
615 ask_clean(
616     char *      tapedev)
617 {
618   return get_clean_state(tapedev);
619 }
620
621 /*
622  * This function should move the cleaning cartridge into the drive
623  */
624 void
625 clean_tape(
626     int         fd,
627     char *      tapedev,
628     char *      cnt_file,
629     int         drivenum,
630     int         cleancart,
631     int         maxclean,
632     char *      usagetime)
633 {
634   int counter;
635
636   if (cleancart == -1 ){
637     return;
638   }
639
640   /* Now we should increment the counter */
641   if (cnt_file != NULL){
642     counter = get_current_slot(cnt_file);
643     counter++;
644     if (counter>=maxclean){
645       /* Now we should inform the administrator */
646       char *mail_cmd;
647       FILE *mailf;
648       int mail_pipe_opened = 1;
649       if(getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0 && 
650          validate_mailto(getconf_str(CNF_MAILTO))) {
651          mail_cmd = vstralloc(MAILER,
652                            " -s", " \"", "AMANDA PROBLEM: PLEASE FIX", "\"",
653                            " ", getconf_str(CNF_MAILTO),
654                            NULL);
655          if((mailf = popen(mail_cmd, "w")) == NULL){
656                 printf("Mail failed\n");
657                 error("could not open pipe to \"%s\": %s",
658                 mail_cmd, strerror(errno));
659                 /*NOTREACHED*/
660         }
661       }
662       else{
663          mail_pipe_opened = 0;
664          mailf = stderr;
665          fprintf(mailf, "\nNo mail recipient specified, output redirected to stderr");
666         }
667         
668       fprintf(mailf,"\nThe usage count of your cleaning tape in slot %d",
669              cleancart);
670       fprintf(mailf,"\nis more than %d. (cleanmax)",maxclean);
671       fprintf(mailf,"\nTapedrive %s needs to be cleaned",tapedev);
672       fprintf(mailf,"\nPlease insert a new cleaning tape and reset");
673       fprintf(mailf,"\nthe countingfile %s",cnt_file);
674
675       if(mail_pipe_opened == 1 && pclose(mailf) != 0) {
676          error("mail command failed: %s", mail_cmd);
677                 /*NOTREACHED*/
678       }
679       
680
681       return;
682     }
683     put_current_slot(cnt_file,counter);
684   }
685   load(fd,drivenum,cleancart);
686   
687   if (drive_loaded(fd, drivenum))
688     unload(fd,drivenum,cleancart);  
689   unlink(usagetime);
690 }
691 /* ----------------------------------------------------------------------*/
692
693 int
694 main(
695     int         argc,
696     char *      argv[])
697 {
698   int loaded;
699   int target = -1;
700   int oldtarget;
701   command com;   /* a little DOS joke */
702   
703   /*
704    * drive_num really should be something from the config file, but..
705    * for now, it is set to zero, since most of the common changers
706    * used by amanda only have one drive ( until someone wants to 
707    * use an EXB60/120, or a Breece Hill Q45.. )
708    */
709   int    drive_num = 0;
710   int need_eject = 0; /* Does the drive need an eject command ? */
711   unsigned need_sleep = 0; /* How many seconds to wait for the drive to get ready */
712   int clean_slot = -1;
713   int maxclean = 0;
714   char *clean_file=NULL;
715   char *time_file=NULL;
716
717   int use_slots;
718   int slot_offset;
719   int confnum;
720
721   int fd, slotcnt, drivecnt;
722   int endstatus = 0;
723   char *changer_dev = NULL;
724   char *tape_device = NULL;
725   char *changer_file = NULL;
726   char *scsitapedevice = NULL;
727
728   set_pname("chg-scsi");
729
730   /* Don't die when child closes pipe */
731   signal(SIGPIPE, SIG_IGN);
732
733   dbopen(DBG_SUBDIR_SERVER);
734   parse_args(argc,argv,&com);
735
736   changer = alloc(SIZEOF(changer_t));
737   if(read_conffile(CONFFILE_NAME)) {
738     fprintf(stderr, "%s: could not find config file \"%s\"",
739                     changer_dev, conffile);
740     exit(1);
741   }
742
743   changer_dev = getconf_str(CNF_CHNGRDEV);
744   changer_file = getconf_str(CNF_CHNGRFILE);
745   tape_device = getconf_str(CNF_TAPEDEV);
746
747   /* Get the configuration parameters */
748
749   if (strlen(tape_device)==1){
750     read_config(changer_file, changer);
751     confnum=atoi(tape_device);
752     use_slots    = changer->conf[confnum].end-changer->conf[confnum].start+1;
753     slot_offset  = changer->conf[confnum].start;
754     drive_num    = changer->conf[confnum].drivenum;
755     need_eject   = changer->eject;
756     need_sleep   = changer->sleep;
757     clean_file   = stralloc(changer->conf[confnum].cleanfile);
758     clean_slot   = changer->conf[confnum].cleanslot;
759     maxclean     = changer->cleanmax;
760     if (NULL != changer->conf[confnum].timefile)
761       time_file = stralloc(changer->conf[confnum].timefile);
762     if (NULL != changer->conf[confnum].slotfile)
763       changer_file = stralloc(changer->conf[confnum].slotfile);
764     else
765       changer_file = NULL;
766     if (NULL != changer->conf[confnum].device)
767       tape_device  = stralloc(changer->conf[confnum].device);
768     if (NULL != changer->device)
769       changer_dev  = stralloc(changer->device); 
770     if (NULL != changer->conf[confnum].scsitapedev)
771       scsitapedevice = stralloc(changer->conf[confnum].scsitapedev);
772     if (NULL != changer->conf[confnum].tapestatfile)
773       tapestatfile = stralloc(changer->conf[confnum].tapestatfile);
774     dump_changer_struct(changer);
775     /* get info about the changer */
776     fd = OpenDevice(INDEX_CHANGER , changer_dev,
777                         "changer_dev", changer->conf[confnum].changerident);
778     if (fd == -1) {
779       int localerr = errno;
780       fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
781               changer_dev, strerror(localerr));
782       printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
783       dbprintf(("%s: open: %s: %s\n", get_pname(),
784                 changer_dev, strerror(localerr)));
785       return 2;
786     }
787
788     if (tape_device == NULL)
789       {
790         tape_device = stralloc(changer_dev);
791       }
792
793     if (scsitapedevice == NULL)
794       {
795          scsitapedevice = stralloc(tape_device);
796       }
797
798     if ((changer->conf[confnum].end == -1) || (changer->conf[confnum].start == -1)){
799       slotcnt = get_slot_count(fd);
800       use_slots    = slotcnt;
801       slot_offset  = 0;
802     }
803     free_changer_struct(&changer);
804   } else {
805     /* get info about the changer */
806     confnum = 0;
807     fd = OpenDevice(INDEX_CHANGER , changer_dev,
808                         "changer_dev", changer->conf[confnum].changerident);
809     if (fd == -1) {
810       int localerr = errno;
811       fprintf(stderr, "%s: open: %s: %s\n", get_pname(), 
812               changer_dev, strerror(localerr));
813       printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
814       dbprintf(("%s: open: %s: %s\n", get_pname(),
815                 changer_dev, strerror(localerr)));
816       return 2;
817     }
818     slotcnt = get_slot_count(fd);
819     use_slots    = slotcnt;
820     slot_offset  = 0;
821     drive_num    = 0;
822     need_eject   = 0;
823     need_sleep   = 0;
824   }
825
826   drivecnt = get_drive_count(fd);
827
828   if (drive_num > drivecnt) {
829     printf("%s drive number error (%d > %d)\n", "<none>", 
830            drive_num, drivecnt);
831     fprintf(stderr, "%s: requested drive number (%d) greater than "
832             "number of supported drives (%d)\n", get_pname(), 
833             drive_num, drivecnt);
834     dbprintf(("%s: requested drive number (%d) greater than "
835               "number of supported drives (%d)\n", get_pname(), 
836               drive_num, drivecnt));
837     CloseDevice("", fd);
838     return 2;
839   }
840
841   loaded = drive_loaded(fd, drive_num);
842
843   switch(com.command_code) {
844   case COM_SLOT:  /* slot changing command */
845     if (is_positive_number(com.parameter)) {
846       if ((target = atoi(com.parameter))>=use_slots) {
847         printf("<none> no slot `%d'\n",target);
848         close(fd);
849         endstatus = 2;
850         break;
851       } else {
852         target = target+slot_offset;
853       }
854     } else
855       target=get_relative_target(fd, use_slots,
856                                  com.parameter,
857                                  loaded, 
858                                  changer_file,slot_offset,slot_offset+use_slots);
859     if (loaded) {
860       if (changer_file != NULL)
861         {
862           oldtarget=get_current_slot(changer_file);
863         } else {
864           oldtarget = GetCurrentSlot(fd, drive_num);
865         }
866       if ((oldtarget)!=target) {
867         if (need_eject)
868           eject_tape(scsitapedevice, need_eject);
869         (void)unload(fd, drive_num, oldtarget);
870         if (ask_clean(scsitapedevice))
871           clean_tape(fd,tape_device,clean_file,drive_num,
872                      clean_slot,maxclean,time_file);
873         loaded=0;
874       }
875     }
876     if (changer_file != NULL)
877       {
878       put_current_slot(changer_file, target);
879     }
880     if (!loaded && isempty(fd, target)) {
881       printf("%d slot %d is empty\n",target-slot_offset,
882              target-slot_offset);
883       close(fd);
884       endstatus = 1;
885       break;
886     }
887     if (!loaded)
888       if (load(fd, drive_num, target) != 0) {
889         printf("%d slot %d move failed\n",target-slot_offset,
890                target-slot_offset);  
891         close(fd);
892         endstatus = 2;
893         break;
894       }
895     if (need_sleep)
896       Tape_Ready(scsitapedevice, need_sleep);
897     printf("%d %s\n", target-slot_offset, tape_device);
898     break;
899
900   case COM_INFO:
901     if (changer_file != NULL)
902       {
903         printf("%d ", get_current_slot(changer_file)-slot_offset);
904       } else {
905         printf("%d ", GetCurrentSlot(fd, drive_num)-slot_offset);
906       }
907     printf("%d 1\n", use_slots);
908     break;
909
910   case COM_RESET:
911     if (changer_file != NULL)
912       {
913         target=get_current_slot(changer_file);
914       } else {
915         target = GetCurrentSlot(fd, drive_num);
916       }
917     if (loaded) {
918       if (!isempty(fd, target))
919         target=find_empty(fd,0 ,0);
920       if (need_eject)
921         eject_tape(scsitapedevice, need_eject);
922       (void)unload(fd, drive_num, target);
923       if (ask_clean(scsitapedevice))
924         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
925                    maxclean,time_file);
926     }
927
928     if (isempty(fd, slot_offset)) {
929       printf("0 slot 0 is empty\n");
930       close(fd);
931       endstatus = 1;
932       break;
933     }
934
935     if (load(fd, drive_num, slot_offset) != 0) {
936       printf("%d slot %d move failed\n",slot_offset,
937              slot_offset);  
938       close(fd);
939       endstatus = 2;
940       break;
941     }
942     if (changer_file != NULL)
943     {
944       put_current_slot(changer_file, slot_offset);
945     }
946     if (need_sleep)
947       Tape_Ready(scsitapedevice, need_sleep);
948     if (changer_file != NULL)
949       {
950         printf("%d %s\n", get_current_slot(changer_file), tape_device);
951       } else {
952         printf("%d %s\n", GetCurrentSlot(fd, drive_num), tape_device);
953       }
954     break;
955
956   case COM_EJECT:
957     if (loaded) {
958       if (changer_file != NULL)
959         {
960           target=get_current_slot(changer_file);
961         } else {
962           target = GetCurrentSlot(fd, drive_num);
963         }
964       if (need_eject)
965         eject_tape(scsitapedevice, need_eject);
966       (void)unload(fd, drive_num, target);
967       if (ask_clean(scsitapedevice))
968         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
969                    maxclean,time_file);
970       printf("%d %s\n", target, tape_device);
971     } else {
972       printf("%d %s\n", target, "drive was not loaded");
973       endstatus = 1;
974     }
975     break;
976   case COM_CLEAN:
977     if (loaded) {
978       if (changer_file  != NULL)
979         {
980           target=get_current_slot(changer_file);
981         } else {
982           target = GetCurrentSlot(fd, drive_num);
983         }
984       if (need_eject)
985         eject_tape(scsitapedevice, need_eject);
986       (void)unload(fd, drive_num, target);
987     } 
988     clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
989                maxclean,time_file);
990     printf("%s cleaned\n", tape_device);
991     break;
992   };
993
994   CloseDevice("", 0);
995   dbclose();
996   return endstatus;
997 }
998 /*
999  * Local variables:
1000  * indent-tabs-mode: nil
1001  * tab-width: 4
1002  * End:
1003  */