1 static char rcsid[] = "$Id: chg-scsi.c,v 1.52 2006/07/25 18:18:46 martinea Exp $";
5 * chg-scsi.c -- generic SCSI changer driver
7 * This program provides the framework to control
8 * SCSI changers. It is based on the original chg-scsi
9 * from Eric Schnoebelen <eric@cirr.com> (Original copyright below)
10 * The device dependent part is handled by scsi-changer-driver.c
11 * The SCSI OS interface is handled by scsi-ostype.c
13 * Original copyrights:
15 * This program provides a driver to control generic
16 * SCSI changers, no matter what platform. The host/OS
17 * specific portions of the interface are implemented
18 * in libscsi.a, which contains a module for each host/OS.
19 * The actual interface for HP/UX is in scsi-hpux.c;
20 * chio is in scsi-chio.c, etc.. A prototype system
21 * dependent scsi interface file is in scsi-proto.c.
23 * Copyright 1997, 1998 Eric Schnoebelen <eric@cirr.com>
25 * This module based upon seagate-changer, by Larry Pyeatt
26 * <pyeatt@cs.colostate.edu>
28 * The original introductory comments follow:
30 * This program was written to control the Seagate/Conner/Archive
31 * autoloading DAT drive. This drive normally has 4 tape capacity
32 * but can be expanded to 12 tapes with an optional tape cartridge.
33 * This program may also work on onther drives. Try it and let me
34 * know of successes/failures.
36 * I have attempted to conform to the requirements for Amanda tape
37 * changer interface. There could be some bugs.
39 * This program works for me under Linux with Gerd Knorr's
40 * <kraxel@cs.tu-berlin.de> SCSI media changer driver installed
41 * as a kernel module. The kernel module is available at
42 * http://sunsite.unc.edu/pub/Linux/kernel/patches/scsi/scsi-changer*
43 * Since the Linux media changer is based on NetBSD, this program
44 * should also work for NetBSD, although I have not tried it.
45 * It may be necessary to change the IOCTL calls to work on other
48 * (c) 1897 Larry Pyeatt, pyeatt@cs.colostate.edu
49 * All Rights Reserved.
51 * Permission to use, copy, modify, distribute, and sell this software and its
52 * documentation for any purpose is hereby granted without fee, provided that
53 * the above copyright notice appear in all copies and that both that
54 * copyright notice and this permission notice appear in supporting
55 * documentation. The author makes no representations about the
56 * suitability of this software for any purpose. It is provided "as is"
57 * without express or implied warranty.
59 * Michael C. Povel 03.06.98 added ejetct_tape and sleep for external tape
60 * devices, and changed some code to allow multiple drives to use their
61 * own slots. Also added support for reserverd slots.
62 * At the moment these parameters are hard coded and only tested under Linux
71 #include "scsi-defs.h"
74 char *tapestatfile = NULL;
75 FILE *debug_file = NULL;
78 * So we have 3 devices, here will all the infos be stored after an
82 OpenFiles_T *pDev = NULL;
84 /* Defined in scsi-changer-driver.c
86 extern int ElementStatusValid;
87 extern ElementInfo_T *pMTE; /*Medium Transport Element */
88 extern ElementInfo_T *pSTE; /*Storage Element */
89 extern ElementInfo_T *pIEE; /*Import Export Element */
90 extern ElementInfo_T *pDTE; /*Data Transfer Element */
91 extern size_t MTE; /*Counter for the above element types */
96 int do_inventory = 0; /* Set if load/unload functions thinks an inventory should be done */
100 NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM,
101 CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE, LABELFILE, CHANGERIDENT,
102 TAPEIDENT, EMUBARCODE, HAVEBARCODE, DEBUGLEVEL, AUTOINV
110 tokentable_t t_table[] = {
111 { "number_configs", NUMDRIVE},
112 { "autoinv", AUTOINV},
115 { "cleanmax", CLEANMAX},
117 { "startuse", START},
119 { "cleancart", CLEAN},
121 { "statfile", STATFILE},
122 { "cleanfile", CLEANFILE},
123 { "drivenum", DRIVENUM},
124 { "changerdev", CHANGERDEV},
125 { "usagecount", USAGECOUNT},
126 { "scsitapedev", SCSITAPEDEV},
127 { "tapestatus", TAPESTATFILE},
128 { "labelfile", LABELFILE},
129 { "changerident", CHANGERIDENT},
130 { "tapeident", TAPEIDENT},
131 { "emubarcode", EMUBARCODE},
132 { "havebarcode", HAVEBARCODE},
133 { "debuglevel", DEBUGLEVEL},
137 int ask_clean(char *tapedev);
138 int get_current_slot(char *count_file);
139 int get_relative_target(int fd, int nslots, char *parameter,
140 int param_index, int loaded, char *slot_file,
141 int slot_offset, int maxslot);
142 int is_positive_number(char *tmp);
143 int MapBarCode(char *labelfile, MBC_T *result);
144 int read_config(char *configfile, changer_t *chg);
145 void clean_tape(int fd, char *tapedev, char *cnt_file, int drivenum,
146 int cleancart, int maxclean, char *usagetime);
147 void dump_changer_struct(changer_t *chg);
148 void free_changer_struct(changer_t **chg);
149 void init_changer_struct(changer_t *chg, int number_of_config);
150 void parse_line(char *linebuffer, int *token,char **value);
151 void put_current_slot(char *count_file, int slot);
152 void usage(char *argv[]);
154 int main(int argc, char *argv[]);
157 /* Initialize data structures with default values */
161 int number_of_config)
165 memset(chg, 0, SIZEOF(*chg));
166 chg->number_of_configs = number_of_config;
168 chg->conf = alloc(SIZEOF(config_t) * (size_t)number_of_config);
169 for (i=0; i < number_of_config; i++){
170 chg->conf[i].drivenum = 0;
171 chg->conf[i].start = -1;
172 chg->conf[i].end = -1;
173 chg->conf[i].cleanslot = -1;
174 chg->conf[i].device = NULL;
175 chg->conf[i].slotfile = NULL;
176 chg->conf[i].cleanfile = NULL;
177 chg->conf[i].timefile = NULL;
178 chg->conf[i].scsitapedev = NULL;
179 chg->conf[i].tapestatfile = NULL;
180 chg->conf[i].changerident = NULL;
181 chg->conf[i].tapeident = NULL;
185 /* Dump of information for debug */
192 dbprintf(("Number of configurations: %d\n",chg->number_of_configs));
193 dbprintf(("Tapes need eject: %s\n",(chg->eject>0?"Yes":"No")));
194 dbprintf (("\traw: %d\n",chg->eject));
195 dbprintf(("Inv. auto update: %s\n",(chg->autoinv>0?"Yes":"No")));
196 dbprintf (("\traw: %d\n",chg->autoinv));
197 dbprintf(("barcode reader : %s\n",(chg->havebarcode>0?"Yes":"No")));
198 dbprintf (("\traw: %d\n",chg->havebarcode));
199 dbprintf(("Emulate Barcode : %s\n",(chg->emubarcode>0?"Yes":"No")));
200 dbprintf (("\traw: %d\n",chg->emubarcode));
201 if (chg->debuglevel != NULL)
202 dbprintf(("debug level : %s\n", chg->debuglevel));
203 dbprintf(("Tapes need sleep: %d seconds\n",chg->sleep));
204 dbprintf(("Cleancycles : %d\n",chg->cleanmax));
205 dbprintf(("Changerdevice : %s\n",chg->device));
206 if (chg->labelfile != NULL)
207 dbprintf(("Labelfile : %s\n", chg->labelfile));
208 for (i=0; i<chg->number_of_configs; i++){
209 dbprintf(("Tapeconfig Nr: %d\n",i));
210 dbprintf((" Drivenumber : %d\n",chg->conf[i].drivenum));
211 dbprintf((" Startslot : %d\n",chg->conf[i].start));
212 dbprintf((" Endslot : %d\n",chg->conf[i].end));
213 dbprintf((" Cleanslot : %d\n",chg->conf[i].cleanslot));
215 if (chg->conf[i].device != NULL)
216 dbprintf((" Devicename : %s\n",chg->conf[i].device));
218 dbprintf((" Devicename : none\n"));
220 if (chg->conf[i].changerident != NULL)
221 dbprintf((" changerident : %s\n",chg->conf[i].changerident));
223 dbprintf((" changerident : none\n"));
225 if (chg->conf[i].scsitapedev != NULL)
226 dbprintf((" SCSITapedev : %s\n",chg->conf[i].scsitapedev));
228 dbprintf((" SCSITapedev : none\n"));
230 if (chg->conf[i].tapeident != NULL)
231 dbprintf((" tapeident : %s\n",chg->conf[i].tapeident));
233 dbprintf((" tapeident : none\n"));
235 if (chg->conf[i].tapestatfile != NULL)
236 dbprintf((" statfile : %s\n", chg->conf[i].tapestatfile));
238 dbprintf((" statfile : none\n"));
240 if (chg->conf[i].slotfile != NULL)
241 dbprintf((" Slotfile : %s\n",chg->conf[i].slotfile));
243 dbprintf((" Slotfile : none\n"));
245 if (chg->conf[i].cleanfile != NULL)
246 dbprintf((" Cleanfile : %s\n",chg->conf[i].cleanfile));
248 dbprintf((" Cleanfile : none\n"));
250 if (chg->conf[i].timefile != NULL)
251 dbprintf((" Usagecount : %s\n",chg->conf[i].timefile));
253 dbprintf((" Usagecount : none\n"));
257 /* Free all allocated memory */
266 if (chg->device != NULL)
268 for (i = 0; i < chg->number_of_configs; i++){
269 if (chg->conf[i].device != NULL)
270 amfree(chg->conf[i].device);
271 if (chg->conf[i].slotfile != NULL)
272 amfree(chg->conf[i].slotfile);
273 if (chg->conf[i].cleanfile != NULL)
274 amfree(chg->conf[i].cleanfile);
275 if (chg->conf[i].timefile != NULL)
276 amfree(chg->conf[i].timefile);
278 if (chg->conf != NULL)
285 /* This function parses a line, and returns a token and value */
295 *token = -1; /* No Token found */
296 tok=strtok(linebuffer," \t\n");
298 while ((tok != NULL) && (tok[0]!='#')&&(ready==0)){
304 while ((t_table[i].word != NULL)&&(*token==-1)){
305 if (0==strcasecmp(t_table[i].word,tok)){
306 *token=t_table[i].token;
311 tok=strtok(NULL," \t\n");
315 /* This function reads the specified configfile and fills the structure */
330 numconf = 1; /* At least one configuration is assumed */
331 /* If there are more, it should be the first entry in the configurationfile */
334 if ((file=fopen(configfile,"r")) == NULL) {
338 while ((NULL != (linebuffer = agets(file)))) {
339 if (linebuffer[0] == '\0') {
343 parse_line(linebuffer, &token, &value);
348 if (init_flag == 0) {
349 if (token != NUMDRIVE){
350 init_changer_struct(chg, numconf);
352 numconf = atoi(value);
353 if (numconf < 1 || numconf > 100) {
354 fprintf(stderr,"numconf %d is bad\n", numconf);
357 init_changer_struct(chg, numconf);
362 case NUMDRIVE: if (atoi(value) != numconf)
363 fprintf(stderr,"Error: number_drives at wrong place, should be "\
373 chg->debuglevel = stralloc(value);
376 chg->eject = atoi(value);
379 chg->havebarcode = atoi(value);
382 chg->sleep = (unsigned)atoi(value);
385 chg->labelfile = stralloc(value);
388 chg->device = stralloc(value);
391 chg->conf[drivenum].scsitapedev = stralloc(value);
394 chg->conf[drivenum].tapestatfile = stralloc(value);
397 chg->conf[drivenum].changerident = stralloc(value);
398 if (drivenum < 0 || drivenum > 100) {
399 fprintf(stderr,"drivenum %d is bad\n", drivenum);
402 p = chg->conf[drivenum].changerident;
413 chg->conf[drivenum].tapeident = stralloc(value);
416 chg->cleanmax = atoi(value);
419 drivenum = atoi(value);
421 fprintf(stderr,"Error: drive must be >= 0\n");
423 } else if (drivenum >= numconf) {
424 fprintf(stderr,"Error: drive must be less than number_drives\n");
429 if (drivenum < numconf){
430 chg->conf[drivenum].drivenum = atoi(value);
432 fprintf(stderr,"Error: drive is not less than number_drives"\
433 " drivenum ignored\n");
437 if (drivenum < numconf){
438 chg->conf[drivenum].start = atoi(value);
440 fprintf(stderr,"Error: drive is not less than number_drives"\
441 " startuse ignored\n");
445 if (drivenum < numconf){
446 chg->conf[drivenum].end = atoi(value);
448 fprintf(stderr,"Error: drive is not less than number_drives"\
449 " enduse ignored\n");
453 if (drivenum < numconf){
454 chg->conf[drivenum].cleanslot = atoi(value);
456 fprintf(stderr,"Error: drive is not less than number_drives"\
457 " cleanslot ignored\n");
461 if (drivenum < numconf){
462 chg->conf[drivenum].device = stralloc(value);
464 fprintf(stderr,"Error: drive is not less than number_drives"\
465 " device ignored\n");
469 if (drivenum < numconf){
470 chg->conf[drivenum].slotfile = stralloc(value);
472 fprintf(stderr,"Error: drive is not less than number_drives"\
473 " slotfile ignored\n");
477 if (drivenum < numconf){
478 chg->conf[drivenum].cleanfile = stralloc(value);
480 fprintf(stderr,"Error: drive is not less than number_drives"\
481 " cleanfile ignored\n");
485 if (drivenum < numconf){
486 chg->conf[drivenum].timefile = stralloc(value);
488 fprintf(stderr,"Error: drive is not less than number_drives"\
489 " usagecount ignored\n");
493 fprintf(stderr,"Error: Unknown token\n");
505 /*----------------------------------------------------------------------------*/
507 /* The tape drive does not have an idea of current slot so
508 * we use a file to store the current slot. It is not ideal
509 * but it gets the job done
517 int ret; /* return value for the fscanf function */
518 if ((inf=fopen(count_file,"r")) == NULL) {
519 fprintf(stderr, "%s: unable to open (%s)\n",
520 get_pname(), count_file);
524 ret = fscanf(inf,"%d",&retval);
528 * check if we got an result
529 * if no set retval to -1
531 if (ret == 0 || ret == EOF)
536 if (retval < 0 || retval > 10000) {
552 if ((inf=fopen(count_file,"w")) == NULL) {
553 fprintf(stderr, "%s: unable to open current slot file (%s)\n",
554 get_pname(), count_file);
557 fprintf(inf, "%d\n", slot);
562 * Here we handle the inventory DB
563 * With this it should be possible to do an mapping
564 * Barcode -> Volume label
565 * Volume Label -> Barcode
566 * Volume label -> Slot number
571 * The passed struct MBC_T will hold the found entry in the DB
590 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : Parameter\n");
591 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"labelfile -> %s, vol -> %s, barcode -> %s, action -> %c, slot -> %d, from -> %d\n",
594 result->data.barcode,
599 if (labelfile == NULL)
601 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"Got empty labelfile (NULL)\n");
602 ChgExit("MapBarCode", "MapBarCode name of labelfile is not set\n",FATAL);
605 if (access(labelfile, F_OK) == -1)
607 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE, "MapBarCode : creating %s", labelfile);
608 if ((fp = fopen(labelfile, "w+")) == NULL)
610 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE," failed\n");
611 ChgExit("MapBarCode", "MapBarCode, creating labelfile failed\n", FATAL);
614 fprintf(fp,":%d:", LABEL_DB_VERSION);
618 if ((fp = fopen(labelfile, "r+")) == NULL)
620 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"MapBarCode : failed to open %s\n", labelfile);
621 ChgExit("MapBarCode", "MapBarCode, opening labelfile for read/write failed\n", FATAL);
625 if (fscanf(fp,":%d:", &version) != 1) {
626 ChgExit("MapBarCode", "MapBarCode, DB Version unreadable.\n", FATAL);
629 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : DB version %d\n", version);
632 if (version != LABEL_DB_VERSION)
634 ChgExit("MapBarCode", "MapBarCode, DB Version does not match\n", FATAL);
638 if (( plabelv2 = (LabelV2_T *)alloc(SIZEOF(LabelV2_T))) == NULL)
640 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,"MapBarCode : alloc failed\n");
641 ChgExit("MapBarCode", "MapBarCode alloc failed\n", FATAL);
645 memset(plabelv2, 0, SIZEOF(LabelV2_T));
647 while(feof(fp) == 0 && loop == 1)
649 rsize = fread(plabelv2, 1, SIZEOF(LabelV2_T), fp);
650 if (rsize == SIZEOF(LabelV2_T))
653 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : (%d) VolTag \"%s\", BarCode %s, inuse %d, slot %d, from %d, loadcount %d\n",record,
659 plabelv2->LoadCount);
660 switch (result->action)
666 printf("Slot -> %d, from -> %d, valid -> %d, Tag -> %s, Barcode -> %s, Loadcount %u\n",
676 * Set all the record to invalid, used by the Inventory function
680 if (fseek(fp, pos, SEEK_SET) == -1) {
685 if (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T)) {
696 * If it is an invalid record we can use it,
697 * so save the record number.
698 * This value is used at the end if no other
699 * record/action matches.
701 if (plabelv2->valid == 0)
708 * OK this record matches the barcode label
711 if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
714 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : update entry\n");
715 if (fseek(fp, pos, SEEK_SET) == -1) {
721 plabelv2->from = result->data.from;
722 plabelv2->slot = result->data.slot;
723 plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
724 strncpy(plabelv2->voltag, result->data.voltag,
725 SIZEOF(plabelv2->voltag));
726 strncpy(plabelv2->barcode, result->data.barcode,
727 SIZEOF(plabelv2->barcode));
728 rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
735 * Look for an entry an return the entry
736 * if the voltag (the tape name) matches
739 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
741 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode FIND_SLOT : \n");
742 memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
749 * reason can be an load, incr the LoadCount
753 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
755 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode UPDATE_SLOT : update entry\n");
756 if (fseek(fp, pos, SEEK_SET) == -1) {
761 strncpy(plabelv2->voltag, result->data.voltag,
762 SIZEOF(plabelv2->voltag));
763 strncpy(plabelv2->barcode, result->data.barcode,
764 SIZEOF(plabelv2->barcode));
766 plabelv2->slot = result->data.slot;
767 plabelv2->from = result->data.from;
768 plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
769 rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
776 * Look for the barcode label of an given volume label
777 * return the slot number and the barcode label.
778 * If the entry is not valid return -1 as slot number
782 * DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode: (%d) inside BARCODE_VOL\n", record);
783 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"file value: %s, searched for value: %s\n", plabelv2->voltag, result->data.voltag);
785 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
787 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : VOL %s match\n", result->data.voltag);
790 memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
796 * Look for an entry which matches the passed
799 case BARCODE_BARCODE:
800 if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
802 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : BARCODE %s match\n", result->data.barcode);
805 memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
812 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : unknown action\n");
817 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : feof (%d)\n", feof(fp));
818 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : error in read record expect %d, got %d\n",SIZEOF(LabelV2_T), rsize);
824 * OK, if we come here and the action is either
825 * PUT or update it seems that we have to create a new
826 * record, becuae none of the exsisting records matches
828 if (result->action == BARCODE_PUT || result->action == UPDATE_SLOT )
831 * If we have an entry where the valid flag was set to 0
832 * we can use this record, so seek to this position
833 * If we have no record for reuse the new record will be written to the end.
837 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,"MapBarCode : reuse record %d\n", unusedrec);
838 if (fseek(fp, unusedpos, SEEK_SET) == -1) {
845 * Set all values to zero
847 memset(plabelv2, 0, SIZEOF(LabelV2_T));
849 strncpy(plabelv2->voltag, result->data.voltag,
850 SIZEOF(plabelv2->voltag));
851 strncpy(plabelv2->barcode, result->data.barcode,
852 SIZEOF(plabelv2->barcode));
854 plabelv2->from = result->data.from;
855 plabelv2->slot = result->data.slot;
856 rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
863 * If we hit this point nothing was
864 * found, so return an 0
871 /* ----------------------------------------------------------------------
872 This stuff deals with parsing the command line */
874 typedef struct com_arg
882 typedef struct com_stru
888 void parse_args(int argc, char *argv[],command *rval);
890 /* major command line args */
901 #define COM_INVENTORY 9
902 #define COM_DUMPDB 10
904 #define COM_GEN_CONF 12
905 argument argdefs[]={{"-slot",COM_SLOT,1},
906 {"-info",COM_INFO,0},
907 {"-reset",COM_RESET,0},
908 {"-eject",COM_EJECT,0},
909 {"-clean",COM_CLEAN,0},
910 {"-label",COM_LABEL,1},
911 {"-search",COM_SEARCH,1},
912 {"-status",COM_STATUS,1},
913 {"-trace",COM_TRACE,1},
914 {"-inventory", COM_INVENTORY,0},
915 {"-dumpdb", COM_DUMPDB,0},
916 {"-scan", COM_SCAN,0},
917 {"-genconf", COM_GEN_CONF,0}
921 /* minor command line args */
927 #define SLOT_ADVANCE 5
928 argument slotdefs[]={{"current",SLOT_CUR,0},
929 {"next",SLOT_NEXT,0},
930 {"prev",SLOT_PREV,0},
931 {"first",SLOT_FIRST,0},
932 {"last",SLOT_LAST,0},
933 {"advance",SLOT_ADVANCE,0},
935 #define SLOTCOUNT (int)(sizeof(slotdefs) / sizeof(slotdefs[0]))
937 /* is the string a valid positive int? */
943 if ((tmp==NULL)||(tmp[0]==0))
945 while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
958 printf("%s: Usage error.\n", argv[0]);
959 for (cnt=0; cnt < COMCOUNT; cnt++){
960 printf(" %s %s",argv[0],argdefs[cnt].str);
961 if (argdefs[cnt].takesparam)
962 printf(" <param>\n");
978 for (i=0; i < argc; i++)
979 dbprintf(("ARG [%d] : %s\n", i, argv[i]));
981 if ((argc<2)||(argc>3))
983 while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
987 rval->command_code = argdefs[i].command_code;
988 if (argdefs[i].takesparam) {
991 rval->parameter=argv[2];
1000 /* used to find actual slot number from keywords next, prev, first, etc */
1002 get_relative_target(
1014 (void)loaded; /* Quiet unused parameter warning */
1016 current_slot = get_current_slot(slot_file);
1018 if (current_slot > maxslot) {
1019 current_slot = slot_offset;
1021 if (current_slot < slot_offset) {
1022 current_slot = slot_offset;
1025 switch(param_index) {
1027 return current_slot;
1031 if (++current_slot==nslots+slot_offset)
1033 return current_slot;
1036 if (--current_slot<slot_offset)
1038 return current_slot;
1049 printf("<none> no slot `%s'\n",parameter);
1055 /* This function should ask the drive if it wants to be cleaned */
1062 ret = get_clean_state(tapedev);
1064 if (ret < 0) /* < 0 means query does not work ... */
1071 /* This function should move the cleaning cartridge into the drive */
1084 if (cleancart == -1 ){
1088 /* Now we should increment the counter */
1089 if (cnt_file != NULL){
1090 counter = get_current_slot(cnt_file);
1092 if (counter>=maxclean){
1093 /* Now we should inform the administrator */
1094 char *mail_cmd = NULL;
1096 int mail_pipe_opened = 1;
1097 if(getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0 &&
1098 validate_mailto(getconf_str(CNF_MAILTO))) {
1099 mail_cmd = vstralloc(MAILER,
1100 " -s", " \"", "AMANDA PROBLEM: PLEASE FIX", "\"",
1101 " ", getconf_str(CNF_MAILTO),
1103 if((mailf = popen(mail_cmd, "w")) == NULL){
1104 printf("Mail failed\n");
1105 error("could not open pipe to \"%s\": %s",
1106 mail_cmd, strerror(errno));
1111 mail_pipe_opened = 0;
1113 fprintf(mailf, "\nNo mail recipient specified, output redirected to stderr");
1115 fprintf(mailf,"\nThe usage count of your cleaning tape in slot %d",
1117 fprintf(mailf,"\nis more than %d. (cleanmax)",maxclean);
1118 fprintf(mailf,"\nTapedrive %s needs to be cleaned",tapedev);
1119 fprintf(mailf,"\nPlease insert a new cleaning tape and reset");
1120 fprintf(mailf,"\nthe countingfile %s",cnt_file);
1122 if(mail_pipe_opened == 1 && pclose(mailf) != 0) {
1123 error("mail command failed: %s", mail_cmd);
1128 put_current_slot(cnt_file, counter);
1130 load(fd,drivenum,cleancart);
1132 * Hack, sleep for some time
1137 if (drive_loaded(fd, drivenum))
1138 unload(fd, drivenum, cleancart);
1142 /* ----------------------------------------------------------------------*/
1150 int target, oldtarget;
1151 command com; /* a little DOS joke */
1155 * drive_num really should be something from the config file, but..
1156 * for now, it is set to zero, since most of the common changers
1157 * used by amanda only have one drive ( until someone wants to
1158 * use an EXB60/120, or a Breece Hill Q45.. )
1160 unsigned char emubarcode;
1162 int need_eject; /* Does the drive need an eject command ? */
1163 time_t need_sleep; /* How many seconds to wait for the drive to get ready */
1170 * For the emubarcode stuff
1183 char *chg_scsi_conf; /* The config file for us */
1184 char *slot_file; /* Where we will place the info which
1187 char *scsitapedevice;
1189 int param_index = 0;
1191 changer = alloc(SIZEOF(changer_t));
1192 pbarcoderes = alloc(SIZEOF(MBC_T));
1194 memset(pbarcoderes, 0 , SIZEOF(MBC_T));
1195 changer->number_of_configs = 0;
1198 changer->cleanmax = 0;
1199 changer->device = NULL;
1200 changer->labelfile = NULL;
1201 changer->conf = NULL;
1202 #ifdef CHG_SCSI_STANDALONE
1203 printf("Ups standalone\n");
1205 set_pname("chg-scsi");
1207 /* Don't die when child closes pipe */
1208 signal(SIGPIPE, SIG_IGN);
1210 dbopen(DBG_SUBDIR_SERVER);
1212 dbprintf(("chg-scsi: %s\n", rcsid));
1213 ChangerDriverVersion();
1215 if (debug_file == NULL)
1217 debug_file = dbfp();
1220 parse_args(argc,argv,&com);
1222 pDev = (OpenFiles_T *)alloc(SIZEOF(OpenFiles_T) * CHG_MAXDEV);
1223 memset(pDev, 0, SIZEOF(OpenFiles_T) * CHG_MAXDEV );
1226 switch(com.command_code)
1241 if(read_conffile(CONFFILE_NAME)) {
1242 perror(CONFFILE_NAME);
1247 chg_scsi_conf = getconf_str(CNF_CHNGRFILE);
1248 tape_device = getconf_str(CNF_TAPEDEV);
1250 /* Get the configuration parameters */
1251 /* Attention, this will not support more than 10 tape devices 0-9 */
1253 if (strlen(tape_device)==1){
1254 if (read_config(chg_scsi_conf, changer) == -1)
1256 fprintf(stderr, "%s open: of %s failed\n", get_pname(), chg_scsi_conf);
1259 confnum=atoi(tape_device);
1260 if (changer->number_of_configs == 0)
1262 fprintf(stderr,"%s: changer->conf[%d] == NULL\n",
1263 get_pname(), confnum);
1266 if (confnum >= changer->number_of_configs) {
1267 fprintf(stderr,"%s: Configuration %s config # out of range (%d >= %d)\n",
1268 get_pname(), chg_scsi_conf,
1270 changer->number_of_configs);
1274 use_slots = changer->conf[confnum].end-changer->conf[confnum].start+1;
1275 slot_offset = changer->conf[confnum].start;
1276 drive_num = changer->conf[confnum].drivenum;
1277 need_eject = changer->eject;
1278 need_sleep = changer->sleep;
1280 if ( NULL != changer->conf[confnum].cleanfile)
1281 clean_file = stralloc(changer->conf[confnum].cleanfile);
1285 clean_slot = changer->conf[confnum].cleanslot;
1286 maxclean = changer->cleanmax;
1287 emubarcode = changer->emubarcode;
1288 if (NULL != changer->conf[confnum].timefile)
1289 time_file = stralloc(changer->conf[confnum].timefile);
1293 if (NULL != changer->conf[confnum].slotfile)
1294 slot_file = stralloc(changer->conf[confnum].slotfile);
1298 if (NULL != changer->conf[confnum].device)
1299 tape_device = stralloc(changer->conf[confnum].device);
1303 if (NULL != changer->device)
1304 changer_dev = stralloc(changer->device);
1308 if (NULL != changer->conf[confnum].scsitapedev)
1309 scsitapedevice = stralloc(changer->conf[confnum].scsitapedev);
1311 scsitapedevice = NULL;
1313 if (NULL != changer->conf[confnum].tapestatfile)
1314 tapestatfile = stralloc(changer->conf[confnum].tapestatfile);
1316 tapestatfile = NULL;
1317 dump_changer_struct(changer);
1322 * The changer device.
1323 * If we can't open it fail with a message
1326 if (OpenDevice(INDEX_CHANGER , changer_dev, "changer_dev", changer->conf[confnum].changerident) == 0)
1328 int localerr = errno;
1329 fprintf(stderr, "%s: open: %s: %s\n", get_pname(),
1330 changer_dev, strerror(localerr));
1331 printf("%s open: %s: %s\n", "<none>", changer_dev, strerror(localerr));
1332 dbprintf(("%s: open: %s: %s\n", get_pname(),
1333 changer_dev, strerror(localerr)));
1342 * eject if eject is set
1343 * inventory (reading of the labels) if emubarcode (not yet)
1345 if (tape_device != NULL)
1347 if (OpenDevice(INDEX_TAPE, tape_device, "tape_device", changer->conf[confnum].tapeident) == 0)
1349 dbprintf(("warning open of %s: failed\n", tape_device));
1354 * This is for the status pages of the SCSI tape, nice to have but no must....
1356 if (scsitapedevice != NULL)
1358 if (OpenDevice(INDEX_TAPECTL, scsitapedevice, "scsitapedevice", changer->conf[confnum].tapeident) == 0)
1360 dbprintf(("warning open of %s: failed\n", scsitapedevice));
1366 * So if we need eject we need either an raw device to eject with an ioctl,
1367 * or an SCSI device to send the SCSI eject.
1370 if (need_eject != 0 )
1372 if (pDev[INDEX_TAPE].avail == 0 && pDev[INDEX_TAPECTL].avail == 0)
1374 printf("No device found for tape eject");
1380 if ((changer->conf[confnum].end == -1) || (changer->conf[confnum].start == -1)){
1381 slotcnt = get_slot_count(fd);
1382 use_slots = slotcnt;
1387 * Now check if we have all what we need
1388 * If either emubarcode is set or the changer support barcode
1389 * we need an label file
1392 if ( changer->emubarcode == 1 || BarCode(INDEX_CHANGER) == 1)
1394 if (changer->labelfile == NULL)
1396 printf("labelfile param not set in your config\n");
1401 if (slot_file == NULL)
1403 printf("slotfile param. not set in your config\n");
1407 if (access(slot_file,R_OK|W_OK) != 0)
1409 printf("slotfile %s does not exsist or is not read/write\n", slot_file);
1413 } else { /* if (strlen(tape_device)==1) */
1414 printf("please check your config and use a config file for chg-scsi\n");
1418 drivecnt = get_drive_count(fd);
1420 if (drive_num > drivecnt) {
1421 printf("%s drive number error (%d > %d)\n", "<none>",
1422 drive_num, drivecnt);
1423 fprintf(stderr, "%s: requested drive number (%d) greater than "
1424 "number of supported drives (%d)\n", get_pname(),
1425 drive_num, drivecnt);
1426 dbprintf(("%s: requested drive number (%d) greater than "
1427 "number of supported drives (%d)\n", get_pname(),
1428 drive_num, drivecnt));
1432 loaded = (int)drive_loaded(fd, drive_num);
1435 switch(com.command_code) {
1436 /* This is only for the experts ;-) */
1438 ChangerReplay(com.parameter);
1443 pbarcoderes->action = BARCODE_DUMP;
1444 MapBarCode(changer->labelfile, pbarcoderes);
1447 ChangerStatus(com.parameter, changer->labelfile,
1448 BarCode(fd), slot_file, changer_dev, tape_device);
1450 case COM_LABEL: /* Update BarCode/Label mapping file */
1451 pbarcoderes->action = BARCODE_PUT;
1452 strncpy(pbarcoderes->data.voltag, com.parameter,
1453 SIZEOF(pbarcoderes->data.voltag));
1454 strncpy(pbarcoderes->data.barcode, pDTE[drive_num].VolTag,
1455 SIZEOF(pbarcoderes->data.barcode));
1456 MapBarCode(changer->labelfile, pbarcoderes);
1461 * Inventory does an scan of the library and updates the mapping in the label DB
1464 do_inventory = 1; /* Tell the label check not to exit on label errors */
1467 oldtarget = get_current_slot(slot_file);
1470 dbprintf(("COM_INVENTORY: get_current_slot %d\n", oldtarget));
1471 oldtarget = find_empty(fd, slot_offset, use_slots);
1472 dbprintf(("COM_INVENTORY: find_empty %d\n", oldtarget));
1477 eject_tape(scsitapedevice, need_eject);
1479 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1481 LogSense(INDEX_TAPE);
1485 (void)unload(fd, drive_num, oldtarget);
1486 if (ask_clean(scsitapedevice))
1487 clean_tape(fd,tape_device,clean_file,drive_num,
1488 clean_slot, maxclean, time_file);
1490 Inventory(changer->labelfile, drive_num, need_eject, 0, 0, clean_slot);
1491 do_inventory = 0; /* If set on exit the labeldb will be set to invalid ..... */
1495 * Search for the tape, the index is the volume label
1500 * If we have an barcode reader use
1503 if (BarCode(fd) == 1 && emubarcode != 1)
1505 dbprintf(("search : look for %s\n", com.parameter));
1506 pbarcoderes->action = BARCODE_VOL;
1507 pbarcoderes->data.slot = -1;
1508 strncpy(pbarcoderes->data.voltag, com.parameter,
1509 SIZEOF(pbarcoderes->data.voltag));
1510 if (MapBarCode(changer->labelfile, pbarcoderes) == 1)
1513 * If both values are unset we have an problem
1514 * so leave the program
1516 if (pbarcoderes->data.slot == -1 && pbarcoderes->data.barcode == NULL)
1518 printf("Label %s not found (1)\n",com.parameter);
1526 * Let's see, if we got an barcode check if it is
1527 * in the current inventory
1529 if (pbarcoderes->data.barcode != NULL)
1532 for (x = 0; x < (int)STE; x++)
1534 if (strcmp(pSTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1536 dbprintf(("search : found slot %d\n", x));
1541 * Not found in the STE slots
1542 * my be in the DTE (tape)
1543 * If we find it check if it is in the right drive
1544 * if we have more than one drive.
1546 for (x = 0; x < (int)DTE; x++)
1548 if (strcmp(pDTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1550 dbprintf(("search : found in tape %d\n", x));
1553 if (x == drive_num) {
1554 oldtarget = get_current_slot(slot_file);
1555 printf("%d %s\n", oldtarget - slot_offset, tape_device);
1558 printf("LABEL in wrong tape Unit\n");
1564 * not found, so do an exit...
1568 printf("Label %s not found (2) \n",com.parameter);
1573 } /* if barcode[0] != 0 */
1576 * If we didn't find anything we will try the info
1577 * from the DB. A reason for not finding anything in the inventory
1578 * might be an unreadable barcode label
1580 if (target == -1 && pbarcoderes->data.slot != -1)
1582 target = pbarcoderes->data.slot;
1586 * OK, if target is still -1 do the exit
1590 printf("Label %s not found (3)\n",com.parameter);
1600 * And now if we have emubarcode set and no barcode reader
1603 if (emubarcode == 1 && BarCode(fd) != 1)
1605 dbprintf(("search : look for %s\n", com.parameter));
1606 pbarcoderes->action = FIND_SLOT;
1607 pbarcoderes->data.slot = -1;
1608 strncpy(pbarcoderes->data.voltag, com.parameter,
1609 SIZEOF(pbarcoderes->data.voltag));
1611 if (MapBarCode(changer->labelfile, pbarcoderes) == 1)
1613 if (pbarcoderes->data.valid == 1)
1615 target = pbarcoderes->data.slot;
1617 printf("Barcode DB out of sync \n");
1623 printf("Label %s not found \n",com.parameter);
1631 * The slot changing command
1636 if (is_positive_number(com.parameter)) {
1637 if ((target = atoi(com.parameter))>=use_slots) {
1638 printf("<none> no slot `%d'\n",target);
1643 target = target + slot_offset;
1647 while((param_index < SLOTCOUNT) &&
1648 (strcmp(slotdefs[param_index].str,com.parameter))) {
1651 target=get_relative_target(fd, use_slots,
1652 com.parameter,param_index,
1656 (slot_offset + use_slots - 1));
1661 oldtarget = get_current_slot(slot_file);
1664 dbprintf(("COM_SLOT: get_current_slot %d\n", oldtarget));
1665 oldtarget = find_empty(fd, slot_offset, use_slots);
1666 dbprintf(("COM_SLOT: find_empty %d\n", oldtarget));
1670 * TODO check if the request slot for the unload is empty
1674 if ((oldtarget)!=target) {
1677 eject_tape(scsitapedevice, need_eject);
1680 * If we have an SCSI path to the tape and an raw io path
1681 * try to read the Error Counter and the label
1683 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1685 LogSense(INDEX_TAPE);
1689 (void)unload(fd, drive_num, oldtarget);
1690 if (ask_clean(scsitapedevice))
1691 clean_tape(fd, tape_device, clean_file, drive_num,
1692 clean_slot, maxclean, time_file);
1697 put_current_slot(slot_file, target);
1699 if (!loaded && isempty(fd, target)) {
1700 printf("%d slot %d is empty\n",target - slot_offset,
1701 target - slot_offset);
1707 if (!loaded && param_index != SLOT_ADVANCE)
1709 if (ask_clean(scsitapedevice))
1710 clean_tape(fd, tape_device, clean_file, drive_num,
1711 clean_slot, maxclean, time_file);
1712 if (load(fd, drive_num, target) != 0) {
1713 printf("%d slot %d move failed\n",target - slot_offset,
1714 target - slot_offset);
1723 if (pDev[INDEX_TAPECTL].inqdone == 1)
1725 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1727 printf("tape not ready\n");
1732 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1734 printf("tape not ready\n");
1741 printf("%d %s\n", target - slot_offset, tape_device);
1745 loaded = (int)get_current_slot(slot_file);
1749 loaded = find_empty(fd, slot_offset, use_slots);
1751 loaded = loaded - (int)slot_offset;
1753 printf("%d %d 1", loaded, use_slots);
1755 if (BarCode(fd) == 1 || emubarcode == 1)
1764 target=get_current_slot(slot_file);
1768 dbprintf(("COM_RESET: get_current_slot %d\n", target));
1769 target = find_empty(fd, slot_offset, use_slots);
1770 dbprintf(("COM_RESET: find_empty %d\n", target));
1775 if (!isempty(fd, target))
1776 target = find_empty(fd, slot_offset, use_slots);
1780 eject_tape(scsitapedevice, need_eject);
1783 * If we have an SCSI path to the tape and an raw io path
1784 * try to read the Error Counter and the label
1786 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1788 LogSense(INDEX_TAPE);
1792 (void)unload(fd, drive_num, target);
1793 if (ask_clean(scsitapedevice))
1794 clean_tape(fd,tape_device, clean_file, drive_num, clean_slot,
1795 maxclean, time_file);
1798 if (isempty(fd, slot_offset)) {
1799 printf("0 slot 0 is empty\n");
1805 if (load(fd, drive_num, slot_offset) != 0) {
1806 printf("%d slot %d move failed\n",
1807 drive_num, slot_offset);
1809 put_current_slot(slot_file, slot_offset);
1814 put_current_slot(slot_file, slot_offset);
1818 if (pDev[INDEX_TAPECTL].inqdone == 1)
1820 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1822 printf("tape not ready\n");
1827 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1829 printf("tape not ready\n");
1836 printf("%d %s\n", slot_offset, tape_device);
1841 target = get_current_slot(slot_file);
1844 dbprintf(("COM_EJECT: get_current_slot %d\n", target));
1845 target = find_empty(fd, slot_offset, use_slots);
1846 dbprintf(("COM_EJECT: find_empty %d\n", target));
1851 eject_tape(scsitapedevice, need_eject);
1853 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1855 LogSense(INDEX_TAPE);
1860 (void)unload(fd, drive_num, target);
1861 if (ask_clean(scsitapedevice))
1862 clean_tape(fd, tape_device, clean_file, drive_num, clean_slot,
1863 maxclean, time_file);
1864 printf("%d %s\n", target, tape_device);
1866 printf("%d %s\n", target, "drive was not loaded");
1872 target = get_current_slot(slot_file);
1875 dbprintf(("COM_CLEAN: get_current_slot %d\n", target));
1876 target = find_empty(fd, slot_offset, use_slots);
1877 dbprintf(("COM_CLEAN: find_empty %d\n",target));
1882 eject_tape(scsitapedevice, need_eject);
1884 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1886 LogSense(INDEX_TAPE);
1890 (void)unload(fd, drive_num, target);
1893 clean_tape(fd, tape_device, clean_file, drive_num, clean_slot,
1894 maxclean, time_file);
1895 printf("%s cleaned\n", tape_device);
1899 /* FIX ME, should be an function to close the device */
1900 /* if (pChangerDev != NULL) */
1901 /* close(pChangerDev->fd); */
1903 /* if (pTapeDev != NULL) */
1904 /* close(pTapeDev->fd); */
1906 /* if (pTapeDevCtl != NULL) */
1907 /* close(pTapeDevCtl->fd); */
1911 if (do_inventory == 1 && endstatus == 0 && changer->labelfile != NULL)
1913 if (changer->autoinv == 1)
1915 DebugPrint(DEBUG_INFO,SECTION_INFO, "Do an inventory \n");
1916 Inventory(changer->labelfile, drive_num, changer->eject,
1919 DebugPrint(DEBUG_INFO,SECTION_INFO, "Set all entrys in DB to invalid\n");
1920 memset(pbarcoderes, 0 , SIZEOF(MBC_T));
1921 pbarcoderes->action = RESET_VALID;
1922 MapBarCode(changer->labelfile,pbarcoderes);
1926 DebugPrint(DEBUG_INFO,SECTION_INFO,"Exit status -> %d\n", endstatus);
1932 * indent-tabs-mode: nil