1/*
2 * Copyright (c) 2007-2013 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
37amglue_export_ok(
38    open_logfile get_logline close_logfile
39    log_add log_add_full
40);
41
42
43amglue_add_enum_tag_fns(logtype_t);
44amglue_add_constant(L_BOGUS, logtype_t);
45amglue_add_constant(L_FATAL, logtype_t);
46amglue_add_constant(L_ERROR, logtype_t);
47amglue_add_constant(L_WARNING, logtype_t);
48amglue_add_constant(L_INFO, logtype_t);
49amglue_add_constant(L_SUMMARY, logtype_t);
50amglue_add_constant(L_START, logtype_t);
51amglue_add_constant(L_FINISH, logtype_t);
52amglue_add_constant(L_DISK, logtype_t);
53amglue_add_constant(L_DONE, logtype_t);
54amglue_add_constant(L_PART, logtype_t);
55amglue_add_constant(L_PARTPARTIAL, logtype_t);
56amglue_add_constant(L_SUCCESS, logtype_t);
57amglue_add_constant(L_PARTIAL, logtype_t);
58amglue_add_constant(L_FAIL, logtype_t);
59amglue_add_constant(L_STRANGE, logtype_t);
60amglue_add_constant(L_CHUNK, logtype_t);
61amglue_add_constant(L_CHUNKSUCCESS, logtype_t);
62amglue_add_constant(L_STATS, logtype_t);
63amglue_add_constant(L_MARKER, logtype_t);
64amglue_add_constant(L_CONT, logtype_t);
65amglue_copy_to_tag(logtype_t, constants);
66
67amglue_add_enum_tag_fns(program_t);
68amglue_add_constant(P_UNKNOWN, program_t);
69amglue_add_constant(P_PLANNER, program_t);
70amglue_add_constant(P_DRIVER, program_t);
71amglue_add_constant(P_REPORTER, program_t);
72amglue_add_constant(P_DUMPER, program_t);
73amglue_add_constant(P_CHUNKER, program_t);
74amglue_add_constant(P_TAPER, program_t);
75amglue_add_constant(P_AMFLUSH, program_t);
76amglue_add_constant(P_AMDUMP, program_t);
77amglue_add_constant(P_AMIDXTAPED, program_t);
78amglue_add_constant(P_AMFETCHDUMP, program_t);
79amglue_add_constant(P_AMCHECKDUMP, program_t);
80amglue_add_constant(P_AMVAULT, program_t);
81amglue_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. */
87typedef FILE loghandle;
88
89static loghandle *open_logfile(char *filename) {
90    return fopen(filename, "r");
91}
92%}
93
94%inline %{
95static 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%{
103typedef 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}
117LOGLINE_RETURN get_logline(FILE *logfile);
118
119%rename(log_add) log_add_;
120%rename(log_add_full) log_add_full_;
121%inline %{
122static void log_add_(logtype_t typ, char *message)
123{
124    log_add(typ, "%s", message);
125}
126static void log_add_full_(logtype_t typ, char *pname, char *message)
127{
128    log_add_full(typ, pname, "%s", message);
129}
130%}
131
132void log_rename(char *datestamp);
133
134typedef 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
253amglue_export_ok(
254    find_log search_logfile dumps_match log_rename
255);
256
257char **find_log(void);
258
259%rename(search_logfile) search_logfile_wrap;
260%inline %{
261static 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 %{
277static 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
285find_result_t *dumps_match(find_result_t *output_find, char *hostname,
286			   char *diskname, char *datestamp, char *level, int ok);
287
288find_result_t *dumps_match_dumpspecs(find_result_t *output_find,
289    amglue_dumpspec_list *dumpspecs,
290    gboolean ok);
291
292%immutable;
293amanda_log_handler_t *amanda_log_trace_log;
294%mutable;
295amglue_export_ok(
296    $amanda_log_trace_log
297);
298
299
300amglue_export_ok(
301    find_all_logs find_latest_log
302    get_current_log_timestamp
303    make_stats
304);
305
306%perlcode %{
307
308use Amanda::Config qw ( :init :getconf config_dir_relative );
309use Amanda::Debug;
310
311sub find_all_logs
312{
313    my $logdir = shift @_ || config_dir_relative(getconf($CNF_LOGDIR));
314
315    opendir my $logdh, $logdir or die("can't read $logdir");
316    my @logfiles = sort grep { m{^log\.\d+\.\d+$} } readdir $logdh;
317
318    return @logfiles;
319}
320
321sub find_latest_log
322{
323    my $logdir = shift @_;
324    my @logs = find_all_logs($logdir || ());
325    return $logs[-1];
326}
327
328sub get_current_log_timestamp
329{
330    my $logfile = config_dir_relative(getconf($CNF_LOGDIR)) . "/log";
331    if (! -f $logfile) {
332	Amanda::Debug::warning("no current logfile '$logfile'");
333	return undef;
334    }
335
336    my $logh = open_logfile("$logfile");
337    if (!$logh) {
338	Amanda::Debug::warning("could not open logfile '$logfile'");
339	return undef;
340    }
341    while (my ($type, $prog, $str) = get_logline($logh)) {
342	if ($type == $L_START) {
343	    my ($ts) = ($str =~ /date (\d+)/);
344	    return $ts if $ts;
345	}
346    }
347
348    # no timestamp, apparently
349    Amanda::Debug::warning("no current timestamp found in logfile");
350    return undef;
351}
352
353sub make_stats {
354    my ($size, $duration, $orig_kb) = @_;
355
356    $duration = 0.1 if $duration <= 0;  # prevent division by zero
357    my $kb = $size/1024;
358    my $kps = "$kb.0"/$duration; # Perlish cast from BigInt to float
359
360    if (defined $orig_kb) {
361	return sprintf("[sec %f bytes %s kps %f orig-kb %s]", $duration, $size, $kps, $orig_kb);
362    } else {
363	return sprintf("[sec %f bytes %s kps %f]", $duration, $size, $kps);
364    }
365}
366
367%}
368