Useful scripts

i've been working in this script to manage ports in FreeBSD, called Portman
it's still pretty experimental and serves mostly as aliases for make targets, but here it is:
Bash:
#!/usr/bin/env zsh

# extra pull options in case you've
# cloned the tree differently
#PULL_OPTS=(--rebase)

# we need word splitting here
setopt shwordsplit

msg() {
    tput bold

    if [[ $1 = "ERROR" ]]; then
        tput setaf 1
    elif [[ $1 = "WARNING" ]]; then
        tput setaf 3
    else
        tput setaf 7
    fi

    printf "$1"
    tput setaf 7
    printf ": "
    tput sgr0
    printf "${*:2}"
}

error() {
    msg "ERROR" "${*:2}\n"
    exit $1
}

exist_check() {
    # check if we even have an argument
    [[ -z $1 ]] && error 1 "a port must be specified!"

    # then, check if the thing actually
    # exists
    [[ -d /usr/ports/$1 ]] || error 1 "$1 doesn't exist!"
}

# default pager options:
# shift 5 characters,
# quit if the file fits in a single screen,
# interpret ANSI escape codes,
# and 4-character long tabs
export LESS="-5 -F -R -x4"

# check if we're root so we can
# actually operate
perm_check() {
    [[ $(id -u) = 0 ]] || error 2 "you are not root!"
}

cd /usr/ports

case $1 in
    "update" | "u")
        perm_check

        git pull ${PULL_OPTS[@]}
        ;;

    "install" | "i")
        perm_check

        for ARG in ${@:2}; do
            case $ARG in
                "-d")
                    # check if the user doesn't wanna run
                    # make config first;
                    # because of `&&', add a dummy
                    # program here to run
                    WANTS_CONFIG=(true)
                    ;;

                "-k")
                    # also check if they wanna keep the
                    # work files;
                    # ditto
                    WANTS_CLEAN=(true)
                ;;

                "-r")
                    # also check if they actually wanna
                    # reinstall the port
                    WANTS_REINSTALL=(make deinstall)
                ;;

                *)
                    # if those haven't been set, set them
                    # to their defaults
                    [[ ! $WANTS_REINSTALL ]] && WANTS_REINSTALL=(true)
                    [[ ! $WANTS_CONFIG ]] && WANTS_CONFIG=(make config)
                    [[ ! $WANTS_CLEAN ]] && WANTS_CLEAN=(make clean)
                    exist_check ${ARG}

                    cd ${ARG}
                    # run setup
                    ${WANTS_CONFIG[@]}
                    # build it
                    make &&
                    # if performing an update on it,
                    # deinstall it first
                    ${WANTS_REINSTALL[@]}
                    # finally, install it
                    make install &&
                    # and maybe clean its directory
                    ${WANTS_CLEAN[@]}

                    # and go to previous directory as
                    # we iterate thru them
                    cd -
                    ;;
            esac
        done
        ;;

    "clean" | "c")
        perm_check

        for PKG in ${@:2}; do
            exist_check ${PKG}
            cd ${PKG}

            make clean
        done
        ;;

    "cleandist" | "C")
        perm_check

        if [[ -d "distfiles/" ]]; then
            rm -rf distfiles/
        else
            echo "It's already clean!"
        fi
        ;;

    "remove" | "r")
        perm_check

        # ditto #1
        for PKG in ${@:2}; do
            cd ${PKG}
            make deinstall

            # ditto #2
            cd -
        done
        ;;

    "find" | "f")
        [[ -z $2 ]] && error 1 "a search term must be supplied!"

        make search name=$2
        ;;

    "desc" | "d")
        exist_check $2

        less $2/pkg-descr
        ;;

    "getcfg" | "g")
        exist_check $2

        make -C $2 showconfig
        ;;

    "peek" | "p")
        exist_check $2

        less $2/Makefile
        ;;
    
    "switch" | "s")
        perm_check

        git switch $2
        ;;

    "version" | "v")
        tput bold
        echo -n "Portman v1.0.0a"
        tput sgr0
        echo ","
        echo "written by ruby R53 in October 2025"
        ;;

    "help" | "h" | *)
        msg "Usage" "$0 <action>"
        msg "\nActions (and their short forms)" \
            "\n(u)pdate                   - update ports tree by issuing \`git clone'" \
            "\n(i)nstall [-d] [-k] <port> - install port, more than one may be listed." \
            "\n                             You may also install it without running" \
            "\n                             \`make config' as well ([d]efaults), " \
            "\n                             and/or [k]eeping its work files" \
            "\n(c)lean <port>             - clean <port>'s work files" \
            "\n(C)leandist                - get rid of all distfiles in /usr/ports" \
            "\n(r)emove <port>            - uninstall package, more than one may be" \
            "\n                             listed too" \
            "\n(f)ind <query>             - find ports that match <query>" \
            "\n(d)esc <port>              - get <port>'s description" \
            "\n(g)etcfg <port>            - get <port>'s configuration options" \
            "\n(p)eek <port>              - peek at <port>'s Makefile" \
            "\n(s)witch <branch>          - switch ports tree to <branch>" \
            "\n(v)ersion                  - get running Portman version\n"

        [[ $1 != "help" ]] && [[ $1 != "h" ]] && exit 1
        ;;
