Imported Upstream version 3.3.1
[debian/amanda] / perl / Amanda / Device.swg
index 73aac3137fd8f920a5d43d2e6664e4e909dfe4b3..d40031605f9bbb5955f3b74857ced43ba0212182 100644 (file)
 /*
- * Copyright (c) Zmanda, Inc.  All Rights Reserved.
+ * Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc.  All Rights Reserved.
  *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
  *
- * This library is distributed in the hope that it will be useful, but
+ * 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 Lesser General Public
- * License for more details.
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
+ * 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
+ * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
  */
 
 %module "Amanda::Device"
 %include "amglue/amglue.swg"
 %include "exception.i"
+%import "Amanda/Header.swg";
+
+%include "Amanda/Device.pod"
 
 %{
 #include "device.h"
 #include "property.h"
 #include "fileheader.h"
-%}
-
-/* import dumptype_t, among others */
-%import "Amanda/Types.swg";
-
-%perlcode %{
-=head1 NAME
-
-Amanda::Device - interact with Amanda data-storage devices
-
-=head1 SYNOPSIS
-
-  use Amanda::Device qw( :constants );
-
-  my $dev = Amanda::Device->new($device_name);
-  $dev->set_startup_properties_from_config();
-  if ($dev->read_label() == $READ_LABEL_STATUS_SUCCESS) {
-      print "Label on $device_name is '$dev->volume_label'\n";
-  }
-  
-See http://wiki.zmanda.com/index.php/Device_API for details on how Devices are used.
-
-=head1 API STATUS
-
-Stable
-
-=head1 Amanda::Device Objects
-
-=head2 Instance Variables
-
-=over
-
-=item C<$file>
-
-=item C<$block>
-
-=item C<$in_file>
-
-=item C<$device_name>
-
-=item C<$access_mode>
-
-=item C<$is_eof>
-
-=item C<$volume_label>
-
-=item C<$volume_time>
-
-=back
-
-=head2 Methods
-
-See the wiki for descriptions of these functions
-
-=over
-
-=item C<read_label()>
-
-=item C<start($mode, $label, $timestamp)>
-
-=item C<finish()>
-
-=item C<start_file($jobinfo)>
-
-where C<$jobinfo> is a C<dumpfile_t> (see L<Amanda::Datatypes>)
-
-=item C<write_min_size()>
-
-=item C<write_max_size()>
-
-=item C<read_max_size()>
-
-=item C<write_block($size, $data, $short_block)>
-
-Note that Perl code is not expected to handle on-device data, so there
-is currently no way to provide data to this function from Perl.  This may
-change in future revisions.
-
-=item C<write_from_fd($fd)>
-
-where C<$fd> is an integer file descriptor, not a filehandle
-
-=item C<finish_file()>
-
-=item C<seek_file($file)>
-
-=item C<seek_block($block)>
-
-=item C<read_block($size)>
-
-=item C<read_to_fd($fd)>
-
-where C<$fd> is an integer file descriptor, not a filehandle
-
-Note that Perl code is not expected to handle on-device data, so there
-is currently no way to access the data this function returns.  This may
-change in future revisions.
-
-=item C<property_list()>
-
-returns a list of property names.
-
-=item C<property_get($property_name)>
-
-returns the property as the appropriate Perl type.
-
-=item C<property_set($property_name, $value)>
-
-where $value is of an appropriate type for the given property
-
-=item C<recycle_file($filenum)>
-
-=item C<set_startup_properties_from_config()>
-
-=back
-
-=head1 CONSTANTS
-
-This module defines a large number of constants.  Again, consult the
-wiki or C<device.h> for the details on their meaning.  These constants
-are available from the package namespace (e.g.,
-C<Amanda::Device::ACCESS_WRITE>), of imported with the C<:constant>
-import tag.
-
-=cut
+#include "glib-util.h"
+#include "simpleprng.h"
+#include "amanda.h"
+#include "sockaddr-util.h"
 %}
 
 %init %{
@@ -162,6 +44,11 @@ import tag.
 
 /* Utility functions for typemaps, below */
 
+/* return a new, mortal SV corresponding to the given GValue
+ *
+ * @param value: the value to convert
+ * @returns: a new, mortal SV
+ */
 static SV *
 set_sv_from_gvalue(GValue *value)
 {
@@ -171,44 +58,16 @@ set_sv_from_gvalue(GValue *value)
     /* complex reference types */
     switch (fundamental) {
        case G_TYPE_LONG:
-           sv = sv_2mortal(amglue_newSVi64(g_value_get_long(value)));
-           break;
+           return sv_2mortal(amglue_newSVi64(g_value_get_long(value)));
 
        case G_TYPE_ULONG:
-           sv = sv_2mortal(amglue_newSVu64(g_value_get_ulong(value)));
-           break;
+           return sv_2mortal(amglue_newSVu64(g_value_get_ulong(value)));
 
        case G_TYPE_INT64:
-           sv = sv_2mortal(amglue_newSVi64(g_value_get_int64(value)));
-           break;
+           return sv_2mortal(amglue_newSVi64(g_value_get_int64(value)));
 
        case G_TYPE_UINT64:
-           sv = sv_2mortal(amglue_newSVu64(g_value_get_uint64(value)));
-           break;
-
-       case G_TYPE_BOXED: {
-           GType boxed_type = G_VALUE_TYPE(value);
-           QualifiedSize qs;
-           HV *hv;
-
-           if (boxed_type == QUALIFIED_SIZE_TYPE) {
-               qs = *(QualifiedSize*)(g_value_get_boxed(value));
-               
-               /* build a hash */
-               hv = (HV *)sv_2mortal((SV *)newHV());
-               hv_store(hv, "accuracy", 8, newSViv(qs.accuracy), 0);
-               hv_store(hv, "bytes", 5, amglue_newSVi64(qs.bytes), 0);
-
-               sv = newRV((SV *)hv);
-               return newRV((SV *)hv);
-           } else {
-               warn("Unsupported boxed property type #%d", boxed_type);
-
-               sv = sv_newmortal();
-               sv_setsv(sv, &PL_sv_undef);
-               return sv;
-           }
-       }
+           return sv_2mortal(amglue_newSVu64(g_value_get_uint64(value)));
     }
 
     /* simple types that can be constructed with sv_set*v */
@@ -260,7 +119,7 @@ set_sv_from_gvalue(GValue *value)
        case G_TYPE_INTERFACE:
        case G_TYPE_OBJECT:
        case G_TYPE_PARAM:
-           warn("Unsupported fundamental property type #%d", fundamental);
+           warn("Unsupported fundamental property type #%d", (int)fundamental);
            sv_setsv(sv, &PL_sv_undef);
            break;
     }
@@ -268,106 +127,116 @@ set_sv_from_gvalue(GValue *value)
     return sv;
 }
 
