File: system-log.sh

package info (click to toggle)
shepherd 1.0.9-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,828 kB
  • sloc: lisp: 8,779; sh: 3,586; makefile: 290; ansic: 50
file content (287 lines) | stat: -rw-r--r-- 9,743 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# GNU Shepherd --- Test system logging service (syslog).
# Copyright © 2024-2025 Ludovic Courtès <ludo@gnu.org>
#
# This file is part of the GNU Shepherd.
#
# The GNU Shepherd is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# The GNU Shepherd is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the GNU Shepherd.  If not, see <https://www.gnu.org/licenses/>.

shepherd --version
herd --version

socket="t-socket-$$"
conf="t-conf-$$"
log="t-log-$$"
pid="t-pid-$$"
logger="$PWD/t-syslog-logger-$$.scm"
kmsg="$PWD/t-syslog-kmsg-$$"
syslog_file="$PWD/t-syslog-$$"
syslog_auth_file="$PWD/t-syslog-auth-$$"
syslog_debug_file="$PWD/t-syslog-debug-$$"
syslog_dir="$PWD/$t-syslog-dir-$$"
syslog_remote_file="$syslog_dir/t-syslog-remote-$$"
syslog_socket="$PWD/t-syslog-socket-$$"

herd="herd -s $socket"

trap "zcat $log.* || true; cat $log || true;
      rm -f $socket $conf $log* $logger $kmsg $syslog_socket;
      rm -f $syslog_file* $syslog_auth_file* $syslog_debug_file* $syslog_remote_file*;
      rmdir $syslog_dir;
      test -f $pid && kill \`cat $pid\` || true; rm -f $pid" EXIT

cat > "$conf" <<EOF
(use-modules (shepherd service system-log)
             (shepherd service log-rotation)
             (shepherd service timer)
             (shepherd endpoints)
             (srfi srfi-19))

(define (message-destination message)
  (pk 'message-destination->
      (cond ((and=> (system-log-message-sender message)
                    (lambda (address)
                      ;; On the Hurd, 'sender' is set even for
                      ;; AF_UNIX connections.
                      (not (= AF_UNIX (sockaddr:fam address)))))
             (list "$syslog_remote_file"))
            ((= (system-log-message-facility message)
		(system-log-facility authorization/private))
	     (list "$syslog_auth_file"))
            ((= (system-log-message-facility message)
		(system-log-facility mail))
	     '())  ;too much mail: discard these messages
	    ((= (system-log-message-priority message)
		(system-log-priority debug))
	     (list "$syslog_debug_file" "$syslog_file"))
	    (else
	     (list "$syslog_file")))))

(define today
  (time-utc->date (current-time time-utc)))

(define past-month
  (if (= 1 (date-month today))
      12
      (- (date-month today) 1)))

(define %endpoints
  (list (endpoint (make-socket-address AF_UNIX "$syslog_socket")
                  #:style SOCK_DGRAM)
        (endpoint (make-socket-address AF_INET INADDR_LOOPBACK 9898)
                  #:style SOCK_DGRAM)))

(register-services
  (list (system-log-service %endpoints
                            #:message-destination message-destination
                            #:kernel-log-file "$kmsg")
        (log-rotation-service
          ;; Arrange so that it does not trigger automatically.
          (calendar-event #:months (list past-month))
          #:rotation-size-threshold 0)
        (service
          '(logger)
          #:requirement '(syslogd)
          #:start (make-forkexec-constructor '("$logger"))
          #:stop (make-kill-destructor)
          #:respawn? #f)))
EOF

cat > "$logger" <<EOF
#!$GUILE --no-auto-compile
!#
;; -*- coding: utf-8 -*-
(use-modules (rnrs bytevectors))

(display "starting logger\n")
(let ((sock (socket AF_UNIX SOCK_DGRAM 0)))
  (connect sock AF_UNIX "$syslog_socket")
  (set-port-encoding! sock "UTF-8")
  (display "<86> Jun 29 10:45:54 sudo: pam_unix(sudo:session): session opened for user root\n" sock)
  (display "<85>Jul 14 12:32:50 sudo: pam_unix(sudo:auth): authentication failure; logname= uid=1000\n" sock)
  (display "<81>Jul 14 12:33:01 sudo: ludo : 3 incorrect password attempts ; TTY=pts/34\n" sock)
  (display "<31>Jul 14 12:18:28 ntpd[427]: new interface(s) found: waking up resolver\n" sock)
  (display "<15>Jul 14 12:18:28 utf8[42]: checking we can print a λ and a 😃\n" sock)
  (display "<38>Jul 14 12:47:33 elogind[286]: Power key pressed short.\n" sock)
  (display "<30>Jul 14 12:47:33 NetworkManager[319]: <info>  [1720954053.6685] manager: sleep: sleep requested\n" sock)
  (display "<20>Jul 18 22:22:22 exim[42]: too much mail in your inbox\n" sock))

(let ((sock (socket AF_UNIX SOCK_DGRAM 0)))
  (connect sock AF_UNIX "$syslog_socket")
  (set-port-encoding! sock "ISO-8859-1")
  (display "<14>Feb 26 12:12:12 latin[1]: latin1 garbage: ça alors, étrange !\n" sock))

(let ((sock (socket AF_INET SOCK_DGRAM 0))
      (address (make-socket-address AF_INET INADDR_LOOPBACK 9898)))
  (sendto sock
          (string->utf8
           "<31>Aug  4 10:05:55 mtp-probe: checking bus 1, device 111: \"/sys/devices/pci0000:00/0000:00:14.0/usb1/1-8\"\n")
          address))
EOF

chmod +x "$logger"

# The empty line is intentional to test that it is ignored
cat > "$kmsg" <<EOF

<6>[370383.514474] usb 1-2: USB disconnect, device number 57
EOF

file_descriptor_count ()
{
    ls -l /proc/"$(cat $pid)"/fd/[0-9]* | wc -l
}

rm -f "$pid"
shepherd -I -s "$socket" -c "$conf" -l "$log" --pid="$pid" &

# Wait till it's ready.
while ! test -f "$pid" ; do sleep 0.3 ; done

# Trigger startup of the finalizer thread, which creates a couple of pipes.
# That way, those extra file descriptors won't influence the comparison with
# INITIAL_FD_COUNT done at the end.
$herd eval root '(gc)'

initial_fd_count=$(file_descriptor_count)

$herd start logger
until $herd status logger | grep stopped; do sleep 0.3; done

grep "starting logger" "$log"

# Parent directories of log files should be created if needed.
test -d "$syslog_dir"

grep "sudo:.* session opened" "$syslog_auth_file"
grep "sudo:.* authentication failure" "$syslog_auth_file"
grep "3 incorrect password attempts" "$syslog_auth_file"
grep "ntpd\[427\]: new interface" "$syslog_debug_file"
grep "ntpd\[427\]: new interface" "$syslog_file" # this one in both files
grep "a λ and a 😃" "$syslog_debug_file"
grep "a λ and a 😃" "$syslog_file" # in both files too
grep "elogind\[286\]: Power key pressed short" "$syslog_file"
grep "USB disconnect, device number 57" "$syslog_file"
grep "NetworkManager\[319\]: .*sleep" "$syslog_file"
grep "mtp-probe:" "$syslog_remote_file"
grep "latin1 garbage: .*alors.*trange" "$syslog_file"

test $(wc -l < "$syslog_auth_file") -eq 3
test $(wc -l < "$syslog_debug_file") -eq 2
test $(wc -l < "$syslog_remote_file") -eq 1
test $(wc -l < "$syslog_file") -eq 6 # empty line from kmsg is ignored

for file in "$syslog_file" "$syslog_auth_file" "$syslog_debug_file" \
			   "$syslog_remote_file"
do
    cat "$file"
done

# The 'status' command should display the socket and kernel log file.
$herd status system-log | grep "$syslog_socket"
$herd status system-log | grep "$kmsg"

for file in "$syslog_file" "$syslog_auth_file" "$syslog_debug_file" \
			   "$syslog_remote_file"
do
    $herd status system-log | grep "Log files: .*$file"
done

# Check the "Recent messages" part of 'herd status'.
$herd status system-log
$herd status system-log -n 12 | grep "sudo: pam_unix"
$herd status system-log -n 12 | grep "USB disconnect"
$herd status system-log -n 12 | grep "mtp-probe: "

# End of file has been reached on $kmsg so it should be closed and no longer
# be polled.
until grep "Closing .*$kmsg" "$log"; do sleep 0.2; done

# Ensure logs can be rotated.
$herd start log-rotation
$herd trigger log-rotation
for file in "$syslog_file" "$syslog_auth_file" "$syslog_debug_file" \
			   "$syslog_remote_file"
do
    $herd files log-rotation | grep "$file"

    # Rotation happens asynchronously so wait for a while.
    until test -f "$file.1.gz"; do sleep 0.2; done
    gunzip < "$file.1.gz"
    rm "$file.1.gz"
    test -f "$file"		# this one should have been recreated
done

$herd stop system-log
$herd eval root '(gc)'

if test -d "/proc/$$/fd"	# GNU/Hurd lacks /proc/*/fd.
then
    # At this point, shepherd should be back to INITIAL_FD_COUNT.
    # Since the logger's own ports are closed asynchronously, when the service
    # sends it the 'terminate message, retry a few times.
    i=0
    while test $i -lt 20
    do
	ls -l "/proc/$(cat $pid)/fd"
	if test $(file_descriptor_count) -le $initial_fd_count
	then
	    break
	else
	    sleep 0.5		# wait and retry
	    i=$(expr $i + 1)
	fi
    done
    test $(file_descriptor_count) -le $initial_fd_count
fi

# Remove the logs, start it again, and ensure it's working.
rm -f "$syslog_file" "$syslog_auth_file" "$syslog_debug_file"
$herd enable logger
$herd start logger
until $herd status logger | grep stopped; do sleep 0.3; done
for file in "$syslog_file" "$syslog_auth_file" "$syslog_debug_file"
do
    test -f "$file"
done

$herd stop system-log

# Check that an inaccessible #:kernel-log-file does not prevent 'system-log'
# from starting.

cat > "$conf" <<EOF
(use-modules (shepherd service system-log)
             (shepherd endpoints)
             (srfi srfi-19))

(define %endpoints
  (list (endpoint (make-socket-address AF_UNIX "$syslog_socket")
                  #:style SOCK_DGRAM)))

(register-services
  (list (system-log-service %endpoints
                            #:kernel-log-file "/does/not/exist")))
EOF

$herd load root "$conf"
$herd start system-log
$herd status system-log | grep "$syslog_socket"
if $herd status system-log | grep "/does/not/exist"; then false; else true; fi
grep "Dismissing kernel log.*/does/not/exist" "$log"

$herd stop root

# Local Variables:
# coding: utf-8
# End: