Imported Upstream version 2.6.1
[debian/amanda] / server-src / taperscan.c
index 3b5ba5518876f19fcc303d40290c0ab6964b6f59..8e513736bcd4bb3409f38b56df7eecf01e8344c6 100644 (file)
@@ -1,10 +1,9 @@
 /*
- * Copyright (c) 2005 Zmanda Inc.  All Rights Reserved.
+ * Copyright (c) 2005-2008 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.
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
  * 
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * 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
+ * Contact information: Zmanda Inc, 465 S Mathlida Ave, Suite 300
+ * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
  */
 
 /*
- * $Id: taperscan.c,v 1.9 2006/03/10 14:29:22 martinea Exp $
+ * $Id: taperscan.c,v 1.17 2006/07/12 12:28:19 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 <amanda.h>
-#include <tapeio.h>
-#include <conffile.h>
+#include "amanda.h"
+#include "conffile.h"
 #include "changer.h"
 #include "tapefile.h"
+#include "device.h"
+#include "timestamp.h"
+#include "taperscan.h"
 
-int scan_read_label P((char *dev, char *wantlabel,
+struct taper_scan_tracker_s {
+    GHashTable * scanned_slots;
+};
+
+int scan_read_label (char *dev, char * slot, 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();
+                       char**error_message);
+int changer_taper_scan (char *wantlabel, char** gotlabel, char** timestamp,
+                        char **tapedev, taper_scan_tracker_t * tracker,
+                        TaperscanOutputFunctor output_functor,
+                        void *output_data,
+                        TaperscanProlongFunctor prolong_functor,
+                        void *prolong_data);
+int scan_slot (void *data, int rc, char *slotstr, char *device);
+char *find_brand_new_tape_label (void);
+void FILE_taperscan_output_callback (void *data, char *msg);
+void CHAR_taperscan_output_callback (void *data, char *msg);
 
 /* NO GLOBALS PLEASE! */
 
@@ -53,82 +65,151 @@ char *find_brand_new_tape_label();
  */
 
 /* 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. 
+ * have been loaded by the changer. With the addition of char *dev, and *slot,
+ * it has the same interface as taper_scan. slot should be the slot where
+ * this tape is found, or NULL if no changer is in use.
  * 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;
+int scan_read_label(
+    char *dev,
+    char *slot,
+    char *desired_label,
+    char** label,
+    char** timestamp,
+    char** error_message)
+{
+    Device * device;
+    char *labelstr;
+    DeviceStatusFlags device_status;
+
+    g_return_val_if_fail(dev != NULL, -1);
+
+    if (*error_message == NULL)
+       *error_message = stralloc("");
 
     *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;
-        }
+    device = device_open(dev);
+    g_assert(device != NULL);
+
+    if (device->status != DEVICE_STATUS_SUCCESS ) {
+        *error_message = newvstrallocf(*error_message,
+                                       _("%sError opening device %s: %s.\n"),
+                                       *error_message, dev,
+                                      device_error_or_status(device));
+       g_object_unref(device);
+        amfree(*timestamp);
+        amfree(*label);
+        return -1;
+    }
+
+    if (!device_configure(device, TRUE)) {
+        *error_message = newvstrallocf(*error_message,
+                                       _("%sError configuring device %s: %s.\n"),
+                                       *error_message, dev,
+                                      device_error_or_status(device));
+       g_object_unref(device);
         amfree(*timestamp);
         amfree(*label);
-        vstrextend(error_message, result, "\n", NULL);
-        amfree(result);
         return -1;
     }
+
+    device_status = device_read_label(device);
     
-    if ((*label == NULL) || (*timestamp == NULL)) {
-       error("Invalid return from tape_rdlabel");
+    if (device_status == DEVICE_STATUS_SUCCESS && device->volume_label != NULL) {
+        *label = g_strdup(device->volume_label);
+        *timestamp = strdup(device->volume_time);
+    } else if (device_status & DEVICE_STATUS_VOLUME_UNLABELED) {
+        if (!getconf_seen(CNF_LABEL_NEW_TAPES)) {
+            *error_message = newvstrallocf(*error_message,
+                                           _("%sFound an empty or non-amanda tape.\n"),
+                                           *error_message);
+           g_object_unref(device);
+            return -1;
+        }
+
+       /* If we got a header, but the Device doesn't think it's labeled, then this
+        * tape probably has some data on it, so refuse to automatically label it */
+       if (device->volume_header && device->volume_header->type != F_EMPTY) {
+            *error_message = newvstrallocf(*error_message,
+                      _("%sFound a non-amanda tape; check and relabel it with 'amlabel -f'\n"),
+                      *error_message);
+           g_object_unref(device);
+            return -1;
+       }
+       g_object_unref(device);
+
+        *label = find_brand_new_tape_label();
+        if (*label != NULL) {
+            *timestamp = stralloc("X");
+            *error_message = newvstrallocf(*error_message,
+                     _("%sFound an empty tape, will label it `%s'.\n"),
+                                           *error_message, *label);
+
+            return 3;
+        }
+        *error_message = newvstrallocf(*error_message,
+                 _("%sFound an empty tape, but have no labels left.\n"),
+                                       *error_message);
+
+        return -1;
+    } else {
+        char * label_errstr;
+       label_errstr = g_strdup_printf(_("Error reading label: %s.\n"),
+                                      device_error_or_status(device));
+        *error_message = newvstralloc(*error_message, *error_message,
+                                      label_errstr, NULL);
+        g_free(label_errstr);
+        return -1;
     }
 
