b5b76bdfee84c59a650aab7f4a94f5e1ccd12d07
[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 int Tape_Ready1 ( char *tapedev , int wait);
63
64 char *tapestatfile = NULL;
65
66 /*----------------------------------------------------------------------------*/
67
68 typedef enum{
69   NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM,
70     CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE
71     } token_t;
72
73 typedef struct {
74   char *word;
75   token_t token;
76 } tokentable_t;
77
78 tokentable_t t_table[]={
79   { "number_configs",   NUMDRIVE},
80   { "eject",            EJECT},
81   { "sleep",            SLEEP},
82   { "cleanmax",         CLEANMAX},
83   { "config",           DRIVE},
84   { "startuse",         START},
85   { "enduse",           END},
86   { "cleancart",        CLEAN},
87   { "dev",              DEVICE},
88   { "statfile",         STATFILE},
89   { "cleanfile",        CLEANFILE},
90   { "drivenum",         DRIVENUM},
91   { "changerdev",       CHANGERDEV},
92   { "usagecount",       USAGECOUNT},
93   { "scsitapedev",      SCSITAPEDEV},
94   { "tapestatus",       TAPESTATFILE},
95   { NULL,               -1 }
96 };
97
98 changer_t *changer;
99
100 void    init_changer_struct(changer_t *chg, size_t number_of_config);
101 void    dump_changer_struct(changer_t *chg);
102 void    free_changer_struct(changer_t **changer);
103 void    parse_line(char *linebuffer,int *token,char **value);
104 int     read_config(char *configfile, changer_t *chg);
105 int     get_current_slot(char *count_file);
106 void    put_current_slot(char *count_file,int slot);
107 void    usage(char *argv[]);
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   size_t 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: %lld seconds\n"), (long long)chg->sleep);
162   dbprintf(_("Clean cycles    : %d\n"), chg->cleanmax);
163   dbprintf(_("Changer device  : %s\n"), chg->device);
164   for (i = 0; i < chg->number_of_configs; i++){
165     dbprintf(_("Tape config Nr: %d\n"), i);
166     dbprintf(_("  Drive number  : %d\n"), chg->conf[i].drivenum);
167     dbprintf(_("  Start slot    : %d\n"), chg->conf[i].start);
168     dbprintf(_("  End slot      : %d\n"), chg->conf[i].end);
169     dbprintf(_("  Clean slot    : %d\n"), chg->conf[i].cleanslot);
170     if (chg->conf[i].device != NULL)
171       dbprintf(_("  Device name   : %s\n"), chg->conf[i].device);
172     else
173       dbprintf(_("  Device name   : none\n"));
174     if (chg->conf[i].scsitapedev != NULL)
175       dbprintf(_("  SCSI Tape dev : %s\n"), chg->conf[i].scsitapedev);
176     else
177       dbprintf(_("  SCSI Tape dev : none\n"));
178     if (chg->conf[i].tapestatfile != NULL)
179       dbprintf(_("  stat file     : %s\n"), chg->conf[i].tapestatfile);
180     else
181       dbprintf(_("  stat file     : none\n"));
182     if (chg->conf[i].slotfile != NULL)
183       dbprintf(_("  Slot file     : %s\n"), chg->conf[i].slotfile);
184     else
185       dbprintf(_("  Slot file     : none\n"));
186     if (chg->conf[i].cleanfile != NULL)
187       dbprintf(_("  Clean file    : %s\n"), chg->conf[i].cleanfile);
188     else
189       dbprintf(_("  Clean file    : none\n"));
190     if (chg->conf[i].timefile != NULL)
191       dbprintf(_("  Usage count   : %s\n"), chg->conf[i].timefile);
192     else
193       dbprintf(_("  Usage count   : 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   size_t 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 ((size_t)atoi(value) != numconf)
304           g_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             g_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             g_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             g_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             g_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             g_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             g_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             g_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             g_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             g_fprintf(stderr,_("Error: drive is not less than number_drives"
392                     " usagecount ignored\n"));
393           }
394           break;
395         default:
396           g_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     g_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     g_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     g_fprintf(stderr, _("%s: unable to open current slot file (%s)\n"),
440             get_pname(), count_file);
441     exit(2);
442   }
443   g_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 void    parse_args(int argc, char *argv[], command *rval);
466
467 /* major command line args */
468 #define COMCOUNT 5
469 #define COM_SLOT 0
470 #define COM_INFO 1
471 #define COM_RESET 2
472 #define COM_EJECT 3
473 #define COM_CLEAN 4
474 argument argdefs[]={{"-slot",COM_SLOT,1},
475                     {"-info",COM_INFO,0},
476                     {"-reset",COM_RESET,0},
477                     {"-eject",COM_EJECT,0},
478                     {"-clean",COM_CLEAN,0}};
479
480
481 /* minor command line args */
482 #define SLOTCOUNT 5
483 #define SLOT_CUR 0
484 #define SLOT_NEXT 1
485 #define SLOT_PREV 2
486 #define SLOT_FIRST 3
487 #define SLOT_LAST 4
488 argument slotdefs[]={{"current",SLOT_CUR,0},
489                      {"next",SLOT_NEXT,0},
490                      {"prev",SLOT_PREV,0},
491                      {"first",SLOT_FIRST,0},
492                      {"last",SLOT_LAST,0}};
493
494 int is_positive_number(char *tmp) /* is the string a valid positive int? */
495 {
496   int i=0;
497   if ((tmp==NULL)||(tmp[0]==0))
498     return 0;
499   while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
500     i++;
501   if (tmp[i]==0)
502     return 1;
503   else
504     return 0;
505 }
506
507 void usage(char *argv[])
508 {
509   int cnt;
510   g_printf(_("%s: Usage error.\n"), argv[0]);
511   for (cnt=0; cnt < COMCOUNT; cnt++){
512     g_printf("      %s    %s",argv[0],argdefs[cnt].str);
513     if (argdefs[cnt].takesparam)
514       g_printf(_(" <param>\n"));
515     else
516       g_printf("\n");
517   }
518   exit(2);
519 }
520
521
522 void parse_args(int argc, char *argv[],command *rval)
523 {
524   int i=0;
525   if ((argc < 2) || (argc > 3)) {
526     usage(argv);
527     /*NOTREACHED*/
528   }
529
530   while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
531     i++;
532   if (i == COMCOUNT) {
533     usage(argv);
534     /*NOTREACHED*/
535   }
536   rval->command_code = argdefs[i].command_code;
537   if (argdefs[i].takesparam) {
538     if (argc < 3) {
539       usage(argv);
540       /*NOTREACHED*/
541     }
542     rval->parameter=argv[2];      
543   }
544   else {
545     if (argc > 2) {
546       usage(argv);
547       /*NOTREACHED*/
548     }
549     rval->parameter=0;
550   }
551 }
552
553 /* used to find actual slot number from keywords next, prev, first, etc */
554 int
555 get_relative_target(
556     int         fd,
557     int         nslots,
558     char *      parameter,
559     int         loaded, 
560     char *      changer_file,
561     int         slot_offset,
562     int         maxslot)
563 {
564   int current_slot,i;
565
566   (void)loaded;         /* Quiet unused warning */
567   if (changer_file != NULL)
568     {
569       current_slot=get_current_slot(changer_file);
570     } else {
571       current_slot =   GetCurrentSlot(fd, 0);
572     }
573   if (current_slot > maxslot){
574     current_slot = slot_offset;
575   }
576   if (current_slot < slot_offset){
577     current_slot = slot_offset;
578   }
579
580   i=0;
581   while((i < SLOTCOUNT) && (strcmp(slotdefs[i].str,parameter)))
582     i++;
583
584   switch(i) {
585   case SLOT_CUR:
586     break;
587
588   case SLOT_NEXT:
589     if (++current_slot==nslots+slot_offset)
590       return slot_offset;
591     break;
592
593   case SLOT_PREV:
594     if (--current_slot<slot_offset)
595       return maxslot;
596     break;
597
598   case SLOT_FIRST:
599     return slot_offset;
600
601   case SLOT_LAST:
602     return maxslot;
603
604   default: 
605     g_printf(_("<none> no slot `%s'\n"),parameter);
606     close(fd);
607     exit(2);
608     /*NOTREACHED*/
609   }
610   return current_slot;
611 }
612
613 /*
614  * This function should ask the drive if it wants to be cleaned
615  */
616 int
617 ask_clean(
618     char *      tapedev)
619 {
620   return get_clean_state(tapedev);
621 }
622
623 /*
624  * This function should move the cleaning cartridge into the drive
625  */
626 void
627 clean_tape(
628     int         fd,
629     char *      tapedev,
630     char *      cnt_file,
631     int         drivenum,
632     int         cleancart,
633     int         maxclean,
634     char *      usagetime)
635 {
636   int counter;
637
638   if (cleancart == -1 ){
639     return;
640   }
641
642   /* Now we should increment the counter */
643   if (cnt_file != NULL){
644     counter = get_current_slot(cnt_file);
645     counter++;
646     if (counter>=maxclean){
647       /* Now we should inform the administrator */
648       char *mail_cmd;
649       FILE *mailf;
650       int mail_pipe_opened = 1;
651 #ifdef MAILER
652       if(getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0 && 
653          validate_mailto(getconf_str(CNF_MAILTO))) {
654          mail_cmd = vstralloc(MAILER,
655                            " -s", " \"", _("AMANDA PROBLEM: PLEASE FIX"), "\"",
656                            " ", getconf_str(CNF_MAILTO),
657                            NULL);
658          if((mailf = popen(mail_cmd, "w")) == NULL){
659                 g_printf(_("Mail failed\n"));
660                 error(_("could not open pipe to \"%s\": %s"),
661                 mail_cmd, strerror(errno));
662                 /*NOTREACHED*/
663         }
664       } else {
665          mail_pipe_opened = 0;
666          mailf = stderr;
667          g_fprintf(mailf, _("\nNo mail recipient specified, output redirected to stderr"));
668       }
669 #else
670       mail_pipe_opened = 0;
671       mailf = stderr;
672       g_fprintf(mailf, _("\nNo mailer specified; output redirected to stderr"));
673 #endif
674       g_fprintf(mailf, _("\nThe usage count of your cleaning tape in slot %d"),
675              cleancart);
676       g_fprintf(mailf,_("\nis more than %d. (cleanmax)"),maxclean);
677       g_fprintf(mailf,_("\nTapedrive %s needs to be cleaned"),tapedev);
678       g_fprintf(mailf,_("\nPlease insert a new cleaning tape and reset"));
679       g_fprintf(mailf,_("\nthe countingfile %s"),cnt_file);
680
681       if(mail_pipe_opened == 1 && pclose(mailf) != 0) {
682          error(_("mail command failed: %s"), mail_cmd);
683                 /*NOTREACHED*/
684       }
685       
686
687       return;
688     }
689     put_current_slot(cnt_file,counter);
690   }
691   load(fd,drivenum,cleancart);
692   
693   if (drive_loaded(fd, drivenum))
694     unload(fd,drivenum,cleancart);  
695   unlink(usagetime);
696 }
697 /* ----------------------------------------------------------------------*/
698
699 int
700 main(
701     int         argc,
702     char **     argv)
703 {
704   int loaded;
705   int target = -1;
706   int oldtarget;
707   command com;   /* a little DOS joke */
708   
709   /*
710    * drive_num really should be something from the config file, but..
711    * for now, it is set to zero, since most of the common changers
712    * used by amanda only have one drive ( until someone wants to 
713    * use an EXB60/120, or a Breece Hill Q45.. )
714    */
715   int    drive_num = 0;
716   int need_eject = 0; /* Does the drive need an eject command ? */
717   unsigned need_sleep = 0; /* How many seconds to wait for the drive to get ready */
718   int clean_slot = -1;
719   int maxclean = 0;
720   char *clean_file=NULL;
721   char *time_file=NULL;
722
723   int use_slots;
724   int slot_offset;
725   int confnum;
726
727   int fd, slotcnt, drivecnt;
728   int endstatus = 0;
729   char *changer_dev = NULL;
730   char *tape_device = NULL;
731   char *changer_file = NULL;
732   char *scsitapedevice = NULL;
733
734   /*
735    * Configure program for internationalization:
736    *   1) Only set the message locale for now.
737    *   2) Set textdomain for all amanda related programs to "amanda"
738    *      We don't want to be forced to support dozens of message catalogs.
739    */  
740   setlocale(LC_MESSAGES, "C");
741   textdomain("amanda"); 
742
743   set_pname("chg-scsi");
744
745   /* Don't die when child closes pipe */
746   signal(SIGPIPE, SIG_IGN);
747
748   dbopen(DBG_SUBDIR_SERVER);
749   parse_args(argc,argv,&com);
750
751   changer = alloc(SIZEOF(changer_t));
752   config_init(CONFIG_INIT_USE_CWD | CONFIG_INIT_FATAL, NULL);
753
754   changer_dev = getconf_str(CNF_CHANGERDEV);
755   changer_file = getconf_str(CNF_CHANGERFILE);
756   tape_device = getconf_str(CNF_TAPEDEV);
757
758   /* Get the configuration parameters */
759
760   if (strlen(tape_device)==1){
761     read_config(changer_file, changer);
762     confnum=atoi(tape_device);
763     use_slots    = changer->conf[confnum].end-changer->conf[confnum].start+1;
764     slot_offset  = changer->conf[confnum].start;
765     drive_num    = changer->conf[confnum].drivenum;
766     need_eject   = changer->eject;
767     need_sleep   = changer->sleep;
768     clean_file   = stralloc(changer->conf[confnum].cleanfile);
769     clean_slot   = changer->conf[confnum].cleanslot;
770     maxclean     = changer->cleanmax;
771     if (NULL != changer->conf[confnum].timefile)
772       time_file = stralloc(changer->conf[confnum].timefile);
773     if (NULL != changer->conf[confnum].slotfile)
774       changer_file = stralloc(changer->conf[confnum].slotfile);
775     else
776       changer_file = NULL;
777     if (NULL != changer->conf[confnum].device)
778       tape_device  = stralloc(changer->conf[confnum].device);
779     if (NULL != changer->device)
780       changer_dev  = stralloc(changer->device); 
781     if (NULL != changer->conf[confnum].scsitapedev)
782       scsitapedevice = stralloc(changer->conf[confnum].scsitapedev);
783     if (NULL != changer->conf[confnum].tapestatfile)
784       tapestatfile = stralloc(changer->conf[confnum].tapestatfile);
785     dump_changer_struct(changer);
786     /* get info about the changer */
787     fd = OpenDevice(INDEX_CHANGER , changer_dev,
788                         "changer_dev", changer->conf[confnum].changerident);
789     if (fd == -1) {
790       int localerr = errno;
791       g_fprintf(stderr, _("%s: open: %s: %s\n"), get_pname(), 
792               changer_dev, strerror(localerr));
793       g_printf(_("%s open: %s: %s\n"), "<none>", changer_dev, strerror(localerr));
794       dbprintf(_("open: %s: %s\n"), changer_dev, strerror(localerr));
795       return 2;
796     }
797
798     if (tape_device == NULL)
799       {
800         tape_device = stralloc(changer_dev);
801       }
802
803     if (scsitapedevice == NULL)
804       {
805          scsitapedevice = stralloc(tape_device);
806       }
807
808     if ((changer->conf[confnum].end == -1) || (changer->conf[confnum].start == -1)){
809       slotcnt = get_slot_count(fd);
810       use_slots    = slotcnt;
811       slot_offset  = 0;
812     }
813     free_changer_struct(&changer);
814   } else {
815     /* get info about the changer */
816     confnum = 0;
817     fd = OpenDevice(INDEX_CHANGER , changer_dev,
818                         "changer_dev", changer->conf[confnum].changerident);
819     if (fd == -1) {
820       int localerr = errno;
821       g_fprintf(stderr, _("%s: open: %s: %s\n"), get_pname(), 
822               changer_dev, strerror(localerr));
823       g_printf(_("%s open: %s: %s\n"), _("<none>"), changer_dev, strerror(localerr));
824       dbprintf(_("open: %s: %s\n"), changer_dev, strerror(localerr));
825       return 2;
826     }
827     slotcnt = get_slot_count(fd);
828     use_slots    = slotcnt;
829     slot_offset  = 0;
830     drive_num    = 0;
831     need_eject   = 0;
832     need_sleep   = 0;
833   }
834
835   drivecnt = get_drive_count(fd);
836
837   if (drive_num > drivecnt) {
838     g_printf(_("%s drive number error (%d > %d)\n"), _("<none>"), 
839            drive_num, drivecnt);
840     g_fprintf(stderr, _("%s: requested drive number (%d) greater than "
841             "number of supported drives (%d)\n"), get_pname(), 
842             drive_num, drivecnt);
843     dbprintf(_("requested drive number (%d) greater than "
844               "number of supported drives (%d)\n"), drive_num, drivecnt);
845     CloseDevice("", fd);
846     return 2;
847   }
848
849   loaded = drive_loaded(fd, drive_num);
850
851   switch(com.command_code) {
852   case COM_SLOT:  /* slot changing command */
853     if (is_positive_number(com.parameter)) {
854       if ((target = atoi(com.parameter))>=use_slots) {
855         g_printf(_("<none> no slot `%d'\n"),target);
856         close(fd);
857         endstatus = 2;
858         break;
859       } else {
860         target = target+slot_offset;
861       }
862     } else
863       target=get_relative_target(fd, use_slots,
864                                  com.parameter,
865                                  loaded, 
866                                  changer_file,slot_offset,slot_offset+use_slots);
867     if (loaded) {
868       if (changer_file != NULL)
869         {
870           oldtarget=get_current_slot(changer_file);
871         } else {
872           oldtarget = GetCurrentSlot(fd, drive_num);
873         }
874       if ((oldtarget)!=target) {
875         if (need_eject)
876           eject_tape(scsitapedevice, need_eject);
877         (void)unload(fd, drive_num, oldtarget);
878         if (ask_clean(scsitapedevice))
879           clean_tape(fd,tape_device,clean_file,drive_num,
880                      clean_slot,maxclean,time_file);
881         loaded=0;
882       }
883     }
884     if (changer_file != NULL)
885       {
886       put_current_slot(changer_file, target);
887     }
888     if (!loaded && isempty(fd, target)) {
889       g_printf(_("%d slot %d is empty\n"),target-slot_offset,
890              target-slot_offset);
891       close(fd);
892       endstatus = 1;
893       break;
894     }
895     if (!loaded)
896       if (load(fd, drive_num, target) != 0) {
897         g_printf(_("%d slot %d move failed\n"),target-slot_offset,
898                target-slot_offset);  
899         close(fd);
900         endstatus = 2;
901         break;
902       }
903     if (need_sleep)
904       Tape_Ready1(scsitapedevice, need_sleep);
905     g_printf(_("%d %s\n"), target-slot_offset, tape_device);
906     break;
907
908   case COM_INFO:
909     if (changer_file != NULL)
910       {
911         g_printf("%d ", get_current_slot(changer_file)-slot_offset);
912       } else {
913         g_printf("%d ", GetCurrentSlot(fd, drive_num)-slot_offset);
914       }
915     g_printf("%d 1\n", use_slots);
916     break;
917
918   case COM_RESET:
919     if (changer_file != NULL)
920       {
921         target=get_current_slot(changer_file);
922       } else {
923         target = GetCurrentSlot(fd, drive_num);
924       }
925     if (loaded) {
926       if (!isempty(fd, target))
927         target=find_empty(fd,0 ,0);
928       if (need_eject)
929         eject_tape(scsitapedevice, need_eject);
930       (void)unload(fd, drive_num, target);
931       if (ask_clean(scsitapedevice))
932         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
933                    maxclean,time_file);
934     }
935
936     if (isempty(fd, slot_offset)) {
937       g_printf(_("0 slot 0 is empty\n"));
938       close(fd);
939       endstatus = 1;
940       break;
941     }
942
943     if (load(fd, drive_num, slot_offset) != 0) {
944       g_printf(_("%d slot %d move failed\n"),slot_offset,
945              slot_offset);  
946       close(fd);
947       endstatus = 2;
948       break;
949     }
950     if (changer_file != NULL)
951     {
952       put_current_slot(changer_file, slot_offset);
953     }
954     if (need_sleep)
955       Tape_Ready1(scsitapedevice, need_sleep);
956     if (changer_file != NULL)
957       {
958         g_printf("%d %s\n", get_current_slot(changer_file), tape_device);
959       } else {
960         g_printf("%d %s\n", GetCurrentSlot(fd, drive_num), tape_device);
961       }
962     break;
963
964   case COM_EJECT:
965     if (loaded) {
966       if (changer_file != NULL)
967         {
968           target=get_current_slot(changer_file);
969         } else {
970           target = GetCurrentSlot(fd, drive_num);
971         }
972       if (need_eject)
973         eject_tape(scsitapedevice, need_eject);
974       (void)unload(fd, drive_num, target);
975       if (ask_clean(scsitapedevice))
976         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
977                    maxclean,time_file);
978       g_printf("%d %s\n", target, tape_device);
979     } else {
980       g_printf(_("%d drive was not loaded\n"), target);
981       endstatus = 1;
982     }
983     break;
984   case COM_CLEAN:
985     if (loaded) {
986       if (changer_file  != NULL)
987         {
988           target=get_current_slot(changer_file);
989         } else {
990           target = GetCurrentSlot(fd, drive_num);
991         }
992       if (need_eject)
993         eject_tape(scsitapedevice, need_eject);
994       (void)unload(fd, drive_num, target);
995     } 
996     clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
997                maxclean,time_file);
998     g_printf(_("%s cleaned\n"), tape_device);
999     break;
1000   };
1001
1002   CloseDevice("", 0);
1003   dbclose();
1004   return endstatus;
1005 }
1006 /*
1007  * Local variables:
1008  * indent-tabs-mode: nil
1009  * tab-width: 4
1010  * End:
1011  */