Imported Upstream version 3.1.0
[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   char *mailer;
638
639   if (cleancart == -1 ){
640     return;
641   }
642
643   mailer = getconf_str(CNF_MAILER);
644
645   /* Now we should increment the counter */
646   if (cnt_file != NULL){
647     counter = get_current_slot(cnt_file);
648     counter++;
649     if (counter>=maxclean){
650       /* Now we should inform the administrator */
651       char *mail_cmd;
652       FILE *mailf;
653       int mail_pipe_opened = 1;
654       if (mailer && *mailer != '\0') {
655         if (getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0 && 
656            validate_mailto(getconf_str(CNF_MAILTO))) {
657            mail_cmd = vstralloc(mailer,
658                              " -s", " \"", _("AMANDA PROBLEM: PLEASE FIX"), "\"",
659                              " ", getconf_str(CNF_MAILTO),
660                              NULL);
661            if ((mailf = popen(mail_cmd, "w")) == NULL) {
662                   g_printf(_("Mail failed\n"));
663                   error(_("could not open pipe to \"%s\": %s"),
664                         mail_cmd, strerror(errno));
665                   /*NOTREACHED*/
666           }
667         } else {
668            mail_pipe_opened = 0;
669            mailf = stderr;
670            g_fprintf(mailf, _("\nNo mail recipient specified, output redirected to stderr"));
671         }
672       } else {
673         mail_pipe_opened = 0;
674         mailf = stderr;
675         g_fprintf(mailf, _("\nNo mailer specified; output redirected to stderr"));
676       }
677       g_fprintf(mailf, _("\nThe usage count of your cleaning tape in slot %d"),
678              cleancart);
679       g_fprintf(mailf,_("\nis more than %d. (cleanmax)"),maxclean);
680       g_fprintf(mailf,_("\nTapedrive %s needs to be cleaned"),tapedev);
681       g_fprintf(mailf,_("\nPlease insert a new cleaning tape and reset"));
682       g_fprintf(mailf,_("\nthe countingfile %s"),cnt_file);
683
684       if(mail_pipe_opened == 1 && pclose(mailf) != 0) {
685          error(_("mail command failed: %s"), mail_cmd);
686                 /*NOTREACHED*/
687       }
688       
689
690       return;
691     }
692     put_current_slot(cnt_file,counter);
693   }
694   load(fd,drivenum,cleancart);
695   
696   if (drive_loaded(fd, drivenum))
697     unload(fd,drivenum,cleancart);  
698   unlink(usagetime);
699 }
700 /* ----------------------------------------------------------------------*/
701
702 int
703 main(
704     int         argc,
705     char **     argv)
706 {
707   int loaded;
708   int target = -1;
709   int oldtarget;
710   command com;   /* a little DOS joke */
711   
712   /*
713    * drive_num really should be something from the config file, but..
714    * for now, it is set to zero, since most of the common changers
715    * used by amanda only have one drive ( until someone wants to 
716    * use an EXB60/120, or a Breece Hill Q45.. )
717    */
718   int    drive_num = 0;
719   int need_eject = 0; /* Does the drive need an eject command ? */
720   unsigned need_sleep = 0; /* How many seconds to wait for the drive to get ready */
721   int clean_slot = -1;
722   int maxclean = 0;
723   char *clean_file=NULL;
724   char *time_file=NULL;
725
726   int use_slots;
727   int slot_offset;
728   int confnum;
729
730   int fd, slotcnt, drivecnt;
731   int endstatus = 0;
732   char *changer_dev = NULL;
733   char *tape_device = NULL;
734   char *changer_file = NULL;
735   char *scsitapedevice = NULL;
736
737   /*
738    * Configure program for internationalization:
739    *   1) Only set the message locale for now.
740    *   2) Set textdomain for all amanda related programs to "amanda"
741    *      We don't want to be forced to support dozens of message catalogs.
742    */  
743   setlocale(LC_MESSAGES, "C");
744   textdomain("amanda"); 
745
746   set_pname("chg-scsi");
747
748   /* Don't die when child closes pipe */
749   signal(SIGPIPE, SIG_IGN);
750
751   dbopen(DBG_SUBDIR_SERVER);
752   parse_args(argc,argv,&com);
753
754   changer = alloc(SIZEOF(changer_t));
755   config_init(CONFIG_INIT_USE_CWD, NULL);
756
757   if (config_errors(NULL) >= CFGERR_WARNINGS) {
758     config_print_errors();
759     if (config_errors(NULL) >= CFGERR_ERRORS) {
760       g_critical(_("errors processing config file"));
761     }
762   }
763
764   changer_dev = getconf_str(CNF_CHANGERDEV);
765   changer_file = getconf_str(CNF_CHANGERFILE);
766   tape_device = getconf_str(CNF_TAPEDEV);
767
768   /* Get the configuration parameters */
769
770   if (strlen(tape_device)==1){
771     read_config(changer_file, changer);
772     confnum=atoi(tape_device);
773     use_slots    = changer->conf[confnum].end-changer->conf[confnum].start+1;
774     slot_offset  = changer->conf[confnum].start;
775     drive_num    = changer->conf[confnum].drivenum;
776     need_eject   = changer->eject;
777     need_sleep   = changer->sleep;
778     clean_file   = stralloc(changer->conf[confnum].cleanfile);
779     clean_slot   = changer->conf[confnum].cleanslot;
780     maxclean     = changer->cleanmax;
781     if (NULL != changer->conf[confnum].timefile)
782       time_file = stralloc(changer->conf[confnum].timefile);
783     if (NULL != changer->conf[confnum].slotfile)
784       changer_file = stralloc(changer->conf[confnum].slotfile);
785     else
786       changer_file = NULL;
787     if (NULL != changer->conf[confnum].device)
788       tape_device  = stralloc(changer->conf[confnum].device);
789     if (NULL != changer->device)
790       changer_dev  = stralloc(changer->device); 
791     if (NULL != changer->conf[confnum].scsitapedev)
792       scsitapedevice = stralloc(changer->conf[confnum].scsitapedev);
793     if (NULL != changer->conf[confnum].tapestatfile)
794       tapestatfile = stralloc(changer->conf[confnum].tapestatfile);
795     dump_changer_struct(changer);
796     /* get info about the changer */
797     fd = OpenDevice(INDEX_CHANGER , changer_dev,
798                         "changer_dev", changer->conf[confnum].changerident);
799     if (fd == -1) {
800       int localerr = errno;
801       g_fprintf(stderr, _("%s: open: %s: %s\n"), get_pname(), 
802               changer_dev, strerror(localerr));
803       g_printf(_("%s open: %s: %s\n"), "<none>", changer_dev, strerror(localerr));
804       dbprintf(_("open: %s: %s\n"), changer_dev, strerror(localerr));
805       return 2;
806     }
807
808     if (tape_device == NULL)
809       {
810         tape_device = stralloc(changer_dev);
811       }
812
813     if (scsitapedevice == NULL)
814       {
815          scsitapedevice = stralloc(tape_device);
816       }
817
818     if ((changer->conf[confnum].end == -1) || (changer->conf[confnum].start == -1)){
819       slotcnt = get_slot_count(fd);
820       use_slots    = slotcnt;
821       slot_offset  = 0;
822     }
823     free_changer_struct(&changer);
824   } else {
825     /* get info about the changer */
826     confnum = 0;
827     fd = OpenDevice(INDEX_CHANGER , changer_dev,
828                         "changer_dev", changer->conf[confnum].changerident);
829     if (fd == -1) {
830       int localerr = errno;
831       g_fprintf(stderr, _("%s: open: %s: %s\n"), get_pname(), 
832               changer_dev, strerror(localerr));
833       g_printf(_("%s open: %s: %s\n"), _("<none>"), changer_dev, strerror(localerr));
834       dbprintf(_("open: %s: %s\n"), changer_dev, strerror(localerr));
835       return 2;
836     }
837     slotcnt = get_slot_count(fd);
838     use_slots    = slotcnt;
839     slot_offset  = 0;
840     drive_num    = 0;
841     need_eject   = 0;
842     need_sleep   = 0;
843   }
844
845   drivecnt = get_drive_count(fd);
846
847   if (drive_num > drivecnt) {
848     g_printf(_("%s drive number error (%d > %d)\n"), _("<none>"), 
849            drive_num, drivecnt);
850     g_fprintf(stderr, _("%s: requested drive number (%d) greater than "
851             "number of supported drives (%d)\n"), get_pname(), 
852             drive_num, drivecnt);
853     dbprintf(_("requested drive number (%d) greater than "
854               "number of supported drives (%d)\n"), drive_num, drivecnt);
855     CloseDevice("", fd);
856     return 2;
857   }
858
859   loaded = drive_loaded(fd, drive_num);
860
861   switch(com.command_code) {
862   case COM_SLOT:  /* slot changing command */
863     if (is_positive_number(com.parameter)) {
864       if ((target = atoi(com.parameter))>=use_slots) {
865         g_printf(_("<none> no slot `%d'\n"),target);
866         close(fd);
867         endstatus = 2;
868         break;
869       } else {
870         target = target+slot_offset;
871       }
872     } else
873       target=get_relative_target(fd, use_slots,
874                                  com.parameter,
875                                  loaded, 
876                                  changer_file,slot_offset,slot_offset+use_slots);
877     if (loaded) {
878       if (changer_file != NULL)
879         {
880           oldtarget=get_current_slot(changer_file);
881         } else {
882           oldtarget = GetCurrentSlot(fd, drive_num);
883         }
884       if ((oldtarget)!=target) {
885         if (need_eject)
886           eject_tape(scsitapedevice, need_eject);
887         (void)unload(fd, drive_num, oldtarget);
888         if (ask_clean(scsitapedevice))
889           clean_tape(fd,tape_device,clean_file,drive_num,
890                      clean_slot,maxclean,time_file);
891         loaded=0;
892       }
893     }
894     if (changer_file != NULL)
895       {
896       put_current_slot(changer_file, target);
897     }
898     if (!loaded && isempty(fd, target)) {
899       g_printf(_("%d slot %d is empty\n"),target-slot_offset,
900              target-slot_offset);
901       close(fd);
902       endstatus = 1;
903       break;
904     }
905     if (!loaded)
906       if (load(fd, drive_num, target) != 0) {
907         g_printf(_("%d slot %d move failed\n"),target-slot_offset,
908                target-slot_offset);  
909         close(fd);
910         endstatus = 2;
911         break;
912       }
913     if (need_sleep)
914       Tape_Ready1(scsitapedevice, need_sleep);
915     g_printf(_("%d %s\n"), target-slot_offset, tape_device);
916     break;
917
918   case COM_INFO:
919     if (changer_file != NULL)
920       {
921         g_printf("%d ", get_current_slot(changer_file)-slot_offset);
922       } else {
923         g_printf("%d ", GetCurrentSlot(fd, drive_num)-slot_offset);
924       }
925     g_printf("%d 1\n", use_slots);
926     break;
927
928   case COM_RESET:
929     if (changer_file != NULL)
930       {
931         target=get_current_slot(changer_file);
932       } else {
933         target = GetCurrentSlot(fd, drive_num);
934       }
935     if (loaded) {
936       if (!isempty(fd, target))
937         target=find_empty(fd,0 ,0);
938       if (need_eject)
939         eject_tape(scsitapedevice, need_eject);
940       (void)unload(fd, drive_num, target);
941       if (ask_clean(scsitapedevice))
942         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
943                    maxclean,time_file);
944     }
945
946     if (isempty(fd, slot_offset)) {
947       g_printf(_("0 slot 0 is empty\n"));
948       close(fd);
949       endstatus = 1;
950       break;
951     }
952
953     if (load(fd, drive_num, slot_offset) != 0) {
954       g_printf(_("%d slot %d move failed\n"),slot_offset,
955              slot_offset);  
956       close(fd);
957       endstatus = 2;
958       break;
959     }
960     if (changer_file != NULL)
961     {
962       put_current_slot(changer_file, slot_offset);
963     }
964     if (need_sleep)
965       Tape_Ready1(scsitapedevice, need_sleep);
966     if (changer_file != NULL)
967       {
968         g_printf("%d %s\n", get_current_slot(changer_file), tape_device);
969       } else {
970         g_printf("%d %s\n", GetCurrentSlot(fd, drive_num), tape_device);
971       }
972     break;
973
974   case COM_EJECT:
975     if (loaded) {
976       if (changer_file != NULL)
977         {
978           target=get_current_slot(changer_file);
979         } else {
980           target = GetCurrentSlot(fd, drive_num);
981         }
982       if (need_eject)
983         eject_tape(scsitapedevice, need_eject);
984       (void)unload(fd, drive_num, target);
985       if (ask_clean(scsitapedevice))
986         clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
987                    maxclean,time_file);
988       g_printf("%d %s\n", target, tape_device);
989     } else {
990       g_printf(_("%d drive was not loaded\n"), target);
991       endstatus = 1;
992     }
993     break;
994   case COM_CLEAN:
995     if (loaded) {
996       if (changer_file  != NULL)
997         {
998           target=get_current_slot(changer_file);
999         } else {
1000           target = GetCurrentSlot(fd, drive_num);
1001         }
1002       if (need_eject)
1003         eject_tape(scsitapedevice, need_eject);
1004       (void)unload(fd, drive_num, target);
1005     } 
1006     clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1007                maxclean,time_file);
1008     g_printf(_("%s cleaned\n"), tape_device);
1009     break;
1010   };
1011
1012   CloseDevice("", 0);
1013   dbclose();
1014   return endstatus;
1015 }
1016 /*
1017  * Local variables:
1018  * indent-tabs-mode: nil
1019  * tab-width: 4
1020  * End:
1021  */