File Coverage

blib/lib/Sys/Syslog.pm
Criterion Covered Total %
statement 263 406 64.7
branch 110 222 49.5
condition 26 76 34.2
subroutine 41 51 80.3
pod 5 19 26.3
total 445 774 57.4


line stmt bran cond sub pod time code
1             package Sys::Syslog;
2 11     11   426604 use strict;
  11         72  
  11         272  
3 11     11   47 use warnings;
  11         13  
  11         270  
4 11     11   54 use warnings::register;
  11         16  
  11         953  
5 11     11   88 use Carp;
  11         15  
  11         615  
6 11     11   56 use Config;
  11         14  
  11         341  
7 11     11   44 use Exporter ();
  11         14  
  11         175  
8 11     11   45 use File::Basename;
  11         17  
  11         812  
9 11     11   4708 use POSIX qw< strftime setlocale LC_TIME >;
  11         57967  
  11         48  
10 11     11   16272 use Socket qw< :all >;
  11         20562  
  11         10175  
11             require 5.005;
12              
13              
14             *import = \&Exporter::import;
15              
16              
17 11     11   157 { no strict 'vars';
  11         17  
  11         1736  
18             $VERSION = '0.36';
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 11     11   60 use constant HAVE_GETPROTOBYNAME => $Config::Config{d_getpbyname};
  11         17  
  11         1415  
82 11     11   78 use constant HAVE_GETPROTOBYNUMBER => $Config::Config{d_getpbynumber};
  11         33  
  11         871  
83 11     11   62 use constant HAVE_SETLOCALE => $Config::Config{d_setlocale};
  11         77  
  11         1066  
84 11 50   11   64 use constant HAVE_IPPROTO_TCP => defined &Socket::IPPROTO_TCP ? 1 : 0;
  11         21  
  11         657  
85 11 50   11   61 use constant HAVE_IPPROTO_UDP => defined &Socket::IPPROTO_UDP ? 1 : 0;
  11         14  
  11         664  
86 11 50   11   59 use constant HAVE_TCP_NODELAY => defined &Socket::TCP_NODELAY ? 1 : 0;
  11         34  
  11         652  
87              
88 11         588 use constant SOCKET_IPPROTO_TCP =>
89             HAVE_IPPROTO_TCP ? Socket::IPPROTO_TCP
90             : HAVE_GETPROTOBYNAME ? scalar getprotobyname("tcp")
91 11     11   54 : 6;
  11         16  
92              
93 11         507 use constant SOCKET_IPPROTO_UDP =>
94             HAVE_IPPROTO_UDP ? Socket::IPPROTO_UDP
95             : HAVE_GETPROTOBYNAME ? scalar getprotobyname("udp")
96 11     11   52 : 17;
  11         17  
97              
98 11     11   55 use constant SOCKET_TCP_NODELAY => HAVE_TCP_NODELAY ? Socket::TCP_NODELAY : 1;
  11         17  
  11         550  
99              
100              
101             #
102             # Public variables
103             #
104 11     11   60 use vars qw($host); # host to send syslog messages to (see notes at end)
  11         13  
  11         609  
105              
106             #
107             # Prototypes
108             #
109             sub silent_eval (&);
110              
111             #
112             # Global variables
113             #
114 11     11   53 use vars qw($facility);
  11         16  
  11         3207  
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 11     11   66 no strict 'vars';
  11         23  
  11         1121  
191 3     3   788 my $constname;
192 3         12 ($constname = $AUTOLOAD) =~ s/.*:://;
193 3 50       9 croak "Sys::Syslog::constant() not defined" if $constname eq 'constant';
194 3         12 my ($error, $val) = constant($constname);
195 3 50       291 croak $error if $error;
196 11     11   67 no strict 'refs';
  11         14  
  11         24992  
197 0     0   0 *$AUTOLOAD = sub { $val };
  0         0  
198 0         0 goto &$AUTOLOAD;
199             }
200              
201              
202             sub openlog {
203 11     11 1 6146 ($ident, my $logopt, $facility) = @_;
204              
205             # default values
206 11   0     150 $ident ||= basename($0) || getlogin() || getpwuid($<) || 'syslog';
      66        
207 11   100     44 $logopt ||= '';
208 11   66     41 $facility ||= LOG_USER();
209              
210 11         50 for my $opt (split /\b/, $logopt) {
211 10 100       37 $options{$opt} = 1 if exists $options{$opt}
212             }
213              
214 11 50       44 $err_sub = delete $options{nofatal} ? \&warnings::warnif : \&croak;
215 11 100       34 return 1 unless $options{ndelay};
216 6         20 connect_log();
217             }
218              
219             sub closelog {
220 4 100   4 1 1595 disconnect_log() if $connected;
221 4         20 $options{$_} = 0 for keys %options;
222 4         9 $facility = $ident = "";
223 4         7 $connected = 0;
224 4         7 return 1
225             }
226              
227             sub setlogmask {
228 11     11 1 14780 my $oldmask = $maskpri;
229 11 100       23 $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 22     22 1 43178 my %opt;
309              
310             # handle arguments
311             # - old API: setlogsock($sock_type, $sock_path, $sock_timeout)
312             # - new API: setlogsock(\%options)
313 22 100 66     380 croak "setlogsock(): Invalid number of arguments"
314             unless @_ >= 1 and @_ <= 3;
315              
316 20 100       50 if (my $ref = ref $_[0]) {
317 12 100       35 if ($ref eq "HASH") {
    100          
318 2         3 %opt = %{ $_[0] };
  2         6  
319 2 50       143 croak "setlogsock(): No argument given" unless keys %opt;
320             }
321             elsif ($ref eq "ARRAY") {
322 8         22 @opt{qw< type path timeout >} = @_;
323             }
324             else {
325 2         171 croak "setlogsock(): Unexpected \L$ref\E reference"
326             }
327             }
328             else {
329 8         25 @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 16         87 . join ", ", map { "'$_'" } sort keys %mechanism;
  144         242  
335 16 100       192 croak sprintf $diag_invalid_type, "" unless defined $opt{type};
336 14 100       35 my @sock_types = ref $opt{type} eq "ARRAY" ? @{$opt{type}} : ($opt{type});
  8         17  
337 14         19 my @tmp;
338              
339 14         24 for my $sock_type (@sock_types) {
340             carp sprintf $diag_invalid_type, " '$sock_type'" and next
341 14 50 0     25 unless exists $mechanism{$sock_type};
342 14 100 50     36 push @tmp, "tcp", "udp" and next if $sock_type eq "inet";
343 12         26 push @tmp, $sock_type;
344             }
345              
346 14         18 @sock_types = @tmp;
347              
348             # set global options
349 14 100       30 $syslog_path = $opt{path} if defined $opt{path};
350 14 50       25 $host = $opt{host} if defined $opt{host};
351 14 50       33 $sock_timeout = $opt{timeout} if defined $opt{timeout};
352 14 50       24 $sock_port = $opt{port} if defined $opt{port};
353              
354 14 50       23 disconnect_log() if $connected;
355 14         43 $transmit_ok = 0;
356 14         93 @fallbackMethods = ();
357 14         41 @connectMethods = ();
358 14         23 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       38 if ( $mechanism{$sock_type}{check}->() ) {
363 7         13 push @connectMethods, $sock_type;
364 7         12 $found = 1;
365             }
366             else {
367             warnings::warnif("setlogsock(): type='$sock_type': "
368 9         498 . $mechanism{$sock_type}{err_msg});
369             }
370             }
371              
372             # if no mechanism worked from the given ones, use the default ones
373 14 100       40 @connectMethods = @defaultMethods unless @connectMethods;
374              
375 14         46 return $found;
376             }
377              
378             sub syslog {
379 15     15 1 7465 my ($priority, $mask, @args) = @_;
380 15         37 my ($message, $buf);
381 15         0 my (@words, $num, $numpri, $numfac, $sum);
382 15         20 my $failed = undef;
383 15         19 my $fail_time = undef;
384 15         43 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 15 100       46 openlog() unless $ident;
389              
390 15         23 local $facility = $facility; # may need to change temporarily.
391              
392 15 100       312 croak "syslog: expecting argument \$priority" unless defined $priority;
393 11 100       163 croak "syslog: expecting argument \$format" unless defined $mask;
394              
395 9 100       51 if ($priority =~ /^\d+$/) {
    100          
396 2         8 $numpri = LOG_PRI($priority);
397 2         6 $numfac = LOG_FAC($priority) << 3;
398 2 100       7 undef $numfac if $numfac == 0; # no facility given => use default
399             }
400             elsif ($priority =~ /^\w+/) {
401             # Allow "level" or "level|facility".
402 6         21 @words = split /\W+/, $priority, 2;
403              
404 6         10 undef $numpri;
405 6         7 undef $numfac;
406              
407 6         13 for my $word (@words) {
408 9 50       15 next if length $word == 0;
409              
410             # Translate word to number.
411 9         19 $num = xlate($word);
412              
413 9 100 66     54 if ($num < 0) {
    100          
414 1         65 croak "syslog: invalid level/facility: $word"
415             }
416             elsif ($num <= LOG_PRIMASK() and $word ne "kern") {
417 5 100       85 croak "syslog: too many levels given: $word"
418             if defined $numpri;
419 4         10 $numpri = $num;
420             }
421             else {
422 3 100       81 croak "syslog: too many facilities given: $word"
423             if defined $numfac;
424 2 50       7 $facility = $word if $word =~ /^[A-Za-z]/;
425 2         5 $numfac = $num;
426             }
427             }
428             }
429             else {
430 1         84 croak "syslog: invalid level/facility: $priority"
431             }
432              
433 5 50       10 croak "syslog: level must be given" unless defined $numpri;
434              
435             # don't log if priority is below mask level
436 5 50       21 return 0 unless LOG_MASK($numpri) & $maskpri;
437              
438 5 100       23 if (not defined $numfac) { # Facility not specified in this call.
439 3 50       27 $facility = 'user' unless $facility;
440 3         18 $numfac = xlate($facility);
441             }
442              
443 5 100       14 connect_log() unless $connected;
444              
445 5 100       25 if ($mask =~ /%m/) {
446             # escape percent signs for sprintf()
447 1 50       2 $error =~ s/%/%%/g if @args;
448             # replace %m with $error, if preceded by an even number of percent signs
449 1         9 $mask =~ s/(?
450             }
451              
452             # add (or not) a newline
453 5 50 33     42 $mask .= "\n" if !$options{noeol} and rindex($mask, "\n") == -1;
454 5 50       22 $message = @args ? sprintf($mask, @args) : $mask;
455              
456 5 50       14 if ($current_proto eq 'native') {
    0          
457 5         9 $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 5 50 33     15 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 5   66     18 while (scalar @fallbackMethods || $syslog_send) {
507 5 50 33     12 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 5 50 33     35 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 5 50       10 connect_log() unless $connected;
524 5 50 33     27 $failed = undef if ($current_proto && $failed && $current_proto eq $failed);
      33        
525              
526 5 50       20 if ($syslog_send) {
527 5 50       15 if ($syslog_send->($buf, $numpri, $numfac)) {
528 5         9 $transmit_ok++;
529 5         21 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   5 my ($buf) = @_;
586 2         134 return syswrite(SYSLOG, $buf, length($buf));
587             #return send(SYSLOG, $buf, 0);
588             }
589              
590             sub _syslog_send_native {
591 5     5   13 my ($buf, $numpri, $numfac) = @_;
592 5         290 syslog_xs($numpri|$numfac, $buf);
593 5         24 return 1;
594             }
595              
596              
597             # xlate()
598             # -----
599             # private function to translate names to numeric values
600             #
601             sub xlate {
602 22     22 0 39 my ($name) = @_;
603              
604 22 50       59 return $name+0 if $name =~ /^\s*\d+\s*$/;
605 22         37 $name = uc $name;
606 22 50       52 $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 22         80 my $value = constant($name);
615              
616 22 50       55 if (index($value, "not a valid") >= 0) {
617 22         34 $name = "Sys::Syslog::$name";
618 11     11   79 $value = eval { no strict "refs"; &$name };
  11         19  
  11         1395  
  22         27  
  22         86  
619 22 100       53 $value = $@ unless defined $value;
620             }
621              
622 22 100       53 $value = -1 if index($value, "not a valid") >= 0;
623              
624 22 50       186 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 7 50   7 0 30 @fallbackMethods = @connectMethods unless scalar @fallbackMethods;
636              
637 7 50 33     25 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 7         24 $connected = 0;
643 7         11 my @errs = ();
644 7         13 my $proto = undef;
645              
646 7         19 while ($proto = shift @fallbackMethods) {
647 11     11   72 no strict 'refs';
  11         15  
  11         15856  
648 7         19 my $fn = "connect_$proto";
649 7 50       137 $connected = &$fn(\@errs) if defined &$fn;
650 7 100       36 last if $connected;
651             }
652              
653 7         13 $transmit_ok = 0;
654 7 100       24 if ($connected) {
655 4         6 $current_proto = $proto;
656 4         18 my ($old) = select(SYSLOG); $| = 1; select($old);
  4         11  
  4         24  
657             } else {
658 3         6 @fallbackMethods = ();
659 3         374 $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 5 my ($errs) = @_;
707              
708             my $port = $sock_port
709 2   33     6 || eval { local $SIG{__DIE__}; getservbyname('syslog', 'udp') };
710 2 50       9 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       5 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         4 $addr = INADDR_LOOPBACK;
724             }
725 2         9 $addr = sockaddr_in($port, $addr);
726              
727 2 50       106 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       41 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         9 _syslog_send_socket("");
739 2 50       57 if (!connection_ok()) {
740 2         5 push @$errs, "udp connect: nobody listening";
741 2         6 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 3 my ($errs) = @_;
751             # might want syslog_path to be variable based on syslog.h (if only
752             # it were in there!)
753 1 50       3 $syslog_path = '/dev/conslog' unless defined $syslog_path;
754              
755 1 50       16 if (!-w $syslog_path) {
756 1         4 push @$errs, "stream $syslog_path is not writable";
757 1         3 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 4     4 0 8 my ($errs) = @_;
835 4         7 my $logopt = 0;
836              
837             # reconstruct the numeric equivalent of the options
838 4         16 for my $opt (keys %options) {
839 24 100       48 $logopt += xlate($opt) if $options{$opt}
840             }
841              
842 4         11 openlog_xs($ident, $logopt, xlate($facility));
843 4         61 $syslog_send = \&_syslog_send_native;
844              
845 4         44 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 7 0 33 7 0 35 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         5 my $rin = '';
880 2         11 vec($rin, fileno(SYSLOG), 1) = 1;
881 2         23 my $ret = select $rin, undef, $rin, $sock_timeout;
882 2 50       10 return ($ret ? 0 : 1);
883             }
884              
885             sub disconnect_log {
886 3     3 0 7 $connected = 0;
887 3         5 $syslog_send = undef;
888              
889 3 50 33     29 if (defined $current_proto and $current_proto eq 'native') {
    0 0        
890 3         58 closelog_xs();
891 3         7 unshift @fallbackMethods, $current_proto;
892 3         6 $current_proto = undef;
893 3         5 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 12     12 0 25 my ($verbose) = @_;
920 12         53 local($SIG{__DIE__}, $SIG{__WARN__}, $@);
921 12         70 (my $module_path = __FILE__) =~ s:Syslog.pm$:Syslog/Win32.pm:;
922 12 50       25 my $loaded = eval { require $module_path } ? 1 : 0;
  12         355  
923 12 50 33     66 warn $@ if not $loaded and $verbose;
924 12         71 return $loaded
925             }
926              
927              
928             "Eighth Rule: read the documentation."
929              
930             __END__