+/* Given an SV and an initialized GValue, set the GValue to the value
+ * represented by the SV.  The GValue's type must already be set.
+ *
+ * For basic corresponding types (string -> string, integer -> integer),
+ * the translation is straightforward.  However, if the GValue is not a
+ * string, but the SV has a string value, then g_value_set_from_string will
+ * be used to parse the string.
+ *
+ * @param sv: SV to convert
+ * @param value: (input/output) destination
+ * @returns: TRUE on success
+ */
 static gboolean
 set_gvalue_from_sv(SV *sv, GValue *value)
 {
     GType fundamental = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
-    switch (fundamental) {
-       case G_TYPE_CHAR:
-           if (!SvIOK(sv)) return FALSE;
-           g_value_set_char(value, SvIV(sv));
-           break;
 
-       case G_TYPE_UCHAR:
-           if (!SvIOK(sv)) return FALSE;
-           g_value_set_uchar(value, SvUV(sv));
-           break;
+    /* if we got a string, use g_value_set_from_string to parse any funny
+     * values or suffixes */
+    if (SvPOK(sv)) {
+       if (g_value_set_from_string(value, SvPV_nolen(sv)))
+           return TRUE;
+    }
 
+    /* otherwise, handle numeric types with SvIV, SvNV, or the amglue_* functions */
+    switch (fundamental) {
        case G_TYPE_BOOLEAN:
-           if (!SvIOK(sv)) return FALSE;
            g_value_set_boolean(value, SvIV(sv));
-           break;
+           return TRUE;
+
+       case G_TYPE_CHAR:
+           g_value_set_char(value, amglue_SvI8(sv));
+           return TRUE;
+
+       case G_TYPE_UCHAR:
+           g_value_set_uchar(value, amglue_SvU8(sv));
+           return TRUE;
 
        case G_TYPE_INT:
            g_value_set_int(value, amglue_SvI32(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_UINT:
            g_value_set_uint(value, amglue_SvU32(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_LONG:
            g_value_set_int64(value, amglue_SvI64(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_ULONG:
            g_value_set_uint64(value, amglue_SvU64(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_INT64:
            g_value_set_int64(value, amglue_SvI64(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_UINT64:
            g_value_set_uint64(value, amglue_SvU64(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_FLOAT:
-           if (!SvNOK(sv)) return FALSE;
            g_value_set_float(value, SvNV(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_DOUBLE:
-           if (!SvNOK(sv)) return FALSE;
            g_value_set_double(value, SvNV(sv));
-           break;
-
-       case G_TYPE_STRING:
-           if (!SvPOK(sv)) return FALSE;
-           g_value_set_string(value, SvPV_nolen(sv));
-           break;
+           return TRUE;
 
-       case G_TYPE_ENUM: 
-           if (!SvIOK(sv)) return FALSE;
+       case G_TYPE_ENUM:
            g_value_set_enum(value, SvIV(sv));
-           break;
+           return TRUE;
 
        case G_TYPE_FLAGS:
-           if (!SvIOK(sv)) return FALSE;
            g_value_set_flags(value, SvIV(sv));
-           break;
+           return TRUE;
 
-       /* Unsupported */
        default:
-       case G_TYPE_POINTER:
-       case G_TYPE_INTERFACE:
-       case G_TYPE_BOXED: /* note: *getting* boxed values is supported */
-       case G_TYPE_OBJECT:
-       case G_TYPE_PARAM:
-           return FALSE;
+           /* for anything else, let perl stringify it for us and try parsing it */
+           return g_value_set_from_string(value, SvPV_nolen(sv));
     }
-
-    return TRUE;
 }
 
 %}
 
+/*
+ * DirectTCPConnection object
+ */
+
+typedef struct DirectTCPConnection {
+    %extend {
+       ~DirectTCPConnection() {
+           g_object_unref(self);
+       };
+
+       %newobject close;
+       char *close() {
+           return directtcp_connection_close(self);
+       }
+    };
+} DirectTCPConnection;
+
 /*
  * Device struct, %extend-ed into a Perl class
  */
 
+%name(unaliased_name) extern char *device_unaliased_name(char *);
+
 typedef struct Device {
-    /* Instance variables -- all readonly */
-    %immutable;
-    int file;
-    guint64 block;
-    gboolean in_file;
-    char * device_name;
-    DeviceAccessMode access_mode;
-    gboolean is_eof;
-    char * volume_label;
-    char * volume_time;
-    %mutable;
 
     /* methods */
     %extend {
@@ -380,7 +249,27 @@ typedef struct Device {
            g_object_unref(self);
        }
 
-       ReadLabelStatusFlags
+       gboolean
+       configure(gboolean use_global_config) {
+           return device_configure(self, use_global_config);
+       }
+
+       char *
+       error() {
+           return device_error(self);
+       }
+
+       char *
+       status_error() {
+           return device_status_error(self);
+       }
+
+       char *
+       error_or_status() {
+           return device_error_or_status(self);
+       }
+
+       DeviceStatusFlags
        read_label() {
            return device_read_label(self);
        }
@@ -396,33 +285,13 @@ typedef struct Device {
        }
 
        gboolean
-       start_file(const dumpfile_t *jobInfo) {
+       start_file(dumpfile_t *jobInfo) {
            return device_start_file(self, jobInfo);
        }
 
-       guint
-       write_min_size() {
-           return device_write_min_size(self);
-       }
-
-       guint
-       write_max_size() {
-           return device_write_max_size(self);
-       }
-
-       guint
-       read_max_size() {
-           return device_read_max_size(self);
-       }
-
        gboolean
-       write_block(guint size, gpointer data, gboolean short_block) {
-           return device_write_block(self, size, data, short_block);
-       }
-
-       gboolean
-       write_from_fd(int fd) {
-           return device_write_from_fd(self, fd);
+       write_block(guint size, gpointer data) {
+           return device_write_block(self, size, data);
        }
 
        gboolean
@@ -445,34 +314,113 @@ typedef struct Device {
            return device_read_block(self, buffer, size);
        }
 
-       gboolean read_to_fd(int fd) {
-           return device_read_to_fd(self, fd);
+       gboolean
+       erase() {
+           return device_erase(self);
+       }
+
+       gboolean
+       eject() {
+           return device_eject(self);
+       }
+
+       gboolean
+       directtcp_supported() {
+           return device_directtcp_supported(self);
+       }
+
+       void
+       listen(gboolean for_writing, DirectTCPAddr **addrs) {
+           /* ensure that the addresses are empty if there was an error */
+           if (!device_listen(self, for_writing, addrs))
+               *addrs = NULL;
+       }
+
+       %newobject accept; /* connection is already ref'd, so we own it */
+       DirectTCPConnection *
+       accept() {
+           DirectTCPConnection *conn = NULL;
+           gboolean rv;
+
+           rv = device_accept(self, &conn, NULL, NULL);
+           if (!rv && conn) {
+               /* conn is ref'd for our convenience, but we don't want it */
+               g_object_unref(conn);
+               conn = NULL;
+           }
+           return conn;
+       }
+
+       %newobject connect; /* connection is already ref'd, so we own it */
+       DirectTCPConnection *
+       connect(gboolean for_writing, DirectTCPAddr *addrs) {
+           DirectTCPConnection *conn = NULL;
+           gboolean rv;
+
+           rv = device_connect(self, for_writing, addrs, &conn, NULL, NULL);
+           if (!rv && conn) {
+               /* conn is ref'd for our convenience, but we don't want it */
+               g_object_unref(conn);
+               conn = NULL;
+           }
+           return conn;
+       }
+
+       gboolean
+       use_connection(DirectTCPConnection *conn) {
+           return device_use_connection(self, conn);
+       }
+
+       %typemap(in,numinputs=0) guint64 *actual_size (guint64 sz) {
+           sz = 0;
+           $1 = &sz;
+       }
+       %typemap(argout) guint64 *actual_size {
+           SP += argvi; PUTBACK;
+           $result = sv_2mortal(amglue_newSVu64(*$1));
+           SPAGAIN; SP -= argvi; argvi++;
+       }
+       gboolean
+       write_from_connection(guint64 size, guint64 *actual_size) {
+           return device_write_from_connection(self, size, actual_size);
+       }
+
+       gboolean
+       read_to_connection(guint64 size, guint64 *actual_size) {
+           return device_read_to_connection(self, size, actual_size);
        }
 
-       %typemap(out) const DeviceProperty * {
-           int i = 0;
-           int len = 0;
+       %typemap(out) const GSList * {
+           GSList *iter;
 
            /* Count the DeviceProperties */
-           while ($1[len].base) len++;
-           EXTEND(SP, len); /* make room for return values */
+           EXTEND(SP, g_slist_length($1)); /* make room for return values */
 
            /* Note that we set $result several times. the nature of
             * SWIG's wrapping is such that incrementing argvi points
             * $result to the next location in perl's argument stack.
              */
 
-           for (i = 0; i < len ; i++) { 
-               $result = sv_newmortal(); 
-               sv_setpv($result, $1[i].base->name); 
+           for (iter = $1; iter; iter = g_slist_next(iter)) {
+               DeviceProperty *prop = iter->data;
+               HV *hash = newHV();
+               SV *rv = newRV_noinc((SV *)hash);
+
+               hv_store(hash, "name", 4,
+                       newSVpv(prop->base->name, 0), 0);
+               hv_store(hash, "description", 11,
+                       newSVpv(prop->base->description, 0), 0);
+               hv_store(hash, "access", 6,
+                       newSViv(prop->access), 0);
+               $result = sv_2mortal(rv);
                argvi++;
-           }
+           }
        }
-       const DeviceProperty * property_list(void) {
+       const GSList * property_list(void) {
            return device_property_get_list(self);
        }
 
-       %typemap(out) const DeviceProperty *; /* remove typemap */
+       %typemap(out) const GSList *; /* remove typemap */
 
        /* A typemap to convert a property name to a DevicePropertyBase. */
        %typemap(in) DevicePropertyBase * {
@@ -481,40 +429,60 @@ typedef struct Device {
            if (SvPOK($input))
                pname = SvPV_nolen($input);
 
-           if (pname) $1 = (DevicePropertyBase *)device_property_get_by_name(pname);
-           if (!pname || !$1) {
-               SWIG_exception_fail(SWIG_ValueError, "Invalid property name");
-           }
+           if (pname)
+               $1 = (DevicePropertyBase *)device_property_get_by_name(pname);
+           else
+               $1 = NULL;
        }
 
        /* A typemap to convert the GValue in property_get to a return value.  The
         * (in) typemap sets up storage for the parameters, while the (argout) converts
         * them to a perl SV. */
-       %typemap(in,numinputs=0) (GValue *out_val, gboolean *val_found)
-                           (GValue val, gboolean found) {
+       %typemap(in,numinputs=0) (GValue *out_val, PropertySurety *surety,
+                                 PropertySource *source, gboolean *val_found)
+                           (GValue val,
+                            PropertySurety surety,
+                            PropertySource source,
+                            gboolean found) {
            memset(&val, 0, sizeof(val));
            $1 = &val;
-           $2 = &found;
+           if (GIMME_V == G_ARRAY) {
+               $2 = &surety;
+               $3 = &source;
+           }
+           $4 = &found;
        }
-       %typemap(argout) (GValue *out_val, gboolean *val_found) {
+       %typemap(argout) (GValue *out_val, PropertySurety *surety,
+                         PropertySource *source, gboolean *val_found) {
            /* if the result is valid */
-           if (*$2) {
-               /* move data from $1 to $result, somehow */
+           if (*$4) {
+               /* move data from $1 to $result, somehow, being careful to
+                * save the perl stack while doing so */
+               SP += argvi; PUTBACK;
                $result = set_sv_from_gvalue($1);
+               SPAGAIN; SP -= argvi; argvi++;
 
                /* free any memory for the GValue */
                g_value_unset($1);
-           } else {
-               /* silently return 'undef', the sentinel for "undefined" */
-               $result = sv_newmortal();
-               sv_setsv($result, &PL_sv_undef);
+
+               if (GIMME_V == G_ARRAY) {
+                   $result = newSViv(*$2);
+                   argvi++;
+                   $result = newSViv(*$3);
+                   argvi++;
+               }
            }
-           argvi++;
+           /* otherwise, return nothing */
        }
 
        void
-       property_get(DevicePropertyBase *pbase, GValue *out_val, gboolean *val_found) {
-           *val_found = device_property_get(self, pbase->ID, out_val);
+       property_get(DevicePropertyBase *pbase, GValue *out_val, PropertySurety *surety,
+                    PropertySource *source, gboolean *val_found) {
+           if (pbase) {
+               *val_found = device_property_get_ex(self, pbase->ID, out_val, surety, source);
+           } else {
+               *val_found = FALSE;
+           }
        }
 
        /* delete typemaps */
@@ -529,12 +497,35 @@ typedef struct Device {
        gboolean
        property_set(DevicePropertyBase *pbase, SV *sv) {
            GValue gval;
+
+           if (!pbase)
+               goto fail;
            memset(&gval, 0, sizeof(gval));
            g_value_init(&gval, pbase->type);
            if (!set_gvalue_from_sv(sv, &gval))
-               goto fail;
+               goto failunset;
 
            if (!device_property_set(self, pbase->ID, &gval))
+               goto failunset;
+
+           g_value_unset(&gval);
+           return TRUE;
+       failunset:
+           g_value_unset(&gval);
+       fail:
+           return FALSE;
+       }
+
+       gboolean
+       property_set_ex(DevicePropertyBase *pbase, SV *sv,
+                       PropertySurety surety, PropertySource source) {
+           GValue gval;
+           memset(&gval, 0, sizeof(gval));
+           g_value_init(&gval, pbase->type);
+           if (!set_gvalue_from_sv(sv, &gval))
+               goto fail;
+
+           if (!device_property_set_ex(self, pbase->ID, &gval, surety, source))
                goto fail;
 
            g_value_unset(&gval);
@@ -547,13 +538,145 @@ typedef struct Device {
        gboolean recycle_file(guint filenum) {
            return device_recycle_file(self, filenum);
        }
-       
-       void set_startup_properties_from_config(void) {
-           device_set_startup_properties_from_config(self);
-       }
+
+       /* accessor functions */
+
+       int file(void) { return self->file; }
+       guint64 block(void) { return self->block; }
+       gboolean in_file(void) { return self->in_file; }
+       char * device_name(void) { return self->device_name; }
+       DeviceAccessMode access_mode(void) { return self->access_mode; }
+       gboolean is_eof(void) { return self->is_eof; }
+       gboolean is_eom(void) { return self->is_eom; }
+       char * volume_label(void) { return self->volume_label; }
+       char * volume_time(void) { return self->volume_time; }
+       DeviceStatusFlags status(void) { return self->status; }
+       gsize min_block_size(void) { return self->min_block_size; }
+       gsize max_block_size(void) { return self->max_block_size; }
+       gsize block_size(void) { return self->block_size; }
+       gsize header_block_size(void) { return self->header_block_size; }
+       dumpfile_t *volume_header(void) { return self->volume_header; }
     };
+
 } Device;
 
+/* An alternate constructor for RAIT devices */
+%typemap(in) GSList *child_devices {
+    AV *av;
+    int i, len;
+
+    if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV) {
+       SWIG_exception(SWIG_TypeError, "Expected an arrayref");
+    }
+    av = (AV *)SvRV($input);
+
+    $1 = NULL;
+    len = av_len(av);
+    for (i = 0; i <= len; i++) {
+       SV **elt = av_fetch(av, i, 0);
+       Device *d;
+
+       if (elt && !SvOK(*elt)) {
+           $1 = g_slist_append($1, NULL); /* 'undef' => NULL */
+       } else if (!elt || SWIG_ConvertPtr(*elt, (void **)&d, $descriptor(Device *), 0) == -1) {
+           SWIG_exception(SWIG_TypeError, "array member is not a Device");
+       } else {
+           $1 = g_slist_append($1, d);
+       }
+    }
+}
+%typemap(freearg) GSList *child_devices {
+    g_slist_free($1);
+}
+%newobject rait_device_open_from_children;
+Device *rait_device_open_from_children(GSList *child_devices);
+%perlcode %{
+sub new_rait_from_children {
+    my $class = shift; # strip the $class from the arguments
+    return rait_device_open_from_children([@_]);
+}
+%}
+
+/*
+ * Utilities for installchecks (not described in POD)
+ */
+
+%inline %{
+
+/* write LENGTH bytes of random data to FILENAME, seeded with SEED */
+gboolean
+write_random_to_device(guint32 seed, size_t length, Device *device) {
+    simpleprng_state_t prng;
+    char *buf;
+    gsize block_size = device->block_size;
+    g_assert(block_size < G_MAXUINT);
+
+    buf = g_malloc(block_size);
+    simpleprng_seed(&prng, seed);
+
+    while (length) {
+       size_t to_write = min(block_size, length);
+
+       simpleprng_fill_buffer(&prng, buf, to_write);
+       if (!device_write_block(device, (guint)block_size, buf)) {
+           g_free(buf);
+           return FALSE;
+       }
+       length -= to_write;
+    }
+
+    g_free(buf);
+    return TRUE;
+}
+
+/* read LENGTH bytes of random data from FILENAME verifying it against
+ * a PRNG seeded with SEED.  Sends any error messages to stderr.
+ */
+gboolean
+verify_random_from_device(guint32 seed, size_t length, Device *device) {
+    simpleprng_state_t prng;
+    char *buf = NULL; /* first device_read_block will get the size */
+    int block_size = 0;
+
+    simpleprng_seed(&prng, seed);
+
+    while (length) {
+       int bytes_read;
+       int size = block_size;
+
+       bytes_read = device_read_block(device, buf, &size);
+       if (bytes_read == 0 && size > block_size) {
+           g_free(buf);
+           block_size = size;
+           buf = g_malloc(block_size);
+           continue;
+       }
+       if (bytes_read == -1) {
+           if (device->status == DEVICE_STATUS_SUCCESS) {
+               g_assert(device->is_eof);
+               g_debug("verify_random_from_device got unexpected EOF");
+           }
+           goto error;
+       }
+
+       /* strip padding */
+       bytes_read = min(bytes_read, length);
+
+       if (!simpleprng_verify_buffer(&prng, buf, bytes_read))
+           goto error;
+
+       length -= bytes_read;
+    }
+
+    g_free(buf);
+    return TRUE;
+
+error:
+    g_free(buf);
+    return FALSE;
+}
+%}
+
 /*
  * Constants
  */
@@ -569,15 +692,15 @@ gboolean IS_WRITABLE_ACCESS_MODE(DeviceAccessMode mode);
 amglue_export_tag(DeviceAccessMode, IS_WRITABLE_ACCESS_MODE);
 amglue_copy_to_tag(DeviceAccessMode, constants);
 
-amglue_add_flag_tag_fns(ReadLabelStatusFlags);
-amglue_add_constant_short(READ_LABEL_STATUS_SUCCESS, "SUCCESS", ReadLabelStatusFlags);
-amglue_add_constant_short(READ_LABEL_STATUS_DEVICE_MISSING, "DEVICE_MISSING", ReadLabelStatusFlags);
-amglue_add_constant_short(READ_LABEL_STATUS_DEVICE_ERROR, "DEVICE_ERROR", ReadLabelStatusFlags);
-amglue_add_constant_short(READ_LABEL_STATUS_VOLUME_MISSING, "VOLUME_MISSING", ReadLabelStatusFlags);
-amglue_add_constant_short(READ_LABEL_STATUS_VOLUME_UNLABELED, "VOLUME_UNLABELED", ReadLabelStatusFlags);
-amglue_add_constant_short(READ_LABEL_STATUS_VOLUME_ERROR, "VOLUME_ERROR", ReadLabelStatusFlags);
-amglue_add_constant_noshort(READ_LABEL_STATUS_FLAGS_MAX, ReadLabelStatusFlags);
-amglue_copy_to_tag(ReadLabelStatusFlags, constants);
+amglue_add_flag_tag_fns(DeviceStatusFlags);
+amglue_add_constant_short(DEVICE_STATUS_SUCCESS, "SUCCESS", DeviceStatusFlags);
+amglue_add_constant_short(DEVICE_STATUS_DEVICE_ERROR, "DEVICE_ERROR", DeviceStatusFlags);
+amglue_add_constant_short(DEVICE_STATUS_DEVICE_BUSY, "DEVICE_BUSY", DeviceStatusFlags);
+amglue_add_constant_short(DEVICE_STATUS_VOLUME_MISSING, "VOLUME_MISSING", DeviceStatusFlags);
+amglue_add_constant_short(DEVICE_STATUS_VOLUME_UNLABELED, "VOLUME_UNLABELED", DeviceStatusFlags);
+amglue_add_constant_short(DEVICE_STATUS_VOLUME_ERROR, "VOLUME_ERROR", DeviceStatusFlags);
+amglue_add_constant_noshort(DEVICE_STATUS_FLAGS_MAX, DeviceStatusFlags);
+amglue_copy_to_tag(DeviceStatusFlags, constants);
 
 amglue_add_flag_tag_fns(PropertyPhaseFlags);
 amglue_add_constant_short(PROPERTY_PHASE_BEFORE_START, "BEFORE_START", PropertyPhaseFlags);
@@ -591,25 +714,25 @@ amglue_add_constant_noshort(PROPERTY_PHASE_SHIFT, PropertyPhaseFlags);
 amglue_copy_to_tag(PropertyPhaseFlags, constants);
 
 amglue_add_flag_tag_fns(PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_GET_BEFORE_START, 
+amglue_add_constant_short(PROPERTY_ACCESS_GET_BEFORE_START,
                    "GET_BEFORE_START", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_GET_BETWEEN_FILE_WRITE, 
+amglue_add_constant_short(PROPERTY_ACCESS_GET_BETWEEN_FILE_WRITE,
                    "GET_BETWEEN_FILE_WRITE", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_GET_INSIDE_FILE_WRITE, 
+amglue_add_constant_short(PROPERTY_ACCESS_GET_INSIDE_FILE_WRITE,
                    "GET_INSIDE_FILE_WRITE", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_GET_BETWEEN_FILE_READ, 
+amglue_add_constant_short(PROPERTY_ACCESS_GET_BETWEEN_FILE_READ,
                    "GET_BETWEEN_FILE_READ", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_GET_INSIDE_FILE_READ, 
+amglue_add_constant_short(PROPERTY_ACCESS_GET_INSIDE_FILE_READ,
                    "GET_INSIDE_FILE_READ", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_SET_BEFORE_START, 
+amglue_add_constant_short(PROPERTY_ACCESS_SET_BEFORE_START,
                    "SET_BEFORE_START", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_SET_BETWEEN_FILE_WRITE, 
+amglue_add_constant_short(PROPERTY_ACCESS_SET_BETWEEN_FILE_WRITE,
                    "SET_BETWEEN_FILE_WRITE", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE, 
+amglue_add_constant_short(PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE,
                    "SET_INSIDE_FILE_WRITE", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_SET_BETWEEN_FILE_READ, 
+amglue_add_constant_short(PROPERTY_ACCESS_SET_BETWEEN_FILE_READ,
                    "SET_BETWEEN_FILE_READ", PropertyAccessFlags);
-amglue_add_constant_short(PROPERTY_ACCESS_SET_INSIDE_FILE_READ, 
+amglue_add_constant_short(PROPERTY_ACCESS_SET_INSIDE_FILE_READ,
                    "SET_INSIDE_FILE_READ", PropertyAccessFlags);
 amglue_add_constant_noshort(PROPERTY_ACCESS_GET_MASK, PropertyAccessFlags);
 amglue_add_constant_noshort(PROPERTY_ACCESS_SET_MASK, PropertyAccessFlags);
@@ -634,33 +757,20 @@ amglue_add_constant_short(MEDIA_ACCESS_MODE_READ_WRITE, "READ_WRITE", MediaAcces
 amglue_add_constant_short(MEDIA_ACCESS_MODE_WRITE_ONLY, "WRITE_ONLY", MediaAccessMode);
 amglue_copy_to_tag(MediaAccessMode, constants);
 
-amglue_add_enum_tag_fns(SizeAccuracy);
-amglue_add_constant_short(SIZE_ACCURACY_UNKNOWN, "UNKNOWN", SizeAccuracy);
-amglue_add_constant_short(SIZE_ACCURACY_ESTIMATE, "ESTIMATE", SizeAccuracy);
-amglue_add_constant_short(SIZE_ACCURACY_REAL, "REAL", SizeAccuracy);
-amglue_copy_to_tag(SizeAccuracy, constants);
-
-amglue_add_flag_tag_fns(FeatureSupportFlags);
-amglue_add_constant_short(FEATURE_STATUS_ENABLED, "STATUS_ENABLED", FeatureSupportFlags);
-amglue_add_constant_short(FEATURE_STATUS_DISABLED, "STATUS_DISABLED", FeatureSupportFlags);
-amglue_add_constant_short(FEATURE_SURETY_BAD, "SURETY_BAD", FeatureSupportFlags);
-amglue_add_constant_short(FEATURE_SURETY_GOOD, "SURETY_GOOD", FeatureSupportFlags);
-amglue_add_constant_short(FEATURE_SOURCE_DEFAULT, "SOURCE_DEFAULT", FeatureSupportFlags);
-amglue_add_constant_short(FEATURE_SOURCE_DETECTED, "SOURCE_DETECTED", FeatureSupportFlags);
-amglue_add_constant_short(FEATURE_SOURCE_USER, "SOURCE_USER", FeatureSupportFlags);
-amglue_add_constant_noshort(FEATURE_SUPPORT_FLAGS_MAX, FeatureSupportFlags);
-amglue_add_constant_noshort(FEATURE_SUPPORT_FLAGS_MASK, FeatureSupportFlags);
-amglue_add_constant_noshort(FEATURE_SUPPORT_FLAGS_STATUS_MASK, FeatureSupportFlags);
-amglue_add_constant_noshort(FEATURE_SUPPORT_FLAGS_SURETY_MASK, FeatureSupportFlags);
-amglue_add_constant_noshort(FEATURE_SUPPORT_FLAGS_SOURCE_MASK, FeatureSupportFlags);
-
-gboolean feature_support_flags_is_valid(FeatureSupportFlags);
-amglue_export_tag(FeatureSupportFlags, feature_support_flags_is_valid);
-amglue_copy_to_tag(FeatureSupportFlags, constants);
+amglue_add_flag_tag_fns(PropertySurety);
+amglue_add_constant_short(PROPERTY_SURETY_BAD, "SURETY_BAD", PropertySurety);
+amglue_add_constant_short(PROPERTY_SURETY_GOOD, "SURETY_GOOD", PropertySurety);
+amglue_copy_to_tag(PropertySurety, constants);
+
+amglue_add_flag_tag_fns(PropertySource);
+amglue_add_constant_short(PROPERTY_SOURCE_DEFAULT, "SOURCE_DEFAULT", PropertySource);
+amglue_add_constant_short(PROPERTY_SOURCE_DETECTED, "SOURCE_DETECTED", PropertySource);
+amglue_add_constant_short(PROPERTY_SOURCE_USER, "SOURCE_USER", PropertySource);
+amglue_copy_to_tag(PropertySource, constants);
 
 %perlcode %{
 
-# SWIG produces a sub-package for the Device "class", in this case named 
+# SWIG produces a sub-package for the Device "class", in this case named
 # Amanda::Device::Device.  For user convenience, we allow Amanda::Device->new(..) to
 # do the same thing.  This is a wrapper function, and not just a typeglob assignment,
 # because we want to get the right blessing.