esac
lemme know what you guys think, i need ideas for improvements
 
  • Thanks
Reactions: MG
that's a shell built-in, 'tis to tell zsh that it should treat what's quoted as one big thing instead of the individual words contained
 
Pretty old silly script to scan my local network for machines in real-time:
(My modem resets after firmware updates now and then. I use this to find out the new "default" ip's it's using for my computers. 😁 )
Code:
#!/bin/sh

# 0---------------------------------------------------------0
# | poing: network admin utility                            |
# | ping entire ipv4 subnet and display answering nodes     |
# | note: this process forks 254 ping commands in their     |
# | own subshell. If time-out, they stay resident 1 second. |
# | example: poing 192.168.2 tries all nodes on this subnet |
# 0---------------------------------------------------------0

if [ "$1" = "" ] || [ "$(printf "$1" | wc -c)" -lt "5" ]
then
  SUBNET=$(route show default | grep "gateway:" | cut -d ':' -f 2 | tr -d ' ' | cut -d '.' -f 1-3 )
  echo "no adress, trying local subnet: $SUBNET"
else
  SUBNET=$1
fi
ping_host ()
{
  OUTPUT="host $1: "
  if [ $2 ]
  then
    shift
  fi
  ping -c 1 -t 2 -o $1 > /dev/null 2>&1
  if [ $? = 0 ]
  then
    printf "%-20s %s\n" "$OUTPUT" "Reply"
  fi
}
I=1
while [ $I -le 254 ]
do
  ping_host $SUBNET.$I &
  I=$(( I+1 ))
done
sleep 3
 
Inspired by the work Rudy did for his incredibly useful jmem script, to compute the memory being used by a jail, here's a quick oneliner:

sh:
ps -o rss= -J $jid | awk '{ sum += $1; } END { printf("%f GiB\n", sum/(1024*1024)); }'

Where $jid is the id of the jail you're interested in, as revealed by jls(8).
 
Follow up to compute the memory usage of all jails:

sh:
-> for jid in $(jls -n jid | awk -F= '{ print $2; }'); do ps -o rss= -J $jid | awk -v jid=$jid '{ sum += $1; } END { printf("Jail %d: %f GiB\n", jid, sum/(1024*1024)); }'; done
Jail 371: 0.036919 GiB
Jail 372: 0.551922 GiB
Jail 373: 0.598019 GiB
Jail 379: 0.900555 GiB
Jail 380: 0.346405 GiB
Jail 381: 0.235600 GiB
Jail 382: 0.271149 GiB
Jail 383: 0.468822 GiB
Jail 384: 0.792336 GiB
Jail 385: 1.951431 GiB
Jail 386: 4.037846 GiB
Jail 387: 0.111988 GiB
Jail 388: 0.549225 GiB
Jail 389: 0.036457 GiB
Jail 390: 0.663937 GiB
Jail 391: 0.113564 GiB

Now I want to try to get the jail name in there too, as well as producing a summary line for the overall memory consumption…
 
not very useful but fun (and benchmark)
posix shell (sh) implementation of Conway's game of life
by default uses the whole terminal area. i get 60 FPS on a vm hosted by mac mini m4 (30x100) terminal
sh:
#!/bin/sh
frame=0
recal=0
start=$(date +%s)
SEED=$(date +%s)
trap 'intr' SIGINT

