| 74 | rodolico | 1 | #! perl -w
 | 
        
           |  |  | 2 | # use strict; # eval doesn't work with use strict !?
 | 
        
           |  |  | 3 | # see POD documentation at end
 | 
        
           | 152 | rodolico | 4 | # Alternate versions at
 | 
        
           |  |  | 5 | # https://www.windowscentral.com/how-check-your-computer-uptime-windows-10
 | 
        
           | 74 | rodolico | 6 |   | 
        
           | 96 | rodolico | 7 |   | 
        
           |  |  | 8 |   | 
        
           | 74 | rodolico | 9 | =head1 NAME
 | 
        
           |  |  | 10 |   | 
        
           |  |  | 11 | uptime.pl - Uptime for Windows. Version 0.27.
 | 
        
           |  |  | 12 |   | 
        
           |  |  | 13 | =cut
 | 
        
           |  |  | 14 |   | 
        
           | 96 | rodolico | 15 | $^O eq "MSWin32" || exit;
 | 
        
           | 74 | rodolico | 16 |   | 
        
           | 101 | rodolico | 17 | our $VERSION = '0.1';
 | 
        
           |  |  | 18 |   | 
        
           | 74 | rodolico | 19 | my $max_results = 5; # if we have to use tick counts
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 | my $VERSION = 0.27;
 | 
        
           |  |  | 22 |   | 
        
           |  |  | 23 | my $debug = $ENV{'QUERY_STRING'} || $ARGV[0];
 | 
        
           |  |  | 24 | print "$0 version $VERSION\n" if $debug;
 | 
        
           |  |  | 25 | print "Debug mode on\n" if $debug;
 | 
        
           |  |  | 26 | print "Perl version $]\n" if $debug;
 | 
        
           |  |  | 27 |   | 
        
           |  |  | 28 | BEGIN {
 | 
        
           | 96 | rodolico | 29 |    # HTTP headers if needed
 | 
        
           |  |  | 30 |    local $^W = 0;
 | 
        
           |  |  | 31 |    print "$ENV{'SERVER_PROTOCOL'}/200 OK\n" if $ENV{'PERLXS'} eq "PerlIS";
 | 
        
           |  |  | 32 |    print "Content-type: text/plain\n\n" if $ENV{'SERVER_PROTOCOL'};
 | 
        
           | 74 | rodolico | 33 | }
 | 
        
           |  |  | 34 |   | 
        
           |  |  | 35 | my ($several, @uptimes);
 | 
        
           |  |  | 36 |   | 
        
           |  |  | 37 | if (Win32::IsWinNT()) {
 | 
        
           | 96 | rodolico | 38 |    print "Windows NT\n\n" if $debug;
 | 
        
           |  |  | 39 |    push @uptimes, (&event_log || &tick_counts);
 | 
        
           |  |  | 40 |    print "eval error: $@\n" if $@ && $debug;
 | 
        
           | 74 | rodolico | 41 | }
 | 
        
           |  |  | 42 | else {
 | 
        
           | 96 | rodolico | 43 |    print "Windows 9x\n\n" if $debug;
 | 
        
           |  |  | 44 |    push @uptimes, (&system_da0 || &tick_counts);
 | 
        
           | 74 | rodolico | 45 | }
 | 
        
           |  |  | 46 |   | 
        
           |  |  | 47 | sub tick_counts {
 | 
        
           | 96 | rodolico | 48 |    print "Counting ticks\n" if $debug;
 | 
        
           |  |  | 49 |    my @ticks;
 | 
        
           |  |  | 50 |    my $ticks = Win32::GetTickCount() > 0
 | 
        
           |  |  | 51 |             ? Win32::GetTickCount()
 | 
        
           |  |  | 52 |            : Win32::GetTickCount() + 2**32;
 | 
        
           |  |  | 53 |    my $seconds = $ticks/1000;
 | 
        
           |  |  | 54 |    for (1..$max_results-1) {
 | 
        
           |  |  | 55 |       push @ticks, time()-$seconds;
 | 
        
           |  |  | 56 |       $seconds += 2**32/1000;
 | 
        
           |  |  | 57 |    }
 | 
        
           |  |  | 58 |    return @ticks, time()-$seconds;
 | 
        
           | 74 | rodolico | 59 | }
 | 
        
           |  |  | 60 |   | 
        
           |  |  | 61 | sub system_da0 {
 | 
        
           | 96 | rodolico | 62 |    my $file = "$ENV{'WINDIR'}\\system.da0";
 | 
        
           |  |  | 63 |    print "Checking $file\n" if $debug;
 | 
        
           |  |  | 64 |    my $stat = (stat $file)[9]; 
 | 
        
           |  |  | 65 |    print "Could not stat $file ($!)\n" if $debug && !$stat;
 | 
        
           |  |  | 66 |    return $stat || undef;
 | 
        
           | 74 | rodolico | 67 | }
 | 
        
           |  |  | 68 |   | 
        
           |  |  | 69 | sub event_log {
 | 
        
           | 96 | rodolico | 70 |    my $result = eval '
 | 
        
           | 74 | rodolico | 71 |         local $^W = 0;
 | 
        
           | 96 | rodolico | 72 |       use Win32::EventLog;
 | 
        
           |  |  | 73 |       my ($EventLog, $first, $count, $event, %data);
 | 
        
           |  |  | 74 |       Win32::EventLog::Open($EventLog , "System", "") || die ("EventLog Open() failed");
 | 
        
           |  |  | 75 |       $EventLog->GetOldest($first) || die ("EventLog GetOldest() failed");
 | 
        
           |  |  | 76 |       $EventLog->GetNumber($count) || die ("EventLog GetNumber() failed");
 | 
        
           |  |  | 77 |       print "Event log first=$first, count=$count\n" if $debug;
 | 
        
           | 74 | rodolico | 78 |   | 
        
           | 96 | rodolico | 79 |       $EventLog->Read((EVENTLOG_SEEK_READ | EVENTLOG_BACKWARDS_READ),$first+$count,$event);
 | 
        
           | 74 | rodolico | 80 |   | 
        
           | 96 | rodolico | 81 |       for $i (0 .. $first+$count-1) {
 | 
        
           |  |  | 82 |          $EventLog->Read((EVENTLOG_SEQUENTIAL_READ|EVENTLOG_BACKWARDS_READ),0,$event)
 | 
        
           |  |  | 83 |              || die ("EventLog Read() failed at event $i");
 | 
        
           | 74 | rodolico | 84 |   | 
        
           | 96 | rodolico | 85 |          %data = %{$event};
 | 
        
           |  |  | 86 |          $data{"EventID"} = $data{"EventID"} & 0xffff;
 | 
        
           | 74 | rodolico | 87 |   | 
        
           | 96 | rodolico | 88 |           next unless $data{"EventID"} == 6005;
 | 
        
           |  |  | 89 |          print "Found event 6005\n" if $debug;
 | 
        
           |  |  | 90 |          return $data{"TimeGenerated"};
 | 
        
           |  |  | 91 |          print "This script is broken: it should never reach this line\n";
 | 
        
           |  |  | 92 |       }
 | 
        
           |  |  | 93 |       return undef;
 | 
        
           |  |  | 94 |    ';
 | 
        
           |  |  | 95 |    if ($@) {
 | 
        
           |  |  | 96 |       print "Eval error: $@\n";
 | 
        
           |  |  | 97 |       return undef;
 | 
        
           |  |  | 98 |    }
 | 
        
           |  |  | 99 |    else {
 | 
        
           |  |  | 100 |       return $result;
 | 
        
           |  |  | 101 |    }
 | 
        
           | 74 | rodolico | 102 | }
 | 
        
           |  |  | 103 |   | 
        
           |  |  | 104 | $several = @uptimes - 1;
 | 
        
           |  |  | 105 | foreach (@uptimes) {
 | 
        
           | 96 | rodolico | 106 |    print "up ", &time2days($_), " (since ", scalar localtime($_), ")\n";
 | 
        
           |  |  | 107 |    print "or:\n" if $several;
 | 
        
           | 74 | rodolico | 108 | }
 | 
        
           |  |  | 109 |   | 
        
           |  |  | 110 | print "... but who would believe that anyway?...\n" if $several;
 | 
        
           |  |  | 111 |   | 
        
           |  |  | 112 | sub time2days {
 | 
        
           | 96 | rodolico | 113 |    print "converting $_[0]\n" if $debug;
 | 
        
           |  |  | 114 |    my $days = (time() - $_[0])/(24*60*60);
 | 
        
           |  |  | 115 |    my $hours = ($days - int($days)) * 24;
 | 
        
           |  |  | 116 |    my $minutes = ($hours - int($hours)) * 60;
 | 
        
           |  |  | 117 |    my $day_st = $days >= 2 ? 'days' : 'day';
 | 
        
           |  |  | 118 |    return sprintf("%0d $day_st %02d:%02d", $days, $hours, $minutes);
 | 
        
           | 74 | rodolico | 119 | }
 | 
        
           |  |  | 120 |   | 
        
           |  |  | 121 | __END__
 | 
        
           |  |  | 122 |   | 
        
           |  |  | 123 | =head1 SYNOPSIS
 | 
        
           |  |  | 124 |   | 
        
           |  |  | 125 | perl uptime.pl [debug]
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 | =head1 DESCRIPTION
 | 
        
           |  |  | 128 |   | 
        
           |  |  | 129 | Report machine uptime on Windows systems.
 | 
        
           |  |  | 130 |   | 
        
           |  |  | 131 | This script attempts to report the system uptime in a format similar
 | 
        
           |  |  | 132 | to the Unix uptime command. It only reports uptime, not users or
 | 
        
           |  |  | 133 | load statistics.
 | 
        
           |  |  | 134 |   | 
        
           |  |  | 135 | On NT it uses the event log. The event log service itself writes an
 | 
        
           |  |  | 136 | entry to it when it starts (ID 6005).
 | 
        
           |  |  | 137 |   | 
        
           |  |  | 138 | On Win95 it gets the create time of the system.da0 file (the system.dat
 | 
        
           |  |  | 139 | backup file which is recreated at system start).
 | 
        
           |  |  | 140 |   | 
        
           |  |  | 141 | If eihter of these methods fail (like on Win98), it uses the system tick 
 | 
        
           |  |  | 142 | counts. Since these can only hold about 49.7 days, it returns a list of 
 | 
        
           |  |  | 143 | possible uptimes, limited to C<$max_results> (5 by default). (Have you ever 
 | 
        
           |  |  | 144 | seen a Windows system up for more than 200 days? ;-) If yes, set 
 | 
        
           |  |  | 145 | C<$max_results> to a higher value).
 | 
        
           |  |  | 146 |   | 
        
           |  |  | 147 | The script can be used in CGI. It will print the needed headers automatically.
 | 
        
           |  |  | 148 |   | 
        
           |  |  | 149 | The latest version should always be available at
 | 
        
           |  |  | 150 | http://alma.ch/perl/scripts/uptime.pl
 | 
        
           |  |  | 151 |   | 
        
           |  |  | 152 | =head1 OPTIONS
 | 
        
           |  |  | 153 |   | 
        
           |  |  | 154 | If given any argument (at the command line or as a CGI argument), it is
 | 
        
           |  |  | 155 | considered as a 'debug' or 'verbose' option, and additional stuff is printed.
 | 
        
           |  |  | 156 |   | 
        
           |  |  | 157 | =head1 FEATURES (aka BUGS)
 | 
        
           |  |  | 158 |   | 
        
           |  |  | 159 | On NT, the uptime will be wrong if you restarted the event log service.
 | 
        
           |  |  | 160 |   | 
        
           |  |  | 161 | If you have a low limit on your event log file size or logged events, and
 | 
        
           |  |  | 162 | your system has been up for a long time, the event may not be available
 | 
        
           |  |  | 163 | anymore. The script will then use tick counts.
 | 
        
           |  |  | 164 |   | 
        
           |  |  | 165 | On Win95, the uptime will be wrong if the create time of your system.da0 file
 | 
        
           |  |  | 166 | is not the same as when your system booted. I don't see what could cause this
 | 
        
           |  |  | 167 | unless you overwrite the file, but there may be special cases. Let me know if
 | 
        
           |  |  | 168 | you find any.
 | 
        
           |  |  | 169 |   | 
        
           |  |  | 170 | On Win98, there seems to be no way other than tick counts. Let me know if you
 | 
        
           |  |  | 171 | find something better.
 | 
        
           |  |  | 172 |   | 
        
           |  |  | 173 | =head1 SCRIPT CATEGORIES
 | 
        
           |  |  | 174 |   | 
        
           |  |  | 175 | Win32
 | 
        
           |  |  | 176 |   | 
        
           |  |  | 177 | =head1 OSNAMES
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 | MSWin32
 | 
        
           |  |  | 180 |   | 
        
           |  |  | 181 | =head1 AUTHOR
 | 
        
           |  |  | 182 |   | 
        
           |  |  | 183 | M. Ivkovic. email: C<perl -e "printf '%s@%s', 'mi.perl', 'alma.ch'">.
 | 
        
           |  |  | 184 |   | 
        
           |  |  | 185 | Others welcome to extend it to more operating systems which don't have an uptime 
 | 
        
           |  |  | 186 | command.
 | 
        
           |  |  | 187 |   | 
        
           |  |  | 188 | =head1 COPYRIGHT
 | 
        
           |  |  | 189 |   | 
        
           |  |  | 190 | Copyright M. Ivkovic, 1999. Same license as Perl itself.
 | 
        
           |  |  | 191 |   | 
        
           |  |  | 192 | =head1 README
 | 
        
           |  |  | 193 |   | 
        
           |  |  | 194 | Attempt to report machine uptime on various Windows systems.
 | 
        
           |  |  | 195 |   | 
        
           |  |  | 196 | =cut
 |