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 version 2 as published
6 * by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Contact information: Zmanda Inc, 505 N Mathlida Ave, Suite 120
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
22 * $Id: taperscan.c,v 1.17 2006/07/12 12:28:19 martinea Exp $
24 * This contains the implementation of the taper-scan algorithm, as it is
25 * used by taper, amcheck, and amtape. See the header file taperscan.h for
26 * interface information. */
33 #include "timestamp.h"
34 #include "taperscan.h"
36 struct taper_scan_tracker_s {
37 GHashTable * scanned_slots;
40 int scan_read_label (char *dev, char * slot, char *wantlabel,
41 char** label, char** timestamp,
43 int changer_taper_scan (char *wantlabel, char** gotlabel, char** timestamp,
44 char **tapedev, taper_scan_tracker_t * tracker,
45 TaperscanOutputFunctor output_functor,
47 TaperscanProlongFunctor prolong_functor,
49 int scan_slot (void *data, int rc, char *slotstr, char *device);
50 char *find_brand_new_tape_label (void);
51 void FILE_taperscan_output_callback (void *data, char *msg);
52 void CHAR_taperscan_output_callback (void *data, char *msg);
54 /* NO GLOBALS PLEASE! */
56 /* How does the taper scan algorithm work? Like this:
57 * 1) If there is a barcode reader, and we have a recyclable tape, use the
58 * reader to load the oldest tape.
59 * 2) Otherwise, search through the changer until we find a new tape
60 * or the oldest recyclable tape.
61 * 3) If we couldn't find the oldest recyclable tape or a new tape,
62 * but if in #2 we found *some* recyclable tape, use the oldest one we
64 * 4) At this point, we give up.
67 /* This function checks the label of a single tape, which may or may not
68 * have been loaded by the changer. With the addition of char *dev, and *slot,
69 * it has the same interface as taper_scan. slot should be the slot where
70 * this tape is found, or NULL if no changer is in use.
71 * Return value is the same as taper_scan.
83 ReadLabelStatusFlags label_status;
85 g_return_val_if_fail(dev != NULL, -1);
87 if (*error_message == NULL)
88 *error_message = stralloc("");
90 *label = *timestamp = NULL;
92 device = device_open(dev);
93 if (device == NULL ) {
94 *error_message = newvstrallocf(*error_message,
95 _("%sError opening device %s.\n"),
102 device_set_startup_properties_from_config(device);
104 label_status = device_read_label(device);
105 g_assert((device->volume_label != NULL) ==
106 (label_status == READ_LABEL_STATUS_SUCCESS));
108 if (device->volume_label != NULL) {
109 *label = g_strdup(device->volume_label);
110 *timestamp = strdup(device->volume_time);
111 } else if (label_status & READ_LABEL_STATUS_VOLUME_UNLABELED) {
112 g_object_unref(device);
113 if (!getconf_seen(CNF_LABEL_NEW_TAPES)) {
114 *error_message = newvstrallocf(*error_message,
115 _("%sFound a non-amanda tape.\n"),
120 *label = find_brand_new_tape_label();
121 if (*label != NULL) {
122 *timestamp = stralloc("X");
123 *error_message = newvstrallocf(*error_message,
124 _("%sFound a non-amanda tape, will label it `%s'.\n"),
125 *error_message, *label);
129 *error_message = newvstrallocf(*error_message,
130 _("%sFound a non-amanda tape, but have no labels left.\n"),
137 g_flags_nick_to_strv(label_status, READ_LABEL_STATUS_FLAGS_TYPE);
139 switch (g_strv_length(label_strv)) {
141 label_errstr = g_strdup(_("Unknown error reading volume label.\n"));
146 g_strdup_printf(_("Error reading volume label: %s\n"),
152 char * tmp_str = g_english_strjoinv(label_strv, "or");
154 g_strdup_printf(_("Error reading label: One of %s\n"),
160 g_strfreev(label_strv);
162 *error_message = newvstralloc(*error_message, *error_message,
164 g_free(label_errstr);
168 g_assert(*label != NULL && *timestamp != NULL);
169 g_object_unref(device);
171 *error_message = newvstrallocf(*error_message,
172 _("%sread label `%s', date `%s'.\n"),
173 *error_message, *label, *timestamp);
175 /* Register this with the barcode database, even if its not ours. */
177 changer_label(slot, *label);
180 if (desired_label != NULL && strcmp(*label, desired_label) == 0) {
181 /* Got desired label. */
185 /* Is this actually an acceptable tape? */
186 labelstr = getconf_str(CNF_LABELSTR);
187 if(!match(labelstr, *label)) {
188 *error_message = newvstrallocf(*error_message,
189 _("%slabel \"%s\" doesn't match \"%s\".\n"),
190 *error_message, *label, labelstr);
195 if (strcmp(*timestamp, "X") == 0) {
196 /* new, labeled tape. */
200 tp = lookup_tapelabel(*label);
204 newvstrallocf(*error_message,
205 _("%slabel \"%s\" matches labelstr but it is"
206 " not listed in the tapelist file.\n"),
207 *error_message, *label);
209 } else if(tp != NULL && !reusable_tape(tp)) {
211 newvstrallocf(*error_message,
212 _("%sTape with label %s is still active"
213 " and cannot be overwriten.\n"),
214 *error_message, *label);
219 /* Yay! We got a good tape! */
223 /* Interface is the same as taper_scan, with some additional bookkeeping. */
228 char **error_message;
230 char *slotstr; /* Best-choice slot number. */
231 char *first_labelstr_slot;
234 TaperscanOutputFunctor output_callback;
236 TaperscanProlongFunctor prolong_callback;
238 taper_scan_tracker_t * persistent;
249 changertrack_t *ct = ((changertrack_t*)data);
252 if (ct->prolong_callback &&
253 !ct->prolong_callback(ct->prolong_data)) {
257 if (ct->persistent != NULL) {
260 if (g_hash_table_lookup_extended(ct->persistent->scanned_slots,
261 slotstr, &key, &value)) {
262 /* We already returned this slot in a previous invocation,
268 if (*(ct->error_message) == NULL)
269 *(ct->error_message) = stralloc("");
273 *(ct->error_message) = newvstrallocf(*(ct->error_message),
274 _("%sfatal changer error: slot %s: %s\n"),
275 *(ct->error_message), slotstr, changer_resultstr);
280 *(ct->error_message) = newvstrallocf(*(ct->error_message),
281 _("%schanger error: slot %s: %s\n"),
282 *(ct->error_message), slotstr, changer_resultstr);
287 *(ct->error_message) = newvstrallocf(*(ct->error_message),
288 _("slot %s:"), slotstr);
289 amfree(*ct->gotlabel);
290 amfree(*ct->timestamp);
291 label_result = scan_read_label(device, slotstr,
292 ct->wantlabel, ct->gotlabel,
293 ct->timestamp, ct->error_message);
294 if (label_result == 1 || label_result == 3 ||
295 (label_result == 2 && !ct->backwards)) {
296 *(ct->tapedev) = stralloc(device);
297 ct->tape_status = label_result;
299 ct->slotstr = stralloc(slotstr);
302 if ((label_result == 2) && (ct->first_labelstr_slot == NULL))
303 ct->first_labelstr_slot = stralloc(slotstr);
308 ct->output_callback(ct->output_data, *(ct->error_message));
309 amfree(*(ct->error_message));
317 G_GNUC_UNUSED int nslots,
319 G_GNUC_UNUSED int searchable)
321 changertrack_t *ct = ((changertrack_t*)data);
324 *(ct->error_message) = newvstrallocf(*(ct->error_message),
325 _("%scould not get changer info: %s\n"),
326 *(ct->error_message), changer_resultstr);
327 ct->output_callback(ct->output_data, *(ct->error_message));
328 amfree(*(ct->error_message));
331 ct->backwards = backwards;
341 taper_scan_tracker_t * tracker,
342 TaperscanOutputFunctor taperscan_output_callback,
344 TaperscanProlongFunctor prolong_callback,
347 char *error_message = NULL;
348 changertrack_t local_data;
349 char *outslotstr = NULL;
352 *gotlabel = *timestamp = *tapedev = NULL;
353 local_data.wantlabel = wantlabel;
354 local_data.gotlabel = gotlabel;
355 local_data.timestamp = timestamp;
356 local_data.error_message = &error_message;
357 local_data.tapedev = tapedev;
358 local_data.first_labelstr_slot = NULL;
359 local_data.backwards = 0;
360 local_data.tape_status = 0;
361 local_data.output_callback = taperscan_output_callback;
362 local_data.output_data = output_data;
363 local_data.prolong_callback = prolong_callback;
364 local_data.prolong_data = prolong_data;
365 local_data.persistent = tracker;
366 local_data.slotstr = NULL;
368 changer_find(&local_data, scan_init, scan_slot, wantlabel);
370 if (*(local_data.tapedev)) {
371 /* We got it, and it's loaded. */
372 if (local_data.persistent != NULL && local_data.slotstr != NULL) {
373 g_hash_table_insert(local_data.persistent->scanned_slots,
374 local_data.slotstr, NULL);
376 amfree(local_data.slotstr);
378 amfree(local_data.first_labelstr_slot);
379 return local_data.tape_status;
380 } else if (local_data.first_labelstr_slot) {
382 if (prolong_callback && !prolong_callback(prolong_data)) {
385 result = changer_loadslot(local_data.first_labelstr_slot,
386 &outslotstr, tapedev);
387 amfree(local_data.first_labelstr_slot);
392 result = scan_read_label(*tapedev, NULL, NULL,
395 taperscan_output_callback(output_data, error_message);
396 amfree(error_message);
397 if (result > 0 && local_data.persistent != NULL &&
398 local_data.slotstr != NULL) {
399 g_hash_table_insert(local_data.persistent->scanned_slots,
400 local_data.slotstr, NULL);
402 amfree(local_data.slotstr);
408 /* Didn't find a tape. :-( */
409 assert(local_data.tape_status <= 0);
413 int taper_scan(char* wantlabel,
414 char** gotlabel, char** timestamp, char** tapedev,
415 taper_scan_tracker_t * tracker,
416 TaperscanOutputFunctor output_functor,
418 TaperscanProlongFunctor prolong_functor,
419 void *prolong_data) {
420 char *error_message = NULL;
422 *gotlabel = *timestamp = NULL;
424 if (wantlabel == NULL) {
426 tmp = lookup_last_reusable_tape(0);
428 wantlabel = tmp->label;
432 if (changer_init()) {
433 result = changer_taper_scan(wantlabel, gotlabel, timestamp,
435 output_functor, output_data,
436 prolong_functor, prolong_data);
438 /* Note that the tracker is not used in this case. */
439 *tapedev = stralloc(getconf_str(CNF_TAPEDEV));
440 if (*tapedev == NULL) {
442 output_functor(output_data, _("No tapedev specified"));
444 result = scan_read_label(*tapedev, NULL, wantlabel, gotlabel,
445 timestamp, &error_message);
446 output_functor(output_data, error_message);
447 amfree(error_message);
454 #define AUTO_LABEL_MAX_LEN 1024
456 find_brand_new_tape_label(void)
459 char newlabel[AUTO_LABEL_MAX_LEN];
460 char tmpnum[30]; /* 64-bit integers can be 21 digists... */
462 char *auto_pos = NULL;
464 ssize_t label_len, auto_len;
467 if (!getconf_seen(CNF_LABEL_NEW_TAPES)) {
470 format = getconf_str(CNF_LABEL_NEW_TAPES);
472 memset(newlabel, 0, AUTO_LABEL_MAX_LEN);
474 auto_len = -1; /* Only find the first '%' */
475 while (*format != '\0') {
476 if (label_len + 4 > AUTO_LABEL_MAX_LEN) {
477 g_fprintf(stderr, _("Auto label format is too long!\n"));
481 if (*format == '\\') {
482 /* Copy the next character. */
483 newlabel[label_len++] = format[1];
485 } else if (*format == '%' && auto_len == -1) {
486 /* This is the format specifier. */
487 auto_pos = newlabel + label_len;
489 while (*format == '%' && label_len < AUTO_LABEL_MAX_LEN) {
490 newlabel[label_len++] = '%';
495 /* Just copy a character. */
496 newlabel[label_len++] = *(format++);
500 /* Sometimes we copy the null, sometimes not. */
501 if (newlabel[label_len] != '\0') {
502 newlabel[label_len++] = '\0';
505 if (auto_pos == NULL) {
506 g_fprintf(stderr, _("Auto label template contains no '%%'!\n"));
510 g_snprintf(tmpfmt, SIZEOF(tmpfmt), "%%0%zdd",
513 for (i = 1; i < INT_MAX; i ++) {
514 g_snprintf(tmpnum, SIZEOF(tmpnum), tmpfmt, i);
515 if (strlen(tmpnum) != (size_t)auto_len) {
516 g_fprintf(stderr, _("All possible auto-labels used.\n"));
520 strncpy(auto_pos, tmpnum, (size_t)auto_len);
522 tp = lookup_tapelabel(newlabel);
524 /* Got it. Double-check that this is a labelstr match. */
525 if (!match(getconf_str(CNF_LABELSTR), newlabel)) {
526 g_fprintf(stderr, _("New label %s does not match labelstr %s from amanda.conf\n"),
527 newlabel, getconf_str(CNF_LABELSTR));
530 return stralloc(newlabel);
534 /* Should not get here unless you have over two billion tapes. */
535 g_fprintf(stderr, _("Taper internal error in find_brand_new_tape_label."));
540 FILE_taperscan_output_callback(
545 if(strlen(msg) == 0) return;
548 g_fprintf((FILE *)data, "%s", msg);
554 CHAR_taperscan_output_callback(
555 /*@keep@*/ void *data,
558 char **s = (char **)data;
561 if(strlen(msg) == 0) return;
569 taper_scan_tracker_t * taper_scan_tracker_new(void) {
570 taper_scan_tracker_t * rval = malloc(sizeof(*rval));
572 rval->scanned_slots = g_hash_table_new_full(g_str_hash, g_str_equal,
578 void taper_scan_tracker_free(taper_scan_tracker_t * tracker) {
579 if (tracker->scanned_slots != NULL) {
580 g_hash_table_destroy(tracker->scanned_slots);