TP=$(tput cm 123 123)
tPRE=${TP%%124*}
tEND=${TP##*124}
TMP=${TP#*124}
tMID=${TMP%124*}


intr() {
 print_at2 $(tput li) 1 " "
 tput reset
 end=$(date +%s)
 ss=$((end-start))
 rps="NaN"
 [ $ss -ne 0 ]  &&  rps=$(($frame / $ss))
 echo FRAMES=$frame RECALCS=$recal SECONDS=$ss FPS=$rps
 exit
}

rand() {
  SEED=$(((1103515245 * $SEED + 12345) % 2147483648));
  RANDO=$((SEED % 257));
}

print_at2() {
    ZZ=$tPRE$1$tMID$2$tEND
    echo -n "$ZZ$3"
}


get_at() {
#    ret=0
#    [ $c -eq 0 -o $r -eq 0 -o $r -gt $ROWS -o $c -gt $COLS ] && return
    v=tab_${1}_${2}
    eval ret=\$$v
}

recalc2() {
 local x
 local i
 local j
 local r
 local c
 NEIG=0

 IV="0"
 [ $1 -ne 0 ] && IV="-1 0"
 [ $1 -lt $ROWS ] && IV="$IV 1"
 JV="0"
 [ $2 -ne 0 ] && JV="-1 0" && JT="-1"
 [ $2 -lt $COLS ] && JV="$JV 1" && JT="$JT 1"

 recal=$(($recal + 1))
 for i in $IV
  do
  r=$(($1 + $i))
  [ $i -eq 0 ] && JX=$JT || JX=$JV
   for j in $JX
    do
#     [ $i -eq 0 -a $j -eq 0 ] && continue
     c=$(($2 + $j))
     x=NG_${r}_${c}
     eval NEIG=\$$x
     NEIG=$(($NEIG + $3))
     eval $x=$NEIG
    done
  done
}
count_n() {
 local i
 local j
 NEIG=0
 for i in -1 0 1
  do
   for j in -1 0 1
    do
     [ $i -eq 0 -a $j -eq 0 ] && continue
     get_at $(($1 + $i)) $(($2 + $j))
     NEIG=$(($NEIG + $ret))
    done
  done
#echo $1 $2 = $NEIG
}

ROWS=$(tput li)
COLS=$(tput col)
#ROWS=20
#COLS=50
tput vi
clear

JL=$(jot $ROWS)
JC=$(jot $COLS)
for i in $JL
 do
  for j in $JC
   do
   rand
   z=tab_${i}_${j}
   if  [ $RANDO -le 70 ]
    then
     eval $z=1
      print_at2 $i $j "█"
   else
    eval $z=0
   fi
  done
 done

 C2=$(($COLS+1))
 L2=$(($ROWS+1))
 z=tab_0_0
 eval $z=0
 z=tab_0_${C2}
 eval $z=0
 z=tab_${L2}_0
 eval $z=0
 z=tab_${L2}_${C2}
 eval $z=0

 for i in $JL
  do
  z=tab_${i}_0
  y=tab_${i}_${C2}
  eval $z=0
  eval $y=0
 done
 for j in $JC
  do
  z=tab_0_${j}
  y=tab_${L2}_${j}
  eval $z=0
  eval $y=0
 done

for i in $JL
 do
  for j in $JC
   do
   count_n $i $j
   eval NG_${i}_${j}=$NEIG
#   echo -n $NEIG >>u
   done
#   echo >>u
  done
while true
do
chg=0
LSTD=""
LSTN=""
for i in $JL
 do
  for j in $JC
   do
    key="$i:$j"
    x=NG_${i}_${j}
    eval NEIG=\$$x
    z=tab_${i}_${j}
    eval v=\$$z
    if [ $NEIG -lt 2 -o $NEIG -gt 3 ]
     then
       if  [ $v -eq 1 ]
        then
         print_at2 $i $j " "
         LSTD="$LSTD $key:-1"
         eval $z=0
         chg=1
        fi
     elif [ $NEIG -eq 3 ]
       then
       if [ $v -eq 0 ]
       then
        print_at2 $i $j "█"
        LSTD="$LSTD $key:1"
        chg=1
       eval $z=1
       fi
     else
      #nothing to do for 2 neighbors
      #sda=1
     fi
  done
 done
# sleep 1
 frame=$(($frame + 1))
 [ $chg -eq 0 -o $frame -eq 300 ] && intr && exit
 for d in $LSTD
  do
  IFS=":"
  set -- $d
  unset IFS
  recalc2 $1 $2 $3
 done
 done
 
This is another version of the script that finds source code for FreeBSD base system utilities.
Post the about first version of this script.
Original post with discussion about src(1) from Plan9.

UPD: Script from this post has been significantly improved.

This version has the same interface and behaviour as src(1) from Plan9 does: it extracts debug information from the debug symbols located under /usr/lib/debug and opens the source code in your $EDITOR. If the file itself is not a binary file, but an ASCII script (like man(1) - it is a sh(1) script /usr/bin/man), then the source file is the file itself. If it is a binary, by default it'll search for main function, but you can search for the specific symbol with -s symbol. You can use -n to just print the source file name.

It does handle the hardlinks: for instance src stat and src readlink will both point to /usr/src/usr.bin/stat/stat.c.

There are some tricky cases, like clang(1), for which the debug information is a little bit more complicated, and with straightforward solution if wouldn't find main symbol. This script will point to clang_main in /usr/src/usr.bin/clang/clang/clang-driver.cpp, which is still pretty accurate.

In order to have standalone files with debug symbols for each utility compiled and installed under /usr/lib/debug, you need to make sure that you don't have WITHOUT_DEBUG_FILES=YES in your src.conf(5) (or explicitly set WITH_DEBUG_FILES=YES).

The script itself is based on the solution, suggested by @covacat. Thank you!
sh:
#!/bin/sh

#
# src -- find source code for FreeBSD base system executable.
#
# The corresponding source code file is sent to $EDITOR.
# If file is a binary executable, the information about the source file will be
# extracted from the debug-file located under /usr/lib/debug.
# If file is an ASCII text executable, the source file is the file itself.
# Options:
#    -n        Just display the source code filename.
#    -s symbol    Look for a symbol (defaults to 'main' for binaries).
#

progname=$(basename "${0}" .sh)
DBG_DIR="/usr/lib/debug"
DBG_EXT="debug"
DWARFDUMP="llvm-dwarfdump19"

usage()
{
    echo "usage: ${progname} [-n] [-s symbol] file ..." 1>&2
    exit 2
}

warn()
{
    echo "${progname}: ${@}" 1>&2
}

err()
{
    warn "${@}"
    exit 1
}

ensure_prog()
{
    _path=$(which "${1}" 2>/dev/null)
    test -n "${_path}" && test -x "${_path}" || err "You need ${1} to run this"
}

# locate_src_ascii file
locate_src_ascii()
{
    __src="${1}"
    __src_line="1"
}

accumulate_no_debug_warns()
{
    __no_debug_warns="${__no_debug_warns}
$(warn "${1}" 2>&1)"
}

flush_no_debug_warns()
{
    _warns=$(echo "${__no_debug_warns}" |sed '/^$/d')
    test -n "${_warns}" && warn "${_warns}"
}

# locate_dbg_bin file
locate_dbg_bin()
{
    _file="${1}"
    __src_dbg="${DBG_DIR}${_file}.${DBG_EXT}"
    if [ ! -f "${__src_dbg}" ]; then
        accumulate_no_debug_warns "Debug file for ${_file} not found: ${__src_dbg}"
        return 1
    fi
    return 0
}

# get_dbg_info dbg_file symbol
get_dbg_info()
{
    __dbg_info=$(${DWARFDUMP} -n "${2}" "${1}" 2>/dev/null)
}

# locate_src_file dbg_info
locate_src_file()
{
    __src=$(echo "${1}" \
        |grep "DW_AT_decl_file" \
        |sed -e 's/.*("//' -e 's/")//')
}

# locate_src_by_call_site dbg_file symbol dbg_info
locate_src_file_by_call_site()
{
    _dbg_file="${1}"
    _sym="${2}"
    _dbg_info="${3}"
    if echo "${_dbg_info}" |grep -q "DW_AT_GNU_all_call_sites"; then
        __dbg_info=$(${DWARFDUMP} -cn "${_sym}" "${_dbg_file}" 2>/dev/null \
            |awk 'BEGIN {p=0}; {if($2=="DW_TAG_GNU_call_site") {p=1}; if (p==1) {print $0}}')
        test -z "${__dbg_info}" && return 1
        _call_site_sym=$(echo "${__dbg_info}" \
            |grep "DW_AT_abstract_origin" \
            |sed -e 's/.* "//' -e 's/")//')
        get_dbg_info "${_dbg_file}" "${_call_site_sym}"
        test -z "${__dbg_info}" && return 1
        locate_src_file "${__dbg_info}"
        return 0
    fi
    return 1
}

# locate_src_bin file symbol
locate_src_bin()
{
    _file="${1}"
    _sym="${2}"
    locate_dbg_bin "${_file}"
    test ${?} -ne 0 && return 1
    get_dbg_info "${__src_dbg}" "${_sym}"
    if [ -z "${__dbg_info}" ]; then
        warn "No debug information for symbol ${_sym} in ${__src_dbg}"
        return 1
    fi
    locate_src_file "${__dbg_info}"
    if [ -z "${__src}" ]; then
        locate_src_file_by_call_site "${__src_dbg}" "${_sym}" "${__dbg_info}"
        if [ ${?} -ne 0 ] || [ -z "${__src}" ]; then
            warn "No source file for symbol ${_sym} in ${__src_dbg}"
            return 1
        fi
    fi
    if [ ! -f "${__src}" ]; then
        warn "Source file for ${sym} was found, but it doesn't exist: ${__src}"
    fi
    __src_line=$(echo "${__dbg_info}" \
        |grep "DW_AT_decl_line" \
        |sed -e 's/.*(//' -e 's/)//')
    if [ -z "${__src_line}" ]; then
        warn "No source file line found for symbol ${_sym} in ${__src_dbg}"
        return 1
    fi
}

# try_locate_src file symbol?
try_locate_src()
{
    _filepath="${1}"
    _sym="${2}"
    _filetype=$(file -Lb "${_filepath}")
    case "${_filetype}" in
    *ASCII*)
        locate_src_ascii "${_filepath}"
        return 0
        ;;
    ELF*)
        test -z "${_sym}" && _sym="main"
        locate_src_bin "${_filepath}" "${_sym}"
        return ${?}
        ;;
    ?)
        warn "Unsupported file type for ${_filepath}: ${_filetype}"
        return 1
        ;;
    esac
}

