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 8, 2020
1 parent ee4963f commit de1291e
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 26 deletions.
46 changes: 46 additions & 0 deletions usr/lib/console-login-helper-messages/libutil.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/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}"
# Default SELinux context at destination is applied, e.g. for sshd which
# requires that written files in `/run/motd.d` maintain the type
# `pam_var_run_t`.
mv_Z="mv -Z"

# 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() {
local generated_file="$1"
local staged_file="$(mktemp --tmpdir="${tempfile_dir}" "${tempfile_template}")"
cat > "${staged_file}"
${mv_Z} "${staged_file}" "${generated_file}"
}

# Write concatenation of all files with a given suffix from a list of
# source directories to a target file. The target file is the first
# argument; suffix the second; and source directories the remaining,
# searched in the given order in the list. Atomicity of the write to
# the target file is given by appending file contents to a tempfile
# before moving to the target file.
cat_via_tempfile() {
local generated_file="$1"
local filter_suffix="$2"
shift 2
local source_dirs="$@"
local staged_file="$(mktemp --tmpdir="${tempfile_dir}" "${tempfile_template}")"
for source_dir in ${source_dirs[@]}; do
# Ignore stderr, and let the command succeed if no files are
# found in the source directory.
cat "${source_dir}"/*"$filter_suffix" 2>/dev/null >> "${staged_file}" || :
done
${mv_Z} "${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

[Service]
Expand Down
46 changes: 21 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,31 @@
# Get updated system information and generate a motd
# to display this at login.

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

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"
source_dirs=("${ETC_SNIPPETS}" "${RUN_SNIPPETS}" "${USR_LIB_SNIPPETS}")

cat_via_tempfile "${generated_file}" ".motd" "${source_dirs[@]}"

0 comments on commit de1291e

Please sign in to comment.