File Coverage

blib/lib/Sys/Syslog.pm
Criterion Covered Total %
statement 262 406 64.5
branch 105 222 47.3
condition 25 76 32.8
subroutine 41 51 80.3
pod 5 19 26.3
total 438 774 56.5


line stmt bran cond sub pod time code
1             package Sys::Syslog;
2 5     5   384724 use strict;
  5         10  
  5         144  
3 5     5   23 use warnings;
  5         7  
  5         173  
4 5     5   22 use warnings::register;
  5         14  
  5         568  
5 5     5   22 use Carp;
  5         6  
  5         305  
6 5     5   23 use Config;
  5         8  
  5         191  
7 5     5   19 use Exporter ();
  5         6  
  5         84  
8 5     5   20 use File::Basename;
  5         11  
  5         786  
9 5     5   2597 use POSIX qw< strftime setlocale LC_TIME >;
  5         28504  
  5         32  
10 5     5   6828 use Socket qw< :all >;
  5         6949  
  5         6456  
11             require 5.005;
12              
13              
14             *import = \&Exporter::import;
15              
16              
17 5     5   90 { no strict 'vars';
  5         6  
  5         920  
18             $VERSION = '0.35';
19              
20             %EXPORT_TAGS = (
21             standard => [qw(openlog syslog closelog setlogmask)],
22             extended => [qw(setlogsock)],
23             macros => [
24             # levels
25             qw(
26             LOG_ALERT LOG_CRIT LOG_DEBUG LOG_EMERG LOG_ERR
27             LOG_INFO LOG_NOTICE LOG_WARNING
28             ),
29              
30             # standard facilities
31             qw(
32             LOG_AUTH LOG_AUTHPRIV LOG_CRON LOG_DAEMON LOG_FTP LOG_KERN
33             LOG_LOCAL0 LOG_LOCAL1 LOG_LOCAL2 LOG_LOCAL3 LOG_LOCAL4
34             LOG_LOCAL5 LOG_LOCAL6 LOG_LOCAL7 LOG_LPR LOG_MAIL LOG_NEWS
35             LOG_SYSLOG LOG_USER LOG_UUCP
36             ),
37             # Mac OS X specific facilities
38             qw( LOG_INSTALL LOG_LAUNCHD LOG_NETINFO LOG_RAS LOG_REMOTEAUTH ),
39             # modern BSD specific facilities
40             qw( LOG_CONSOLE LOG_NTP LOG_SECURITY ),
41             # IRIX specific facilities
42             qw( LOG_AUDIT LOG_LFMT ),
43              
44             # options
45             qw(
46             LOG_CONS LOG_PID LOG_NDELAY LOG_NOWAIT LOG_ODELAY LOG_PERROR
47             ),
48              
49             # others macros
50             qw(
51             LOG_FACMASK LOG_NFACILITIES LOG_PRIMASK
52             LOG_MASK LOG_UPTO
53             ),
54             ],
55             );
56              
57             @EXPORT = (
58             @{$EXPORT_TAGS{standard}},
59             );
60              
61             @EXPORT_OK = (
62             @{$EXPORT_TAGS{extended}},
63             @{$EXPORT_TAGS{macros}},
64             );
65              
66             eval {
67             require XSLoader;
68             XSLoader::load('Sys::Syslog', $VERSION);
69             1
70             } or do {
71             require DynaLoader;
72             push @ISA, 'DynaLoader';
73             bootstrap Sys::Syslog $VERSION;
74             };
75             }
76              
77              
78             #
79             # Constants
80             #
81 5     5   24 use constant HAVE_GETPROTOBYNAME => $Config::Config{d_getpbyname};
  5         6  
  5         700  
82 5     5   20 use constant HAVE_GETPROTOBYNUMBER => $Config::Config{d_getpbynumber};
  5         11  
  5         392  
83 5     5   20 use constant HAVE_SETLOCALE => $Config::Config{d_setlocale};
  5         6  
  5         467  
84 5 50   5   38 use constant HAVE_IPPROTO_TCP => defined &Socket::IPPROTO_TCP ? 1 : 0;
  5         5  
  5         284  
85 5 50   5   22 use constant HAVE_IPPROTO_UDP => defined &Socket::IPPROTO_UDP ? 1 : 0;
  5         5  
  5         270  
86 5 50   5   20 use constant HAVE_TCP_NODELAY => defined &Socket::TCP_NODELAY ? 1 : 0;
  5         6  
  5         288  
87              
88 5         301 use constant SOCKET_IPPROTO_TCP =>
89             HAVE_IPPROTO_TCP ? Socket::IPPROTO_TCP
90             : HAVE_GETPROTOBYNAME ? scalar getprotobyname("tcp")
91 5     5   21 : 6;
  5         6  