-    vstrextend(error_message, "read label `", *label, "', date `",
-               *timestamp, "'\n", NULL);
+    g_assert(*label != NULL && *timestamp != NULL);
+    g_object_unref(device);
+
+    *error_message = newvstrallocf(*error_message,
+                                   _("%sread label `%s', date `%s'.\n"),
+                                   *error_message, *label, *timestamp);
 
+    /* Register this with the barcode database, even if its not ours. */
+    if (slot != NULL) {
+        changer_label(slot, *label);
+    }
+    
     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;
-            }
+    labelstr = getconf_str(CNF_LABELSTR);
+    if(!match(labelstr, *label)) {
+        *error_message = newvstrallocf(*error_message,
+                              _("%slabel \"%s\" doesn't match \"%s\".\n"),
+                                       *error_message, *label, labelstr);
 
-            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;
-            }
+        return -1;
+    } else {
+        tape_t *tp;
+        if (strcmp(*timestamp, "X") == 0) {
+            /* new, labeled tape. */
+            return 1;
+        }
+        
+        tp = lookup_tapelabel(*label);
+        
+        if(tp == NULL) {
+            *error_message =
+                newvstrallocf(*error_message, 
+                              _("%slabel \"%s\" matches labelstr but it is" 
+                                " not listed in the tapelist file.\n"),
+                              *error_message, *label);
+            return -1;
+        } else if(tp != NULL && !reusable_tape(tp)) {
+            *error_message = 
+                newvstrallocf(*error_message,
+                              _("%sTape with label %s is still active" 
+                                " and cannot be overwritten.\n"),
+                              *error_message, *label);
+            return -1;
         }
     }
   
@@ -136,86 +217,188 @@ int scan_read_label(char *dev, char *desired_label,
     return 2;
 }
 
-/* Interface is the same as taper_scan, with the addition of the tapedev
- * output. */
+/* Interface is the same as taper_scan, with some additional bookkeeping. */
 typedef struct {
     char *wantlabel;
     char **gotlabel;
     char **timestamp;
     char **error_message;
     char **tapedev;
+    char *slotstr; /* Best-choice slot number. */
     char *first_labelstr_slot;
     int backwards;
     int tape_status;
+    TaperscanOutputFunctor output_callback;
+    void *output_data;
+    TaperscanProlongFunctor prolong_callback;
+    void * prolong_data;
+    taper_scan_tracker_t * persistent;
 } changertrack_t;
 
