Useful scripts

Dumping a little helper for sysutils/vm-bhyve I created today, adding a possibility for a "post-startup" script in a horribly hacky way ? ... placed in /var/vm/poststartup.sh

Bash:
#!/bin/sh

vm_script=/var/vm/${1}/poststartup.sh
vm_logfile=/var/vm/${1}/vm-bhyve.log

vm_log()
{
  if [ -w ${vm_logfile} ]; then
    LANG=C echo "`date '+%b %d %H:%M:%S:'` $@" >>${vm_logfile}
  fi
}

if [ -r ${vm_script} ]; then (
  sleep 1;

  for i in 1 2 3 4 5 6 7 8; do
    vm_status=`vm list | grep "^${1} .* Running ("`
    if [ -n "${vm_status}" ]; then
      vm_pid=`echo "${vm_status}" | sed -e 's:.*Running (\([0-9]*\).*:\1:'`
      vm_log poststartup.sh: Running ${vm_script}.
      . ${vm_script}
      exit
    else
      sleep 1
    fi
  done
  vm_log poststartup.sh: Giving up, vm is not running!
) </dev/null >/dev/null 2>&1 &
else
  vm_log poststartup.sh: ${vm_script} is missing!
fi

To use it, just add prestart="/var/vm/poststartup.sh" to your vm config and place an individual poststartup.sh in its directory. E.g. I have the following for my router/firewall vm, so that routing is never ever slowed down or killed by OOM:

Code:
protect -p ${vm_pid}
rtprio 15 -${vm_pid}
 
why did you use () instead of {} for command grouping ?
Dumping a little helper for sysutils/vm-bhyve I created today, adding a possibility for a "post-startup" script in a horribly hacky way ? ... placed in /var/vm/poststartup.sh

Bash:
#!/bin/sh

vm_script=/var/vm/${1}/poststartup.sh
vm_logfile=/var/vm/${1}/vm-bhyve.log

vm_log()
{
  if [ -w ${vm_logfile} ]; then
    LANG=C echo "`date '+%b %d %H:%M:%S:'` $@" >>${vm_logfile}
  fi
}

if [ -r ${vm_script} ]; then (
  sleep 1;

  for i in 1 2 3 4 5 6 7 8; do
    vm_status=`vm list | grep "^${1} .* Running ("`
    if [ -n "${vm_status}" ]; then
      vm_pid=`echo "${vm_status}" | sed -e 's:.*Running (\([0-9]*\).*:\1:'`
      vm_log poststartup.sh: Running ${vm_script}.
      . ${vm_script}
      exit
    else
      sleep 1
    fi
  done
  vm_log poststartup.sh: Giving up, vm is not running!
) </dev/null >/dev/null 2>&1 &
else
  vm_log poststartup.sh: ${vm_script} is missing!
fi

To use it, just add prestart="/var/vm/poststartup.sh" to your vm config and place an individual poststartup.sh in its directory. E.g. I have the following for my router/firewall vm, so that routing is never ever slowed down or killed by OOM:

Code:
protect -p ${vm_pid}
rtprio 15 -${vm_pid}
 
covacat That's not for grouping, I need the subshell (running in background) here, otherwise I'd block the parent preventing it from actually starting the vm, defeating the purpose of the script ?
 
last day of month
date -v -1d -j $(date -v +1m +%Y%m010000) +%d
or say you want to run a cron job in the last day of the month
Code:
#!/bin/sh
[ $(date -v +1d +%m) != $(date  +%m) ] || exit 0
# actual stuff here
Hmm. Today is the last day of the month if tomorrow is a first. That's one less invocation of date:

if test $(date -v +1d +%d) = 01; then; ...; fi
 
Secure Subversion (via svnserve)

Though git tends to be more popular these days, one of the best aspects of Subversion is the standalone server (svnserve) with simple authentication and virtual accounts making it suitable for small to medium teams. Unfortunately traffic (minus passwords) are sent in plain text. One solution is SASL which is fiddly and pulls in a load of random dependencies. Another solution is simply wrapping it all via OpenSSL (and svnserve via stunnel). The following is a quick overview how to do it:

ssvn (svn wrapper)
Code:
#!/bin/sh

set -e

if [ -n "$SVN_TUNNEL" ]; then
  exec openssl s_client -quiet -connect "$1:3691" 2>/dev/null
fi

export SVN_TUNNEL=1

exec svn \
  --no-auth-cache \
  --config-option config:tunnels:ssl="$0" \
  "$@"

stunnel.conf (i.e $ stunnel /path/to/stunnel.conf)
Code:
foreground = yes

[svn]
accept = 3691
connect = 3690
cert = /path/to/random/self/signed.pem

With this in place, you can now access repositories securely using svn+ssl://. For example:

$ ssvn co svn+ssl://example.com/myproj/trunk myproj
 
what a cool topic!!!

I have an script for taking zfs snapshots and sending to an backup HD that are not mounted. The script mounts the partition, and sends the snapshots as backups:


sh:
#!/bin/sh

new_snapshot="zroot@$(date +%d-%m-%Y)"
log_file="/var/log/backup.log"

send_not_script="/root/.dotfiles/scripts/send_notifications"

log_with_timestamp() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$log_file"
}

send_notification() {
    message="$1"
    sh $send_not_script "ZFS Backup" "$message" # Call the send_not script with the message
}

attach_backup_pool() {
    geli_partition="/dev/ada0p2"
    geli_keyfile="/root/keys/ada0p2.key"
    echo | geli attach -k $geli_keyfile -j - $geli_partition
    poolname=$(zpool import | grep "pool:" | awk '{print $2}')
    if [ -z "$(zpool list | grep $poolname)" ]; then
        zpool import -f $poolname
    fi
}

log_with_timestamp "Attaching Backup Pool"
send_notification "Attaching Backup Pool"
attach_backup_pool

log_with_timestamp "Creating new snapshot: $new_snapshot"
zfs snapshot -r "$new_snapshot" >> "$log_file" 2>> "$log_file"

if [ $? -eq 0 ]; then
    log_with_timestamp "Snapshot $new_snapshot created successfully."
    send_notification "Snapshot $new_snapshot created successfully."
   
    log_with_timestamp "Sending snapshot $new_snapshot to backup pool."

    zfs send -R "$new_snapshot" > /backup/"$new_snapshot"
    if [ $? -eq 0 ]; then
        log_with_timestamp "Successfully sent $new_snapshot to backup pool."
        send_notification "Successfully sent $new_snapshot to backup pool."
    else
        log_with_timestamp "Error sending $new_snapshot to backup pool."
        send_notification "Error sending $new_snapshot to backup pool."
    fi
else
    log_with_timestamp "Error creating snapshot $new_snapshot."
    send_notification "Error creating snapshot $new_snapshot."
fi

if [ ! -z "$(zpool list | grep $poolname)" ]; then
    zpool export $poolname
fi

geli detach $geli_partition

send notification:


sh:
#!/bin/sh

tittle="$1"
message="$2"
user="base"
export DISPLAY=":0"
doas -u $user env DISPLAY=$DISPLAY notify-send "$tittle" "$message"


the funny part is that I never tried to revert or actualy see if I can recover a system from the snapshots. But this makes me feel safe anyways.
 
Made a devd rule to have my joystick move to /dev/uhid# automatically for FlightGear:

Code:
/usr/local/etc/devd/sidewinder-precision-pro-joystick-uhidd.conf

Code:
notify 0 {
        match "type" "ATTACH";
        match "ugen" "ugen[0-9]+.[0-9]+";
        match "vendor" "0x045e";
        match "product" "0x0008";
        action "/usr/local/sbin/uhidd -h -H uhid -u /dev/$ugen";
};
 
