--- /dev/null
+# $OpenBSD: Client.pm,v 1.1.1.1 2014/08/20 20:52:14 bluhm Exp $
+
+# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Client;
+use parent 'Proc';
+use Carp;
+use IO::Socket::INET6;
+use Sys::Syslog qw(:standard :extended :macros);
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{ktracefile} ||= "client.ktrace";
+ $args{logfile} ||= "client.log";
+ $args{up} ||= "Openlog";
+ my $self = Proc::new($class, %args);
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ if ($self->{connectdomain}) {
+ my $cs = IO::Socket::INET6->new(
+ Proto => "udp",
+ Domain => $self->{connectdomain},
+ PeerAddr => $self->{connectaddr},
+ PeerPort => $self->{connectport},
+ ) or die ref($self), " socket connect failed: $!";
+ print STDERR "connect sock: ",$cs->sockhost()," ",
+ $cs->sockport(),"\n";
+ print STDERR "connect peer: ",$cs->peerhost()," ",
+ $cs->peerport(),"\n";
+
+ *STDIN = *STDOUT = $self->{cs} = $cs;
+ }
+
+ if ($self->{logsock}) {
+ setlogsock($self->{logsock})
+ or die ref($self), " setlogsock failed: $!";
+ }
+ # we take LOG_UUCP as it is not used nowadays
+ openlog("syslogd-regress", "ndelay,perror,pid", LOG_UUCP);
+}
+
+1;
--- /dev/null
+# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null
+# $OpenBSD: Makefile,v 1.1.1.1 2014/08/20 20:52:14 bluhm Exp $
+
+# The following ports must be installed for the regression tests:
+# p5-IO-Socket-INET6 object interface for AF_INET and AF_INET6 domain sockets
+# p5-Socket6 Perl defines relating to AF_INET6 sockets
+# p5-IO-Socket-SSL perl interface to SSL sockets
+#
+# Check wether all required perl packages are installed. If some
+# are missing print a warning and skip the tests, but do not fail.
+
+PERL_REQUIRE != perl -Mstrict -Mwarnings -e ' \
+ eval { require IO::Socket::INET6 } or print $@; \
+ eval { require Socket6 } or print $@; \
+ eval { require IO::Socket::SSL } or print $@; \
+'
+.if ! empty (PERL_REQUIRE)
+regress:
+ @echo "${PERL_REQUIRE}"
+ @echo install these perl packages for additional tests
+.endif
+
+# Automatically generate regress targets from test cases in directory.
+
+ARGS != cd ${.CURDIR} && ls args-*.pl
+TARGETS ?= ${ARGS}
+REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/}
+CLEANFILES += *.log *.pem *.crt *.key syslogd.conf stamp-*
+CLEANFILES += ktrace.out *.ktrace *.fstat
+
+.MAIN: all
+
+.if make (regress) || make (all)
+.BEGIN:
+ @echo
+ [ -z "${SUDO}" ] || ${SUDO} true
+.END:
+ @echo
+ ${SUDO} /etc/rc.d/syslogd restart
+.endif
+
+# Set variables so that make runs with and without obj directory.
+# Only do that if necessary to keep visible output short.
+
+.if ${.CURDIR} == ${.OBJDIR}
+PERLINC =
+PERLPATH =
+.else
+PERLINC = -I${.CURDIR}
+PERLPATH = ${.CURDIR}/
+.endif
+
+# The arg tests take a perl hash with arguments controlling the
+# test parameters. Generally they consist of client, syslogd, server.
+
+.for a in ${ARGS}
+run-regress-$a: $a
+ @echo '\n======== $@ ========'
+ time SUDO=${SUDO} KTRACE=${KTRACE} SYSLOGD=${SYSLOGD} perl ${PERLINC} ${PERLPATH}syslogd.pl ${PERLPATH}$a
+.endfor
+
+# create the certificates for SSL
+
+127.0.0.1.crt:
+ openssl req -batch -new -nodes -newkey rsa -keyout 127.0.0.1.key -subj /CN=127.0.0.1/ -x509 -out $@
+ ${SUDO} cp 127.0.0.1.crt /etc/ssl/
+ ${SUDO} cp 127.0.0.1.key /etc/ssl/private/
+
+server-cert.pem:
+ openssl req -batch -new -nodes -newkey rsa -keyout server-key.pem -subj /CN=localhost/ -x509 -out $@
+
+${REGRESS_TARGETS:M*ssl*} ${REGRESS_TARGETS:M*https*}: server-cert.pem
+${REGRESS_TARGETS:M*ssl*} ${REGRESS_TARGETS:M*https*}: 127.0.0.1.crt
+
+# make perl syntax check for all args files
+
+.PHONY: syntax
+
+syntax: stamp-syntax
+
+stamp-syntax: ${ARGS}
+.for a in ${ARGS}
+ @perl -c ${PERLPATH}$a
+.endfor
+ @date >$@
+
+.include <bsd.regress.mk>
--- /dev/null
+# $OpenBSD: Proc.pm,v 1.1.1.1 2014/08/20 20:52:14 bluhm Exp $
+
+# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
+# Copyright (c) 2014 Florian Riehm <mail@friehm.de>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Proc;
+use Carp;
+use Errno;
+use IO::File;
+use POSIX;
+use Time::HiRes qw(time alarm sleep);
+
+my %CHILDREN;
+
+sub kill_children {
+ my @pids = @_ ? @_ : keys %CHILDREN
+ or return;
+ my @perms;
+ foreach my $pid (@pids) {
+ if (kill(TERM => $pid) != 1 and $!{EPERM}) {
+ push @perms, $pid;
+ }
+ }
+ if (my $sudo = $ENV{SUDO} and @perms) {
+ local $?; # do not modify during END block
+ my @cmd = ($sudo, '/bin/kill', '-TERM', @perms);
+ system(@cmd);
+ }
+ delete @CHILDREN{@pids};
+}
+
+BEGIN {
+ $SIG{TERM} = $SIG{INT} = sub {
+ my $sig = shift;
+ kill_children();
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+ POSIX::raise($sig);
+ };
+}
+
+END {
+ kill_children();
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+}
+
+sub new {
+ my $class = shift;
+ my $self = { @_ };
+ $self->{down} ||= "Shutdown";
+ $self->{func} && ref($self->{func}) eq 'CODE'
+ or croak "$class func not given";
+ !$self->{ktrace} || $self->{ktracefile}
+ or croak "$class ktrace file not given";
+ $self->{logfile}
+ or croak "$class log file not given";
+ open(my $fh, '>', $self->{logfile})
+ or die "$class log file $self->{logfile} create failed: $!";
+ $fh->autoflush;
+ $self->{log} = $fh;
+ return bless $self, $class;
+}
+
+sub run {
+ my $self = shift;
+
+ pipe(my $reader, my $writer)
+ or die ref($self), " pipe to child failed: $!";
+ defined(my $pid = fork())
+ or die ref($self), " fork child failed: $!";
+ if ($pid) {
+ $CHILDREN{$pid} = 1;
+ $self->{pid} = $pid;
+ close($reader);
+ $self->{pipe} = $writer;
+ return $self;
+ }
+ %CHILDREN = ();
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+ $SIG{__DIE__} = sub {
+ die @_ if $^S;
+ warn @_;
+ IO::Handle::flush(\*STDERR);
+ POSIX::_exit(255);
+ };
+ open(STDERR, '>&', $self->{log})
+ or die ref($self), " dup STDERR failed: $!";
+ open(STDOUT, '>&', $self->{log})
+ or die ref($self), " dup STDOUT failed: $!";
+ close($writer);
+ open(STDIN, '<&', $reader)
+ or die ref($self), " dup STDIN failed: $!";
+ close($reader);
+
+ if ($self->{ktrace}) {
+ my @cmd = ("ktrace", "-f", $self->{ktracefile}, "-p", $$);
+ system(@cmd)
+ and die ref($self), " system '@cmd' failed: $?";
+ }
+ $self->child();
+ print STDERR $self->{up}, "\n";
+ $self->{func}->($self);
+ print STDERR "Shutdown", "\n";
+
+ IO::Handle::flush(\*STDOUT);
+ IO::Handle::flush(\*STDERR);
+ POSIX::_exit(0);
+}
+
+sub wait {
+ my $self = shift;
+ my $flags = shift;
+
+ my $pid = $self->{pid}
+ or croak ref($self), " no child pid";
+ my $kid = waitpid($pid, $flags);
+ if ($kid > 0) {
+ my $status = $?;
+ my $code;
+ $code = "exit: ". WEXITSTATUS($?) if WIFEXITED($?);
+ $code = "signal: ". WTERMSIG($?) if WIFSIGNALED($?);
+ $code = "stop: ". WSTOPSIG($?) if WIFSTOPPED($?);
+ delete $CHILDREN{$pid} if WIFEXITED($?) || WIFSIGNALED($?);
+ return wantarray ? ($kid, $status, $code) : $kid;
+ }
+ return $kid;
+}
+
+sub loggrep {
+ my $self = shift;
+ my($regex, $timeout) = @_;
+
+ my $end = time() + $timeout if $timeout;
+
+ do {
+ my($kid, $status, $code) = $self->wait(WNOHANG);
+ if ($kid > 0 && $status != 0) {
+ # child terminated with failure
+ die ref($self), " child status: $status $code";
+ }
+ open(my $fh, '<', $self->{logfile})
+ or die ref($self), " log file open failed: $!";
+ my @match = grep { /$regex/ } <$fh>;
+ return wantarray ? @match : $match[0] if @match;
+ close($fh);
+ # pattern not found
+ if ($kid == 0) {
+ # child still running, wait for log data
+ sleep .1;
+ } else {
+ # child terminated, no new log data possible
+ return;
+ }
+ } while ($timeout and time() < $end);
+
+ return;
+}
+
+sub up {
+ my $self = shift;
+ my $timeout = shift || 10;
+ $self->loggrep(qr/$self->{up}/, $timeout)
+ or croak ref($self), " no '$self->{up}' in $self->{logfile} ".
+ "after $timeout seconds";
+ return $self;
+}
+
+sub down {
+ my $self = shift;
+ my $timeout = shift || 30;
+ $self->loggrep(qr/$self->{down}/, $timeout)
+ or croak ref($self), " no '$self->{down}' in $self->{logfile} ".
+ "after $timeout seconds";
+ return $self;
+}
+
+sub kill_child {
+ my $self = shift;
+ kill_children($self->{pid});
+ return $self;
+}
+
+1;
--- /dev/null
+Run syslogd regressions tests. As only one syslogd can run per
+machine, each test kills any syslogd first. At the end the system's
+syslogd gets restarted.
+The test framework runs a client, and a server, and a syslogd. The
+messages are passed via the log socket or via UDP from the client
+to syslogd. From there UDP transport is used to reach the server.
+All processes write log files where the message has to show up.
+The test arguments are kept in the args-*.pl files.
+The content of a log file, the data sent to a pipe process and what
+the server received are checked. The invocation of the sendsyslog(2)
+syscall is checked with ktrace, the open file descriptors of syslogd
+are checked with fstat.
+
+SUDO=sudo
+As syslogd needs root privileges either run the tests as root or
+set this variable and run make as a regular user. Only the code
+that requires it, is run as root.
+
+KTRACE=ktrace
+Set this variable if you want a ktrace output from syslogd. Note that
+ktrace is invoked after sudo as sudo would disable it.
+
+SYSLOGD=/usr/src/usr.sbin/syslogd/obj/syslogd
+Start an alternative syslogd program that is not in the path.
--- /dev/null
+# $OpenBSD: Server.pm,v 1.1.1.1 2014/08/20 20:52:14 bluhm Exp $
+
+# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Server;
+use parent 'Proc';
+use Carp;
+use Socket;
+use Socket6;
+use IO::Socket;
+use IO::Socket::INET6;
+use IO::Socket::SSL;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{ktracefile} ||= "server.ktrace";
+ $args{logfile} ||= "server.log";
+ $args{up} ||= "Accepted";
+ my $self = Proc::new($class, %args);
+ $self->{listenprotocol} ||= "udp";
+ $self->{listendomain}
+ or croak "$class listen domain not given";
+ $SSL_ERROR = "";
+ my $iosocket = $self->{listenprotocol} eq "tls" ?
+ "IO::Socket::SSL" : "IO::Socket::INET6";
+ my $proto = $self->{listenprotocol};
+ $proto = "tcp" if $proto eq "tls";
+ my $ls = $iosocket->new(
+ Proto => $proto,
+ ReuseAddr => 1,
+ Domain => $self->{listendomain},
+ $self->{listenaddr} ? (LocalAddr => $self->{listenaddr}) : (),
+ $self->{listenport} ? (LocalPort => $self->{listenport}) : (),
+ SSL_key_file => "server-key.pem",
+ SSL_cert_file => "server-cert.pem",
+ SSL_verify_mode => SSL_VERIFY_NONE,
+ ) or die ref($self), " $iosocket socket listen failed: $!,$SSL_ERROR";
+ if ($self->{listenprotocol} eq "tcp") {
+ listen($ls, 1)
+ or die ref($self), " socket failed: $!";
+ }
+ my $log = $self->{log};
+ print $log "listen sock: ",$ls->sockhost()," ",$ls->sockport(),"\n";
+ $self->{listenaddr} = $ls->sockhost() unless $self->{listenaddr};
+ $self->{listenport} = $ls->sockport() unless $self->{listenport};
+ $self->{ls} = $ls;
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ my $iosocket = $self->{ssl} ? "IO::Socket::SSL" : "IO::Socket::INET6";
+ my $as = $self->{ls};
+ if ($self->{listenprotocol} ne "udp") {
+ $as = $self->{ls}->accept()
+ or die ref($self), " $iosocket socket accept failed: $!";
+ print STDERR "accept sock: ",$as->sockhost()," ",
+ $as->sockport(),"\n";
+ print STDERR "accept peer: ",$as->peerhost()," ",
+ $as->peerport(),"\n";
+ }
+
+ *STDIN = *STDOUT = $self->{as} = $as;
+}
+
+1;
--- /dev/null
+# $OpenBSD: Syslogd.pm,v 1.1.1.1 2014/08/20 20:52:14 bluhm Exp $
+
+# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
+# Copyright (c) 2014 Florian Riehm <mail@friehm.de>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package Syslogd;
+use parent 'Proc';
+use Carp;
+use Cwd;
+use File::Basename;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{fstatfile} ||= "syslogd.fstat";
+ $args{logfile} ||= "syslogd.log";
+ $args{up} ||= "syslogd: started";
+ $args{down} ||= "syslogd: exiting";
+ $args{func} = sub { Carp::confess "$class func may not be called" };
+ $args{conffile} ||= "syslogd.conf";
+ $args{outfile} ||= "file.log";
+ $args{outpipe} ||= "pipe.log";
+ my $self = Proc::new($class, %args);
+ $self->{connectaddr}
+ or croak "$class connect addr not given";
+
+ _make_abspath(\$self->{$_}) foreach (qw(conffile outfile outpipe));
+
+ # substitute variables in config file
+ my $connectprotocol = $self->{connectprotocol};
+ my $connectdomain = $self->{connectdomain};
+ my $connectaddr = $self->{connectaddr};
+ my $connectport = $self->{connectport};
+
+ open(my $fh, '>', $self->{conffile})
+ or die ref($self), " create conf file $self->{conffile} failed: $!";
+ print $fh "*.*\t$self->{outfile}\n";
+ print $fh "*.*\t|dd of=$self->{outpipe} status=none\n";
+ my $loghost = $self->{loghost};
+ if ($loghost) {
+ $loghost =~ s/(\$[a-z]+)/$1/eeg;
+ } else {
+ $loghost = "\@$connectaddr";
+ $loghost .= ":$connectport" if $connectport;
+ }
+ print $fh "*.*\t$loghost\n";
+ close $fh;
+
+ open($fh, '>', $self->{outfile})
+ or die ref($self), " create log file $self->{outfile} failed: $!";
+ close $fh;
+
+ open($fh, '>', $self->{outpipe})
+ or die ref($self), " create pipe file $self->{outpipe} failed: $!";
+ close $fh;
+ chmod(0666, $self->{outpipe})
+ or die ref($self), " chmod pipe file $self->{outpipe} failed: $!";
+
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+ my @sudo = $ENV{SUDO} ? $ENV{SUDO} : ();
+
+ my @pkill = (@sudo, "pkill", "-x", "syslogd");
+ my @pgrep = ("pgrep", "-x", "syslogd");
+ system(@pkill) && $? != 256
+ and die ref($self), " system '@pkill' failed: $?";
+ while ($? == 0) {
+ print STDERR "syslogd still running\n";
+ system(@pgrep) && $? != 256
+ and die ref($self), " system '@pgrep' failed: $?";
+ }
+ print STDERR "syslogd not running\n";
+
+ my @ktrace = $ENV{KTRACE} ? ($ENV{KTRACE}, "-i") : ();
+ my $syslogd = $ENV{SYSLOGD} ? $ENV{SYSLOGD} : "syslogd";
+ my @cmd = (@sudo, @ktrace, $syslogd, "-d", "-f", $self->{conffile});
+ push @cmd, @{$self->{options}} if $self->{options};
+ print STDERR "execute: @cmd\n";
+ exec @cmd;
+ die ref($self), " exec '@cmd' failed: $!";
+}
+
+sub up {
+ my $self = Proc::up(shift, @_);
+
+ if ($self->{fstat}) {
+ open(my $fh, '>', $self->{fstatfile}) or die ref($self),
+ " open $self->{fstatfile} for writing failed: $!";
+ my @cmd = ("fstat");
+ open(my $fs, '-|', @cmd)
+ or die ref($self), " open pipe from '@cmd' failed: $!";
+ print $fh grep { /^\w+ *syslogd *\d+/ } <$fs>;
+ close($fs) or die ref($self), $! ?
+ " close pipe from '@cmd' failed: $!" :
+ " command '@cmd' failed: $?";
+ close($fh)
+ or die ref($self), " close $self->{fstatfile} failed: $!";
+ }
+}
+
+sub _make_abspath {
+ my $file = ref($_[0]) ? ${$_[0]} : $_[0];
+ if (substr($file, 0, 1) ne "/") {
+ $file = getcwd(). "/". $file;
+ ${$_[0]} = $file if ref($_[0]);
+ }
+ return $file;
+}
+
+1;
--- /dev/null
+# The client writes a message to Sys::Syslog UDP method.
+# The syslogd writes it into a file and through a pipe without dns.
+# The syslogd passes it via UDP to the loghost.
+# The server receives the message on its UDP socket.
+# Find the message in client, file, pipe, syslogd, server log.
+# Check that the file log contains the 127.0.0.1 address.
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ logsock => { type => "udp", host => "127.0.0.1", port => 514 },
+ },
+ syslogd => {
+ options => ["-un"],
+ },
+ file => {
+ loggrep => qr/ 127.0.0.1 syslogd-regress\[\d+\]: /. get_log(),
+ },
+);
+
+1;
--- /dev/null
+# The client writes a message to Sys::Syslog UDP method.
+# The syslogd writes it into a file and through a pipe.
+# The syslogd passes it via UDP to the loghost.
+# The server receives the message on its UDP socket.
+# Find the message in client, file, pipe, syslogd, server log.
+# Check that the file log contains the localhost name.
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ logsock => { type => "udp", host => "127.0.0.1", port => 514 },
+ },
+ syslogd => {
+ options => ["-u"],
+ },
+ file => {
+ loggrep => qr/ localhost syslogd-regress\[\d+\]: /. get_log(),
+ },
+);
+
+1;
--- /dev/null
+# The client writes a message to a localhost IPv4 UDP socket.
+# The syslogd writes it into a file and through a pipe without dns.
+# The syslogd passes it via UDP to the loghost.
+# The server receives the message on its UDP socket.
+# Find the message in client, file, pipe, syslogd, server log.
+# Check that the file log contains the 127.0.0.1 address.
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ connect => { domain => AF_INET, addr => "127.0.0.1", port => 514 },
+ },
+ syslogd => {
+ options => ["-un"],
+ },
+ file => {
+ loggrep => qr/ 127.0.0.1 /. get_log(),
+ },
+);
+
+1;
--- /dev/null
+# The client writes a message to a localhost IPv4 UDP socket.
+# The syslogd writes it into a file and through a pipe.
+# The syslogd passes it via UDP to the loghost.
+# The server receives the message on its UDP socket.
+# Find the message in client, file, pipe, syslogd, server log.
+# Check that the file log contains the localhost name.
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ connect => { domain => AF_INET, addr => "127.0.0.1", port => 514 },
+ },
+ syslogd => {
+ options => ["-u"],
+ },
+ file => {
+ loggrep => qr/ localhost /. get_log(),
+ },
+);
+
+1;
--- /dev/null
+# Test with default values, that is:
+# The client writes a message to Sys::Syslog native method.
+# The syslogd writes it into a file and through a pipe.
+# The syslogd passes it via UDP to the loghost.
+# The server receives the message on its UDP socket.
+# Find the message in client, file, pipe, syslogd, server log.
+
+use strict;
+use warnings;
+
+our %args = (
+);
+
+1;
--- /dev/null
+# The client writes a message to Sys::Syslog native method.
+# The syslogd writes it into a file and through a pipe.
+# The syslogd passes it via UDP to the loghost.
+# The server receives the message on its UDP socket.
+# Find the message in client, file, pipe, syslogd, server log.
+# Create a ktrace dump of the client and check that sendsyslog(2)
+# has been used.
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ ktrace => 1,
+ kdump => {
+ qr/CALL sendsyslog/ => 2,
+ qr/GIO fd -1 wrote \d+ bytes/ => 2,
+ qr/RET sendsyslog 0/ => 2,
+ },
+ },
+);
+
+1;
--- /dev/null
+# Test with default values, that is:
+# The client writes a message to a localhost IPv4 UDP socket.
+# The syslogd writes it into a file and through a pipe.
+# The syslogd passes it via UDP to the loghost.
+# The server receives the message on its UDP socket.
+# Find the message in client, file, pipe, syslogd, server log.
+# Check that the syslogd has one IPv4 socket in fstat output.
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ connect => { domain => AF_INET, addr => "127.0.0.1", port => 514 },
+ },
+ syslogd => {
+ fstat => 1,
+ options => ["-nu"],
+ },
+ fstat => {
+ loggrep => {
+ qr/ internet dgram udp \*:514$/ => 1,
+ },
+ },
+);
+
+1;
--- /dev/null
+# $OpenBSD: funcs.pl,v 1.1.1.1 2014/08/20 20:52:14 bluhm Exp $
+
+# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+use Errno;
+use List::Util qw(first);
+use Socket;
+use Socket6;
+use Sys::Syslog qw(:standard :extended :macros);
+use IO::Socket;
+use IO::Socket::INET6;
+
+my $testlog = "syslogd regress test log message";
+my $downlog = "syslogd regress client shutdown";
+
+########################################################################
+# Client funcs
+########################################################################
+
+sub write_log {
+ my $self = shift;
+
+ if ($self->{connectdomain}) {
+ print $testlog;
+ print STDERR $testlog, "\n";
+ } else {
+ syslog(LOG_INFO, $testlog);
+ }
+ write_shutdown($self, @_);
+}
+
+sub write_shutdown {
+ my $self = shift;
+
+ setlogsock("native")
+ or die ref($self), " setlogsock native failed: $!";
+ syslog(LOG_NOTICE, $downlog);
+}
+
+########################################################################
+# Server funcs
+########################################################################
+
+sub read_log {
+ my $self = shift;
+
+ for (;;) {
+ defined(sysread(STDIN, my $line, 8194))
+ or die ref($self), " read log line failed: $!";
+ chomp $line;
+ print STDERR ">>> $line\n";
+ last if $line =~ /$downlog/;
+ }
+}
+
+########################################################################
+# Script funcs
+########################################################################
+
+sub get_log {
+ return $testlog;
+}
+
+sub check_logs {
+ my ($c, $r, $s, %args) = @_;
+
+ return if $args{nocheck};
+
+ check_log($c, $r, $s, %args);
+ check_out($r, %args);
+ check_stat($r, %args);
+ check_kdump($c, $s, %args);
+}
+
+sub check_pattern {
+ my ($name, $proc, $pattern, $func) = @_;
+
+ $pattern = [ $pattern ] unless ref($pattern) eq 'ARRAY';
+ foreach my $pat (@$pattern) {
+ if (ref($pat) eq 'HASH') {
+ while (my($re, $num) = each %$pat) {
+ my @matches = $func->($proc, $re);
+ @matches == $num
+ or die "$name matches '@matches': ",
+ "'$re' => $num";
+ }
+ } else {
+ $func->($proc, $pat)
+ or die "$name log missing pattern: $pat";
+ }
+ }
+}
+
+sub check_log {
+ my ($c, $r, $s, %args) = @_;
+
+ my %name2proc = (client => $c, syslogd => $r, server => $s);
+ foreach my $name (qw(client syslogd server)) {
+ next if $args{$name}{nocheck};
+ my $p = $name2proc{$name} or next;
+ my $pattern = $args{$name}{loggrep} || $testlog;
+ check_pattern($name, $p, $pattern, \&loggrep);
+ }
+}
+
+sub loggrep {
+ my ($proc, $pattern) = @_;
+
+ return $proc->loggrep($pattern);
+}
+
+sub check_out {
+ my ($r, %args) = @_;
+
+ foreach my $name (qw(file pipe)) {
+ next if $args{$name}{nocheck};
+ my $file = $r->{"out$name"} or next;
+ my $pattern = $args{$name}{loggrep} || $testlog;
+ check_pattern($name, $file, $pattern, \&filegrep);
+ }
+}
+
+sub check_stat {
+ my ($r, %args) = @_;
+
+ foreach my $name (qw(fstat)) {
+ next if $args{$name}{nocheck};
+ my $file = $r->{$name} && $r->{"${name}file"} or next;
+ my $pattern = $args{$name}{loggrep} or next;
+ check_pattern($name, $file, $pattern, \&filegrep);
+ }
+}
+
+sub filegrep {
+ my ($file, $pattern) = @_;
+
+ open(my $fh, '<', $file)
+ or die "Open file $file for reading failed: $!";
+ return wantarray ?
+ grep { /$pattern/ } <$fh> : first { /$pattern/ } <$fh>;
+}
+
+sub check_kdump {
+ my ($c, $s, %args) = @_;
+
+ my %name2proc = (client => $c, server => $s);
+ foreach my $name (qw(client server)) {
+ next unless $args{$name}{ktrace};
+ my $p = $name2proc{$name} or next;
+ my $file = $p->{ktracefile} or next;
+ my $pattern = $args{$name}{kdump} or next;
+ check_pattern($name, $file, $pattern, \&kdumpgrep);
+ }
+}
+
+sub kdumpgrep {
+ my ($file, $pattern) = @_;
+
+ my @cmd = ("kdump", "-f", $file);
+ open(my $fh, '-|', @cmd)
+ or die "Open pipe from '@cmd' failed: $!";
+ my @matches = grep { /$pattern/ } <$fh>;
+ close($fh) or die $! ?
+ "Close pipe from '@cmd' failed: $!" :
+ "Command '@cmd' failed: $?";
+ return wantarray ? @matches : $matches[0];
+}
+
+1;
--- /dev/null
+#!/usr/bin/perl
+# $OpenBSD: syslogd.pl,v 1.1.1.1 2014/08/20 20:52:14 bluhm Exp $
+
+# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+use Socket;
+use Socket6;
+
+use Client;
+use Syslogd;
+use Server;
+require 'funcs.pl';
+
+sub usage {
+ die "usage: syslogd.pl [test-args.pl]\n";
+}
+
+my $testfile;
+our %args;
+if (@ARGV and -f $ARGV[-1]) {
+ $testfile = pop;
+ do $testfile
+ or die "Do test file $testfile failed: ", $@ || $!;
+}
+@ARGV == 0 or usage();
+
+foreach my $name (qw(client syslogd server)) {
+ foreach my $action (qw(connect listen)) {
+ my $h = $args{$name}{$action} or next;
+ foreach my $k (qw(protocol domain addr port)) {
+ $args{$name}{"$action$k"} = $h->{$k};
+ }
+ }
+}
+my $s = Server->new(
+ func => \&read_log,
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ %{$args{server}},
+ testfile => $testfile,
+) unless $args{server}{noserver};
+my $r = Syslogd->new(
+ connectaddr => "127.0.0.1",
+ connectport => $s && $s->{listenport},
+ %{$args{syslogd}},
+ testfile => $testfile,
+);
+my $c = Client->new(
+ func => \&write_log,
+ %{$args{client}},
+ testfile => $testfile,
+) unless $args{client}{noclient};
+
+$s->run unless $args{server}{noserver};
+$r->run;
+$r->up;
+$c->run->up unless $args{client}{noclient};
+$s->up unless $args{server}{noserver};
+
+$c->down unless $args{client}{noclient};
+$s->down unless $args{server}{noserver};
+$r->kill_child;
+$r->down;
+
+check_logs($c, $r, $s, %args);