Shell Here's a way to be informed about updates at login

Hello all,

Allow me to share some useful scriptlet with you.
It's something I find has been lacking for years for the average self-hosted FreeBSD user like me who occasionally logs in to his server to check if everything's going well, perform package upgrades etc, and wants to stay up to date with the latest version available of the operating system. Yet AFAIK (unless I missed something in the documentation for all these years, which is totally possible) there's no simple way for the system to tell you "hey there, a new production version is available, you should start considering upgrading" right at login. I know I could subscribe to a mailing list to be notified directly in my mailbox, but these emails rarely come at a moment where I'm ready to address this issue, and I'd rather be notified of that when I login to my server - because that's exactly when I'm available to perform routine maintenance tasks.

So in order to address this issue I've been using these few lines of shell script, which I put at the end of my ~/.profile.

Code:
# let's do an automatic update check (use http:// as long as it's supported since it's lighter, but note that https:// also works)
echo ""
echo -ne "  Checking for operating system updates, please wait... "
_CUR_MMN="$(freebsd-version|awk -F- '{print $1}')"
_CUR_VER="$(freebsd-version)"
_AVL_MMN="$(HTTP_TIMEOUT=3 fetch -qo - "http://www.freebsd.org" 2>/dev/null|tr -d '\n'|awk -F"Production:" '{print $2}'|awk -F'>' '{print $2}'|awk -F'<' '{print $1}')"
_AVL_VER="${_AVL_MMN}-$(HTTP_TIMEOUT=3 fetch -qo - "http://cgit.freebsd.org/src/plain/sys/conf/newvers.sh?h=releng/${_AVL_MMN}" 2>/dev/null|grep "^BRANCH="|cut -d'"' -f2)"
if [ "_${_AVL_MMN}" = "_" ]; then
    echo -e "\r  /!\\ Automatic update check failed (error retrieving the latest production version from www.freebsd.org)."
elif [ "_$(echo "${_AVL_VER}"|grep "RELEASE")" = "_" ]; then
    echo -e "\r  /!\\ Automatic update check failed (error retrieving the latest patchlevel version from cgit.freebsd.org)."
elif [ "_${_CUR_VER}" = "_${_AVL_VER}" ]; then
    echo -e "\r  Your base system is up to date. Congratulations!        "
elif [ ! "_${_CUR_MMN}" = "_${_AVL_MMN}" ]; then
    echo -e "\r  /!\\ A new FreeBSD version is available: \e[1;37m${_AVL_VER}\e[0m. Consider using \e[33mfreebsd-update\e[0m(8)."
else
    echo -e "\r  /!\\ A security update is available: \e[1;37m${_AVL_VER}\e[0m. Consider using \e[33mfreebsd-update\e[0m(8)."
fi
unset _AVL_VER _AVL_MMN _CUR_VER _CUR_MMN
echo ""

Basically what it does, it simply fetches the www.freebsd.org website index, and looks at the number that's right after "Production:" in the middle of the page that tells the visitor which is the latest supported production release. Then it does a second query to cgit.freebsd.org to find out the current patchlevel for this version out of the sys/conf/newvers.sh script. It then assembles that data in a variable, and compares it with the current version string that the "freebsd-version" command returns. If the major/minor part differs, it suggests an OS upgrade. If the patchlevel differs, it tells you a security update is available. And if an error occured while trying to retrieve the latest available version tag, it tells you where it happened.

For example when I logged in today on my server, I got that line printed just before the shell (in glorious ANSI colors):

/!\ A security update is available: 13.0-RELEASE-p11. Consider using freebsd-update(8).

Should my system had been up to date, I'd have received that line instead:

Your base system is up to date. Congratulations!

I totally admit it's not the orthodox way of being informed - mailing lists are here for this purpose - but it nonetheless answered a need that I had, which was to obtain that information at the moment I'm focused on maintenance, and not in my mailbox among the mass of mail that one can receive daily.

I hope some of you will find it useful. Feel free to improve it.
 
Last edited:
Pierre-Marie Baty That's some good stuff. I have to admit that is one of the few things I like about the Ubuntu systems I use for work. Theres a periodic script that checks for update, sets something and when you log in you see "6 updates available, 4 security updates".

I've just been to lazy to emulate the behavior on my FreeBSD systems, so thank you.
 
Geezer - thank you for the hint. I see freebsd-update-probe.sh involves OpenSSL, so at the moment I'll stick with my solution which looks lighter to me (as it involves just 2 HTTP plaintext requests and a lilbit of parsing).