This is a script I wrote to take care of the arduous task of updating the ssh key of a single machine on some the machines listed in the variable, HOSTS below. Comments and suggestions are welcome.
I’m not sure whether this was part of a FreeBSD installation in 2009, but today you can use ssh-copy-id(1). Moreover, it prevents duplicate entries, too. ?️‍♀️
Some backup tools have problems with long names. So we have to check the length of the path + file name. This can be done with: […]
Depending on your requirements pathchk(1) might be just the right tool for you. ?​
Bash:
pathchk -p '/some/path/to/check' # -p stands for portability check
Some of people want to ping some hosts and find which host is up […]
Consider setting up rwhod(8) instead. ?️​
This night I was compiling LibreOffice. By one o'clock it still wasn't finished.
I didn't want computer to stay up all night, so I wrote this little script: […]
Bash:
pwait $(pgrep make) && shutdown -p now # replace `make` as appropriate☻
I would only change that above into that below: […]
[…] Yes, that seems stupid running /usr/bin/basename twelve times instead of one. […]
You use basename(1) if you know nothing about the value’s structure (read user input). ?‍♀️ We know about $0 though that it successfully led to reading our shell script, so it is a valid pathname to a regular file, and thus using parameter expansion is sufficient; no spawning of new processes necessary.​
Bash:
printf 'My name is %s.\n' "${0##*/}" # `##` means remove largest prefix
[…] Please, post here useful scripts which can help to do some useful or difficult operation on FreeBSD OS. […]
Yeah, the above comments exemplify sometimes it’s just ignorance. The difficultly really lies in knowing your way around. ?️ And I certainly scripted some redundant stuff, too.​
Here is a very simple little script that I use to reduce the size of pictures. […]
It may be worth pointing out any prerequisites; in your particular script not everyone necessarily knows convert(1) is part of graphics/ImageMagick. ?​
In bash I use alias recall='history | egrep' so that I can do something like recall ssh. […]
And if you set HISTIGNORE='recall *' it does not even pollute the history. ☸️
admin script... […]
admin(1) is the name of a POSIX™‐standardized utility from devel/sccs, so maybe it’s not the best choice. ?​
[…] Sometimes it's difficult to locate a particular directory in a long $PATH variable. […]
$ echo $PATH | tr ':' ' ' | xargs -n 1 echoYou can probably write this even shorter
How about
% echo $PATH | tr ':' '\n'
Neat, yet this implementation does not take account of the possibility that $PATH members may be empty strings. ?‍⬛ An empty string implicilty denotes the current working directory; concededly, this notation is a legacy feature.​
Here's better one
$ echo $PATH | awk 'BEGIN {RS=":"}; {print $0}'

this one will respect spaces in directories
I always get a blank line at the end. Considering that an empty string is a valid $PATH specification, this is a bug. ? My take on the task:​
Bash:
#!/bin/sh -u
#        name: print path
# description: list values of `${PATH}` and highlight current working directory
#  maintainer: Firstname Lastname <mailbox@host> # in multi‐sysadmin environment

# Reset graphics rendition selection.
# `2>&-` removes any complaint should `tput` not be accessible via `${PATH}`.
[ -t 1 ] && tput sgr0 2>&-
# We presume this script never gets `source`d by another script.
IFS=':'
# The empty strings are necessary so a leading or trailing `:`
# is regarded as a word _splitter_, i. e. a character _between_ two words.
for path in ''${PATH:?Empty \$PATH has implementation-defined behavior.}''
do
    # Enumerating items is helpful
    # if there are too few `tput lines` to display everything on one screen.
    printf '%2d. ' "${list_item_number:=1}"
    list_item_number=$((${list_item_number} + 1))
   
    # Print current working directory in bold (if supported).
    [ -t 1 ] && [ "${path%.}"              ] || tput bold 2>&-
    # Some people like to indicate directories with a trailing slash.
    [ -t 1 ] && [ "${path%/}" = "${PWD%/}" ] && tput bold 2>&-
   
    # Empty string implicitly means current working directory.
    printf '%s\n' "${path:-(current working directory)}"
    # The last command in a loop determines the entire loop’s exit status.
    # If `tput` is not present in `${PATH}`, this will be non‐zero.
    # We want to report success, though, hence the null command `:`.
    [ -t 1 ] && tput sgr0 2>&- || :
