1#!/usr/bin/perl
2#	$OpenBSD: syslogd.pl,v 1.11 2019/09/17 22:24:08 bluhm Exp $
3
4# Copyright (c) 2010-2014 Alexander Bluhm <bluhm@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18use strict;
19use warnings;
20use Socket;
21use Socket6;
22
23use Client;
24use Syslogd;
25use Server;
26use Syslogc;
27use RSyslogd;
28require 'funcs.pl';
29
30sub usage {
31	die "usage: syslogd.pl [test-args.pl]\n";
32}
33
34my $testfile;
35our %args;
36if (@ARGV and -f $ARGV[-1]) {
37	$testfile = pop;
38	do $testfile
39	    or die "Do test file $testfile failed: ", $@ || $!;
40}
41@ARGV == 0 or usage();
42
43create_multifile(@{$args{multifile} || []});
44foreach my $name (qw(client syslogd server rsyslogd)) {
45	$args{$name} or next;
46	foreach my $action (qw(connect listen)) {
47		my $h = $args{$name}{$action} or next;
48		defined $h->{domain}
49		    or die "No domain specified in $name $action";
50		foreach my $k (qw(domain proto addr port)) {
51			next unless defined $h->{$k};
52			$args{$name}{"$action$k"} = $h->{$k};
53		}
54	}
55}
56my($s, $c, $r, $rc, @m);
57$s = RSyslogd->new(
58    %{$args{rsyslogd}},
59    listenport          => scalar find_ports(%{$args{rsyslogd}{listen}}),
60    testfile            => $testfile,
61) if $args{rsyslogd}{listen} && !$args{rsyslogd}{connect};
62$s ||= Server->new(
63    func                => \&read_log,
64    listendomain        => AF_INET,
65    listenaddr          => "127.0.0.1",
66    %{$args{server}},
67    testfile            => $testfile,
68    client              => \$c,
69    syslogd             => \$r,
70) unless $args{server}{noserver};
71$args{syslogc} = [ $args{syslogc} ] if ref $args{syslogc} eq 'HASH';
72my $i = 0;
73@m = map { Syslogc->new(
74    %{$_},
75    testfile            => $testfile,
76    ktracefile          => "syslogc-$i.ktrace",
77    logfile             => "syslogc-".$i++.".log",
78) } @{$args{syslogc}};
79$r = Syslogd->new(
80    connectaddr         => "127.0.0.1",
81    connectport         => $s && $s->{listenport},
82    ctlsock		=> @m && $m[0]->{ctlsock},
83    %{$args{syslogd}},
84    testfile            => $testfile,
85    client              => \$c,
86    server              => \$s,
87);
88$rc = RSyslogd->new(
89    %{$args{rsyslogd}},
90    listenport          => scalar find_ports(%{$args{rsyslogd}{listen}}),
91    testfile            => $testfile,
92) if $args{rsyslogd}{connect};
93$c = Client->new(
94    func                => \&write_log,
95    connectport         => $rc && $rc->{listenport},
96    %{$args{client}},
97    testfile            => $testfile,
98    syslogd             => \$r,
99    server              => \$s,
100) unless $args{client}{noclient};
101($rc, $c) = ($c, $rc) if $rc;  # chain client -> rsyslogd -> syslogd
102
103if (!$args{client}{noclient} && $c->{early}) {
104	$c->run->up;
105	$c->loggrep(get_firstlog(), 10)
106	   or die ref($c), " no first log during early startup";
107}
108$r->run unless $r->{late};
109$s->run->up unless $args{server}{noserver};
110$r->run if $r->{late};
111$r->up;
112my $control = 0;
113foreach (@m) {
114	if ($_->{early} || $_->{stop}) {
115		$_->run->up;
116		$control++;
117	}
118}
119$r->loggrep("Accepting control connection") if $control;
120foreach (@m) {
121	if ($_->{stop}) {
122		$_->kill('STOP');
123	}
124}
125$c->run->up if !$args{client}{noclient} && !$c->{early};
126$rc->run->up if $args{rsyslogd}{connect};
127
128$rc->down if $args{rsyslogd}{connect};
129$c->down if !$args{client}{noclient} && !$c->{early};
130$s->down unless $args{server}{noserver};
131foreach (@m) {
132	if ($_->{stop}) {
133		$_->kill('CONT');
134		$_->down;
135	} elsif ($_->{early}) {
136		$_->down;
137	} else {
138		$_->run->up->down;
139	}
140}
141$r->kill_child;
142$r->down;
143$c->down if !$args{client}{noclient} && $c->{early};
144
145$args{check}->({client => $c, syslogd => $r, server => $s}) if $args{check};
146check_logs($c, $r, $s, \@m, %args);
147