home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / perl / 5.8.8 / Sys / Syslog.pm < prev   
Encoding:
Perl POD Document  |  2006-07-07  |  24.6 KB  |  974 lines

  1. package Sys::Syslog;
  2. use strict;
  3. use Carp;
  4. require 5.006;
  5. require Exporter;
  6.  
  7. our $VERSION = '0.13';
  8. our @ISA = qw(Exporter);
  9.  
  10. our %EXPORT_TAGS = (
  11.     standard => [qw(openlog syslog closelog setlogmask)],
  12.     extended => [qw(setlogsock)],
  13.     macros => [qw(
  14.         LOG_ALERT LOG_AUTH LOG_AUTHPRIV LOG_CONS LOG_CRIT LOG_CRON
  15.         LOG_DAEMON LOG_DEBUG LOG_EMERG LOG_ERR LOG_FACMASK LOG_FTP
  16.         LOG_INFO LOG_KERN LOG_LFMT LOG_LOCAL0 LOG_LOCAL1 LOG_LOCAL2
  17.         LOG_LOCAL3 LOG_LOCAL4 LOG_LOCAL5 LOG_LOCAL6 LOG_LOCAL7 LOG_LPR
  18.         LOG_MAIL LOG_NDELAY LOG_NEWS LOG_NFACILITIES LOG_NOTICE
  19.         LOG_NOWAIT LOG_ODELAY LOG_PERROR LOG_PID LOG_PRIMASK LOG_SYSLOG
  20.         LOG_USER LOG_UUCP LOG_WARNING
  21.     )],
  22. );
  23.  
  24. our @EXPORT = (
  25.     @{$EXPORT_TAGS{standard}}, 
  26. );
  27.  
  28. our @EXPORT_OK = (
  29.     @{$EXPORT_TAGS{extended}}, 
  30.     @{$EXPORT_TAGS{macros}}, 
  31. );
  32.  
  33. # it would be nice to try stream/unix first, since that will be
  34. # most efficient. However streams are dodgy - see _syslog_send_stream
  35. my @connectMethods = ( 'tcp', 'udp', 'unix', 'stream', 'console' );
  36. if ($^O =~ /^(freebsd|linux)$/) {
  37.     @connectMethods = grep { $_ ne 'udp' } @connectMethods;
  38. }
  39. my @defaultMethods = @connectMethods;
  40. my $syslog_path = undef;
  41. my $transmit_ok = 0;
  42. my $current_proto = undef;
  43. my $failed = undef;
  44. my $fail_time = undef;
  45. our ($connected, @fallbackMethods, $syslog_send, $host);
  46.  
  47. use Socket ':all';
  48. use POSIX qw(strftime setlocale LC_TIME);
  49.  
  50. =head1 NAME
  51.  
  52. Sys::Syslog - Perl interface to the UNIX syslog(3) calls
  53.  
  54. =head1 VERSION
  55.  
  56. Version 0.13
  57.  
  58. =head1 SYNOPSIS
  59.  
  60.     use Sys::Syslog;                          # all except setlogsock(), or:
  61.     use Sys::Syslog qw(:DEFAULT setlogsock);  # default set, plus setlogsock()
  62.     use Sys::Syslog qw(:standard :macros);    # standard functions, plus macros
  63.  
  64.     setlogsock $sock_type;
  65.     openlog $ident, $logopt, $facility;       # don't forget this
  66.     syslog $priority, $format, @args;
  67.     $oldmask = setlogmask $mask_priority;
  68.     closelog;
  69.  
  70.  
  71. =head1 DESCRIPTION
  72.  
  73. C<Sys::Syslog> is an interface to the UNIX C<syslog(3)> program.
  74. Call C<syslog()> with a string priority and a list of C<printf()> args
  75. just like C<syslog(3)>.
  76.  
  77.  
  78. =head1 EXPORTS
  79.  
  80. C<Sys::Syslog> exports the following C<Exporter> tags: 
  81.  
  82. =over 4
  83.  
  84. =item *
  85.  
  86. C<:standard> exports the standard C<syslog(3)> functions: 
  87.  
  88.     openlog closelog setlogmask syslog
  89.  
  90. =item *
  91.  
  92. C<:extended> exports the Perl specific functions for C<syslog(3)>: 
  93.  
  94.     setlogsock
  95.  
  96. =item *
  97.  
  98. C<:macros> exports the symbols corresponding to most of your C<syslog(3)> 
  99. macros. See L<"CONSTANTS"> for the supported constants and their meaning. 
  100.  
  101. =back
  102.  
  103. By default, C<Sys::Syslog> exports the symbols from the C<:standard> tag. 
  104.  
  105.  
  106. =head1 FUNCTIONS
  107.  
  108. =over 4
  109.  
  110. =item B<openlog($ident, $logopt, $facility)>
  111.  
  112. Opens the syslog.
  113. C<$ident> is prepended to every message.  C<$logopt> contains zero or
  114. more of the words C<pid>, C<ndelay>, C<nowait>.  The C<cons> option is
  115. ignored, since the failover mechanism will drop down to the console
  116. automatically if all other media fail.  C<$facility> specifies the
  117. part of the system to report about, for example C<LOG_USER> or C<LOG_LOCAL0>:
  118. see your C<syslog(3)> documentation for the facilities available in
  119. your system. Facility can be given as a string or a numeric macro. 
  120.  
  121. This function will croak if it can't connect to the syslog daemon.
  122.  
  123. Note that C<openlog()> now takes three arguments, just like C<openlog(3)>.
  124.  
  125. B<You should use openlog() before calling syslog().>
  126.  
  127. B<Options>
  128.  
  129. =over 4
  130.  
  131. =item *
  132.  
  133. C<ndelay> - Open the connection immediately (normally, the connection is
  134. opened when the first message is logged).
  135.  
  136. =item *
  137.  
  138. C<nowait> - Don't wait for child processes that may have been created 
  139. while logging the message.  (The GNU C library does not create a child
  140. process, so this option has no effect on Linux.)
  141.  
  142. =item *
  143.  
  144. C<pid> - Include PID with each message.
  145.  
  146. =back
  147.  
  148. B<Examples>
  149.  
  150. Open the syslog with options C<ndelay> and C<pid>, and with facility C<LOCAL0>: 
  151.  
  152.     openlog($name, "ndelay,pid", "local0");
  153.  
  154. Same thing, but this time using the macro corresponding to C<LOCAL0>: 
  155.  
  156.     openlog($name, "ndelay,pid", LOG_LOCAL0);
  157.  
  158.  
  159. =item B<syslog($priority, $message)>
  160.  
  161. =item B<syslog($priority, $format, @args)>
  162.  
  163. If C<$priority> permits, logs C<$message> or C<sprintf($format, @args)>
  164. with the addition that C<%m> in $message or C<$format> is replaced with
  165. C<"$!"> (the latest error message). 
  166.  
  167. C<$priority> can specify a level, or a level and a facility.  Levels and 
  168. facilities can be given as strings or as macros.
  169.  
  170. If you didn't use C<openlog()> before using C<syslog()>, C<syslog()> will 
  171. try to guess the C<$ident> by extracting the shortest prefix of 
  172. C<$format> that ends in a C<":">.
  173.  
  174. B<Examples>
  175.  
  176.     syslog("info", $message);           # informational level
  177.     syslog(LOG_INFO, $message);         # informational level
  178.  
  179.     syslog("info|local0", $message);        # information level, Local0 facility
  180.     syslog(LOG_INFO|LOG_LOCAL0, $message);  # information level, Local0 facility
  181.  
  182. =over 4
  183.  
  184. =item B<Note>
  185.  
  186. C<Sys::Syslog> version v0.07 and older passed the C<$message> as the 
  187. formatting string to C<sprintf()> even when no formatting arguments
  188. were provided.  If the code calling C<syslog()> might execute with 
  189. older versions of this module, make sure to call the function as
  190. C<syslog($priority, "%s", $message)> instead of C<syslog($priority,
  191. $message)>.  This protects against hostile formatting sequences that
  192. might show up if $message contains tainted data.
  193.  
  194. =back
  195.  
  196.  
  197. =item B<setlogmask($mask_priority)>
  198.  
  199. Sets the log mask for the current process to C<$mask_priority> and 
  200. returns the old mask.  If the mask argument is 0, the current log mask 
  201. is not modified.  See L<"Levels"> for the list of available levels. 
  202.  
  203. B<Examples>
  204.  
  205. Only log errors: 
  206.  
  207.     setlogmask(LOG_ERR);
  208.  
  209. Log critical messages, errors and warnings: 
  210.  
  211.     setlogmask(LOG_CRIT|LOG_ERR|LOG_WARNING);
  212.  
  213.  
  214. =item B<setlogsock($sock_type)>
  215.  
  216. =item B<setlogsock($sock_type, $stream_location)> (added in 5.004_02)
  217.  
  218. Sets the socket type to be used for the next call to
  219. C<openlog()> or C<syslog()> and returns true on success,
  220. C<undef> on failure.
  221.  
  222. A value of C<"unix"> will connect to the UNIX domain socket (in some
  223. systems a character special device) returned by the C<_PATH_LOG> macro
  224. (if your system defines it), or F</dev/log> or F</dev/conslog>,
  225. whatever is writable.  A value of 'stream' will connect to the stream
  226. indicated by the pathname provided as the optional second parameter.
  227. (For example Solaris and IRIX require C<"stream"> instead of C<"unix">.)
  228. A value of C<"inet"> will connect to an INET socket (either C<tcp> or C<udp>,
  229. tried in that order) returned by C<getservbyname()>. C<"tcp"> and C<"udp"> can
  230. also be given as values. The value C<"console"> will send messages
  231. directly to the console, as for the C<"cons"> option in the logopts in
  232. C<openlog()>.
  233.  
  234. A reference to an array can also be passed as the first parameter.
  235. When this calling method is used, the array should contain a list of
  236. sock_types which are attempted in order.
  237.  
  238. The default is to try C<tcp>, C<udp>, C<unix>, C<stream>, C<console>.
  239.  
  240. Giving an invalid value for C<$sock_type> will croak.
  241.  
  242.  
  243. =item B<closelog()>
  244.  
  245. Closes the log file and return true on success.
  246.  
  247. =back
  248.  
  249.  
  250. =head1 EXAMPLES
  251.  
  252.     openlog($program, 'cons,pid', 'user');
  253.     syslog('info', '%s', 'this is another test');
  254.     syslog('mail|warning', 'this is a better test: %d', time);
  255.     closelog();
  256.  
  257.     syslog('debug', 'this is the last test');
  258.  
  259.     setlogsock('unix');
  260.     openlog("$program $$", 'ndelay', 'user');
  261.     syslog('notice', 'fooprogram: this is really done');
  262.  
  263.     setlogsock('inet');
  264.     $! = 55;
  265.     syslog('info', 'problem was %m'); # %m == $! in syslog(3)
  266.  
  267.     # Log to UDP port on $remotehost instead of logging locally
  268.     setlogsock('udp');
  269.     $Sys::Syslog::host = $remotehost;
  270.     openlog($program, 'ndelay', 'user');
  271.     syslog('info', 'something happened over here');
  272.  
  273.  
  274. =head1 CONSTANTS
  275.  
  276. =head2 Facilities
  277.  
  278. =over 4
  279.  
  280. =item *
  281.  
  282. C<LOG_AUTH> - security/authorization messages
  283.  
  284. =item *
  285.  
  286. C<LOG_AUTHPRIV> - security/authorization messages (private)
  287.  
  288. =item *
  289.  
  290. C<LOG_CRON> - clock daemon (B<cron> and B<at>)
  291.  
  292. =item *
  293.  
  294. C<LOG_DAEMON> - system daemons without separate facility value
  295.  
  296. =item *
  297.  
  298. C<LOG_FTP> - ftp daemon
  299.  
  300. =item *
  301.  
  302. C<LOG_KERN> - kernel messages
  303.  
  304. =item *
  305.  
  306. C<LOG_LOCAL0> through C<LOG_LOCAL7> - reserved for local use
  307.  
  308. =item *
  309.  
  310. C<LOG_LPR> - line printer subsystem
  311.  
  312. =item *
  313.  
  314. C<LOG_MAIL> - mail subsystem
  315.  
  316. =item *
  317.  
  318. C<LOG_NEWS> - USENET news subsystem
  319.  
  320. =item *
  321.  
  322. C<LOG_SYSLOG> - messages generated internally by B<syslogd>
  323.  
  324. =item *
  325.  
  326. C<LOG_USER> (default) - generic user-level messages
  327.  
  328. =item *
  329.  
  330. C<LOG_UUCP> - UUCP subsystem
  331.  
  332. =back
  333.  
  334.  
  335. =head2 Levels
  336.  
  337. =over 4
  338.  
  339. =item *
  340.  
  341. C<LOG_EMERG> - system is unusable
  342.  
  343. =item *
  344.  
  345. C<LOG_ALERT> - action must be taken immediately
  346.  
  347. =item *
  348.  
  349. C<LOG_CRIT> - critical conditions
  350.  
  351. =item *
  352.  
  353. C<LOG_ERR> - error conditions
  354.  
  355. =item *
  356.  
  357. C<LOG_WARNING> - warning conditions
  358.  
  359. =item *
  360.  
  361. C<LOG_NOTICE> - normal, but significant, condition
  362.  
  363. =item *
  364.  
  365. C<LOG_INFO> - informational message
  366.  
  367. =item *
  368.  
  369. C<LOG_DEBUG> - debug-level message
  370.  
  371. =back
  372.  
  373.  
  374. =head1 DIAGNOSTICS
  375.  
  376. =over 4
  377.  
  378. =item Invalid argument passed to setlogsock
  379.  
  380. B<(F)> You gave C<setlogsock()> an invalid value for C<$sock_type>. 
  381.  
  382. =item no connection to syslog available
  383.  
  384. B<(F)> C<syslog()> failed to connect to the specified socket.
  385.  
  386. =item stream passed to setlogsock, but %s is not writable
  387.  
  388. B<(W)> You asked C<setlogsock()> to use a stream socket, but the given 
  389. path is not writable. 
  390.  
  391. =item stream passed to setlogsock, but could not find any device
  392.  
  393. B<(W)> You asked C<setlogsock()> to use a stream socket, but didn't 
  394. provide a path, and C<Sys::Syslog> was unable to find an appropriate one.
  395.  
  396. =item tcp passed to setlogsock, but tcp service unavailable
  397.  
  398. B<(W)> You asked C<setlogsock()> to use a TCP socket, but the service 
  399. is not available on the system. 
  400.  
  401. =item syslog: expecting argument %s
  402.  
  403. B<(F)> You forgot to give C<syslog()> the indicated argument.
  404.  
  405. =item syslog: invalid level/facility: %s
  406.  
  407. B<(F)> You specified an invalid level or facility, like C<LOG_KERN> 
  408. (which is reserved to the kernel). 
  409.  
  410. =item syslog: too many levels given: %s
  411.  
  412. B<(F)> You specified too many levels. 
  413.  
  414. =item syslog: too many facilities given: %s
  415.  
  416. B<(F)> You specified too many facilities. 
  417.  
  418. =item syslog: level must be given
  419.  
  420. B<(F)> You forgot to specify a level.
  421.  
  422. =item udp passed to setlogsock, but udp service unavailable
  423.  
  424. B<(W)> You asked C<setlogsock()> to use a UDP socket, but the service 
  425. is not available on the system. 
  426.  
  427. =item unix passed to setlogsock, but path not available
  428.  
  429. B<(W)> You asked C<setlogsock()> to use a UNIX socket, but C<Sys::Syslog> 
  430. was unable to find an appropriate an appropriate device.
  431.  
  432. =back
  433.  
  434.  
  435. =head1 SEE ALSO
  436.  
  437. L<syslog(3)>
  438.  
  439. I<Syslogging with Perl>, L<http://lexington.pm.org/meetings/022001.html>
  440.  
  441.  
  442. =head1 AUTHOR
  443.  
  444. Tom Christiansen E<lt>F<tchrist@perl.com>E<gt> and Larry Wall
  445. E<lt>F<larry@wall.org>E<gt>.
  446.  
  447. UNIX domain sockets added by Sean Robinson
  448. E<lt>F<robinson_s@sc.maricopa.edu>E<gt> with support from Tim Bunce 
  449. E<lt>F<Tim.Bunce@ig.co.uk>E<gt> and the C<perl5-porters> mailing list.
  450.  
  451. Dependency on F<syslog.ph> replaced with XS code by Tom Hughes
  452. E<lt>F<tom@compton.nu>E<gt>.
  453.  
  454. Code for C<constant()>s regenerated by Nicholas Clark E<lt>F<nick@ccl4.org>E<gt>.
  455.  
  456. Failover to different communication modes by Nick Williams
  457. E<lt>F<Nick.Williams@morganstanley.com>E<gt>.
  458.  
  459. Extracted from core distribution for publishing on the CPAN by 
  460. SE<eacute>bastien Aperghis-Tramoni E<lt>sebastien@aperghis.netE<gt>.
  461.  
  462.  
  463. =head1 BUGS
  464.  
  465. Please report any bugs or feature requests to
  466. C<bug-sys-syslog at rt.cpan.org>, or through the web interface at
  467. L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Sys-Syslog>.
  468. I will be notified, and then you'll automatically be notified of progress on
  469. your bug as I make changes.
  470.  
  471.  
  472. =head1 SUPPORT
  473.  
  474. You can find documentation for this module with the perldoc command.
  475.  
  476.     perldoc Sys::Syslog
  477.  
  478. You can also look for information at:
  479.  
  480. =over 4
  481.  
  482. =item * AnnoCPAN: Annotated CPAN documentation
  483.  
  484. L<http://annocpan.org/dist/Sys-Syslog>
  485.  
  486. =item * CPAN Ratings
  487.  
  488. L<http://cpanratings.perl.org/d/Sys-Syslog>
  489.  
  490. =item * RT: CPAN's request tracker
  491.  
  492. L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Sys-Syslog>
  493.  
  494. =item * Search CPAN
  495.  
  496. L<http://search.cpan.org/dist/Sys-Syslog>
  497.  
  498. =back
  499.  
  500.  
  501. =head1 LICENSE
  502.  
  503. This program is free software; you can redistribute it and/or modify it
  504. under the same terms as Perl itself.
  505.  
  506. =cut
  507.  
  508. sub AUTOLOAD {
  509.     # This AUTOLOAD is used to 'autoload' constants from the constant()
  510.     # XS function.
  511.     my $constname;
  512.     our $AUTOLOAD;
  513.     ($constname = $AUTOLOAD) =~ s/.*:://;
  514.     croak "&Sys::Syslog::constant not defined" if $constname eq 'constant';
  515.     my ($error, $val) = constant($constname);
  516.     croak $error if $error;
  517.     no strict 'refs';
  518.     *$AUTOLOAD = sub { $val };
  519.     goto &$AUTOLOAD;
  520. }
  521.  
  522. eval {
  523.     require XSLoader;
  524.     XSLoader::load('Sys::Syslog', $VERSION);
  525.     1
  526. } or do {
  527.     require DynaLoader;
  528.     push @ISA, 'DynaLoader';
  529.     bootstrap Sys::Syslog $VERSION;
  530. };
  531.  
  532. our $maskpri = &LOG_UPTO(&LOG_DEBUG);
  533.  
  534. sub openlog {
  535.     our ($ident, $logopt, $facility) = @_;  # package vars
  536.     our $lo_pid = $logopt =~ /\bpid\b/;
  537.     our $lo_ndelay = $logopt =~ /\bndelay\b/;
  538.     our $lo_nowait = $logopt =~ /\bnowait\b/;
  539.     return 1 unless $lo_ndelay;
  540.     &connect;
  541.  
  542. sub closelog {
  543.     our $facility = our $ident = '';
  544.     &disconnect;
  545.  
  546. sub setlogmask {
  547.     my $oldmask = $maskpri;
  548.     $maskpri = shift unless $_[0] == 0;
  549.     $oldmask;
  550. }
  551.  
  552. sub setlogsock {
  553.     my $setsock = shift;
  554.     $syslog_path = shift;
  555.     &disconnect if $connected;
  556.     $transmit_ok = 0;
  557.     @fallbackMethods = ();
  558.     @connectMethods = @defaultMethods;
  559.     if (ref $setsock eq 'ARRAY') {
  560.     @connectMethods = @$setsock;
  561.     } elsif (lc($setsock) eq 'stream') {
  562.     unless (defined $syslog_path) {
  563.         my @try = qw(/dev/log /dev/conslog);
  564.         if (length &_PATH_LOG) { # Undefined _PATH_LOG is "".
  565.         unshift @try, &_PATH_LOG;
  566.             }
  567.         for my $try (@try) {
  568.         if (-w $try) {
  569.             $syslog_path = $try;
  570.             last;
  571.         }
  572.         }
  573.         carp "stream passed to setlogsock, but could not find any device"
  574.         unless defined $syslog_path
  575.         }
  576.     unless (-w $syslog_path) {
  577.         carp "stream passed to setlogsock, but $syslog_path is not writable";
  578.         return undef;
  579.     } else {
  580.         @connectMethods = ( 'stream' );
  581.     }
  582.     } elsif (lc($setsock) eq 'unix') {
  583.         if (length _PATH_LOG() && !defined $syslog_path) {
  584.         $syslog_path = _PATH_LOG();
  585.         @connectMethods = ( 'unix' );
  586.         } else {
  587.         carp 'unix passed to setlogsock, but path not available';
  588.         return undef;
  589.         }
  590.     } elsif (lc($setsock) eq 'tcp') {
  591.     if (getservbyname('syslog', 'tcp') || getservbyname('syslogng', 'tcp')) {
  592.         @connectMethods = ( 'tcp' );
  593.     } else {
  594.         carp "tcp passed to setlogsock, but tcp service unavailable";
  595.         return undef;
  596.     }
  597.     } elsif (lc($setsock) eq 'udp') {
  598.     if (getservbyname('syslog', 'udp')) {
  599.         @connectMethods = ( 'udp' );
  600.     } else {
  601.         carp "udp passed to setlogsock, but udp service unavailable";
  602.         return undef;
  603.     }
  604.     } elsif (lc($setsock) eq 'inet') {
  605.     @connectMethods = ( 'tcp', 'udp' );
  606.     } elsif (lc($setsock) eq 'console') {
  607.     @connectMethods = ( 'console' );
  608.     } else {
  609.         croak "Invalid argument passed to setlogsock; must be 'stream', 'unix', 'tcp', 'udp' or 'inet'"
  610.     }
  611.     return 1;
  612. }
  613.  
  614. sub syslog {
  615.     my $priority = shift;
  616.     my $mask = shift;
  617.     my ($message, $whoami);
  618.     my (@words, $num, $numpri, $numfac, $sum);
  619.     our $facility;
  620.     local($facility) = $facility;    # may need to change temporarily.
  621.  
  622.     croak "syslog: expecting argument \$priority" unless defined $priority;
  623.     croak "syslog: expecting argument \$format"   unless defined $mask;
  624.  
  625.     @words = split(/\W+/, $priority, 2);# Allow "level" or "level|facility".
  626.     undef $numpri;
  627.     undef $numfac;
  628.     foreach (@words) {
  629.     $num = &xlate($_);        # Translate word to number.
  630.     if ($_ eq 'kern' || $num <= 0) {
  631.         croak "syslog: invalid level/facility: $_"
  632.     }
  633.     elsif ($num <= &LOG_PRIMASK) {
  634.         croak "syslog: too many levels given: $_" if defined($numpri);
  635.         $numpri = $num;
  636.         return 0 unless &LOG_MASK($numpri) & $maskpri;
  637.     }
  638.     else {
  639.         croak "syslog: too many facilities given: $_" if defined($numfac);
  640.         $facility = $_;
  641.         $numfac = $num;
  642.     }
  643.     }
  644.  
  645.     croak "syslog: level must be given" unless defined($numpri);
  646.  
  647.     if (!defined($numfac)) {    # Facility not specified in this call.
  648.     $facility = 'user' unless $facility;
  649.     $numfac = &xlate($facility);
  650.     }
  651.  
  652.     &connect unless $connected;
  653.  
  654.     $whoami = our $ident;
  655.  
  656.     if (!$whoami && $mask =~ /^(\S.*?):\s?(.*)/) {
  657.     $whoami = $1;
  658.     $mask = $2;
  659.     } 
  660.  
  661.     unless ($whoami) {
  662.     ($whoami = getlogin) ||
  663.         ($whoami = getpwuid($<)) ||
  664.         ($whoami = 'syslog');
  665.     }
  666.  
  667.     $whoami .= "[$$]" if our $lo_pid;
  668.  
  669.     if ($mask =~ /%m/) {
  670.     my $err = $!;
  671.     # escape percent signs if sprintf will be called
  672.     $err =~ s/%/%%/g if @_;
  673.     # replace %m with $err, if preceded by an even number of percent signs
  674.     $mask =~ s/(?<!%)((?:%%)*)%m/$1$err/g;
  675.     }
  676.  
  677.     $mask .= "\n" unless $mask =~ /\n$/;
  678.     $message = @_ ? sprintf($mask, @_) : $mask;
  679.  
  680.     $sum = $numpri + $numfac;
  681.     my $oldlocale = setlocale(LC_TIME);
  682.     setlocale(LC_TIME, 'C');
  683.     my $timestamp = strftime "%b %e %T", localtime;
  684.     setlocale(LC_TIME, $oldlocale);
  685.     my $buf = "<$sum>$timestamp $whoami: $message\0";
  686.  
  687.     # it's possible that we'll get an error from sending
  688.     # (e.g. if method is UDP and there is no UDP listener,
  689.     # then we'll get ECONNREFUSED on the send). So what we
  690.     # want to do at this point is to fallback onto a different
  691.     # connection method.
  692.     while (scalar @fallbackMethods || $syslog_send) {
  693.     if ($failed && (time - $fail_time) > 60) {
  694.         # it's been a while... maybe things have been fixed
  695.         @fallbackMethods = ();
  696.         disconnect();
  697.         $transmit_ok = 0; # make it look like a fresh attempt
  698.         &connect;
  699.         }
  700.     if ($connected && !connection_ok()) {
  701.         # Something was OK, but has now broken. Remember coz we'll
  702.         # want to go back to what used to be OK.
  703.         $failed = $current_proto unless $failed;
  704.         $fail_time = time;
  705.         disconnect();
  706.     }
  707.     &connect unless $connected;
  708.     $failed = undef if ($current_proto && $failed && $current_proto eq $failed);
  709.     if ($syslog_send) {
  710.         if (&{$syslog_send}($buf)) {
  711.         $transmit_ok++;
  712.         return 1;
  713.         }
  714.         # typically doesn't happen, since errors are rare from write().
  715.         disconnect();
  716.     }
  717.     }
  718.     # could not send, could not fallback onto a working
  719.     # connection method. Lose.
  720.     return 0;
  721. }
  722.  
  723. sub _syslog_send_console {
  724.     my ($buf) = @_;
  725.     chop($buf); # delete the NUL from the end
  726.     # The console print is a method which could block
  727.     # so we do it in a child process and always return success
  728.     # to the caller.
  729.     if (my $pid = fork) {
  730.     our $lo_nowait;
  731.     if ($lo_nowait) {
  732.         return 1;
  733.     } else {
  734.         if (waitpid($pid, 0) >= 0) {
  735.             return ($? >> 8);
  736.         } else {
  737.         # it's possible that the caller has other
  738.         # plans for SIGCHLD, so let's not interfere
  739.         return 1;
  740.         }
  741.     }
  742.     } else {
  743.         if (open(CONS, ">/dev/console")) {
  744.         my $ret = print CONS $buf . "\r";
  745.         exit ($ret) if defined $pid;
  746.         close CONS;
  747.     }
  748.     exit if defined $pid;
  749.     }
  750. }
  751.  
  752. sub _syslog_send_stream {
  753.     my ($buf) = @_;
  754.     # XXX: this only works if the OS stream implementation makes a write 
  755.     # look like a putmsg() with simple header. For instance it works on 
  756.     # Solaris 8 but not Solaris 7.
  757.     # To be correct, it should use a STREAMS API, but perl doesn't have one.
  758.     return syswrite(SYSLOG, $buf, length($buf));
  759. }
  760.  
  761. sub _syslog_send_socket {
  762.     my ($buf) = @_;
  763.     return syswrite(SYSLOG, $buf, length($buf));
  764.     #return send(SYSLOG, $buf, 0);
  765. }
  766.  
  767. sub xlate {
  768.     my($name) = @_;
  769.     return $name+0 if $name =~ /^\s*\d+\s*$/;
  770.     $name = uc $name;
  771.     $name = "LOG_$name" unless $name =~ /^LOG_/;
  772.     $name = "Sys::Syslog::$name";
  773.     # Can't have just eval { &$name } || -1 because some LOG_XXX may be zero.
  774.     my $value = eval { no strict 'refs'; &$name };
  775.     defined $value ? $value : -1;
  776. }
  777.  
  778. sub connect {
  779.     @fallbackMethods = @connectMethods unless (scalar @fallbackMethods);
  780.     if ($transmit_ok && $current_proto) {
  781.     # Retry what we were on, because it's worked in the past.
  782.     unshift(@fallbackMethods, $current_proto);
  783.     }
  784.     $connected = 0;
  785.     my @errs = ();
  786.     my $proto = undef;
  787.     while ($proto = shift(@fallbackMethods)) {
  788.     no strict 'refs';
  789.     my $fn = "connect_$proto";
  790.     $connected = &$fn(\@errs) if defined &$fn;
  791.     last if ($connected);
  792.     }
  793.  
  794.     $transmit_ok = 0;
  795.     if ($connected) {
  796.     $current_proto = $proto;
  797.         my($old) = select(SYSLOG); $| = 1; select($old);
  798.     } else {
  799.     @fallbackMethods = ();
  800.     croak join "\n\t- ", "no connection to syslog available", @errs
  801.     }
  802. }
  803.  
  804. sub connect_tcp {
  805.     my ($errs) = @_;
  806.     my $tcp = getprotobyname('tcp');
  807.     if (!defined $tcp) {
  808.     push(@{$errs}, "getprotobyname failed for tcp");
  809.     return 0;
  810.     }
  811.     my $syslog = getservbyname('syslog','tcp');
  812.     $syslog = getservbyname('syslogng','tcp') unless (defined $syslog);
  813.     if (!defined $syslog) {
  814.     push(@{$errs}, "getservbyname failed for syslog/tcp and syslogng/tcp");
  815.     return 0;
  816.     }
  817.  
  818.     my $this = sockaddr_in($syslog, INADDR_ANY);
  819.     my $that;
  820.     if (defined $host) {
  821.     $that = inet_aton($host);
  822.     if (!$that) {
  823.         push(@{$errs}, "can't lookup $host");
  824.         return 0;
  825.     }
  826.     } else {
  827.     $that = INADDR_LOOPBACK;
  828.     }
  829.     $that = sockaddr_in($syslog, $that);
  830.  
  831.     if (!socket(SYSLOG,AF_INET,SOCK_STREAM,$tcp)) {
  832.     push(@{$errs}, "tcp socket: $!");
  833.     return 0;
  834.     }
  835.     setsockopt(SYSLOG, SOL_SOCKET, SO_KEEPALIVE, 1);
  836.     setsockopt(SYSLOG, IPPROTO_TCP, TCP_NODELAY, 1);
  837.     if (!CORE::connect(SYSLOG,$that)) {
  838.     push(@{$errs}, "tcp connect: $!");
  839.     return 0;
  840.     }
  841.     $syslog_send = \&_syslog_send_socket;
  842.     return 1;
  843. }
  844.  
  845. sub connect_udp {
  846.     my ($errs) = @_;
  847.     my $udp = getprotobyname('udp');
  848.     if (!defined $udp) {
  849.     push(@{$errs}, "getprotobyname failed for udp");
  850.     return 0;
  851.     }
  852.     my $syslog = getservbyname('syslog','udp');
  853.     if (!defined $syslog) {
  854.     push(@{$errs}, "getservbyname failed for syslog/udp");
  855.     return 0;
  856.     }
  857.     my $this = sockaddr_in($syslog, INADDR_ANY);
  858.     my $that;
  859.     if (defined $host) {
  860.     $that = inet_aton($host);
  861.     if (!$that) {
  862.         push(@{$errs}, "can't lookup $host");
  863.         return 0;
  864.     }
  865.     } else {
  866.     $that = INADDR_LOOPBACK;
  867.     }
  868.     $that = sockaddr_in($syslog, $that);
  869.  
  870.     if (!socket(SYSLOG,AF_INET,SOCK_DGRAM,$udp)) {
  871.     push(@{$errs}, "udp socket: $!");
  872.     return 0;
  873.     }
  874.     if (!CORE::connect(SYSLOG,$that)) {
  875.     push(@{$errs}, "udp connect: $!");
  876.     return 0;
  877.     }
  878.     # We want to check that the UDP connect worked. However the only
  879.     # way to do that is to send a message and see if an ICMP is returned
  880.     _syslog_send_socket("");
  881.     if (!connection_ok()) {
  882.     push(@{$errs}, "udp connect: nobody listening");
  883.     return 0;
  884.     }
  885.     $syslog_send = \&_syslog_send_socket;
  886.     return 1;
  887. }
  888.  
  889. sub connect_stream {
  890.     my ($errs) = @_;
  891.     # might want syslog_path to be variable based on syslog.h (if only
  892.     # it were in there!)
  893.     $syslog_path = '/dev/conslog'; 
  894.     if (!-w $syslog_path) {
  895.     push(@{$errs}, "stream $syslog_path is not writable");
  896.     return 0;
  897.     }
  898.     if (!open(SYSLOG, ">" . $syslog_path)) {
  899.     push(@{$errs}, "stream can't open $syslog_path: $!");
  900.     return 0;
  901.     }
  902.     $syslog_send = \&_syslog_send_stream;
  903.     return 1;
  904. }
  905.  
  906. sub connect_unix {
  907.     my ($errs) = @_;
  908.     if (length _PATH_LOG()) {
  909.     $syslog_path = _PATH_LOG();
  910.     } else {
  911.         push(@{$errs}, "_PATH_LOG not available in syslog.h");
  912.     return 0;
  913.     }
  914.     if (! -S $syslog_path) {
  915.     push(@{$errs}, "$syslog_path is not a socket");
  916.     return 0;
  917.     }
  918.     my $that = sockaddr_un($syslog_path);
  919.     if (!$that) {
  920.     push(@{$errs}, "can't locate $syslog_path");
  921.     return 0;
  922.     }
  923.     if (!socket(SYSLOG,AF_UNIX,SOCK_STREAM,0)) {
  924.     push(@{$errs}, "unix stream socket: $!");
  925.     return 0;
  926.     }
  927.     if (!CORE::connect(SYSLOG,$that)) {
  928.         if (!socket(SYSLOG,AF_UNIX,SOCK_DGRAM,0)) {
  929.         push(@{$errs}, "unix dgram socket: $!");
  930.         return 0;
  931.     }
  932.         if (!CORE::connect(SYSLOG,$that)) {
  933.         push(@{$errs}, "unix dgram connect: $!");
  934.         return 0;
  935.     }
  936.     }
  937.     $syslog_send = \&_syslog_send_socket;
  938.     return 1;
  939. }
  940.  
  941. sub connect_console {
  942.     my ($errs) = @_;
  943.     if (!-w '/dev/console') {
  944.     push(@{$errs}, "console is not writable");
  945.     return 0;
  946.     }
  947.     $syslog_send = \&_syslog_send_console;
  948.     return 1;
  949. }
  950.  
  951. # to test if the connection is still good, we need to check if any
  952. # errors are present on the connection. The errors will not be raised
  953. # by a write. Instead, sockets are made readable and the next read
  954. # would cause the error to be returned. Unfortunately the syslog 
  955. # 'protocol' never provides anything for us to read. But with 
  956. # judicious use of select(), we can see if it would be readable...
  957. sub connection_ok {
  958.     return 1 if (defined $current_proto && $current_proto eq 'console');
  959.     my $rin = '';
  960.     vec($rin, fileno(SYSLOG), 1) = 1;
  961.     my $ret = select $rin, undef, $rin, 0;
  962.     return ($ret ? 0 : 1);
  963. }
  964.  
  965. sub disconnect {
  966.     $connected = 0;
  967.     $syslog_send = undef;
  968.     return close SYSLOG;
  969. }
  970.  
  971. 1;
  972.