#!/usr/bin/env bash # nc-inbound.sh — identify inbound threads in a Cloverleaf NetConfig. # # Three classes of inbound thread: # 1. tcp-listen — PROTOCOL.ISSERVER=1, listens for upstream client TCP connections. # This is "directly fed by upstream client systems" in the strict sense. # 2. icl-or-file — OBWORKASIB=1, TYPE=file. Fed via Cloverleaf's inter-cloverleaf # link (ICL) or file-drop from another internal source. # 3. file-edge — OBWORKASIB=1 + TYPE=file + no ICLSERVERPORT, suggests file-drop # from an external system (less common). # # Usage: # nc-inbound.sh [--mode tcp-listen|icl-or-file|all] [--format tsv|jsonl|table] # # Defaults: --mode all --format tsv # # TSV columns: name, process, class, port, host, type set -u set -o pipefail NC_SELF="$0" LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)" NCP="$LIB_DIR/nc-parse.sh" die() { printf 'nc-inbound: %s\n' "$*" >&2; exit 1; } NC="" MODE="all" FMT="tsv" while [ $# -gt 0 ]; do case "$1" in --mode) shift; MODE="$1" ;; --format) shift; FMT="$1" ;; -h|--help) sed -n '2,18p' "$NC_SELF"; exit 0 ;; -*) die "unknown flag: $1" ;; *) [ -z "$NC" ] && NC="$1" || die "extra arg: $1" ;; esac shift done [ -n "$NC" ] || die "usage: $NC_SELF [--mode ...] [--format ...]" [ -f "$NC" ] || die "not a file: $NC" case "$MODE" in tcp-listen|icl-or-file|all) ;; *) die "bad --mode: $MODE" ;; esac case "$FMT" in tsv|jsonl|table) ;; *) die "bad --format: $FMT" ;; esac # Build records: TSV stream emit collect() { local n "$NCP" list-protocols "$NC" | while IFS= read -r n; do local pname obib outonly ptype phost pport isserver klass pname=$("$NCP" protocol-field "$NC" "$n" PROCESSNAME | head -1) obib=$("$NCP" protocol-field "$NC" "$n" OBWORKASIB | head -1) outonly=$("$NCP" protocol-field "$NC" "$n" OUTBOUNDONLY | head -1) ptype=$("$NCP" protocol-nested "$NC" "$n" PROTOCOL.TYPE 2>/dev/null | head -1) phost=$("$NCP" protocol-nested "$NC" "$n" PROTOCOL.HOST 2>/dev/null | head -1) pport=$("$NCP" protocol-nested "$NC" "$n" PROTOCOL.PORT 2>/dev/null | head -1) isserver=$("$NCP" protocol-nested "$NC" "$n" PROTOCOL.ISSERVER 2>/dev/null | head -1) # Clean empty-brace markers phost=$(printf '%s' "$phost" | sed 's/^{}$//') pport=$(printf '%s' "$pport" | sed 's/^{}$//') if [ "$isserver" = "1" ]; then klass="tcp-listen" elif [ "$obib" = "1" ]; then klass="icl-or-file" else continue # not inbound — skip fi # Mode filter case "$MODE" in tcp-listen) [ "$klass" = "tcp-listen" ] || continue ;; icl-or-file) [ "$klass" = "icl-or-file" ] || continue ;; esac printf "%s\t%s\t%s\t%s\t%s\t%s\n" \ "$n" "${pname:-}" "$klass" "${pport:-}" "${phost:-}" "${ptype:-}" done } case "$FMT" in tsv) printf "name\tprocess\tclass\tport\thost\ttype\n" collect ;; jsonl) collect | awk -F'\t' ' function esc(s) { gsub(/\\/, "\\\\", s); gsub(/"/, "\\\"", s); return s } { printf "{\"name\":\"%s\",\"process\":\"%s\",\"class\":\"%s\",\"port\":\"%s\",\"host\":\"%s\",\"type\":\"%s\"}\n", esc($1), esc($2), esc($3), esc($4), esc($5), esc($6) }' ;; table) { printf "name\tprocess\tclass\tport\thost\ttype\n" collect } | awk -F'\t' ' { for (i=1;i<=NF;i++){ if (length($i)>w[i]) w[i]=length($i); cell[NR,i]=$i }; rows=NR; cols=NF } END { for (r=1; r<=rows; r++) { for (c=1; c<=cols; c++) printf "%-*s ", w[c], cell[r,c] printf "\n" if (r==1) { for (c=1; c<=cols; c++) for (k=0;k