/*
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, 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.
*
* 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 General Public License
* for more details.
*
* 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: Carbonite Inc., 756 N Pastoria Ave
* Sunnyvale, CA 94085, 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"
#include "glib-util.h"
#include "simpleprng.h"
#include "amanda.h"
#include "sockaddr-util.h"
%}
%init %{
/* Initialize the Device API on load */
device_api_init();
%}
%{
/* 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)
{
GType fundamental = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
SV *sv = NULL;
/* complex reference types */
switch (fundamental) {
case G_TYPE_LONG:
return sv_2mortal(amglue_newSVi64(g_value_get_long(value)));
case G_TYPE_ULONG:
return sv_2mortal(amglue_newSVu64(g_value_get_ulong(value)));
case G_TYPE_INT64:
return sv_2mortal(amglue_newSVi64(g_value_get_int64(value)));
case G_TYPE_UINT64:
return sv_2mortal(amglue_newSVu64(g_value_get_uint64(value)));
}
/* simple types that can be constructed with sv_set*v */
sv = sv_newmortal();
switch (fundamental) {
case G_TYPE_CHAR:
sv_setiv(sv, g_value_get_char(value));
break;
case G_TYPE_UCHAR:
sv_setuv(sv, g_value_get_uchar(value));
break;
case G_TYPE_BOOLEAN:
sv_setiv(sv, g_value_get_boolean(value));
break;
case G_TYPE_INT:
sv_setiv(sv, g_value_get_int(value));
break;
case G_TYPE_UINT:
sv_setuv(sv, g_value_get_uint(value));
break;
case G_TYPE_FLOAT:
sv_setnv(sv, g_value_get_float(value));
break;
case G_TYPE_DOUBLE:
sv_setnv(sv, g_value_get_double(value));
break;
case G_TYPE_STRING:
sv_setpv(sv, g_value_get_string(value));
break;
case G_TYPE_ENUM:
sv_setiv(sv, g_value_get_enum(value));
break;
case G_TYPE_FLAGS:
sv_setiv(sv, g_value_get_flags(value));
break;
/* Unsupported */
default:
case G_TYPE_POINTER:
case G_TYPE_INTERFACE:
case G_TYPE_OBJECT:
case G_TYPE_PARAM:
warn("Unsupported fundamental property type #%d", (int)fundamental);
sv_setsv(sv, &PL_sv_undef);
break;
}
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));
gchar *err = NULL;
/* 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:
g_value_set_boolean(value, SvIV(sv));
return TRUE;
case G_TYPE_CHAR:
{ gint8 val = amglue_SvI8(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_char(value, val);
return TRUE;
}
case G_TYPE_UCHAR:
{ guint8 val = amglue_SvU8(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_uchar(value, val);
return TRUE;
}
case G_TYPE_INT:
{ gint32 val = amglue_SvI32(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_int(value, val);
return TRUE;
}
case G_TYPE_UINT:
{ guint32 val = amglue_SvU32(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_uint(value, val);
return TRUE;
}
case G_TYPE_LONG:
{ gint64 val = amglue_SvI64(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_int64(value, val);
return TRUE;
}
case G_TYPE_ULONG:
{ guint64 val = amglue_SvU64(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_uint64(value, val);
return TRUE;
}
case G_TYPE_INT64:
{ gint64 val = amglue_SvI64(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_int64(value, val);
return TRUE;
}
case G_TYPE_UINT64:
{ guint64 val = amglue_SvU64(sv, &err);
if (err) { g_free(err); return FALSE; };
g_value_set_uint64(value, val);
return TRUE;
}
case G_TYPE_FLOAT:
g_value_set_float(value, SvNV(sv));
return TRUE;
case G_TYPE_DOUBLE:
g_value_set_double(value, SvNV(sv));
return TRUE;
case G_TYPE_ENUM:
g_value_set_enum(value, SvIV(sv));
return TRUE;
case G_TYPE_FLAGS:
g_value_set_flags(value, SvIV(sv));
return TRUE;
default:
/* for anything else, let perl stringify it for us and try parsing it */
return g_value_set_from_string(value, SvPV_nolen(sv));
}
}
%}
/*
* DirectTCPConnection object
*/
typedef struct {
%extend {
~DirectTCPConnection() {
g_object_unref(self);
};
%newobject close;
char *close() {
return directtcp_connection_close(self);
}
};
} DirectTCPConnection;
/*
* Device struct, %extend-ed into a Perl class
*/
%rename(unaliased_name) device_unaliased_name;
char *device_unaliased_name(char *device_name);
typedef struct {
/* methods */
%extend {
/* constructor */
Device(char *device_name) {
return device_open(device_name);
}
~Device() {
g_object_unref(self);
}
gboolean
configure(gboolean use_global_config) {
return device_configure(self, use_global_config);
}
void
reset() {
return device_reset(self);
}
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);
}
gboolean
start(DeviceAccessMode mode, char *label, char *timestamp) {
return device_start(self, mode, label, timestamp);
}
gboolean
finish() {
return device_finish(self);
}
void
clear_bytes_read() {
return device_clear_bytes_read(self);
}
guint64
get_bytes_read() {
return device_get_bytes_read(self);
}
guint64
get_bytes_written() {
return device_get_bytes_written(self);
}
gboolean
start_file(dumpfile_t *jobInfo) {
return device_start_file(self, jobInfo);
}
DeviceWriteResult
write_block(guint size, gpointer data) {
return device_write_block(self, size, data);
}
gboolean
finish_file() {
return device_finish_file(self);
}
gboolean
init_seek_file(guint file) {
return device_init_seek_file(self, file);
}
dumpfile_t*
seek_file(guint file) {
return device_seek_file(self, file);
}
gboolean
seek_block(guint64 block) {
return device_seek_block(self, block);
}
int
read_block(gpointer buffer, int *size, int max_block) {
return device_read_block(self, buffer, size, max_block);
}
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;
}
gboolean
use_connection(DirectTCPConnection *conn) {
return device_use_connection(self, conn);
}
gboolean
check_writable() {
return device_check_writable(self);
}
gboolean
have_set_reuse() {
return device_have_set_reuse(self);
}
gboolean
set_reuse() {
return device_set_reuse(self);
}
gboolean
set_no_reuse(char *label, char *datestamp) {
return device_set_no_reuse(self, label, datestamp);
}
%typemap(in) (char **slot_names) {
AV *av;
unsigned int len;
unsigned int i;
/* check that it's an arrayref */
if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV) {
SWIG_exception(SWIG_TypeError, "Expected a non-empty arrayref");
}
av = (AV *)SvRV($input);
/* allocate memory for $1 */
len = av_len(av)+1; /* av_len(av) is like $#av */
if (!len) {
SWIG_exception(SWIG_TypeError, "Expected a non-empty arrayref");
}
$1 = g_new0(gchar *, len+1);
for (i = 0; i < len; i++) {
SV **sv = av_fetch(av, i, 0);
g_assert(sv != NULL);
$1[i] = (char*)(SvPV_nolen(*sv));
}
/* final element is already NULL due to g_new0 */
}
%typemap(freearg) (char **slot_names) {
g_free($1);
}
gboolean
sync_catalog(int request, int wait, char **slot_names) {
return device_sync_catalog(self, request, wait, slot_names);
}
gboolean
create() {
return device_create(self);
}
gboolean
allow_take_scribe_from() {
return device_allow_take_scribe_from(self);
}
%typemap(out) const GSList * {
GSList *iter;
/* Count the DeviceProperties */
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 (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 GSList * property_list(void) {
return device_property_get_list(self);
}
%typemap(out) const GSList *; /* remove typemap */
/* A typemap to convert a property name to a DevicePropertyBase. */
%typemap(in) DevicePropertyBase * {
char *pname = NULL;
if (SvPOK($input))
pname = SvPV_nolen($input);
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, PropertySurety *surety,
PropertySource *source, gboolean *val_found)
(GValue val,
PropertySurety surety,
PropertySource source,
gboolean found) {
memset(&val, 0, sizeof(val));
$1 = &val;
if (GIMME_V == G_ARRAY) {
$2 = &surety;
$3 = &source;
}
$4 = &found;
}
%typemap(argout) (GValue *out_val, PropertySurety *surety,
PropertySource *source, gboolean *val_found) {
/* if the result is valid */
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);
if (GIMME_V == G_ARRAY) {
$result = newSViv(*$2);
argvi++;
$result = newSViv(*$3);
argvi++;
}
}
/* otherwise, return nothing */
}
void
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 */
%typemap(in) (GValue *out_val, gboolean *val_found);
%typemap(argout) (GValue *out_val, gboolean *val_found);
/* We cheat a little bit here and just pass the native Perl type in to
* the function. This is the easiest way to make sure we know the property
* information (in particular, its type) before trying to convert the SV. */
%typemap(in) SV *sv "$1 = $input;"
%newobject property_set;
char *
property_set(DevicePropertyBase *pbase, SV *sv) {
GValue gval;
char *r;
if (!pbase) {
return g_strdup("No such device-property");
}
memset(&gval, 0, sizeof(gval));
g_value_init(&gval, pbase->type);
if (!set_gvalue_from_sv(sv, &gval)) {
return g_strdup("The value is no allowed");
}
r = device_property_set(self, pbase->ID, &gval);
g_value_unset(&gval);
return r;
}
%newobject property_set_ex;
char *
property_set_ex(DevicePropertyBase *pbase, SV *sv,
PropertySurety surety, PropertySource source) {
GValue gval;
char *r;
if (!pbase) {
return g_strdup("No such device-property");
}
memset(&gval, 0, sizeof(gval));
g_value_init(&gval, pbase->type);
if (!set_gvalue_from_sv(sv, &gval)) {
return g_strdup("The value is no allowed");
}
r = device_property_set_ex(self, pbase->ID, &gval, surety, source);
g_value_unset(&gval);
return r;
}
gboolean recycle_file(guint filenum) {
return device_recycle_file(self, filenum);
}
/* 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) != WRITE_SUCCEED) {
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, -1);
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
*/
amglue_add_flag_tag_fns(DeviceAccessMode);
amglue_add_constant_short(ACCESS_NULL, "NULL", DeviceAccessMode);
amglue_add_constant_short(ACCESS_READ, "READ", DeviceAccessMode);
amglue_add_constant_short(ACCESS_WRITE, "WRITE", DeviceAccessMode);
amglue_add_constant_short(ACCESS_APPEND, "APPEND", DeviceAccessMode);
/* (this is really a macro, but SWIG will Do The Right Thing */
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(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);
amglue_add_constant_short(PROPERTY_PHASE_BETWEEN_FILE_WRITE, "BETWEEN_FILE_WRITE", PropertyPhaseFlags);
amglue_add_constant_short(PROPERTY_PHASE_INSIDE_FILE_WRITE, "INSIDE_FILE_WRITE", PropertyPhaseFlags);
amglue_add_constant_short(PROPERTY_PHASE_BETWEEN_FILE_READ, "BETWEEN_FILE_READ", PropertyPhaseFlags);
amglue_add_constant_short(PROPERTY_PHASE_INSIDE_FILE_READ, "INSIDE_FILE_READ", PropertyPhaseFlags);
amglue_add_constant_noshort(PROPERTY_PHASE_MAX, PropertyPhaseFlags);
amglue_add_constant_noshort(PROPERTY_PHASE_MASK, PropertyPhaseFlags);
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,
"GET_BEFORE_START", PropertyAccessFlags);
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,
"GET_INSIDE_FILE_WRITE", PropertyAccessFlags);
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,
"GET_INSIDE_FILE_READ", PropertyAccessFlags);
amglue_add_constant_short(PROPERTY_ACCESS_SET_BEFORE_START,
"SET_BEFORE_START", PropertyAccessFlags);
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,
"SET_INSIDE_FILE_WRITE", PropertyAccessFlags);
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,
"SET_INSIDE_FILE_READ", PropertyAccessFlags);
amglue_add_constant_noshort(PROPERTY_ACCESS_GET_MASK, PropertyAccessFlags);
amglue_add_constant_noshort(PROPERTY_ACCESS_SET_MASK, PropertyAccessFlags);
amglue_copy_to_tag(PropertyAccessFlags, constants);
amglue_add_enum_tag_fns(ConcurrencyParadigm);
amglue_add_constant_short(CONCURRENCY_PARADIGM_EXCLUSIVE, "EXCLUSIVE", ConcurrencyParadigm);
amglue_add_constant_short(CONCURRENCY_PARADIGM_SHARED_READ, "SHARED_READ", ConcurrencyParadigm);
amglue_add_constant_short(CONCURRENCY_PARADIGM_RANDOM_ACCESS, "RANDOM_ACCESS", ConcurrencyParadigm);
amglue_copy_to_tag(ConcurrencyParadigm, constants);
amglue_add_enum_tag_fns(StreamingRequirement);
amglue_add_constant_short(STREAMING_REQUIREMENT_NONE, "NONE", StreamingRequirement);
amglue_add_constant_short(STREAMING_REQUIREMENT_DESIRED, "DESIRED", StreamingRequirement);
amglue_add_constant_short(STREAMING_REQUIREMENT_REQUIRED, "REQUIRED", StreamingRequirement);
amglue_copy_to_tag(StreamingRequirement, constants);
amglue_add_enum_tag_fns(MediaAccessMode);
amglue_add_constant_short(MEDIA_ACCESS_MODE_READ_ONLY, "READ_ONLY", MediaAccessMode);
amglue_add_constant_short(MEDIA_ACCESS_MODE_WORM, "WORM", MediaAccessMode);
amglue_add_constant_short(MEDIA_ACCESS_MODE_READ_WRITE, "READ_WRITE", MediaAccessMode);
amglue_add_constant_short(MEDIA_ACCESS_MODE_WRITE_ONLY, "WRITE_ONLY", MediaAccessMode);
amglue_copy_to_tag(MediaAccessMode, 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
# 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.
sub new {
my $pkg = shift;
Amanda::Device::Device->new(@_);
}
%}
%perlcode %{
sub to_message {
my $dev = shift;
return Amanda::Device::Message->new(
source_filename => __FILE__,
source_line => __LINE__,
code => 1700000,
severity => $dev->status == $DEVICE_SUCCESS ? $Amanda::Message::INFO : $Amanda::Message::ERROR,
device_name => $dev->device_name,
dev_status => $dev->status,
dev_error => $dev->error_or_status);
}
%}
%perlcode %{
package Amanda::Device::Message;
use strict;
use warnings;
use Amanda::Message;
use vars qw( @ISA );
@ISA = qw( Amanda::Message );
sub local_message {
my $self = shift;
if ($self->{'code'} == 1700000) {
return "device '%self->{'device_name'}: status: $self->{'dev_error'}.";
} elsif ($self->{'code'} == 1700001) {
return "property '$self->{'property_name'}' is '$self->{'property_value'}'.";
}
}
%}