「motd」「.bashrc」…LinuxにSSH接続した時のメッセージを動的に変化させる方法

Linux(およびUNIX系のOS)には、SSH接続でログインした際にあらかじめ設定したメッセージを表示させる機能がある。
基本的には「/etc/motd」にそのメッセージを記述しておくのだが、個人的な趣味嗜好(派手なの大好き!)より、このメッセージにコマンドの実行結果を表示させたくなった。

俺はSSHでサーバにログインした時、色付きのアスキーアートとか表示させたいんだ!派手な感じの見た目がいいんだ!なんかメモリとかの値を色々表示させたいんだー!!

というわけで、その方法を調べてみることにした。
なお、ここで扱うLinuxには、「screenFetch」および「ARCHEY」をインストールしているので、基本的にそれを用いて動的に変化させていく。

1.「.bashrc」に記述する方法

最もお手軽な方法としては、「.bashrc」に実行させるコマンドを記述させることで、ターミナルに入るごとにそのコマンド実行結果が表示させる方法だ。
上の画像は、単純に「.bashrc」で「archey」を実行させるように設定させたもの。
以下に、サンプルとしてtestユーザの「.bashrc」を記載する。

test@test-vm-ubuntu:~$ cat .bashrc
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
        # We have color support; assume it's compliant with Ecma-48
        # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
        # a case would tend to support setf rather than setaf.)
        color_prompt=yes
    else
        color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi

# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# Add an "alert" alias for long running commands.  Use like so:
#   sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

# add
archey

変更したのは、最終行に「archey」を追加しただけ。結構お手軽に動的なログインメッセージが作成できた。
しかし、今の表示だとmotdの表示も同時に存在しているため、ちょっとくどい…
そこで、「/etc/pam.d/sshd」に記述されている以下の2行をコメントアウトし、motdを無効化する。

session    optional     pam_motd.so  motd=/run/motd.dynamic noupdate
session    optional     pam_motd.so # [1]

その状態でログインした結果が以下。

デフォルトで設定されていたmotdの表示を削除することに成功した。

さて、これで実用に全く問題が無いかと思われるだろうが、この手法には以下の3つの問題点がある。

  1. 「.bashrc」はユーザごとに存在する
  2. 普通にローカルの端末を開いただけでも表示される
  3. ユーザのスイッチでも表示される

それぞれの問題点について説明していこう。

1.「.bashrc」はユーザごとに存在する

そう、この「.bashrc」は各ユーザのホームフォルダに存在するファイルのため、もし全ユーザで設定するのであれば、各ユーザごとに設定を行う必要がある。
…といっても、これは簡単に解決出来る。ユーザの作成前に、ユーザ追加時のデフォルト設定を変更すればいいのだ。

ユーザのデフォルト設定は「/etc/skel/」に存在するので、ここのファイルを書きかえればいい。

2.普通にローカルの端末を開いただけでも表示される

以降の2つがちょっとよろしくない内容。
そう、この設定だとローカルホストで端末を開いた場合にも同じ表示がされるのだ!

実際の画面がこちら。

うーん、これはちょっと…

3.ユーザのスイッチでも表示される

1点目の問題として、各ユーザごとに「.bashrc」で設定されている必要があると記述したが、それによってsuコマンドでユーザをスイッチしても表示されてしまう。
例えば、「sudo su」でrootに切り替えたとして、rootの「.bashrc」で同じ設定がされていた場合、以下のように表示される。

うーん、これはウザイ…
個人的にこの方法は不採用かなぁ…

やはり、「motd」を用いた方法で考えるべきかな。

2.Debian/Ubuntuの場合

さて、それではどうするか。
実はDebian/Ubuntuの場合、結構簡単に設定出来るのだ。

実はDebianやUbuntuの場合、motdはすでに動的に作成されている。(以後、検証にはUbuntuを用いているのでその内容を記述している)
Ubuntuの場合、デフォルトで「/etc/update-motd.d」というディレクトリが存在しており、その配下にスクリプトが存在している。

root@test-vm-ubuntu:~# ls -la /etc/update-motd.d/
合計 40
drwxr-xr-x 2 root root 4096 4月 22 09:31 .
drwxr-xr-x 133 root root 12288 7月 6 18:39 ..
-rwxr-xr-x 1 root root 1220 2月 20 11:43 00-header
-rwxr-xr-x 1 root root 1358 2月 20 11:43 10-help-text
-rwxr-xr-x 1 root root 149 8月 22 2011 90-updates-available
-rwxr-xr-x 1 root root 299 4月 12 07:06 91-release-upgrade
-rwxr-xr-x 1 root root 142 8月 22 2011 98-fsck-at-reboot
-rwxr-xr-x 1 root root 144 8月 22 2011 98-reboot-required

