-# $OpenBSD: Makefile,v 1.12 2015/10/09 17:07:06 bluhm Exp $
+# $OpenBSD: Makefile,v 1.13 2015/10/19 20:16:09 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
TARGETS ?= ${ARGS:Nargs-rsyslog*}
.endif
REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/}
+LDFLAGS += -lutil
CLEANFILES += *.log *.log.? *.conf ktrace.out stamp-*
-CLEANFILES += *.out *.sock *.ktrace *.fstat
+CLEANFILES += *.out *.sock *.ktrace *.fstat ttylog
CLEANFILES += *.pem *.req *.crt *.key *.srl empty toobig
.MAIN: all
${REGRESS_TARGETS:M*empty*}: empty
${REGRESS_TARGETS:M*toobig*}: toobig
${REGRESS_TARGETS:M*fake*}: fake-ca.crt
+${REGRESS_TARGETS}: ttylog
# make perl syntax check for all args files
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.
+the server received are checked. Logging to a user's tty is tested
+with a fake login. The invocation of the sendsyslog(2) syscall is
+checked with ktrace, the open file descriptors of syslogd are checked
+with fstat.
When invoked with "make libevent", all tests are executed three
times. They pass the EVENT_NO... environment variables over sudo
into syslogd. This way the select(2) and poll(2) and kqueue(2)
-# $OpenBSD: Syslogd.pm,v 1.15 2015/08/25 20:52:44 bluhm Exp $
+# $OpenBSD: Syslogd.pm,v 1.16 2015/10/19 20:16:09 bluhm Exp $
# Copyright (c) 2010-2015 Alexander Bluhm <bluhm@openbsd.org>
# Copyright (c) 2014 Florian Riehm <mail@friehm.de>
$args{conffile} ||= "syslogd.conf";
$args{outfile} ||= "file.log";
$args{outpipe} ||= "pipe.log";
+ $args{outtty} ||= "tty.log";
if ($args{memory}) {
$args{memory} = {} unless ref $args{memory};
$args{memory}{name} ||= "memory";
or die ref($self), " create conf file $self->{conffile} failed: $!";
print $fh "*.*\t$self->{outfile}\n";
print $fh "*.*\t|dd of=$self->{outpipe}\n";
+ print $fh "*.*\tsyslogd-regress\n";
my $memory = $self->{memory};
print $fh "*.*\t:$memory->{size}:$memory->{name}\n" if $memory;
my $loghost = $self->{loghost};
chmod(0666, $self->{outpipe})
or die ref($self), " chmod pipe file $self->{outpipe} failed: $!";
+ unlink($self->{outtty});
+ open($fh, '>', $self->{outtty})
+ or die ref($self), " create tty file $self->{outtty} failed: $!";
+ close $fh;
+ my @sudo = $ENV{SUDO} ? $ENV{SUDO} : ();
+ my @cmd = (@sudo, "./ttylog", "syslogd-regress", $self->{outtty});
+ open($fh, '|-', @cmd)
+ or die ref($self), " pipe to ttylog $self->{outfile} failed: $!";
+ # remember until object is destroyed, perl autoclose will send EOF
+ $self->{fhtty} = $fh;
+
return $self;
}
"after $timeout seconds";
sleep .1;
}
+
+ while ($self->{fhtty}) {
+ open(my $fh, '<', $self->{outtty}) or die ref($self),
+ " open $self->{outtty} for reading failed: $!";
+ last if grep { /ttylog: started/ } <$fh>;
+ time() < $end
+ or croak ref($self), " no 'started' in $self->{outtty} ".
+ "after $timeout seconds";
+ sleep .1;
+ }
+
return $self;
}
# syslog over TCP appends a \n
loggrep => { qr/^>>> 8193 .{8192}\n/ => 1 },
},
- pipe => {
- nocheck => 1,
- },
file => {
loggrep => { qr/^.{$filelen}\n/ => 1 },
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
listen => { domain => AF_UNSPEC, proto => "tcp", addr => "localhost" },
loggrep => { get_charlog() => 8 },
},
- pipe => {
- nocheck => 1,
- },
file => {
loggrep => { get_charlog() => 8 },
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
listen => { domain => AF_UNSPEC, proto => "tcp", addr => "localhost" },
loggrep => { get_charlog() => 8 },
},
- pipe => {
- nocheck => 1,
- },
file => {
loggrep => { get_charlog() => 8 },
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
listen => { domain => AF_UNSPEC, proto => "tcp", addr => "localhost" },
loggrep => { get_charlog() => 8 },
},
- pipe => {
- nocheck => 1,
- },
file => {
loggrep => { get_charlog() => 8 },
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
},
loggrep => {},
},
- pipe => {
- loggrep => {},
- },
file => {
loggrep => {
qr/syslogd: tcp logger .* connection close/ => 1,
},
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
},
loggrep => {},
},
- pipe => {
- loggrep => {},
- },
file => {
loggrep => {
qr/syslogd: tcp logger .* connection error: $errors/ => 1,
},
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
generate_chars(MAXLINE).qr/$/ => 2,
},
},
- pipe => { loggrep => {} }, # XXX syslogd ignore short writes to pipe
+ pipe => { nocheck => 1 }, # XXX syslogd ignore short writes to pipe
+ tty => { nocheck => 1 },
);
1;
server => { loggrep => \%threegrep },
file => { loggrep => \%threegrep },
pipe => { loggrep => \%threegrep },
+ tty => { loggrep => \%threegrep },
);
1;
generate_chars(MAXLINE).qr/$/ => 2,
},
},
- pipe => { loggrep => {} }, # XXX syslogd ignore short writes to pipe
+ pipe => { nocheck => 1 }, # XXX syslogd ignore short writes to pipe
+ tty => { nocheck => 1 },
);
1;
generate_chars(MAXLINE).qr/$/ => 2,
},
},
- pipe => { loggrep => {} }, # XXX syslogd ignore short writes to pipe
+ pipe => { nocheck => 1 }, # XXX syslogd ignore short writes to pipe
+ tty => { nocheck => 1 }, # XXX syslogd ignore short writes to pipe
);
1;
},
loggrep => {},
},
- pipe => {
- loggrep => {},
- },
file => {
loggrep => {
qr/syslogd: tls logger .* connection close/ => 1,
},
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
},
loggrep => {},
},
- pipe => {
- loggrep => {},
- },
file => {
loggrep => {
qr/syslogd: tls logger .* connection error: read failed: $errors/
=> 1,
},
},
+ pipe => { nocheck => 1, },
+ tty => { nocheck => 1, },
);
1;
},
loggrep => {},
},
- pipe => {
- loggrep => {},
- },
file => {
loggrep => {
qr/syslogd: tls logger .* connection error: /.
qr/SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol/ => 1,
},
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
pipe => {
loggrep => get_testlog(),
},
+ tty => {
+ loggrep => get_testlog(),
+ },
file => {
# Sys::Syslog UDP is broken, it appends a \n\0.
loggrep => qr/ 127.0.0.1 syslogd-regress\[\d+\]: /.get_testlog().qr/ $/,
pipe => {
loggrep => get_testlog(),
},
+ tty => {
+ loggrep => get_testlog(),
+ },
file => {
# Sys::Syslog UDP is broken, it appends a \n\0.
loggrep => qr/ localhost syslogd-regress\[\d+\]: /.get_testlog().qr/ $/,
pipe => {
loggrep => get_testlog(),
},
+ tty => {
+ loggrep => get_testlog(),
+ },
file => {
# Sys::Syslog unix is broken, it appends a \n\0.
loggrep => qr/ $host syslogd-regress\[\d+\]: /.get_testlog().qr/ $/,
# 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 writes it into a file and through a pipe and to tty.
# 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.
get_charlog() => '>=10',
},
},
- pipe => {
- loggrep => {},
- },
file => {
loggrep => {
get_firstlog() => 1,
qr/syslogd: dropped 2[0-9][0-9] messages to remote loghost/ => 1,
},
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
get_charlog() => '>=10',
},
},
- pipe => {
- loggrep => {},
- },
file => {
loggrep => {
get_firstlog() => 1,
qr/syslogd: dropped 2[0-9][0-9] messages to remote loghost/ => 1,
},
},
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
loggrep => {
# If not in startup, each failed PRIV_OPEN_LOG is logged
# to tty, so PRIV_OPEN_TTY fails again.
- qr/syslogd: receive_fd: recvmsg: Message too long/ => 4+2*3,
+ qr/syslogd: receive_fd: recvmsg: Message too long/ => '>='.(4+2*3),
# During first initialization the lockpipe is open. When
# SIGHUP happens it is closed and one more file can be opened.
qr/X FILE:/ => 1+16+1+17,
(map { { loggrep => get_testgrep() } } 0..16),
(map { { loggrep => { qr/./s => 0 } } } 17..19),
],
+ tty => {
+ loggrep => {
+ get_firstlog() => 1,
+ get_testlog() => 0,
+ }
+ }
);
1;
file => {
loggrep => qr/syslogd: accept deferred: Too many open files/,
},
- pipe => { loggrep => {} },
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
qr/RET setsid.* errno / => 0,
},
},
- pipe => {
- nocheck => 1,
- },
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
qr/CALL setsid/ => 0,
},
},
- pipe => {
- nocheck => 1,
- },
+ pipe => { nocheck => 1 },
+ tty => { nocheck => 1 },
);
1;
},
file => { loggrep => (get_between2loggrep())[0] },
pipe => { loggrep => (get_between2loggrep())[0] },
+ tty => { loggrep => (get_between2loggrep())[0] },
);
1;
qr/Logging to FORWTLS \@tls:\/\/localhost:\d+/ => '>=4',
qr/syslogd: loghost .* connection error: /.
qr/handshake failed: error:.*/.
- qr/RSA_padding_check_PKCS1_type_1:block type is not 01/ => 2,
+ qr/RSA_EAY_PUBLIC_DECRYPT:data too large for modulus/ => 2,
get_testgrep() => 1,
},
cacrt => "fake-ca.crt",
--- /dev/null
+# The client writes a message to Sys::Syslog native method.
+# The syslogd writes it into a file and through a pipe and to tty.
+# 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;
+use Sys::Syslog qw(:macros);
+
+our %args = (
+ client => {
+ func => sub {
+ my $self = shift;
+ syslog(LOG_LOCAL5|LOG_ERR, "test message to all users");
+ write_log($self);
+ },
+ },
+ syslogd => {
+ conf => "local5.err\t*",
+ },
+ tty => {
+ loggrep => {
+ qr/Message from syslogd/ => 1,
+ qr/syslogd-regress.* test message to all users/ => 2,
+ },
+ },
+);
+
+1;
-# $OpenBSD: funcs.pl,v 1.25 2015/10/09 17:07:06 bluhm Exp $
+# $OpenBSD: funcs.pl,v 1.26 2015/10/19 20:16:09 bluhm Exp $
# Copyright (c) 2010-2015 Alexander Bluhm <bluhm@openbsd.org>
#
}
sub get_testgrep {
- return qr/$testlog$/;
+ return qr/$testlog\r*$/;
}
sub get_firstlog {
$r->loggrep("bytes transferred", 1) or sleep 1;
}
- foreach my $name (qw(file pipe)) {
+ foreach my $name (qw(file pipe tty)) {
next if $args{$name}{nocheck};
my $file = $r->{"out$name"} or die;
my $pattern = $args{$name}{loggrep} || get_testgrep();
--- /dev/null
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/sockio.h>
+
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <util.h>
+#include <utmp.h>
+
+__dead void usage(void);
+void timeout(int);
+void terminate(int);
+void iostdin(int);
+
+FILE *lg;
+char *tty;
+
+__dead void
+usage()
+{
+ fprintf(stderr, "usage: %s username logfile\n", getprogname());
+ exit(2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buf[8192], ptyname[16], *username, *logfile;
+ struct utmp utmp;
+ int mfd, sfd;
+ ssize_t n;
+ int i;
+
+ if (argc != 3)
+ usage();
+ username = argv[1];
+ logfile = argv[2];
+
+ if ((lg = fopen(logfile, "w")) == NULL)
+ err(1, "fopen %s", logfile);
+ if (setlinebuf(lg) != 0)
+ err(1, "setlinebuf");
+
+ if (signal(SIGTERM, terminate) == SIG_ERR)
+ err(1, "signal SIGTERM");
+ if (signal(SIGINT, terminate) == SIG_ERR)
+ err(1, "signal SIGINT");
+
+ if (openpty(&mfd, &sfd, ptyname, NULL, NULL) == -1)
+ err(1, "openpty");
+ fprintf(lg, "openpty %s\n", ptyname);
+ if ((tty = strrchr(ptyname, '/')) == NULL)
+ errx(1, "tty: %s", ptyname);
+ tty++;
+
+ /* login(3) searches for a controlling tty, use the created one */
+ if (dup2(sfd, 1) == -1)
+ err(1, "dup2 stdout");
+
+ memset(&utmp, 0, sizeof(utmp));
+ strlcpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
+ strlcpy(utmp.ut_name, username, sizeof(utmp.ut_name));
+ time(&utmp.ut_time);
+ login(&utmp);
+ fprintf(lg, "login %s %s\n", username, tty);
+
+ if (signal(SIGIO, iostdin) == SIG_ERR)
+ err(1, "signal SIGIO");
+ if (setpgid(0, 0) == -1)
+ err(1, "setpgid");
+ i = getpid();
+ if (fcntl(0, F_SETOWN, i) == -1 &&
+ ioctl(0, SIOCSPGRP, &i) == -1) /* pipe(2) with F_SETOWN broken */
+ err(1, "fcntl F_SETOWN, ioctl SIOCSPGRP");
+ if (fcntl(0, F_SETFL, O_ASYNC) == -1)
+ err(1, "fcntl O_ASYNC");
+
+ if (signal(SIGALRM, timeout) == SIG_ERR)
+ err(1, "signal SIGALRM");
+ if (alarm(30) == (unsigned int)-1)
+ err(1, "alarm");
+
+ fprintf(lg, "%s: started\n", getprogname());
+
+ while ((n = read(mfd, buf, sizeof(buf))) > 0) {
+ fprintf(lg, ">>> ");
+ if (fwrite(buf, 1, n, lg) != (size_t)n)
+ err(1, "fwrite %s", logfile);
+ if (buf[n-1] != '\n')
+ fprintf(lg, "\n");
+ }
+ if (n < 0)
+ err(1, "read %s", ptyname);
+ fprintf(lg, "EOF %s\n", ptyname);
+
+ if (logout(tty) == 0)
+ errx(1, "logout %s", tty);
+ fprintf(lg, "logout %s\n", tty);
+
+ errx(3, "EOF");
+}
+
+void
+timeout(int sig)
+{
+ fprintf(lg, "signal timeout %d\n", sig);
+ if (tty) {
+ logout(tty);
+ fprintf(lg, "logout %s\n", tty);
+ }
+ errx(3, "timeout");
+}
+
+void
+terminate(int sig)
+{
+ fprintf(lg, "signal terminate %d\n", sig);
+ if (tty) {
+ logout(tty);
+ fprintf(lg, "logout %s\n", tty);
+ }
+ errx(3, "terminate");
+}
+
+void
+iostdin(int sig)
+{
+ char buf[8192];
+ size_t n;
+
+ fprintf(lg, "signal iostdin %d\n", sig);
+ if ((n = read(0, buf, sizeof(buf))) < 0)
+ err(1, "read stdin");
+ if (tty) {
+ logout(tty);
+ fprintf(lg, "logout %s\n", tty);
+ }
+ if (n > 0)
+ errx(3, "read stdin %zd bytes", n);
+ exit(0);
+}