SirDice - thanks for that one. Let me adjust my script a bit with your idea:
Code:
echo ""
echo -ne "  Checking for operating system updates, please wait... "
_CUR_MMN="$(freebsd-version|awk -F- '{print $1}')"
_CUR_VER="$(freebsd-version)"
_AVL_MMN="$(HTTP_TIMEOUT=3 fetch -qo - "http://www.freebsd.org" 2>/dev/null|tr -d '\n'|awk -F"Production:" '{print $2}'|awk -F'>' '{print $2}'|awk -F'<' '{print $1}')"
_AVL_VER="${_AVL_MMN}-$(HTTP_TIMEOUT=3 fetch -qo - "http://cgit.freebsd.org/src/plain/sys/conf/newvers.sh?h=releng/${_AVL_MMN}" 2>/dev/null|grep "^BRANCH="|cut -d'"' -f2)"
if [ -z "${_AVL_MMN}" ]; then
    echo -e "\r  /!\\ Automatic update check failed (error retrieving the latest production version from www.freebsd.org)."
elif [ -z "$(echo "${_AVL_VER}"|grep "RELEASE")" ]; then
    echo -e "\r  /!\\ Automatic update check failed (error retrieving the latest patchlevel version from cgit.freebsd.org)."
elif [ "_${_CUR_VER}" = "_${_AVL_VER}" ]; then
    echo -ne "\r  Checking for available package updates, please wait... "
    _PKG_CNT="$(pkg version -qvRL=|grep -v "orphaned: "|wc -l|sed 's/^[ \t]*//')"
    if [ "${_PKG_CNT}" -gt 0 ]; then
        echo -e "\r  /!\\ You have \e[1;37m${_PKG_CNT}\e[0m outdated package(s). Consider using \e[33mpkg(8) upgrade\e[0m."
    else
        echo -e "\r  Your system is up to date. Congratulations!             "
    fi
elif [ "_${_CUR_MMN}" = "_${_AVL_MMN}" ]; then
    echo -e "\r  /!\\ A security update is available: \e[1;37m${AVL_VER}\e[0m. Consider using \e[33mfreebsd-update\e[0m(8)."
else
    echo -e "\r  /!\\ A new FreeBSD version is available: \e[1;37m${AVL_VER}\e[0m. Consider using \e[33mfreebsd-update\e[0m(8)."
fi
unset _AVL_VER _AVL_MMN _CUR_VER _CUR_MMN _PKG_CNT
echo ""
I'll now be notified of OS upgrades, security updates and package updates. Excellent addition!
 
Last edited:
The lightness is psychological more than anything else.

freebsd-update-probe.sh is the right tool for the job. You can incorporate it in your script easily enough, your script would be about one third of what it is now.

 
your script would be about one third of what it is now.

That's just shuffling the complexity and the responsibility for maintenance around. Sometimes it's fun to do things like this. Sometimes freebsd-update-probe is what you need.

The "lightness" of unencrypted transport however... that is like "holistic computing" or something. Striving for a very particular amount and type of complexity...
 
Well, I wasn't expecting aggro in the replies when I posted these 10 lines of shell script for the sole purpose of sharing something useful.

I am totally okay with people who don't like my way of doing it, yet I totally agree, emotional considerations should be taken aside as much as possible. So okay, let me make my best effort to keep this factual. Sorry in advance for being unpleasant.

The script I proposed can notify the user of 1) new FreeBSD releases, 2) new patches, 3) new packages versions (with SirDice's addition). Parts 1) and 2) fit exactly in 4 lines of shell code and 2 plaintext HTTP requests, part 3) in 1 line, all the rest (the if block) is mere cosmetic display of the results.

The freebsd-update-probe.sh script ("the right tool for the job") can notify the user of 2) i.e. new patches only. It is objectively much longer, and proceeds as such:
1. Make a DNS request to retrieve the SRV records for the freebsd.org domain to find out what are the FQDN for the FreeBSD update servers.
2. Arbitrarily pick the first one, no matter what.
3. Make a plaintext HTTP request to this server to retrieve the latest update's hash value in the form of an encrypted SSL blob.
3. Call OpenSSL to decypher that SSL block into readable plaintext using the public key from /var/db/freebsd-update and
4. Extract the patchlevel information from that line of text.
All this for implementing just part 2 (querying the availability of new patches).

Now, having written my own code signature tool for iOS and macOS in C as part of a cross-build toolchain for non-Apple systems, I am particularly well aware of what computational complexity is involved with SSL. To make it clear: PKI-based crypto is something I am particularly familiar with. From where I talk I feel forced to object that attempting to make the computational cost involved in that operation appear as an "emotional" perspective is, in my opinion, at best misinformed.

So, again, I am totally fine with people who prefer doing it otherwise, and each and every person having come up with an alternate solution to that problem deserve sincere thanks, but so far, my opinion is that my solution does more and is objectively lighter.

*edit* end rant removed.
 
It's good code. It's for the terminal. The message can be combined with motd.

For periodic it needs crontab. A few ways, including with crontab can be used for startup. Crontab is good for running checks in the background.

