Skip to content

Commit

Permalink
motdgen: do not share a staged file, use mktemp and mv
Browse files Browse the repository at this point in the history
With the `staged` file shared, there would be potential for two or
more processes executing `motdgen` to write to it resulting in
corrupted output, or the error in the `cat` command due to missing
file reported in coreos#35 (comment). Currently, this is not a problem with
motdgen, but could be if `motdgen` were invoked by something like the
udev rules that invoke `issuegen`. A bug reported in `issuegen` for
this reason is: coreos#35

Instead, write the intermediate output to a unique tempfile and mv the
tempfile to the final output location. If the final output is on the
same filesystem as the tempfile, this operation should be atomic. This
ensures only valid output is written to the issue file shown to the
terminal.

Additionally, perform code tidyups similar to those done for
`issuegen` in coreos#40.
  • Loading branch information
Robert Fairley committed Jul 1, 2020
1 parent c5edd4d commit 0ad679a
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 26 deletions.
23 changes: 23 additions & 0 deletions usr/lib/console-login-helper-messages/libutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/bash

# Collection of util functions and common definitions for
# console-login-helper-messages scripts.

PKG_NAME=console-login-helper-messages
TEMPFILE_TEMPLATE=$PKG_NAME.XXXXXXXXXX.tmp
# Use same filesystem, under /run, as where snippets are generated,
# so that rename operations through `mv` are atomic.
TEMPFILE_DIR=/run/$PKG_NAME

# Write stdin to a tempfile, and rename the tempfile to the path
# given as an argument. When called from multiple processes on the
# same generated file path, this avoids interleaving writes to the
# generated file by using `mv` to overwrite the file.
write_via_tempfile() {
generated_file=$1
staged_file=$(mktemp --tmpdir=$TEMPFILE_DIR $TEMPFILE_TEMPLATE)
cat > "${staged_file}"
# Atomically rename staged to generated file (see TEMPFILE_DIR
# above).
mv "$staged_file" "$generated_file"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[Unit]
Description=Generate /run/motd.d/console-login-helper-messages.motd
Description=Generate console-login-helper-messages motd snippet
Documentation=https://github.com/coreos/console-login-helper-messages
Before=systemd-user-sessions.service

Expand Down
47 changes: 22 additions & 25 deletions usr/libexec/console-login-helper-messages/motdgen
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,32 @@
# Get updated system information and generate a motd
# to display this at login.

. /usr/lib/console-login-helper-messages/libutil

set -e

PKG_NAME=console-login-helper-messages
MOTD_DIR_PUBLIC=motd.d
# Should only be read by this script.
MOTD_DIR_PRIVATE="${PKG_NAME}/motd.d"
MOTD_SNIPPETS_PATH="${PKG_NAME}/motd.d"
ETC_SNIPPETS="/etc/${MOTD_SNIPPETS_PATH}"
RUN_SNIPPETS="/run/${MOTD_SNIPPETS_PATH}"
USR_LIB_SNIPPETS="/usr/lib/${MOTD_SNIPPETS_PATH}"

staged="/run/${PKG_NAME}/40_${PKG_NAME}.motd.staged"
# Pick 40 as an index as other files can order around it easily.
generated="/run/motd.d/40_${PKG_NAME}.motd"
# Parts of this script write to the `${RUN_SNIPPETS}` directory,
# make sure it is created upfront.
mkdir -p "${RUN_SNIPPETS}"

mkdir -p "/run/${MOTD_DIR_PRIVATE}"
mkdir -p "/run/${MOTD_DIR_PUBLIC}"
rm -f "${generated}"

# Write distro release information to MOTD, in distro-specific color.
source /usr/lib/os-release
echo -e "\e[${ANSI_COLOR}m${PRETTY_NAME}\e[39m" > "/run/${MOTD_DIR_PRIVATE}/21_os_release.motd"

# Generate a motd from files found in the private (package-specific) directories,
# and place the motd in a public directory.
if [[ -d "/etc/${MOTD_DIR_PRIVATE}" ]]; then
cat /etc/${MOTD_DIR_PRIVATE}/* 2>/dev/null >> "${staged}" || true
fi
if [[ -d /run/"${MOTD_DIR_PRIVATE}" ]]; then
cat /run/${MOTD_DIR_PRIVATE}/* 2>/dev/null >> "${staged}" || true
fi
if [[ -d /usr/lib/"${MOTD_DIR_PRIVATE}" ]]; then
cat /usr/lib/${MOTD_DIR_PRIVATE}/* 2>/dev/null >> "${staged}" || true
fi

cat "${staged}" > "${generated}"
rm -rf "${staged}"
OS_RELEASE_OUTDIR="${RUN_SNIPPETS}"
echo -e "\e[${ANSI_COLOR}m${PRETTY_NAME}\e[39m" | write_via_tempfile "${OS_RELEASE_OUTDIR}/21_os_release.motd"


# Generate a final motd from compiling the snippets.
# Pick 40 as a prefix as other files can order around it easily.
generated_file="/run/motd.d/40_${PKG_NAME}.motd"
staged_file=$(mktemp --tmpdir=$TEMPFILE_DIR $TEMPFILE_TEMPLATE)
cat $ETC_SNIPPETS/*.motd 2>/dev/null >> "$staged_file" || :
cat $RUN_SNIPPETS/*.motd 2>/dev/null >> "$staged_file" || :
cat $USR_LIB_SNIPPETS/*.motd 2>/dev/null >> "$staged_file" || :
mv "$staged_file" "$generated_file"

0 comments on commit 0ad679a

Please sign in to comment.