#!/bin/sh
#==============================================================================
# Copyright 2008 by Mark Post
#
# This script is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#==============================================================================

CMD=$(basename $0)
num_parms=$#
DEBUG=${DEBUG:-false}

debug() {
if [ "${DEBUG}" == "true" ]; then
	echo $@
fi
}

get_valid_devno() {
valid_dev=0
until [ ${valid_dev} -eq 1 ]; do
	read devno
	if [ -n "${devno}" ]; then
		if [ -n "$(echo ${qeth_offline_devs} | /usr/bin/grep ${devno})" ]; then
			valid_dev=1
			new_devno=${devno}
		else echo That isn\'t a valid offline QETH device number. >&2
		fi
	else echo You have to enter _something_. >&2
	fi
done
}

# Am I running on a mainframe?
if [ ! "$(uname -m)" == "s390x" -a ! "$(uname -m)" == "s390" ]; then
	echo This command only makes sense to run on an IBM mainframe. >&2
	echo Exiting. >&2
	exit 1
fi

# If -h or --help appears anywhere on the command line, output the usage information
if [ -n "$(echo $@ | /usr/bin/grep -w -E -- '--help|-h')" ] ; then
	cat << EOF

Vary on three (3) QETH devices to be used as a network interface.

USAGE: qethon [-h|--help] [readdevno] [writedevno] [datadevno]

   -h, --help - This help message
   readdevno  - The device number of the OSA read subchannel
   writedevno - The device number of the OSA write subchannel
   datadevno  - The device number of the OSA data subchannel

   Note that device numbers must be in the form of 0.cssnum.devnum and all hexadecimal letters must be lower case.
   For example, 0.0.0700 or 0.0.f310

EOF
	exit 1
fi

# If the user wasn't asking for help, then they should only be providing a max of 3 parms
if [ ${num_parms} -gt 3 ]; then
	echo You specified too many device numbers.  Only 3 are allowed. >&2
	exit 1
fi

# Check to see if sysfs support is there
if [ -z "$(/usr/bin/grep sysfs /proc/filesystems)" ]; then
	echo "ERROR: sysfs not found in /proc/filesystems. ${CMD} requires sysfs support!" >&2
	exit 1
fi

# Now check to make sure sysfs is actually mounted
if [ -z "$(/usr/bin/grep sysfs /proc/mounts)" ]; then
	echo "ERROR: ${CMD} requires the sysfs filesystem to be mounted!" >&2
	exit 1
fi

# Check to see if any network interfaces are already available
if [ -n "$(/sbin/ifconfig -a | grep -E "hsi|eth|lcs|ctc")" ]; then
	echo >&2
	echo You already have at least one available network interface >&2
	/sbin/ifconfig -a | grep -E "hsi|eth|lcs|ctc" >&2
	echo >&2
	echo Use the ifconfig command to assign an IP address and network mask >&2
	echo and the route command to define the default gateway. >&2
	echo >&2
	exit 1
fi

# Check to see if the qeth kernel module is loaded
if [ -z "$(/sbin/lsmod | /usr/bin/grep qeth)" ]; then
	echo The qeth kernel module isn\'t loaded.
	echo Do you want to load it now?
	select answer in Yes No
	  do if [ "${answer}" == "Yes" ]; then
		/sbin/modprobe qeth
		break
          elif [ "${answer}" == "No" ]; then
		echo Ok, I\'m quitting then. >&2
		exit 1
		break
	  else echo That wasn\'t a valid response. >&2
	      fi
	  done
fi