# try_locate_src file symbol?
locate_src()
{
    _file="${1}"
    _sym="${2}"
    _filepath=$(which "${_file}" 2>/dev/null)
    if [ ${?} -ne 0 ]; then
        warn "Can't locate path for ${_file}"
        return 1
    fi
    _candidates=$(find $(dirname "${_filepath}") -samefile "${_filepath}")
    for _candidate in ${_candidates}; do
        try_locate_src "${_candidate}" "${_sym}"
        test ${?} -eq 0 && return 0
    done
    flush_no_debug_warns
    return 1
}

# print_file_name filename line_number
print_filename()
{
    echo "${1}:${2}"
}

# serve_results src_file src_line
serve_results()
{
    _src_file="${1}"
    _src_line="${2}"
    if [ "${print_name_only}" = "1" ]; then
        print_filename "${_src_file}" "${_src_line}"
        return
    fi
    if [ -n "${EDITOR}" ]; then
        ${EDITOR} +"${_src_line}" "${_src_file}"
    else
        print_filename "${_src_file}" "${_src_line}"
        return
    fi
}

handle_opts()
{
    while getopts "ns:" _o; do
        case "${_o}" in
        n)    print_name_only=1 ;;
        s)    sym="${OPTARG}" ;;
        ?)    usage ;;
        esac
    done
}