92              
93 5         227 use constant SOCKET_IPPROTO_UDP =>
94             HAVE_IPPROTO_UDP ? Socket::IPPROTO_UDP
95             : HAVE_GETPROTOBYNAME ? scalar getprotobyname("udp")
96 5     5   20 : 17;
  5         7  
97              
98 5     5   21 use constant SOCKET_TCP_NODELAY => HAVE_TCP_NODELAY ? Socket::TCP_NODELAY : 1;
  5         6  
  5         243  
99              
100              
101             #
102             # Public variables
103             #
104 5     5   24 use vars qw($host); # host to send syslog messages to (see notes at end)
  5         6  
  5         316  
105              
106             #
107             # Prototypes
108             #
109             sub silent_eval (&);
110              
111             #
112             # Global variables
113             #
114 5     5   19 use vars qw($facility);
  5         4  
  5         1536  
115             my $connected = 0; # flag to indicate if we're connected or not
116             my $syslog_send; # coderef of the function used to send messages
117             my $syslog_path = undef; # syslog path for "stream" and "unix" mechanisms
118             my $syslog_xobj = undef; # if defined, holds the external object used to send messages
119             my $transmit_ok = 0; # flag to indicate if the last message was transmitted
120             my $sock_port = undef; # socket port
121             my $sock_timeout = 0; # socket timeout, see below
122             my $current_proto = undef; # current mechanism used to transmit messages
123             my $ident = ''; # identifiant prepended to each message
124             $facility = ''; # current facility
125             my $maskpri = LOG_UPTO(&LOG_DEBUG); # current log mask
126              
127             my %options = (
128             ndelay => 0,
129             noeol => 0,
130             nofatal => 0,
131             nonul => 0,
132             nowait => 0,
133             perror => 0,
134             pid => 0,
135             );
136              
137             # Default is now to first use the native mechanism, so Perl programs
138             # behave like other normal Unix programs, then try other mechanisms.
139             my @connectMethods = qw(native tcp udp unix pipe stream console);
140             if ($^O eq "freebsd" or $^O eq "linux") {
141             @connectMethods = grep { $_ ne 'udp' } @connectMethods;
142             }
143              
144             # And on Win32 systems, we try to use the native mechanism for this
145             # platform, the events logger, available through Win32::EventLog.
146             EVENTLOG: {
147             my $verbose_if_Win32 = $^O =~ /Win32/i;
148              
149             if (can_load_sys_syslog_win32($verbose_if_Win32)) {
150             unshift @connectMethods, 'eventlog';
151             }
152             }
153              
154             my @defaultMethods = @connectMethods;
155             my @fallbackMethods = ();
156              
157             # The timeout in connection_ok() was pushed up to 0.25 sec in
158             # Sys::Syslog v0.19 in order to address a heisenbug on MacOSX:
159             # http://london.pm.org/pipermail/london.pm/Week-of-Mon-20061211/005961.html
160             #
161             # However, this also had the effect of slowing this test for
162             # all other operating systems, which apparently impacted some
163             # users (cf. CPAN-RT #34753). So, in order to make everybody
164             # happy, the timeout is now zero by default on all systems
165             # except on OSX where it is set to 250 msec, and can be set
166             # with the infamous setlogsock() function.
167             #
168             # Update 2011-08: this issue is also been seen on multiprocessor
169             # Debian GNU/kFreeBSD systems. See http://bugs.debian.org/627821
170             # and https://rt.cpan.org/Ticket/Display.html?id=69997
171             # Also, lowering the delay to 1 ms, which should be enough.
172              
173             $sock_timeout = 0.001 if $^O =~ /darwin|gnukfreebsd/;
174              
175              
176             # Perl 5.6.0's warnings.pm doesn't have warnings::warnif()
177             if (not defined &warnings::warnif) {
178             *warnings::warnif = sub {
179             goto &warnings::warn if warnings::enabled(__PACKAGE__)
180             }
181             }
182              
183             # coderef for a nicer handling of errors
184             my $err_sub = $options{nofatal} ? \&warnings::warnif : \&croak;
185              
186              
187             sub AUTOLOAD {
188             # This AUTOLOAD is used to 'autoload' constants from the constant()
189             # XS function.
190 5     5   23 no strict 'vars';
  5         6  
  5         418  
191 3     3   816 my $constname;
192 3         19 ($constname = $AUTOLOAD) =~ s/.*:://;
193 3 50       13 croak "Sys::Syslog::constant() not defined" if $constname eq 'constant';
194 3         16 my ($error, $val) = constant($constname);
195 3 50       506 croak $error if $error;
196 5     5   21 no strict 'refs';
  5         5  
  5         11087  
197 0     0   0 *$AUTOLOAD = sub { $val };
  0         0  
198 0         0 goto &$AUTOLOAD;
199             }
200              
201              
202             sub openlog {
203 7     7 1 3821 ($ident, my $logopt, $facility) = @_;
204              
205             # default values
206 7   0     80 $ident ||= basename($0) || getlogin() || getpwuid($<) || 'syslog';
      66        
207 7   100     24 $logopt ||= '';
208 7   66     14 $facility ||= LOG_USER();
209              
210 7         31 for my $opt (split /\b/, $logopt) {
211 3 50       15 $options{$opt} = 1 if exists $options{$opt}
212             }
213              
214 7 50       23 $err_sub = delete $options{nofatal} ? \&warnings::warnif : \&croak;
215 7 100       32 return 1 unless $options{ndelay};
216 4         13 connect_log();
217             }
218              
219             sub closelog {
220 1 50   1 1 1127 disconnect_log() if $connected;
221 1         352 $options{$_} = 0 for keys %options;
222 1         6 $facility = $ident = "";
223 1         2 $connected = 0;
224 1         5 return 1
225             }
226              
227             sub setlogmask {
228 11     11 1 10233 my $oldmask = $maskpri;
229 11 100       26 $maskpri = shift unless $_[0] == 0;
230 11         18 $oldmask;
231             }
232              
233              
234             my %mechanism = (
235             console => {
236             check => sub { 1 },
237             },
238             eventlog => {
239             check => sub { return can_load_sys_syslog_win32() },
240             err_msg => "no Win32 API available",
241             },
242             inet => {
243             check => sub { 1 },
244             },
245             native => {
246             check => sub { 1 },
247             },
248             pipe => {
249             check => sub {
250             ($syslog_path) = grep { defined && length && -p && -w _ }
251             $syslog_path, &_PATH_LOG, "/dev/log";
252             return $syslog_path ? 1 : 0
253             },
254             err_msg => "path not available",
255             },
256             stream => {
257             check => sub {
258             if (not defined $syslog_path) {
259             my @try = qw(/dev/log /dev/conslog);
260             unshift @try, &_PATH_LOG if length &_PATH_LOG;
261             ($syslog_path) = grep { -w } @try;
262             }
263             return defined $syslog_path && -w $syslog_path
264             },
265             err_msg => "could not find any writable device",
266             },
267             tcp => {
268             check => sub {
269             return 1 if defined $sock_port;
270              
271             if (eval { local $SIG{__DIE__};
272             getservbyname('syslog','tcp') || getservbyname('syslogng','tcp')
273             }) {
274             $host = $syslog_path;
275             return 1
276             }
277             else {
278             return
279             }
280             },
281             err_msg => "TCP service unavailable",
282             },
283             udp => {
284             check => sub {
285             return 1 if defined $sock_port;
286              
287             if (eval { local $SIG{__DIE__}; getservbyname('syslog', 'udp') }) {
288             $host = $syslog_path;
289             return 1
290             }
291             else {
292             return
293             }
294             },
295             err_msg => "UDP service unavailable",
296             },
297             unix => {
298             check => sub {
299             my @try = ($syslog_path, &_PATH_LOG);
300             ($syslog_path) = grep { defined && length && -w } @try;
301             return defined $syslog_path && -w $syslog_path
302             },
303             err_msg => "path not available",
304             },
305             );
306            
307             sub setlogsock {
308 18     18 1 24865 my %opt;
309              
310             # handle arguments
311             # - old API: setlogsock($sock_type, $sock_path, $sock_timeout)
312             # - new API: setlogsock(\%options)
313 18 100 66     228 croak "setlogsock(): Invalid number of arguments"
314             unless @_ >= 1 and @_ <= 3;
315              
316 17 100       47 if (my $ref = ref $_[0]) {
317 10 100       34 if ($ref eq "HASH") {
    100          
318 1         1 %opt = %{ $_[0] };
  1         3  
319 1 50       75 croak "setlogsock(): No argument given" unless keys %opt;
320             }
321             elsif ($ref eq "ARRAY") {
322 8         31 @opt{qw< type path timeout >} = @_;
323             }
324             else {
325 1         89 croak "setlogsock(): Unexpected \L$ref\E reference"
326             }
327             }
328             else {
329 7         24 @opt{qw< type path timeout >} = @_;
330             }
331              
332             # check socket type, remove invalid ones
333             my $diag_invalid_type = "setlogsock(): Invalid type%s; must be one of "
334 15         104 . join ", ", map { "'$_'" } sort keys %mechanism;
  135         220  
335 15 100       130 croak sprintf $diag_invalid_type, "" unless defined $opt{type};
336 14 100       40 my @sock_types = ref $opt{type} eq "ARRAY" ? @{$opt{type}} : ($opt{type});
  8         24  
337 14         13 my @tmp;
338              
339 14         25 for my $sock_type (@sock_types) {
340             carp sprintf $diag_invalid_type, " '$sock_type'" and next
341 14 50 0     30 unless exists $mechanism{$sock_type};
342 14 100 50     45 push @tmp, "tcp", "udp" and next if $sock_type eq "inet";
343 12         29 push @tmp, $sock_type;
344             }
345              
346 14         23 @sock_types = @tmp;
347              
348             # set global options
349 14 100       28 $syslog_path = $opt{path} if defined $opt{path};
350 14 50       30 $host = $opt{host} if defined $opt{host};
351 14 50       22 $sock_timeout = $opt{timeout} if defined $opt{timeout};
352 14 50       24 $sock_port = $opt{port} if defined $opt{port};
353              
354 14 50       25 disconnect_log() if $connected;
355 14         13 $transmit_ok = 0;
356 14         18 @fallbackMethods = ();
357 14         21 @connectMethods = ();
358 14         17 my $found = 0;
359              
360             # check each given mechanism and test if it can be used on the current system
361 14         17 for my $sock_type (@sock_types) {
362 16 100       41 if ( $mechanism{$sock_type}{check}->() ) {
363 7         10 push @connectMethods, $sock_type;
364 7         14 $found = 1;
365             }
366             else {
367             warnings::warnif("setlogsock(): type='$sock_type': "
368 9         860 . $mechanism{$sock_type}{err_msg});
369             }
370             }
371              
372             # if no mechanism worked from the given ones, use the default ones
373 14 100       44 @connectMethods = @defaultMethods unless @connectMethods;
374              
375 14         63 return $found;
376             }
377              
378             sub syslog {
379 9     9 1 7913 my ($priority, $mask, @args) = @_;
380 9         13 my ($message, $buf);
381 0         0 my (@words, $num, $numpri, $numfac, $sum);
382 9         11 my $failed = undef;
383 9         10 my $fail_time = undef;
384 9         46 my $error = $!;
385              
386             # if $ident is undefined, it means openlog() wasn't previously called
387             # so do it now in order to have sensible defaults
388 9 100       35 openlog() unless $ident;
389              
390 9         16 local $facility = $facility; # may need to change temporarily.
391              
392 9 100       151 croak "syslog: expecting argument \$priority" unless defined $priority;
393 7 100       92 croak "syslog: expecting argument \$format" unless defined $mask;
394              
395 6 100       39 if ($priority =~ /^\d+$/) {
    100          
396 1         6 $numpri = LOG_PRI($priority);
397 1         5 $numfac = LOG_FAC($priority) << 3;
398 1 50       6 undef $numfac if $numfac == 0; # no facility given => use default
399             }
400             elsif ($priority =~ /^\w+/) {
401             # Allow "level" or "level|facility".
402 4         21 @words = split /\W+/, $priority, 2;
403              
404 4         7 undef $numpri;
405 4         7 undef $numfac;
406              
407 4         13 for my $word (@words) {
408 6 50       17 next if length $word == 0;
409              
410             # Translate word to number.
411 6         16 $num = xlate($word);
412              
413 6 100 66     45 if ($num < 0) {
    100          
414 1         134 croak "syslog: invalid level/facility: $word"
415             }
416             elsif ($num <= LOG_PRIMASK() and $word ne "kern") {
417 3 100       162 croak "syslog: too many levels given: $word"
418             if defined $numpri;
419 2         6 $numpri = $num;
420             }
421             else {
422 2 100       201 croak "syslog: too many facilities given: $word"
423             if defined $numfac;
424 1 50       7 $facility = $word if $word =~ /^[A-Za-z]/;
425 1         4 $numfac = $num;
426             }
427             }
428             }
429             else {
430 1         126 croak "syslog: invalid level/facility: $priority"
431             }
432              
433 2 50       9 croak "syslog: level must be given" unless defined $numpri;
434              
435             # don't log if priority is below mask level
436 2 50       13 return 0 unless LOG_MASK($numpri) & $maskpri;
437              
438 2 50       6 if (not defined $numfac) { # Facility not specified in this call.
439 2 50       6 $facility = 'user' unless $facility;
440 2         7 $numfac = xlate($facility);
441             }
442              
443 2 50       8 connect_log() unless $connected;
444              
445 2 100       9 if ($mask =~ /%m/) {
446             # escape percent signs for sprintf()
447 1 50       5 $error =~ s/%/%%/g if @args;
448             # replace %m with $error, if preceded by an even number of percent signs
449 1         15 $mask =~ s/(?
450             }
451              
452             # add (or not) a newline
453 2 50 33     21 $mask .= "\n" if !$options{noeol} and rindex($mask, "\n") == -1;
454 2 50       8 $message = @args ? sprintf($mask, @args) : $mask;
455              
456 2 50       7 if ($current_proto eq 'native') {
    0          
457 2         4 $buf = $message;
458             }
459             elsif ($current_proto eq 'eventlog') {
460 0         0 $buf = $message;
461             }
462             else {
463 0         0 my $whoami = $ident;
464 0 0       0 $whoami .= "[$$]" if $options{pid};
465              
466 0         0 $sum = $numpri + $numfac;
467              
468 0         0 my $oldlocale;
469 0         0 if (HAVE_SETLOCALE) {
470 0         0 $oldlocale = setlocale(LC_TIME);
471 0         0 setlocale(LC_TIME, 'C');
472             }
473              
474             # %e format isn't available on all systems (Win32, cf. CPAN RT #69310)
475 0         0 my $day = strftime "%e", localtime;
476              
477 0 0       0 if (index($day, "%") == 0) {
478 0         0 $day = strftime "%d", localtime;
479 0         0 $day =~ s/^0/ /;
480             }
481              
482 0         0 my $timestamp = strftime "%b $day %H:%M:%S", localtime;
483 0         0 setlocale(LC_TIME, $oldlocale) if HAVE_SETLOCALE;
484              
485             # construct the stream that will be transmitted
486 0         0 $buf = "<$sum>$timestamp $whoami: $message";
487              
488             # add (or not) a NUL character
489 0 0       0 $buf .= "\0" if !$options{nonul};
490             }
491              
492             # handle PERROR option
493             # "native" mechanism already handles it by itself
494 2 50 33     18 if ($options{perror} and $current_proto ne 'native') {
495 0         0 my $whoami = $ident;
496 0 0       0 $whoami .= "[$$]" if $options{pid};
497 0         0 print STDERR "$whoami: $message";
498 0 0       0 print STDERR "\n" if rindex($message, "\n") == -1;
499             }
500              
501             # it's possible that we'll get an error from sending
502             # (e.g. if method is UDP and there is no UDP listener,
503             # then we'll get ECONNREFUSED on the send). So what we
504             # want to do at this point is to fallback onto a different
505             # connection method.
506 2   33     15 while (scalar @fallbackMethods || $syslog_send) {
507 2 50 33     9 if ($failed && (time - $fail_time) > 60) {
508             # it's been a while... maybe things have been fixed
509 0         0 @fallbackMethods = ();
510 0         0 disconnect_log();
511 0         0 $transmit_ok = 0; # make it look like a fresh attempt
512 0         0 connect_log();
513             }
514              
515 2 50 33     15 if ($connected && !connection_ok()) {
516             # Something was OK, but has now broken. Remember coz we'll
517             # want to go back to what used to be OK.
518 0 0       0 $failed = $current_proto unless $failed;
519 0         0 $fail_time = time;
520 0         0 disconnect_log();
521             }
522              
523 2 50       7 connect_log() unless $connected;
524 2 50 33     23 $failed = undef if ($current_proto && $failed && $current_proto eq $failed);
      33        
525              
526 2 50       7 if ($syslog_send) {
527 2 50       8 if ($syslog_send->($buf, $numpri, $numfac)) {
528 2         5 $transmit_ok++;
529 2         13 return 1;
530             }
531             # typically doesn't happen, since errors are rare from write().
532 0         0 disconnect_log();
533             }
534             }
535             # could not send, could not fallback onto a working
536             # connection method. Lose.
537 0         0 return 0;
538             }
539              
540             sub _syslog_send_console {
541 0     0   0 my ($buf) = @_;
542              
543             # The console print is a method which could block
544             # so we do it in a child process and always return success
545             # to the caller.
546 0 0       0 if (my $pid = fork) {
547              
548 0 0       0 if ($options{nowait}) {
549 0         0 return 1;
550             } else {
551 0 0       0 if (waitpid($pid, 0) >= 0) {
552 0         0 return ($? >> 8);
553             } else {
554             # it's possible that the caller has other
555             # plans for SIGCHLD, so let's not interfere
556 0         0 return 1;
557             }
558             }
559             } else {
560 0 0       0 if (open(CONS, ">/dev/console")) {
561 0         0 my $ret = print CONS $buf . "\r"; # XXX: should this be \x0A ?
562 0 0       0 POSIX::_exit($ret) if defined $pid;
563 0         0 close CONS;
564             }
565              
566 0 0       0 POSIX::_exit(0) if defined $pid;
567             }
568             }
569              
570             sub _syslog_send_stream {
571 0     0   0 my ($buf) = @_;
572             # XXX: this only works if the OS stream implementation makes a write
573             # look like a putmsg() with simple header. For instance it works on
574             # Solaris 8 but not Solaris 7.
575             # To be correct, it should use a STREAMS API, but perl doesn't have one.
576 0         0 return syswrite(SYSLOG, $buf, length($buf));
577             }
578              
579             sub _syslog_send_pipe {
580 0     0   0 my ($buf) = @_;
581 0         0 return print SYSLOG $buf;
582             }
583              
584             sub _syslog_send_socket {
585 2     2   4 my ($buf) = @_;
586 2         114 return syswrite(SYSLOG, $buf, length($buf));
587             #return send(SYSLOG, $buf, 0);
588             }
589              
590             sub _syslog_send_native {
591 2     2   5 my ($buf, $numpri, $numfac) = @_;
592 2         323 syslog_xs($numpri|$numfac, $buf);
593 2         11 return 1;
594             }
595              
596              
597             # xlate()
598             # -----
599             # private function to translate names to numeric values
600             #
601             sub xlate {
602 10     10 0 16 my ($name) = @_;
603              
604 10 50       43 return $name+0 if $name =~ /^\s*\d+\s*$/;
605 10         19 $name = uc $name;
606 10 50       32 $name = "LOG_$name" unless $name =~ /^LOG_/;
607              
608             # ExtUtils::Constant 0.20 introduced a new way to implement
609             # constants, called ProxySubs. When it was used to generate
610             # the C code, the constant() function no longer returns the
611             # correct value. Therefore, we first try a direct call to
612             # constant(), and if the value is an error we try to call the
613             # constant by its full name.
614 10         58 my $value = constant($name);
615              
616 10 50       35 if (index($value, "not a valid") >= 0) {
617 10         16 $name = "Sys::Syslog::$name";
618 5     5   38 $value = eval { no strict "refs"; &$name };
  5         5  
  5         611  
  10         16  
  10         88  
619 10 100       29 $value = $@ unless defined $value;
620             }
621              
622 10 100       30 $value = -1 if index($value, "not a valid") >= 0;
623              
624 10 50       77 return defined $value ? $value : -1;
625             }
626              
627              
628             # connect_log()
629             # -----------
630             # This function acts as a kind of front-end: it tries to connect to
631             # a syslog service using the selected methods, trying each one in the
632             # selected order.
633             #
634             sub connect_log {
635 4 50   4 0 15 @fallbackMethods = @connectMethods unless scalar @fallbackMethods;
636              
637 4 50 33     19 if ($transmit_ok && $current_proto) {
638             # Retry what we were on, because it has worked in the past.
639 0         0 unshift(@fallbackMethods, $current_proto);
640             }
641              
642 4         6 $connected = 0;
643 4         8 my @errs = ();
644 4         5 my $proto = undef;
645              
646 4         12 while ($proto = shift @fallbackMethods) {
647 5     5   24 no strict 'refs';
  5         6  
  5         7041  
648 4         10 my $fn = "connect_$proto";
649 4 50       29 $connected = &$fn(\@errs) if defined &$fn;
650 4 100       57 last if $connected;
651             }
652              
653 4         6 $transmit_ok = 0;
654 4 100       8 if ($connected) {
655 1         1 $current_proto = $proto;
656 1         4 my ($old) = select(SYSLOG); $| = 1; select($old);
  1         3  
  1         5  
657             } else {
658 3         7 @fallbackMethods = ();
659 3         483 $err_sub->(join "\n\t- ", "no connection to syslog available", @errs);
660 0         0 return undef;
661             }
662             }
663              
664             sub connect_tcp {
665 0     0 0 0 my ($errs) = @_;
666              
667             my $port = $sock_port
668             || eval { local $SIG{__DIE__}; getservbyname('syslog', 'tcp') }
669 0   0     0 || eval { local $SIG{__DIE__}; getservbyname('syslogng', 'tcp') };
670 0 0       0 if (!defined $port) {
671 0         0 push @$errs, "getservbyname failed for syslog/tcp and syslogng/tcp";
672 0         0 return 0;
673             }
674              
675 0         0 my $addr;
676 0 0       0 if (defined $host) {
677 0         0 $addr = inet_aton($host);
678 0 0       0 if (!$addr) {
679 0         0 push @$errs, "can't lookup $host";
680 0         0 return 0;
681             }
682             } else {
683 0         0 $addr = INADDR_LOOPBACK;
684             }
685 0         0 $addr = sockaddr_in($port, $addr);
686              
687 0 0       0 if (!socket(SYSLOG, AF_INET, SOCK_STREAM, SOCKET_IPPROTO_TCP)) {
688 0         0 push @$errs, "tcp socket: $!";
689 0         0 return 0;
690             }
691              
692 0         0 setsockopt(SYSLOG, SOL_SOCKET, SO_KEEPALIVE, 1);
693 0         0 setsockopt(SYSLOG, SOCKET_IPPROTO_TCP, SOCKET_TCP_NODELAY, 1);
694              
695 0 0       0 if (!connect(SYSLOG, $addr)) {
696 0         0 push @$errs, "tcp connect: $!";
697 0         0 return 0;
698             }
699              
700 0         0 $syslog_send = \&_syslog_send_socket;
701              
702 0         0 return 1;
703             }
704              
705             sub connect_udp {
706 2     2 0 2 my ($errs) = @_;
707              
708             my $port = $sock_port
709 2   33     7 || eval { local $SIG{__DIE__}; getservbyname('syslog', 'udp') };
710 2 50       8 if (!defined $port) {
711 0         0 push @$errs, "getservbyname failed for syslog/udp";
712 0         0 return 0;
713             }
714              
715 2         3 my $addr;
716 2 50       7 if (defined $host) {
717 0         0 $addr = inet_aton($host);
718 0 0       0 if (!$addr) {
719 0         0 push @$errs, "can't lookup $host";
720 0         0 return 0;
721             }
722             } else {
723 2         5 $addr = INADDR_LOOPBACK;
724             }
725 2         11 $addr = sockaddr_in($port, $addr);
726              
727 2 50       77 if (!socket(SYSLOG, AF_INET, SOCK_DGRAM, SOCKET_IPPROTO_UDP)) {
728 0         0 push @$errs, "udp socket: $!";
729 0         0 return 0;
730             }
731 2 50       31 if (!connect(SYSLOG, $addr)) {
732 0         0 push @$errs, "udp connect: $!";
733 0         0 return 0;
734             }
735              
736             # We want to check that the UDP connect worked. However the only
737             # way to do that is to send a message and see if an ICMP is returned
738 2         6 _syslog_send_socket("");
739 2 50       6 if (!connection_ok()) {
740 2         3 push @$errs, "udp connect: nobody listening";
741 2         5 return 0;
742             }
743              
744 0         0 $syslog_send = \&_syslog_send_socket;
745              
746 0         0 return 1;
747             }
748              
749             sub connect_stream {
750 1     1 0 4 my ($errs) = @_;
751             # might want syslog_path to be variable based on syslog.h (if only
752             # it were in there!)
753 1 50       5 $syslog_path = '/dev/conslog' unless defined $syslog_path;
754              
755 1 50       19 if (!-w $syslog_path) {
756 1         4 push @$errs, "stream $syslog_path is not writable";
757 1         2 return 0;
758             }
759              
760 0         0 require Fcntl;
761              
762 0 0       0 if (!sysopen(SYSLOG, $syslog_path, Fcntl::O_WRONLY(), 0400)) {
763 0         0 push @$errs, "stream can't open $syslog_path: $!";
764 0         0 return 0;
765             }
766              
767 0         0 $syslog_send = \&_syslog_send_stream;
768              
769 0         0 return 1;
770             }
771              
772             sub connect_pipe {
773 0     0 0 0 my ($errs) = @_;
774              
775 0   0     0 $syslog_path ||= &_PATH_LOG || "/dev/log";
      0        
776              
777 0 0       0 if (not -w $syslog_path) {
778 0         0 push @$errs, "$syslog_path is not writable";
779 0         0 return 0;
780             }
781              
782 0 0       0 if (not open(SYSLOG, ">$syslog_path")) {
783 0         0 push @$errs, "can't write to $syslog_path: $!";
784 0         0 return 0;
785             }
786              
787 0         0 $syslog_send = \&_syslog_send_pipe;
788              
789 0         0 return 1;
790             }
791              
792             sub connect_unix {
793 0     0 0 0 my ($errs) = @_;
794              
795 0 0 0     0 $syslog_path ||= _PATH_LOG() if length _PATH_LOG();
796              
797 0 0       0 if (not defined $syslog_path) {
798 0         0 push @$errs, "_PATH_LOG not available in syslog.h and no user-supplied socket path";
799 0         0 return 0;
800             }
801              
802 0 0 0     0 if (not (-S $syslog_path or -c _)) {
803 0         0 push @$errs, "$syslog_path is not a socket";
804 0         0 return 0;
805             }
806              
807 0         0 my $addr = sockaddr_un($syslog_path);
808 0 0       0 if (!$addr) {
809 0         0 push @$errs, "can't locate $syslog_path";
810 0         0 return 0;
811             }
812 0 0       0 if (!socket(SYSLOG, AF_UNIX, SOCK_STREAM, 0)) {
813 0         0 push @$errs, "unix stream socket: $!";
814 0         0 return 0;
815             }
816              
817 0 0       0 if (!connect(SYSLOG, $addr)) {
818 0 0       0 if (!socket(SYSLOG, AF_UNIX, SOCK_DGRAM, 0)) {
819 0         0 push @$errs, "unix dgram socket: $!";
820 0         0 return 0;
821             }
822 0 0       0 if (!connect(SYSLOG, $addr)) {
823 0         0 push @$errs, "unix dgram connect: $!";
824 0         0 return 0;
825             }
826             }
827              
828 0         0 $syslog_send = \&_syslog_send_socket;
829              
830 0         0 return 1;
831             }
832              
833             sub connect_native {
834 1     1 0 1 my ($errs) = @_;
835 1         2 my $logopt = 0;
836              
837             # reconstruct the numeric equivalent of the options
838 1         4 for my $opt (keys %options) {
839 6 100       12 $logopt += xlate($opt) if $options{$opt}
840             }
841              
842 1         3 openlog_xs($ident, $logopt, xlate($facility));
843 1         2 $syslog_send = \&_syslog_send_native;
844              
845 1         6 return 1;
846             }
847              
848             sub connect_eventlog {
849 0     0 0 0 my ($errs) = @_;
850              
851 0         0 $syslog_xobj = Sys::Syslog::Win32::_install();
852 0         0 $syslog_send = \&Sys::Syslog::Win32::_syslog_send;
853              
854 0         0 return 1;
855             }
856              
857             sub connect_console {
858 0     0 0 0 my ($errs) = @_;
859 0 0       0 if (!-w '/dev/console') {
860 0         0 push @$errs, "console is not writable";
861 0         0 return 0;
862             }
863 0         0 $syslog_send = \&_syslog_send_console;
864 0         0 return 1;
865             }
866              
867             # To test if the connection is still good, we need to check if any
868             # errors are present on the connection. The errors will not be raised
869             # by a write. Instead, sockets are made readable and the next read
870             # would cause the error to be returned. Unfortunately the syslog
871             # 'protocol' never provides anything for us to read. But with
872             # judicious use of select(), we can see if it would be readable...
873             sub connection_ok {
874 4 0 33 4 0 28 return 1 if defined $current_proto and (
      66        
875             $current_proto eq 'native' or $current_proto eq 'console'
876             or $current_proto eq 'eventlog'
877             );
878              
879 2         3 my $rin = '';
880 2         8 vec($rin, fileno(SYSLOG), 1) = 1;
881 2         13 my $ret = select $rin, undef, $rin, $sock_timeout;
882 2 50       10 return ($ret ? 0 : 1);
883             }
884              
885             sub disconnect_log {
886 1     1 0 3 $connected = 0;
887 1         3 $syslog_send = undef;
888              
889 1 50 33     13 if (defined $current_proto and $current_proto eq 'native') {
    0 0        
890 1         6 closelog_xs();
891 1         3 unshift @fallbackMethods, $current_proto;
892 1         3 $current_proto = undef;
893 1         3 return 1;
894             }
895             elsif (defined $current_proto and $current_proto eq 'eventlog') {
896 0         0 $syslog_xobj->Close();
897 0         0 unshift @fallbackMethods, $current_proto;
898 0         0 $current_proto = undef;
899 0         0 return 1;
900             }
901              
902 0         0 return close SYSLOG;
903             }
904              
905              
906             #
907             # Wrappers around eval() that makes sure that nobody, ever knows that
908             # we wanted to poke & test if something was here or not. This is needed
909             # because some applications are trying to be too smart, install their
910             # own __DIE__ handler, and mysteriously, things are starting to fail
911             # when they shouldn't. SpamAssassin among them.
912             #
913             sub silent_eval (&) {
914 0     0 0 0 local($SIG{__DIE__}, $SIG{__WARN__}, $@);
915 0         0 return eval { $_[0]->() }
  0         0  
916             }
917              
918             sub can_load_sys_syslog_win32 {
919 6     6 0 12 my ($verbose) = @_;
920 6         37 local($SIG{__DIE__}, $SIG{__WARN__}, $@);
921 6         39 (my $module_path = __FILE__) =~ s:Syslog.pm$:Syslog/Win32.pm:;
922 6 50       12 my $loaded = eval { require $module_path } ? 1 : 0;
  6         360  
923 6 50 33     52 warn $@ if not $loaded and $verbose;
924 6         43 return $loaded
925             }
926              
927              
928             "Eighth Rule: read the documentation."
929              
930             __END__