# Find out how many QETH devices are on this system
num_qeth=$(/bin/ls /sys/bus/ccw/drivers/qeth/*/online | /usr/bin/wc -l)

# find out how many of those QETH devices are online and offline
num_qeth_on=$(/usr/bin/grep -l 1 /sys/bus/ccw/drivers/qeth/*/online | /usr/bin/cut -f7 -d/ | wc -l)
num_qeth_off=$(/usr/bin/grep -l 0 /sys/bus/ccw/drivers/qeth/*/online | /usr/bin/cut -f7 -d/ | /usr/bin/wc -l)

debug " "
debug You have ${num_qeth} QETH devices attached to this system.
debug Of those, ${num_qeth_on} are online and ${num_qeth_off} are offline.

# Get a list of the onine and offline QETH devices
qeth_online_devs=$(/usr/bin/grep -l 1 /sys/bus/ccw/drivers/qeth/*/online | /usr/bin/cut -f7 -d/)
qeth_offline_devs=$(/usr/bin/grep -l 0 /sys/bus/ccw/drivers/qeth/*/online | /usr/bin/cut -f7 -d/)

# Check to see if there are enough QETH devices already online to work with
if [ ${num_qeth_on} -ge 3 ]; then
	echo >&2
	echo You already have at least three \(3\) QETH devices online >&2
	echo ${qeth_online_devs} >&2
	echo >&2
	echo That should be enough to allow you to use the ifconfig command to >&2
	echo assign an IP address and network mask and the route command to >&2
	echo define the default gateway. >&2
	echo >&2
	exit 1
fi

# Do we have enough offline devices to work with?
if [ ${num_qeth_off} -lt 3 ]; then
	echo You have ${num_qeth_off} offline QETH devices. >&2
	echo That\'s not enough to create a working network interface. >&2
	exit 1
fi

if [ ${num_parms} -lt 3 ]; then
	if [ ${num_qeth_off} -le 10 ]; then
		echo
		echo Online QETH devices
	        echo ${qeth_online_devs}
		echo
		echo Offline QETH devices
		echo ${qeth_offline_devs}
		echo
	else
		echo
		echo There are ${num_qeth_off} offline QETH devices on this system.  Do you want to see them all?
		select answer in Yes No; do
			if [ "${answer}" == "Yes" ]; then
				echo
				echo Online QETH devices
	        		echo ${qeth_online_devs}
				echo
				echo Offline QETH devices
				echo ${qeth_offline_devs}
				break
			elif [ "${answer}" == "No" ]; then
				break
			else echo That wasn\'t a valid response. >&2
			fi
		done
	fi
fi

debug You specified ${num_parms} parameters to this script

declare -a valid_devices
valid_devices[1]=""
valid_devices[2]=""
valid_devices[3]=""
# If the user specified any device numbers, see if they're in the list of offline devices
bad_devno=0
for (( index=1 ; index<=${num_parms} ; index=${index}+1 )); do
	if [ -z "$(echo ${qeth_offline_devs} | /usr/bin/grep ${!index})" ]; then
		echo ${!index} isn\'t a valid offline QETH device number. >&2
		bad_devno=1
	else valid_devices[${index}]=${!index}
	fi
done

if [ ${bad_devno} -eq 1 ]; then
	debug An invalid device number was specified on the command line.
	echo Let\'s start over again. >&2
	num_parms=0
	debug exit 1
fi

valid_input=0
until [ ${valid_input} -eq 1 ]; do
	valid_input=1
	for (( num_valid_devs=${num_parms}; num_valid_devs<3; dummy="xxx" )); do
		debug The number of valid QETH device numbers specified is currently ${num_valid_devs};
		case ${num_valid_devs} in
			0) dev_type="read subchannel"
				;;
			1) dev_type="write subchannel"
				;;
			2) dev_type="data subchannel"
				;;
			*) echo Sanity check failure.  I got here when num_valid_devices is greater than 2. >&2
				;;
		esac
		echo Please enter the device number for the ${dev_type} of an offline QETH device
		new_devno=""
		get_valid_devno new_devno
		if [ "${new_devno}" == "${valid_devices[1]}" -o "${new_devno}" == "${valid_devices[2]}" ]; then
			echo That device number is a duplicate.  Try again. >&2
		else
			let num_valid_devs=${num_valid_devs}+1
			valid_devices[${num_valid_devs}]=${new_devno}
		fi
	done

	read_devno=${valid_devices[1]}
	write_devno=${valid_devices[2]}
	data_devno=${valid_devices[3]}
	debug The read device number is ${read_devno}
	debug The write device number is ${write_devno}
	debug The data device number is ${data_devno}

	# Check to see if the write subchannel is equal to the read subchannel plus one
	# Use the periods in the device id as separators to be able to use the last
	# value as a hexadecimal number
	set -- $(echo ${valid_devices[1]} | tr '.' ' ')
	let read_devnum=0x${3}
	set -- $(echo ${valid_devices[2]} | tr '.' ' ')
	let write_devnum=0x${3}

	hex_devno=$(printf %x ${read_devnum})
	debug The hex read subchannel is ${hex_devno}

	let next_devnum=${read_devnum}+1
	if [ ${next_devnum} != ${write_devnum} ]; then
		echo >&2
		echo Error: The write subchannel must be the device number of the read subchannel plus one. >&2
		echo >&2
		# Wipe out all the old device numbers so that invalid duplicate errors won't get generated
		valid_devices[1]=""
		valid_devices[2]=""
		valid_devices[3]=""
		num_valid_devs=0
		valid_input=0
	fi
done

# Find out if the device needs to be in layer 2 mode or not.
# First, find out if we're running on z/VM, so we can see what the virtual network is like
layer2=""
cputype=$(grep -i "processor 0" /proc/cpuinfo | tr -s " ," " " | cut -f5 -d" ")
if [ "${cputype}" == "FF" ]; then
	debug We ARE running on z/VM
	/sbin/modprobe -q cpint
	/sbin/modprobe -q vmcp
	if [ -n "$(/sbin/lsmod | /usr/bin/grep vmcp)" ]; then
		debug The vmcp kernel module is loaded
		vmcmd=vmcp
	else debug The vmcp kernel module is NOT loaded
	fi
	if [ -n "$(/sbin/lsmod | /usr/bin/grep cpint)" ]; then
		debug The cpint kernel module is loaded
		vmcmd=hcp
	else debug The cpint kernel module is NOT loaded
	fi
	debug The CP interface command name is ${vmcmd}

	# Is this a virtual NIC, or an ATTACHed OSA?
	if [ "$(${vmcmd} q v ${hex_devno} | grep """${hex_devno} ON OSA""")" ]; then
		debug Device ${hex_devno} is an ATTACHed OSA.  I can\'t tell if it needs to be layer 2 or not
	else
		q_nic=$(${vmcmd} q v nic ${hex_devno} | /usr/bin/grep -E "LAN|VSWITCH" | tr -s " ")
		set -f
		set -- $(echo ${q_nic})
		lan_name=${7}
		set +f
		if [ ${lan_name} == Internal -o ${lan_name} == None ]; then
			echo NIC ${hex_devno} is not coupled to a Guest LAN or VSWITCH.  Exiting. >&2
			exit 1
		else
			if [ "$(${vmcmd} q lan ${lan_name} | /usr/bin/grep ETHERNET)" ]; then
				layer2=1
			else if [ "$(${vmcmd} q lan ${lan_name} | /usr/bin/grep -w IP)" ]; then
				layer2=0
			     else	echo LAN ${lan_name} didn\'t show up as ETHERNET or IP. >&2
					exit 1
			     fi
			fi
		fi
	fi

	if [ -n "${layer2}" ]; then
		if [ ${layer2} -eq 1 ]; then
			echo The network that NIC ${hex_devno} is coupled to is in Layer 2 \(Ethernet\) mode
		else	if [ ${layer2} -eq 0 ]; then
			echo The network that NIC ${hex_devno} is coupled to is in Layer 3 \(IP\) mode
			fi
		fi
	fi
fi

# Now ask the user whether to put the NIC in Layer 2 or Layer 3 mode
echo Does this NIC need to be in Layer 2 \(Ethernet\) mode, or Layer 3 \(IP\) mode?
select answer in "Layer 2" "Layer 3"; do
	if [ "${answer}" == "Layer 2" ]; then
		debug Ok, we\'ll set the NIC to Layer 2 mode
		layer2=1
		break
	elif [ "${answer}" == "Layer 3" ]; then
		debug Ok, we\'ll set the NIC to Layer 3 mode
		layer2=0
		break
	else echo That wasn\'t a valid response. >&2
	fi
done

# We're finally to the point we can actually try to do something useful
debug " "
debug Ok, we\'re ready to try to bring these devices online
debug " "

# Sanity check 1.  Make sure the device has really been detected
if [ ! -d /sys/bus/ccw/devices/${read_devno} ]; then
	echo >&2
	echo Error: /sys/bus/ccw/devices/${read_devno} doesn\'t exist! >&2
	exit 1
fi

# Sanity check 2.  Has a qeth group been created from these devices?  There shouldn't be.
if [ -L /sys/bus/ccwgroup/drivers/qeth/${read_devno} ]; then
	echo
	echo /sys/bus/ccwgroup/drivers/qeth/${read_devno} already exists
	echo which means a qeth group has previously been created using ${read_devno}
	echo
	if [ $(cat /sys/bus/ccwgroup/drivers/qeth/${read_devno}/online) == "1" ]; then
		echo And it\'s already online!
		echo I\'m quitting now, since you already have an available interface. >&2
	else	echo But it\'s not online yet.  I\'ll try to bring it online.
		echo ${layer2} > /sys/bus/ccwgroup/drivers/qeth/${read_devno}/layer2
		echo 1 > /sys/bus/ccwgroup/drivers/qeth/${read_devno}/online
		if [ $(cat /sys/bus/ccwgroup/drivers/qeth/${read_devno}/online) == "0" ]; then
			echo It failed to come online.  Sorry, but I don\'t know how to fix that. >&2
			exit 1
		else	echo Success!  It came online for you.
			exit
		fi
	fi
fi

# So, let's actually go ahead and try to bring it online
# Try to create the group first
debug ${read_devno},${write_devno},${data_devno} into /sys/bus/ccwgroup/drivers/qeth/group
echo ${read_devno},${write_devno},${data_devno} > /sys/bus/ccwgroup/drivers/qeth/group

# Did the group get created, and the symbolic link set up by the kernel?
if [ ! -L /sys/bus/ccwgroup/drivers/qeth/${read_devno} ]; then
	echo The kernel didn\'t create the symbolic link at /sys/bus/ccwgroup/drivers/qeth/${read_devno}. >&2
	echo Bailing out on you.  Sorry. >&2
	exit 1
fi

# Echo the 0 or 1 into the layer2 file, depending on what was answered previously
debug echo ${layer2} into /sys/bus/ccwgroup/drivers/qeth/${read_devno}/layer2
echo ${layer2} > /sys/bus/ccwgroup/drivers/qeth/${read_devno}/layer2

# And the final step of echoing a 1 into the "online" pseudo-file
debug echo 1 into /sys/bus/ccwgroup/drivers/qeth/${read_devno}/online
echo 1 > /sys/bus/ccwgroup/drivers/qeth/${read_devno}/online

# Now check to see if the contents of the pseudo-file have changed to a "1".
if [ $(cat /sys/bus/ccwgroup/drivers/qeth/${read_devno}/online) == "0" ]; then
	echo The NIC failed to come online.  Sorry, but I don\'t know how to fix that. >&2
	exit 1
else	echo Success!  It came online for you.
	exit
fi