done
# EOF
which - Obtain a full path to the script if run from $PATH (i.e 'run_blender')
Why do you still include a which when olli@ clarified:​
[…] When you call a shell script via PATH, $0 always includes the directory in which it was found.
cd && pwd - Use temp shell to cd into directory and output current directory name (solves relative paths)
Unnecessary, realpath(1) always returns an absolute pathname. ? FYI, the GNU coreutils version of realpath grealpath(1) lets you retain symbolic links unresolved. This is the only reason I could think of for using the cd/ pwd detour (if it had not been for realpath(1) already resolving symbolic links).​
Bash:
grealpath -L "${0}" # -L prints logical pathname, symlinks remaining unresolved
 
Why do you still include a which when olli@ clarified:​
Because after testing it on a range of different (often non-conforming) platforms, the experience did not align to the clarification. Robustness is key and of course some interpreters strip off the path if it is absolute.

Unnecessary, realpath(1) always returns an absolute pathname.​
Similarly. Robustness. I recall there was an (older) version of busybox who's realpath was not non-conforming on the Windows platform. Cygwin also had issues due to the logical drive mapping. The cd && pwd worked around that.

This script has been evolved over many years to be "robust". Making an assumption that everything is perfectly aligned with POSIX unfortunately undermines that. That said, it is still a "best effort" approach. There are at least two platforms I have used that which fails when a relative path is passed through it. So feel free to cut parts out as needed.
 
Code:
#!/bin/sh

# 0--------------------------------------------------------0
# | validate_ip                                            |
# | resource-efficient sh-script to validate a given v4 ip |
# 0--------------------------------------------------------0

