This file is indexed.

/usr/share/perl5/Munin/Node/SpoolWriter.pm is in munin-node 2.0.33-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package Munin::Node::SpoolWriter;

# $Id$

use strict;
use warnings;

use Carp;
use IO::File;

use Fcntl qw(:DEFAULT :flock);

use Munin::Common::Defaults;
use Munin::Common::SyncDictFile;
use Munin::Node::Logger;


use constant DEFAULT_TIME => 86_400;      # put 1 day of results into a spool file
use constant MAXIMUM_AGE  => 7;           # remove spool files more than a week old

sub _snap_to_epoch_boundary { my $self = shift; return $_[0] - ($_[0] % $self->{interval_size}) }


sub new
{
    my ($class, %args) = @_;

    $args{spooldir} or croak "no spooldir provided";

    opendir $args{spooldirhandle}, $args{spooldir}
        or croak "Could not open spooldir '$args{spooldir}': $!";

    $args{metadata} = _init_metadata($args{spooldir});

    if(!$args{interval_size} && (my $interval_size = get_metadata(\%args, "interval_size"))) {
	    $args{interval_size} = $interval_size;
    }

    if(!$args{interval_keep} && (my $interval_keep = get_metadata(\%args, "interval_keep"))) {
	    $args{interval_keep} = $interval_keep;
    }

    if(!$args{hostname} && (my $hostname = get_metadata(\%args, "hostname"))) {
	    $args{hostname} = $hostname;
    }

    $args{interval_size} = DEFAULT_TIME unless ($args{interval_size});
    $args{interval_keep} = MAXIMUM_AGE unless ($args{interval_keep});
    $args{hostname} = "unknown" unless ($args{hostname});

    set_metadata(\%args, "interval_size", $args{interval_size}) if $args{interval_size} != get_metadata(\%args, "interval_size");
    set_metadata(\%args, "interval_keep", $args{interval_keep}) if $args{interval_keep} != get_metadata(\%args, "interval_keep");
    set_metadata(\%args, "hostname", $args{hostname}) if $args{hostname} ne get_metadata(\%args, "hostname");

    # TODO: paranoia check?  except dir doesn't (currently) have to be
    # root-owned.

    # TODO: set umask

    return bless \%args, $class;
}


#prepare tied hash for metadata persisted to $spooldir/SPOOL-META
#should we pull these methods into a base class or create a spool manager class?
sub _init_metadata
{

	my $spooldir = shift;
	my %metadata;

	tie %metadata, 'Munin::Common::SyncDictFile', $spooldir . "/SPOOL-META";

	return \%metadata;

}


#retrieve metadata value for key
sub get_metadata
{
	my ($self, $key) = @_;

	return ${ $self->{metadata} }{$key};

}


#set metadata key:value and persist
sub set_metadata
{
	my ($self, $key, $value) = @_;

	${ $self->{metadata} }{$key} = $value;

}


# writes the results of a plugin run to the appropriate spool-file
# need to remove any lines containing only a '.'
sub write
{
    my ($self, $timestamp, $service, $data) = @_;

    my $fmtTimestamp = $self->_snap_to_epoch_boundary($timestamp);

    open my $fh , '>>', "$self->{spooldir}/munin-daemon.$service.$fmtTimestamp." . $self->{interval_size}
        or die "Unable to open spool file: $!";
    flock($fh, LOCK_EX);

    print {$fh} "timestamp $timestamp\n";

    # squash the $service name with the same rules as the munin-update when using plain TCP
    # Closes D:710529
    my $service_squashed = $service; $service_squashed =~ tr/.:/__/;
    print {$fh} "multigraph $service_squashed\n" unless $data->[0] =~ m{^multigraph};

    foreach my $line (@$data) {
        # Ignore blank lines and "."-ones.
        next if (!defined($line) || $line eq '' || $line eq '.');

        print {$fh} $line, "\n" or logger("Error writing results: $!");
    }

    return;
}


# removes files from the spooldir older than MAXIMUM_AGE
sub cleanup
{
    my ($self) = @_;

    my $maxage = $self->{interval_size} * $self->{interval_keep};

    opendir my $dir, $self->{spooldir} or die $!;

    foreach my $file (readdir $dir) {
        next unless $file =~ m{munin-daemon\.\w+\.\d+\.\d+$};

        my $filename = "$self->{spooldir}/$file";
        my $mtime = (stat $filename)[9];
        next unless (time - $mtime) > $maxage;

        unlink $filename or die "Unable to unlink '$filename': $!\n";
    }

    return;
}


1;

__END__

=head1 NAME

Munin::Node::SpoolWriter - Writing side of the spool functionality

=head1 SYNOPSIS

  my $spool = Munin::Node::SpoolWriter->new(spooldir => $spooldir);
  $spool->write(1234567890, 'cpu', \@results);

=head1 METHODS

=over 4

=item B<new(%args)>

Constructor.  'spooldir' key should be the directory
L<Munin::Node::SpoolReader> is reading from.

=item B<write($timestamp, $service, \@results)>

Takes a timestamp, service name, and the results of running config and fetch on
it.  Writes it to the spool directory for L<Munin::Node::SpoolReader> to read.

=item B<cleanup($timestamp)>

Removes any items in the spool directory older than $timestamp.

=back

=cut

# vim: sw=4 : ts=4 : et