2 * Copyright (c) 2005 Zmanda Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Contact information: Zmanda Inc, 505 N Mathlida Ave, Suite 120
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
23 * $Id: taperscan.c,v 1.9 2006/03/10 14:29:22 martinea Exp $
25 * This contains the implementation of the taper-scan algorithm, as it is
26 * used by taper, amcheck, and amtape. See the header file taperscan.h for
27 * interface information. */
35 int scan_read_label P((char *dev, char *wantlabel,
36 char** label, char** timestamp,
37 char**error_message));
38 int changer_taper_scan P((char *wantlabel, char** gotlabel, char**timestamp,
39 char**error_message, char **tapedev));
40 char *find_brand_new_tape_label();
42 /* NO GLOBALS PLEASE! */
44 /* How does the taper scan algorithm work? Like this:
45 * 1) If there is a barcode reader, and we have a recyclable tape, use the
46 * reader to load the oldest tape.
47 * 2) Otherwise, search through the changer until we find a new tape
48 * or the oldest recyclable tape.
49 * 3) If we couldn't find the oldest recyclable tape or a new tape,
50 * but if in #2 we found *some* recyclable tape, use the oldest one we
52 * 4) At this point, we give up.
55 /* This function checks the label of a single tape, which may or may not
56 * have been loaded by the changer. With the addition of char *dev, it has
57 * the same interface as taper_scan.
58 * Return value is the same as taper_scan.
60 int scan_read_label(char *dev, char *desired_label,
61 char** label, char** timestamp, char** error_message) {
65 *label = *timestamp = NULL;
66 result = tape_rdlabel(dev, timestamp, label);
68 if (CHECK_NOT_AMANDA_TAPE_MSG(result) &&
69 getconf_seen(CNF_LABEL_NEW_TAPES)) {
72 *label = find_brand_new_tape_label();
74 *timestamp = stralloc("X");
75 vstrextend(error_message,
76 "Found a non-amanda tape, will label it `",
77 *label, "'.\n", NULL);
80 vstrextend(error_message,
81 "Found a non-amanda tape, but have no labels left.\n",
87 vstrextend(error_message, result, "\n", NULL);
92 if ((*label == NULL) || (*timestamp == NULL)) {
93 error("Invalid return from tape_rdlabel");
96 vstrextend(error_message, "read label `", *label, "', date `",
97 *timestamp, "'\n", NULL);
99 if (desired_label != NULL && strcmp(*label, desired_label) == 0) {
100 /* Got desired label. */
104 /* Is this actually an acceptable tape? */
105 if (strcmp(*label, FAKE_LABEL) != 0) {
107 labelstr = getconf_str(CNF_LABELSTR);
108 if(!match(labelstr, *label)) {
109 vstrextend(&errstr, "label ", *label,
110 " doesn\'t match labelstr \"",
111 labelstr, "\"\n", NULL);
115 if (strcmp(*timestamp, "X") == 0) {
116 /* new, labeled tape. */
120 tp = lookup_tapelabel(*label);
123 vstrextend(&errstr, "label ", *label,
124 " match labelstr but it not listed in the tapelist file.\n",
127 } else if(tp != NULL && !reusable_tape(tp)) {
128 vstrextend(&errstr, "cannot overwrite active tape ", *label,
135 /* Yay! We got a good tape! */
139 /* Interface is the same as taper_scan, with the addition of the tapedev
145 char **error_message;
147 char *first_labelstr_slot;
152 int scan_slot(void *data, int rc, char *slotstr, char *device) {
154 changertrack_t *ct = ((changertrack_t*)data);
158 newvstralloc(*(ct->error_message), *(ct->error_message),
159 "fatal changer error ", slotstr, ": ",
160 changer_resultstr, NULL);
163 newvstralloc(*(ct->error_message), *(ct->error_message),
164 "changer error ", slotstr, ": ", changer_resultstr, NULL);
167 vstrextend(ct->error_message, "slot ", slotstr, ": ", NULL);
168 label_result = scan_read_label(device, ct->wantlabel, ct->gotlabel,
169 ct->timestamp, ct->error_message);
170 if (label_result == 1 || label_result == 3 ||
171 (label_result == 2 && !ct->backwards)) {
172 *(ct->tapedev) = stralloc(device);
173 ct->tape_status = label_result;
175 } else if (label_result == 2) {
176 if (ct->first_labelstr_slot == NULL)
177 ct->first_labelstr_slot = stralloc(slotstr);
188 scan_init(void *data, int rc, int nslots, int backwards, int searchable) {
189 changertrack_t *ct = ((changertrack_t*)data);
192 newvstralloc(*(ct->error_message), *(ct->error_message),
193 "could not get changer info: ", changer_resultstr, NULL);
196 ct->backwards = backwards;
200 int changer_taper_scan(char* wantlabel,
201 char** gotlabel, char** timestamp,
202 char** error_message, char **tapedev) {
203 changertrack_t local_data = {wantlabel, gotlabel, timestamp,
204 error_message, tapedev, NULL, 0, 0};
206 *gotlabel = *timestamp = *tapedev = NULL;
208 changer_find(&local_data, scan_init, scan_slot, wantlabel);
210 if (*(local_data.tapedev)) {
211 /* We got it, and it's loaded. */
212 return local_data.tape_status;
213 } else if (local_data.first_labelstr_slot) {
215 if (changer_loadslot(local_data.first_labelstr_slot,
216 NULL, tapedev) == 0) {
217 return scan_read_label(*tapedev, NULL, gotlabel, timestamp,
222 /* Didn't find a tape. :-( */
223 assert(local_data.tape_status <= 0);
227 int taper_scan(char* wantlabel,
228 char** gotlabel, char** timestamp, char** error_message,
231 *gotlabel = *timestamp = *error_message = NULL;
232 *tapedev = getconf_str(CNF_TAPEDEV);
234 if (wantlabel == NULL) {
236 tmp = lookup_last_reusable_tape(0);
238 wantlabel = tmp->label;
242 if (changer_init()) {
243 return changer_taper_scan(wantlabel, gotlabel, timestamp,
244 error_message, tapedev);
247 return scan_read_label(*tapedev, wantlabel,
248 gotlabel, timestamp, error_message);
251 #define AUTO_LABEL_MAX_LEN 1024
252 char* find_brand_new_tape_label() {
254 char newlabel[AUTO_LABEL_MAX_LEN];
257 char *auto_pos = NULL;
258 int i, format_len, label_len, auto_len;
261 if (!getconf_seen(CNF_LABEL_NEW_TAPES)) {
264 format = getconf_str(CNF_LABEL_NEW_TAPES);
265 format_len = strlen(format);
267 memset(newlabel, 0, AUTO_LABEL_MAX_LEN);
269 auto_len = -1; /* Only find the first '%' */
270 while (*format != '\0') {
271 if (label_len + 4 > AUTO_LABEL_MAX_LEN) {
272 fprintf(stderr, "Auto label format is too long!\n");
276 if (*format == '\\') {
277 /* Copy the next character. */
278 newlabel[label_len++] = format[1];
280 } else if (*format == '%' && auto_len == -1) {
281 /* This is the format specifier. */
282 auto_pos = newlabel + label_len;
284 while (*format == '%' && label_len < AUTO_LABEL_MAX_LEN) {
285 newlabel[label_len++] = '%';
290 /* Just copy a character. */
291 newlabel[label_len++] = *(format++);
295 /* Sometimes we copy the null, sometimes not. */
296 if (newlabel[label_len] != '\0') {
297 newlabel[label_len++] = '\0';
300 if (auto_pos == NULL) {
301 fprintf(stderr, "Auto label template contains no '%%'!\n");
305 sprintf(tmpfmt, "%%0%dd", auto_len);
307 for (i = 1; i < INT_MAX; i ++) {
308 sprintf(tmpnum, tmpfmt, i);
309 if (strlen(tmpnum) != auto_len) {
310 fprintf(stderr, "All possible auto-labels used.\n");
314 strncpy(auto_pos, tmpnum, auto_len);
316 tp = lookup_tapelabel(newlabel);
318 /* Got it. Double-check that this is a labelstr match. */
319 if (!match(getconf_str(CNF_LABELSTR), newlabel)) {
320 fprintf(stderr, "New label %s does not match labelstr %s!\n",
321 newlabel, getconf_str(CNF_LABELSTR));
324 return stralloc(newlabel);
328 /* NOTREACHED. Unless you have over two billion tapes. */
329 fprintf(stderr, "Taper internal error in find_brand_new_tape_label.");