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"
73 char *tapestatfile = NULL;
74 FILE *debug_file = NULL;
77 * So we have 3 devices, here will all the infos be stored after an
81 OpenFiles_T *pDev = NULL;
83 /* Defined in scsi-changer-driver.c
85 extern int ElementStatusValid;
86 extern ElementInfo_T *pMTE; /*Medium Transport Element */
87 extern ElementInfo_T *pSTE; /*Storage Element */
88 extern ElementInfo_T *pIEE; /*Import Export Element */
89 extern ElementInfo_T *pDTE; /*Data Transfer Element */
90 extern size_t MTE; /*Counter for the above element types */
95 int do_inventory = 0; /* Set if load/unload functions thinks an inventory should be done */
99 NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM,
100 CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE, LABELFILE, CHANGERIDENT,
101 TAPEIDENT, EMUBARCODE, HAVEBARCODE, DEBUGLEVEL, AUTOINV
109 tokentable_t t_table[] = {
110 { "number_configs", NUMDRIVE},
111 { "autoinv", AUTOINV},
114 { "cleanmax", CLEANMAX},
116 { "startuse", START},
118 { "cleancart", CLEAN},
120 { "statfile", STATFILE},
121 { "cleanfile", CLEANFILE},
122 { "drivenum", DRIVENUM},
123 { "changerdev", CHANGERDEV},
124 { "usagecount", USAGECOUNT},
125 { "scsitapedev", SCSITAPEDEV},
126 { "tapestatus", TAPESTATFILE},
127 { "labelfile", LABELFILE},
128 { "changerident", CHANGERIDENT},
129 { "tapeident", TAPEIDENT},
130 { "emubarcode", EMUBARCODE},
131 { "havebarcode", HAVEBARCODE},
132 { "debuglevel", DEBUGLEVEL},
136 int ask_clean(char *tapedev);
137 int get_current_slot(char *count_file);
138 int get_relative_target(int fd, int nslots, char *parameter,
139 int param_index, int loaded, char *slot_file,
140 int slot_offset, int maxslot);
141 int is_positive_number(char *tmp);
142 int MapBarCode(char *labelfile, MBC_T *result);
143 int read_config(char *configfile, changer_t *chg);
144 void clean_tape(int fd, char *tapedev, char *cnt_file, int drivenum,
145 int cleancart, int maxclean, char *usagetime);
146 void dump_changer_struct(changer_t *chg);
147 void free_changer_struct(changer_t **chg);
148 void init_changer_struct(changer_t *chg, int number_of_config);
149 void parse_line(char *linebuffer, int *token,char **value);
150 void put_current_slot(char *count_file, int slot);
151 void usage(char *argv[]);
153 int main(int argc, char *argv[]);
156 /* Initialize data structures with default values */
160 int number_of_config)
164 memset(chg, 0, SIZEOF(*chg));
165 chg->number_of_configs = number_of_config;
167 chg->conf = alloc(SIZEOF(config_t) * (size_t)number_of_config);
168 for (i=0; i < number_of_config; i++){
169 chg->conf[i].drivenum = 0;
170 chg->conf[i].start = -1;
171 chg->conf[i].end = -1;
172 chg->conf[i].cleanslot = -1;
173 chg->conf[i].device = NULL;
174 chg->conf[i].slotfile = NULL;
175 chg->conf[i].cleanfile = NULL;
176 chg->conf[i].timefile = NULL;
177 chg->conf[i].scsitapedev = NULL;
178 chg->conf[i].tapestatfile = NULL;
179 chg->conf[i].changerident = NULL;
180 chg->conf[i].tapeident = NULL;
184 /* Dump of information for debug */
191 dbprintf(_("Number of configurations: %d\n"), chg->number_of_configs);
192 dbprintf(_("Tapes need eject: %s\n"), (chg->eject>0 ? _("Yes") : _("No")));
193 dbprintf (_("\traw: %d\n"),chg->eject);
194 dbprintf(_("Inv. auto update: %s\n"), (chg->autoinv>0 ? _("Yes") : _("No")));
195 dbprintf (_("\traw: %d\n"),chg->autoinv);
196 dbprintf(_("barcode reader : %s\n"), (chg->havebarcode>0 ? _("Yes") : _("No")));
197 dbprintf (_("\traw: %d\n"),chg->havebarcode);
198 dbprintf(_("Emulate Barcode : %s\n"), (chg->emubarcode>0 ? _("Yes") : _("No")));
199 dbprintf (_("\traw: %d\n"),chg->emubarcode);
200 if (chg->debuglevel != NULL)
201 dbprintf(_("debug level : %s\n"), chg->debuglevel);
202 dbprintf(_("Tapes need sleep: %ld seconds\n"), (long int)chg->sleep);
203 dbprintf(_("Clean cycles : %d\n"), chg->cleanmax);
204 dbprintf(_("Changer device : %s\n"), chg->device);
205 if (chg->labelfile != NULL)
206 dbprintf(_("Label file : %s\n"), chg->labelfile);
207 for (i=0; i<chg->number_of_configs; i++){
208 dbprintf(_("Tape config Nr: %d\n"), i);
209 dbprintf(_(" Drive number : %d\n"), chg->conf[i].drivenum);
210 dbprintf(_(" Start slot : %d\n"), chg->conf[i].start);
211 dbprintf(_(" End slot : %d\n"), chg->conf[i].end);
212 dbprintf(_(" Clean slot : %d\n"), chg->conf[i].cleanslot);
214 if (chg->conf[i].device != NULL)
215 dbprintf(_(" Device name : %s\n"), chg->conf[i].device);
217 dbprintf(_(" Device name : none\n"));
219 if (chg->conf[i].changerident != NULL)
220 dbprintf(_(" changer ident : %s\n"), chg->conf[i].changerident);
222 dbprintf(_(" changer ident : none\n"));
224 if (chg->conf[i].scsitapedev != NULL)
225 dbprintf(_(" SCSI Tape dev : %s\n"), chg->conf[i].scsitapedev);
227 dbprintf(_(" SCSI Tape dev : none\n"));
229 if (chg->conf[i].tapeident != NULL)
230 dbprintf(_(" tape ident : %s\n"), chg->conf[i].tapeident);
232 dbprintf(_(" tape ident : none\n"));
234 if (chg->conf[i].tapestatfile != NULL)
235 dbprintf(_(" stat file : %s\n"), chg->conf[i].tapestatfile);
237 dbprintf(_(" stat file : none\n"));
239 if (chg->conf[i].slotfile != NULL)
240 dbprintf(_(" Slot file : %s\n"), chg->conf[i].slotfile);
242 dbprintf(_(" Slot file : none\n"));
244 if (chg->conf[i].cleanfile != NULL)
245 dbprintf(_(" Clean file : %s\n"), chg->conf[i].cleanfile);
247 dbprintf(_(" Clean file : none\n"));
249 if (chg->conf[i].timefile != NULL)
250 dbprintf(_(" Usage count : %s\n"), chg->conf[i].timefile);
252 dbprintf(_(" Usage count : none\n"));
256 /* Free all allocated memory */
265 if (chg->device != NULL)
267 for (i = 0; i < chg->number_of_configs; i++){
268 if (chg->conf[i].device != NULL)
269 amfree(chg->conf[i].device);
270 if (chg->conf[i].slotfile != NULL)
271 amfree(chg->conf[i].slotfile);
272 if (chg->conf[i].cleanfile != NULL)
273 amfree(chg->conf[i].cleanfile);
274 if (chg->conf[i].timefile != NULL)
275 amfree(chg->conf[i].timefile);
277 if (chg->conf != NULL)
284 /* This function parses a line, and returns a token and value */
294 *token = -1; /* No Token found */
295 tok=strtok(linebuffer," \t\n");
297 while ((tok != NULL) && (tok[0]!='#')&&(ready==0)){
303 while ((t_table[i].word != NULL)&&(*token==-1)){
304 if (0==strcasecmp(t_table[i].word,tok)){
305 *token=t_table[i].token;
310 tok=strtok(NULL," \t\n");
314 /* This function reads the specified configfile and fills the structure */
329 numconf = 1; /* At least one configuration is assumed */
330 /* If there are more, it should be the first entry in the configurationfile */
333 if ((file=fopen(configfile,"r")) == NULL) {
337 while ((NULL != (linebuffer = agets(file)))) {
338 if (linebuffer[0] == '\0') {
342 parse_line(linebuffer, &token, &value);
347 if (init_flag == 0) {
348 if (token != NUMDRIVE){
349 init_changer_struct(chg, numconf);
351 numconf = atoi(value);
352 if (numconf < 1 || numconf > 100) {
353 g_fprintf(stderr,_("numconf %d is bad\n"), numconf);
356 init_changer_struct(chg, numconf);
361 case NUMDRIVE: if (atoi(value) != numconf)
362 g_fprintf(stderr,_("Error: number_drives at wrong place, should be "
372 if (chg->debuglevel != NULL) {
373 g_fprintf(stderr,_("Error: debuglevel is specified twice "
374 "(%s then %s).\n"), chg->debuglevel, value);
375 amfree(chg->debuglevel);
377 chg->debuglevel = stralloc(value);
380 chg->eject = atoi(value);
383 chg->havebarcode = atoi(value);
386 chg->sleep = (unsigned)atoi(value);
389 if (chg->labelfile != NULL) {
390 g_fprintf(stderr,_("Error: labelfile is specified twice "
391 "(%s then %s).\n"), chg->labelfile, value);
392 amfree(chg->labelfile);
394 chg->labelfile = stralloc(value);
397 if (chg->device != NULL) {
398 g_fprintf(stderr,_("Error: changerdev is specified twice "
399 "(%s then %s).\n"), chg->device, value);
402 chg->device = stralloc(value);
405 chg->conf[drivenum].scsitapedev = stralloc(value);
408 chg->conf[drivenum].tapestatfile = stralloc(value);
411 chg->conf[drivenum].changerident = stralloc(value);
412 if (drivenum < 0 || drivenum > 100) {
413 g_fprintf(stderr,_("drivenum %d is bad\n"), drivenum);
416 if (strcmp(chg->conf[drivenum].changerident,"generic_changer") != 0) {
417 p = chg->conf[drivenum].changerident;
429 chg->conf[drivenum].tapeident = stralloc(value);
432 chg->cleanmax = atoi(value);
435 drivenum = atoi(value);
437 g_fprintf(stderr,_("Error: drive must be >= 0\n"));
439 } else if (drivenum >= numconf) {
440 g_fprintf(stderr,_("Error: drive must be less than number_drives\n"));
445 if (drivenum < numconf){
446 chg->conf[drivenum].drivenum = atoi(value);
448 g_fprintf(stderr,_("Error: drive is not less than number_drives"
449 " drivenum ignored\n"));
453 if (drivenum < numconf){
454 chg->conf[drivenum].start = atoi(value);
456 g_fprintf(stderr,_("Error: drive is not less than number_drives"
457 " startuse ignored\n"));
461 if (drivenum < numconf){
462 chg->conf[drivenum].end = atoi(value);
464 g_fprintf(stderr,_("Error: drive is not less than number_drives"
465 " enduse ignored\n"));
469 if (drivenum < numconf){
470 chg->conf[drivenum].cleanslot = atoi(value);
472 g_fprintf(stderr,_("Error: drive is not less than number_drives"
473 " cleanslot ignored\n"));
477 if (drivenum < numconf){
478 chg->conf[drivenum].device = stralloc(value);
480 g_fprintf(stderr,_("Error: drive is not less than number_drives"
481 " device ignored\n"));
485 if (drivenum < numconf){
486 chg->conf[drivenum].slotfile = stralloc(value);
488 g_fprintf(stderr,_("Error: drive is not less than number_drives"
489 " slotfile ignored\n"));
493 if (drivenum < numconf){
494 chg->conf[drivenum].cleanfile = stralloc(value);
496 g_fprintf(stderr,_("Error: drive is not less than number_drives"
497 " cleanfile ignored\n"));
501 if (drivenum < numconf){
502 chg->conf[drivenum].timefile = stralloc(value);
504 g_fprintf(stderr,_("Error: drive is not less than number_drives"
505 " usagecount ignored\n"));
509 g_fprintf(stderr,_("Error: Unknown token\n"));
521 /*----------------------------------------------------------------------------*/
523 /* The tape drive does not have an idea of current slot so
524 * we use a file to store the current slot. It is not ideal
525 * but it gets the job done
533 int ret; /* return value for the fscanf function */
534 if ((inf=fopen(count_file,"r")) == NULL) {
535 g_fprintf(stderr, _("%s: unable to open (%s)\n"),
536 get_pname(), count_file);
540 ret = fscanf(inf,"%d",&retval);
544 * check if we got an result
545 * if no set retval to -1
547 if (ret == 0 || ret == EOF)
552 if (retval < 0 || retval > 10000) {
568 if ((inf=fopen(count_file,"w")) == NULL) {
569 g_fprintf(stderr, _("%s: unable to open current slot file (%s)\n"),
570 get_pname(), count_file);
573 g_fprintf(inf, "%d\n", slot);
578 * Here we handle the inventory DB
579 * With this it should be possible to do an mapping
580 * Barcode -> Volume label
581 * Volume Label -> Barcode
582 * Volume label -> Slot number
587 * The passed struct MBC_T will hold the found entry in the DB
606 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : Parameter\n"));
607 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("labelfile -> %s, vol -> %s, barcode -> %s, action -> %c, slot -> %d, from -> %d\n"),
610 result->data.barcode,
615 if (labelfile == NULL)
617 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_("Got empty labelfile (NULL)\n"));
618 ChgExit("MapBarCode", _("MapBarCode name of labelfile is not set\n"),FATAL);
621 if (access(labelfile, F_OK) == -1)
623 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE, _("MapBarCode : creating %s"), labelfile);
624 if ((fp = fopen(labelfile, "w+")) == NULL)
626 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_(" failed\n"));
627 ChgExit("MapBarCode", _("MapBarCode, creating labelfile failed\n"), FATAL);
630 g_fprintf(fp,":%d:", LABEL_DB_VERSION);
634 if ((fp = fopen(labelfile, "r+")) == NULL)
636 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_("MapBarCode : failed to open %s\n"), labelfile);
637 ChgExit("MapBarCode", _("MapBarCode, opening labelfile for read/write failed\n"), FATAL);
641 if (fscanf(fp,":%d:", &version) != 1) {
642 ChgExit("MapBarCode", _("MapBarCode, DB Version unreadable.\n"), FATAL);
645 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : DB version %d\n"), version);
648 if (version != LABEL_DB_VERSION)
650 ChgExit("MapBarCode", _("MapBarCode, DB Version does not match\n"), FATAL);
654 if (( plabelv2 = (LabelV2_T *)alloc(SIZEOF(LabelV2_T))) == NULL)
656 DebugPrint(DEBUG_ERROR,SECTION_MAP_BARCODE,_("MapBarCode : alloc failed\n"));
657 ChgExit("MapBarCode", _("MapBarCode alloc failed\n"), FATAL);
661 memset(plabelv2, 0, SIZEOF(LabelV2_T));
663 while(feof(fp) == 0 && loop == 1)
665 rsize = fread(plabelv2, 1, SIZEOF(LabelV2_T), fp);
666 if (rsize == SIZEOF(LabelV2_T))
669 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : (%d) VolTag \"%s\", BarCode %s, inuse %d, slot %d, from %d, loadcount %d\n"),record,
675 plabelv2->LoadCount);
676 switch (result->action)
682 g_printf(_("Slot -> %d, from -> %d, valid -> %d, Tag -> %s, Barcode -> %s, Loadcount %u\n"),
692 * Set all the record to invalid, used by the Inventory function
696 if (fseek(fp, pos, SEEK_SET) == -1) {
701 if (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T)) {
712 * If it is an invalid record we can use it,
713 * so save the record number.
714 * This value is used at the end if no other
715 * record/action matches.
717 if (plabelv2->valid == 0)
724 * OK this record matches the barcode label
727 if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
730 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : update entry\n"));
731 if (fseek(fp, pos, SEEK_SET) == -1) {
737 plabelv2->from = result->data.from;
738 plabelv2->slot = result->data.slot;
739 plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
740 strncpy(plabelv2->voltag, result->data.voltag,
741 SIZEOF(plabelv2->voltag));
742 strncpy(plabelv2->barcode, result->data.barcode,
743 SIZEOF(plabelv2->barcode));
744 rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
751 * Look for an entry an return the entry
752 * if the voltag (the tape name) matches
755 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
757 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode FIND_SLOT : \n"));
758 memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
765 * reason can be an load, incr the LoadCount
769 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
771 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode UPDATE_SLOT : update entry\n"));
772 if (fseek(fp, pos, SEEK_SET) == -1) {
777 strncpy(plabelv2->voltag, result->data.voltag,
778 SIZEOF(plabelv2->voltag));
779 strncpy(plabelv2->barcode, result->data.barcode,
780 SIZEOF(plabelv2->barcode));
782 plabelv2->slot = result->data.slot;
783 plabelv2->from = result->data.from;
784 plabelv2->LoadCount = plabelv2->LoadCount + result->data.LoadCount;
785 rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
792 * Look for the barcode label of an given volume label
793 * return the slot number and the barcode label.
794 * If the entry is not valid return -1 as slot number
798 * DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode: (%d) inside BARCODE_VOL\n"), record);
799 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("file value: %s, searched for value: %s\n"), plabelv2->voltag, result->data.voltag);
801 if (strcmp(plabelv2->voltag, result->data.voltag) == 0)
803 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : VOL %s match\n"), result->data.voltag);
806 memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
812 * Look for an entry which matches the passed
815 case BARCODE_BARCODE:
816 if (strcmp(plabelv2->barcode, result->data.barcode) == 0)
818 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : BARCODE %s match\n"), result->data.barcode);
821 memcpy(&(result->data), plabelv2, SIZEOF(LabelV2_T));
828 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : unknown action\n"));
833 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : feof (%d)\n"), feof(fp));
834 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : error in read record expect %d, got %d\n"),SIZEOF(LabelV2_T), rsize);
840 * OK, if we come here and the action is either
841 * PUT or update it seems that we have to create a new
842 * record, becuae none of the exsisting records matches
844 if (result->action == BARCODE_PUT || result->action == UPDATE_SLOT )
847 * If we have an entry where the valid flag was set to 0
848 * we can use this record, so seek to this position
849 * If we have no record for reuse the new record will be written to the end.
853 DebugPrint(DEBUG_INFO,SECTION_MAP_BARCODE,_("MapBarCode : reuse record %d\n"), unusedrec);
854 if (fseek(fp, unusedpos, SEEK_SET) == -1) {
861 * Set all values to zero
863 memset(plabelv2, 0, SIZEOF(LabelV2_T));
865 strncpy(plabelv2->voltag, result->data.voltag,
866 SIZEOF(plabelv2->voltag));
867 strncpy(plabelv2->barcode, result->data.barcode,
868 SIZEOF(plabelv2->barcode));
870 plabelv2->from = result->data.from;
871 plabelv2->slot = result->data.slot;
872 rc = (fwrite(plabelv2, 1, SIZEOF(LabelV2_T), fp) < SIZEOF(LabelV2_T));
879 * If we hit this point nothing was
880 * found, so return an 0
887 /* ----------------------------------------------------------------------
888 This stuff deals with parsing the command line */
890 typedef struct com_arg
898 typedef struct com_stru
904 void parse_args(int argc, char *argv[],command *rval);
906 /* major command line args */
917 #define COM_INVENTORY 9
918 #define COM_DUMPDB 10
920 #define COM_GEN_CONF 12
921 argument argdefs[]={{"-slot",COM_SLOT,1},
922 {"-info",COM_INFO,0},
923 {"-reset",COM_RESET,0},
924 {"-eject",COM_EJECT,0},
925 {"-clean",COM_CLEAN,0},
926 {"-label",COM_LABEL,1},
927 {"-search",COM_SEARCH,1},
928 {"-status",COM_STATUS,1},
929 {"-trace",COM_TRACE,1},
930 {"-inventory", COM_INVENTORY,0},
931 {"-dumpdb", COM_DUMPDB,0},
932 {"-scan", COM_SCAN,0},
933 {"-genconf", COM_GEN_CONF,0}
937 /* minor command line args */
943 #define SLOT_ADVANCE 5
944 argument slotdefs[]={{"current",SLOT_CUR,0},
945 {"next",SLOT_NEXT,0},
946 {"prev",SLOT_PREV,0},
947 {"first",SLOT_FIRST,0},
948 {"last",SLOT_LAST,0},
949 {"advance",SLOT_ADVANCE,0},
951 #define SLOTCOUNT (int)(sizeof(slotdefs) / sizeof(slotdefs[0]))
953 /* is the string a valid positive int? */
959 if ((tmp==NULL)||(tmp[0]==0))
961 while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0))
974 g_printf(_("%s: Usage error.\n"), argv[0]);
975 for (cnt=0; cnt < COMCOUNT; cnt++){
976 g_printf(" %s %s",argv[0],argdefs[cnt].str);
977 if (argdefs[cnt].takesparam)
978 g_printf(" <param>\n");
994 for (i=0; i < argc; i++)
995 dbprintf(_("ARG [%d] : %s\n"), i, argv[i]);
997 if ((argc<2)||(argc>3))
999 while ((i<COMCOUNT)&&(strcmp(argdefs[i].str,argv[1])))
1003 rval->command_code = argdefs[i].command_code;
1004 if (argdefs[i].takesparam) {
1007 rval->parameter=argv[2];
1016 /* used to find actual slot number from keywords next, prev, first, etc */
1018 get_relative_target(
1030 (void)loaded; /* Quiet unused parameter warning */
1032 current_slot = get_current_slot(slot_file);
1034 if (current_slot > maxslot) {
1035 current_slot = slot_offset;
1037 if (current_slot < slot_offset) {
1038 current_slot = slot_offset;
1041 switch(param_index) {
1043 return current_slot;
1047 if (++current_slot==nslots+slot_offset)
1049 return current_slot;
1052 if (--current_slot<slot_offset)
1054 return current_slot;
1065 g_printf(_("<none> no slot `%s'\n"),parameter);
1071 /* This function should ask the drive if it wants to be cleaned */
1078 ret = get_clean_state(tapedev);
1080 if (ret < 0) /* < 0 means query does not work ... */
1087 /* This function should move the cleaning cartridge into the drive */
1101 if (cleancart == -1 ){
1105 /* Now we should increment the counter */
1106 if (cnt_file != NULL){
1107 mailer = getconf_str(CNF_MAILER);
1108 counter = get_current_slot(cnt_file);
1110 if (counter>=maxclean){
1111 /* Now we should inform the administrator */
1112 char *mail_cmd = NULL;
1114 int mail_pipe_opened = 1;
1115 if (mailer && *mailer != '\0') {
1116 if (getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0 &&
1117 validate_mailto(getconf_str(CNF_MAILTO))) {
1118 mail_cmd = vstralloc(mailer,
1119 " -s", " \"", _("AMANDA PROBLEM: PLEASE FIX"), "\"",
1120 " ", getconf_str(CNF_MAILTO),
1122 if ((mailf = popen(mail_cmd, "w")) == NULL) {
1123 g_printf(_("Mail failed\n"));
1124 error(_("could not open pipe to \"%s\": %s"),
1125 mail_cmd, strerror(errno));
1129 mail_pipe_opened = 0;
1131 g_fprintf(mailf, _("\nNo mail recipient specified, output redirected to stderr"));
1134 mail_pipe_opened = 0;
1136 g_fprintf(mailf, _("\nNo mailer specified; output redirected to stderr"));
1138 g_fprintf(mailf,_("\nThe usage count of your cleaning tape in slot %d"),
1140 g_fprintf(mailf,_("\nis more than %d. (cleanmax)"),maxclean);
1141 g_fprintf(mailf,_("\nTapedrive %s needs to be cleaned"),tapedev);
1142 g_fprintf(mailf,_("\nPlease insert a new cleaning tape and reset"));
1143 g_fprintf(mailf,_("\nthe countingfile %s"),cnt_file);
1145 if(mail_pipe_opened == 1 && pclose(mailf) != 0) {
1146 error(_("mail command failed: %s"), mail_cmd);
1151 put_current_slot(cnt_file, counter);
1153 load(fd,drivenum,cleancart);
1155 * Hack, sleep for some time
1160 if (drive_loaded(fd, drivenum))
1161 unload(fd, drivenum, cleancart);
1165 /* ----------------------------------------------------------------------*/
1173 int target, oldtarget;
1174 command com; /* a little DOS joke */
1178 * drive_num really should be something from the config file, but..
1179 * for now, it is set to zero, since most of the common changers
1180 * used by amanda only have one drive ( until someone wants to
1181 * use an EXB60/120, or a Breece Hill Q45.. )
1183 unsigned char emubarcode;
1185 int need_eject; /* Does the drive need an eject command ? */
1186 time_t need_sleep; /* How many seconds to wait for the drive to get ready */
1193 * For the emubarcode stuff
1206 char *chg_scsi_conf; /* The config file for us */
1207 char *slot_file; /* Where we will place the info which
1210 char *scsitapedevice;
1212 int param_index = 0;
1215 * Configure program for internationalization:
1216 * 1) Only set the message locale for now.
1217 * 2) Set textdomain for all amanda related programs to "amanda"
1218 * We don't want to be forced to support dozens of message catalogs.
1220 setlocale(LC_MESSAGES, "C");
1221 textdomain("amanda");
1223 changer = alloc(SIZEOF(changer_t));
1224 pbarcoderes = alloc(SIZEOF(MBC_T));
1226 memset(pbarcoderes, 0 , SIZEOF(MBC_T));
1227 changer->number_of_configs = 0;
1230 changer->cleanmax = 0;
1231 changer->device = NULL;
1232 changer->labelfile = NULL;
1233 changer->conf = NULL;
1234 #ifdef CHG_SCSI_STANDALONE
1235 g_printf(_("Ups standalone\n"));
1237 set_pname("chg-scsi");
1239 /* Don't die when child closes pipe */
1240 signal(SIGPIPE, SIG_IGN);
1242 dbopen(DBG_SUBDIR_SERVER);
1244 dbprintf("chg-scsi: %s\n", rcsid);
1245 ChangerDriverVersion();
1247 if (debug_file == NULL)
1249 debug_file = dbfp();
1252 parse_args(argc,argv,&com);
1254 pDev = (OpenFiles_T *)alloc(SIZEOF(OpenFiles_T) * CHG_MAXDEV);
1255 memset(pDev, 0, SIZEOF(OpenFiles_T) * CHG_MAXDEV );
1258 switch(com.command_code)
1273 config_init(CONFIG_INIT_USE_CWD, NULL);
1275 if (config_errors(NULL) >= CFGERR_WARNINGS) {
1276 config_print_errors();
1277 if (config_errors(NULL) >= CFGERR_ERRORS) {
1278 g_critical(_("errors processing config file"));
1282 chg_scsi_conf = getconf_str(CNF_CHANGERFILE);
1283 tape_device = getconf_str(CNF_TAPEDEV);
1285 /* Get the configuration parameters */
1286 /* Attention, this will not support more than 10 tape devices 0-9 */
1288 if (strlen(tape_device)==1){
1289 if (read_config(chg_scsi_conf, changer) == -1)
1291 g_fprintf(stderr, _("%s open: of %s failed\n"), get_pname(), chg_scsi_conf);
1294 confnum=atoi(tape_device);
1295 if (changer->number_of_configs == 0)
1297 g_fprintf(stderr,_("%s: changer->conf[%d] == NULL\n"),
1298 get_pname(), confnum);
1301 if (confnum >= changer->number_of_configs) {
1302 g_fprintf(stderr,_("%s: Configuration %s config # out of range (%d >= %d)\n"),
1303 get_pname(), chg_scsi_conf,
1305 changer->number_of_configs);
1309 use_slots = changer->conf[confnum].end-changer->conf[confnum].start+1;
1310 slot_offset = changer->conf[confnum].start;
1311 drive_num = changer->conf[confnum].drivenum;
1312 need_eject = changer->eject;
1313 need_sleep = changer->sleep;
1315 if ( NULL != changer->conf[confnum].cleanfile)
1316 clean_file = stralloc(changer->conf[confnum].cleanfile);
1320 clean_slot = changer->conf[confnum].cleanslot;
1321 maxclean = changer->cleanmax;
1322 emubarcode = changer->emubarcode;
1323 if (NULL != changer->conf[confnum].timefile)
1324 time_file = stralloc(changer->conf[confnum].timefile);
1328 if (NULL != changer->conf[confnum].slotfile)
1329 slot_file = stralloc(changer->conf[confnum].slotfile);
1333 if (NULL != changer->conf[confnum].device)
1334 tape_device = stralloc(changer->conf[confnum].device);
1338 if (NULL != changer->device)
1339 changer_dev = stralloc(changer->device);
1343 if (NULL != changer->conf[confnum].scsitapedev)
1344 scsitapedevice = stralloc(changer->conf[confnum].scsitapedev);
1346 scsitapedevice = NULL;
1348 if (NULL != changer->conf[confnum].tapestatfile)
1349 tapestatfile = stralloc(changer->conf[confnum].tapestatfile);
1351 tapestatfile = NULL;
1352 dump_changer_struct(changer);
1357 * The changer device.
1358 * If we can't open it fail with a message
1361 if (OpenDevice(INDEX_CHANGER , changer_dev, "changer_dev", changer->conf[confnum].changerident) == 0)
1363 int localerr = errno;
1364 g_fprintf(stderr, _("%s: open: %s: %s\n"), get_pname(),
1365 changer_dev, strerror(localerr));
1366 g_printf(_("%s open: %s: %s\n"), _("<none>"), changer_dev, strerror(localerr));
1367 dbprintf(_("open: %s: %s\n"), changer_dev, strerror(localerr));
1376 * eject if eject is set
1377 * inventory (reading of the labels) if emubarcode (not yet)
1379 if (tape_device != NULL)
1381 if (OpenDevice(INDEX_TAPE, tape_device, "tape_device", changer->conf[confnum].tapeident) == 0)
1383 dbprintf(_("warning open of %s: failed\n"), tape_device);
1388 * This is for the status pages of the SCSI tape, nice to have but no must....
1390 if (scsitapedevice != NULL)
1392 if (OpenDevice(INDEX_TAPECTL, scsitapedevice, "scsitapedevice", changer->conf[confnum].tapeident) == 0)
1394 dbprintf(_("warning open of %s: failed\n"), scsitapedevice);
1400 * So if we need eject we need either an raw device to eject with an ioctl,
1401 * or an SCSI device to send the SCSI eject.
1404 if (need_eject != 0 )
1406 if (pDev[INDEX_TAPE].avail == 0 && pDev[INDEX_TAPECTL].avail == 0)
1408 g_printf(_("No device found for tape eject"));
1414 if ((changer->conf[confnum].end == -1) || (changer->conf[confnum].start == -1)){
1415 slotcnt = get_slot_count(fd);
1416 use_slots = slotcnt;
1421 * Now check if we have all what we need
1422 * If either emubarcode is set or the changer support barcode
1423 * we need an label file
1426 if ( changer->emubarcode == 1 || BarCode(INDEX_CHANGER) == 1)
1428 if (changer->labelfile == NULL)
1430 g_printf(_("labelfile param not set in your config\n"));
1435 if (slot_file == NULL)
1437 g_printf(_("slotfile param. not set in your config\n"));
1441 if (access(slot_file,R_OK|W_OK) != 0)
1443 g_printf(_("slotfile %s does not exsist or is not read/write\n"), slot_file);
1447 } else { /* if (strlen(tape_device)==1) */
1448 g_printf(_("please check your config and use a config file for chg-scsi\n"));
1452 drivecnt = get_drive_count(fd);
1454 if (drive_num > drivecnt) {
1455 g_printf(_("%s drive number error (%d > %d)\n"), _("<none>"),
1456 drive_num, drivecnt);
1457 g_fprintf(stderr, _("%s: requested drive number (%d) greater than "
1458 "number of supported drives (%d)\n"), get_pname(),
1459 drive_num, drivecnt);
1460 dbprintf(_("requested drive number (%d) is greater than "
1461 "number of supported drives (%d)\n"), drive_num, drivecnt);
1465 loaded = (int)drive_loaded(fd, drive_num);
1468 switch(com.command_code) {
1469 /* This is only for the experts ;-) */
1471 ChangerReplay(com.parameter);
1476 pbarcoderes->action = BARCODE_DUMP;
1477 MapBarCode(changer->labelfile, pbarcoderes);
1480 ChangerStatus(com.parameter, changer->labelfile,
1481 BarCode(fd), slot_file, changer_dev, tape_device);
1483 case COM_LABEL: /* Update BarCode/Label mapping file */
1484 pbarcoderes->action = BARCODE_PUT;
1485 strncpy(pbarcoderes->data.voltag, com.parameter,
1486 SIZEOF(pbarcoderes->data.voltag));
1487 strncpy(pbarcoderes->data.barcode, pDTE[drive_num].VolTag,
1488 SIZEOF(pbarcoderes->data.barcode));
1489 MapBarCode(changer->labelfile, pbarcoderes);
1490 g_printf("0 0 0\n");
1494 * Inventory does an scan of the library and updates the mapping in the label DB
1497 do_inventory = 1; /* Tell the label check not to exit on label errors */
1500 oldtarget = get_current_slot(slot_file);
1503 dbprintf(_("COM_INVENTORY: get_current_slot %d\n"), oldtarget);
1504 oldtarget = find_empty(fd, slot_offset, use_slots);
1505 dbprintf(_("COM_INVENTORY: find_empty %d\n"), oldtarget);
1510 eject_tape(scsitapedevice, need_eject);
1512 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1514 LogSense(INDEX_TAPE);
1518 (void)unload(fd, drive_num, oldtarget);
1519 if (ask_clean(scsitapedevice))
1520 clean_tape(fd,tape_device,clean_file,drive_num,
1521 clean_slot, maxclean, time_file);
1523 Inventory(changer->labelfile, drive_num, need_eject, 0, 0, clean_slot);
1524 do_inventory = 0; /* If set on exit the labeldb will be set to invalid ..... */
1528 * Search for the tape, the index is the volume label
1533 * If we have an barcode reader use
1536 if (BarCode(fd) == 1 && emubarcode != 1)
1538 dbprintf(_("search : look for %s\n"), com.parameter);
1539 pbarcoderes->action = BARCODE_VOL;
1540 pbarcoderes->data.slot = -1;
1541 strncpy(pbarcoderes->data.voltag, com.parameter,
1542 SIZEOF(pbarcoderes->data.voltag));
1543 if (MapBarCode(changer->labelfile, pbarcoderes) == 1)
1546 * If both values are unset we have an problem
1547 * so leave the program
1549 if (pbarcoderes->data.slot == -1 && pbarcoderes->data.barcode == NULL)
1551 g_printf(_("Label %s not found (1)\n"),com.parameter);
1559 * Let's see, if we got an barcode check if it is
1560 * in the current inventory
1562 if (pbarcoderes->data.barcode != NULL)
1565 for (x = 0; x < (int)STE; x++)
1567 if (strcmp(pSTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1569 dbprintf(_("search : found slot %d\n"), x);
1574 * Not found in the STE slots
1575 * my be in the DTE (tape)
1576 * If we find it check if it is in the right drive
1577 * if we have more than one drive.
1579 for (x = 0; x < (int)DTE; x++)
1581 if (strcmp(pDTE[x].VolTag, pbarcoderes->data.barcode) == 0)
1583 dbprintf(_("search : found in tape %d\n"), x);
1586 if (x == drive_num) {
1587 oldtarget = get_current_slot(slot_file);
1588 g_printf("%d %s\n", oldtarget - slot_offset, tape_device);
1591 g_printf(_("LABEL in wrong tape Unit\n"));
1597 * not found, so do an exit...
1601 g_printf(_("Label %s not found (2) \n"),com.parameter);
1606 } /* if barcode[0] != 0 */
1609 * If we didn't find anything we will try the info
1610 * from the DB. A reason for not finding anything in the inventory
1611 * might be an unreadable barcode label
1613 if (target == -1 && pbarcoderes->data.slot != -1)
1615 target = pbarcoderes->data.slot;
1619 * OK, if target is still -1 do the exit
1623 g_printf(_("Label %s not found (3)\n"),com.parameter);
1633 * And now if we have emubarcode set and no barcode reader
1636 if (emubarcode == 1 && BarCode(fd) != 1)
1638 dbprintf(_("search : look for %s\n"), com.parameter);
1639 pbarcoderes->action = FIND_SLOT;
1640 pbarcoderes->data.slot = -1;
1641 strncpy(pbarcoderes->data.voltag, com.parameter,
1642 SIZEOF(pbarcoderes->data.voltag));
1644 if (MapBarCode(changer->labelfile, pbarcoderes) == 1)
1646 if (pbarcoderes->data.valid == 1)
1648 target = pbarcoderes->data.slot;
1650 g_printf(_("Barcode DB out of sync \n"));
1656 g_printf(_("Label %s not found \n"),com.parameter);
1664 * The slot changing command
1669 if (is_positive_number(com.parameter)) {
1670 if ((target = atoi(com.parameter))>=use_slots) {
1671 g_printf(_("<none> no slot `%d'\n"),target);
1676 target = target + slot_offset;
1680 while((param_index < SLOTCOUNT) &&
1681 (strcmp(slotdefs[param_index].str,com.parameter))) {
1684 target=get_relative_target(fd, use_slots,
1685 com.parameter,param_index,
1689 (slot_offset + use_slots - 1));
1694 oldtarget = get_current_slot(slot_file);
1697 dbprintf(_("COM_SLOT: get_current_slot %d\n"), oldtarget);
1698 oldtarget = find_empty(fd, slot_offset, use_slots);
1699 dbprintf(_("COM_SLOT: find_empty %d\n"), oldtarget);
1703 * TODO check if the request slot for the unload is empty
1707 if ((oldtarget)!=target) {
1710 eject_tape(scsitapedevice, need_eject);
1713 * If we have an SCSI path to the tape and an raw io path
1714 * try to read the Error Counter and the label
1716 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1718 LogSense(INDEX_TAPE);
1722 (void)unload(fd, drive_num, oldtarget);
1723 if (ask_clean(scsitapedevice))
1724 clean_tape(fd, tape_device, clean_file, drive_num,
1725 clean_slot, maxclean, time_file);
1730 put_current_slot(slot_file, target);
1732 if (!loaded && isempty(fd, target)) {
1733 g_printf(_("%d slot %d is empty\n"),target - slot_offset,
1734 target - slot_offset);
1740 if (!loaded && param_index != SLOT_ADVANCE)
1742 if (ask_clean(scsitapedevice))
1743 clean_tape(fd, tape_device, clean_file, drive_num,
1744 clean_slot, maxclean, time_file);
1745 if (load(fd, drive_num, target) != 0) {
1746 g_printf(_("%d slot %d move failed\n"),target - slot_offset,
1747 target - slot_offset);
1756 if (pDev[INDEX_TAPECTL].inqdone == 1)
1758 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1760 g_printf(_("tape not ready\n"));
1765 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1767 g_printf(_("tape not ready\n"));
1774 g_printf("%d %s\n", target - slot_offset, tape_device);
1778 loaded = (int)get_current_slot(slot_file);
1782 loaded = find_empty(fd, slot_offset, use_slots);
1784 loaded = loaded - (int)slot_offset;
1786 g_printf("%d %d 1", loaded, use_slots);
1788 if (BarCode(fd) == 1 || emubarcode == 1)
1797 target=get_current_slot(slot_file);
1801 dbprintf(_("COM_RESET: get_current_slot %d\n"), target);
1802 target = find_empty(fd, slot_offset, use_slots);
1803 dbprintf(_("COM_RESET: find_empty %d\n"), target);
1808 if (!isempty(fd, target))
1809 target = find_empty(fd, slot_offset, use_slots);
1813 eject_tape(scsitapedevice, need_eject);
1816 * If we have an SCSI path to the tape and an raw io path
1817 * try to read the Error Counter and the label
1819 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1821 LogSense(INDEX_TAPE);
1825 (void)unload(fd, drive_num, target);
1826 if (ask_clean(scsitapedevice))
1827 clean_tape(fd,tape_device, clean_file, drive_num, clean_slot,
1828 maxclean, time_file);
1831 if (isempty(fd, slot_offset)) {
1832 g_printf(_("0 slot 0 is empty\n"));
1838 if (load(fd, drive_num, slot_offset) != 0) {
1839 g_printf(_("%d slot %d move failed\n"),
1840 drive_num, slot_offset);
1842 put_current_slot(slot_file, slot_offset);
1847 put_current_slot(slot_file, slot_offset);
1851 if (pDev[INDEX_TAPECTL].inqdone == 1)
1853 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1855 g_printf(_("tape not ready\n"));
1860 if (Tape_Ready(INDEX_TAPECTL, need_sleep) == -1)
1862 g_printf(_("tape not ready\n"));
1869 g_printf(_("%d %s\n"), slot_offset, tape_device);
1874 target = get_current_slot(slot_file);
1877 dbprintf(_("COM_EJECT: get_current_slot %d\n"), target);
1878 target = find_empty(fd, slot_offset, use_slots);
1879 dbprintf(_("COM_EJECT: find_empty %d\n"), target);
1884 eject_tape(scsitapedevice, need_eject);
1886 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1888 LogSense(INDEX_TAPE);
1893 (void)unload(fd, drive_num, target);
1894 if (ask_clean(scsitapedevice))
1895 clean_tape(fd, tape_device, clean_file, drive_num, clean_slot,
1896 maxclean, time_file);
1897 g_printf("%d %s\n", target, tape_device);
1899 g_printf(_("%d drive was not loaded\n"), target);
1905 target = get_current_slot(slot_file);
1908 dbprintf(_("COM_CLEAN: get_current_slot %d\n"), target);
1909 target = find_empty(fd, slot_offset, use_slots);
1910 dbprintf(_("COM_CLEAN: find_empty %d\n"),target);
1915 eject_tape(scsitapedevice, need_eject);
1917 if (pDev[INDEX_TAPECTL].avail == 1 && pDev[INDEX_TAPE].avail == 1)
1919 LogSense(INDEX_TAPE);
1923 (void)unload(fd, drive_num, target);
1926 clean_tape(fd, tape_device, clean_file, drive_num, clean_slot,
1927 maxclean, time_file);
1928 g_printf(_("%s cleaned\n"), tape_device);
1932 /* FIX ME, should be an function to close the device */
1933 /* if (pChangerDev != NULL) */
1934 /* close(pChangerDev->fd); */
1936 /* if (pTapeDev != NULL) */
1937 /* close(pTapeDev->fd); */
1939 /* if (pTapeDevCtl != NULL) */
1940 /* close(pTapeDevCtl->fd); */
1944 if (do_inventory == 1 && endstatus == 0 && changer->labelfile != NULL)
1946 if (changer->autoinv == 1)
1948 DebugPrint(DEBUG_INFO,SECTION_INFO, _("Do an inventory \n"));
1949 Inventory(changer->labelfile, drive_num, changer->eject,
1952 DebugPrint(DEBUG_INFO,SECTION_INFO, _("Set all entrys in DB to invalid\n"));
1953 memset(pbarcoderes, 0 , SIZEOF(MBC_T));
1954 pbarcoderes->action = RESET_VALID;
1955 MapBarCode(changer->labelfile,pbarcoderes);
1959 DebugPrint(DEBUG_INFO,SECTION_INFO,_("Exit status -> %d\n"), endstatus);
1965 * indent-tabs-mode: nil