handle_opts ${@}
shift $((OPTIND - 1))
test ${#} -lt 1 && usage
if [ ${#} -gt 1 ]; then
    print_name_only=1
    test -n "${sym}" && err "-s is only supported for single file argument"
fi
ensure_prog "${DWARFDUMP}"
for file in "${@}"; do
    locate_src "${file}" "${sym}"
    test ${?} -eq 0 && serve_results "${__src}" "${__src_line}"
done
 
Last edited:
a bit off topic
a nice tool for navigating src is elixir.bootlin.com. you click on a symbol (function, global var, macro) and it shows where it is defined, used, ...
 
There are all kinds of ways to get things related to memory via sysctl, top, vmstat, etc. but none of the outputs entirely thrill me. I don't know if this is well-known or not (I couldn't find it specifically mentioned here but the searches, even quoted, just seemed to turn up generic memory stuff so it might have gotten lost in the shuffle) but I just came across this and I like it a lot:

freebsd-memory.sh

Sample output:

Code:
j@direwolf:~/Downloads $ sh freebsd-memory.sh
SYSTEM MEMORY INFORMATION:
mem_wire:        1206157312 (   1150MB) [ 14%] Wired: disabled for paging out
mem_active:  +    176738304 (    168MB) [  2%] Active: recently referenced
mem_inactive:+   5844680704 (   5573MB) [ 71%] Inactive: recently not referenced
mem_cache:   +            0 (      0MB) [  0%] Cached: almost avail. for allocation
mem_free:    +    831909888 (    793MB) [ 10%] Free: fully available for allocation
mem_gap_vm:  +    143003648 (    136MB) [  1%] Memory gap: UNKNOWN
______________ ____________ ___________ ______
mem_all:     =   8202489856 (   7822MB) [100%] Total real memory managed
mem_gap_sys: +    225558528 (    215MB)        Memory gap: Kernel?!
______________ ____________ ___________
mem_phys:    =   8428048384 (   8037MB)        Total real memory available
mem_gap_hw:  +    161886208 (    154MB)        Memory gap: Segment Mappings?!
______________ ____________ ___________
mem_hw:      =   8589934592 (   8192MB)        Total real memory installed

SYSTEM MEMORY SUMMARY:
mem_used:        1913344000 (   1824MB) [ 22%] Logically used memory
mem_avail:   +   6676590592 (   6367MB) [ 77%] Logically available memory
______________ ____________ __________ _______
mem_total:   =   8589934592 (   8192MB) [100%] Logically total memory
 
There are many tools to measure - but the pattern stays the same.

vermaden_2026-02-21_00-36-44.png
 
i had almost the same,
Code:
#!/usr/local/bin/zsh

W=`sysctl vm.stats.vm.v_wire_count    | gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
A=`sysctl vm.stats.vm.v_active_count  | gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
L=`sysctl vm.stats.vm.v_laundry_count | gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
I=`sysctl vm.stats.vm.v_inactive_count| gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
C=`sysctl vm.stats.vm.v_cache_count   | gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
F=`sysctl vm.stats.vm.v_free_count    | gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
T=`sysctl vm.stats.vm.v_page_count    | gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
T2="$(( $W + $A + $L + $I + $C + $F))"
G=`echo "$(( $T -$T2))"               | gawk '{$1=$1*4096/1024/1024;printf("%04.0f\n",$1);}'`
echo "   Wired:"$W"M"
echo "  Active:"$A"M"
echo " Laundry:"$L"M"
echo "Inactive:"$I"M"
echo "   Cache:"$C"M"
echo "    Free:"$F"M"
echo "     Gap:"$G"M"
echo "--------------"
echo "   Total:"$T"M"
 
I've already posted two versions of src script (the previous post), but I think I finally settled on this, more improved version of that script, that now is capable of locating sources for kernel (by default, system calls), libraries (by default, C library (libc)), or arbitrary file that contains debug information. It can be hard-linked to either:
ksrc (kernel)
lsrc (library)
or
dsrc (any debug file)

Examples:
ksrc fork - find sources for fork(2) system call.
ksrc -s kern_mmap - find sources for kernel function kern_mmap.
lsrc strdup - find sources for C library function strdup(3).
lsrc -l m asin - find sources for math(3) library function asin(3).
lsrc -l fetch fetchMakeURL - find source for fetch(3) library function fetchMakeURL.

You can find more info in the title comment in the script itself:
sh:
#!/bin/sh

#
# src -- find source code for FreeBSD components.
#
# The corresponding source code file is sent to $EDITOR.
# This script can be invoked as src, ksrc, lsrc or dsrc.
#
# src locates source code for base system program named 'file'.  If 'file' is a
# binary executable, the information about the source file will be extracted
# from the debug-file located under /usr/lib/debug.  If file is an ASCII text
# executable, the source file is the file itself.
#
# ksrc locates source code for kernel components.  By default 'argument' is
# treated as a system call name.  A particular symbol can be searched instead
# with -s option.  The information is extracted from the kernel debug-file
# /usr/lib/debug/boot/kernel/kernel.debug.
#
# lsrc locates source code for C library functions.  By default it will search
# for libc functions (libc.so), but other libraries can be specified with -l.
#
# dsrc locates source code for the specified symbols in the specified
# debug-files.
#
# Options:
#    -d        Just display the source code directory.
#    -f file        (Mandatory if invoked as dsrc): add debug-file 'file' to
#                   the list of files to search in.
#                Unused for other invocation forms.
#    -l lib        (If invoked as lsrc only): add library 'lib' to the
#                  list of libraries to search in.
#    -n        Just display the source code filename.
#    -s symbol    Look for a symbol.
#                 If invoked as src, option names a symbol to find in the
#               source of a program 'file'.
#               If invoked as ksrc, option doesn't take argument and
#               means that argument 'argument' should be treated not as
#               a syscall name, but as a symbol.
#               This options is not supported if invoked as lsrc.
#

progname=$(basename "${0}" .sh)
DBG_DIR="/usr/lib/debug"
DBG_EXT="debug"
DWARFDUMP="llvm-dwarfdump19"
DEBUG_MODE_PREFIX="d"
LIB_MODE_PREFIX="l"
LIB_DIRS="/lib /usr/lib"
LIB_DEFAULT="c"
KERN_MODE_PREFIX="k"
KERN_PATH="/boot/kernel/kernel"
KERN_SYSCALL_PREFIX="sys_"
REG_SYM_DEFAULT="main"

usage()
{
    _reg_name=$(echo "${progname}" |sed -e "s/^${KERN_MODE_PREFIX}//" \
        -e "s/^${LIB_MODE_PREFIX}//" -e "s/^${DEBUG_MODE_PREFIX}//")
    cat 1>&2 <<__EOF__
usage: ${_reg_name} [-d] [-n] [-s symbol] file ...
       ${KERN_MODE_PREFIX}${_reg_name} [-d] [-n] [-s] argument ...
       ${LIB_MODE_PREFIX}${_reg_name} [-d] [-n] [-l lib ...] function ...
       ${DEBUG_MODE_PREFIX}${_reg_name} [-d] [-n] -f file ... symbol ...
__EOF__
    exit 2
}

warn()
{
    echo "${progname}: ${@}" 1>&2
}

err()
{
    warn "${@}"
    exit 1
}

# get_line line_number list
get_line()
{
    _num="${1}"
    shift
    echo "${@}" |sed -n "${_num}p"
}

ensure_prog()
{
    _path=$(which "${1}" 2>/dev/null)
    test -n "${_path}" && test -x "${_path}" || err "You need ${1} to run this"
}

# append value var
append()
{
    _val="${1}"
    _var="${2}"
    _var_val=$(eval echo \"\$${_var}\")
    eval "${_var}=\$(printf '${_var_val}\n${_val}')"
}

# trim_empty var
trim_empty_var()
{
    _var="${1}"
    eval "${_var}=\$(echo \"\$${_var}\" |sort -u |sed '/^$/d')"
}

# locate_lib lib
locate_lib()
{
    find ${LIB_DIRS} -type f -name "lib${1}.so.*" \
        -not -path "${DBG_DIR}*" 2>/dev/null
}

check_debug_mode()
{
    test $(echo "${progname}" |head -c 1) = "${DEBUG_MODE_PREFIX}"
}

check_lib_mode()
{
    test $(echo "${progname}" |head -c 1) = "${LIB_MODE_PREFIX}"
}

check_kern_mode()
{
    test $(echo "${progname}" |head -c 1) = "${KERN_MODE_PREFIX}"
}

# locate_src_ascii file
locate_src_ascii()
{
    __src="${1}"
    __src_line="1"
}

accumulate_no_debug_warns()
{
    __no_debug_warns="${__no_debug_warns}
$(warn "${1}" 2>&1)"
}

flush_no_debug_warns()
{
    _warns=$(echo "${__no_debug_warns}" |sort -u |sed '/^$/d')
    test -n "${_warns}" && echo "${_warns}" 1>&2
}

# locate_dbg_bin files
locate_dbg_bin()
{
    for _file in ${@}; do
        _src_dbg="${DBG_DIR}${_file}.${DBG_EXT}"
        if [ ! -f "${_src_dbg}" ]; then
            accumulate_no_debug_warns "Debug file for ${_file} not found: ${_src_dbg}"
            return 1
        fi
        __src_dbg=$(printf "${__src_dbg}\n${_src_dbg}")
    done
    __src_dbg=$(echo "${__src_dbg}" |sed '/^$/d')
    return 0
}

# get_dbg_info dbg_files symbol
get_dbg_info()
{
    __dbg_info=$(${DWARFDUMP} -n "${2}" ${1} 2>/dev/null)
}

# locate_src_file dbg_info
#    Sets source filepath in __src and source line number in __src_line.
#    The caller should test these variables for sanity.
locate_src_file()
{
    # Get matches in the following order:
    #     - DW_AT_decl_line
    #     - DW-AT_decl_file
    #     ... then in 1-6 lines we hopefully get DW_TAG_*.
    _src_info=$(echo "${1}" \
        |tail -r \
        |grep -B 1 -A 6 "DW_AT_decl_file" \
        |sed -e 's/.*("//' -e 's/")//')
    test -z "${_src_info}" && return
    _offset=0;
    # Try to find a C source file, but don't count DW_TAG_label.
    while true; do
        unset _src_c_info
        # Search for the first match of .c file that is not related to
        # DW_TAG_label.  If we found a .c file, but it is related to
        # DW_TAG_label, then remember the line number of a match
        # (_offset) and start the next search from that line.  I.e.
        # incrementally search for the first satisfying match in the
        # list.
        _src_c_info=$(echo "${_src_info}" |tail -n "+${_offset}" \
            |grep -B 1 -A 6 -m 1 -n ".*\.c")
        test -z "${_src_c_info}" && break
        _line_num=$(echo "${_src_c_info}" |grep -Eo '^[0-9]+:' |sed 's/://')
        # Remove line number from the match
        _src_c_info=$(echo "${_src_c_info}" |cut -d : -f 2-)
        # That's the match that we need.  Quit the loop.
        echo "${_src_c_info}" |grep -q "DW_TAG_subprogram" && break
        _offset=$((_offset + _line_num + 4))
    done
    # If we found a C source file, pick it, otherwise get the latest source
    # file matched.
    if [ -n "${_src_c_info}" ]; then
        _src_res_info="${_src_c_info}"
        __src=$(get_line 2 "${_src_c_info}")
    else
        _src_res_info=$(echo "${_src_info}" |head -n 2)
        __src=$(get_line 2 "${_src_info}")
    fi
    __src_line=$(echo "${_src_res_info}" \
        |grep "DW_AT_decl_line" \
        |sed -e 's/.*(//' -e 's/)//')
}

# locate_src_by_call_site dbg_files symbol dbg_info
locate_src_file_by_call_site()
{
    _dbg_files="${1}"
    _sym="${2}"
    _dbg_info="${3}"
    if echo "${_dbg_info}" |grep -q "DW_AT_GNU_all_call_sites"; then
        __dbg_info=$(${DWARFDUMP} -cn "${_sym}" "${_dbg_files}" 2>/dev/null \
            |awk 'BEGIN {p=0}; {if($2=="DW_TAG_GNU_call_site") {p=1}; if (p==1) {print $0}}')
        test -z "${__dbg_info}" && return 1
        _call_site_sym=$(echo "${__dbg_info}" \
            |grep "DW_AT_abstract_origin" \
            |sed -e 's/.* "//' -e 's/")//')
        get_dbg_info "${_dbg_files}" "${_call_site_sym}"
        test -z "${__dbg_info}" && return 1
        locate_src_file "${__dbg_info}"
        return 0
    fi
    return 1
}

# locate_src_bin files symbol need_locate_dbg=1
#    If we need to treat files as debug files, then need_locate_dbg should be
#    set to 0.
locate_src_bin()
{
    _files="${1}"
    _sym="${2}"
    _need_locate_dbg="${3:-"1"}"
    if [ "${_need_locate_dbg}" = "1" ]; then
        locate_dbg_bin "${_files}"
        test ${?} -ne 0 && return 1
    else
        __src_dbg="${_files}"
    fi
    get_dbg_info "${__src_dbg}" "${_sym}"
    _src_dbg_comma=$(printf "${__src_dbg}" |tr '\n' ',' |sed 's/,/& /g')
    if [ -z "${__dbg_info}" ]; then
        warn "No debug information for symbol ${_sym} in ${_src_dbg_comma}"
        return 1
    fi
    unset __src
    unset __src_line
    locate_src_file "${__dbg_info}"
    if [ -z "${__src}" ]; then
        locate_src_file_by_call_site "${__src_dbg}" "${_sym}" "${__dbg_info}"
        if [ ${?} -ne 0 ] || [ -z "${__src}" ]; then
            warn "No source file for symbol ${_sym} in ${_src_dbg_comma}"
            return 1
        fi
    fi
    if [ ! -f "${__src}" ]; then
        warn "Source file for ${sym} was found, but it doesn't exist: ${__src}"
    fi
    # __src_line has been set earlier in locate_src_file().
    if [ -z "${__src_line}" ]; then
        warn "No source file line found for symbol ${_sym} in ${_src_dbg_comma}"
        return 1
    fi
}

# try_locate_src file symbols
try_locate_src()
{
    _filepath="${1}"
    _sym="${2}"
    _filetype=$(file -Lb "${_filepath}")
    case "${_filetype}" in
    *ASCII*)
        locate_src_ascii "${_filepath}"
        return 0
        ;;
    ELF*)
        test -z "${_sym}" && _sym="${REG_SYM_DEFAULT}"
        locate_src_bin "${_filepath}" "${_sym}"
        return ${?}
        ;;
    ?)
        warn "Unsupported file type for ${_filepath}: ${_filetype}"
        return 1
        ;;
    esac
}

# locate_src arg1 arg2
#    In regular mode arg1 is a program name, and arg2 is an optional symbol,
#    in kernel mode arg1 holds a syscall name or a symbol,
#    in lib mode arg1 holds a (possibly empty) list of libraries and arg2 is
#    for function name,
#    in debug mode arg1 holds a list of debug-filepaths, arg2 holds a symbol.
locate_src()
{
    _arg1="${1}"
    _arg2="${2}"
    if check_kern_mode; then
        _filepath="${KERN_PATH}"
        test -f "${_filepath}" || err "Kernel file not found: ${_filepath}"
        _sym="${_arg1}"
        test -z "${sym}" && _sym="${KERN_SYSCALL_PREFIX}${_sym}"
        locate_src_bin "${_filepath}" "${_sym}"
        test ${?} -eq 0 && return 0
    elif check_lib_mode; then
        _libs="${_arg1:-"${LIB_DEFAULT}"}"
        _sym="${_arg2}"
        for _lib in ${_libs}; do
            _lib_path=$(locate_lib "${_lib}")
            if [ -z "${_lib_path}" ]; then
                warn "Can't locate shared object for library: ${_lib}"
                _err=1
            fi
            _lib_paths=$(printf "${_lib_paths}\n${_lib_path}")
        done
        if [ "${_err}" = "1" ]; then
            __fatal_err=1
            return 1
        fi
        _lib_paths=$(echo "${_lib_paths}" |sort -u |sed '/^$/d')
        locate_src_bin "${_lib_paths}" "${_sym}"
        test ${?} -eq 0 && return 0
    elif check_debug_mode; then
        _filepaths="${_arg1}"
        _sym="${_arg2}"
        for _filepath in ${_filepaths}; do
            if [ ! -f "${_filepath}" ]; then
                warn "Debug file not found: ${_filepath}"
                _err=1
            fi
        done
        if [ "${_err}" = "1" ]; then
            __fatal_err=1
            return 1
        fi
        locate_src_bin "${_filepaths}" "${_sym}" 0
        test ${?} -eq 0 && return 0
    else
        _file="${_arg1}"
        _filepath=$(which "${_file}" 2>/dev/null)
        if [ ${?} -ne 0 ]; then
            warn "Can't locate path for ${_arg1}"
            return 1
        fi
        _sym="${_arg2}"
        _candidates=$(find $(dirname "${_filepath}") -samefile "${_filepath}")
        for _candidate in ${_candidates}; do
            try_locate_src "${_candidate}" "${_sym}"
            test ${?} -eq 0 && return 0
        done
    fi
    flush_no_debug_warns
    return 1
}

# print_file_name filename line_number
print_filename()
{
    echo "${1}:${2}"
}

# print_dir_name filename
print_dir_name()
{
    dirname "${1}"
}

# serve_results src_file src_line
serve_results()
{
    _src_file="${1}"
    _src_line="${2}"
    if [ "${print_name_only}" = "1" ]; then
        print_filename "${_src_file}" "${_src_line}"
        return
    fi
    if [ "${print_dir_only}" = "1" ]; then
        print_dir_name "${_src_file}"
        return
    fi
    if [ -n "${EDITOR}" ]; then
        ${EDITOR} +"${_src_line}" "${_src_file}"
    else
        print_filename "${_src_file}" "${_src_line}"
        return
    fi
}

handle_opts()
{
    if check_kern_mode; then
        _optstr="dns"
    elif check_lib_mode; then
        _optstr="dl:n"
    elif check_debug_mode; then
        _optstr="df:n"
    else
        _optstr="dns:"
    fi
    while getopts "${_optstr}" _o; do
        case "${_o}" in
        d)    print_dir_only=1 ;;
        f)    append "${OPTARG}" files ;;
        l)    append "${OPTARG}" libs ;;
        n)    print_name_only=1 ;;
        s)    sym="${OPTARG:-"1"}" ;;
        ?)    usage ;;
        esac
    done
    trim_empty_var files
    trim_empty_var libs
}

validate_opts()
{
    test "${print_dir_only}" = "1" && test "${print_name_only}" = "1" &&
        err "-d and -n options are mutually exclusive"
    if [ ${#} -gt 1 ]; then
        test "${print_dir_only}" != "1" && print_name_only=1
        ! check_lib_mode && ! check_kern_mode && test -n "${sym}" &&
            err "-s is only supported for single argument"
    fi
    check_debug_mode && test -z "${files}" && usage
}

handle_opts ${@}
shift $((OPTIND - 1))
validate_opts ${@}
test ${#} -lt 1 && usage
ensure_prog "${DWARFDUMP}"
for arg in "${@}"; do
    if check_lib_mode; then
        locate_src "${libs}" "${arg}"
    elif check_debug_mode; then
        locate_src "${files}" "${arg}"
    else
        locate_src "${arg}" "${sym}"
    fi
    test ${?} -eq 0 && serve_results "${__src}" "${__src_line}"
    test "${__fatal_err}" = "1" && return 1
done
 
Back
Top