I was showing a display update notice for the desktop. Also that crontab can be used for terminal or desktop to run in the background.
 
I've cut and pasted the code into a standalone shell script, looks like the "echo -ne" is a bash thing?
 
Yes, that's because bash is my shell. As I said all the display stuff is merely cosmetic. echo -n prints a line without newline (i.e. stays on the same line), echo -e enables ANSI escape sequences (for colored output). Putting \r (carriage return) in front of a line makes the cursor jump back to the first column. When the previous line was written without newline, it allows for displaying changing messages on the same line. E.g.

Checking for operating system updates, please wait...

changes into (once the HTTP reply has arrived)

Checking for package updates, please wait...

which itself changes into (once the list of package updates is built)

Your system is up to date. Congratulations!

Without consuming more lines of output. Of course, if you want to put that in a standalone script (e.g. for cron), you should remove all bash'isms and handle the output differently.

*edit* BTW, /usr/bin/printf can provide the same functionality on shells with limited echo. Simply replace all echo -ne "..." with printf "..." and all echo -e "..." with printf "...\n" (note the ending newline).
 
Pierre-Marie Baty your inaccurate assessment of the suggestion to use freebsd-update-probe.sh needs a few items addressed:
The freebsd-update-probe.sh script ("the right tool for the job") can notify the user of 2) i.e. new patches only.
Correct. It was your choice to construe "the right tool for the job", freebsd-update-probe.sh covers what you called "querying the availability of new patches". The suggestion was "incorporate".
It is objectively much longer, and proceeds as such:
1. Make a DNS request to retrieve the SRV records for the freebsd.org domain to find out what are the FQDN for the FreeBSD update servers.
2. Arbitrarily pick the first one, no matter what.
3. Make a plaintext HTTP request to this server to retrieve the latest update's hash value in the form of an encrypted SSL blob.
3. Call OpenSSL to decypher that SSL block into readable plaintext using the public key from /var/db/freebsd-update and
4. Extract the patchlevel information from that line of text.
All this for implementing just part 2 (querying the availability of new patches).
You are wrong at your point 2. The rest of what you are trying to score points on originate from freebsd-update. freebsd-update-probe.sh efficiently does exactly what it was designed to do, it has one precise purpose and it is very well documented (as is its history).

The suggestion of using freebsd-update-probe.sh was to potentially save you reinventing the wheel for the respective section of your code (not fewer total lines or some notion of being "lighter"), freebsd-update-probe.sh is not the tool for you because you want to do something vaguely similar your own way (which no one contested).

You cried "aggro" and come back with an itemised list attempting to be critical while oozing thinly veiled self-aggrandisement, your bizarre overreaction entirely due to you feeling challenged over your self proclaimed role as script weighmaster. Well done.
 
Last edited:
2022-05-22 - the above scripts were edited to account for the case where the available patchlevel is 0.

tux2bsd, I'm not sure what point your last paragraph intends to make, but it is very instructive indeed, especially from someone who said initially that my script "would be about one third of what it is now" if I used "the right tool for the job". I'm still unable to figure out how you would achieve that. May I ask you humbly to show me the right way to do things please ?
 
SirDice: If you don't mind I'll make a little addition to your suggestion. Today I was notified that I had 3 outdated packages, yet pkg upgrade told me that everything was up to date. Running pkg version -qvRL= in fact showed that these 3 packages were "orphaned". So I suggest to filter that out to obtain the correct count (although it could be useful to report orphaned packages as well).
_PKG_CNT="$(pkg version -qvRL=|grep -v "orphaned: "|wc -l|sed 's/^[ \t]*//')"
The scripts above were updated.

*edit* could a knowledgeable person tell me if there's a way to specify the acceptable TCP timeout when using pkg ?
 

SirDice

Administrator
Staff member
Administrator
Moderator
Sure, if you want those split out, why not? On my systems I consider those 'orphaned' packages a problem because that shouldn't happen (I have my own repositories). It is an indication something went wrong and I need to investigate.
 
Thank you for the script I am testing it on my laptop and on my office server.

On my laptop
Code:
$ pkg version -qvRL=|grep -v "orphaned: "
liboping-1.10.0_1                  >   succeeds remote (remote has 1.8.0_2)
mpv-0.34.1_1,1                     <   needs updating (remote has 0.34.1_2,1)
The liboping package is newer then remote and the mpv package is locked
Code:
$ pkg lock -l
Currently locked packages:
mpv-0.34.1_1,1
In your script I have replaced the line
Bash:
_PKG_CNT="$(pkg version -qvRL=|grep -v "orphaned: "|wc -l|sed 's/^[ \t]*//')"
with these 2 lines
Bash:
_PKG_CNT="$(pkg upgrade -n | egrep -o "The following [0-9]+ package\(s\) will be affected" | egrep -o "[0-9]+")"
test -z "$_PKG_CNT" && _PKG_CNT="0"
The _PKG_CNT variable has the correct number of packages to upgrade including the new packages to be installed, packages to be upgraded and the packages to be reinstalled, excluding the locked packages or the packages newer than remote.
Regards
--
Maurizio
 
