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.17 2006/07/12 12:28:19 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 (char *dev, char *wantlabel,
36 char** label, char** timestamp,
38 int changer_taper_scan (char *wantlabel, char** gotlabel, char** timestamp,
39 char **tapedev, void (*)(void *data, char *msg),
41 int scan_slot (void *data, int rc, char *slotstr, char *device);
42 int taper_scan (char* wantlabel, char** gotlabel, char** timestamp,
44 void taperscan_output_callback(void *data, char *msg),
46 char *find_brand_new_tape_label (void);
47 void FILE_taperscan_output_callback (void *data, char *msg);
48 void CHAR_taperscan_output_callback (void *data, char *msg);
50 /* NO GLOBALS PLEASE! */
52 /* How does the taper scan algorithm work? Like this:
53 * 1) If there is a barcode reader, and we have a recyclable tape, use the
54 * reader to load the oldest tape.
55 * 2) Otherwise, search through the changer until we find a new tape
56 * or the oldest recyclable tape.
57 * 3) If we couldn't find the oldest recyclable tape or a new tape,
58 * but if in #2 we found *some* recyclable tape, use the oldest one we
60 * 4) At this point, we give up.
63 /* This function checks the label of a single tape, which may or may not
64 * have been loaded by the changer. With the addition of char *dev, it has
65 * the same interface as taper_scan.
66 * Return value is the same as taper_scan.
77 *label = *timestamp = NULL;
78 result = tape_rdlabel(dev, timestamp, label);
80 if (CHECK_NOT_AMANDA_TAPE_MSG(result) &&
81 getconf_seen(CNF_LABEL_NEW_TAPES)) {
84 *label = find_brand_new_tape_label();
86 *timestamp = stralloc("X");
87 vstrextend(error_message,
88 "Found a non-amanda tape, will label it `",
89 *label, "'.\n", NULL);
92 vstrextend(error_message,
93 "Found a non-amanda tape, but have no labels left.\n",
99 vstrextend(error_message, result, "\n", NULL);
104 if ((*label == NULL) || (*timestamp == NULL)) {
105 error("Invalid return from tape_rdlabel");
108 vstrextend(error_message, "read label `", *label, "', date `",
109 *timestamp, "'\n", NULL);
111 if (desired_label != NULL && strcmp(*label, desired_label) == 0) {
112 /* Got desired label. */
116 /* Is this actually an acceptable tape? */
117 if (strcmp(*label, FAKE_LABEL) != 0) {
119 labelstr = getconf_str(CNF_LABELSTR);
120 if(!match(labelstr, *label)) {
121 vstrextend(error_message, "label ", *label,
122 " doesn\'t match labelstr \"",
123 labelstr, "\"\n", NULL);
127 if (strcmp(*timestamp, "X") == 0) {
128 /* new, labeled tape. */
132 tp = lookup_tapelabel(*label);
135 vstrextend(error_message, "label ", *label,
136 " match labelstr but it not listed in the tapelist file.\n",
139 } else if(tp != NULL && !reusable_tape(tp)) {
140 vstrextend(error_message, "cannot overwrite active tape ", *label,
147 /* Yay! We got a good tape! */
151 /* Interface is the same as taper_scan, with the addition of the tapedev
157 char **error_message;
159 char *first_labelstr_slot;
162 void (*taperscan_output_callback)(void *data, char *msg);
174 changertrack_t *ct = ((changertrack_t*)data);
179 vstrextend(ct->error_message,
180 "fatal changer error: slot ", slotstr, ": ",
181 changer_resultstr, "\n", NULL);
186 vstrextend(ct->error_message,
187 "changer error: slot ", slotstr, ": ", changer_resultstr,
193 *(ct->error_message) = newvstralloc(*(ct->error_message), "slot ",
194 slotstr, ": ", NULL);
195 amfree(*ct->gotlabel);
196 amfree(*ct->timestamp);
197 label_result = scan_read_label(device, ct->wantlabel, ct->gotlabel,
198 ct->timestamp, ct->error_message);
199 if (label_result == 1 || label_result == 3 ||
200 (label_result == 2 && !ct->backwards)) {
201 *(ct->tapedev) = stralloc(device);
202 ct->tape_status = label_result;
205 if ((label_result == 2) && (ct->first_labelstr_slot == NULL))
206 ct->first_labelstr_slot = stralloc(slotstr);
211 ct->taperscan_output_callback(ct->data, *(ct->error_message));
212 amfree(*(ct->error_message));
224 changertrack_t *ct = ((changertrack_t*)data);
226 (void)nslots; /* Quiet unused parameter warning */
227 (void)searchable; /* Quiet unused parameter warning */
230 vstrextend(ct->error_message,
231 "could not get changer info: ", changer_resultstr, "\n",
233 ct->taperscan_output_callback(ct->data, *(ct->error_message));
234 amfree(*(ct->error_message));
237 ct->backwards = backwards;
247 void (*taperscan_output_callback)(void *data, char *msg),
250 char *error_message = NULL;
251 changertrack_t local_data;
252 char *outslotstr = NULL;
255 *gotlabel = *timestamp = *tapedev = NULL;
256 local_data.wantlabel = wantlabel;
257 local_data.gotlabel = gotlabel;
258 local_data.timestamp = timestamp;
259 local_data.error_message = &error_message;
260 local_data.tapedev = tapedev;
261 local_data.first_labelstr_slot = NULL;
262 local_data.backwards = 0;
263 local_data.tape_status = 0;
264 local_data.taperscan_output_callback = taperscan_output_callback;
265 local_data.data = data;
267 changer_find(&local_data, scan_init, scan_slot, wantlabel);
269 if (*(local_data.tapedev)) {
270 /* We got it, and it's loaded. */
271 return local_data.tape_status;
272 } else if (local_data.first_labelstr_slot) {
274 result = changer_loadslot(local_data.first_labelstr_slot,
275 &outslotstr, tapedev);
278 result = scan_read_label(*tapedev, NULL, gotlabel, timestamp,
280 taperscan_output_callback(data, error_message);
281 amfree(error_message);
286 /* Didn't find a tape. :-( */
287 assert(local_data.tape_status <= 0);
288 taperscan_output_callback(data, "changer problem: ");
289 taperscan_output_callback(data, changer_resultstr);
293 int taper_scan(char* wantlabel,
294 char** gotlabel, char** timestamp, char** tapedev,
295 void (*taperscan_output_callback)(void *data, char *msg),
298 char *error_message = NULL;
300 *gotlabel = *timestamp = NULL;
302 if (wantlabel == NULL) {
304 tmp = lookup_last_reusable_tape(0);
306 wantlabel = tmp->label;
310 if (changer_init()) {
311 result = changer_taper_scan(wantlabel, gotlabel, timestamp,
313 taperscan_output_callback, data);
316 *tapedev = stralloc(getconf_str(CNF_TAPEDEV));
317 if (*tapedev == NULL) {
319 taperscan_output_callback(data, "No tapedev spefified");
321 *tapedev = stralloc(*tapedev);
322 result = scan_read_label(*tapedev, wantlabel,
323 gotlabel, timestamp, &error_message);
324 taperscan_output_callback(data, error_message);
325 amfree(error_message);
332 #define AUTO_LABEL_MAX_LEN 1024
334 find_brand_new_tape_label(void)
337 char newlabel[AUTO_LABEL_MAX_LEN];
338 char tmpnum[30]; /* 64-bit integers can be 21 digists... */
340 char *auto_pos = NULL;
342 ssize_t label_len, auto_len;
345 if (!getconf_seen(CNF_LABEL_NEW_TAPES)) {
348 format = getconf_str(CNF_LABEL_NEW_TAPES);
350 memset(newlabel, 0, AUTO_LABEL_MAX_LEN);
352 auto_len = -1; /* Only find the first '%' */
353 while (*format != '\0') {
354 if (label_len + 4 > AUTO_LABEL_MAX_LEN) {
355 fprintf(stderr, "Auto label format is too long!\n");
359 if (*format == '\\') {
360 /* Copy the next character. */
361 newlabel[label_len++] = format[1];
363 } else if (*format == '%' && auto_len == -1) {
364 /* This is the format specifier. */
365 auto_pos = newlabel + label_len;
367 while (*format == '%' && label_len < AUTO_LABEL_MAX_LEN) {
368 newlabel[label_len++] = '%';
373 /* Just copy a character. */
374 newlabel[label_len++] = *(format++);
378 /* Sometimes we copy the null, sometimes not. */
379 if (newlabel[label_len] != '\0') {
380 newlabel[label_len++] = '\0';
383 if (auto_pos == NULL) {
384 fprintf(stderr, "Auto label template contains no '%%'!\n");
388 snprintf(tmpfmt, SIZEOF(tmpfmt), "%%0" SIZE_T_FMT "d",
389 (SIZE_T_FMT_TYPE)auto_len);
391 for (i = 1; i < INT_MAX; i ++) {
392 snprintf(tmpnum, SIZEOF(tmpnum), tmpfmt, i);
393 if (strlen(tmpnum) != (size_t)auto_len) {
394 fprintf(stderr, "All possible auto-labels used.\n");
398 strncpy(auto_pos, tmpnum, (size_t)auto_len);
400 tp = lookup_tapelabel(newlabel);
402 /* Got it. Double-check that this is a labelstr match. */
403 if (!match(getconf_str(CNF_LABELSTR), newlabel)) {
404 fprintf(stderr, "New label %s does not match labelstr %s!\n",
405 newlabel, getconf_str(CNF_LABELSTR));
408 return stralloc(newlabel);
412 /* Should not get here unless you have over two billion tapes. */
413 fprintf(stderr, "Taper internal error in find_brand_new_tape_label.");
418 FILE_taperscan_output_callback(
423 if(strlen(msg) == 0) return;
426 fprintf((FILE *)data, "%s", msg);
432 CHAR_taperscan_output_callback(
433 /*@keep@*/ void *data,
436 char **s = (char **)data;
439 if(strlen(msg) == 0) return;