Display LWN news using the message of the day framework.
Create 90-lwn
shell script inside /etc/update-motd.d/
directory.
#!/bin/bash # Display LWN news using update-motd framework # cache file lwn_cache_file="/var/run/motd.lwn" lwn_cache_time="7200" # curl options curl_max_time="5" # LWN options lwn_header="LWN news" lwn_url="https://lwn.net/headlines/newrss" lwn_color_link="\\\e[31m" lwn_color_date="\\\e[2m" lwn_color_clear="\\\e[0m" # update cache file function update_cache() { curl --max-time $curl_max_time --silent $lwn_url | \ xmlstarlet select -N it="http://purl.org/rss/1.0/" \ -N dc="http://purl.org/dc/elements/1.1/" \ --text \ --noblanks \ --template --match "//rdf:RDF/it:item[position() <= 5]" \ -o "- $lwn_color_link" -v it:title -o "$lwn_color_clear" -o " at " -o "$lwn_color_date" -v dc:date -o "$lwn_color_clear" -o '\\n' | \ xargs echo -e > $lwn_cache_file } # display contents of the cache file if it is not empty function display_cache() { if [ -s "$lwn_cache_file" ]; then echo echo "$lwn_header" cat $lwn_cache_file fi } # check for specific command check_for_command(){ command -v "$1" 1>/dev/null 2>/dev/null } # check for curl and xmlstarlet commands="curl xmlstarlet" for command in $commands; do if ! check_for_command "$command"; then exit fi done # main if [ -f "$lwn_cache_file" ]; then # compare time of last data modification, seconds since Epoch # to decide if it needs to be updated date_cache="$(stat --format %Y $lwn_cache_file)" date_now="$(date +%s)" date_diff="$(expr $date_now - $date_cache)" if [ "$date_diff" -ge "$lwn_cache_time" ]; then update_cache fi else update_cache fi # display news display_cache
Ensure that the executable bit is set.
$ chmod +x /etc/update-motd.d/90-lwn
Verify shell scripts’ execution order and output without initiating the login process.
$ ls /etc/update-motd.d/ 10-uname 90-motd
$ run-parts --lsbsysinit /etc/update-motd.d Linux buster 4.19.0-4-amd64 #1 SMP Debian 4.19.28-2 (2019-03-15) x86_64 LWN news - [$] Examining exFAT at 2019-08-30T18:43:28+00:00 - A very deep dive into iOS Exploit chains found in the wild (Project Zero) at 2019-08-30T15:59:37+00:00 - Security updates for Friday at 2019-08-30T12:56:46+00:00 - [$] Change IDs for kernel patches at 2019-08-29T16:58:52+00:00 - Stable kernels 5.2.11, 4.19.69, and 4.14.141 at 2019-08-29T15:25:16+00:00
Empirically check the message of the day.
$ ssh debian.example.org -l milosz -i ~/.ssh/ext_milosz Linux buster 4.19.0-4-amd64 #1 SMP Debian 4.19.28-2 (2019-03-15) x86_64 LWN news - [$] Examining exFAT at 2019-08-30T18:43:28+00:00 - A very deep dive into iOS Exploit chains found in the wild (Project Zero) at 2019-08-30T15:59:37+00:00 - Security updates for Friday at 2019-08-30T12:56:46+00:00 - [$] Change IDs for kernel patches at 2019-08-29T16:58:52+00:00 - Stable kernels 5.2.11, 4.19.69, and 4.14.141 at 2019-08-29T15:25:16+00:00 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Sun Sep 1 18:28:09 2019 from 10.0.2.2 milosz@buster:~$
References
This behavior is defined in pam_motd source code, look for update-motd
patch.
/* Run the update-motd dynamic motd scripts, outputting to /run/motd.dynamic. This will be displayed only when calling pam_motd with motd=/run/motd.dynamic; current /etc/pam.d/login and /etc/pam.d/sshd display both this file and /etc/motd. */ if (do_update && (stat("/etc/update-motd.d", &st) == 0) && S_ISDIR(st.st_mode)) { mode_t old_mask = umask(0022); if (!system("/usr/bin/env -i PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin run-parts --lsbsysinit /etc/update-motd.d > /run/motd.dynamic.new")) rename("/run/motd.dynamic.new", "/run/motd.dynamic"); umask(old_mask); }
Inspect update-motd – dynamic MOTD generation manual page for more information.