ip=$1
result="."
if [ ${#ip} -lt 7 ]
then
  # -> too short
  result="invalid"
else
  if [ ${#ip} -gt 15 ]
  then
    # -> too long
    result="invalid"
  else
    result="valid"
    if [ $(echo "$ip" | sed 's/[^.]//g' | awk '{ print length }') -ne 3 ]
    then
      # -> no 3 dots
      result="invalid"
    else
      result="valid"
      digit1=$(echo $ip | cut -d '.' -f 1)
      case $digit1 in
        (*[^0-9]*|'')
          # -> not a number
          result="invalid"
        ;;
        (*)
          result="valid"
          if [ $digit1 -lt 0 ] || [ $digit1 -gt 255 ]
          then
            # -> 1st byte invalid (0..255)
            result="invalid"
          else
            digit2=$(echo $ip | cut -d '.' -f 2)
            case $digit2 in
              (*[^0-9]*|'')
                # -> not a number
                result="invalid"
              ;;
              (*)
                result="valid"
                if [ $digit2 -lt 0 ] || [ $digit2 -gt 255 ]
                then
                  # -> 2nd byte invalid (0..255)
                  result="invalid"
                else
                  result="valid"
                  digit3=$(echo $ip | cut -d '.' -f 3)
                  case $digit3 in
                    (*[^0-9]*|'')
                      # -> not a number
                      result="invalid"
                    ;;
                    (*)
                      result="valid"
                      if [ $digit3 -lt 0 ] || [ $digit3 -gt 255 ]
                      then
                        # -> 3rd byte invalid (0..255)
                        result="invalid"
                      else
                        result="valid"
                        digit4=$(echo $ip | cut -d '.' -f 4)
                        case $digit4 in
                          (*[^0-9]*|'')
                            # -> not a number
                            result="invalid"
                          ;;
                          (*)
                            result="valid"
                            if [ $digit4 -lt 0 ] || [ $digit4 -gt 255 ]
                            then
                              # -> 4th byte invalid (0..255)
                              result="invalid"
                            else
                              # -> ip valid
                              result="valid"
                            fi
                          ;;
                        esac
                      fi
                    ;;
                  esac
                fi
              ;;
            esac
          fi
        ;;
      esac
    fi
  fi
fi
echo "$result"
 
Code:
# | resource-efficient sh-script to validate a given v4 ip |
Way too complicated and – in my book – “resource‑efficient” for a shell script (i. e. already consuming quite some resources for an awfully trivial task) implies relying on shell built‑ins as much as possible (not spawning extra processes).​
Bash:
#!/bin/sh
#        name: valid IPv4
# description: validate IPv4 quad‑octet address specification (decimal only)
#  maintainer: Firstname Lastname <mailbox@host> # in multi‐sysadmin environment

# We presume this script never gets `source`d by another script.
# Otherwise overwriting IFS without restoring it is bound to cause trouble.
IFS='.'
# Split the shell script’s first argument on period.
# The empty strings are necessary because the period _separates_ fields.
# Without surrounding empty strings a leading or trailing empty field vanishes.
set -- ''${1:?Usage: $0 ⟨decimal IPv4 address string⟩ # Example: $0 8.8.8.8}''
# Check that there are exactly four octets.
[ $# -eq 4 ] || exit 1
# Check that each octet is a value in the interval 0 – 255.
for octet do
	case ${octet} in
		([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])
			# Intentionally empty.
			;;
		(*)
			exit 1
			;;
	esac
done
# The script returns the last statement’s exit status.
# In this case the `for` statement is otherwise successful.
There is a programming dogma that each sub‑program should fit on one screen, on one printed page. This supposedly improved understanding. Now this implementation certainly fulfills this criterion.

Note that inet_addr(3) accepts octal and hexadecimal numbers, too, and such unusual values as 127.123.45678 (= 2138813038), so in this regard the shell script is incomplete.​
 
Way too complicated and – in my book – “resource‑efficient” for a shell script (i. e. already consuming quite some resources for an awfully trivial task) implies relying on shell built‑ins as much as possible (not spawning extra processes).​
Bash:
#!/bin/sh
#        name: valid IPv4
# description: validate IPv4 quad‑octet address specification (decimal only)
#  maintainer: Firstname Lastname <mailbox@host> # in multi‐sysadmin environment

# We presume this script never gets `source`d by another script.
# Otherwise overwriting IFS without restoring it is bound to cause trouble.
IFS='.'
# Split the shell script’s first argument on period.
set -- $1
# Check that there are exactly four octets.
[ $# -eq 4 ] || exit 1
# Check that each octet is a value in the interval 0 – 255.
for octet do
    case ${octet} in
        ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])
            # Intentionally empty.
            ;;
        (*)
            exit 1
            ;;
    esac
done
# The script returns the last statement’s exit status.
# In this case the `for` statement is otherwise successful.
There is a programming dogma that each sub‑program should fit on one screen, on one printed page. This supposedly improved understanding. Now this implementation certainly fulfills this criterion.

Note that inet_addr(3) accepts octal and hexadecimal numbers, too, and such unusual values as 127.123.45678 (= 2138813038), so in this regard the shell script is incomplete.​
The trick is to stop processing at the first fault, and have the possible dropouts ordered in likeliness.
 
As I wrote, this script only takes decimal specifications. The leading zero is – by the standardized inet_addr(3) function – interpreted as an octal base indicator. That means 010.000.001.001 = 8.0.1.1 = 134217985.​
Yes, but people are largely ignorant of the subtleties of IP(v4) address formats. But, many equipment vendors will flush out each octet to a three digit value "to look nice" or to arrange for addresses to nicely align in columnar outputs. Folks copying a script would be likely be ignorant of this unless explicitly stated.

[IIRC, my NCDs were this way. And I think at least one of my QNAPs has a "two button" front panel feature to set IP address that relies on three digit fields -- so you don't have to count up from "0", one at a time.]

Sadly, there is no way to alert a user providing input in that form of the potential for misinterpretation -- unless you see an invalid octal character prefaced with a leading zero, etc.

Safer to insist on an absence of leading zeroes and throw an error if any are detected (another case in your script). I.e., error handling (as always) often leads to code bloat.
 
Back
Top