Description: znetconf: qualified return codes and further error handling
Symptom:     always returned 1 in case of errors
Problem:     wrapper tools not able to generate qualified error msgs
Solution:    return codes
---
 zconf/znetconf   |  150 +++++++++++++++++++++++++------------
 zconf/znetconf.8 |  219 ++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 288 insertions(+), 81 deletions(-)

--- a/zconf/znetconf
+++ b/zconf/znetconf
@@ -10,6 +10,50 @@
 # Author(s): Einar Lueck <elelueck@de.ibm.com>
 #            Steffen Maier <maier@de.ibm.com>
 #
+# 9 Could not group devices
+readonly RC_COULD_NOT_GROUP_DEVICES=9
+# 10 Could not set device online
+readonly RC_COULD_NOT_SET_DEV_ONLINE=10
+# 11 Could not set device offline
+readonly RC_COULD_NOT_SET_DEV_OFFLINE=11
+# 12 Invalid attribute value pair
+readonly RC_INVALID_ATTRIBUTE_VALUE_PAIR=12
+# 13 Missing component (broken installation)
+readonly RC_BROKEN_INSTALLATION=13
+# 14 Missing device ID
+readonly RC_MISSING_DEVICE_ID=14
+# 15 Invalid device ID format
+readonly RC_INVALID_DEVICE_ID_FORMAT=15
+# 16 Driver name expected
+readonly RC_DRIVER_NAME_EXPECTED=16
+# 17 Unknown driver
+readonly RC_UNKNOWN_DRIVER=17
+# 18 Missing attribute value pair for -o|--option
+readonly RC_MISSING_ATTRIBUTE_VALUE_PAIR=18
+# 19 Invalid argument
+readonly RC_INVALID_ARGUMENT=19
+# 20 Too much arguments
+readonly RC_TOO_MUCH_ARGUMENTS=20
+# 21 No configuration found for device ID
+readonly RC_NO_CONFIG_FOUND_FOR_DEV=21
+# 22 Device is not configured
+readonly RC_DEVICE_NOT_CONFIGURED=22
+# 23 Could not ungroup device
+readonly RC_COULD_NOT_UNGROUP_DEVICE=23
+# 24 At least one option could not be configured
+readonly RC_OPTION_NOT_CONFIGURED=24
+# 25 Missing value for attribute
+readonly RC_MISSING_VALUE=25
+# 26 Device does not exist
+readonly RC_DEVICE_DOES_NOT_EXIST=26
+# 27 Device already in use
+readonly RC_DEVICE_ALREADY_IN_USE=27
+# 28 Net device did not come online
+readonly RC_NET_DEVICE_NOT_ONLINE=28
+# 29 Some devices could not be added or failed
+readonly RC_SOME_DEVICES_FAILED=29
+# 99 internal error, this should never happen
+readonly RC_INTERNAL_ERROR=99
 
 #==============================================================================
 # constants
@@ -29,6 +73,7 @@ else
 fi
 SYSFSDIR=$(cat /proc/mounts|awk '$3=="sysfs"{print $2; exit}')
 CCWGROUPBUS_DIR=$SYSFSDIR/bus/ccwgroup
+CCWDEV_DIR=$SYSFSDIR/bus/ccw/devices
 CCWGROUPBUS_DEVICEDIR=$CCWGROUPBUS_DIR/devices
 CCWGROUPBUS_DRIVERDIR=$CCWGROUPBUS_DIR/drivers
 DEVNOSEP=","
@@ -296,8 +341,7 @@ function lookup_layer()
 #	$3: driver
 # returns
 #	0 success
-#	1 group file not found
-#	2 grouping failed
+#	RC_COULD_NOT_GROUP_DEVICES
 #
 function group_device()
 {
@@ -340,13 +384,13 @@ function group_device()
 			*)
 				print_error "Could not group devices" \
 					"$DEVICES"
-				return 2
+				return $RC_COULD_NOT_GROUP_DEVICES
 				;;
 		esac
 	else
 		print_error "$GROUPFILE does not exist"
 
