1/*
2 * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 *
18 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20 */
21
22%module "Amanda::Device"
23%include "amglue/amglue.swg"
24%include "exception.i"
25%import "Amanda/Header.swg";
26
27%include "Amanda/Device.pod"
28
29%{
30#include "device.h"
31#include "property.h"
32#include "fileheader.h"
33#include "glib-util.h"
34#include "simpleprng.h"
35#include "amanda.h"
36#include "sockaddr-util.h"
37%}
38
39%init %{
40    /* Initialize the Device API on load */
41    device_api_init();
42%}
43
44%{
45
46/* Utility functions for typemaps, below */
47
48/* return a new, mortal SV corresponding to the given GValue
49 *
50 * @param value: the value to convert
51 * @returns: a new, mortal SV
52 */
53static SV *
54set_sv_from_gvalue(GValue *value)
55{
56    GType fundamental = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
57    SV *sv = NULL;
58
59    /* complex reference types */
60    switch (fundamental) {
61	case G_TYPE_LONG:
62	    return sv_2mortal(amglue_newSVi64(g_value_get_long(value)));
63
64	case G_TYPE_ULONG:
65	    return sv_2mortal(amglue_newSVu64(g_value_get_ulong(value)));
66
67	case G_TYPE_INT64:
68	    return sv_2mortal(amglue_newSVi64(g_value_get_int64(value)));
69
70	case G_TYPE_UINT64:
71	    return sv_2mortal(amglue_newSVu64(g_value_get_uint64(value)));
72    }
73
74    /* simple types that can be constructed with sv_set*v */
75    sv = sv_newmortal();
76    switch (fundamental) {
77	case G_TYPE_CHAR:
78	    sv_setiv(sv, g_value_get_char(value));
79	    break;
80
81	case G_TYPE_UCHAR:
82	    sv_setuv(sv, g_value_get_uchar(value));
83	    break;
84
85	case G_TYPE_BOOLEAN:
86	    sv_setiv(sv, g_value_get_boolean(value));
87	    break;
88
89	case G_TYPE_INT:
90	    sv_setiv(sv, g_value_get_int(value));
91	    break;
92
93	case G_TYPE_UINT:
94	    sv_setuv(sv, g_value_get_uint(value));
95	    break;
96
97	case G_TYPE_FLOAT:
98	    sv_setnv(sv, g_value_get_float(value));
99	    break;
100
101	case G_TYPE_DOUBLE:
102	    sv_setnv(sv, g_value_get_double(value));
103	    break;
104
105	case G_TYPE_STRING:
106	    sv_setpv(sv, g_value_get_string(value));
107	    break;
108
109	case G_TYPE_ENUM:
110	    sv_setiv(sv, g_value_get_enum(value));
111	    break;
112
113	case G_TYPE_FLAGS:
114	    sv_setiv(sv, g_value_get_flags(value));
115	    break;
116
117	/* Unsupported */
118	default:
119	case G_TYPE_POINTER:
120	case G_TYPE_INTERFACE:
121	case G_TYPE_OBJECT:
122	case G_TYPE_PARAM:
123	    warn("Unsupported fundamental property type #%d", (int)fundamental);
124	    sv_setsv(sv, &PL_sv_undef);
125	    break;
126    }
127
128    return sv;
129}
130
131/* Given an SV and an initialized GValue, set the GValue to the value
132 * represented by the SV.  The GValue's type must already be set.
133 *
134 * For basic corresponding types (string -> string, integer -> integer),
135 * the translation is straightforward.  However, if the GValue is not a
136 * string, but the SV has a string value, then g_value_set_from_string will
137 * be used to parse the string.
138 *
139 * @param sv: SV to convert
140 * @param value: (input/output) destination
141 * @returns: TRUE on success
142 */
143static gboolean
144set_gvalue_from_sv(SV *sv, GValue *value)
145{
146    GType fundamental = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
147
148    /* if we got a string, use g_value_set_from_string to parse any funny
149     * values or suffixes */
150    if (SvPOK(sv)) {
151	if (g_value_set_from_string(value, SvPV_nolen(sv)))
152	    return TRUE;
153    }
154
155    /* otherwise, handle numeric types with SvIV, SvNV, or the amglue_* functions */
156    switch (fundamental) {
157	case G_TYPE_BOOLEAN:
158	    g_value_set_boolean(value, SvIV(sv));
159	    return TRUE;
160
161	case G_TYPE_CHAR:
162	    g_value_set_char(value, amglue_SvI8(sv));
163	    return TRUE;
164
165	case G_TYPE_UCHAR:
166	    g_value_set_uchar(value, amglue_SvU8(sv));
167	    return TRUE;
168
169	case G_TYPE_INT:
170	    g_value_set_int(value, amglue_SvI32(sv));
171	    return TRUE;
172
173	case G_TYPE_UINT:
174	    g_value_set_uint(value, amglue_SvU32(sv));
175	    return TRUE;
176
177	case G_TYPE_LONG:
178	    g_value_set_int64(value, amglue_SvI64(sv));
179	    return TRUE;
180
181	case G_TYPE_ULONG:
182	    g_value_set_uint64(value, amglue_SvU64(sv));
183	    return TRUE;
184
185	case G_TYPE_INT64:
186	    g_value_set_int64(value, amglue_SvI64(sv));
187	    return TRUE;
188
189	case G_TYPE_UINT64:
190	    g_value_set_uint64(value, amglue_SvU64(sv));
191	    return TRUE;
192
193	case G_TYPE_FLOAT:
194	    g_value_set_float(value, SvNV(sv));
195	    return TRUE;
196
197	case G_TYPE_DOUBLE:
198	    g_value_set_double(value, SvNV(sv));
199	    return TRUE;
200
201	case G_TYPE_ENUM:
202	    g_value_set_enum(value, SvIV(sv));
203	    return TRUE;
204
205	case G_TYPE_FLAGS:
206	    g_value_set_flags(value, SvIV(sv));
207	    return TRUE;
208
209	default:
210	    /* for anything else, let perl stringify it for us and try parsing it */
211	    return g_value_set_from_string(value, SvPV_nolen(sv));
212    }
213}
214
215%}
216
217/*
218 * DirectTCPConnection object
219 */
220
221typedef struct {
222    %extend {
223	~DirectTCPConnection() {
224	    g_object_unref(self);
225	};
226
227	%newobject close;
228	char *close() {
229	    return directtcp_connection_close(self);
230	}
231    };
232} DirectTCPConnection;
233
234/*
235 * Device struct, %extend-ed into a Perl class
236 */
237
238%name(unaliased_name) extern char *device_unaliased_name(char *);
239
240typedef struct {
241
242    /* methods */
243    %extend {
244	/* constructor */
245	Device(char *device_name) {
246	    return device_open(device_name);
247	}
248
249	~Device() {
250	    g_object_unref(self);
251	}
252
253	gboolean
254	configure(gboolean use_global_config) {
255	    return device_configure(self, use_global_config);
256	}
257
258	char *
259	error() {
260	    return device_error(self);
261	}
262
263	char *
264	status_error() {
265	    return device_status_error(self);
266	}
267
268	char *
269	error_or_status() {
270	    return device_error_or_status(self);
271	}
272
273	DeviceStatusFlags
274	read_label() {
275	    return device_read_label(self);
276	}
277
278	gboolean
279	start(DeviceAccessMode mode, char *label, char *timestamp) {
280	    return device_start(self, mode, label, timestamp);
281	}
282
283	gboolean
284	finish() {
285	    return device_finish(self);
286	}
287
288	guint64
289	get_bytes_read() {
290	    return device_get_bytes_read(self);
291	}
292
293	guint64
294	get_bytes_written() {
295	    return device_get_bytes_written(self);
296	}
297
298	gboolean
299	start_file(dumpfile_t *jobInfo) {
300	    return device_start_file(self, jobInfo);
301	}
302
303	gboolean
304	write_block(guint size, gpointer data) {
305	    return device_write_block(self, size, data);
306	}
307
308	gboolean
309	finish_file() {
310	    return device_finish_file(self);
311	}
312
313	dumpfile_t*
314	seek_file(guint file) {
315	    return device_seek_file(self, file);
316	}
317
318	gboolean
319	seek_block(guint64 block) {
320	    return device_seek_block(self, block);
321	}
322
323	int
324	read_block(gpointer buffer, int *size) {
325	    return device_read_block(self, buffer, size);
326	}
327
328	gboolean
329	erase() {
330	    return device_erase(self);
331	}
332
333	gboolean
334	eject() {
335	    return device_eject(self);
336	}
337
338	gboolean
339	directtcp_supported() {
340	    return device_directtcp_supported(self);
341	}
342
343	void
344	listen(gboolean for_writing, DirectTCPAddr **addrs) {
345	    /* ensure that the addresses are empty if there was an error */
346	    if (!device_listen(self, for_writing, addrs))
347		*addrs = NULL;
348	}
349
350	gboolean
351	use_connection(DirectTCPConnection *conn) {
352	    return device_use_connection(self, conn);
353	}
354
355	gboolean
356	allow_take_scribe_from() {
357	    return device_allow_take_scribe_from(self);
358	}
359
360	%typemap(out) const GSList * {
361	    GSList *iter;
362
363	    /* Count the DeviceProperties */
364	    EXTEND(SP, g_slist_length($1)); /* make room for return values */
365
366	    /* Note that we set $result several times. the nature of
367	     * SWIG's wrapping is such that incrementing argvi points
368	     * $result to the next location in perl's argument stack.
369             */
370
371	    for (iter = $1; iter; iter = g_slist_next(iter)) {
372		DeviceProperty *prop = iter->data;
373		HV *hash = newHV();
374		SV *rv = newRV_noinc((SV *)hash);
375
376		hv_store(hash, "name", 4,
377			newSVpv(prop->base->name, 0), 0);
378		hv_store(hash, "description", 11,
379			newSVpv(prop->base->description, 0), 0);
380		hv_store(hash, "access", 6,
381			newSViv(prop->access), 0);
382		$result = sv_2mortal(rv);
383		argvi++;
384	    }
385	}
386	const GSList * property_list(void) {
387	    return device_property_get_list(self);
388	}
389
390	%typemap(out) const GSList *; /* remove typemap */
391
392	/* A typemap to convert a property name to a DevicePropertyBase. */
393	%typemap(in) DevicePropertyBase * {
394	    char *pname = NULL;
395
396	    if (SvPOK($input))
397		pname = SvPV_nolen($input);
398
399	    if (pname)
400		$1 = (DevicePropertyBase *)device_property_get_by_name(pname);
401	    else
402		$1 = NULL;
403	}
404
405	/* A typemap to convert the GValue in property_get to a return value.  The
406	 * (in) typemap sets up storage for the parameters, while the (argout) converts
407	 * them to a perl SV. */
408	%typemap(in,numinputs=0) (GValue *out_val, PropertySurety *surety,
409				  PropertySource *source, gboolean *val_found)
410			    (GValue val,
411			     PropertySurety surety,
412			     PropertySource source,
413			     gboolean found) {
414	    memset(&val, 0, sizeof(val));
415	    $1 = &val;
416	    if (GIMME_V == G_ARRAY) {
417		$2 = &surety;
418		$3 = &source;
419	    }
420	    $4 = &found;
421	}
422	%typemap(argout) (GValue *out_val, PropertySurety *surety,
423			  PropertySource *source, gboolean *val_found) {
424	    /* if the result is valid */
425	    if (*$4) {
426		/* move data from $1 to $result, somehow, being careful to
427		 * save the perl stack while doing so */
428		SP += argvi; PUTBACK;
429		$result = set_sv_from_gvalue($1);
430		SPAGAIN; SP -= argvi; argvi++;
431
432		/* free any memory for the GValue */
433		g_value_unset($1);
434
435		if (GIMME_V == G_ARRAY) {
436		    $result = newSViv(*$2);
437		    argvi++;
438		    $result = newSViv(*$3);
439		    argvi++;
440		}
441	    }
442	    /* otherwise, return nothing */
443	}
444
445	void
446	property_get(DevicePropertyBase *pbase, GValue *out_val, PropertySurety *surety,
447		     PropertySource *source, gboolean *val_found) {
448	    if (pbase) {
449		*val_found = device_property_get_ex(self, pbase->ID, out_val, surety, source);
450	    } else {
451		*val_found = FALSE;
452	    }
453	}
454
455	/* delete typemaps */
456	%typemap(in) (GValue *out_val, gboolean *val_found);
457	%typemap(argout) (GValue *out_val, gboolean *val_found);
458
459	/* We cheat a little bit here and just pass the native Perl type in to
460	 * the function.  This is the easiest way to make sure we know the property
461	 * information (in particular, its type) before trying to convert the SV.  */
462	%typemap(in) SV *sv "$1 = $input;"
463
464	gboolean
465	property_set(DevicePropertyBase *pbase, SV *sv) {
466	    GValue gval;
467
468	    if (!pbase)
469		goto fail;
470	    memset(&gval, 0, sizeof(gval));
471	    g_value_init(&gval, pbase->type);
472	    if (!set_gvalue_from_sv(sv, &gval))
473		goto failunset;
474
475	    if (!device_property_set(self, pbase->ID, &gval))
476		goto failunset;
477
478	    g_value_unset(&gval);
479	    return TRUE;
480	failunset:
481	    g_value_unset(&gval);
482	fail:
483	    return FALSE;
484	}
485
486	gboolean
487	property_set_ex(DevicePropertyBase *pbase, SV *sv,
488			PropertySurety surety, PropertySource source) {
489	    GValue gval;
490	    memset(&gval, 0, sizeof(gval));
491	    g_value_init(&gval, pbase->type);
492	    if (!set_gvalue_from_sv(sv, &gval))
493		goto fail;
494
495	    if (!device_property_set_ex(self, pbase->ID, &gval, surety, source))
496		goto fail;
497
498	    g_value_unset(&gval);
499	    return TRUE;
500	fail:
501	    g_value_unset(&gval);
502	    return FALSE;
503	}
504
505	gboolean recycle_file(guint filenum) {
506	    return device_recycle_file(self, filenum);
507	}
508
509	/* accessor functions */
510
511	int file(void) { return self->file; }
512	guint64 block(void) { return self->block; }
513	gboolean in_file(void) { return self->in_file; }
514	char * device_name(void) { return self->device_name; }
515	DeviceAccessMode access_mode(void) { return self->access_mode; }
516	gboolean is_eof(void) { return self->is_eof; }
517	gboolean is_eom(void) { return self->is_eom; }
518	char * volume_label(void) { return self->volume_label; }
519	char * volume_time(void) { return self->volume_time; }
520	DeviceStatusFlags status(void) { return self->status; }
521	gsize min_block_size(void) { return self->min_block_size; }
522	gsize max_block_size(void) { return self->max_block_size; }
523	gsize block_size(void) { return self->block_size; }
524	gsize header_block_size(void) { return self->header_block_size; }
525	dumpfile_t *volume_header(void) { return self->volume_header; }
526    };
527
528} Device;
529
530/* An alternate constructor for RAIT devices */
531%typemap(in) GSList *child_devices {
532    AV *av;
533    int i, len;
534
535    if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV) {
536	SWIG_exception(SWIG_TypeError, "Expected an arrayref");
537    }
538    av = (AV *)SvRV($input);
539
540    $1 = NULL;
541    len = av_len(av);
542    for (i = 0; i <= len; i++) {
543	SV **elt = av_fetch(av, i, 0);
544	Device *d;
545
546	if (elt && !SvOK(*elt)) {
547	    $1 = g_slist_append($1, NULL); /* 'undef' => NULL */
548	} else if (!elt || SWIG_ConvertPtr(*elt, (void **)&d, $descriptor(Device *), 0) == -1) {
549	    SWIG_exception(SWIG_TypeError, "array member is not a Device");
550	} else {
551	    $1 = g_slist_append($1, d);
552	}
553    }
554}
555%typemap(freearg) GSList *child_devices {
556    g_slist_free($1);
557}
558%newobject rait_device_open_from_children;
559Device *rait_device_open_from_children(GSList *child_devices);
560%perlcode %{
561sub new_rait_from_children {
562    my $class = shift; # strip the $class from the arguments
563    return rait_device_open_from_children([@_]);
564}
565%}
566
567/*
568 * Utilities for installchecks (not described in POD)
569 */
570
571%inline %{
572
573/* write LENGTH bytes of random data to FILENAME, seeded with SEED */
574gboolean
575write_random_to_device(guint32 seed, size_t length, Device *device) {
576    simpleprng_state_t prng;
577    char *buf;
578    gsize block_size = device->block_size;
579    g_assert(block_size < G_MAXUINT);
580
581    buf = g_malloc(block_size);
582    simpleprng_seed(&prng, seed);
583
584    while (length) {
585	size_t to_write = min(block_size, length);
586
587	simpleprng_fill_buffer(&prng, buf, to_write);
588	if (!device_write_block(device, (guint)block_size, buf)) {
589	    g_free(buf);
590	    return FALSE;
591	}
592	length -= to_write;
593    }
594
595    g_free(buf);
596    return TRUE;
597}
598
599/* read LENGTH bytes of random data from FILENAME verifying it against
600 * a PRNG seeded with SEED.  Sends any error messages to stderr.
601 */
602gboolean
603verify_random_from_device(guint32 seed, size_t length, Device *device) {
604    simpleprng_state_t prng;
605    char *buf = NULL; /* first device_read_block will get the size */
606    int block_size = 0;
607
608    simpleprng_seed(&prng, seed);
609
610    while (length) {
611	int bytes_read;
612	int size = block_size;
613
614	bytes_read = device_read_block(device, buf, &size);
615	if (bytes_read == 0 && size > block_size) {
616	    g_free(buf);
617	    block_size = size;
618	    buf = g_malloc(block_size);
619	    continue;
620	}
621	if (bytes_read == -1) {
622	    if (device->status == DEVICE_STATUS_SUCCESS) {
623		g_assert(device->is_eof);
624		g_debug("verify_random_from_device got unexpected EOF");
625	    }
626	    goto error;
627	}
628
629	/* strip padding */
630	bytes_read = min(bytes_read, length);
631
632	if (!simpleprng_verify_buffer(&prng, buf, bytes_read))
633	    goto error;
634
635	length -= bytes_read;
636    }
637
638    g_free(buf);
639    return TRUE;
640
641error:
642    g_free(buf);
643    return FALSE;
644}
645%}
646
647/*
648 * Constants
649 */
650
651amglue_add_flag_tag_fns(DeviceAccessMode);
652amglue_add_constant_short(ACCESS_NULL, "NULL", DeviceAccessMode);
653amglue_add_constant_short(ACCESS_READ, "READ", DeviceAccessMode);
654amglue_add_constant_short(ACCESS_WRITE, "WRITE", DeviceAccessMode);
655amglue_add_constant_short(ACCESS_APPEND, "APPEND", DeviceAccessMode);
656
657/* (this is really a macro, but SWIG will Do The Right Thing */
658gboolean IS_WRITABLE_ACCESS_MODE(DeviceAccessMode mode);
659amglue_export_tag(DeviceAccessMode, IS_WRITABLE_ACCESS_MODE);
660amglue_copy_to_tag(DeviceAccessMode, constants);
661
662amglue_add_flag_tag_fns(DeviceStatusFlags);
663amglue_add_constant_short(DEVICE_STATUS_SUCCESS, "SUCCESS", DeviceStatusFlags);
664amglue_add_constant_short(DEVICE_STATUS_DEVICE_ERROR, "DEVICE_ERROR", DeviceStatusFlags);
665amglue_add_constant_short(DEVICE_STATUS_DEVICE_BUSY, "DEVICE_BUSY", DeviceStatusFlags);
666amglue_add_constant_short(DEVICE_STATUS_VOLUME_MISSING, "VOLUME_MISSING", DeviceStatusFlags);
667amglue_add_constant_short(DEVICE_STATUS_VOLUME_UNLABELED, "VOLUME_UNLABELED", DeviceStatusFlags);
668amglue_add_constant_short(DEVICE_STATUS_VOLUME_ERROR, "VOLUME_ERROR", DeviceStatusFlags);
669amglue_add_constant_noshort(DEVICE_STATUS_FLAGS_MAX, DeviceStatusFlags);
670amglue_copy_to_tag(DeviceStatusFlags, constants);
671
672amglue_add_flag_tag_fns(PropertyPhaseFlags);
673amglue_add_constant_short(PROPERTY_PHASE_BEFORE_START, "BEFORE_START", PropertyPhaseFlags);
674amglue_add_constant_short(PROPERTY_PHASE_BETWEEN_FILE_WRITE, "BETWEEN_FILE_WRITE", PropertyPhaseFlags);
675amglue_add_constant_short(PROPERTY_PHASE_INSIDE_FILE_WRITE, "INSIDE_FILE_WRITE", PropertyPhaseFlags);
676amglue_add_constant_short(PROPERTY_PHASE_BETWEEN_FILE_READ, "BETWEEN_FILE_READ", PropertyPhaseFlags);
677amglue_add_constant_short(PROPERTY_PHASE_INSIDE_FILE_READ, "INSIDE_FILE_READ", PropertyPhaseFlags);
678amglue_add_constant_noshort(PROPERTY_PHASE_MAX, PropertyPhaseFlags);
679amglue_add_constant_noshort(PROPERTY_PHASE_MASK, PropertyPhaseFlags);
680amglue_add_constant_noshort(PROPERTY_PHASE_SHIFT, PropertyPhaseFlags);
681amglue_copy_to_tag(PropertyPhaseFlags, constants);
682
683amglue_add_flag_tag_fns(PropertyAccessFlags);
684amglue_add_constant_short(PROPERTY_ACCESS_GET_BEFORE_START,
685		    "GET_BEFORE_START", PropertyAccessFlags);
686amglue_add_constant_short(PROPERTY_ACCESS_GET_BETWEEN_FILE_WRITE,
687		    "GET_BETWEEN_FILE_WRITE", PropertyAccessFlags);
688amglue_add_constant_short(PROPERTY_ACCESS_GET_INSIDE_FILE_WRITE,
689		    "GET_INSIDE_FILE_WRITE", PropertyAccessFlags);
690amglue_add_constant_short(PROPERTY_ACCESS_GET_BETWEEN_FILE_READ,
691		    "GET_BETWEEN_FILE_READ", PropertyAccessFlags);
692amglue_add_constant_short(PROPERTY_ACCESS_GET_INSIDE_FILE_READ,
693		    "GET_INSIDE_FILE_READ", PropertyAccessFlags);
694amglue_add_constant_short(PROPERTY_ACCESS_SET_BEFORE_START,
695		    "SET_BEFORE_START", PropertyAccessFlags);
696amglue_add_constant_short(PROPERTY_ACCESS_SET_BETWEEN_FILE_WRITE,
697		    "SET_BETWEEN_FILE_WRITE", PropertyAccessFlags);
698amglue_add_constant_short(PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE,
699		    "SET_INSIDE_FILE_WRITE", PropertyAccessFlags);
700amglue_add_constant_short(PROPERTY_ACCESS_SET_BETWEEN_FILE_READ,
701		    "SET_BETWEEN_FILE_READ", PropertyAccessFlags);
702amglue_add_constant_short(PROPERTY_ACCESS_SET_INSIDE_FILE_READ,
703		    "SET_INSIDE_FILE_READ", PropertyAccessFlags);
704amglue_add_constant_noshort(PROPERTY_ACCESS_GET_MASK, PropertyAccessFlags);
705amglue_add_constant_noshort(PROPERTY_ACCESS_SET_MASK, PropertyAccessFlags);
706amglue_copy_to_tag(PropertyAccessFlags, constants);
707
708amglue_add_enum_tag_fns(ConcurrencyParadigm);
709amglue_add_constant_short(CONCURRENCY_PARADIGM_EXCLUSIVE, "EXCLUSIVE", ConcurrencyParadigm);
710amglue_add_constant_short(CONCURRENCY_PARADIGM_SHARED_READ, "SHARED_READ", ConcurrencyParadigm);
711amglue_add_constant_short(CONCURRENCY_PARADIGM_RANDOM_ACCESS, "RANDOM_ACCESS", ConcurrencyParadigm);
712amglue_copy_to_tag(ConcurrencyParadigm, constants);
713
714amglue_add_enum_tag_fns(StreamingRequirement);
715amglue_add_constant_short(STREAMING_REQUIREMENT_NONE, "NONE", StreamingRequirement);
716amglue_add_constant_short(STREAMING_REQUIREMENT_DESIRED, "DESIRED", StreamingRequirement);
717amglue_add_constant_short(STREAMING_REQUIREMENT_REQUIRED, "REQUIRED", StreamingRequirement);
718amglue_copy_to_tag(StreamingRequirement, constants);
719
720amglue_add_enum_tag_fns(MediaAccessMode);
721amglue_add_constant_short(MEDIA_ACCESS_MODE_READ_ONLY, "READ_ONLY", MediaAccessMode);
722amglue_add_constant_short(MEDIA_ACCESS_MODE_WORM, "WORM", MediaAccessMode);
723amglue_add_constant_short(MEDIA_ACCESS_MODE_READ_WRITE, "READ_WRITE", MediaAccessMode);
724amglue_add_constant_short(MEDIA_ACCESS_MODE_WRITE_ONLY, "WRITE_ONLY", MediaAccessMode);
725amglue_copy_to_tag(MediaAccessMode, constants);
726
727amglue_add_flag_tag_fns(PropertySurety);
728amglue_add_constant_short(PROPERTY_SURETY_BAD, "SURETY_BAD", PropertySurety);
729amglue_add_constant_short(PROPERTY_SURETY_GOOD, "SURETY_GOOD", PropertySurety);
730amglue_copy_to_tag(PropertySurety, constants);
731
732amglue_add_flag_tag_fns(PropertySource);
733amglue_add_constant_short(PROPERTY_SOURCE_DEFAULT, "SOURCE_DEFAULT", PropertySource);
734amglue_add_constant_short(PROPERTY_SOURCE_DETECTED, "SOURCE_DETECTED", PropertySource);
735amglue_add_constant_short(PROPERTY_SOURCE_USER, "SOURCE_USER", PropertySource);
736amglue_copy_to_tag(PropertySource, constants);
737
738%perlcode %{
739
740# SWIG produces a sub-package for the Device "class", in this case named
741# Amanda::Device::Device.  For user convenience, we allow Amanda::Device->new(..) to
742# do the same thing.  This is a wrapper function, and not just a typeglob assignment,
743# because we want to get the right blessing.
744sub new {
745    my $pkg = shift;
746    Amanda::Device::Device->new(@_);
747}
748%}
749