2 static char rcsid[] = "$Id: chg-scsi.c,v 1.6.2.22.2.7.2.9 2003/07/05 16:59:01 ant Exp $";
7 * chg-scsi.c -- generic SCSI changer driver
9 * This program provides the framework to control
10 * SCSI changers. It is based on the original chg-scsi
11 * from Eric Schnoebelen <eric@cirr.com> (Original copyright below)
12 * The device dependent part is handled by scsi-changer-driver.c
13 * The SCSI OS interface is handled by scsi-ostype.c
15 * Original copyrigths:
17 * This program provides a driver to control generic
18 * SCSI changers, no matter what platform. The host/OS
19 * specific portions of the interface are implemented
20 * in libscsi.a, which contains a module for each host/OS.
21 * The actual interface for HP/UX is in scsi-hpux.c;
22 * chio is in scsi-chio.c, etc.. A prototype system
23 * dependent scsi interface file is in scsi-proto.c.
25 * Copyright 1997, 1998 Eric Schnoebelen <eric@cirr.com>
27 * This module based upon seagate-changer, by Larry Pyeatt
28 * <pyeatt@cs.colostate.edu>
30 * The original introductory comments follow:
32 * This program was written to control the Seagate/Conner/Archive
33 * autoloading DAT drive. This drive normally has 4 tape capacity
34 * but can be expanded to 12 tapes with an optional tape cartridge.
35 * This program may also work on onther drives. Try it and let me
36 * know of successes/failures.
38 * I have attempted to conform to the requirements for Amanda tape
39 * changer interface. There could be some bugs.
41 * This program works for me under Linux with Gerd Knorr's
42 * <kraxel@cs.tu-berlin.de> SCSI media changer driver installed
43 * as a kernel module. The kernel module is available at
44 * http://sunsite.unc.edu/pub/Linux/kernel/patches/scsi/scsi-changer*
45 * Since the Linux media changer is based on NetBSD, this program
46 * should also work for NetBSD, although I have not tried it.
47 * It may be necessary to change the IOCTL calls to work on other
50 * (c) 1897 Larry Pyeatt, pyeatt@cs.colostate.edu
51 * All Rights Reserved.
53 * Permission to use, copy, modify, distribute, and sell this software and its
54 * documentation for any purpose is hereby granted without fee, provided that
55 * the above copyright notice appear in all copies and that both that
56 * copyright notice and this permission notice appear in supporting
57 * documentation. The author makes no representations about the
58 * suitability of this software for any purpose. It is provided "as is"
59 * without express or implied warranty.
61 * Michael C. Povel 03.06.98 added ejetct_tape and sleep for external tape
62 * devices, and changed some code to allow multiple drives to use their
63 * own slots. Also added support for reserverd slots.
64 * At the moment these parameters are hard coded and only tested under Linux
81 #include "scsi-defs.h"
84 char *tapestatfile = NULL;
85 FILE *debug_file = NULL;
88 * So we have 3 devices, here will all the infos be stored after an
92 OpenFiles_T *pDev = NULL;
94 /* Defined in scsi-changer-driver.c
96 extern int ElementStatusValid;
97 extern ElementInfo_T *pMTE; /*Medium Transport Element */
98 extern ElementInfo_T *pSTE; /*Storage Element */
99 extern ElementInfo_T *pIEE; /*Import Export Element */
100 extern ElementInfo_T *pDTE; /*Data Transfer Element */
101 extern int MTE; /*Counter for the above element types */
108 int do_inventory = 0; /* Set if load/unload functions thinks an inventory should be done */
112 NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM,
113 CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE, LABELFILE, CHANGERIDENT,
114 TAPEIDENT, EMUBARCODE, HAVEBARCODE, DEBUGLEVEL, AUTOINV
122 tokentable_t t_table[]={
123 { "number_configs",NUMDRIVE},
124 { "autoinv", AUTOINV},
127 { "cleanmax",CLEANMAX},
131 { "cleancart",CLEAN},
133 { "statfile",STATFILE},
134 { "cleanfile",CLEANFILE},
135 { "drivenum",DRIVENUM},
136 { "changerdev",CHANGERDEV},
137 { "usagecount",USAGECOUNT},
138 { "scsitapedev", SCSITAPEDEV},
139 { "tapestatus", TAPESTATFILE},
140 { "labelfile", LABELFILE},
141 { "changerident" , CHANGERIDENT},
142 { "tapeident", TAPEIDENT},
143 { "emubarcode", EMUBARCODE},
144 { "havebarcode", HAVEBARCODE},
145 { "debuglevel", DEBUGLEVEL},
149 void init_changer_struct(changer_t *chg,int number_of_config)
150 /* Initialize datasructures with default values */
154 chg->number_of_configs = number_of_config;
159 chg->havebarcode = 0;
162 chg->labelfile = NULL;
163 chg->debuglevel = NULL;
164 chg->conf = malloc(sizeof(config_t)*number_of_config);
165 if (chg->conf != NULL){
166 for (i=0; i < number_of_config; i++){
167 chg->conf[i].drivenum = 0;
168 chg->conf[i].start = -1;
169 chg->conf[i].end = -1;
170 chg->conf[i].cleanslot = -1;
171 chg->conf[i].device = NULL;
172 chg->conf[i].slotfile = NULL;
173 chg->conf[i].cleanfile = NULL;
174 chg->conf[i].timefile = NULL;
175 chg->conf[i].scsitapedev = NULL;
176 chg->conf[i].tapestatfile = NULL;
177 chg->conf[i].changerident = NULL;
178 chg->conf[i].tapeident = NULL;
181 fprintf(stderr,"init_changer_struct malloc failed\n");
185 void dump_changer_struct(changer_t chg)
186 /* Dump of information for debug */
190 dbprintf(("Number of configurations: %d\n",chg.number_of_configs));
191 dbprintf(("Tapes need eject: %s\n",(chg.eject>0?"Yes":"No")));
192 dbprintf (("\traw: %d\n",chg.eject));
193 dbprintf(("Inv. auto update: %s\n",(chg.autoinv>0?"Yes":"No")));
194 dbprintf (("\traw: %d\n",chg.autoinv));
195 dbprintf(("barcode reader : %s\n",(chg.havebarcode>0?"Yes":"No")));
196 dbprintf (("\traw: %d\n",chg.havebarcode));
197 dbprintf(("Emulate Barcode : %s\n",(chg.emubarcode>0?"Yes":"No")));
198 dbprintf (("\traw: %d\n",chg.emubarcode));
199 if (chg.debuglevel != NULL)
200 dbprintf(("debug level : %s\n", chg.debuglevel));
201 dbprintf(("Tapes need sleep: %d seconds\n",chg.sleep));
202 dbprintf(("Cleancycles : %d\n",chg.cleanmax));
203 dbprintf(("Changerdevice : %s\n",chg.device));
204 if (chg.labelfile != NULL)
205 dbprintf(("Labelfile : %s\n", chg.labelfile));
206 for (i=0; i<chg.number_of_configs; i++){
207 dbprintf(("Tapeconfig Nr: %d\n",i));
208 dbprintf((" Drivenumber : %d\n",chg.conf[i].drivenum));
209 dbprintf((" Startslot : %d\n",chg.conf[i].start));
210 dbprintf((" Endslot : %d\n",chg.conf[i].end));
211 dbprintf((" Cleanslot : %d\n",chg.conf[i].cleanslot));
213 if (chg.conf[i].device != NULL)
214 dbprintf((" Devicename : %s\n",chg.conf[i].device));
216 dbprintf((" Devicename : none\n"));
218 if (chg.conf[i].changerident != NULL)
219 dbprintf((" changerident : %s\n",chg.conf[i].changerident));
221 dbprintf((" changerident : none\n"));
223 if (chg.conf[i].scsitapedev != NULL)
224 dbprintf((" SCSITapedev : %s\n",chg.conf[i].scsitapedev));
226 dbprintf((" SCSITapedev : none\n"));
228 if (chg.conf[i].tapeident != NULL)
229 dbprintf((" tapeident : %s\n",chg.conf[i].tapeident));
231 dbprintf((" tapeident : none\n"));
233 if (chg.conf[i].tapestatfile != NULL)
234 dbprintf((" statfile : %s\n", chg.conf[i].tapestatfile));
236 dbprintf((" statfile : none\n"));
238 if (chg.conf[i].slotfile != NULL)
239 dbprintf((" Slotfile : %s\n",chg.conf[i].slotfile));
241 dbprintf((" Slotfile : none\n"));
243 if (chg.conf[i].cleanfile != NULL)
244 dbprintf((" Cleanfile : %s\n",chg.conf[i].cleanfile));
246 dbprintf((" Cleanfile : none\n"));
248 if (chg.conf[i].timefile != NULL)
249 dbprintf((" Usagecount : %s\n",chg.conf[i].timefile));
251 dbprintf((" Usagecount : none\n"));
255 void free_changer_struct(changer_t *chg)
256 /* Free all allocated memory */
260 if (chg->device != NULL)
262 for (i=0; i<chg->number_of_configs; i++){
263 if (chg->conf[i].device != NULL)
264 free(chg->conf[i].device);
265 if (chg->conf[i].slotfile != NULL)
266 free(chg->conf[i].slotfile);
267 if (chg->conf[i].cleanfile != NULL)
268 free(chg->conf[i].cleanfile);
269 if (chg->conf[i].timefile != NULL)
270 free(chg->conf[i].timefile);
272 if (chg->conf != NULL)
278 void parse_line(char *linebuffer,int *token,char **value)
279 /* This function parses a line, and returns a token and value */
284 *token = -1; /* No Token found */
285 tok=strtok(linebuffer," \t\n");
287 while ((tok != NULL) && (tok[0]!='#')&&(ready==0)){
293 while ((t_table[i].word != NULL)&&(*token==-1)){
294 if (0==strcasecmp(t_table[i].word,tok)){
295 *token=t_table[i].token;
300 tok=strtok(NULL," \t\n");
305 int read_config(char *configfile, changer_t *chg)
306 /* This function reads the specified configfile and fills the structure */
312 char *linebuffer = NULL;
317 numconf = 1; /* At least one configuration is assumed */
318 /* If there are more, it should be the first entry in the configurationfile */
321 if (NULL==(file=fopen(configfile,"r"))){
326 while ((NULL!=(linebuffer=agets(file)))) {
327 parse_line(linebuffer,&token,&value);
330 if (token != NUMDRIVE){
331 init_changer_struct(chg,numconf);
333 numconf = atoi(value);
334 init_changer_struct(chg,numconf);
339 case NUMDRIVE: if (atoi(value) != numconf)
340 fprintf(stderr,"Error: number_drives at wrong place, should be "\
350 chg->debuglevel = stralloc(value);
353 chg->eject = atoi(value);
356 chg->havebarcode = atoi(value);
359 chg->sleep = atoi(value);
362 chg->labelfile = stralloc(value);
365 chg->device = stralloc(value);
368 chg->conf[drivenum].scsitapedev = stralloc(value);
371 chg->conf[drivenum].tapestatfile = stralloc(value);
374 chg->conf[drivenum].changerident = stralloc(value);
375 p = chg->conf[drivenum].changerident;
386 chg->conf[drivenum].tapeident = stralloc(value);
389 chg->cleanmax = atoi(value);
392 drivenum = atoi(value);
393 if(drivenum >= numconf){
394 fprintf(stderr,"Error: drive must be less than number_drives\n");
398 if (drivenum < numconf){
399 chg->conf[drivenum].drivenum = atoi(value);
401 fprintf(stderr,"Error: drive is not less than number_drives"\
402 " drivenum ignored\n");
406 if (drivenum < numconf){
407 chg->conf[drivenum].start = atoi(value);
409 fprintf(stderr,"Error: drive is not less than number_drives"\
410 " startuse ignored\n");
414 if (drivenum < numconf){
415 chg->conf[drivenum].end = atoi(value);
417 fprintf(stderr,"Error: drive is not less than number_drives"\
418 " enduse ignored\n");
422 if (drivenum < numconf){
423 chg->conf[drivenum].cleanslot = atoi(value);
425 fprintf(stderr,"Error: drive is not less than number_drives"\
426 " cleanslot ignored\n");
430 if (drivenum < numconf){
431 chg->conf[drivenum].device = stralloc(value);
433 fprintf(stderr,"Error: drive is not less than number_drives"\
434 " device ignored\n");
438 if (drivenum < numconf){
439 chg->conf[drivenum].slotfile = stralloc(value);
441 fprintf(stderr,"Error: drive is not less than number_drives"\
442 " slotfile ignored\n");
446 if (drivenum < numconf){
447 chg->conf[drivenum].cleanfile = stralloc(value);
449 fprintf(stderr,"Error: drive is not less than number_drives"\
450 " cleanfile ignored\n");
454 if (drivenum < numconf){
455 chg->conf[drivenum].timefile = stralloc(value);
457 fprintf(stderr,"Error: drive is not less than number_drives"\
458 " usagecount ignored\n");
462 fprintf(stderr,"Error: Unknown token\n");
474 /*----------------------------------------------------------------------------*/
476 /* The tape drive does not have an idea of current slot so
477 * we use a file to store the current slot. It is not ideal
478 * but it gets the job done
480 int get_current_slot(char *count_file)
484 int ret; /* return value for the fscanf function */
485 if ((inf=fopen(count_file,"r")) == NULL) {
486 fprintf(stderr, "%s: unable to open (%s)\n",
487 get_pname(), count_file);
490 ret = fscanf(inf,"%d",&retval);
494 * check if we got an result
495 * if no set retval to -1
497 if (ret == 0 || ret == EOF)
505 void put_current_slot(char *count_file,int slot)
509 if ((inf=fopen(count_file,"w")) == NULL) {
510 fprintf(stderr, "%s: unable to open current slot file (%s)\n",
511 get_pname(), count_file);
514 fprintf(inf, "%d\n", slot);
519 * Here we handle the inventory DB
520 * With this it should be possible to do an mapping
521 * Barcode -> Volume label
522 * Volume Label -> Barcode
523 * Volume label -> Slot number
528 * The passed struct MBC_T will hold the found entry in the DB
531 int MapBarCode(char *labelfile, MBC_T *result)
544 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : Parameter\n");
545 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"labelfile -> %s, vol -> %s, barcode -> %s, action -> %c, slot -> %d, from -> %d\n",
548 result->data.barcode,
553 if (labelfile == NULL)
555 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"Got empty labelfile (NULL)\n");
556 ChgExit("MapBarCode", "MapBarCode name of labelfile is not set\n",FATAL);
558 if (access(labelfile, F_OK) == -1)
560 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE, "MapBarCode : creating %s", labelfile);
561 if ((fp = fopen(labelfile, "w+")) == NULL)
563 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE," failed\n");
564 ChgExit("MapBarCode", "MapBarCode, creating labelfile failed\n", FATAL);
566 fprintf(fp,":%d:", LABEL_DB_VERSION);
570 if ((fp = fopen(labelfile, "r+")) == NULL)
572 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"MapBarCode : failed to open %s\n", labelfile);
573 ChgExit("MapBarCode", "MapBarCode, opening labelfile for read/write failed\n", FATAL);
576 fscanf(fp,":%d:", &version);
577 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : DB version %d\n", version);
580 if (version != LABEL_DB_VERSION)
582 ChgExit("MapBarCode", "MapBarCode, DB Version does not match\n", FATAL);
585 if (( plabelv2 = (LabelV2_T *)malloc(sizeof(LabelV2_T))) == NULL)
587 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"MapBarCode : malloc failed\n");
588 ChgExit("MapBarCode", "MapBarCode malloc failed\n", FATAL);
591 memset(plabelv2, 0, sizeof(LabelV2_T));
593 while(feof(fp) == 0 && loop == 1)
595 rsize = fread(plabelv2, 1, sizeof(LabelV2_T), fp);
596 if (rsize == sizeof(LabelV2_T))
599 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : (%d) VolTag \"%s\", BarCode %s, inuse %d, slot %d, from %d, loadcount %d\n",record,
605 plabelv2->LoadCount);
606 switch (result->action)
612 printf("Slot -> %d, from -> %d, valid -> %d, Tag -> %s, Barcode -> %s, Loadcount %d\n",
622 * Set all the record to invalid, used by the Inventory function
625 fseek(fp, pos, SEEK_SET);
627 fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
634 * If it is an invalid record we can use it,
635 * so save the record number.
636 * This value is used at the end if no other
637 * record/action matches.
639 if (plabelv2->valid == 0)
648 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
654 * OK this record matches the barcode label
657 if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
659 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : update entry\n");
660 fseek(fp, pos, SEEK_SET);
662 plabelv2->from = result->data.from;
663 plabelv2->slot = result->data.slot;
664 plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
665 strcpy(plabelv2->voltag, result->data.voltag);
666 strcpy(plabelv2->barcode, result->data.barcode);
667 fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
673 * Look for an entry an return the entry
674 * if the voltag (the tape name) matches
677 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
679 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode FIND_SLOT : \n");
680 memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
686 * reason can be an load, incr the LoadCount
690 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
692 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode UPDATE_SLOT : update entry\n");
693 fseek(fp, pos, SEEK_SET);
694 strcpy(plabelv2->voltag, result->data.voltag);
695 strcpy(plabelv2->barcode, result->data.barcode);
697 plabelv2->slot = result->data.slot;
698 plabelv2->from = result->data.from;
699 plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
700 fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
706 * Look for the barcode label of an given volume label
707 * return the slot number and the barcode label.
708 * If the entry is not valid return -1 as slot number
712 * DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode: (%d) inside BARCODE_VOL\n", record);
713 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"file value: %s, searched for value: %s\n", plabelv2->voltag, result->data.voltag);
715 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
717 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : VOL %s match\n", result->data.voltag);
720 memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
725 * Look for an entry which matches the passed
728 case BARCODE_BARCODE:
729 if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
731 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : BARCODE %s match\n", result->data.barcode);
734 memcpy(&(result->data), plabelv2, sizeof(LabelV2_T));
740 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : unknown action\n");
745 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : feof (%d)\n", feof(fp));
746 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : error in read record expect %d, got %d\n",sizeof(LabelV2_T), rsize);
752 * OK, if we come here and the action is either
753 * PUT or update it seems that we have to create a new
754 * record, becuae none of the exsisting records matches
756 if (result->action == BARCODE_PUT || result->action == UPDATE_SLOT )
759 * If we have an entry where the valid flag was set to 0
760 * we can use this record, so seek to this position
761 * If we have no record for reuse the new record will be written to the end.
765 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : reuse record %d\n", unusedrec);
766 fseek(fp, unusedpos, SEEK_SET);
769 * Set all values to zero
771 memset(plabelv2, 0, sizeof(LabelV2_T));
773 strcpy(plabelv2->voltag, result->data.voltag);
774 strncpy(plabelv2->barcode, result->data.barcode, TAG_SIZE);
776 plabelv2->from = result->data.from;
777 plabelv2->slot = result->data.slot;
778 fwrite(plabelv2, 1, sizeof(LabelV2_T), fp);
784 * If we hit this point nothing was
785 * found, so return an 0
791 /* ----------------------------------------------------------------------
792 This stuff deals with parsing the command line */
794 typedef struct com_arg
802 typedef struct com_stru
809 /* major command line args */
820 #define COM_INVENTORY 9
821 #define COM_DUMPDB 10
823 #define COM_GEN_CONF 12
824 argument argdefs[]={{"-slot",COM_SLOT,1},
825 {"-info",COM_INFO,0},
826 {"-reset",COM_RESET,0},
827 {"-eject",COM_EJECT,0},
828 {"-clean",COM_CLEAN,0},
829 {"-label",COM_LABEL,1},
830 {"-search",COM_SEARCH,1},
831 {"-status",COM_STATUS,1},
832 {"-trace",COM_TRACE,1},
833 {"-inventory", COM_INVENTORY,0},
834 {"-dumpdb", COM_DUMPDB,0},
835 {"-scan", COM_SCAN,0},
836 {"-genconf", COM_GEN_CONF,0}
840 /* minor command line args */
846 #define SLOT_ADVANCE 5
847 argument slotdefs[]={{"current",SLOT_CUR,0},
848 {"next",SLOT_NEXT,0},
849 {"prev",SLOT_PREV,0},
850 {"first",SLOT_FIRST,0},
851 {"last",SLOT_LAST,0},
852 {"advance",SLOT_ADVANCE,0},
854 #define SLOTCOUNT (sizeof(slotdefs) / sizeof(slotdefs[0]))
856 int is_positive_number(char *tmp) /* is the string a valid positive int? */
859 if ((tmp==NULL)||(tmp[0]==0))
861 while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
869 void usage(char *argv[])
872 printf("%s: Usage error.\n", argv[0]);
873 for (cnt=0; cnt < COMCOUNT; cnt++){
874 printf(" %s %s",argv[0],argdefs[cnt].str);
875 if (argdefs[cnt].takesparam)
876 printf(" <param>\n");
884 void parse_args(int argc, char *argv[],command *rval)
887 for (i=0; i < argc; i++)
888 dbprintf(("ARG [%d] : %s\n", i, argv[i]));
890 if ((argc<2)||(argc>3))
892 while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
896 rval->command_code = argdefs[i].command_code;
897 if (argdefs[i].takesparam) {
900 rval->parameter=argv[2];
909 /* used to find actual slot number from keywords next, prev, first, etc */
910 int get_relative_target(int fd,int nslots,char *parameter,int param_index,
911 int loaded,char *slot_file,
912 int slot_offset,int maxslot)
916 current_slot = get_current_slot(slot_file);
918 if (current_slot > maxslot){
919 current_slot = slot_offset;
921 if (current_slot < slot_offset){
922 current_slot = slot_offset;
925 switch(param_index) {
931 if (++current_slot==nslots+slot_offset)
937 if (--current_slot<slot_offset)
949 printf("<none> no slot `%s'\n",parameter);
954 return(-1); /* never executed */
957 int ask_clean(char *tapedev)
958 /* This function should ask the drive if it wants to be cleaned */
962 ret = get_clean_state(tapedev);
964 if (ret < 0) /* < 0 means query does not work ... */
971 void clean_tape(int fd,char *tapedev,char *cnt_file, int drivenum,
972 int cleancart, int maxclean,char *usagetime)
973 /* This function should move the cleaning cartridge into the drive */
976 if (cleancart == -1 ){
979 /* Now we should increment the counter */
980 if (cnt_file != NULL){
981 counter = get_current_slot(cnt_file);
983 if (counter>=maxclean){
984 /* Now we should inform the administrator */
987 mail_cmd = vstralloc(MAILER,
988 " -s", " \"", "AMANDA PROBLEM: PLEASE FIX", "\"",
989 " ", getconf_str(CNF_MAILTO),
991 if((mailf = popen(mail_cmd, "w")) == NULL){
992 error("could not open pipe to \"%s\": %s",
993 mail_cmd, strerror(errno));
994 printf("Mail failed\n");
997 fprintf(mailf,"\nThe usage count of your cleaning tape in slot %d",
999 fprintf(mailf,"\nis more than %d. (cleanmax)",maxclean);
1000 fprintf(mailf,"\nTapedrive %s needs to be cleaned",tapedev);
1001 fprintf(mailf,"\nPlease insert a new cleaning tape and reset");
1002 fprintf(mailf,"\nthe countingfile %s",cnt_file);
1004 if(pclose(mailf) != 0)
1005 error("mail command failed: %s", mail_cmd);
1009 put_current_slot(cnt_file,counter);
1011 load(fd,drivenum,cleancart);
1013 * Hack, sleep for some time
1018 if (drive_loaded(fd, drivenum))
1019 unload(fd,drivenum,cleancart);
1022 /* ----------------------------------------------------------------------*/
1024 int main(int argc, char *argv[])
1026 int loaded,target,oldtarget;
1027 command com; /* a little DOS joke */
1030 MBC_T *pbarcoderes = malloc(sizeof(MBC_T));
1032 * drive_num really should be something from the config file, but..
1033 * for now, it is set to zero, since most of the common changers
1034 * used by amanda only have one drive ( until someone wants to
1035 * use an EXB60/120, or a Breece Hill Q45.. )
1037 unsigned char emubarcode = 0;
1039 int need_eject = 0; /* Does the drive need an eject command ? */
1040 int need_sleep = 0; /* How many seconds to wait for the drive to get ready */
1043 char *clean_file=NULL;
1044 char *time_file=NULL;
1046 char *ptr; /* a public pointer .... */
1048 * For the emubarcode stuff
1054 int fd, slotcnt, drivecnt;
1057 char *changer_dev = NULL;
1058 char *tape_device = NULL;
1059 char *chg_scsi_conf = NULL; /* The config file for us */
1060 char *slot_file = NULL; /* Where we will place the info which
1063 char *scsitapedevice = NULL;
1065 int param_index = 0;
1067 memset(pbarcoderes, 0 , sizeof(MBC_T));
1068 chg.number_of_configs = 0;
1073 chg.labelfile = NULL;
1075 chg.debuglevel = NULL;
1076 #ifdef CHG_SCSI_STANDALONE
1077 printf("Ups standalone\n");
1079 set_pname("chg-scsi");
1082 dbprintf(("chg-scsi: %s\n",rcsid));
1083 ChangerDriverVersion();
1085 if (debug_file == NULL)
1087 debug_file = dbfp();
1090 parse_args(argc,argv,&com);
1092 pDev = (OpenFiles_T *)malloc(sizeof(OpenFiles_T) * CHG_MAXDEV);
1093 memset(pDev, 0, sizeof(OpenFiles_T) * CHG_MAXDEV );
1096 if ((ptr=getenv("CHG_DEBUG")) != NULL)
1098 chg.debuglevel = strdup(ptr);
1101 switch(com.command_code)
1116 if(read_conffile(CONFFILE_NAME)) {
1117 perror(CONFFILE_NAME);
1121 chg_scsi_conf = getconf_str(CNF_CHNGRFILE);
1122 tape_device = getconf_str(CNF_TAPEDEV);
1124 /* Get the configuration parameters */
1125 /* Attention, this will not support more than 10 tape devices 0-9 */
1127 if (strlen(tape_device)==1){
1128 if (read_config(chg_scsi_conf,&chg) == -1)
1130 fprintf(stderr, "%s open: of %s failed\n", get_pname(), chg_scsi_conf);
1133 confnum=atoi(tape_device);
1134 if (chg.number_of_configs == 0)
1136 fprintf(stderr,"%s: chg.conf[%d] == NULL\n",get_pname(), confnum);
1139 use_slots = chg.conf[confnum].end-chg.conf[confnum].start+1;
1140 slot_offset = chg.conf[confnum].start;
1141 drive_num = chg.conf[confnum].drivenum;
1142 need_eject = chg.eject;
1143 need_sleep = chg.sleep;
1145 if ( NULL != chg.conf[confnum].cleanfile)
1146 clean_file = stralloc(chg.conf[confnum].cleanfile);
1150 clean_slot = chg.conf[confnum].cleanslot;
1151 maxclean = chg.cleanmax;
1152 emubarcode = chg.emubarcode;
1153 if (NULL != chg.conf[confnum].timefile)
1154 time_file = stralloc(chg.conf[confnum].timefile);
1156 if (NULL != chg.conf[confnum].slotfile)
1157 slot_file = stralloc(chg.conf[confnum].slotfile);
1161 if (NULL != chg.conf[confnum].device)
1162 tape_device = stralloc(chg.conf[confnum].device);
1164 if (NULL != chg.device)
1165 changer_dev = stralloc(chg.device);
1167 if (NULL != chg.conf[confnum].scsitapedev)
1168 scsitapedevice = stralloc(chg.conf[confnum].scsitapedev);
1170 if (NULL != chg.conf[confnum].tapestatfile)
1171 tapestatfile = stralloc(chg.conf[confnum].tapestatfile);
1172 dump_changer_struct(chg);
1177 * The changer device.
1178 * If we can't open it fail with a message
1181 if (OpenDevice(INDEX_CHANGER , changer_dev, "changer_dev", chg.conf[confnum].changerident) == 0)
1183 int localerr = errno;
1184 fprintf(stderr, "%s: open: %s: %s\n", get_pname(),
1185 changer_dev, strerror(localerr));
1186 printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
1187 dbprintf(("%s: open: %s: %s\n", get_pname(),
1188 changer_dev, strerror(localerr)));
1197 * eject if eject is set
1198 * inventory (reading of the labels) if emubarcode (not yet)
1200 if (tape_device != NULL)
1202 if (OpenDevice(INDEX_TAPE, tape_device, "tape_device", chg.conf[confnum].tapeident) == 0)
1204 dbprintf(("warning open of %s: failed\n", tape_device));
1209 * This is for the status pages of the SCSI tape, nice to have but no must....
1211 if (scsitapedevice != NULL)
1213 if (OpenDevice(INDEX_TAPECTL, scsitapedevice, "scsitapedevice", chg.conf[confnum].tapeident) == 0)
1215 dbprintf(("warning open of %s: failed\n", scsitapedevice));
1221 * So if we need eject we need either an raw device to eject with an ioctl,
1222 * or an SCSI device to send the SCSI eject.
1225 if (need_eject != 0 )
1227 if (pDev[INDEX_TAPE].avail == 0 && pDev[INDEX_TAPECTL].avail == 0)
1229 printf("No device found for tape eject");
1235 if ((chg.conf[confnum].end == -1) || (chg.conf[confnum].start == -1)){
1236 slotcnt = get_slot_count(fd);
1237 use_slots = slotcnt;
1242 * Now check if we have all what we need
1243 * If either emubarcode is set or the changer support barcode
1244 * we need an label file
1247 if ( chg.emubarcode == 1 || BarCode(INDEX_CHANGER) == 1)
1249 if (chg.labelfile == NULL)
1251 printf("labelfile param not set in your config\n");
1256 if (slot_file == NULL)
1258 printf("slotfile param. not set in your config\n");
1262 if (access(slot_file,R_OK|W_OK) != 0)
1264 printf("slotfile %s does not exsist or is not read/write\n", slot_file);
1268 } else { /* if (strlen(tape_device)==1) */
1269 printf("please check your config and use a config file for chg-scsi\n");
1273 drivecnt = get_drive_count(fd);
1275 if (drive_num > drivecnt) {
1276 printf("%s drive number error (%d > %d)\n", "<none>",
1277 drive_num, drivecnt);
1278 fprintf(stderr, "%s: requested drive number (%d) greater than "
1279 "number of supported drives (%d)\n", get_pname(),
1280 drive_num, drivecnt);
1281 dbprintf(("%s: requested drive number (%d) greater than "
1282 "number of supported drives (%d)\n", get_pname(),
1283 drive_num, drivecnt));
1287 loaded = drive_loaded(fd, drive_num);
1290 switch(com.command_code) {
1291 /* This is only for the experts ;-) */
1293 ChangerReplay(com.parameter);
1298 pbarcoderes->action = BARCODE_DUMP;
1299 MapBarCode(chg.labelfile, pbarcoderes);
1302 ChangerStatus(com.parameter, chg.labelfile, BarCode(fd),slot_file, changer_dev, tape_device);
1304 case COM_LABEL: /* Update BarCode/Label mapping file */
1305 pbarcoderes->action = BARCODE_PUT;
1306 pbarcoderes->data.from = pbarcoderes->data.slot = get_current_slot(slot_file);
1307 strcpy(pbarcoderes->data.voltag, com.parameter);
1308 if (BarCode(fd) == 1 && emubarcode != 1)
1310 strcpy( pbarcoderes->data.barcode, pDTE[drive_num].VolTag);
1312 strcpy( pbarcoderes->data.barcode, com.parameter);
1314 MapBarCode(chg.labelfile, pbarcoderes);
1319 * Inventory does an scan of the library and updates the mapping in the label DB
1322 do_inventory = 1; /* Tell the label check not to exit on label errors */
1325 oldtarget = get_current_slot(slot_file);
1328 dbprintf(("COM_INVENTORY: get_current_slot %d\n", oldtarget));
1329 oldtarget = find_empty(fd, slot_offset, use_slots);
1330 dbprintf(("COM_INVENTORY: find_empty %d\n", oldtarget));
1335 eject_tape(scsitapedevice, need_eject);
1337 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1339 LogSense(INDEX_TAPE);
1343 (void)unload(fd, drive_num, oldtarget);
1344 if (ask_clean(scsitapedevice))
1345 clean_tape(fd,tape_device,clean_file,drive_num,
1346 clean_slot,maxclean,time_file);
1348 Inventory(chg.labelfile, drive_num, need_eject, 0, 0, clean_slot);
1349 do_inventory = 0; /* If set on exit the labeldb will be set to invalid ..... */
1353 * Search for the tape, the index is the volume label
1358 * If we have an barcode reader use
1361 if (BarCode(fd) == 1 && emubarcode != 1)
1363 dbprintf(("search : look for %s\n", com.parameter));
1364 pbarcoderes->action = BARCODE_VOL;
1365 pbarcoderes->data.slot = -1;
1366 strcpy(pbarcoderes->data.voltag, com.parameter);
1367 if (MapBarCode(chg.labelfile, pbarcoderes) == 1)
1370 * If both values are unset we have an problem
1371 * so leave the program
1373 if (pbarcoderes->data.slot == -1 && pbarcoderes->data.barcode == NULL)
1375 printf("Label %s not found (1)\n",com.parameter);
1383 * Let's see, if we got an barcode check if it is
1384 * in the current inventory
1386 if (pbarcoderes->data.barcode != NULL)
1389 for (x = 0; x < STE; x++)
1391 if (strcmp(pSTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1393 dbprintf(("search : found slot %d\n", x));
1398 * Not found in the STE slots
1399 * my be in the DTE (tape)
1400 * If we find it check if it is in the right drive
1401 * if we have more than one drive.
1403 for (x = 0; x < DTE; x++)
1405 if (strcmp(pDTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1407 dbprintf(("search : found in tape %d\n", x));
1410 if (x == drive_num) {
1411 oldtarget = get_current_slot(slot_file);
1412 printf("%d %s\n", oldtarget- slot_offset, tape_device);
1415 printf("LABEL in wrong tape Unit\n");
1421 * not found, so do an exit...
1425 printf("Label %s not found (2) \n",com.parameter);
1430 } /* if barcode[0] != 0 */
1433 * If we didn't find anything we will try the info
1434 * from the DB. A reason for not finding anything in the inventory
1435 * might be an unreadable barcode label
1437 if (target == -1 && pbarcoderes->data.slot != -1)
1439 target = pbarcoderes->data.slot;
1443 * OK, if target is still -1 do the exit
1447 printf("Label %s not found (3)\n",com.parameter);
1457 * And now if we have emubarcode set and no barcode reader
1460 if (emubarcode == 1 && BarCode(fd) != 1)
1462 dbprintf(("search : look for %s\n", com.parameter));
1463 pbarcoderes->action = FIND_SLOT;
1464 pbarcoderes->data.slot = -1;
1465 strcpy(pbarcoderes->data.voltag, com.parameter);
1467 if (MapBarCode(chg.labelfile, pbarcoderes) == 1)
1469 if (pbarcoderes->data.valid == 1)
1471 target = pbarcoderes->data.slot;
1473 printf("Barcode DB out of sync \n");
1479 printf("Label %s not found \n",com.parameter);
1487 * The slot changing command
1492 if (is_positive_number(com.parameter)) {
1493 if ((target = atoi(com.parameter))>=use_slots) {
1494 printf("<none> no slot `%d'\n",target);
1499 target = target+slot_offset;
1503 while((param_index<SLOTCOUNT)
1504 &&(strcmp(slotdefs[param_index].str,com.parameter))) {
1507 target=get_relative_target(fd, use_slots,
1508 com.parameter,param_index,
1511 slot_offset,slot_offset+use_slots-1);
1516 oldtarget = get_current_slot(slot_file);
1519 dbprintf(("COM_SLOT: get_current_slot %d\n", oldtarget));
1520 oldtarget = find_empty(fd, slot_offset, use_slots);
1521 dbprintf(("COM_SLOT: find_empty %d\n", oldtarget));
1525 * TODO check if the request slot for the unload is empty
1529 if ((oldtarget)!=target) {
1532 eject_tape(scsitapedevice, need_eject);
1535 * If we have an SCSI path to the tape and an raw io path
1536 * try to read the Error Counter and the label
1538 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1540 LogSense(INDEX_TAPE);
1544 (void)unload(fd, drive_num, oldtarget);
1545 if (ask_clean(scsitapedevice))
1546 clean_tape(fd,tape_device,clean_file,drive_num,
1547 clean_slot,maxclean,time_file);
1552 put_current_slot(slot_file, target);
1554 if (!loaded && isempty(fd, target)) {
1555 printf("%d slot %d is empty\n",target-slot_offset,
1556 target-slot_offset);
1562 if (!loaded && param_index != SLOT_ADVANCE)
1564 if (ask_clean(scsitapedevice))
1565 clean_tape(fd,tape_device,clean_file,drive_num,
1566 clean_slot,maxclean,time_file);
1567 if (load(fd, drive_num, target) != 0) {
1568 printf("%d slot %d move failed\n",target-slot_offset,
1569 target-slot_offset);
1578 if (pDev[INDEX_TAPECTL].inqdone == 1)
1580 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1582 printf("tape not ready\n");
1587 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1589 printf("tape not ready\n");
1596 printf("%d %s\n", target-slot_offset, tape_device);
1600 loaded = get_current_slot(slot_file);
1604 loaded = find_empty(fd, slot_offset, use_slots);
1606 loaded = loaded - slot_offset;
1608 printf("%d %d 1", loaded, use_slots);
1610 if (BarCode(fd) == 1 || emubarcode == 1 || chg.havebarcode == 1)
1619 target=get_current_slot(slot_file);
1623 dbprintf(("COM_RESET: get_current_slot %d\n", target));
1624 target=find_empty(fd, slot_offset, use_slots);
1625 dbprintf(("COM_RESET: find_empty %d\n", target));
1630 if (!isempty(fd, target))
1631 target=find_empty(fd, slot_offset, use_slots);
1635 eject_tape(scsitapedevice, need_eject);
1638 * If we have an SCSI path to the tape and an raw io path
1639 * try to read the Error Counter and the label
1641 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1643 LogSense(INDEX_TAPE);
1647 (void)unload(fd, drive_num, target);
1648 if (ask_clean(scsitapedevice))
1649 clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1650 maxclean,time_file);
1653 if (isempty(fd, slot_offset)) {
1654 printf("0 slot 0 is empty\n");
1660 if (load(fd, drive_num, slot_offset) != 0) {
1661 printf("%d slot %d move failed\n",drive_num,
1664 put_current_slot(slot_file, slot_offset);
1669 put_current_slot(slot_file, slot_offset);
1673 if (pDev[INDEX_TAPECTL].inqdone == 1)
1675 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1677 printf("tape not ready\n");
1682 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1684 printf("tape not ready\n");
1691 printf("%d %s\n", slot_offset, tape_device);
1696 target=get_current_slot(slot_file);
1699 dbprintf(("COM_EJECT: get_current_slot %d\n", target));
1700 target = find_empty(fd, slot_offset, use_slots);
1701 dbprintf(("COM_EJECT: find_empty %d\n", target));
1706 eject_tape(scsitapedevice, need_eject);
1708 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1710 LogSense(INDEX_TAPE);
1715 (void)unload(fd, drive_num, target);
1716 if (ask_clean(scsitapedevice))
1717 clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1718 maxclean,time_file);
1719 printf("%d %s\n", target, tape_device);
1721 printf("%d %s\n", target, "drive was not loaded");
1727 target=get_current_slot(slot_file);
1730 dbprintf(("COM_CLEAN: get_current_slot %d\n", target));
1731 target = find_empty(fd, slot_offset, use_slots);
1732 dbprintf(("COM_CLEAN: find_empty %d\n",target));
1737 eject_tape(scsitapedevice, need_eject);
1739 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1741 LogSense(INDEX_TAPE);
1745 (void)unload(fd, drive_num, target);
1748 clean_tape(fd,tape_device,clean_file,drive_num,clean_slot,
1749 maxclean,time_file);
1750 printf("%s cleaned\n", tape_device);
1754 /* FIX ME, should be an function to close the device */
1755 /* if (pChangerDev != NULL) */
1756 /* close(pChangerDev->fd); */
1758 /* if (pTapeDev != NULL) */
1759 /* close(pTapeDev->fd); */
1761 /* if (pTapeDevCtl != NULL) */
1762 /* close(pTapeDevCtl->fd); */
1766 if (do_inventory == 1 && endstatus == 0 && chg.labelfile != NULL)
1768 if ( chg.autoinv == 1)
1770 DebugPrint(DEBUG_INFO,SECTION_INFO, "Do an inventory \n");
1771 Inventory(chg.labelfile, drive_num , chg.eject, 0, 0, clean_slot);
1773 DebugPrint(DEBUG_INFO,SECTION_INFO, "Set all entrys in DB to invalid\n");
1774 memset(pbarcoderes, 0 , sizeof(MBC_T));
1775 pbarcoderes->action = RESET_VALID;
1776 MapBarCode(chg.labelfile,pbarcoderes);
1780 DebugPrint(DEBUG_INFO,SECTION_INFO,"Exit status -> %d\n", endstatus);
1786 * indent-tabs-mode: nil