#!/bin/ksh
#
#	dmorder - show the order that tapes will be mounted to satisfy
#	recalls.  Also shows which users require which tapes.
#

#
#	Copyright (c) 2008-2009 CSIRO Advanced Scientific Computing.
#	All rights reserved.
#

USE_TMF=yes	# faster, but less information (and requires TMF)
USE_TMF=no	# slower, smarter and ought to work with OpenVault

if [[ $1 == '-raw' ]]; then
    stg2='_cat'	# Just the raw data, for use by other scripts
else
    stg2='awk'	# Nice and pretty for humans
fi

# Invoke common startup dot-script

PATH=/usr/lib/dmf	# only for locating the dot-file
TMP=''			# need TMP directory
. dmf_script_startup.dot
trap 'rc=$?; trap - EXIT; cd /; rm -rf $TMP; exit $rc' EXIT HUP INT QUIT TERM
scratch=$TMP/dmorder.$$
scratch2=$TMP/dmorder2.$$

_assert_root	# Obviously, unprivileged users can run this script by
		# simply deleting this line.  But then they are likely to
		# fail with non-obvious file permission problems.  This
		# assertion is to make it fail politely; remove it if you wish.
_assert_dmf_up	# Abort if the dmfdaemon is *not* running and responding

function _cat	# A 'cat' equivalent which ignores its parameters
{
    cat
} # _cat

function _show_users
{
    if [ "$1" = '-c' ]; then
	shift
	[ -s $1 ] || return
	(
	    sort $1
	    echo
	) \
	| awk -F : '
	    FILENAME == "/etc/passwd" {
		user[$1] = $5
		next
	    }
	    $1 != prev {
		if (prev) print "", cnt, prev, user[prev]
		prev = $1
		cnt=0
	    }
	    {
		cnt++
	    }
	' OFS='\t' /etc/passwd -
    else
	[ -s $1 ] || return
	sort -u $1 \
	| while read user; do
	    sed -n "
		/$user/{
		    s-:/.*--
		    s/:.*:/	/
		    s/^/	/
		    p
		}" /etc/passwd
	done
    fi
} # _show_users

(
    if [[ $USE_TMF == yes ]]; then
	tmstat -p 				# for details of mounted tapes
    else
	dmstat -t | sed -n '4,$s/^/dmstat-t /p'	# for details of mounted tapes
    fi
    dmvoladm -qc 'list hlock f ""' | tail +4	# for details of locked tapes
    dmstat -Rp					# for dmfdaemon requests
) \
| awk '
    BEGIN {stderr = "cat >&2"; omax = 1; sort = "sort -n"}

    # tmstat -p
    {
	#root:1175:T9840C:-:assn::cd0:9:i:n::AA0118:AA0118:10:
	if (split($1, tmstat, ":") == 15) {
	    if (tmstat[1] == "root" && tmstat[5] == "assn") \
		mounted[tmstat[12]] = 1
	    next
	}
    }

    # dmstat -t
    $1 == "dmstat-t" {
	if ($6 == "Mounting") {
	    mounting[$3] = 1
	} else if ($6 != "Unmounting") {
	    mounted[$3] = 1
	}
	next
    }

    # dmvoladm -qc list hlock f ""
    NF == 1 {
	lock[$1] = $1
	next
    }

    # dmstat -Rp
    /Requests at/ {print; next}

    /No requests in progress/ {next}

    /^$/ {print; next}

    $1 == "Req-type" {next}

    {
	#krclvoi 1252867 17:28:43 0:02:21 tha051 sec:R30983 <blah>
	req = $2
	time = $3

#	Mark implicit recalls by using colour
# 	if ($1 != "recall") time = "\033[32m" time "\033[m\033(B"

#	Mark implicit recalls & moves by using colour
#	(to go with new dmget wrapper)
#	if ($1 !~ /recall|move/) time = "\033[32m" time "\033[m\033(B"
 	if ($1 == "move") time = "\033[34m" time "\033[m\033(B"
	else if ($1 != "recall") time = "\033[32m" time "\033[m\033(B"

	owner = $5
	split($6, s, ":")
	vg = s[1]
	vsn = s[2]
	if (!vsn) next

	if (! oldest[vg] && ! mounted[vsn]) oldest[vg] = $4
	vgs[vg] = 1
	vsns[vsn] = vg
	if (! order[vsn]) order[vsn] = ++omax
	if (mounted[vsn]) order[vsn] = 0
	    else if (mounting[vsn]) order[vsn] = 1
	reqs[vsn] = reqs[vsn] " " owner "|" time
    }

    END {
	for (vg in vgs) {
	    m = 0
	    for (v in vsns) {
		if (vsns[v] == vg) m++
	    }
	    printf("Volume Group: %s (%d mounts)", vg, m)
	    if (oldest[vg]) printf("\t\tLongest tape wait is %s", oldest[vg])
	    printf("\n")
	    for (v in vsns) {
		if (vsns[v] != vg) continue
		if (mounted[v] && lock[v]) {
		    tflag = "%"
		    lm_note = 1
		} else if (mounted[v]) {
		    tflag = "*"
		    m_note = 1
		} else if (mounting[v] && lock[v]) {
		    tflag = "#"
		    li_note = 1
		} else if (mounting[v]) {
		    tflag = "+"
		    i_note = 1
		} else if (lock[v]) {
		    tflag = "-"
		    l_note = 1
		} else {
		    tflag = "="
		}
		print order[v], tflag v, reqs[v] | sort
	    }
	    close(sort)
	    print ""
	}
	if (!m) {
	    print "No recalls in progress" | stderr
		exit
	}
	if (m_note) print "  - Tapes which are already mounted are marked with a \"*\"." > scratch2
	if (i_note) print "  - Tapes which are currently mounting are marked with a \"+\"." > scratch2
	if (l_note) print "  - Tapes which are locked are marked with a \"-\"." > scratch2
	if (li_note) print "  - Tapes which are locked AND currently mounting are marked with a \"#\"!" > scratch2
	if (lm_note) print "  - Tapes which are locked AND already mounted are marked with a \"%\"!" > scratch2
    }
    ' scratch2=$scratch2 \
| $stg2 '
    /^Volume Group:/ {num_vgs++}
    $1 !~ /^[0-9]/ {print $0; next}
    {
	sub(/=/, " ", $2)
	printf("    %s", $2)
	for (f = 3; f <= NF; f++) {
	    split($f, sf, "|")
	    owner = sf[1]; time = sf[2]
	    times[owner] = times[owner] ", " time
	    print owner > scratch
	}
	for (o in times) {
	    printf(" %s[%s]", o, substr(times[o], 3))
	}
	delete times
	print ""
    }
    END {exit num_vgs}
    ' scratch=$scratch
num_vgs=$?

[[ ${1:-} == '-raw' ]] && exit $num_vgs

# Show users' names
_show_users -c $scratch

# Show notes
if (( $num_vgs > 0 )); then
    cat << +++
Notes:
  - Shown for each file is its owner and the time of the recall request.
    Highlighted times indicate implicit recalls (green) or moves (blue).
  - Tapes in the same volume group will normally be used in the order shown.
+++
fi
if (( $num_vgs > 1 )); then
    cat << +++
  - Tapes in different volume groups have no relationship with each other.
+++
fi
[ -s $scratch2 ] && cat $scratch2

#end dmorder
