📄 chg-scsi.c
字号:
static char rcsid[] = "$Id: chg-scsi.c,v 1.52 2006/07/25 18:18:46 martinea Exp $";/* * * * chg-scsi.c -- generic SCSI changer driver * * This program provides the framework to control * SCSI changers. It is based on the original chg-scsi * from Eric Schnoebelen <eric@cirr.com> (Original copyright below) * The device dependent part is handled by scsi-changer-driver.c * The SCSI OS interface is handled by scsi-ostype.c * * Original copyrights: * * This program provides a driver to control generic * SCSI changers, no matter what platform. The host/OS * specific portions of the interface are implemented * in libscsi.a, which contains a module for each host/OS. * The actual interface for HP/UX is in scsi-hpux.c; * chio is in scsi-chio.c, etc.. A prototype system * dependent scsi interface file is in scsi-proto.c. * * Copyright 1997, 1998 Eric Schnoebelen <eric@cirr.com> * * This module based upon seagate-changer, by Larry Pyeatt * <pyeatt@cs.colostate.edu> * * The original introductory comments follow: * * This program was written to control the Seagate/Conner/Archive * autoloading DAT drive. This drive normally has 4 tape capacity * but can be expanded to 12 tapes with an optional tape cartridge. * This program may also work on onther drives. Try it and let me * know of successes/failures. * * I have attempted to conform to the requirements for Amanda tape * changer interface. There could be some bugs. * * This program works for me under Linux with Gerd Knorr's * <kraxel@cs.tu-berlin.de> SCSI media changer driver installed * as a kernel module. The kernel module is available at * http://sunsite.unc.edu/pub/Linux/kernel/patches/scsi/scsi-changer* * Since the Linux media changer is based on NetBSD, this program * should also work for NetBSD, although I have not tried it. * It may be necessary to change the IOCTL calls to work on other * OS's. * * (c) 1897 Larry Pyeatt, pyeatt@cs.colostate.edu * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. The author makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * Michael C. Povel 03.06.98 added ejetct_tape and sleep for external tape * devices, and changed some code to allow multiple drives to use their * own slots. Also added support for reserverd slots. * At the moment these parameters are hard coded and only tested under Linux * */#include "config.h"#include "amanda.h"#include "conffile.h"#include "libscsi.h"#include "scsi-defs.h"#include "tapeio.h"char *tapestatfile = NULL;FILE *debug_file = NULL;/* * So we have 3 devices, here will all the infos be stored after an * successfull open */OpenFiles_T *pDev = NULL;/* Defined in scsi-changer-driver.c */extern int ElementStatusValid;extern ElementInfo_T *pMTE; /*Medium Transport Element */extern ElementInfo_T *pSTE; /*Storage Element */extern ElementInfo_T *pIEE; /*Import Export Element */extern ElementInfo_T *pDTE; /*Data Transfer Element */extern size_t MTE; /*Counter for the above element types */extern size_t STE;extern size_t IEE;extern size_t DTE;int do_inventory = 0; /* Set if load/unload functions thinks an inventory should be done */int clean_slot = -1;typedef enum{ NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM, CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE, LABELFILE, CHANGERIDENT, TAPEIDENT, EMUBARCODE, HAVEBARCODE, DEBUGLEVEL, AUTOINV } token_t;typedef struct { char *word; token_t token;} tokentable_t;tokentable_t t_table[] = { { "number_configs", NUMDRIVE}, { "autoinv", AUTOINV}, { "eject", EJECT}, { "sleep", SLEEP}, { "cleanmax", CLEANMAX}, { "config", DRIVE}, { "startuse", START}, { "enduse", END}, { "cleancart", CLEAN}, { "dev", DEVICE}, { "statfile", STATFILE}, { "cleanfile", CLEANFILE}, { "drivenum", DRIVENUM}, { "changerdev", CHANGERDEV}, { "usagecount", USAGECOUNT}, { "scsitapedev", SCSITAPEDEV}, { "tapestatus", TAPESTATFILE}, { "labelfile", LABELFILE}, { "changerident", CHANGERIDENT}, { "tapeident", TAPEIDENT}, { "emubarcode", EMUBARCODE}, { "havebarcode", HAVEBARCODE}, { "debuglevel", DEBUGLEVEL}, { NULL, -1 }};changer_t *changer;int ask_clean(char *tapedev);int get_current_slot(char *count_file);int get_relative_target(int fd, int nslots, char *parameter, int param_index, int loaded, char *slot_file, int slot_offset, int maxslot);int is_positive_number(char *tmp);int MapBarCode(char *labelfile, MBC_T *result);int read_config(char *configfile, changer_t *chg);void clean_tape(int fd, char *tapedev, char *cnt_file, int drivenum, int cleancart, int maxclean, char *usagetime);void dump_changer_struct(changer_t *chg);void free_changer_struct(changer_t **chg);void init_changer_struct(changer_t *chg, int number_of_config);void parse_line(char *linebuffer, int *token,char **value);void put_current_slot(char *count_file, int slot);void usage(char *argv[]);int main(int argc, char *argv[]);/* Initialize data structures with default values */voidinit_changer_struct( changer_t *chg, int number_of_config){ int i; memset(chg, 0, SIZEOF(*chg)); chg->number_of_configs = number_of_config; chg->eject = 1; chg->conf = alloc(SIZEOF(config_t) * (size_t)number_of_config); for (i=0; i < number_of_config; i++){ chg->conf[i].drivenum = 0; chg->conf[i].start = -1; chg->conf[i].end = -1; chg->conf[i].cleanslot = -1; chg->conf[i].device = NULL; chg->conf[i].slotfile = NULL; chg->conf[i].cleanfile = NULL; chg->conf[i].timefile = NULL; chg->conf[i].scsitapedev = NULL; chg->conf[i].tapestatfile = NULL; chg->conf[i].changerident = NULL; chg->conf[i].tapeident = NULL; }}/* Dump of information for debug */voiddump_changer_struct( changer_t *chg){ int i; dbprintf(_("Number of configurations: %d\n"), chg->number_of_configs); dbprintf(_("Tapes need eject: %s\n"), (chg->eject>0 ? _("Yes") : _("No"))); dbprintf (_("\traw: %d\n"),chg->eject); dbprintf(_("Inv. auto update: %s\n"), (chg->autoinv>0 ? _("Yes") : _("No"))); dbprintf (_("\traw: %d\n"),chg->autoinv); dbprintf(_("barcode reader : %s\n"), (chg->havebarcode>0 ? _("Yes") : _("No"))); dbprintf (_("\traw: %d\n"),chg->havebarcode); dbprintf(_("Emulate Barcode : %s\n"), (chg->emubarcode>0 ? _("Yes") : _("No"))); dbprintf (_("\traw: %d\n"),chg->emubarcode); if (chg->debuglevel != NULL) dbprintf(_("debug level : %s\n"), chg->debuglevel); dbprintf(_("Tapes need sleep: %ld seconds\n"), (long int)chg->sleep); dbprintf(_("Clean cycles : %d\n"), chg->cleanmax); dbprintf(_("Changer device : %s\n"), chg->device); if (chg->labelfile != NULL) dbprintf(_("Label file : %s\n"), chg->labelfile); for (i=0; i<chg->number_of_configs; i++){ dbprintf(_("Tape config Nr: %d\n"), i); dbprintf(_(" Drive number : %d\n"), chg->conf[i].drivenum); dbprintf(_(" Start slot : %d\n"), chg->conf[i].start); dbprintf(_(" End slot : %d\n"), chg->conf[i].end); dbprintf(_(" Clean slot : %d\n"), chg->conf[i].cleanslot); if (chg->conf[i].device != NULL) dbprintf(_(" Device name : %s\n"), chg->conf[i].device); else dbprintf(_(" Device name : none\n")); if (chg->conf[i].changerident != NULL) dbprintf(_(" changer ident : %s\n"), chg->conf[i].changerident); else dbprintf(_(" changer ident : none\n")); if (chg->conf[i].scsitapedev != NULL) dbprintf(_(" SCSI Tape dev : %s\n"), chg->conf[i].scsitapedev); else dbprintf(_(" SCSI Tape dev : none\n")); if (chg->conf[i].tapeident != NULL) dbprintf(_(" tape ident : %s\n"), chg->conf[i].tapeident); else dbprintf(_(" tape ident : none\n")); if (chg->conf[i].tapestatfile != NULL) dbprintf(_(" stat file : %s\n"), chg->conf[i].tapestatfile); else dbprintf(_(" stat file : none\n")); if (chg->conf[i].slotfile != NULL) dbprintf(_(" Slot file : %s\n"), chg->conf[i].slotfile); else dbprintf(_(" Slot file : none\n")); if (chg->conf[i].cleanfile != NULL) dbprintf(_(" Clean file : %s\n"), chg->conf[i].cleanfile); else dbprintf(_(" Clean file : none\n")); if (chg->conf[i].timefile != NULL) dbprintf(_(" Usage count : %s\n"), chg->conf[i].timefile); else dbprintf(_(" Usage count : none\n")); }}/* Free all allocated memory */voidfree_changer_struct( changer_t **changer){ changer_t *chg; int i; chg = *changer; if (chg->device != NULL) amfree(chg->device); for (i = 0; i < chg->number_of_configs; i++){ if (chg->conf[i].device != NULL) amfree(chg->conf[i].device); if (chg->conf[i].slotfile != NULL) amfree(chg->conf[i].slotfile); if (chg->conf[i].cleanfile != NULL) amfree(chg->conf[i].cleanfile); if (chg->conf[i].timefile != NULL) amfree(chg->conf[i].timefile); } if (chg->conf != NULL) amfree(chg->conf); chg->conf = NULL; chg->device = NULL; amfree(*changer);}/* This function parses a line, and returns a token and value */voidparse_line( char *linebuffer, int *token, char **value){ char *tok; int i; int ready = 0; *token = -1; /* No Token found */ tok=strtok(linebuffer," \t\n"); while ((tok != NULL) && (tok[0]!='#')&&(ready==0)){ if (*token != -1){ *value=tok; ready=1; } else { i=0; while ((t_table[i].word != NULL)&&(*token==-1)){ if (0==strcasecmp(t_table[i].word,tok)){ *token=t_table[i].token; } i++; } } tok=strtok(NULL," \t\n"); }}/* This function reads the specified configfile and fills the structure */intread_config( char *configfile, changer_t *chg){ int numconf; FILE *file; int init_flag = 0; int drivenum=0; char *linebuffer; int token; char *value; char *p; numconf = 1; /* At least one configuration is assumed */ /* If there are more, it should be the first entry in the configurationfile */ assert(chg != NULL); if ((file=fopen(configfile,"r")) == NULL) { return (-1); } while ((NULL != (linebuffer = agets(file)))) { if (linebuffer[0] == '\0') { amfree(linebuffer); continue; } parse_line(linebuffer, &token, &value); if (token != -1){ if (value == NULL) value = "0"; if (init_flag == 0) { if (token != NUMDRIVE){ init_changer_struct(chg, numconf); } else { numconf = atoi(value); if (numconf < 1 || numconf > 100) { g_fprintf(stderr,_("numconf %d is bad\n"), numconf); numconf = 1; } init_changer_struct(chg, numconf); } init_flag=1; } switch (token) { case NUMDRIVE: if (atoi(value) != numconf) g_fprintf(stderr,_("Error: number_drives at wrong place, should be " "first in file\n")); break; case AUTOINV: chg->autoinv = 1; break; case EMUBARCODE: chg->emubarcode = 1; break; case DEBUGLEVEL: chg->debuglevel = stralloc(value); break; case EJECT: chg->eject = atoi(value); break; case HAVEBARCODE: chg->havebarcode = atoi(value); break; case SLEEP: chg->sleep = (unsigned)atoi(value); break; case LABELFILE: chg->labelfile = stralloc(value); break; case CHANGERDEV: chg->device = stralloc(value); break; case SCSITAPEDEV: chg->conf[drivenum].scsitapedev = stralloc(value); break; case TAPESTATFILE: chg->conf[drivenum].tapestatfile = stralloc(value); break; case CHANGERIDENT: chg->conf[drivenum].changerident = stralloc(value); if (drivenum < 0 || drivenum > 100) { g_fprintf(stderr,_("drivenum %d is bad\n"), drivenum); drivenum = 0; } if (strcmp(chg->conf[drivenum].changerident,"generic_changer") != 0) { p = chg->conf[drivenum].changerident; while (*p != '\0') { if (*p == '_') { *p=' '; } p++; } } break; case TAPEIDENT: chg->conf[drivenum].tapeident = stralloc(value); break; case CLEANMAX: chg->cleanmax = atoi(value); break; case DRIVE: drivenum = atoi(value); if (drivenum < 0) { g_fprintf(stderr,_("Error: drive must be >= 0\n")); drivenum = 0; } else if (drivenum >= numconf) { g_fprintf(stderr,_("Error: drive must be less than number_drives\n")); drivenum = numconf; } break; case DRIVENUM: if (drivenum < numconf){ chg->conf[drivenum].drivenum = atoi(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " drivenum ignored\n")); } break; case START: if (drivenum < numconf){ chg->conf[drivenum].start = atoi(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " startuse ignored\n")); } break; case END: if (drivenum < numconf){ chg->conf[drivenum].end = atoi(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " enduse ignored\n")); } break; case CLEAN: if (drivenum < numconf){ chg->conf[drivenum].cleanslot = atoi(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " cleanslot ignored\n")); } break; case DEVICE: if (drivenum < numconf){ chg->conf[drivenum].device = stralloc(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " device ignored\n")); } break; case STATFILE: if (drivenum < numconf){ chg->conf[drivenum].slotfile = stralloc(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " slotfile ignored\n")); } break; case CLEANFILE: if (drivenum < numconf){ chg->conf[drivenum].cleanfile = stralloc(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " cleanfile ignored\n")); } break; case USAGECOUNT: if (drivenum < numconf){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -