Imported Upstream version 3.3.3
[debian/amanda] / perl / Amanda / IPC / Binary.swg
1 /*
2  * Copyright (c) 2009-2012 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::IPC::Binary"
23 %include "amglue/amglue.swg"
24 %include "exception.i"
25
26 %include "Amanda/IPC/Binary.pod"
27
28 %{
29 #include <glib.h>
30 #include "ipc-binary.h"
31 %}
32
33 /*
34  * types
35  */
36 typedef struct ipc_binary_proto_t ipc_binary_proto_t;
37 typedef struct ipc_binary_cmd_t ipc_binary_cmd_t;
38 typedef struct ipc_binary_channel_t ipc_binary_channel_t;
39 typedef struct ipc_binary_message_t ipc_binary_message_t;
40
41 /*
42  * typemaps
43  */
44
45 %typemap(out) ipc_binary_message_t * {
46     static HV *amanda_xfer_msg_stash = NULL;
47     HV *hash;
48     SV *rv;
49     AV *args;
50     int i, nargs;
51
52     if ($1) {
53         hash = newHV();
54         rv = newRV_noinc((SV *)hash);
55
56         /* bless the rv as an Amanda::Xfer::Msg object */
57         if (!amanda_xfer_msg_stash) {
58             amanda_xfer_msg_stash = gv_stashpv("Amanda::IPC::Binary::Message", GV_ADD);
59         }
60         sv_bless(rv, amanda_xfer_msg_stash);
61
62         args = newAV();
63         hv_store(hash, "cmd_id", 6, newSViv($1->cmd_id), 0);
64         hv_store(hash, "args", 4, newRV_noinc((SV *)args), 0);
65
66         /* loop over all messages, using av_store to insert the args which are present;
67         * this will fill in undef's where necessary */
68         for (i = 0; i < $1->n_args; i++) {
69             if ($1->args[i].data == NULL)
70                 continue;
71
72             g_assert(NULL !=
73                 av_store(args, i, newSVpvn($1->args[i].data, $1->args[i].len)));
74         }
75
76         /* we don't need the C data any more */
77         ipc_binary_free_message($1);
78
79         $result = rv;
80         argvi++;
81     }
82 }
83
84 %typemap(in) ipc_binary_message_t * {
85     HV *hv;
86     AV *av;
87     SV **svp;
88     int cmd_id;
89     ipc_binary_channel_t *chan = NULL;
90     ipc_binary_message_t *msg;
91     int i, len;
92
93     if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVHV
94             || !sv_isa($input, "Amanda::IPC::Binary::Message"))
95         SWIG_exception(SWIG_TypeError, "Expected an Amanda::IPC::Binary::Message");
96
97     hv = (HV *)SvRV($input);
98
99     /* get cmd_id */
100     svp = hv_fetch(hv, "cmd_id", 6, FALSE);
101     if (!svp || !SvIOK(*svp))
102         SWIG_exception(SWIG_TypeError, "'cmd_id' key missing or not numeric");
103     cmd_id = SvIV(*svp);
104
105     /* get channel */
106     svp = hv_fetch(hv, "chan", 4, FALSE);
107     if (!svp || SWIG_ConvertPtr(*svp, (void **)&chan,
108                                 $descriptor(ipc_binary_channel_t *), 0) == -1
109              || !chan)
110         SWIG_exception(SWIG_TypeError, "'chan' key missing or incorrect");
111
112     /* get args */
113     svp = hv_fetch(hv, "args", 4, FALSE);
114     if (!svp || !SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVAV)
115         SWIG_exception(SWIG_TypeError, "'args' key missing or not an arrayref");
116     av = (AV *)SvRV(*svp);
117
118     msg = ipc_binary_new_message(chan, cmd_id);
119
120     len = av_len(av);
121     for (i = 0; i <= len; i++) {
122         SV **elt = av_fetch(av, i, 0);
123         STRLEN datasize;
124         gpointer data;
125
126         if (elt && SvPOK(*elt)) {
127             data = (gpointer)SvPV(*elt, datasize);
128             ipc_binary_add_arg(msg, i, datasize, data, 0);
129         }
130     }
131
132     $1 = msg;
133 }
134
135 /*
136  * functions
137  */
138
139 ipc_binary_proto_t *ipc_binary_proto_new(
140     guint16 magic);
141
142 ipc_binary_cmd_t *ipc_binary_proto_add_cmd(
143     ipc_binary_proto_t *proto,
144     guint16 id);
145
146 void ipc_binary_cmd_add_arg(
147     ipc_binary_cmd_t *cmd,
148     guint16 id,
149     guint8 flags);
150
151 /* flag symbols for use in perl; values don't matter */
152 enum {
153     IPC_BINARY_STRING,
154     IPC_BINARY_OPTIONAL,
155 };
156 amglue_export(
157     $IPC_BINARY_STRING $IPC_BINARY_OPTIONAL);
158
159 ipc_binary_channel_t *ipc_binary_new_channel(
160     ipc_binary_proto_t *proto);
161
162 void ipc_binary_free_channel(
163     ipc_binary_channel_t *channel);
164
165 ipc_binary_message_t *ipc_binary_read_message(
166     ipc_binary_channel_t *chan,
167     int fd);
168
169 int ipc_binary_write_message(
170     ipc_binary_channel_t *chan,
171     int fd,
172     ipc_binary_message_t *msg);
173
174 void ipc_binary_feed_data(
175     ipc_binary_channel_t *chan,
176     gsize size,
177     gpointer data);
178
179 void ipc_binary_data_transmitted(
180     ipc_binary_channel_t *chan,
181     gsize size);
182
183 ipc_binary_message_t *ipc_binary_poll_message(
184     ipc_binary_channel_t *chan);
185
186 void ipc_binary_queue_message(
187     ipc_binary_channel_t *chan,
188     ipc_binary_message_t *msg);
189
190
191 /*
192  * Perl layer
193  */
194
195 %perlcode %{
196
197 use Carp;
198 push @EXPORT, qw( magic command new message );
199
200 # a map from package name to protocol
201 my %protos_by_pkg;
202
203 sub magic {
204     my ($magic) = @_;
205     my $caller = caller;
206
207     croak "magic already set for this protocol"
208         if (exists $protos_by_pkg{$caller});
209
210     $protos_by_pkg{$caller} = ipc_binary_proto_new($magic);
211 }
212
213 sub command {
214     my ($cmd_id, @args) = @_;
215     my $caller = caller;
216
217     croak "magic not set for this protocol"
218         unless (exists $protos_by_pkg{$caller});
219
220     croak "command args must be specified in pairs"
221         unless (@args % 2 == 0);
222
223     my $proto = $protos_by_pkg{$caller};
224     $cmd = ipc_binary_proto_add_cmd($proto, $cmd_id);
225
226     while (@args) {
227         my $arg = shift @args;
228         my $flags = shift @args;
229         ipc_binary_cmd_add_arg($cmd, $arg, $flags);
230     }
231 }
232
233 ##
234 # Class Methods
235
236 sub new {
237     my $class = shift;
238
239     my $self = bless {
240         chan => ipc_binary_new_channel($protos_by_pkg{$class}),
241     }, $class;
242 }
243
244 sub message {
245     my $self = shift;
246     my ($cmd_id, @args) = @_;
247
248     $self = bless {
249         cmd_id => $cmd_id,
250         chan => $self->{'chan'},
251         args => [],
252     }, "Amanda::IPC::Binary::Message";
253
254
255     while (@args) {
256         my $arg = shift @args;
257         my $val = shift @args;
258         $self->{'args'}[$arg] = $val;
259     }
260
261     return $self;
262 }
263
264 sub close {
265     if ($self->{'chan'}) {
266         ipc_binary_free_channel($self->{'chan'});
267         $self->{'chan'} = undef;
268     }
269 }
270
271 *DESTROY = *close;
272
273 ##
274 # Blocking interface
275
276 sub read_message {
277     my $self = shift;
278     my ($fd) = @_;
279
280     return ipc_binary_read_message($self->{'chan'}, $fd);
281 }
282
283 sub write_message {
284     my $self = shift;
285     my ($fd, $msg) = @_;
286
287     if (ipc_binary_write_message($self->{'chan'}, $fd, $msg) < 0) {
288         return 0;
289     }
290     return 1;
291 }
292
293 ##
294 # Nonblocking interface -- TODO
295
296 ##
297 # Message structure
298
299 package Amanda::IPC::Binary::Message;
300
301 # (constructor is the protocol's C<message> method)
302
303 # format:
304 # { cmd_id => $cmd_id,
305 #   chan => $channel,
306 #   args => [ $arg0, $arg1, .. ],
307 # }
308
309 sub get_cmd {
310     return $self->{'cmd_id'};
311 }
312
313 sub get_arg {
314     my ($self, $arg_id) = @_;
315
316     return $self->{'args'}[$arg_id];
317 }
318
319 sub set_arg {
320     my ($self, $arg_id, $value) = @_;
321     $self->{'args'}[$arg_id] = $value;
322 }
323
324 package Amanda::IPC::Binary;
325
326 %}