├── .gitignore ├── motd.png ├── LICENSE ├── README.md ├── GEMINI.md └── motd.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /examples -------------------------------------------------------------------------------- /motd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SixBytesUnder/custom-motd/HEAD/motd.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Wojciech Kosinski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Custom MOTD 2 | ==== 3 | 4 | ### Highly customizable Message of the Day script for Raspberry Pi #### 5 | 6 | ![](motd.png?raw=true "Custom MOTD") 7 | 8 | Written in Bash. No other dependancies. So far tested with Raspbian Jessie only, but should work with most other Linux distributions. 9 | 10 | The following steps may vary depending on the OS. 11 | 12 | - Download and save the `motd.sh` bash script onto your machine. Remember to add execute permissions and change the owner: 13 | 14 | ```bash 15 | $ wget https://raw.githubusercontent.com/SixBytesUnder/custom-motd/master/motd.sh 16 | $ sudo cp motd.sh /etc/profile.d/motd.sh 17 | $ sudo chown root:root /etc/profile.d/motd.sh 18 | $ sudo chmod +x /etc/profile.d/motd.sh 19 | ``` 20 | 21 | Simply execute the script to test if it works 22 | 23 | ```bash 24 | ./motd.sh 25 | ``` 26 | That's it, from now on every time you ssh into your Raspberry, you should see the custom message of the day. 27 | 28 | ### All below is optional 29 | 30 | - You can remove default MOTD, but it's not necessary since the script will clean the screen anyway. 31 | 32 | ```bash 33 | $ sudo rm /etc/motd 34 | ``` 35 | 36 | - For the same reason as above, not necessary, but you may want to remove the "last login" message. Disable the `PrintLastLog` option from the `sshd` service. 37 | 38 | ```bash 39 | $ sudo vim.tiny /etc/ssh/sshd_config 40 | ``` 41 | 42 | You should see: 43 | 44 | ```text 45 | PrintLastLog yes 46 | ``` 47 | 48 | Change it to: 49 | 50 | ```text 51 | PrintLastLog no 52 | ``` 53 | 54 | Restart the `sshd` service: 55 | 56 | ```bash 57 | $ sudo systemctl restart sshd 58 | ``` 59 | 60 | ### Options ### 61 | 62 | At the top of the file are variables allowing customization of the messages: 63 | 64 | - `settings` array contains all possible messages to be displayed. 65 | Comment lines with a `#` for messages you don't want to see. 66 | Change order of items in array to change order of displayed messages. 67 | 68 | - `weatherCode` set region code for the weather message. 69 | Full list of available [Accuweather location codes](accuweather_location_codes.txt) 70 | 71 | - `degrees` change value to "C" to show all temperatures in Celsius or "F" for Fahrenheit 72 | 73 | - `colour` array, lets you set your own colours. List of colour codes: 74 | 75 | | Colour | Value | 76 | |--------|:-----:| 77 | | black | 0 | 78 | | red | 1 | 79 | | green | 2 | 80 | | yellow | 3 | 81 | | blue | 4 | 82 | | magenta| 5 | 83 | | cyan | 6 | 84 | | white | 7 | 85 | 86 | ## License 87 | 88 | [MIT](https://github.com/SixBytesUnder/custom-motd/blob/master/LICENSE) 89 | -------------------------------------------------------------------------------- /GEMINI.md: -------------------------------------------------------------------------------- 1 | # Project: Raspberry Pi MOTD 2 | 3 | ## Description 4 | 5 | This project consists of a highly customizable Bash script (`motd.sh`) designed to display a "Message of the Day" (MOTD) upon logging into a Raspberry Pi or other Linux system. It provides a colorful and informative overview of various system metrics. 6 | 7 | The script is modular, allowing users to easily enable, disable, and reorder the information panels by editing a configuration array. It fetches data from system files (`/proc`, `/sys`), standard Linux commands, and external services (for weather and IP information). 8 | 9 | ## Coding Style 10 | 11 | - **Indentation**: 4 spaces are used for indentation within blocks and control structures. 12 | - **Variable Naming**: A mix of `camelCase` (e.g., `weatherCode`) and `lowercase` (e.g., `logo`) is used for variables. Module names in the configuration section are `UPPERCASE` (e.g., `SYSTEM`, `MEMORY`). 13 | - **Functions**: Function names are written in `camelCase` (e.g., `displayMessage`). 14 | - **Comments**: The script is well-commented, with clear sections for user settings and explanations for specific commands. 15 | - **Quoting**: Both single (`'`) and double (`"`) quotes are used for strings. 16 | - **Shebang**: The script explicitly uses `#!/bin/bash`. 17 | 18 | ## Code Structure 19 | 20 | 1. **Configuration Section**: All user-configurable settings are consolidated at the top of the script. This includes: 21 | - An array `settings` that defines which information modules are displayed and in what order. 22 | - Variables for location (`weatherCode`) and temperature units (`degrees`). 23 | - An associative array `colour` for defining terminal color codes using `tput`. 24 | 2. **Core Logic**: 25 | - A helper function `displayMessage` handles the formatted and colored output. 26 | - A central function `metrics` contains a `case` statement that holds the logic for each individual information module (e.g., `UPTIME`, `DISKS`, `WEATHER`). 27 | 3. **Execution Flow**: 28 | - The script begins by checking for Bash version compatibility (specifically for array support). 29 | - It then iterates through the user-defined `settings` array. 30 | - For each setting, it calls the `metrics` function to display the corresponding information panel. 31 | - Finally, it resets the terminal colors. 32 | 33 | ## Tool Preferences 34 | 35 | The script relies on a set of standard and powerful command-line tools to gather and process information: 36 | 37 | - **System & Hardware Info**: 38 | - `uname`, `date`, `df`, `ps`, `who`, `last` 39 | - Direct reads from `/proc` filesystem (`/proc/uptime`, `/proc/meminfo`, `/proc/loadavg`). 40 | - Direct reads from `/sys` filesystem for CPU temperature. 41 | - `vcgencmd` (Raspberry Pi specific) for GPU temperature. 42 | - **Text Processing**: 43 | - `awk`: Used extensively for parsing and extracting data from command outputs. 44 | - `grep`: For filtering lines based on patterns. 45 | - `sed`: For stream editing, particularly for parsing the weather RSS feed. 46 | - `cut`: For extracting specific fields from lines. 47 | - `tr`: For character translation. 48 | - **Networking**: 49 | - `ip`: To get the local IP address. 50 | - `curl` / `wget`: To fetch the external IP address and weather data from online sources. 51 | - **Terminal Formatting**: 52 | - `tput`: To apply colors to the output for better readability. 53 | -------------------------------------------------------------------------------- /motd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clear 4 | 5 | # 6 | # Test whether bash supports arrays. 7 | # (Support for arrays was only added recently.) 8 | # 9 | whotest[0]='test' || (echo 'Failure: arrays not supported in your version of bash. Must be at least version 4 to have associative arrays.' && exit 2) 10 | 11 | ############################################################################# 12 | # SETTINGS # 13 | # Comment with a # messages you don't want displayed # 14 | # Change order of items in array to change order of displayed messages # 15 | ############################################################################# 16 | 17 | settings=( 18 | LOGOSMALL 19 | # LOGOBIG 20 | SYSTEM 21 | DATE 22 | UPTIME 23 | MEMORY 24 | DISKS 25 | LOADAVERAGE 26 | PROCESSES 27 | IP 28 | # Please be aware UPDATES command may take a few seconds to run 29 | # If you don't like waiting, just comment it out 30 | UPDATES 31 | WEATHER 32 | CPUTEMP 33 | GPUTEMP 34 | SSHLOGINS 35 | LASTLOGIN 36 | MESSAGES 37 | ) 38 | 39 | # Accuweather location codes: https://github.com/SixBytesUnder/custom-motd/blob/master/accuweather_location_codes.txt 40 | weatherCode="EUR|UK|UK001|LONDON|" 41 | 42 | # Show temperatures in "C" for Celsius or "F" for Fahrenheit 43 | degrees=C 44 | 45 | # Colour reference 46 | # Colour Value 47 | # black 0 48 | # red 1 49 | # green 2 50 | # yellow 3 51 | # blue 4 52 | # magenta 5 53 | # cyan 6 54 | # white 7 55 | declare -A colour=( 56 | [header]=$(tput setaf 6) 57 | [neutral]=$(tput setaf 2) 58 | [info]=$(tput setaf 4) 59 | [warning]=$(tput setaf 1) 60 | [reset]=$(tput sgr0) 61 | ) 62 | 63 | 64 | ############################################################################# 65 | # # 66 | # DO NOT TOUCH ANYTHING BELOW THIS POINT, UNLESS YOU KNOW WHAT YOU'RE DOING # 67 | # # 68 | ############################################################################# 69 | 70 | # Expects two arguments: 71 | # $1 is the header 72 | # $2 is the message 73 | function displayMessage { 74 | if [ -z "$1" ]; then 75 | echo "${colour[neutral]}$2" 76 | else 77 | while read line; do 78 | echo "${colour[header]}$1 ${colour[neutral]}$line"; 79 | done <<< "$2" 80 | fi 81 | } 82 | 83 | function metrics { 84 | case "$1" in 85 | 'LOGOSMALL') 86 | logo="${colour[neutral]} 87 | \\\ // ${colour[warning]} 88 | ◖ ● ◗ 89 | ◖ ● ● ◗ ${colour[neutral]}Raspberry Pi${colour[warning]} 90 | ◖ ● ◗ 91 | • 92 | " 93 | displayMessage '' "$logo" 94 | ;; 95 | 'LOGOBIG') 96 | logo="${colour[neutral]} 97 | .~~. .~~. 98 | '. \ ' ' / .'${colour[warning]} 99 | .~ .~~~..~. _ _ 100 | : .~.'~'.~. : ___ ___ ___ ___| |_ ___ ___ ___ _ _ ___|_| 101 | ~ ( ) ( ) ~ | _| .'|_ -| . | . | -_| _| _| | | | . | | 102 | ( : '~'.~.'~' : ) |_| |__,|___| _|___|___|_| |_| |_ | | _|_| 103 | ~ .~ ( ) ~. ~ |_| |___| |_| 104 | ( : '~' : ) 105 | '~ .~~~. ~' 106 | '~'" 107 | displayMessage '' "$logo" 108 | ;; 109 | 'SYSTEM') 110 | displayMessage 'System.............:' "$(uname -snrmo)" 111 | ;; 112 | 'DATE') 113 | displayMessage 'Date...............:' "$(date +"%A, %e %B %Y, %r")" 114 | ;; 115 | 'UPTIME') 116 | let upSeconds="$(/usr/bin/cut -d. -f1 /proc/uptime)" 117 | let sec=$((${upSeconds}%60)) 118 | let min=$((${upSeconds}/60%60)) 119 | let hour=$((${upSeconds}/3600%24)) 120 | let day=$((${upSeconds}/86400)) 121 | displayMessage 'Uptime.............:' "$(printf "%d days, %02dh %02dm %02ds" "$day" "$hour" "$min" "$sec")" 122 | ;; 123 | 'MEMORY') 124 | displayMessage 'Memory.............:' "$(cat /proc/meminfo | grep MemFree | awk {'print $2'})kB (Free) / $(cat /proc/meminfo | grep MemTotal | awk {'print $2'})kB (Total)" 125 | ;; 126 | 'DISKS') 127 | displayMessage 'Disk...............:' "$(df -hT -x tmpfs -x vfat | grep "^/dev/" | awk '{print $1" - "$5" (Free) / "$3" (Total)"}')" 128 | ;; 129 | 'LOADAVERAGE') 130 | read one five fifteen rest < /proc/loadavg 131 | displayMessage 'Load average.......:' "${one}, ${five}, ${fifteen} (1, 5, 15 min)" 132 | ;; 133 | 'PROCESSES') 134 | displayMessage 'Running processes..:' "$(ps ax | wc -l | tr -d " ")" 135 | ;; 136 | 'IP') 137 | lip=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}' | cut -f1 -d'/') 138 | eip=$(wget -q -O - http://icanhazip.com/ | tail) 139 | if [ "$lip" ]; then 140 | localIP="local: ${lip}" 141 | else 142 | localIP="" 143 | fi 144 | if [ "$eip" ]; then 145 | if [ "$lip" ]; then 146 | externalIP=", external: ${eip}" 147 | else 148 | externalIP="external: ${eip}" 149 | fi 150 | else 151 | externalIP="" 152 | fi 153 | displayMessage 'IP addresses.......:' "${localIP}${externalIP}" 154 | ;; 155 | 'UPDATES') 156 | updates=$(apt-get -s dist-upgrade | grep -Po '^\d+(?= upgraded)') 157 | if [ -z "$updates" ]; then 158 | updates=0 159 | fi 160 | if [ "$updates" -eq 0 ]; then 161 | displayMessage 'Available updates..:' "System is up to date." 162 | elif [ "$updates" -eq 1 ]; then 163 | displayMessage 'Available updates..:' "1 package can be updated." 164 | else 165 | displayMessage 'Available updates..:' "$updates packages can be updated." 166 | fi 167 | ;; 168 | 'WEATHER') 169 | if [ "$degrees" == "F" ]; then 170 | metric=0 171 | else 172 | metric=1 173 | fi 174 | displayMessage 'Weather............:' "$(curl -s "http://rss.accuweather.com/rss/liveweather_rss.asp?metric=$metric&locCode=$weatherCode" | sed -n '/Currently:/ s/.*: \(.*\): \([0-9]*\)\([CF]\).*/\2°\3, \1/p')" 175 | ;; 176 | 'CPUTEMP') 177 | cpu=$(