📄 chg-scsi-chio.c
字号:
/* * $Id: chg-scsi-chio.c,v 1.12 2006/07/25 18:18:46 martinea Exp $ * * chg-scsi-chio.c -- generic SCSI changer driver * * 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"int Tape_Ready1 ( char *tapedev , int wait);char *tapestatfile = NULL;/*----------------------------------------------------------------------------*/typedef enum{ NUMDRIVE,EJECT,SLEEP,CLEANMAX,DRIVE,START,END,CLEAN,DEVICE,STATFILE,CLEANFILE,DRIVENUM, CHANGERDEV,USAGECOUNT,SCSITAPEDEV, TAPESTATFILE } token_t;typedef struct { char *word; token_t token;} tokentable_t;tokentable_t t_table[]={ { "number_configs", NUMDRIVE}, { "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}, { NULL, -1 }};changer_t *changer;void init_changer_struct(changer_t *chg, size_t number_of_config);void dump_changer_struct(changer_t *chg);void free_changer_struct(changer_t **changer);void parse_line(char *linebuffer,int *token,char **value);int read_config(char *configfile, changer_t *chg);int get_current_slot(char *count_file);void put_current_slot(char *count_file,int slot);void usage(char *argv[]);int get_relative_target(int fd,int nslots,char *parameter,int loaded, char *changer_file,int slot_offset,int maxslot);int is_positive_number(char *tmp);int ask_clean(char *tapedev);void clean_tape(int fd,char *tapedev,char *cnt_file, int drivenum, int cleancart, int maxclean,char *usagetime);int main(int argc, char *argv[]);/* * Initialize data structures with default values*/voidinit_changer_struct( changer_t * chg, size_t number_of_config){ size_t i; memset(chg, 0, SIZEOF(*chg)); chg->number_of_configs = number_of_config; chg->eject = 1; chg->sleep = 0; chg->cleanmax = 0; chg->device = NULL; chg->conf = alloc(SIZEOF(config_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(_("Tapes need sleep: %lld seconds\n"), (long long)chg->sleep); dbprintf(_("Clean cycles : %d\n"), chg->cleanmax); dbprintf(_("Changer device : %s\n"), chg->device); 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].scsitapedev != NULL) dbprintf(_(" SCSI Tape dev : %s\n"), chg->conf[i].scsitapedev); else dbprintf(_(" SCSI Tape dev : 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; assert(changer != NULL); assert(*changer != NULL); 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"); } return;}/* * This function reads the specified configfile and fills the structure*/intread_config( char * configfile, changer_t * chg){ size_t numconf; FILE *file; int init_flag = 0; size_t drivenum=0; char *linebuffer; int token; char *value; numconf = 1; /* At least one configuration is assumed */ /* If there are more, it should be the first entry in the configurationfile */ if (NULL==(file=fopen(configfile,"r"))){ return (-1); } while (NULL != (linebuffer = agets(file))) { if (linebuffer[0] == '\0') { amfree(linebuffer); continue; } parse_line(linebuffer,&token,&value); if (token != -1){ if (0==init_flag) { if (token != NUMDRIVE){ init_changer_struct(chg, numconf); } else { numconf = atoi(value); init_changer_struct(chg, numconf); } init_flag=1; } switch (token){ case NUMDRIVE: if ((size_t)atoi(value) != numconf) g_fprintf(stderr,_("Error: number_drives at wrong place, should be " "first in file\n")); break; case EJECT: chg->eject = atoi(value); break; case SLEEP: chg->sleep = atoi(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 CLEANMAX: chg->cleanmax = atoi(value); break; case DRIVE: drivenum = atoi(value); if(drivenum >= numconf){ g_fprintf(stderr,_("Error: drive must be less than number_drives\n")); } 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){ chg->conf[drivenum].timefile = stralloc(value); } else { g_fprintf(stderr,_("Error: drive is not less than number_drives" " usagecount ignored\n")); } break; default: g_fprintf(stderr,_("Error: Unknown token\n")); break; } } amfree(linebuffer); } amfree(linebuffer); fclose(file); return 0;}/*----------------------------------------------------------------------------*//* The tape drive does not have an idea of current slot so * we use a file to store the current slot. It is not ideal * but it gets the job done */int get_current_slot(char *count_file){ FILE *inf; int retval; if ((inf=fopen(count_file,"r")) == NULL) { g_fprintf(stderr, _("%s: unable to open current slot file (%s)\n"), get_pname(), count_file); return 0; } if (fscanf(inf, "%d", &retval) != 1) { g_fprintf(stderr, _("%s: unable to read current slot file (%s)\n"), get_pname(), count_file); retval = 0; } fclose(inf); return retval;}void put_current_slot(char *count_file,int slot){ FILE *inf; if ((inf=fopen(count_file,"w")) == NULL) { g_fprintf(stderr, _("%s: unable to open current slot file (%s)\n"), get_pname(), count_file); exit(2); } g_fprintf(inf, "%d\n", slot); fclose(inf);}/* ---------------------------------------------------------------------- This stuff deals with parsing the command line */typedef struct com_arg{ char *str; int command_code; int takesparam;} argument;typedef struct com_stru{ int command_code; char *parameter;} command;void parse_args(int argc, char *argv[], command *rval);/* major command line args */#define COMCOUNT 5#define COM_SLOT 0#define COM_INFO 1#define COM_RESET 2#define COM_EJECT 3#define COM_CLEAN 4argument argdefs[]={{"-slot",COM_SLOT,1}, {"-info",COM_INFO,0}, {"-reset",COM_RESET,0}, {"-eject",COM_EJECT,0}, {"-clean",COM_CLEAN,0}};/* minor command line args */#define SLOTCOUNT 5#define SLOT_CUR 0#define SLOT_NEXT 1#define SLOT_PREV 2#define SLOT_FIRST 3#define SLOT_LAST 4argument slotdefs[]={{"current",SLOT_CUR,0}, {"next",SLOT_NEXT,0}, {"prev",SLOT_PREV,0}, {"first",SLOT_FIRST,0}, {"last",SLOT_LAST,0}};int is_positive_number(char *tmp) /* is the string a valid positive int? */{ int i=0; if ((tmp==NULL)||(tmp[0]==0)) return 0; while ((tmp[i]>='0')&&(tmp[i]<='9')&&(tmp[i]!=0)) i++; if (tmp[i]==0) return 1; else return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -