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