unitrunker
I should have done that, you are right. Instead I just copy/pasted a bit of my .profile without adding the shell specification. That's a bad practice. My apologies.

emmex:
Thank you very much for your suggestion. That's definitely an improvement, and I'll use it right away.


I've also updated the HTML parsing of the FreeBSD webpage as today it was reporting mistakenly that I should upgrade to 12.3 from 13.1p2 because the page displays: "Production: 12.3, 13.1". Since I can no longer edit the original post, here's the updated version:
Code:
#!/usr/bin/env sh

# let's do an automatic update check (use http as long as it's supported, https also works)
_ICON_THUMBSUP="\xf0\x9f\x91\x8d"
_ICON_WARNING="\xe2\x9a\xa0\xef\xb8\x8f"
_ICON_NOTICE="\xf0\x9f\x92\xac"
_ICON_WAIT1="\xe2\x8f\xb3"
_ICON_WAIT2="\xe2\x8c\x9b"
echo ""
printf "  ${_ICON_WAIT1}  Checking for operating system updates, please wait... "
_CUR_MMN="$(freebsd-version|awk -F- '{print $1}')"
_CUR_VER="$(freebsd-version)"
_AVL_MMN="$(HTTP_TIMEOUT=3 fetch -qo - "http://www.freebsd.org" 2>/dev/null|tr -d '\n'|sed -E -e 's/<\/?(a|span)[^>]*>//g' -e 's/<\/?(br|li|p|div)>/\n/g'|grep "Production:"|tr ',' ' '|awk '{print $NF}')"
_AVL_VER="${_AVL_MMN}-$(HTTP_TIMEOUT=3 fetch -qo - "http://cgit.freebsd.org/src/plain/sys/conf/newvers.sh?h=releng/${_AVL_MMN}" 2>/dev/null|grep "^BRANCH="|cut -d'"' -f2)"
if [ -z "${_AVL_MMN}" ]; then
    printf "\r  ${_ICON_WARNING}  Automatic update check failed (error retrieving the latest production version from www.freebsd.org).\n"
elif [ -z "$(echo "${_AVL_VER}"|grep "RELEASE")" ]; then
    printf "\r  ${_ICON_WARNING}  Automatic update check failed (error retrieving the latest patchlevel version from cgit.freebsd.org).\n"
elif [ "_${_CUR_VER}" = "_${_AVL_VER}" ]; then
    printf "\r  ${_ICON_WAIT2}  Checking for available package updates, please wait... "
    _PKG_CNT="$(pkg upgrade -n|grep -E -o "^The following [0-9]+"|grep -E -o "[0-9]+"||echo "0")"
    if [ "${_PKG_CNT}" -gt 0 ]; then
        printf "\r  ${_ICON_NOTICE}  You have \e[1;37m${_PKG_CNT}\e[0m outdated package(s). Consider using \e[33mpkg(8) upgrade\e[0m.\n"
    else
        printf "\r  ${_ICON_THUMBSUP}  Your system is up to date. Congratulations!           \n"
    fi
elif [ "_${_CUR_MMN}" = "_${_AVL_MMN}" ]; then
    printf "\r  ${_ICON_WARNING}  A security update is available: \e[1;37m${_AVL_VER}\e[0m. Consider using \e[33mfreebsd-update(8)\e[0m.\n"
else
    printf "\r  ${_ICON_WARNING}  A new FreeBSD version is available: \e[1;37m${_AVL_VER}\e[0m. Consider using \e[33mfreebsd-update(8)\e[0m.\n"
fi
unset _AVL_VER _AVL_MMN _CUR_VER _CUR_MMN _PKG_CNT _ICON_WAIT2 _ICON_WAIT1 _ICON_NOTICE _ICON_WARNING _ICON_THUMBSUP
echo ""
The new version strips some HTML tags and does some breakout to better identify the last number in the "Production: " line in case there are several of them (like today). It does no longer depend on Bash. I added some Unicode characters (hourglass, warning sign) to bring some perfectly superfluous eyecandiness.

⏳ Checking for operating system updates, please wait...

⌛ Checking for available package updates, please wait...

⚠️ A security update is available: 13.1-RELEASE-p2. Consider using freebsd-update(8).

💬 You have 15 outdated package(s). Consider using pkg(8) upgrade.

👍 Your system is up to date. Congratulations!

Looks good on a Mac terminal (with black background), and the icon does that I notice the message immediately.

tux2bsd
I don't understand what is your problem exactly, but we can't stay in that situation. What can I do to help ?
 
Top