1#!perl 2use strict; 3use warnings; 4use File::Basename; 5use File::Copy; 6use File::Path; 7 8my $name = shift || 'PerlLog'; 9 10# get the version from the message file 11open(my $msgfh, '<', "$name.mc") or die "fatal: Can't read file '$name.mc': $!\n"; 12my $top = <$msgfh>; 13close($msgfh); 14 15my ($version) = $top =~ /Sys::Syslog Message File (\d+\.\d+\.\d+)/ 16 or die "error: File '$name.mc' doesn't have a version number\n"; 17 18# compile the message text files 19system("mc -d $name.mc"); 20system("rc $name.rc"); 21system(qq{ link -nodefaultlib -incremental:no -release /nologo -base:0x60000000 } 22 .qq{ -comment:"Perl Syslog Message File v$version" } 23 .qq{ -machine:i386 -dll -noentry -out:$name.dll $name.res }); 24 25# uuencode the resource file 26open(my $rsrc, '<', "$name.RES") or die "fatal: Can't read resource file '$name.RES': $!"; 27binmode($rsrc); 28my $uudata = pack "u", do { local $/; <$rsrc> }; 29close($rsrc); 30 31open(my $uufh, '>', "$name\_RES.uu") or die "fatal: Can't write file '$name\_RES.uu': $!"; 32print $uufh $uudata; 33close($uufh); 34 35# uuencode the DLL 36open(my $dll, '<', "$name.dll") or die "fatal: Can't read DLL '$name.dll': $!"; 37binmode($dll); 38$uudata = pack "u", do { local $/; <$dll> }; 39close($dll); 40 41open($uufh, '>', "$name\_dll.uu") or die "fatal: Can't write file '$name\_dll.uu': $!"; 42print $uufh $uudata; 43close($uufh); 44 45# parse the generated header to extract the constants 46open(my $header, '<', "$name.h") or die "fatal: Can't read header file '$name.h': $!"; 47my %vals; 48my $max = 0; 49 50while (<$header>) { 51 if (/^#define\s+(\w+)\s+(\d+)$/ || /^#define\s+(\w+)\s+\(\(DWORD\)(\d+)L\)/) { 52 $vals{$1} = $2; 53 if (substr($1, 0, 1) eq 'C') { 54 $max = $2 if $max < $2; 55 } 56 } 57} 58 59close($header); 60 61my ($hash, $f2c, %fac); 62 63for my $name (sort { substr($a,0,1) cmp substr($b,0,1) || $vals{$a} <=> $vals{$b} } keys %vals) { 64 $hash .= " $name => $vals{$name},\n" ; 65 if ($name =~ /^CAT_(\w+)$/) { 66 $fac{$1} = $vals{$name}; 67 } 68} 69 70for my $name (sort {$fac{$a} <=> $fac{$b}} keys %fac) { 71 $f2c .= " Sys::Syslog::LOG_$name() => '$name',\n"; 72} 73 74# write the Sys::Syslog::Win32 module 75open my $out, '>', "Win32.pm" or die "fatal: Can't write Win32.pm: $!"; 76my $template = join '', <DATA>; 77$template =~ s/__CONSTANT__/$hash/; 78$template =~ s/__F2C__/$f2c/; 79$template =~ s/__NAME_VER__/$name/; 80$template =~ s/__VER__/$version/; 81$max = sprintf "0x%08x", $max; 82$template =~ s/__MAX__/'$max'/g; 83$template =~ s/__TIME__/localtime()/ge; 84print $out $template; 85close $out; 86print "Updated Win32.pm and relevent message files\n"; 87 88__END__ 89package Sys::Syslog::Win32; 90use strict; 91use warnings; 92use Carp; 93use File::Spec; 94 95# === WARNING === WARNING === WARNING === WARNING === WARNING === WARNING === 96# 97# This file was generated by Sys-Syslog/win32/compile.pl on __TIME__ 98# Any changes being made here will be lost the next time Sys::Syslog 99# is installed. 100# 101# Do NOT USE THIS MODULE DIRECTLY: this is a utility module for Sys::Syslog. 102# It may change at any time to fit the needs of Sys::Syslog therefore no 103# warranty is made WRT to its API. You Have Been Warned. 104# 105# === WARNING === WARNING === WARNING === WARNING === WARNING === WARNING === 106 107our $Source; 108my $logger; 109my $Registry; 110 111use Win32::EventLog; 112use Win32::TieRegistry 0.20 ( 113 TiedRef => \$Registry, 114 Delimiter => "/", 115 ArrayValues => 1, 116 SplitMultis => 1, 117 AllowLoad => 1, 118 qw( 119 REG_SZ 120 REG_EXPAND_SZ 121 REG_DWORD 122 REG_BINARY 123 REG_MULTI_SZ 124 KEY_READ 125 KEY_WRITE 126 KEY_ALL_ACCESS 127 ), 128); 129 130my $is_Cygwin = $^O =~ /Cygwin/i; 131my $is_Win32 = $^O =~ /Win32/i; 132 133my %const = ( 134__CONSTANT__ 135); 136 137my %id2name = ( 138__F2C__ 139); 140 141my @priority2eventtype = ( 142 EVENTLOG_ERROR_TYPE(), # LOG_EMERG 143 EVENTLOG_ERROR_TYPE(), # LOG_ALERT 144 EVENTLOG_ERROR_TYPE(), # LOG_CRIT 145 EVENTLOG_ERROR_TYPE(), # LOG_ERR 146 EVENTLOG_WARNING_TYPE(), # LOG_WARNING 147 EVENTLOG_WARNING_TYPE(), # LOG_NOTICE 148 EVENTLOG_INFORMATION_TYPE(), # LOG_INFO 149 EVENTLOG_INFORMATION_TYPE(), # LOG_DEBUG 150); 151 152 153# 154# _install() 155# -------- 156# Used to set up a connection to the eventlog. 157# 158sub _install { 159 return $logger if $logger; 160 161 # can't just use basename($0) here because Win32 path often are a 162 # a mix of / and \, and File::Basename::fileparse() can't handle that, 163 # while File::Spec::splitpath() can.. Go figure.. 164 my (undef, undef, $basename) = File::Spec->splitpath($0); 165 ($Source) ||= $basename; 166 167 $Source.=" [SSW:__VER__]"; 168 169 #$Registry->Delimiter("/"); # is this needed? 170 my $root = 'LMachine/SYSTEM/CurrentControlSet/Services/Eventlog/Application/'; 171 my $dll = 'Sys/Syslog/__NAME_VER__.dll'; 172 173 if (!$Registry->{$root.$Source} || 174 !$Registry->{$root.$Source.'/CategoryMessageFile'}[0] || 175 !-e $Registry->{$root.$Source.'/CategoryMessageFile'}[0] ) 176 { 177 178 # find the resource DLL, which should be along Syslog.dll 179 my ($file) = grep { -e $_ } map { ("$_/$dll" => "$_/auto/$dll") } @INC; 180 $dll = $file if $file; 181 182 # on Cygwin, convert the Unix path into absolute Windows path 183 if ($is_Cygwin) { 184 if ($] > 5.009005) { 185 chomp($file = Cygwin::posix_to_win_path($file, 1)); 186 } 187 else { 188 local $ENV{PATH} = ''; 189 chomp($dll = `/usr/bin/cygpath --absolute --windows "$dll"`); 190 } 191 } 192 193 $dll =~ s![\\/]+!\\!g; # must be backslashes! 194 die "fatal: Can't find resource DLL for Sys::Syslog\n" if !$dll; 195 196 $Registry->{$root.$Source} = { 197 '/EventMessageFile' => [ $dll, REG_EXPAND_SZ ], 198 '/CategoryMessageFile' => [ $dll, REG_EXPAND_SZ ], 199 '/CategoryCount' => [ __MAX__, REG_DWORD ], 200 #'/TypesSupported' => [ __MAX__, REG_DWORD ], 201 }; 202 203 warn "Configured eventlog to use $dll for $Source\n" if $Sys::Syslog::DEBUG; 204 } 205 206 #Carp::confess("Registry has the wrong value for '$Source', possibly mismatched dll!\nMine:$dll\nGot :$Registry->{$root.$Source.'/CategoryMessageFile'}[0]\n") 207 # if $Registry->{$root.$Source.'/CategoryMessageFile'}[0] ne $dll; 208 209 # we really should do something useful with this but for now 210 # we set it to "" to prevent Win32::EventLog from warning 211 my $host = ""; 212 213 $logger = Win32::EventLog->new($Source, $host) 214 or Carp::confess("Failed to connect to the '$Source' event log"); 215 216 return $logger; 217} 218 219 220# 221# _syslog_send() 222# ------------ 223# Used to convert syslog messages into eventlog messages 224# 225sub _syslog_send { 226 my ($buf, $numpri, $numfac) = @_; 227 $numpri ||= EVENTLOG_INFORMATION_TYPE(); 228 $numfac ||= Sys::Syslog::LOG_USER(); 229 my $name = $id2name{$numfac}; 230 231 my $opts = { 232 EventType => $priority2eventtype[$numpri], 233 EventID => $const{"MSG_$name"}, 234 Category => $const{"CAT_$name"}, 235 Strings => "$buf\0", 236 Data => "", 237 }; 238 239 if ($Sys::Syslog::DEBUG) { 240 require Data::Dumper; 241 warn Data::Dumper->Dump( 242 [$numpri, $numfac, $name, $opts], 243 [qw(numpri numfac name opts)] 244 ); 245 } 246 247 return $logger->Report($opts); 248} 249 250 251=head1 NAME 252 253Sys::Syslog::Win32 - Win32 support for Sys::Syslog 254 255=head1 DESCRIPTION 256 257This module is a back-end plugin for C<Sys::Syslog>, for supporting the Win32 258event log. It is not expected to be directly used by any module other than 259C<Sys::Syslog> therefore it's API may change at any time and no warranty is 260made with regards to backward compatibility. You Have Been Warned. 261 262=head1 SEE ALSO 263 264L<Sys::Syslog> 265 266=head1 AUTHORS 267 268SE<eacute>bastien Aperghis-Tramoni and Yves Orton 269 270=head1 LICENSE 271 272This program is free software; you can redistribute it and/or modify it 273under the same terms as Perl itself. 274 275=cut 276 2771; 278