Imported Upstream version 3.3.3
[debian/amanda] / perl / Amanda / Logfile.swg
1 /*
2  * Copyright (c) 2007-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::Logfile"
23 %include "amglue/amglue.swg"
24 %include "exception.i"
25 %include "amglue/dumpspecs.swg"
26 %import "Amanda/Cmdline.swg"
27
28 %include "Amanda/Logfile.pod"
29
30 %{
31 #include <glib.h>
32 #include "logfile.h"
33 #include "find.h"
34 #include "diskfile.h" /* for the gross hack, below */
35 %}
36
37 amglue_export_ok(
38     open_logfile get_logline close_logfile
39     log_add log_add_full
40 );
41
42
43 amglue_add_enum_tag_fns(logtype_t);
44 amglue_add_constant(L_BOGUS, logtype_t);
45 amglue_add_constant(L_FATAL, logtype_t);
46 amglue_add_constant(L_ERROR, logtype_t);
47 amglue_add_constant(L_WARNING, logtype_t);
48 amglue_add_constant(L_INFO, logtype_t);
49 amglue_add_constant(L_SUMMARY, logtype_t);
50 amglue_add_constant(L_START, logtype_t);
51 amglue_add_constant(L_FINISH, logtype_t);
52 amglue_add_constant(L_DISK, logtype_t);
53 amglue_add_constant(L_DONE, logtype_t);
54 amglue_add_constant(L_PART, logtype_t);
55 amglue_add_constant(L_PARTPARTIAL, logtype_t);
56 amglue_add_constant(L_SUCCESS, logtype_t);
57 amglue_add_constant(L_PARTIAL, logtype_t);
58 amglue_add_constant(L_FAIL, logtype_t);
59 amglue_add_constant(L_STRANGE, logtype_t);
60 amglue_add_constant(L_CHUNK, logtype_t);
61 amglue_add_constant(L_CHUNKSUCCESS, logtype_t);
62 amglue_add_constant(L_STATS, logtype_t);
63 amglue_add_constant(L_MARKER, logtype_t);
64 amglue_add_constant(L_CONT, logtype_t);
65 amglue_copy_to_tag(logtype_t, constants);
66
67 amglue_add_enum_tag_fns(program_t);
68 amglue_add_constant(P_UNKNOWN, program_t);
69 amglue_add_constant(P_PLANNER, program_t);
70 amglue_add_constant(P_DRIVER, program_t);
71 amglue_add_constant(P_REPORTER, program_t);
72 amglue_add_constant(P_DUMPER, program_t);
73 amglue_add_constant(P_CHUNKER, program_t);
74 amglue_add_constant(P_TAPER, program_t);
75 amglue_add_constant(P_AMFLUSH, program_t);
76 amglue_add_constant(P_AMDUMP, program_t);
77 amglue_add_constant(P_AMIDXTAPED, program_t);
78 amglue_add_constant(P_AMFETCHDUMP, program_t);
79 amglue_add_constant(P_AMCHECKDUMP, program_t);
80 amglue_add_constant(P_AMVAULT, program_t);
81 amglue_copy_to_tag(program_t, constants);
82
83 /* TODO: support for writing logfiles is omitted for the moment. */
84
85 %inline %{
86 /* open_ and close_logfile are both simple wrappers around fopen/fclose. */
87 typedef FILE loghandle;
88
89 static loghandle *open_logfile(char *filename) {
90     return fopen(filename, "r");
91 }
92 %}
93
94 %inline %{
95 static void close_logfile(loghandle *logfile) {
96     if (logfile) fclose(logfile);
97 }
98 %}
99
100 /* We fake the return type of get_logline, and use a typemap to
101  * slurp curstr, curprog, and curlog into a return value.  */
102 %{
103 typedef int LOGLINE_RETURN;
104 %}
105 %typemap(out) LOGLINE_RETURN {
106     if ($1 != 0) {
107         EXTEND(SP, 3);
108         $result = sv_2mortal(newSViv(curlog));
109         argvi++;
110         $result = sv_2mortal(newSViv(curprog));
111         argvi++;
112         $result = sv_2mortal(newSVpv(curstr, 0));
113         argvi++;
114     }
115     /* otherwise (end of logfile) return an empty list */
116 }
117 LOGLINE_RETURN get_logline(FILE *logfile);
118
119 %rename(log_add) log_add_;
120 %rename(log_add_full) log_add_full_;
121 %inline %{
122 static void log_add_(logtype_t typ, char *message)
123 {
124     log_add(typ, "%s", message);
125 }
126 static void log_add_full_(logtype_t typ, char *pname, char *message)
127 {
128     log_add_full(typ, pname, "%s", message);
129 }
130 %}
131
132 void log_rename(char *datestamp);
133
134 typedef struct {
135     %extend {
136         /* destructor */
137         ~find_result_t() {
138             find_result_t *selfp = self;
139             free_find_result(&selfp);
140         }
141     }
142
143     %immutable;
144     char *timestamp;
145     char *write_timestamp;
146     char *hostname;
147     char *diskname;
148     int level;
149     char *label;
150     off_t filenum;
151     char *status;
152     char *dump_status;
153     char *message;
154     int partnum;
155     int totalparts;
156     double sec;
157     off_t bytes;
158     off_t kb;
159     off_t orig_kb;
160     %mutable;
161 } find_result_t;
162
163 /* This typemap is used in a few functions.  It converts a linked list of find_result_t's
164  * into an array of same, de-linking the list in the process.  This gives ownership of the
165  * objects to perl, which is consistent with the C interface to this module.
166  */
167 %typemap(out) find_result_t * {
168     find_result_t *iter;
169     int len;
170
171     /* measure the list and make room on the perl stack */
172     for (len=0, iter=$1; iter; iter=iter->next) len++;
173     EXTEND(SP, len);
174
175     iter = $1;
176     while (iter) {
177         find_result_t *next;
178         /* Let SWIG take ownership of the object */
179         $result = SWIG_NewPointerObj(iter, $descriptor(find_result_t *), SWIG_OWNER | SWIG_SHADOW);
180         argvi++;
181
182         /* null out the 'next' field */
183         next = iter->next;
184         iter->next = NULL;
185         iter = next;
186     }
187 }
188
189 /* Similarly, on input we link an array full of find_result_t's.  The list is then
190  * unlinked on return.  Note that the array is supplied as an arrayref (since it's 
191  * usually the first argument).
192  */
193 %typemap(in) find_result_t * {
194     AV *av;
195     I32 len, i;
196     find_result_t *head = NULL, *tail = NULL;
197
198     if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV) {
199         SWIG_exception(SWIG_TypeError, "expected an arrayref of find_result_t's");
200     }
201
202     av = (AV *)SvRV($input);
203     len = av_len(av) + 1;
204
205     for (i = 0; i < len; i++) {
206         SV **val = av_fetch(av, i, 0);
207         find_result_t *r;
208
209         if (!val || SWIG_ConvertPtr(*val, (void **)&r, $descriptor(find_result_t *), 0) == -1) {
210             SWIG_exception(SWIG_TypeError, "array member is not a find_result_t");
211         }
212
213         if (!head) {
214             head = tail = r;
215         } else {
216             tail->next = r;
217             tail = r;
218         }
219
220         tail->next = NULL;
221     }
222
223     /* point to the head of that list */
224     $1 = head;
225 }
226
227 %typemap(freearg) find_result_t * {
228     find_result_t *iter = $1, *next;
229
230     /* undo all the links we added earlier */
231     while (iter) {
232         next = iter->next;
233         iter->next = NULL;
234         iter = next;
235     }
236 }
237
238 %typemap(out) char ** {
239     char **iter;
240     int len, i;
241     
242     /* measure the length of the array and make sure perl has enough room */
243     for (len=0, iter=$1; *iter; iter++) len++;
244     EXTEND(SP, len);
245
246     /* now copy it to the perl stack */
247     for (i=0, iter=$1; *iter; iter++, i++) {
248         $result = sv_2mortal(newSVpv(*iter, 0));
249         argvi++;
250     }
251 }
252
253 amglue_export_ok(
254     find_log search_logfile dumps_match log_rename
255 );
256
257 char **find_log(void);
258
259 %rename(search_logfile) search_logfile_wrap;
260 %inline %{
261 static find_result_t *search_logfile_wrap(char *label, char *datestamp,
262                                    char *logfile, int add_missing_disks) {
263     find_result_t *rv = NULL;
264
265     /* We use a static variable to collect any unrecognized disks */
266     static disklist_t unrecognized_disks = { NULL, NULL };
267
268     search_logfile(&rv, label, datestamp, logfile, 
269         add_missing_disks? &unrecognized_disks : NULL);
270
271     return rv;
272 }
273 %}
274
275 %rename(search_holding_disk) search_holding_disk_wrap;
276 %inline %{
277 static find_result_t *search_holding_disk_wrap(void) {
278     find_result_t *rv = NULL;
279     static disklist_t unrecognized_disks = { NULL, NULL };
280     search_holding_disk(&rv, &unrecognized_disks);
281     return rv;
282 }
283 %}
284
285 find_result_t *dumps_match(find_result_t *output_find, char *hostname,
286                            char *diskname, char *datestamp, char *level, int ok);
287
288 find_result_t *dumps_match_dumpspecs(find_result_t *output_find,
289     amglue_dumpspec_list *dumpspecs,
290     gboolean ok);
291
292 %immutable;
293 amanda_log_handler_t *amanda_log_trace_log;
294 %mutable;
295 amglue_export_ok(
296     $amanda_log_trace_log
297 );
298
299
300 amglue_export_ok(
301     find_all_logs find_latest_log
302     get_current_log_timestamp
303     make_stats
304 );
305
306 %perlcode %{
307
308 sub find_all_logs
309 {
310     my $logdir = shift @_ || config_dir_relative(getconf($CNF_LOGDIR));
311
312     opendir my $logdh, $logdir or die("can't read $logdir");
313     my @logfiles = sort grep { m{^log\.\d+\.\d+$} } readdir $logdh;
314
315     return @logfiles;
316 }
317
318 sub find_latest_log
319 {
320     my $logdir = shift @_;
321     my @logs = find_all_logs($logdir || ());
322     return $logs[-1];
323 }
324
325 use Amanda::Config;
326 use Amanda::Debug;
327
328 sub get_current_log_timestamp
329 {
330     my $logfile = Amanda::Config::config_dir_relative(
331                 Amanda::Config::getconf($Amanda::Config::CNF_LOGDIR)) . "/log";
332     if (! -f $logfile) {
333         Amanda::Debug::warning("no current logfile '$logfile'");
334         return undef;
335     }
336
337     my $logh = open_logfile("$logfile");
338     if (!$logh) {
339         Amanda::Debug::warning("could not open logfile '$logfile'");
340         return undef;
341     }
342     while (my ($type, $prog, $str) = get_logline($logh)) {
343         if ($type == $L_START) {
344             my ($ts) = ($str =~ /date (\d+)/);
345             return $ts if $ts;
346         }
347     }
348
349     # no timestamp, apparently
350     Amanda::Debug::warning("no current timestamp found in logfile");
351     return undef;
352 }
353
354 sub make_stats {
355     my ($size, $duration, $orig_kb) = @_;
356
357     $duration = 0.1 if $duration <= 0;  # prevent division by zero
358     my $kb = $size/1024;
359     my $kps = "$kb.0"/$duration; # Perlish cast from BigInt to float
360
361     if (defined $orig_kb) {
362         return sprintf("[sec %f bytes %s kps %f orig-kb %s]", $duration, $size, $kps, $orig_kb);
363     } else {
364         return sprintf("[sec %f bytes %s kps %f]", $duration, $size, $kps);
365     }
366 }
367
368 %}