-int scan_slot(void *data, int rc, char *slotstr, char *device) {
+int
+scan_slot(
+     void *data,
+     int rc,
+     char *slotstr,
+     char *device)
+{
     int label_result;
     changertrack_t *ct = ((changertrack_t*)data);
+    int result;
+
+    if (ct->prolong_callback &&
+        !ct->prolong_callback(ct->prolong_data)) {
+        return 1;
+    }
+
+    if (ct->persistent != NULL) {
+        gpointer key;
+        gpointer value;
+        if (g_hash_table_lookup_extended(ct->persistent->scanned_slots,
+                                         slotstr, &key, &value)) {
+            /* We already returned this slot in a previous invocation,
+               skip it now. */
+            return 0;
+        }
+    }
+
+    if (*(ct->error_message) == NULL)
+       *(ct->error_message) = stralloc("");
 
     switch (rc) {
     default:
-        newvstralloc(*(ct->error_message), *(ct->error_message),
-                     "fatal changer error ", slotstr, ": ",
-                     changer_resultstr, NULL);
-        return 1;
+       *(ct->error_message) = newvstrallocf(*(ct->error_message),
+                  _("%sfatal changer error: slot %s: %s\n"),
+                  *(ct->error_message), slotstr, changer_resultstr);
+        result = 1;
+       break;
+
     case 1:
-        newvstralloc(*(ct->error_message), *(ct->error_message),
-                     "changer error ", slotstr, ": ", changer_resultstr, NULL);
-        return 0;
+       *(ct->error_message) = newvstrallocf(*(ct->error_message),
+                  _("%schanger error: slot %s: %s\n"),
+                  *(ct->error_message), slotstr, changer_resultstr);
+        result = 0;
+       break;
+
     case 0:
-       vstrextend(ct->error_message, "slot ", slotstr, ": ", NULL);
-        label_result = scan_read_label(device, ct->wantlabel, ct->gotlabel,
+       *(ct->error_message) = newvstrallocf(*(ct->error_message),
+                                       _("slot %s:"), slotstr);
+       amfree(*ct->gotlabel);
+       amfree(*ct->timestamp);
+        label_result = scan_read_label(device, slotstr,
+                                       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;
+            amfree(ct->slotstr);
+            ct->slotstr = stralloc(slotstr);
+            result = 1;
         } else {
-            return 0;
-        }
+           if ((label_result == 2) && (ct->first_labelstr_slot == NULL))
+               ct->first_labelstr_slot = stralloc(slotstr);
+           result = 0;
+       }
+       break;
     }
-    /* NOTREACHED */
-    return 1;
+    ct->output_callback(ct->output_data, *(ct->error_message));
+    amfree(*(ct->error_message));
+    return result;
 }
 
 static int 
-scan_init(void *data, int rc, int nslots, int backwards, int searchable) {
+scan_init(
+    void *data,
+    int rc,
+    G_GNUC_UNUSED int nslots,
+    int backwards,
+    G_GNUC_UNUSED 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->error_message) = newvstrallocf(*(ct->error_message),
+               _("%scould not get changer info: %s\n"),
+               *(ct->error_message), changer_resultstr);
+       ct->output_callback(ct->output_data, *(ct->error_message));
+       amfree(*(ct->error_message));
     }
 
     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};
+int
+changer_taper_scan(
+    char *wantlabel,
+    char **gotlabel,
+    char **timestamp,
+    char **tapedev,
+    taper_scan_tracker_t * tracker,
+    TaperscanOutputFunctor taperscan_output_callback,
+    void *output_data,
+    TaperscanProlongFunctor prolong_callback,
+    void * prolong_data)
+{
+    char *error_message = NULL;
+    changertrack_t local_data;
+    char *outslotstr = NULL;
+    int result;
 
     *gotlabel = *timestamp = *tapedev = NULL;
-    
+    local_data.wantlabel = wantlabel;
+    local_data.gotlabel  = gotlabel;
+    local_data.timestamp = timestamp;
+    local_data.error_message = &error_message;
+    local_data.tapedev = tapedev;
+    local_data.first_labelstr_slot = NULL;
+    local_data.backwards = 0;
+    local_data.tape_status = 0;
+    local_data.output_callback  = taperscan_output_callback;
+    local_data.output_data = output_data;
+    local_data.prolong_callback = prolong_callback;
+    local_data.prolong_data = prolong_data;
+    local_data.persistent = tracker;
+    local_data.slotstr = NULL;
+
     changer_find(&local_data, scan_init, scan_slot, wantlabel);
     
     if (*(local_data.tapedev)) {
         /* We got it, and it's loaded. */
+        if (local_data.persistent != NULL && local_data.slotstr != NULL) {
+            g_hash_table_insert(local_data.persistent->scanned_slots,
+                                local_data.slotstr, NULL);
+        } else {
+            amfree(local_data.slotstr);
+        }
+       amfree(local_data.first_labelstr_slot);
         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);
+        if (prolong_callback && !prolong_callback(prolong_data)) {
+            return -1;
+        }
+        result = changer_loadslot(local_data.first_labelstr_slot,
+                                  &outslotstr, tapedev);
+       amfree(local_data.first_labelstr_slot);
+        amfree(outslotstr);
+        if (result == 0) {
+           amfree(*gotlabel);
+           amfree(*timestamp);
+            result = scan_read_label(*tapedev, NULL, NULL,
+                                     gotlabel, timestamp,
+                                     &error_message);
+            taperscan_output_callback(output_data, error_message);
+            amfree(error_message);
+            if (result > 0 && local_data.persistent != NULL &&
+                local_data.slotstr != NULL) {
+                g_hash_table_insert(local_data.persistent->scanned_slots,
+                                    local_data.slotstr, NULL);
+            } else {
+                amfree(local_data.slotstr);
+            }
+            return result;
         }
     }
 