-		return 1
+		return $RC_COULD_NOT_GROUP_DEVICES
 	fi
 	return 0
 }
@@ -359,8 +403,7 @@ function group_device()
 #	$2: network device name (just for display purposes)
 # returns
 #	0 success
-#	1 group file not found
-#	2 ungrouping failed
+#	RC_COULD_NOT_UNGROUP_DEVICE
 #
 function ungroup_device()
 {
@@ -378,12 +421,12 @@ function ungroup_device()
 				;;
 			*)
 				print_error "Failed to ungroup $TARGET_DEVID"
-				return 2
+				return $RC_COULD_NOT_UNGROUP_DEVICE
 				;;
 		esac
 	else
 		print_error "$DEVICE_UNGROUPFILE does not exist."
-		return 1
+		return $RC_COULD_NOT_UNGROUP_DEVICE
 	fi
 	return 0
 }
@@ -576,10 +619,10 @@ function configure_ccwgroupdev_option()
 #	$5+opt_count..5+2*opt_count: opt_value_array
 # returns
 #	0 success
-#	1 grouping failed
-#	2 failed to make device online
-#	3 net device did not become available
-#	4 device online but some option could not be set
+#	RC_COULD_NOT_GROUP_DEVICES
+#	RC_COULD_NOT_SET_DEV_ONLINE
+#	RC_NET_DEVICE_NOT_ONLINE
+#	RC_OPTION_NOT_CONFIGURED
 #
 function add_net_device()
 {
@@ -591,10 +634,9 @@ function add_net_device()
 	local OPT_NAME_ARRAY=("${RAW[@]:4:$OPT_COUNT}")
 	local OPT_VALUE_ARRAY=("${RAW[@]:$[4 + $OPT_COUNT]:$OPT_COUNT}")
 
-	group_device $DEVICES $CCWGROUPDEVID $DRIVER
-	if [ $? -ne 0 ]
+	if ! group_device $DEVICES $CCWGROUPDEVID $DRIVER
 	then
-		return 1
+		return $?
 	fi
 
 	local i=0
@@ -602,9 +644,8 @@ function add_net_device()
 	local HAS_LAYER2_OPTION=0
 	while [ $i -lt $OPT_COUNT ]
 	do
-		configure_ccwgroupdev_option $CCWGROUPDEVID \
+		if ! configure_ccwgroupdev_option $CCWGROUPDEVID \
 			 ${OPT_NAME_ARRAY[$i]} ${OPT_VALUE_ARRAY[$i]}
-		if [ $? -ne 0 ]
 		then
 			SOMEOPTION_FAILED=1
 		fi
@@ -629,21 +670,19 @@ function add_net_device()
 		fi
 	fi
 
-	switch_device $CCWGROUPDEVNO 1
-	if [ $? -ne 0 ]
+	if ! switch_device $CCWGROUPDEVNO 1
 	then
-		return 2
+		return $RC_COULD_NOT_SET_DEV_ONLINE
 	fi
 
-	wait_for_net_device $CCWGROUPDEVNO
-	if [ $? -ne 0 ]
+	if ! wait_for_net_device $CCWGROUPDEVNO
 	then
-		return 3
+		return $RC_NET_DEVICE_NOT_ONLINE
 	fi
 
 	if [ $SOMEOPTION_FAILED -ne 0 ]
 	then
-		return 4
+		return $RC_OPTION_NOT_CONFIGURED
 	fi
 
 	return 0
@@ -657,24 +696,22 @@ function add_net_device()
 #	$2: network interface name (just for display purposes)
 # returns
 #	0 success
-#	1 making device offline failed
-#	2 ungrouping device failed
+#	RC_COULD_NOT_SET_DEV_OFFLINE
+#	RC_COULD_NOT_UNGROUP_DEVICE
 #
 function remove_net_device()
 {
 	local DEVICE_TO_REMOVE="$1"
 	local DEVNAME="$2"
 
-	switch_device $DEVICE_TO_REMOVE 0
-	if [ $? -ne 0 ]
+	if ! switch_device $DEVICE_TO_REMOVE 0
 	then
-		return 1
+		return $RC_COULD_NOT_SET_DEV_OFFLINE
 	fi
 
-	ungroup_device $DEVICE_TO_REMOVE $DEVNAME
-	if [ $? -ne 0 ]
+	if ! ungroup_device $DEVICE_TO_REMOVE $DEVNAME
 	then
-		return 2
+		return $RC_COULD_NOT_UNGROUP_DEVICE
 	fi
 	return 0
 }
@@ -1103,7 +1140,7 @@ then
 				if [ $# -lt 1 ]
 				then
 					print_error "Device ID expected"
-					exit 1
+					exit $RC_MISSING_DEVICE_ID
 				fi
 
 				# get parameter expected to be a
@@ -1129,7 +1166,7 @@ then
 					print_error "Invalid device ID format" \
 						"$DEVICE_TO_ADD"
 
-					exit 1
+					exit $RC_INVALID_DEVICE_ID_FORMAT
 				fi
 
 				;;
@@ -1142,7 +1179,7 @@ then
 				if [ $# -lt 1 ]
 				then
 					print_error "Driver name expected"
-					exit 1
+					exit $RC_DRIVER_NAME_EXPECTED
 				fi
 
 				# ensure driver is supported
@@ -1153,7 +1190,7 @@ then
 				then
 					# unknown driver
 					print_error "Unknown driver $DRIVER"
-					exit 1
+					exit $RC_UNKNOWN_DRIVER
 				fi
 				;;
 			-o|--option)
@@ -1164,7 +1201,7 @@ then
 					print_error "Missing attrtibute" \
 						"value pair for -o|--" \
 						"option"
-					exit 1
+					exit $RC_MISSING_ATTRIBUTE_VALUE_PAIR
 				fi
 
 				# get option string
@@ -1174,7 +1211,7 @@ then
 				# store option
 				if ! store_option $OPTIONSTRING
 				then
-					exit $?
+					exit $RC_INVALID_ATTRIBUTE_VALUE_PAIR
 				fi
 				;;
 			-r|--remove)
@@ -1186,7 +1223,7 @@ then
 				then
 					print_error "Expecting <device_bus_id>"\
 						"of device to be removed"
-					exit 1
+					exit $RC_MISSING_ATTRIBUTE_VALUE_PAIR
 				fi
 
 				# get device to be removed
@@ -1203,7 +1240,7 @@ then
 				then
 					print_error "Invalid device ID format" \
 						"$DEVICE_TO_REMOVE"
-					exit 1
+					exit $RC_INVALID_DEVICE_ID_FORMAT
 				fi
 				;;
 			-R|--remove-all)