これらのスクリプトが順番に実行され、「motd」で表示されるファイルとなる「/var/run/motd.dynamic」というファイルが作成されるようだ。
ということは、このディレクトリにmotdで表示させたいコマンドを記述したスクリプトを設置することで、動的なメッセージを作成可能ということになる。

早速やってみることにした。
作成を単純にするため、設置するスクリプトは作成する一つのみにして、もともとあったスクリプトは全部削除した。
作成したスクリプトは以下。なお、archeyは動かなかったので、ここではscreenfetchを実行させている。

00-motd

#!/bin/sh
# Header
[ -r /etc/lsb-release ] && . /etc/lsb-release

if [ -z "$DISTRIB_DESCRIPTION" ] && [ -x /usr/bin/lsb_release ]; then
        # Fall back to using the very slow lsb_release utility
        DISTRIB_DESCRIPTION=$(lsb_release -s -d)
fi

printf "=========================================================\n"
printf "Welcome to %s (%s %s %s)\n" "$DISTRIB_DESCRIPTION" "$(uname -o)" "$(uname -r)" "$(uname -m)"
printf "\n"

# run screenFetch
screenfetch

# Footer
printf "=========================================================\n"

スクリプト設置後、権限を755にしないとうまく動いてくれない。

chmod 755 /etc/update-motd.d/*

実行した結果がこちら。

ログイン時のみの表示になっているし、無事動的なメッセージを表示させることに成功したようだ。
普通にSSH接続する際のメッセージよりかっこいいし、サーバの情報も動的に取得出来ている。

「ARCHEY」を用いる事が出来なかったのは残念だが、これなら有りかなぁ。

3.RHEL系Linuxの場合

さて、次はRHEL系Linuxで動的なmotdのメッセージを表示させるにはどうすればいいのか。

そもそも、「/etc/motd」にある程度動的な値が入ってさえいればいいんだよなぁ…
というわけで、「/etc/motd」に値を記述するスクリプトを別途作成し、それをcronで5分ごとに実行させる事にした。
作成には、こちらのスクリプトを参考にさせてもらった。感謝!

まず、以下のようなスクリプトを作成する。

update_motd.sh

#!/bin/bash

FILE_MOTD='/etc/motd'

BIN_DATE='/bin/date'
BIN_FIGLET='/usr/bin/figlet'
BIN_HOSTNAME='/bin/hostname'
BIN_IP='/sbin/ip'
BIN_GREP='/bin/grep'
BIN_SED='/bin/sed'
BIN_UPTIME='/usr/bin/uptime'
BIN_CAT='/bin/cat'
BIN_UNAME='/bin/uname'
BIN_FREE='/usr/bin/free'
BIN_HEAD='/usr/bin/head'
BIN_TAIL='/usr/bin/tail'
BIN_DF='/bin/df'
BIN_AWK='/bin/awk'
BIN_BC='/usr/bin/bc'
BIN_PS='/bin/ps'
BIN_SCREENFETCH='/usr/bin/screenfetch'

COLOR_LIGHT_GREEN='\033[1;32m'
COLOR_LIGHT_BLUE='\033[1;34m'
COLOR_YELLOW='\033[1;33m'
COLOR_RED='\033[0;31m'
COLOR_WHITE='\033[1;37m'
COLOR_DEFAULT='\033[0m'

echo -e "
${COLOR_DEFAULT}=========================================================
${COLOR_DEFAULT}Welcome to ${COLOR_LIGHT_BLUE}$(${BIN_CAT} /etc/redhat-release) ${COLOR_DEFAULT}.
" > ${FILE_MOTD}

${BIN_SCREENFETCH} >> ${FILE_MOTD}

echo -e "
${COLOR_DEFAULT}=========================================================
" >> ${FILE_MOTD}

このスクリプトを「/usr/local/bin/」に設置し、権限を755にして実行権限を付与する。

[root@test-centos ~]# cp update_motd.sh /usr/local/bin/
[root@test-centos ~]# chmod 755 /usr/local/bin/update_motd.sh
[root@test-centos ~]# chmod +x /usr/local/bin/update_motd.sh

最後に、crontabで5分間隔で実行させるよう設定する。
これで、CentOS等でも動的にmotdを作成出来るようになった。