Merge branch 'master' into squeeze
[debian/amanda] / perl / Amanda / IPC / Binary.swg
diff --git a/perl/Amanda/IPC/Binary.swg b/perl/Amanda/IPC/Binary.swg
new file mode 100644 (file)
index 0000000..f80b8f1
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2009 Zmanda, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * 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: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
+ * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
+ */
+
+%module "Amanda::IPC::Binary"
+%include "amglue/amglue.swg"
+%include "exception.i"
+
+%include "Amanda/IPC/Binary.pod"
+
+%{
+#include <glib.h>
+#include "ipc-binary.h"
+%}
+
+/*
+ * types
+ */
+typedef struct ipc_binary_proto_t ipc_binary_proto_t;
+typedef struct ipc_binary_cmd_t ipc_binary_cmd_t;
+typedef struct ipc_binary_channel_t ipc_binary_channel_t;
+typedef struct ipc_binary_message_t ipc_binary_message_t;
+
+/*
+ * typemaps
+ */
+
+%typemap(out) ipc_binary_message_t * {
+    static HV *amanda_xfer_msg_stash = NULL;
+    HV *hash;
+    SV *rv;
+    AV *args;
+    int i, nargs;
+
+    if ($1) {
+       hash = newHV();
+       rv = newRV_noinc((SV *)hash);
+
+       /* bless the rv as an Amanda::Xfer::Msg object */
+       if (!amanda_xfer_msg_stash) {
+           amanda_xfer_msg_stash = gv_stashpv("Amanda::IPC::Binary::Message", GV_ADD);
+       }
+       sv_bless(rv, amanda_xfer_msg_stash);
+
+       args = newAV();
+       hv_store(hash, "cmd_id", 6, newSViv($1->cmd_id), 0);
+       hv_store(hash, "args", 4, newRV_noinc((SV *)args), 0);
+
+       /* loop over all messages, using av_store to insert the args which are present;
+       * this will fill in undef's where necessary */
+       for (i = 0; i < $1->n_args; i++) {
+           if ($1->args[i].data == NULL)
+               continue;
+
+           g_assert(NULL !=
+               av_store(args, i, newSVpvn($1->args[i].data, $1->args[i].len)));
+       }
+
+       /* we don't need the C data any more */
+       ipc_binary_free_message($1);
+
+       $result = rv;
+       argvi++;
+    }
+}
+
+%typemap(in) ipc_binary_message_t * {
+    HV *hv;
+    AV *av;
+    SV **svp;
+    int cmd_id;
+    ipc_binary_channel_t *chan = NULL;
+    ipc_binary_message_t *msg;
+    int i, len;
+
+    if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVHV
+           || !sv_isa($input, "Amanda::IPC::Binary::Message"))
+       SWIG_exception(SWIG_TypeError, "Expected an Amanda::IPC::Binary::Message");
+
+    hv = (HV *)SvRV($input);
+
+    /* get cmd_id */
+    svp = hv_fetch(hv, "cmd_id", 6, FALSE);
+    if (!svp || !SvIOK(*svp))
+       SWIG_exception(SWIG_TypeError, "'cmd_id' key missing or not numeric");
+    cmd_id = SvIV(*svp);
+
+    /* get channel */
+    svp = hv_fetch(hv, "chan", 4, FALSE);
+    if (!svp || SWIG_ConvertPtr(*svp, (void **)&chan,
+                               $descriptor(ipc_binary_channel_t *), 0) == -1
+            || !chan)
+       SWIG_exception(SWIG_TypeError, "'chan' key missing or incorrect");
+
+    /* get args */
+    svp = hv_fetch(hv, "args", 4, FALSE);
+    if (!svp || !SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVAV)
+       SWIG_exception(SWIG_TypeError, "'args' key missing or not an arrayref");
+    av = (AV *)SvRV(*svp);
+
+    msg = ipc_binary_new_message(chan, cmd_id);
+
+    len = av_len(av);
+    for (i = 0; i <= len; i++) {
+       SV **elt = av_fetch(av, i, 0);
+       STRLEN datasize;
+       gpointer data;
+
+       if (elt && SvPOK(*elt)) {
+           data = (gpointer)SvPV(*elt, datasize);
+           ipc_binary_add_arg(msg, i, datasize, data, 0);
+       }
+    }
+
+    $1 = msg;
+}
+
+/*
+ * functions
+ */
+
+ipc_binary_proto_t *ipc_binary_proto_new(
+    guint16 magic);
+
+ipc_binary_cmd_t *ipc_binary_proto_add_cmd(
+    ipc_binary_proto_t *proto,
+    guint16 id);
+
+void ipc_binary_cmd_add_arg(
+    ipc_binary_cmd_t *cmd,
+    guint16 id,
+    guint8 flags);
+
+/* flag symbols for use in perl; values don't matter */
+enum {
+    IPC_BINARY_STRING,
+    IPC_BINARY_OPTIONAL,
+};
+amglue_export(
+    $IPC_BINARY_STRING $IPC_BINARY_OPTIONAL);
+
+ipc_binary_channel_t *ipc_binary_new_channel(
+    ipc_binary_proto_t *proto);
+
+void ipc_binary_free_channel(
+    ipc_binary_channel_t *channel);
+
+ipc_binary_message_t *ipc_binary_read_message(
+    ipc_binary_channel_t *chan,
+    int fd);
+
+int ipc_binary_write_message(
+    ipc_binary_channel_t *chan,
+    int fd,
+    ipc_binary_message_t *msg);
+
+void ipc_binary_feed_data(
+    ipc_binary_channel_t *chan,
+    gsize size,
+    gpointer data);
+
+void ipc_binary_data_transmitted(
+    ipc_binary_channel_t *chan,
+    gsize size);
+
+ipc_binary_message_t *ipc_binary_poll_message(
+    ipc_binary_channel_t *chan);
+
+void ipc_binary_queue_message(
+    ipc_binary_channel_t *chan,
+    ipc_binary_message_t *msg);
+
+
+/*
+ * Perl layer
+ */
+
+%perlcode %{
+
+use Carp;
+push @EXPORT, qw( magic command new message );
+
+# a map from package name to protocol
+my %protos_by_pkg;
+
+sub magic {
+    my ($magic) = @_;
+    my $caller = caller;
+
+    croak "magic already set for this protocol"
+       if (exists $protos_by_pkg{$caller});
+
+    $protos_by_pkg{$caller} = ipc_binary_proto_new($magic);
+}
+
+sub command {
+    my ($cmd_id, @args) = @_;
+    my $caller = caller;
+
+    croak "magic not set for this protocol"
+       unless (exists $protos_by_pkg{$caller});
+
+    croak "command args must be specified in pairs"
+       unless (@args % 2 == 0);
+
+    my $proto = $protos_by_pkg{$caller};
+    $cmd = ipc_binary_proto_add_cmd($proto, $cmd_id);
+
+    while (@args) {
+       my $arg = shift @args;
+       my $flags = shift @args;
+       ipc_binary_cmd_add_arg($cmd, $arg, $flags);
+    }
+}
+
+##
+# Class Methods
+
+sub new {
+    my $class = shift;
+
+    my $self = bless {
+       chan => ipc_binary_new_channel($protos_by_pkg{$class}),
+    }, $class;
+}
+
+sub message {
+    my $self = shift;
+    my ($cmd_id, @args) = @_;
+
+    $self = bless {
+       cmd_id => $cmd_id,
+       chan => $self->{'chan'},
+       args => [],
+    }, "Amanda::IPC::Binary::Message";
+
+
+    while (@args) {
+       my $arg = shift @args;
+       my $val = shift @args;
+       $self->{'args'}[$arg] = $val;
+    }
+
+    return $self;
+}
+
+sub close {
+    if ($self->{'chan'}) {
+       ipc_binary_free_channel($self->{'chan'});
+       $self->{'chan'} = undef;
+    }
+}
+
+*DESTROY = *close;
+
+##
+# Blocking interface
+
+sub read_message {
+    my $self = shift;
+    my ($fd) = @_;
+
+    return ipc_binary_read_message($self->{'chan'}, $fd);
+}
+
+sub write_message {
+    my $self = shift;
+    my ($fd, $msg) = @_;
+
+    if (ipc_binary_write_message($self->{'chan'}, $fd, $msg) < 0) {
+       return 0;
+    }
+    return 1;
+}
+
+##
+# Nonblocking interface -- TODO
+
+##
+# Message structure
+
+package Amanda::IPC::Binary::Message;
+
+# (constructor is the protocol's C<message> method)
+
+# format:
+# { cmd_id => $cmd_id,
+#   chan => $channel,
+#   args => [ $arg0, $arg1, .. ],
+# }
+
+sub get_cmd {
+    return $self->{'cmd_id'};
+}
+
+sub get_arg {
+    my ($self, $arg_id) = @_;
+
+    return $self->{'args'}[$arg_id];
+}
+
+sub set_arg {
+    my ($self, $arg_id, $value) = @_;
+    $self->{'args'}[$arg_id] = $value;
+}
+
+package Amanda::IPC::Binary;
+
+%}