/*
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Carbonite Inc., 756 N Pastoria Ave
* Sunnyvale, CA 94085, or: http://www.zmanda.com
*/
%module "Amanda::Logfile"
%include "amglue/amglue.swg"
%include "exception.i"
%include "amglue/dumpspecs.swg"
%import "Amanda/Cmdline.swg"
%include "Amanda/Logfile.pod"
%{
#include <glib.h>
#include "logfile.h"
#include "find.h"
#include "diskfile.h" /* for the gross hack, below */
#include "amindex.h"
%}
amglue_export_ok(
open_logfile get_logline close_logfile
log_add log_add_full log_start_multiline log_end_multiline
);
amglue_add_enum_tag_fns(logtype_t);
amglue_add_constant(L_BOGUS, logtype_t);
amglue_add_constant(L_FATAL, logtype_t);
amglue_add_constant(L_ERROR, logtype_t);
amglue_add_constant(L_WARNING, logtype_t);
amglue_add_constant(L_INFO, logtype_t);
amglue_add_constant(L_SUMMARY, logtype_t);
amglue_add_constant(L_START, logtype_t);
amglue_add_constant(L_FINISH, logtype_t);
amglue_add_constant(L_DISK, logtype_t);
amglue_add_constant(L_DONE, logtype_t);
amglue_add_constant(L_PART, logtype_t);
amglue_add_constant(L_PARTPARTIAL, logtype_t);
amglue_add_constant(L_SUCCESS, logtype_t);
amglue_add_constant(L_PARTIAL, logtype_t);
amglue_add_constant(L_FAIL, logtype_t);
amglue_add_constant(L_STRANGE, logtype_t);
amglue_add_constant(L_CHUNK, logtype_t);
amglue_add_constant(L_CHUNKSUCCESS, logtype_t);
amglue_add_constant(L_STATS, logtype_t);
amglue_add_constant(L_CONT, logtype_t);
amglue_add_constant(L_RETRY, logtype_t);
amglue_add_constant(L_MARKER, logtype_t);
amglue_copy_to_tag(logtype_t, constants);
amglue_add_enum_tag_fns(program_t);
amglue_add_constant(P_UNKNOWN, program_t);
amglue_add_constant(P_PLANNER, program_t);
amglue_add_constant(P_DRIVER, program_t);
amglue_add_constant(P_AMREPORT, program_t);
amglue_add_constant(P_DUMPER, program_t);
amglue_add_constant(P_CHUNKER, program_t);
amglue_add_constant(P_TAPER, program_t);
amglue_add_constant(P_AMFLUSH, program_t);
amglue_add_constant(P_AMDUMP, program_t);
amglue_add_constant(P_AMIDXTAPED, program_t);
amglue_add_constant(P_AMFETCHDUMP, program_t);
amglue_add_constant(P_AMCHECKDUMP, program_t);
amglue_add_constant(P_AMVAULT, program_t);
amglue_add_constant(P_AMCLEANUP, program_t);
amglue_add_constant(P_AMBACKUPD, program_t);
amglue_add_constant(P_AMTRMIDX, program_t);
amglue_add_constant(P_AMTRMLOG, program_t);
amglue_copy_to_tag(program_t, constants);
char *get_logtype_str(logtype_t logtype);
char *get_program_str(program_t program);
/* TODO: support for writing logfiles is omitted for the moment. */
%inline %{
/* open_ and close_logfile are both simple wrappers around fopen/fclose. */
typedef FILE loghandle;
static loghandle *open_logfile(char *filename) {
return fopen(filename, "r");
}
%}
%inline %{
static void close_logfile(loghandle *logfile) {
if (logfile) fclose(logfile);
}
%}
/* We fake the return type of get_logline, and use a typemap to
* slurp curstr, curprog, and curlog into a return value. */
%{
typedef int LOGLINE_RETURN;
%}
%typemap(out) LOGLINE_RETURN {
if ($1 != 0) {
EXTEND(SP, 3);
$result = sv_2mortal(newSViv(curlog));
argvi++;
$result = sv_2mortal(newSViv(curprog));
argvi++;
$result = sv_2mortal(newSVpv(curstr, 0));
argvi++;
}
/* otherwise (end of logfile) return an empty list */
}
LOGLINE_RETURN get_logline(FILE *logfile);
%rename(log_add) log_add_;
%rename(log_add_full) log_add_full_;
%inline %{
static void log_add_(logtype_t typ, char *message)
{
log_add(typ, "%s", message);
}
static void log_add_full_(logtype_t typ, char *pname, char *message)
{
log_add_full(typ, pname, "%s", message);
}
%}
char *make_logname(char *process, char *datestamp);
%newobject get_logname;
char *get_logname(void);
void set_logname(char *filename);
void log_rename(char *datestamp);
void log_start_multiline(void);
void log_end_multiline(void);
%typemap(in) crc_t {
parse_crc(SvPV_nolen($input), &$1);
}
%typemap(out) crc_t {
char *s_crc;
s_crc = g_strdup_printf("%08x:%lld", $1.crc, (long long)$1.size);
$result = sv_2mortal(newSVpv(s_crc, 0));
g_free(s_crc);
argvi++;
}
typedef struct {
%extend {
/* destructor */
~find_result_t() {
find_result_t *selfp = self;
free_find_result(&selfp);
}
}
%immutable;
char *timestamp;
char *write_timestamp;
char *hostname;
char *diskname;
char *storage;
int storage_id;
char *pool;
int level;
char *label;
off_t filenum;
char *status;
char *dump_status;
char *message;
int partnum;
int totalparts;
double sec;
off_t bytes;
off_t kb;
off_t orig_kb;
crc_t native_crc;
crc_t client_crc;
crc_t server_crc;
%mutable;
} find_result_t;
/* This typemap is used in a few functions. It converts a linked list of find_result_t's
* into an array of same, de-linking the list in the process. This gives ownership of the
* objects to perl, which is consistent with the C interface to this module.
*/
%typemap(out) find_result_t * {
find_result_t *iter;
int len;
/* measure the list and make room on the perl stack */
for (len=0, iter=$1; iter; iter=iter->next) len++;
EXTEND(SP, len);
iter = $1;
while (iter) {
find_result_t *next;
/* Let SWIG take ownership of the object */
$result = SWIG_NewPointerObj(iter, $descriptor(find_result_t *), SWIG_OWNER | SWIG_SHADOW);
argvi++;
/* null out the 'next' field */
next = iter->next;
iter->next = NULL;
iter = next;
}
}
/* Similarly, on input we link an array full of find_result_t's. The list is then
* unlinked on return. Note that the array is supplied as an arrayref (since it's
* usually the first argument).
*/
%typemap(in) find_result_t * {
AV *av;
I32 len, i;
find_result_t *head = NULL, *tail = NULL;
if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV) {
SWIG_exception(SWIG_TypeError, "expected an arrayref of find_result_t's");
}
av = (AV *)SvRV($input);
len = av_len(av) + 1;
for (i = 0; i < len; i++) {
SV **val = av_fetch(av, i, 0);
find_result_t *r;
if (!val || SWIG_ConvertPtr(*val, (void **)&r, $descriptor(find_result_t *), 0) == -1) {
SWIG_exception(SWIG_TypeError, "array member is not a find_result_t");
}
if (!head) {
head = tail = r;
} else {
tail->next = r;
tail = r;
}
tail->next = NULL;
}
/* point to the head of that list */
$1 = head;
}
%typemap(freearg) find_result_t * {
find_result_t *iter = $1, *next;
/* undo all the links we added earlier */
while (iter) {
next = iter->next;
iter->next = NULL;
iter = next;
}
}
%typemap(out) char ** {
char **iter;
int len, i;
/* measure the length of the array and make sure perl has enough room */
for (len=0, iter=$1; *iter; iter++) len++;
EXTEND(SP, len);
/* now copy it to the perl stack */
for (i=0, iter=$1; *iter; iter++, i++) {
$result = sv_2mortal(newSVpv(*iter, 0));
argvi++;
}
}
amglue_export_ok(
find_log search_logfile dumps_match log_rename
);
char **find_log(void);
%rename(search_logfile) search_logfile_wrap;
%inline %{
static find_result_t *search_logfile_wrap(char *label, char *datestamp,
char *logfile, int add_missing_disks,
int added_todo) {
find_result_t *rv = NULL;
/* We use a static variable to collect any unrecognized disks */
static disklist_t unrecognized_disks = { NULL, NULL };
search_logfile(&rv, label, datestamp, logfile,
add_missing_disks? &unrecognized_disks : NULL, added_todo);
return rv;
}
%}
%rename(search_holding_disk) search_holding_disk_wrap;
%inline %{
static find_result_t *search_holding_disk_wrap(int added_todo) {
find_result_t *rv = NULL;
static disklist_t unrecognized_disks = { NULL, NULL };
search_holding_disk(&rv, &unrecognized_disks, added_todo);
return rv;
}
%}
find_result_t *dumps_match(find_result_t *output_find, char *hostname,
char *diskname, char *datestamp, char *level, int ok);
find_result_t *dumps_match_dumpspecs(find_result_t *output_find,
amglue_dumpspec_list *dumpspecs,
gboolean ok);
%newobject getheaderfname;
char *getheaderfname(char *host, char *disk, char *date, int level);
%newobject getstatefname;
char *getstatefname(char *host, char *disk, char *date, int level);
%newobject getindexfname;
char *getindexfname(char *host, char *disk, char *date, int level);
%newobject getindex_unsorted_fname;
char *getindex_unsorted_fname(char *host, char *disk, char *date, int level);
%newobject getindex_unsorted_gz_fname;
char *getindex_unsorted_gz_fname(char *host, char *disk, char *date, int level);
%newobject getindex_sorted_fname;
char *getindex_sorted_fname(char *host, char *disk, char *date, int level);
%newobject getindex_sorted_gz_fname;
char *getindex_sorted_gz_fname(char *host, char *disk, char *date, int level);
%immutable;
amanda_log_handler_t *amanda_log_trace_log;
%mutable;
amglue_export_ok(
$amanda_log_trace_log
);
amglue_export_ok(
find_all_logs find_latest_log
get_current_log_timestamp
make_stats
make_chunker_stats
);
%perlcode %{
use Amanda::Config qw ( :init :getconf config_dir_relative );
use Amanda::Debug;
sub find_all_logs
{
my $logdir = shift @_ || config_dir_relative(getconf($CNF_LOGDIR));
opendir my $logdh, $logdir or die("can't read $logdir");
my @logfiles = sort grep { m{^log\.\d+\.\d+$} } readdir $logdh;
return @logfiles;
}
sub find_latest_log
{
my $logdir = shift @_;
my @logs = find_all_logs($logdir || ());
return $logs[-1];
}
sub get_current_log_timestamp
{
my $logfile = config_dir_relative(getconf($CNF_LOGDIR)) . "/log";
if (! -f $logfile) {
Amanda::Debug::warning("no current logfile '$logfile'");
return undef;
}
my $logh = open_logfile("$logfile");
if (!$logh) {
Amanda::Debug::warning("could not open logfile '$logfile'");
return undef;
}
while (my ($type, $prog, $str) = get_logline($logh)) {
if ($type == $L_START) {
my ($ts) = ($str =~ /date (\d+)/);
return $ts if $ts;
}
}
# no timestamp, apparently
Amanda::Debug::warning("no current timestamp found in logfile");
return undef;
}
sub make_stats {
my ($size, $duration, $orig_kb) = @_;
$duration = 0.1 if $duration <= 0; # prevent division by zero
my $kb = $size/1024;
my $kps = "$kb.0"/$duration; # Perlish cast from BigInt to float
if (defined $orig_kb) {
return sprintf("[sec %f bytes %s kps %f orig-kb %s]", $duration, $size, $kps, $orig_kb);
} else {
return sprintf("[sec %f bytes %s kps %f]", $duration, $size, $kps);
}
}
sub make_chunker_stats {
my ($size, $duration) = @_;
$duration = 0.1 if $duration <= 0; # prevent division by zero
my $kb = $size/1024;
my $kps = "$kb.0"/$duration; # Perlish cast from BigInt to float
return sprintf("[sec %f kb %s kps %f]", $duration, $kb, $kps);
}
%}