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