2 * Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
21 %module "Amanda::Xfer"
22 %include "amglue/amglue.swg"
23 %include "exception.i"
25 %import "Amanda/MainLoop.swg"
30 #include "glib-util.h"
33 #include "sockaddr-util.h"
36 /* The SWIGging of the transfer architecture.
38 * The C layer of the transfer architecture exposes some structs, which are
39 * arranged through GObject magic into a class hierarchy. It also exposes
40 * regular C functions which are intended to act as methods on these structs.
41 * Furthermore, it exposes Perl callbacks (via Amanda::MainLoop) with
42 * parameters involving objects of these classes.
44 * SWIG doesn't support callbacks very well, and makes it particularly
45 * difficult to represent a GObject class hierarchy. Rather than try to "make
46 * it fit" into SWIG, this module uses custom typemaps and perl/C conversions
47 * to get all of this stuff right in the first place.
49 * For Xfer objects, we define two functions, new_sv_for_xfer and xfer_from_sv,
50 * which create a new SV for an Xfer object, and subsequently extract a pointer
51 * to the object from the SV. The SV is both blessed and tied to the
52 * Amanda::Xfer::Xfer class, in which all of the method calls are defined, and
53 * which defines a DESTROY method that calls xfer_unref.
55 * XferElements are similar, but we have the added challenge of representing
56 * subclasses with appropriate perl subclasses. The solution is to tag each C
57 * class with a perl class name, and use that name when blessing a new SV.
59 * Finally, XMsgs are reflected entirely into perl hashrefs, in the interest of
68 /* We need GType and GThread initialized to use xfers */
76 amglue_add_enum_tag_fns(xfer_status);
77 amglue_add_constant(XFER_INIT, xfer_status);
78 amglue_add_constant(XFER_START, xfer_status);
79 amglue_add_constant(XFER_RUNNING, xfer_status);
80 amglue_add_constant(XFER_DONE, xfer_status);
81 amglue_copy_to_tag(xfer_status, constants);
83 amglue_add_enum_tag_fns(xmsg_type);
84 amglue_add_constant(XMSG_INFO, xmsg_type);
85 amglue_add_constant(XMSG_ERROR, xmsg_type);
86 amglue_add_constant(XMSG_DONE, xmsg_type);
87 amglue_add_constant(XMSG_CANCEL, xmsg_type);
88 amglue_add_constant(XMSG_PART_DONE, xmsg_type);
89 amglue_add_constant(XMSG_READY, xmsg_type);
90 amglue_copy_to_tag(xmsg_type, constants);
97 /* Given an XMsg, return a hashref representing the message as a pure-perl
98 * object. The object is new, has refcount 1, and is totally independent of
99 * the underlying XMsg.
101 * Reflecting the XMsg directly into Perl avoids the need to reference-count
102 * the XMsg objects themselves, which can simply be freed after a callback
103 * completes. The overhead of creating a hash is likely equivalent to or
104 * less than the overhead that would be consumed with SWIG's swig_$field_get
105 * accessors, assuming that perl code examines most of the fields in a message.
107 * @param msg: the message to represent
108 * @returns: a perl SV
114 static HV *amanda_xfer_msg_stash = NULL;
116 SV *rv = newRV_noinc((SV *)hash);
118 /* bless the rv as an Amanda::Xfer::Msg object */
119 if (!amanda_xfer_msg_stash) {
120 amanda_xfer_msg_stash = gv_stashpv("Amanda::Xfer::Msg", GV_ADD);
122 sv_bless(rv, amanda_xfer_msg_stash);
124 /* TODO: consider optimizing by precomputing the hash values of
128 hv_store(hash, "elt", 3, new_sv_for_xfer_element(msg->elt), 0);
131 hv_store(hash, "type", 4, newSViv(msg->type), 0);
134 hv_store(hash, "version", 7, newSViv(msg->version), 0);
138 hv_store(hash, "message", 7, newSVpv(msg->message, 0), 0);
141 hv_store(hash, "successful", 10, newSViv(msg->successful), 0);
144 hv_store(hash, "eom", 3, newSViv(msg->eom), 0);
147 hv_store(hash, "eof", 3, newSViv(msg->eof), 0);
150 hv_store(hash, "size", 4, amglue_newSVu64(msg->size), 0);
153 hv_store(hash, "duration", 8, newSVnv(msg->duration), 0);
156 hv_store(hash, "partnum", 7, amglue_newSVu64(msg->partnum), 0);
159 hv_store(hash, "fileno", 6, amglue_newSVu64(msg->fileno), 0);
165 %typemap(in) Xfer * {
166 $1 = xfer_from_sv($input);
169 %typemap(in) XferElement * {
170 $1 = xfer_element_from_sv($input);
173 %typemap(out) Xfer * {
174 $result = sv_2mortal(new_sv_for_xfer($1));
178 %typemap(out) XferElement * {
179 $result = sv_2mortal(new_sv_for_xfer_element($1));
183 %typemap(newfree) Xfer * {
187 %typemap(newfree) XferElement * {
188 xfer_element_unref($1);
195 /* A typemap for the input to the Xfer constructor, a.k.a. xfer_new */
196 %typemap(in,numinputs=1) (XferElement **elementlist, unsigned int nelements) {
200 /* check that it's an arrayref */
201 if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV) {
202 SWIG_exception(SWIG_TypeError, "Expected an arrayref");
204 av = (AV *)SvRV($input);
206 /* allocate memory for $1 */
207 $2 = av_len(av)+1; /* av_len(av) is like $#av */
208 $1 = g_new(XferElement *, $2);
210 /* extract the underlying XferElement objects and add pointers to
211 * them, "borrowing" the caller's references for the moment. */
212 for (i = 0; i < $2; i++) {
213 SV **sv = av_fetch(av, i, 0);
214 XferElement *elt = sv? xfer_element_from_sv(*sv):NULL;
217 SWIG_exception(SWIG_TypeError, "Expected an arrayref of Amanda::Xfer::Element objects");
223 %typemap(freearg) (XferElement **elementlist, unsigned int nelements) {
224 /* free the element vector allocated in the (in) typemap */
229 Xfer *xfer_new(XferElement **elementlist, unsigned int nelements);
230 void xfer_unref(Xfer *);
231 xfer_status xfer_get_status(Xfer *xfer);
232 char *xfer_repr(Xfer *xfer);
233 void xfer_start(Xfer *xfer, gint64 offset, gint64 size);
234 void xfer_cancel(Xfer *xfer);
235 /* xfer_get_source is implemented below */
238 /* SWIG wants to treat this as a function */
239 #define xfer_get_status(xfer) ((xfer)->status)
242 /* upgrade the start method to optionally take a callback, which is
243 * passed to the GSource's set_callback */
245 sub xfer_start_with_callback {
246 my ($xfer, $cb, $offset, $size) = @_;
248 my $releasing_cb = sub {
249 my ($src, $msg, $xfer) = @_;
250 my $done = $msg->{'type'} == $XMSG_DONE;
251 $src->remove() if $done;
253 $cb = undef if $done; # break potential reference loop
255 $xfer->get_source()->set_callback($releasing_cb);
257 $offset = 0 if !defined $offset;
258 $size = 0 if !defined $size;
259 xfer_start($xfer, $offset, $size);
263 /* Change the callback */
265 sub xfer_set_callback {
266 my ($xfer, $cb) = @_;
268 my $releasing_cb = sub {
269 my ($src, $msg, $xfer) = @_;
270 my $done = $msg->{'type'} == $XMSG_DONE;
271 $src->remove() if $done;
273 $cb = undef if $done; # break potential reference loop
275 $xfer->get_source()->set_callback($releasing_cb);
277 $xfer->get_source()->set_callback(undef);
283 * XferElement functions
285 * Some of these methods are not intended to be used from Perl; they are annotated
289 void xfer_element_unref(XferElement *elt); /* (wrap the macro, above) */
290 /* xfer_element_link_to -- private */
291 char *xfer_element_repr(XferElement *elt);
292 /* xfer_element_set_size -- private */
293 /* xfer_element_start -- private */
294 /* xfer_element_cancel -- private */
297 static gboolean same_elements(
305 /* subclass constructors */
307 /* N.B. When adding new classes, ensure that the class_init function
308 * sets perl_class to the appropriate value. */
310 %newobject xfer_source_random;
311 XferElement *xfer_source_random(
315 guint32 xfer_source_random_get_seed(
318 %typemap(in) (void * pattern, size_t pattern_length) {
322 pat = SvPV($input, len);
323 $1 = g_memdup(pat, len);
327 %typemap(in) (gchar **argv) {
332 /* check that it's an arrayref */
333 if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV) {
334 SWIG_exception(SWIG_TypeError, "Expected a non-empty arrayref");
336 av = (AV *)SvRV($input);
338 /* allocate memory for $1 */
339 len = av_len(av)+1; /* av_len(av) is like $#av */
341 SWIG_exception(SWIG_TypeError, "Expected a non-empty arrayref");
343 $1 = g_new0(gchar *, len+1);
345 for (i = 0; i < len; i++) {
346 SV **sv = av_fetch(av, i, 0);
347 g_assert(sv != NULL);
348 $1[i] = g_strdup(SvPV_nolen(*sv));
351 /* final element is already NULL due to g_new0; xfer_filter_process takes
352 * care of freeing this array, so we don't have to */
355 %newobject xfer_source_pattern;
356 XferElement *xfer_source_pattern(
359 size_t pattern_length);
361 %newobject xfer_source_fd;
362 XferElement *xfer_source_fd(
365 %newobject xfer_source_directtcp_listen;
366 XferElement *xfer_source_directtcp_listen(void);
369 static DirectTCPAddr *
370 xfer_source_directtcp_listen_get_addrs(XferElement *elt) {
371 return elt->input_listen_addrs;
375 %newobject xfer_source_directtcp_connect;
376 XferElement *xfer_source_directtcp_connect(DirectTCPAddr *addrs);
378 %newobject xfer_filter_xor;
379 XferElement *xfer_filter_xor(
380 unsigned char xor_key);
382 %newobject xfer_filter_process;
383 XferElement *xfer_filter_process(
389 %newobject xfer_dest_null;
390 XferElement *xfer_dest_null(
393 %newobject xfer_dest_buffer;
394 XferElement *xfer_dest_buffer(
397 %cstring_output_allocate_size(gpointer *buf, gsize *size, );
398 void xfer_dest_buffer_get(
403 %newobject xfer_dest_fd;
404 XferElement *xfer_dest_fd(
407 %newobject xfer_dest_directtcp_listen;
408 XferElement *xfer_dest_directtcp_listen(void);
411 static DirectTCPAddr *
412 xfer_dest_directtcp_listen_get_addrs(XferElement *elt) {
413 return elt->output_listen_addrs;
417 %newobject xfer_dest_directtcp_connect;
418 XferElement *xfer_dest_directtcp_connect(DirectTCPAddr *addrs);
424 %types(amglue_Source *);
427 xmsgsource_perl_callback(
433 amglue_Source *src = (amglue_Source *)data;
438 /* keep the source around long enough for the call to finish */
439 amglue_source_ref(src);
440 g_assert(src->callback_sv != NULL);
445 /* create a new SV pointing to 'src', and increase its refcount
447 amglue_source_ref(src);
448 src_sv = SWIG_NewPointerObj(src, SWIGTYPE_p_amglue_Source,
449 SWIG_OWNER | SWIG_SHADOW);
450 SvREFCNT_inc(src_sv);
452 msg_sv = new_sv_for_xmsg(msg);
453 xfer_sv = new_sv_for_xfer(xfer);
456 XPUSHs(sv_2mortal(src_sv));
457 XPUSHs(sv_2mortal(msg_sv));
458 XPUSHs(sv_2mortal(xfer_sv));
461 call_sv(src->callback_sv, G_EVAL|G_DISCARD);
466 /* we no longer need the src */
467 amglue_source_unref(src);
470 /* these may be gone, so NULL them out */
475 /* check for an uncaught 'die'. If we don't do this, then Perl will longjmp()
476 * over the GMainLoop mechanics, leaving GMainLoop in an inconsistent (locked)
479 /* We handle this just the way the default 'die' handler in Amanda::Debug
480 * does, but since Amanda's debug support may not yet be running, we back
481 * it up with an exit() */
482 g_critical("%s", SvPV_nolen(ERRSV));
490 %newobject xfer_get_amglue_source;
493 xfer_get_amglue_source(
496 return amglue_source_get(xfer_get_source(xfer),
497 (GSourceFunc)xmsgsource_perl_callback);
502 * XMsg and XMsgSource handling
509 /* First, a few macros to generate decent Perl */
517 %define XFER_ELEMENT_SUBCLASS_OF(PARENT)
524 %define XFER_ELEMENT_SUBCLASS()
525 XFER_ELEMENT_SUBCLASS_OF(Amanda::Xfer::Element)
528 %define DECLARE_CONSTRUCTOR(C_CONSTRUCTOR)
532 # The C function adds the proper blessing -- this function
533 # just gets $pkg out of the way.
539 %define OVERLOAD_REPR()
541 use overload '""' => sub { $_[0]->repr(); };
542 # overload comparison, so users can ask if one obj == another
543 use overload '==' => sub { Amanda::Xfer::same_elements($_[0], $_[1]); };
544 use overload '!=' => sub { not Amanda::Xfer::same_elements($_[0], $_[1]); };
548 %define DECLARE_METHOD(METHOD_NAME, C_FUNCTION)
549 %perlcode {*METHOD_NAME = *C_FUNCTION;
553 /* And now define the required perl classes */
555 PACKAGE(Amanda::Xfer::Xfer)
556 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_new);
557 DECLARE_METHOD(DESTROY, Amanda::Xfer::xfer_unref);
559 DECLARE_METHOD(repr, Amanda::Xfer::xfer_repr);
560 DECLARE_METHOD(get_status, Amanda::Xfer::xfer_get_status);
561 DECLARE_METHOD(get_source, Amanda::Xfer::xfer_get_amglue_source);
562 DECLARE_METHOD(start, Amanda::Xfer::xfer_start_with_callback);
563 DECLARE_METHOD(set_callback, Amanda::Xfer::xfer_set_callback);
564 DECLARE_METHOD(cancel, Amanda::Xfer::xfer_cancel);
568 PACKAGE(Amanda::Xfer::Element)
569 DECLARE_METHOD(DESTROY, Amanda::Xfer::xfer_element_unref);
571 DECLARE_METHOD(repr, Amanda::Xfer::xfer_element_repr);
575 PACKAGE(Amanda::Xfer::Element::Glue)
576 XFER_ELEMENT_SUBCLASS()
577 /* no constructor -- internal use only */
581 PACKAGE(Amanda::Xfer::Source::Fd)
582 XFER_ELEMENT_SUBCLASS()
583 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_source_fd)
587 PACKAGE(Amanda::Xfer::Source::Random)
588 XFER_ELEMENT_SUBCLASS()
589 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_source_random)
590 DECLARE_METHOD(get_seed, Amanda::Xfer::xfer_source_random_get_seed)
594 PACKAGE(Amanda::Xfer::Source::DirectTCPListen)
595 XFER_ELEMENT_SUBCLASS()
596 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_source_directtcp_listen)
597 DECLARE_METHOD(get_addrs, Amanda::Xfer::xfer_source_directtcp_listen_get_addrs)
601 PACKAGE(Amanda::Xfer::Source::DirectTCPConnect)
602 XFER_ELEMENT_SUBCLASS()
603 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_source_directtcp_connect)
607 PACKAGE(Amanda::Xfer::Source::Pattern)
608 XFER_ELEMENT_SUBCLASS()
609 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_source_pattern)
613 PACKAGE(Amanda::Xfer::Filter::Xor)
614 XFER_ELEMENT_SUBCLASS()
615 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_filter_xor)
619 PACKAGE(Amanda::Xfer::Filter::Process)
620 XFER_ELEMENT_SUBCLASS()
621 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_filter_process)
622 DECLARE_METHOD(get_stderr_fd, Amanda::Xfer::get_err_fd)
626 PACKAGE(Amanda::Xfer::Dest::Fd)
627 XFER_ELEMENT_SUBCLASS()
628 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_dest_fd)
632 PACKAGE(Amanda::Xfer::Dest::Null)
633 XFER_ELEMENT_SUBCLASS()
634 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_dest_null)
638 PACKAGE(Amanda::Xfer::Dest::Buffer)
639 XFER_ELEMENT_SUBCLASS()
640 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_dest_buffer)
641 DECLARE_METHOD(get, Amanda::Xfer::xfer_dest_buffer_get)
645 PACKAGE(Amanda::Xfer::Dest::DirectTCPListen)
646 XFER_ELEMENT_SUBCLASS()
647 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_dest_directtcp_listen)
648 DECLARE_METHOD(get_addrs, Amanda::Xfer::xfer_dest_directtcp_listen_get_addrs)
652 PACKAGE(Amanda::Xfer::Dest::DirectTCPConnect)
653 XFER_ELEMENT_SUBCLASS()
654 DECLARE_CONSTRUCTOR(Amanda::Xfer::xfer_dest_directtcp_connect)
658 PACKAGE(Amanda::Xfer::Msg)
661 use overload '""' => sub { $_[0]->repr(); };
665 local $Data::Dumper::Indent = 0;
666 local $Data::Dumper::Terse = 1;
667 local $Data::Dumper::Useqq = 1;
669 my $typestr = Amanda::Xfer::xmsg_type_to_string($self->{'type'});
670 my $str = "{ type => \$$typestr, elt => $self->{'elt'}, version => $self->{'version'},";
672 my %skip = ( "type" => 1, "elt" => 1, "version" => 1 );
673 for my $k (keys %$self) {
675 $str .= " $k => " . Dumper($self->{$k}) . ",";
678 # strip the trailing comma and add a closing brace
687 PACKAGE(Amanda::Xfer)
689 # make Amanda::Xfer->new equivalent to Amanda::Xfer::Xfer->new (don't
690 # worry, the blessings work out just fine)
691 *new = *Amanda::Xfer::Xfer::new;
693 # try to load Amanda::XferServer, which is server-only. If it's not found, then
694 # its classes just remain undefined.
697 if (Amanda::Util::built_with_component("server")) {
698 eval "use Amanda::XferServer;";