@@ -225,11 +408,15 @@ int changer_taper_scan(char* wantlabel,
 }
 
 int taper_scan(char* wantlabel,
-               char** gotlabel, char** timestamp, char** error_message,
-               char** tapedev) {
-
-    *gotlabel = *timestamp = *error_message = NULL;
-    *tapedev = getconf_str(CNF_TAPEDEV);
+               char** gotlabel, char** timestamp, char** tapedev,
+               taper_scan_tracker_t * tracker,
+               TaperscanOutputFunctor output_functor,
+               void *output_data,
+               TaperscanProlongFunctor prolong_functor,
+              void *prolong_data) {
+    char *error_message = NULL;
+    int result;
+    *gotlabel = *timestamp = NULL;
 
     if (wantlabel == NULL) {
         tape_t *tmp;
@@ -240,36 +427,51 @@ int taper_scan(char* wantlabel,
     }
 
     if (changer_init()) {
-        return changer_taper_scan(wantlabel, gotlabel, timestamp,
-                                    error_message, tapedev);
+        result =  changer_taper_scan(wantlabel, gotlabel, timestamp,
+                                    tapedev, tracker,
+                                     output_functor, output_data,
+                                     prolong_functor, prolong_data);
+    } else {
+        /* Note that the tracker is not used in this case. */
+       *tapedev = stralloc(getconf_str(CNF_TAPEDEV));
+       if (*tapedev == NULL) {
+           result = -1;
+           output_functor(output_data, _("No tapedev specified"));
+       } else {
+           result =  scan_read_label(*tapedev, NULL, wantlabel, gotlabel,
+                                      timestamp, &error_message);
+            output_functor(output_data, error_message);
+           amfree(error_message);
+       }
     }
 
-    return scan_read_label(*tapedev, wantlabel,
-                           gotlabel, timestamp, error_message);
+    return result;
 }
 
 #define AUTO_LABEL_MAX_LEN 1024
-char* find_brand_new_tape_label() {
+char *
+find_brand_new_tape_label(void)
+{
     char *format;
     char newlabel[AUTO_LABEL_MAX_LEN];
-    char tmpnum[12];
+    char tmpnum[30]; /* 64-bit integers can be 21 digists... */
     char tmpfmt[16];
     char *auto_pos = NULL;
-    int i, format_len, label_len, auto_len;
+    int i;
+    ssize_t 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");
+            g_fprintf(stderr, _("Auto label format is too long!\n"));
             return NULL;
         }
 
@@ -298,26 +500,27 @@ char* find_brand_new_tape_label() {
     }
 
     if (auto_pos == NULL) {
-        fprintf(stderr, "Auto label template contains no '%%'!\n");
+        g_fprintf(stderr, _("Auto label template contains no '%%'!\n"));
         return NULL;
     }
 
-    sprintf(tmpfmt, "%%0%dd", auto_len);
+    g_snprintf(tmpfmt, SIZEOF(tmpfmt), "%%0%zdd",
+            (size_t)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");
+        g_snprintf(tmpnum, SIZEOF(tmpnum), tmpfmt, i);
+        if (strlen(tmpnum) != (size_t)auto_len) {
+            g_fprintf(stderr, _("All possible auto-labels used.\n"));
             return NULL;
         }
 
-        strncpy(auto_pos, tmpnum, auto_len);
+        strncpy(auto_pos, tmpnum, (size_t)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",
+                g_fprintf(stderr, _("New label %s does not match labelstr %s from amanda.conf\n"),
                         newlabel, getconf_str(CNF_LABELSTR));
                 return 0;
             }
@@ -325,7 +528,55 @@ char* find_brand_new_tape_label() {
         }
     }
 
-    /* NOTREACHED. Unless you have over two billion tapes. */
-    fprintf(stderr, "Taper internal error in find_brand_new_tape_label.");
+    /* Should not get here unless you have over two billion tapes. */
+    g_fprintf(stderr, _("Taper internal error in find_brand_new_tape_label."));
     return 0;
 }
+
+void
+FILE_taperscan_output_callback(
+    void *data,
+    char *msg)
+{
+    if(!msg) return;
+    if(strlen(msg) == 0) return;
+
+    if(data)
+       g_fprintf((FILE *)data, "%s", msg);
+    else
+       g_printf("%s", msg);
+}
+
+void
+CHAR_taperscan_output_callback(
+    /*@keep@*/ void *data,
+               char *msg)
+{
+    char **s = (char **)data;
+
+    if(!msg) return;
+    if(strlen(msg) == 0) return;
+
+    if(*s)
+       strappend(*s, msg);
+    else
+       *s = stralloc(msg);
+}
+
+taper_scan_tracker_t * taper_scan_tracker_new(void) {
+    taper_scan_tracker_t * rval = malloc(sizeof(*rval));
+    
+    rval->scanned_slots = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                g_free, NULL);
+
+    return rval;
+}
+
+void taper_scan_tracker_free(taper_scan_tracker_t * tracker) {
+    if (tracker->scanned_slots != NULL) {
+        g_hash_table_destroy(tracker->scanned_slots);
+    }
+    
+    free(tracker);
+}
+