@@ -1219,7 +1256,7 @@ then
 				then
 					print_error "<device_bus_id> missing"\
 						"for -e|--except"
-					exit 1
+					exit $RC_MISSING_DEVICE_ID
 				fi
 
 				# get device to be removed
@@ -1236,7 +1273,7 @@ then
 				then
 					print_error "Invalid device ID format" \
 						"$EXCEPT_DEVNO"
-					exit 1
+					exit $RC_INVALID_DEVICE_ID_FORMAT
 				fi
 
 				# create a filter statement
@@ -1258,7 +1295,7 @@ then
 			*)
 				echo "$CMD: Invalid option $1"
 				echo "Try '$CMD --help' for more information."
-				exit 1
+				exit $RC_INVALID_ARGUMENT
 				;;
 		esac
 	done
@@ -1271,7 +1308,7 @@ if [ $(($DO_ADD + $DO_ADDALL + $DO_LIST_
 then
 	print_error "Too much arguments"
 	echo "Try '$CMD --help' for more information."
-	exit 1
+	exit $RC_TOO_MUCH_ARGUMENTS
 fi
 
 # react to parsed options
@@ -1290,12 +1327,31 @@ then
 		then
 			print_error "No configuration found for device ID" \
 				"$DEVICE_TO_ADD"
-			exit 1
+			exit $RC_NO_CONFIG_FOUND_FOR_DEV
 		fi
 	else
 		# define set of device numbers to be used
 		DEVICES="$DEVICE_TO_ADD"
 
+		# ensure none of the CCW devices is already in use
+		CCW_DEVS=${DEVICES//,/ }
+		for CCW_DEVNO in $CCW_DEVS
+		do
+			CCW_DEV_ONLINEFILE="$CCWDEV_DIR/$CCW_DEVNO/online"
+			if [ ! -e  $CCW_DEV_ONLINEFILE ]
+			then
+				print_error "Device $CCW_DEVNO does not exist"
+				exit $RC_DEVICE_DOES_NOT_EXIST
+			fi
+
+			read CCW_DEV_ONLINE < $CCW_DEV_ONLINEFILE
+			if [ "$CCW_DEV_ONLINE" == "1" ]
+			then
+				print_error "$CCW_DEVNO is already in use"
+				exit $RC_DEVICE_ALREADY_IN_USE
+			fi
+		done
+
 		# try to find a driver for the given set of device numbers
 		CANDIDATE=$($LSZNET_CALL|
 			awk "\$7~/${DEVICE_TO_ADD//,// && \$7~/}/ {print \$5}")
@@ -1345,7 +1401,7 @@ then
 				$ATTRIBUTE_COUNT "${ATTRIBUTE_NAME[@]}" \
 				"${ATTRIBUTE_VALUE[@]}"
 			then
-				ADDALL_RC=1
+				ADDALL_RC=$RC_SOME_DEVICES_FAILED
 			fi
 		done
 		exit "$ADDALL_RC"
@@ -1363,7 +1419,7 @@ then
 	if [[ -z "$CCWGROUPDEVID_TO_REMOVE" ]]
 	then
 		print_error "$DEVICE_TO_REMOVE is not a configured device"
-		exit 1
+		exit $RC_DEVICE_NOT_CONFIGURED
 	fi
 
 	echo "Remove network device $CCWGROUPDEVID_TO_REMOVE" \
--- a/zconf/znetconf.8
+++ b/zconf/znetconf.8
@@ -1,6 +1,6 @@
 .TH ZNETCONF 8 "Mar 2009" "s390-tools"
 
-.SH NAME 
+.SH NAME
 znetconf \- list and configure network devices for System z network adapters
 
 .SH SYNOPSIS
@@ -8,10 +8,10 @@ znetconf \- list and configure network d
 .B [-h|--help] [-v|--version]
 .br
 
-.br  
-.B znetconf -u | -c 
 .br
-.B znetconf -a <device_bus_id>[,...]{2} [-o <ATTR>=<VALUE>]+ [-d <DRIVER>] 
+.B znetconf -u | -c
+.br
+.B znetconf -a <device_bus_id>[,...]{2} [-o <ATTR>=<VALUE>]+ [-d <DRIVER>]
 .br
 .B znetconf -A [-o <ATTR>=<VALUE>]+ [-d <DRIVER>] [-e <device_bus_id>]+
 .br
@@ -27,12 +27,12 @@ Based on these lists, it automatically a
 For automatic configuration, znetconf builds a channel command word
 (CCW) group device from sensed CCW devices, configures any specified
 option through the sensed network device driver and sets the new
-network device online. 
+network device online.
 .P
 During automatic removal, znetconf sets the device offline and removes it.
 Be aware that removing all network devices leads to the
-complete loss of network connectivity. So a terminal session (e.g. 3270) 
-might be required to restore. 
+complete loss of network connectivity. So a terminal session (e.g. 3270)
+might be required to restore.
 
 .SH OPTIONS
 .TP 8
@@ -43,30 +43,30 @@ Print help text.
 .BR -v | --version
 Print the version of the s390-tools package and the znetconf command.
 
-.TP 
-.BR -u | --unconfigured	
+.TP
+.BR -u | --unconfigured
 List potential network devices that are not yet configured.
 For each device, the following data is provided:
 .RS
 .TP 4
-* 
-Device IDs (device bus bus-IDs) of the CCW devices constituting the network 
+*
+Device IDs (device bus bus-IDs) of the CCW devices constituting the network
 device
 .TP
-* 
+*
 Type of control unit (e.g. 1731/01)
 .TP
-* 
+*
 Network card type (e.g. OSA (QDIO))
 .TP
 *
-Channel path identifier (CHPID) 
+Channel path identifier (CHPID)
 .TP
 *
 Device driver (qeth, lcs, ctc, ctcm)
 .RE
-.TP 
-.BR -c | --configured 
+.TP
+.BR -c | --configured
 List configured network devices. For each device, the following data is
 provided:
 .RS
@@ -104,7 +104,7 @@ device_bus_id can be any of the device
 IDs listed as part of the potential network device list (argument
 .BR -u ")."
 For example, if znetconf
-.BR -u 
+.BR -u
 lists 0.0.f503,0.0.f504,0.0.f505 for a potential network device, device_bus_id
 may be 0.0.f503 or 0.0.f504 or 0.0.f505.
 If a device bus-ID begins with 0.0., you can abbreviate it to the final 4
@@ -115,11 +115,11 @@ If attribute value pairs are given with
 .BR -o ", "
 these pairs are configured for the created network device. The
 device is then set online regardless of whether the given attribute value pairs
-were applied successfully. 
+were applied successfully.
 .br
 Finally, the corresponding network interface name (e.g. eth1) is displayed.
 .br
-If more then one device_bus_id is given, the given set of devices is configured as a network device. znetconf tries to sense the required device driver 
+If more then one device_bus_id is given, the given set of devices is configured as a network device. znetconf tries to sense the required device driver
 automatically. If the device driver cannot be sensed, you must specify it with
 -d.
 .BR -d "."
@@ -145,7 +145,7 @@ znetconf continues with the next remaini
 
 .TP
 .BR -r | --remove " <device_bus_id> [-n | --non-interactive]"
-Remove the network device identified by device_bus_id. device_bus_id is one of 
+Remove the network device identified by device_bus_id. device_bus_id is one of
 the device IDs of the network device. They are listed as part of znetconf
 .BR -c "."
 znetconf sets the device offline and removes it. If
@@ -164,21 +164,105 @@ with
 .TP
 \fB<ATTR>\fR
 Specify a device option. The option must match a sysfs attribute for the device
-to be configured.
+to be configured. For a detailed description of the semantics of sysfs
+attributes please refer to the Device Drivers, Features, and Commands book for
+Linux on System z. The attributes are:
+
+.RS
+.B qeth
+.br
+broadcast_mode
+.br
+buffer_count
+.br
+canonical_macaddr
+.br
+checksumming
+.br
+fake_broadcast
+.br
+ipa_takeover/add4
+.br
+ipa_takeover/add6
+.br
+ipa_takeover/del4
+.br
+ipa_takeover/del6
+.br
+ipa_takeover/enable
+.br
+ipa_takeover/invert4
+.br
+ipa_takeover/invert6
+.br
+isolation
+.br
+large_send
+.br
+layer2
+.br
+performance_stats
+.br
+portname
+.br
+portno
+.br
+priority_queueing
+.br
+route4
+.br
+route6
+.br
+rxip/add4
+.br
+rxip/add6
+.br
+rxip/del4
+.br
+rxip/del6
+.br
+vipa/add4
+.br
+vipa/add6
+.br
+vipa/del4
+.br
+vipa/del
+.RE
+
+.RS
+.B ctc(m)
+.br
+buffer
+.br
+loglevel
+.br
+protocol
+.br
+stats
+.RE
+
+.RS
+.B lcs
+.br
+portno
+.br
+lancmd_timeout
+.RE
 
 .TP
-\fB<device_bus_id>\fR 
+\fB<device_bus_id>\fR
 Specify the device bus-ID of a CCW device. Device bus-IDs have the form
 ([A-Fa-f0-9].[A-Fa-f0-9].)[A-Fa-f0-9]{4}.
 
-If a device bus-ID begins with 0.0., you can abbreviate it to the final 4 
+If a device bus-ID begins with 0.0., you can abbreviate it to the final 4
 hexadecimal digits.
 
 For example, you can abbreviate 0.0.f503 to f503.
 
 .TP
 \fB<DRIVER>\fR
-Specify the device driver for the device. Valid values are qeth, lcs, ctc, or 
+Specify the device driver for the device. Valid values are qeth, lcs, ctc, or
 ctcm.
 
 .SH EXAMPLES
@@ -242,9 +326,9 @@ Shows the list of potential network devi
 Device IDs                 Type    Card Type  CHPID Drv.
 .br
 --------------------------------------------------------
-0.0.f500,0.0.f501,0.0.f502 1731/01 OSA (QDIO) 00    qeth   
+0.0.f500,0.0.f501,0.0.f502 1731/01 OSA (QDIO) 00    qeth
 .br
-0.0.f503,0.0.f504,0.0.f505 1731/01 OSA (QDIO) 01    qeth 
+0.0.f503,0.0.f504,0.0.f505 1731/01 OSA (QDIO) 01    qeth
 .RE
 .P
 \fBznetconf -a 0.0.f503\fR
@@ -258,13 +342,13 @@ lists the new network device.
 .P
 \fBznetconf -a f503\fR
 .RS
-This command is equivalent to \fBznetconf -a 0.0.f503\fR. 
+This command is equivalent to \fBznetconf -a 0.0.f503\fR.
 .RE
 .P
 \fBznetconf -a f503 -o layer2=0 -o portname=myname\fR
 .RS
-Adds the potential network device 
-with 0.0.f503 as one of its device bus-IDs  
+Adds the potential network device
+with 0.0.f503 as one of its device bus-IDs
 and configures the options layer2 with value 0 and
 portname with myname.
 .RE
@@ -288,9 +372,9 @@ Device IDs                 Type    Card 
 
 \fBznetconf -r 0.0.f503\fR
 .RS
-Removes the network device with 0.0.f503 as one of its device bus-IDs. 
+Removes the network device with 0.0.f503 as one of its device bus-IDs.
 You can only remove configured devices as listed by znetconf
-.BR -c "." 
+.BR -c "."
 After successfully running this command the corresponding device appears in the
 list of potential network devices as listed by znetconf
 .BR -u "."
@@ -303,9 +387,76 @@ This command is equivalent to \fBznetcon
 .P
 
 .SH DIAGNOSTICS
-If znetconf runs successfully, the exit status is 0. In case of errors, 1 is
-returned.
-
+If znetconf runs successfully, the exit status is 0. In case of errors, the following codes are returned:
+.TP
+.BR 0
+success
+.TP
+.BR 9
+could not group devices
+.TP
+.BR 10
+could not set device online
+.TP
+.BR 11
+could not set device offline
+.TP
+.BR 12
+invalid attribute value pair
+.TP
+.BR 13
+missing component (broken installation)
+.TP
+.BR 14
+missing device ID
+.TP
+.BR 15
+invalid device ID format
+.TP
+.BR 16
+driver name expected
+.TP
+.BR 17
+unknown driver
+.TP
+.BR 18
+missing attribute value pair for -o|--option
+.TP
+.BR 19
+invalid argument
+.TP
+.BR 20
+too much arguments
+.TP
+.BR 21
+no configuration found for device ID
+.TP
+.BR 22
+device is not configured
+.TP
+.BR 23
+could not ungroup device
+.TP
+.BR 24
+at least one option could not be configured
+.TP
+.BR 25
+missing value for attribute
+.TP
+.BR 26
+device does not exist
+.TP
+.BR 27
+device already in use
+.TP
+.BR 28
+net device did not come online
+.TP
+.BR 29
+some devices could not be added or failed
+.TP
+.BR 99
+internal znetconf bug
 .SH AUTHOR
 .nf
 This man-page was written by Einar Lueck <elelueck@de.ibm.com>.
