File: fixup-duplicate-uprecords

package info (click to toggle)
uptimed 1%3A0.4.6-3.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 696 kB
  • sloc: ansic: 2,684; sh: 217; perl: 166; makefile: 51
file content (166 lines) | stat: -rwxr-xr-x 5,612 bytes parent folder | download | duplicates (2)
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
#!/usr/bin/perl

use strict;
use warnings;
use 5.020; # needs at least Debian 8 Jessie

# No modules, must work with just the essential package perl-base

# Check commandline options
my $work_on_backup = 0;
my $usage_text = <<"EOT";
Usage: $0 [-b]

$0 has no parameters and only these commandline flags:

-b          work on backup database "records.old" instead of "records".
-h, --help  show this help.

See https://github.com/rpodgorny/uptimed/issues/2,
https://github.com/rpodgorny/uptimed/pull/22 and
https://bugs.debian.org/654830 for details on the issue this script
has been written for.
EOT

if (@ARGV) {
    if ($ARGV[0] eq '--help' or $ARGV[0] eq '-h') {
        print "$0 -- Fix uptimed records db wrt. duplicate entries due to leap seconds\n\n".
            $usage_text;
        exit 0;
    }

    if ($#ARGV >= 1) {
        die "Error: too many arguments.\n\n".
            $usage_text;
    }

    if ($ARGV[0] ne '-b') {
        die "Error: unknown commandline option: $ARGV[0]\n\n".
            $usage_text;
    }

    if ($ARGV[0] eq '-b') {
        $work_on_backup = 1;
    }
}

# Check if we're running as root
if ($> != 0) {
    die "$0 needs to be run with super-user (root) permissions.\n";
}

# Constants taken from the 0.4.6 upstream release
my $boot_fuzz = 30; # seconds

# Cache / lookup variables
my @records = ();
my %record_by_boot_time = ();
my @ch_cmd;
my $last_boot_time = 0;
my $last_boot_record = '0:0:none';
my @last_boot_record = (0,0,'none');

# Filehandle variables
my $infh = undef;
my $outfh = undef;

# Spool directory and file
my $spooldir = '/var/spool/uptimed';
my $records = "$spooldir/records".($work_on_backup ? '.old' : '');
my $records_backup = "$records.fixup-duplicates-backup";

# Temporary file
my $mktemp_cmd = "mktemp $spooldir/uptime-records-fixup-XXXXXXXXXX";
my $tempfile = `$mktemp_cmd`;

# Check exit code of mktemp command
if    ($? == -1)       { die "\"$mktemp_cmd\" failed to execute: $!"; }
elsif ($? & 127)       { die "\"$mktemp_cmd\" died with signal ".($? & 127); }
elsif (($? >> 8) != 0) { die "\"$mktemp_cmd\" exited with value ".($? >> 8); }
chomp($tempfile);

# Then fixup the permissions of the temporary file.
@ch_cmd = ('chown', "--reference=$records", $tempfile);
system(@ch_cmd) == 0 or die '"'.join(' ', @ch_cmd)."\" failed: $?";

@ch_cmd = ('chmod', "--reference=$records", $tempfile);
system(@ch_cmd) == 0 or die '"'.join(' ', @ch_cmd)."\" failed: $?";

# Telling the use that we're going to fixup that records file
say "$0: Will fixup $records for https://bugs.debian.org/654830".
    "\nrespectively https://github.com/rpodgorny/uptimed/issues/2".
    "\nleftovers.";

# Read the original records file
open($infh, '<', $records) or die "Can't open $records: $!";
while (my $line = <$infh>) {
    # Store original line minus the trailing line break;
    chomp($line);
    push(@records, $line);

    # Extract the boot time and save
    my @line = split(/:/, $line);
    $record_by_boot_time{$line[1]} = $line;
}
close($infh);

# Sort boot times and check if there's any difference less than
# $boot_fuzz, but with an uprecord higher than $boot_fuzz (and the
# same kernel).
foreach my $boot_time (sort { $a <=> $b } keys %record_by_boot_time) {
    my $boot_record = $record_by_boot_time{$boot_time};
    my @boot_record = split(/:/, $boot_record);

    # Check for boot times close behind each other. Can only be
    # consecutive boot times, as we sorted the whole list.
    if ($last_boot_time + $boot_fuzz > $boot_time) {
        # Now cross-check if the uptime is bigger than $boot_fuzz
        if ($boot_record[0] > $boot_fuzz) {
            # Now compare the kernels
            if ($boot_record[2] eq $last_boot_record[2]) {
                say "Dropping near-duplicate uptimed entry \"$last_boot_record\".\n".
                    "                  (more recent entry: \"$boot_record\")";
                @records = grep { $_ ne $last_boot_record } @records;
                # Check for higher uptime record
                if ($last_boot_record[0] > $boot_record[0]) {
                    my $merged_boot_record = $boot_record;
                    # Re-add leap-seconds
                    my $fixed_uptime =
                        $last_boot_record[0] + ($boot_time - $last_boot_time);
                    $merged_boot_record =~
                        s/^$boot_record[0]:/$fixed_uptime:/;
                    $boot_record = $merged_boot_record;
                    @records =
                        map { s/^$boot_record[0]:/$fixed_uptime:/r }
                        @records;
                }
            } else {
                warn "Kernel differs between \"$last_boot_record\" and \"$boot_record\", not removed.\n";
            }
        } else {
            warn "Two close boots, but with very low uptime: $last_boot_record and $boot_record\", not removed.\n";
        }
    }
    $last_boot_time = $boot_time;
    $last_boot_record = $boot_record;
    @last_boot_record = @boot_record;
}

# Save the result as temporary file
#say $tempfile;
open($outfh, '>', $tempfile) or die "Can't write to $tempfile: $!";
foreach my $record (@records) {
    say $outfh $record or die "Couldn't write \"$record\" to $tempfile: $!";
}
close($outfh);

# Finally rename the original file and replace it with the new file.
if (! -e $records_backup) {
    rename($records, $records_backup)
        or die "Couldn't rename $records to $records_backup: $!";
}

rename($tempfile, $records)
    or die "Couldn't rename $tempfile to $records: $!";

say "$records backed up at $records_backup and replaced with fixed version.\n";