X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Ftaperscan.c;fp=server-src%2Ftaperscan.c;h=3b5ba5518876f19fcc303d40290c0ab6964b6f59;hb=1194fb66aa28d9929c3f2bef3cc6c1c3f40a60a4;hp=0000000000000000000000000000000000000000;hpb=2df780bff19c457b0debb7adc29972a0bc2a5dc2;p=debian%2Famanda diff --git a/server-src/taperscan.c b/server-src/taperscan.c new file mode 100644 index 0000000..3b5ba55 --- /dev/null +++ b/server-src/taperscan.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2005 Zmanda Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contact information: Zmanda Inc, 505 N Mathlida Ave, Suite 120 + * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com + */ + +/* + * $Id: taperscan.c,v 1.9 2006/03/10 14:29:22 martinea Exp $ + * + * This contains the implementation of the taper-scan algorithm, as it is + * used by taper, amcheck, and amtape. See the header file taperscan.h for + * interface information. */ + +#include +#include +#include +#include "changer.h" +#include "tapefile.h" + +int scan_read_label P((char *dev, char *wantlabel, + char** label, char** timestamp, + char**error_message)); +int changer_taper_scan P((char *wantlabel, char** gotlabel, char**timestamp, + char**error_message, char **tapedev)); +char *find_brand_new_tape_label(); + +/* NO GLOBALS PLEASE! */ + +/* How does the taper scan algorithm work? Like this: + * 1) If there is a barcode reader, and we have a recyclable tape, use the + * reader to load the oldest tape. + * 2) Otherwise, search through the changer until we find a new tape + * or the oldest recyclable tape. + * 3) If we couldn't find the oldest recyclable tape or a new tape, + * but if in #2 we found *some* recyclable tape, use the oldest one we + * found. + * 4) At this point, we give up. + */ + +/* This function checks the label of a single tape, which may or may not + * have been loaded by the changer. With the addition of char *dev, it has + * the same interface as taper_scan. + * Return value is the same as taper_scan. + */ +int scan_read_label(char *dev, char *desired_label, + char** label, char** timestamp, char** error_message) { + char *result = NULL; + char *errstr = NULL; + + *label = *timestamp = NULL; + result = tape_rdlabel(dev, timestamp, label); + if (result != NULL) { + if (CHECK_NOT_AMANDA_TAPE_MSG(result) && + getconf_seen(CNF_LABEL_NEW_TAPES)) { + amfree(result); + + *label = find_brand_new_tape_label(); + if (*label != NULL) { + *timestamp = stralloc("X"); + vstrextend(error_message, + "Found a non-amanda tape, will label it `", + *label, "'.\n", NULL); + return 3; + } + vstrextend(error_message, + "Found a non-amanda tape, but have no labels left.\n", + NULL); + return -1; + } + amfree(*timestamp); + amfree(*label); + vstrextend(error_message, result, "\n", NULL); + amfree(result); + return -1; + } + + if ((*label == NULL) || (*timestamp == NULL)) { + error("Invalid return from tape_rdlabel"); + } + + vstrextend(error_message, "read label `", *label, "', date `", + *timestamp, "'\n", NULL); + + if (desired_label != NULL && strcmp(*label, desired_label) == 0) { + /* Got desired label. */ + return 1; + } + + /* Is this actually an acceptable tape? */ + if (strcmp(*label, FAKE_LABEL) != 0) { + char *labelstr; + labelstr = getconf_str(CNF_LABELSTR); + if(!match(labelstr, *label)) { + vstrextend(&errstr, "label ", *label, + " doesn\'t match labelstr \"", + labelstr, "\"\n", NULL); + return -1; + } else { + tape_t *tp; + if (strcmp(*timestamp, "X") == 0) { + /* new, labeled tape. */ + return 1; + } + + tp = lookup_tapelabel(*label); + + if(tp == NULL) { + vstrextend(&errstr, "label ", *label, + " match labelstr but it not listed in the tapelist file.\n", + NULL); + return -1; + } else if(tp != NULL && !reusable_tape(tp)) { + vstrextend(&errstr, "cannot overwrite active tape ", *label, + "\n", NULL); + return -1; + } + } + } + + /* Yay! We got a good tape! */ + return 2; +} + +/* Interface is the same as taper_scan, with the addition of the tapedev + * output. */ +typedef struct { + char *wantlabel; + char **gotlabel; + char **timestamp; + char **error_message; + char **tapedev; + char *first_labelstr_slot; + int backwards; + int tape_status; +} changertrack_t; + +int scan_slot(void *data, int rc, char *slotstr, char *device) { + int label_result; + changertrack_t *ct = ((changertrack_t*)data); + + switch (rc) { + default: + newvstralloc(*(ct->error_message), *(ct->error_message), + "fatal changer error ", slotstr, ": ", + changer_resultstr, NULL); + return 1; + case 1: + newvstralloc(*(ct->error_message), *(ct->error_message), + "changer error ", slotstr, ": ", changer_resultstr, NULL); + return 0; + case 0: + vstrextend(ct->error_message, "slot ", slotstr, ": ", NULL); + label_result = scan_read_label(device, ct->wantlabel, ct->gotlabel, + ct->timestamp, ct->error_message); + if (label_result == 1 || label_result == 3 || + (label_result == 2 && !ct->backwards)) { + *(ct->tapedev) = stralloc(device); + ct->tape_status = label_result; + return 1; + } else if (label_result == 2) { + if (ct->first_labelstr_slot == NULL) + ct->first_labelstr_slot = stralloc(slotstr); + return 0; + } else { + return 0; + } + } + /* NOTREACHED */ + return 1; +} + +static int +scan_init(void *data, int rc, int nslots, int backwards, int searchable) { + changertrack_t *ct = ((changertrack_t*)data); + + if (rc) { + newvstralloc(*(ct->error_message), *(ct->error_message), + "could not get changer info: ", changer_resultstr, NULL); + } + + ct->backwards = backwards; + return 0; +} + +int changer_taper_scan(char* wantlabel, + char** gotlabel, char** timestamp, + char** error_message, char **tapedev) { + changertrack_t local_data = {wantlabel, gotlabel, timestamp, + error_message, tapedev, NULL, 0, 0}; + + *gotlabel = *timestamp = *tapedev = NULL; + + changer_find(&local_data, scan_init, scan_slot, wantlabel); + + if (*(local_data.tapedev)) { + /* We got it, and it's loaded. */ + return local_data.tape_status; + } else if (local_data.first_labelstr_slot) { + /* Use plan B. */ + if (changer_loadslot(local_data.first_labelstr_slot, + NULL, tapedev) == 0) { + return scan_read_label(*tapedev, NULL, gotlabel, timestamp, + error_message); + } + } + + /* Didn't find a tape. :-( */ + assert(local_data.tape_status <= 0); + return -1; +} + +int taper_scan(char* wantlabel, + char** gotlabel, char** timestamp, char** error_message, + char** tapedev) { + + *gotlabel = *timestamp = *error_message = NULL; + *tapedev = getconf_str(CNF_TAPEDEV); + + if (wantlabel == NULL) { + tape_t *tmp; + tmp = lookup_last_reusable_tape(0); + if (tmp != NULL) { + wantlabel = tmp->label; + } + } + + if (changer_init()) { + return changer_taper_scan(wantlabel, gotlabel, timestamp, + error_message, tapedev); + } + + return scan_read_label(*tapedev, wantlabel, + gotlabel, timestamp, error_message); +} + +#define AUTO_LABEL_MAX_LEN 1024 +char* find_brand_new_tape_label() { + char *format; + char newlabel[AUTO_LABEL_MAX_LEN]; + char tmpnum[12]; + char tmpfmt[16]; + char *auto_pos = NULL; + int i, format_len, label_len, auto_len; + tape_t *tp; + + if (!getconf_seen(CNF_LABEL_NEW_TAPES)) { + return NULL; + } + format = getconf_str(CNF_LABEL_NEW_TAPES); + format_len = strlen(format); + + memset(newlabel, 0, AUTO_LABEL_MAX_LEN); + label_len = 0; + auto_len = -1; /* Only find the first '%' */ + while (*format != '\0') { + if (label_len + 4 > AUTO_LABEL_MAX_LEN) { + fprintf(stderr, "Auto label format is too long!\n"); + return NULL; + } + + if (*format == '\\') { + /* Copy the next character. */ + newlabel[label_len++] = format[1]; + format += 2; + } else if (*format == '%' && auto_len == -1) { + /* This is the format specifier. */ + auto_pos = newlabel + label_len; + auto_len = 0; + while (*format == '%' && label_len < AUTO_LABEL_MAX_LEN) { + newlabel[label_len++] = '%'; + auto_len ++; + format ++; + } + } else { + /* Just copy a character. */ + newlabel[label_len++] = *(format++); + } + } + + /* Sometimes we copy the null, sometimes not. */ + if (newlabel[label_len] != '\0') { + newlabel[label_len++] = '\0'; + } + + if (auto_pos == NULL) { + fprintf(stderr, "Auto label template contains no '%%'!\n"); + return NULL; + } + + sprintf(tmpfmt, "%%0%dd", auto_len); + + for (i = 1; i < INT_MAX; i ++) { + sprintf(tmpnum, tmpfmt, i); + if (strlen(tmpnum) != auto_len) { + fprintf(stderr, "All possible auto-labels used.\n"); + return NULL; + } + + strncpy(auto_pos, tmpnum, auto_len); + + tp = lookup_tapelabel(newlabel); + if (tp == NULL) { + /* Got it. Double-check that this is a labelstr match. */ + if (!match(getconf_str(CNF_LABELSTR), newlabel)) { + fprintf(stderr, "New label %s does not match labelstr %s!\n", + newlabel, getconf_str(CNF_LABELSTR)); + return 0; + } + return stralloc(newlabel); + } + } + + /* NOTREACHED. Unless you have over two billion tapes. */ + fprintf(stderr, "Taper internal error in find_brand_new_tape_label."); + return 0; +}