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