#! /bin/bash # linuxrc.s390: init process of Red Hat's installer initrd for s390(x) # Copyright (C) 2000-2004 by # Bernhard Rosenkraenzer # Oliver Paukstadt # Karsten Hopp # Florian La Roche # Nils Philippsen # Helge Deller # David Sainty # Copyright (C) IBM Corp. 2008,2009 # Author: Steffen Maier # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # prerequisites of this script to run inside the installer initrd: # - udevadm and udevd need to be there # - have /etc/udev/udev.conf with at least one comment line as content # - if necessary, have udev rules # - lsznet.raw and znetcontrolunits from s390utils-base in /lib/s390-tools # - pack kernel modules and module-init-tools (no longer use busybox for that) # - "multi on" in /etc/host.conf [RH bugs 486457,486461,483244] # TODOs: # - make sure driver modules get loaded automatically # - udev rule for lcs/ctcm vs. cu3088 # debug: set -x VERSION=1.2 export TEXTDOMAIN=s390installer export TEXTDOMAINDIR=/usr/lib/locale # helper function to execute command in arguments and print command on stdout function debug() { # uncomment the following echo "$*" to enable debug output #echo "$*" $* } # FIXME: maybe change to "$$" for production use, in case it wouldn't be init declare -r INITPID="1" unset testing [ "$$" != "$INITPID" ] && testing="1" # uncomment the following test="1" to never execute sensitive commands #testing="1" if [ "$RUNKS" = "0" ]; then RUNKS="" fi # helper function to disable commands while running outside the initrd function tv() { if [ -z "$testing" ]; then $* else return 0 fi } function checkipv6() { local ip=$1 [ -z "$ip" ] && return 1 /sbin/ipcalc -c -6 "$ip" >/dev/null 2>&1 return $? } function checkipv4() { local ip=$1 [ -z "$ip" ] && return 1 /sbin/ipcalc -c -4 "$ip" >/dev/null 2>&1 return $? } function doshutdown() { echo $"about to exec shutdown" exec /sbin/shutdown exit 0 } function doreboot() { if [ -e "/sys/firmware/reipl" ]; then read REIPL_TYPE < /sys/firmware/reipl/reipl_type echo "reipl_type=$REIPL_TYPE" pushd /sys/firmware/reipl/$REIPL_TYPE >/dev/null 2>&1 for i in *; do echo "$i=`cat $i`" done popd >/dev/null 2>&1 fi echo $"about to exec shutdown -r" exec /sbin/shutdown -r exit 0 } function sysecho () { file=$1 shift local i=1 while [ $i -le 10 ] ; do if [ ! -f "$file" ]; then sleep 1 i=$((i+1)) else break fi done [ -f "$file" ] && echo $* > $file } function dasd_settle() { local dasd_status=/sys/bus/ccw/devices/$1/status if [ ! -f $dasd_status ]; then return 1 fi local i=1 while [ $i -le 30 ] ; do local status read status < $dasd_status case $status in online|unformatted) return 0 ;; *) sleep 0.1 i=$((i+1)) ;; esac done return 1 } function dasd_settle_all() { for dasdccw in $(cut -d '(' -f 1 /proc/dasd/devices) ; do if ! dasd_settle $dasdccw ; then echo $"Could not access DASD $dasdccw in time" return 1 fi done return 0 } function startinetd() { echo echo $"Starting sshd to allow login over the network." if [ -z "$testing" ]; then echo $"Welcome to the anaconda install environment $VERSION for $S390ARCH" > /etc/issue.net echo $"Welcome to the anaconda install environment $VERSION for $S390ARCH" > /etc/motd echo >> /etc/motd fi # testing /sbin/sshd -f /etc/ssh/sshd_config.anaconda if [ -z "$RUNKS" ]; then echo echo $"Connect now to $IPADDR and log in as user install to start the installation." echo $"E.g. using: ssh -x install@$IPADDR" echo $"You may log in as the root user to start an interactive shell." read while : ; do /bin/sh --login [ $? = 0 ] || break done fi } # prints a canonocalized device bus ID for a given devno of any format function canonicalize_devno() { case ${#1} in 3) echo "0.0.0${1}" ;; 4) echo "0.0.${1}" ;; *) echo "${1}" ;; esac return 0 } # read file from CMS and write it to /tmp function readcmsfile() # $1=dasdport $2=filename { local dev if [ $# -ne 2 ]; then return; fi # precondition: udevd created dasda block device node if ! sysecho /proc/cio_ignore "free $1"; then echo $"DASD $1 could not be cleared from device blacklist" return 1 fi # /proc/cio_ignore won't block on freeing devices until resensing # has been completed, so wait until the udev event queue depletes # (without udevadm settle we could wait 2 seconds unconditionally) #debug ls -laF /dev/.udev udevadm settle # even though the device might now be online, some of its # sysfs attributes might not yet be available sleep 1 # precondition: dasd_eckd_mod driver incl. dependencies loaded, # dasd_mod must be loaded without setting any DASD online dev=$(canonicalize_devno $1) if ! sysecho /sys/bus/ccw/devices/$dev/online 1; then echo $"DASD $dev could not be set online" return 1 fi udevadm settle if ! dasd_settle $dev ; then echo $"Could not access DASD $dev in time" return 1 fi udevadm settle if ! cmsfscat -d /dev/dasda -a $2 > /tmp/$2; then echo $"Could not read conf file $2 on CMS DASD $1." fi if ! sysecho /sys/bus/ccw/devices/$dev/online 0; then echo $"DASD $dev could not be set offline again" return 1 fi udevadm settle # consequences of no more module unload: loader can no longer # use DASD module option to online DASDs and set other DASD parameters! } # adaption of the same function in init.c (udevd gets started later) function createDevices() { awk '{ printf("mknod /dev/%s %s %s %s\n", $1, $2, $3, $4); printf("chmod %s /dev/%s\n", $5, $1); printf("chown %s /dev/%s\n", $6, $1); }' < /proc/sys/kernel/printk # make /tmp/ramfs mount -t ramfs none /tmp ifconfig lo 127.0.0.1 netmask 255.0.0.0 route add -host 127.0.0.1 dev lo echo -e "127.0.0.1\tlocalhost.localdomain localhost localhost4 localhost4.localdomain4" > /etc/hosts echo -e "::1\t\tlocalhost.localdomain localhost localhost6 localhost6.localdomain6" >> /etc/hosts /sbin/dbus-uuidgen --ensure & [ $? != 0 ] && echo "error on calling /sbin/dbus-uuidgen --ensure" /sbin/dbus-daemon --system & [ $? != 0 ] && echo "error on calling /sbin/dbus-daemon --system" fi # testing } # trigger udev to automatically load device drivers function udev_setup() { if [ -z "$testing" ]; then # debug: udevadm monitor & udevadm trigger udevadm settle fi # testing } # from here on accesses to sysfs try to follow # linux/Documentation/sysfs-rules.txt ### lsznet.raw integration declare -a nettable function read_lsznet_output() { count=0 local line while read line; do nettable[$count]="$line" count=$((count + 1)) # using the more sophisticated process substitution instead of temp file # requires the symlink /dev/fd -> /proc/self/fd => createDevices done < <(/lib/s390-tools/lsznet.raw) } function print_nettable() { local fmtstring="%3s %-14s %-7s %-5s %-4s %-6s %-7s %s\n" printf "$fmtstring" \ "NUM" "CARD" "CU" "CHPID" "TYPE" "DRIVER" "IF" "DEVICES" local i for ((i=0; i < count; i++)); do local item cutype chp chpidtype devdrv devname chlist cardtype read item cutype chp chpidtype devdrv devname chlist cardtype <<< ${nettable[$i]} printf "$fmtstring" \ $item "$cardtype" $cutype $chp "$chpidtype" $devdrv $devname $chlist done } function clear_screen() { # FIXME: find a way to clear screen despite 3215 line mode terminal echo } function dialog_network_table() { while : ; do echo $"Scanning for available network devices..." # This may take a long time so we show "progress": #( while true; do echo -n "."; sleep 1; done ) & #local childpid=$! read_lsznet_output #kill $childpid #echo echo $"Autodetection found ${count} devices." # count==0: there might still be a blacklist the user wants to clear. # do not flood user with long list if there are many devices if [ "$count" -le 15 ]; then # Show list answer=s else # [ $count -gt 15 ] echo while : ; do echo $"s) show all, m) manual config:" local answer read answer case $answer in s|m) break ;; esac done fi [ "$answer" = "m" ] && break # show network table to select network hardware configuration from if [ "$count" -gt 0 ]; then clear_screen print_nettable echo fi # account for possibly ignored common I/O devices # cio_wc_bytes is NOT local so it can be re-used outside this function cio_wc_bytes=0 local cio_wc_filename cio_wc_foo if [ -f /proc/cio_ignore ]; then local cio_wc=$(wc -c /proc/cio_ignore) read cio_wc_bytes cio_wc_filename cio_wc_foo <<< "$cio_wc" if [ "$cio_wc_bytes" != "0" ]; then echo $"Note: There is a device blacklist active! (Clearing might take long)" #cat /proc/cio_ignore | tr '\n' ',' #echo else if [ "$count" -eq 0 ]; then # count==0 AND no device blacklist => manual mode echo $"Entering manual configuration mode." break fi fi fi # selection dialog while : ; do [ "$count" -gt 0 ] && echo -n $") use config, " [ "$cio_wc_bytes" != "0" ] && echo -n $"c) clear blacklist, " echo $"m) manual config, r) rescan, s) shell:" local choice read choice [ -z "$choice" ] && continue if [ "$choice" = "s" ]; then echo $"Enter 'exit' at the shell prompt to get back to the installation dialog." /bin/bash continue 2 fi [ "$choice" = "m" ] && break [ "$choice" = "r" ] && continue 2 [ "$cio_wc_bytes" != "0" -a "$choice" = "c" ] && break [[ "$choice" =~ ^[[:digit:]]+$ ]] case $? in 0) # string matched the pattern [ "$choice" -ge 1 -a "$choice" -le "$count" ] && break ;; 1) # string did not match the pattern continue ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac done if [ "$choice" = "c" ]; then echo $"Clearing device blacklist..." if sysecho /proc/cio_ignore "free all"; then cio_wc_bytes=0 # /proc/cio_ignore won't block on freeing devices # until resensing has been completed, so wait until # the udev event queue depletes. # This may take a long time so we show "progress": #( while true; do echo -n "."; sleep 3; done ) & #local childpid=$! #debug ls -laF /dev/.udev udevadm settle # (virtual) CTC/A takes some more time to appear in sysfs # FIXME: how long to wait? 3 seconds seems to be enough. sleep 3 #kill $childpid #echo continue else echo $"Device blacklist could not be cleared" fi fi [ "$choice" = "m" ] && break # finally extract config info from selected item # array nettable starts at index zero, user input starts at index one choice=$((choice - 1)) local item cutype chp chpidtype devdrv devname chlist cardtype read item cutype chp chpidtype devdrv devname chlist cardtype <<< ${nettable[$choice]} # $NETTYPE happens to be exactly the network driver name if [ "$devdrv" = "ctcm" ]; then NETTYPE="ctc" else NETTYPE=$devdrv fi SUBCHANNELS=$chlist break done echo } declare -r PREFIXFORMAT=[[:xdigit:]]* declare -r SSIDFORMAT=[0-3] declare -r BUSIDFORMAT=[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]] declare -r IDFORMAT=$PREFIXFORMAT.$SSIDFORMAT.$BUSIDFORMAT declare -r SUBCHANNEL_TYPE_IO=0 . /lib/s390-tools/znetcontrolunits function cardtype2cleartext() { local cardtype=$1 case $cardtype in OSD_10GIG) echo "OSA card in OSD mode, 10 Gigabit Ethernet" ;; OSD_1000) echo "OSA card in OSD mode, Gigabit Ethernet" ;; OSD_100) echo "OSA card in OSD mode, Fast Ethernet" ;; OSD_GbE_LANE) echo "OSA card in OSD mode, Gigabit Ethernet, LAN Emulation" ;; OSD_FE_LANE) echo "OSA card in OSD mode, Fast Ethernet, LAN Emulation" ;; OSD_TR_LANE) echo "OSA card in OSD mode, Token Ring, LAN Emulation" ;; OSD_ATM_LANE) echo "OSA card in OSD mode, ATM, LAN Emulation" ;; OSD_Express) echo "OSA card in OSD mode, unknown link type" ;; HSTR) echo "OSA card in OSD mode, High Speed Token Ring" ;; OSN) echo "OSA for NCP, ESCON/CDLC bridge" ;; HiperSockets) echo "HiperSockets with CHPID type IQD" ;; "GuestLAN QDIO") echo "GuestLAN based on OSA (QDIO)" ;; "GuestLAN Hiper") echo "GuestLAN based on HiperSockets" ;; unknown) echo "other" ;; *) echo "unknown" echo "l.$LINENO: found unknown card_type, code needs to be fixed" 1>&2 ;; esac } # returns true iff running under z/VM function isVM() { local cpu_version=$(cat /proc/cpuinfo |grep "^processor " | head -n1 | sed 's/.*version = \([[:xdigit:]][[:xdigit:]]\).*/\1/' | tr '[:lower:]' '[:upper:]') if [ "$cpu_version" = "FF" ]; then return 0 else return 1 fi } # watch out: potential error message as side effect function isLayer2Default() { # Read default from sysfs because according to device # drivers book there are differences in the default between # OSA (l2), hipersockets (l3). # This only works here in installer where nobody has overwritten # the default setting with another custom value already! if [ ! -f /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/layer2 ]; then echo $"Could not read layer mode from sysfs" return 1 fi local layer2 read layer2 < /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/layer2 if [ "$layer2" = "1" ]; then return 0 else return 1 fi } # returns true iff either LAYER2 has been set to 1 or is the default # watch out: potential error message as side effect function isLayer2() { case "x$LAYER2" in x0) return 1 ;; # layer 3 x1) return 0 ;; # layer 2 x) # LAYER2 is unset or empty => qeth driver default applies. isLayer2Default return $? ;; *) echo "l.$LINENO: unknown value \"$LAYER2\" for LAYER2, code needs to be fixed" 1>&2 return 2 ;; esac } # returns true iff qeth device $SCH_R_DEVBUSID # is capable of supporting IPv6 # watch out: potential error message as side effect function ipv6_capable() { [ "$NETTYPE" = "qeth" ] || return 1 case $cardtype in OSD_10GIG|OSD_1000|OSD_100|OSD_Express|HiperSockets|"GuestLAN QDIO") return 0 ;; OSD_GbE_LANE|OSD_FE_LANE|OSD_TR_LANE|OSD_ATM_LANE) return 1 ;; HSTR|OSN|unknown) return 1 ;; "GuestLAN Hiper") return 1 ;; *) echo $"Unknown card_type to determine IPv6 support" return 1 ;; esac } # sets device online _and_ retrieves DEVICE at the same time function set_device_online() { echo $"Activating network device..." local sysnettype case "${NETTYPE}" in qeth|lcs) sysnettype=${NETTYPE} ;; ctc) sysnettype=ctm ;; esac if ! [ -f /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online ]; then echo $"Sysfs path to set device online does not exist." return 1 fi if ! sysecho /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online "1"; then echo $"Could not set device ($SUBCHANNELS) online" return 1 fi udevadm settle local i=1 while : ; do local online read online < /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online [ "$online" == "1" ] && break sleep 1 i=$((i+1)) if [ "$i" -gt 10 ]; then echo $"Could not set device ($SUBCHANNELS) online within timeout" return 1 fi done if [ "$NETTYPE" = "lcs" -o "$NETTYPE" = "ctc" ]; then # KH FIXME: Workaround for missing sysfs interface # DEVICE=$(cat /sys/devices/lcs/${SUBCHANNELS//,*/}/if_name) # replaced with flexible solution: # https://bugzilla.redhat.com/show_bug.cgi?id=204803#c9 # "sys/bus/ccwgroup/devices/${SUBCHANNEL}/net\:* # for lcs after setting online" if [ ! -h /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/net:* ]; then echo $"Device $SUBCHANNELS does not have required sysfs attribute 'net:*'" return 1 fi DEVICE=$(echo /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/net:*) DEVICE=${DEVICE//*:/} if [ "$DEVICE" = "" ]; then echo $"Could not get device name for $SUBCHANNELS" return 1 fi else # qeth if [ ! -f /sys/devices/qeth/$SCH_R_DEVBUSID/if_name ]; then echo $"Device $SUBCHANNELS does not have required sysfs attribute 'if_name'" return 1 fi # (device needs to be online to read if_name from sysfs attribute!) read DEVICE < /sys/devices/qeth/$SCH_R_DEVBUSID/if_name if [ "$DEVICE" = "" ]; then echo $"Could not get device name for $SUBCHANNELS" return 1 fi if [ -f /sys/devices/qeth/$SCH_R_DEVBUSID/card_type ]; then read cardtype < /sys/devices/qeth/$SCH_R_DEVBUSID/card_type #debug echo "$cardtype" # device is now online and link type will be known echo -n $"Detected: " cardtype2cleartext "$cardtype" else echo $"Could not read qeth network card type from sysfs." fi fi } # sets device up and blocks until device appears to be up function set_device_up() { if [ -z "$DEVICE" ]; then echo $"Could not determine interface name to bring up device $SUBCHANNELS" return 1 fi # Device does not come up fast enough to use "ip" to configure, so block. # While OSA come up themselves after setting online, # e.g. HiperSockets won't => set them up explicitly for the following check debug ip link set up $DEVICE local i=1 while : ; do local tst=$(ip -o link show up dev $DEVICE) [ -n "$tst" ] && break sleep 1 i=$((i+1)) if [ "$i" -gt 10 ]; then echo $"Could not bring up device $DEVICE within timeout" return 1 fi done return 0 } function syntax_check_domainname() { # - match against regex adopted from RFC1035,sec.2.3.1 or RFC1034,sec.3.5 # (Internationalized Domain Names in Applications (IDNA) [RFC4690] # have to be entered after encoding by punycode [RFC3492]) [[ "$1" =~ ^[[:alpha:]]([[:alnum:]-]{0,61}[[:alnum:]])?(\.[[:alpha:]]([[:alnum:]-]{0,61}[[:alnum:]])?)*$ ]] case $? in 0) # string matched the pattern return 0 ;; 1) # string did not match the pattern echo "$2" ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac return 1 } function modprobe_alias() { if [ ":$NETTYPE" = ":ctc" ]; then echo "alias $DEVICE ctcm" >> /tmp/modprobe.conf else echo "alias $DEVICE $NETTYPE" >> /tmp/modprobe.conf fi if [ $? -ne 0 ]; then echo $"Could not append alias for network device $DEVICE to modprobe.conf" return 1 fi return 0 } function disable_ipv6_autoconf() { sysctl -w net.ipv6.conf.all.accept_ra=0 > /dev/null sysctl -w net.ipv6.conf.all.accept_redirects=0 > /dev/null sysctl -w net.ipv6.conf.all.autoconf=0 > /dev/null sysctl -w net.ipv6.conf.default.accept_ra=0 > /dev/null sysctl -w net.ipv6.conf.default.accept_redirects=0 > /dev/null sysctl -w net.ipv6.conf.default.autoconf=0 > /dev/null } function configure_ipv6_address() { # device needs to be online # arp flag needs to be on for ipv6 over osa because of ndisc. # happens automatically by the driver. do NOT mess with default setting. #NO#debug ip link set dev $DEVICE arp on if ! debug ip -6 address add $IPADDR/$NETMASK dev $DEVICE; then echo $"Could net set IPv6 address $IPADDR/$NETMASK for device $DEVICE" return 1 fi # network route has been set by above "ip address add" already # take care of MTU, which is bundled with ifconfig in the other IPv4 cases if [ -n "$MMTU" ]; then if ! debug ip link set $DEVICE $MMTU; then echo $"Could net set maximum transfer unit ($MMTU) for device $DEVICE" return 1 fi fi return 0 } function configure_ipv4_address() { # it's IPv4 and we can make use of ipcalc for better usability if ipcalc -bmnp $ipcalc_arg > /tmp/ipcalc.$$.out 2> /dev/null; then . /tmp/ipcalc.$$.out else echo $"Could not calculate network address and broadcast address from" echo $" IPv4 address $IPADDR and netmask $NETMASK" return 1 fi rm /tmp/ipcalc.$$.out # device needs to be online if ! debug ifconfig $DEVICE $IPADDR $MMTU netmask $NETMASK broadcast $BROADCAST; then echo $"Could not set IPv4 address $IPADDR for device $DEVICE" echo $" with network mask $NETMASK and broadcast address $BROADCAST" [ -n "$MMTU" ] && echo $" and maximum transfer unit: $MMTU" return 1 fi # This network route is already there after ifconfig! #if ! debug route add -net $NETWORK netmask $NETMASK dev $DEVICE; then # echo $"Could not add network route to $NETWORK/$NETMASK on device $DEVICE" # return 1 #fi return 0 } function handle_mtu() { # don't ask for MTU, but use it if it has been set in the .parm file # don't overwrite MMTU if it has been set for CTC [ -n "$MTU" -a -z "$MMTU" ] && MMTU="mtu $MTU" } function rollback_config() { # each transaction to roll back may fail, if previous setup has not # made progress that far to reach a certain transation # => error output is misleading and should be avoided [ -n "$DEVICE" ] && tv ip -4 route flush default dev $DEVICE [ -n "$DEVICE" ] && tv ip -6 route flush default dev $DEVICE # address flush seems to be effective for all address families [ -n "$DEVICE" ] && ip address flush dev $DEVICE if [ -n "$NETTYPE" ]; then if [ -n "$SCH_R_DEVBUSID" ]; then local sysnettype case "${NETTYPE}" in qeth|lcs) sysnettype=${NETTYPE} ;; ctcm) sysnettype=ctcm ;; esac [ -f /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online ] && \ sysecho /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online "0" udevadm settle [ -f /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/ungroup ] && \ sysecho /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/ungroup "1" udevadm settle fi fi [ -z "$mtu_was_set" ] && unset MTU [ -z "$mmtu_was_set" ] && unset MMTU [ -z "$vswitch_was_set" ] && unset VSWITCH # prevent possible reuse of an old DEVICE on restarting dialog unset DEVICE # set activated DASDs offline again local dasd while read dasd < /proc/dasd/devices; do dasd=${dasd%%(*} sysecho /sys/bus/ccw/devices/$dasd/online 0 done udevadm settle } ### workflow helper functions # workflow ideas: # - setting/applying single configuration steps right away save us explicit # syntactical & semantic checks PLUS we get direct feedback on error # - check error level of forked external programs and react on errors unset reenter unset redoitem unset interaction_happened function reenter() { [ -z "$reenter" ] && return 1 # reenter menu should only be shown if NOT redoing item if [ -n "$redoitem" ]; then # unset redoitem # wrong => do NOT do this here return 1 fi return 0 } function reenter_menu() { local oldvalue=$1 interaction_happened="yes" # unsetting input here is not sufficient, since reenter_menu # is not called for predefined parameters # which then might get assigned a previous old input of another parameter! #unset input reenter || return 0 # don't present reenter menu for empty parameters # (currently ignoring parameters that are allowed to be empty!) # this could be improved by checking if variable has been set/defined #[ -z "$1" ] && return 0 while : ; do if [ -n "$helptext" ]; then echo $"0) default is previous \"$oldvalue\", 1) new value, ?) help" else echo $"0) default is previous \"$oldvalue\", 1) new value" fi # uncoded alternative: 2) skip parameter local answer read answer [ -z "$answer" ] && return 1 case $answer in 0) return 1 ;; 1) # Deciding to enter new value gets user out of reenter-mode # temporarily for this parameter. # To put it differently: redoing does NOT present old values. redoitem="yes" echo -n $"new value: " return 0 ;; "?") input="?" return 1 ;; esac done } function workflow_item_menu() { local noredo=$1 # default is to continue if running kickstart to prevent interaction [ -n "$RUNKS" ] && return 0 interaction_happened="yes" while : ; do unset redoitem if [ "$noredo" = "noredo" ]; then echo $"1) continue, 2) restart dialog, 3) halt, 4) shell" else echo $"0) redo this parameter, 1) continue, 2) restart dialog, 3) halt, 4) shell" fi local answer read answer case $answer in 0) [ "$noredo" = "noredo" ] && continue redoitem="yes" continue 2 ;; 1) return 0 ;; # can be used to break at caller on ignore 2) reenter="yes" rollback_config continue 3 ;; 3) tv doshutdown exit 0 ;; 4) echo $"Enter 'exit' at the shell prompt to get back to the installation dialog." /bin/bash if [ "$noredo" != "noredo" ] && [ -n "$question_prefix" ]; then $question_prefix echo fi ;; # stay in workflow item menu esac done } # input variables: PARMNAME, question_prefix, question_choices, # "options" ... # output variables: $question_prefix, $helptext # modifies: the variable named $PARMNAME, $OPTIND function ask() { [ $# -lt 3 ] && echo "l.$LINENO: too few arguments (<3), please fix calling code." 1>&2 local PARMNAME=$1 shift question_prefix=$1 shift local question_choices=$1 shift local exception local syntax_check unset helptext local handle local finish local optname OPTIND=1 while getopts ":e:s:h:c:f:" optname; do case $optname in e) exception=$OPTARG ;; s) syntax_check=$OPTARG ;; h) helptext=$OPTARG ;; c) handle=$OPTARG ;; f) finish=$OPTARG ;; "?") ;; # ignore invalid option :) echo "l.$LINENO: Missing parameter to option -$OPTARG" 1>&2 ;; esac done while : ; do unset input local input # actually ask question if one of the following is true: # - $PARMNAME parameter has not been set yet, e.g. not in parm file # - on 2nd and further attempts, i.e. redoing the parameter # - on having restarted the whole dialog # describing the same from another viewpoint: # - if $PARMNAME has been set, try to check syntax and apply # - on redo, $PARMNAME has been set and reenter is false, # but still ask question again # - on reenter, $PARMNAME might have been set, but still ask question if [ -z "${!PARMNAME}" -o -n "$redoitem" -o -n "$reenter" ]; then # one empty line to separate parameter questions from each other echo $question_prefix if reenter; then echo else $question_choices fi # on reenter, give choice between old value and entering new one reenter_menu ${!PARMNAME} && read input \ && [ "$input" != "?" ] && eval ${PARMNAME}=\$input # escaping the $ in the RHS of the eval statement makes it safe fi if [ -n "$helptext" ] && [ "$input" = "?" ]; then $helptext continue fi # optional: default or exceptional handling [ -n "$exception" ] && $exception if [ -n "$syntax_check" -a -z "$handle" ]; then # some parameters have only syntax check (and deferred config): if $syntax_check; then break else workflow_item_menu && break fi elif [ -n "$syntax_check" -a -n "$handle" ]; then # most common parameters have syntax and configuration: # user might still continue on syntax error $syntax_check || workflow_item_menu # optional: actual configuration if $handle; then # parmname has been configured successfully break else # user might still continue on configuration failure workflow_item_menu && break fi elif [ -n "$finish" ]; then # few parameters need special handling done by their own function: $finish else echo $"Unsupported calling of ask function, please fix calling code" fi done # PARMNAME # disable potential temporary redoing-mode during reenter-mode unset redoitem } ### NETTYPE function syntax_check_nettype() { # - NETTYPE \in {qeth,lcs,ctc} [[ "$NETTYPE" =~ (^qeth$)|(^lcs$)|(^ctc$) ]] case $? in 0) # string matched the pattern return 0 ;; 1) # string did not match the pattern ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac echo $"Incorrect format or value for network type (NETTYPE): $NETTYPE" return 1 } function question_prefix_nettype() { echo -n $"Network type" } function question_choices_nettype() { echo $" (qeth, lcs, ctc, ? for help). Default is qeth:" } function helptext_nettype() { echo $" Help text for network type:" echo $" qeth: OSA-Express Fast Ethernet, Gigabit Ethernet (including 1000Base-T)," echo $" High Speed Token Ring, Hipersockets, and ATM (running Ethernet LAN emulation)" echo $" features in QDIO mode." echo $" [default]" echo $" lcs: OSA-2 Ethernet/Token Ring, OSA-Express Fast Ethernet in non-QDIO mode," echo $" OSA-Express High Speed Token Ring in non-QDIO mode and Gigabit Ethernet" echo $" in non-QDIO mode." echo $" ctc: Deprecated, useful for migration." } function exception_nettype() { # - default is qeth since it should be common if [ -z "$NETTYPE" ]; then NETTYPE=qeth break fi } function finish_nettype() { if syntax_check_nettype; then break else # necessary parts which would otherwise be done by workflow_item_menu interaction_happened="yes" redoitem="yes" fi } function do_nettype() { ask NETTYPE \ question_prefix_nettype question_choices_nettype \ -h helptext_nettype -e exception_nettype -f finish_nettype } ### CHANDEV function do_chandev() { echo echo $"The CHANDEV variable isn't used anymore, please update your " echo $".parm or the .conf file to use NETTYPE, SUBCHANNELS, etc. instead." echo } ### SUBCHANNELS function syntax_check_subchannels() { SUBCHANNELS=$(echo $SUBCHANNELS | tr ABCDEF abcdef) # - make subchannel question dependent on NETTYPE (2 vs. 3 subchannels) if [ "$NETTYPE" = "qeth" ]; then # - match against regex, depending on qeth [[ "$SUBCHANNELS" =~ ^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4},[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4},[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$ ]] else # - match against regex, depending on lcs/ctc [[ "$SUBCHANNELS" =~ ^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4},[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$ ]] fi case $? in 0) # string matched the pattern return 0 ;; 1) # string did not match the pattern echo $"Incorrect format for channels (SUBCHANNELS): $SUBCHANNELS" ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac return 1 } function semantic_check_subchannels() { local subch_count if [ "$NETTYPE" = "qeth" ]; then subch_count=3 else subch_count=2 fi # done: make subchannel handling more robust by not relying on REMATCH local -a subch_array IFS=, read -a subch_array <<< "indexzero,$SUBCHANNELS" unset IFS local i local all_subch_good=0 for ((i=1; i <= $subch_count; i++)); do local devbusid=${subch_array[$i]} # remember first subchannel for potential undo of ccwgroup # (via /sys/devices/qeth/$SCH_R_DEVBUSID/ungroup) [ "$i" -eq 1 ] && SCH_R_DEVBUSID=$devbusid local prefix ssid devno foo IFS=. read prefix ssid devno foo <<< "$devbusid" unset IFS local dev_p=$(echo /sys/devices/css$prefix/$IDFORMAT/$devbusid) # - check for existence of devnos in sysfs if [ ! -d "$dev_p" -a "$cio_wc_bytes" != "0" ]; then # - try to free from /proc/cio_ignore if they don't exist echo $"Device $devbusid not present, trying to clear from blacklist and resense..." if sysecho /proc/cio_ignore "free $devbusid"; then # /proc/cio_ignore won't block on freeing devices # until resensing has been completed, so wait until # the udev event queue depletes (without udevadm settle we # could wait 2 seconds unconditionally) #debug ls -laF /dev/.udev udevadm settle # even though the device might now be online, some of its # sysfs attributes (e.g. cutype) might not yet be available sleep 1 else echo $"Device $devbusid could not be cleared from device blacklist" fi fi # reevaluate since globbing might not have worked before device existed dev_p=$(echo /sys/devices/css$prefix/$IDFORMAT/$devbusid) if [ ! -d "$dev_p" ]; then echo $"Device $devbusid does not exist" all_subch_good=1 continue fi # devno does exist now local subch_p=${dev_p%/*} local subch=${subch_p##*/} # filter definitely unusable subchannels ... # - check for subchannel type I/O if [ -f $subch_p/type ]; then local type read type < $subch_p/type if [ "$type" != "$SUBCHANNEL_TYPE_IO" ]; then echo $"Channel $subch (device $devbusid) is not of type I/O" all_subch_good=1 continue fi fi # - check for correct CU type/model, depending on qeth/lcs/ctc if [ ! -f $dev_p/cutype ]; then echo $"Device $devbusid does not have required sysfs attribute 'cutype'" all_subch_good=1 continue fi local cutype read cutype < $dev_p/cutype if search_cu $cutype; then local driver if [ "$NETTYPE" = "ctc" ]; then driver="ctcm" else driver=$NETTYPE fi if [ "${CU_DEVDRV[$cu_idx]}" != "$driver" ]; then echo $"Device $devbusid has control unit type $cutype," echo $" which does not match your selected network type $NETTYPE" all_subch_good=1 continue fi else echo $"Device $devbusid has control unit type $cutype which is unknown" all_subch_good=1 continue fi # read CHPIDs information about subchannels if [ ! -f $subch_p/chpids ]; then echo $"Channel $subch (device $devbusid) does not have required sysfs attribute 'chpids'" all_subch_good=1 continue fi local chpid_list read chpid_list < $subch_p/chpids local -a chpids read -a chpids <<< "$chpid_list" if [ ${#chpids[@]} -ne 8 ]; then echo $"sysfs reported ${#chpids[@]} CHPIDs instead of expected 8, code needs fix" fi if [ ! -f $subch_p/pimpampom ]; then echo $"Channel $subch (device $devbusid) does not have required sysfs attribute 'pimpampom'" all_subch_good=1 continue fi local pim pam pom foo read pim pam pom foo < $subch_p/pimpampom local pimchpidZ="" for ((chp=0; chp < 8; chp++)); do local mask=$((0x80 >> chp)) if (( 0x$pim & $mask )); then pimchpidZ=${pimchpidZ}${chpids[chp]} else pimchpidZ=${pimchpidZ}"ZZ" fi done local pimchpids=${pimchpidZ//ZZ/} if [ "x$pimchpids" == "x" ]; then echo $"Channel $subch (device $devbusid) does not have any installed channel path" all_subch_good=1 continue fi # compare parts of different subchannels for required matches if [ "$i" -eq 1 ]; then # remember parts of first subchannel for comparison local sch_r_prefix=$prefix local sch_r_ssid=$ssid local sch_r_devno=$devno local sch_r_pimchipidZ=$pimchpidZ local sch_r_cutype=$cutype else local comparison=0 # $sch_r_... might be empty if first channel was wrong # => be sure to quote all variable accesses in test statements. # - all subchannels must be of same CU type/model if [ "$cutype" != "$sch_r_cutype" ]; then echo $"Device $devbusid does not have the same control unit type as device $SCH_R_DEVBUSID" comparison=1 fi # - all subchannels must have same CHPIDs if [ "$pimchpidZ" != "$sch_r_pimchipidZ" ]; then echo $"Device $devbusid does not have the same CHPIDs as device $SCH_R_DEVBUSID" comparison=1 fi # - all subchannels should have same prefix & ssid ? if [ "$prefix" != "$sch_r_prefix" \ -o "$ssid" != "$sch_r_ssid" ]; then echo $"Device $devbusid does not have the same prefix and subchannel set ID as device $SCH_R_DEVBUSID" comparison=1 fi if [ "$i" -eq 2 ]; then local sch_w_devbusid=$devbusid local sch_w_devno=$devno # TODO: not true for CTCM => relax # - write_devbusid == read_devbusid+1 if [ $((0x$devno)) -ne $((0x$sch_r_devno + 1)) ]; then echo $"Device bus ID of write channel (dev $devbusid) must be one larger than" echo $" that of read channel (dev $SCH_R_DEVBUSID)" comparison=1 fi elif [ "$i" -eq 3 ]; then # check data subchannel unequal to read/write subchannel # (also seems to be handled by ccwgroup kernel subsystem) if [ "$devbusid" = "$sch_w_devbusid" \ -o "$devbusid" = "$SCH_R_DEVBUSID" ]; then echo $"Device bus ID of data channel (dev $devbusid) must be different to that of" echo $" read channel ($SCH_R_DEVBUSID) and write channel ($sch_w_devbusid)" comparison=1 fi fi if [ "$comparison" != 0 ]; then all_subch_good=1 continue fi fi # filter potentially good subchannels ... if [ -h $dev_p/group_device ]; then echo $"Device $devbusid is already in a ccwgroup and thus unavailable" all_subch_good=1 continue fi if [ ! -f $dev_p/online ]; then echo $"Device $devbusid does not have required sysfs attribute 'online'" all_subch_good=1 continue fi local online read online < $dev_p/online if [ "$online" = "1" ]; then echo $"Device $devbusid is already in use and thus unavailable" all_subch_good=1 continue fi # - check availability if [ ! -f $dev_p/availability ]; then echo $"Device $devbusid does not have required sysfs attribute 'availability'" all_subch_good=1 continue fi local availability read availability < $dev_p/availability if [ "$availability" != "good" ]; then echo $"Device $devbusid is not available but '$availiability'" all_subch_good=1 continue fi done # for ((i=1; i <= $subch_count; i++)) if [ "$all_subch_good" = "0" ]; then return 0 fi return 1 } function handle_subchannels() { # - try to establish ccwgroup right here and fail out on error local driver if [ "$NETTYPE" = "ctc" ]; then driver="ctcm" else driver=$NETTYPE fi if sysecho /sys/bus/ccwgroup/drivers/${driver}/group "$SUBCHANNELS"; then udevadm settle case "$NETTYPE" in qeth) # Just preliminary card_type info until device goes online! # In fact it seems enough to separate OSA from HiperSockets. if [ -f /sys/devices/qeth/$SCH_R_DEVBUSID/card_type ]; then read cardtype < /sys/devices/qeth/$SCH_R_DEVBUSID/card_type else echo $"Could not read qeth network card type from sysfs." fi ;; ctc|lcs) if [ -f /sys/devices/$driver/$SCH_R_DEVBUSID/type ]; then local type read type < /sys/devices/$driver/$SCH_R_DEVBUSID/type [ "$type" = "CTC/A" ] && \ type="channel-to-channel adapter (CTC/A)" echo $"Detected: $type" else echo $"Could not read ctc network card type from sysfs." fi ;; esac return 0 else echo $"Channels $SUBCHANNELS could not be grouped" fi return 1 } function question_prefix_subchannels() { if [ "$NETTYPE" = "qeth" ]; then echo -n $"Read,write,data channel" else echo -n $"Read,write channel" fi } function question_choices_subchannels() { if [ "$NETTYPE" = "qeth" ]; then echo $" (e.g. 0.0.0300,0.0.0301,0.0.0302 or ? for help)." else echo $" (e.g. 0.0.0600,0.0.0601 or ? for help)" fi } function helptext_subchannels() { if [ "$NETTYPE" = "qeth" ]; then echo $" Help text for qeth channels:" echo $" Enter the device bus ID of your CCW devices." echo $" QETH needs three channels for read, write, and data," echo $" e.g. 0.0.0300,0.0.0301,0.0.0302" else echo $" Help text for lcs/ctc channels:" echo $" Enter the device bus ID of your CCW devices." echo $" CTC/ESCON and LCS need two channels for read and write," echo $" e.g. 0.0.0600,0.0.0601 will configure the CTC or ESCON interface" echo $" with the channels 0x600 and 0x601" fi } function finish_subchannels() { syntax_check_subchannels || workflow_item_menu # continuing on syntax error is doomed to fail, # since handle_subchannels relies on the regex-based strict parsing # in syntax_check_subchannels which does not match anything then # news: relaxed by splitting semantic check and actual handling semantic_check_subchannels || workflow_item_menu if handle_subchannels; then break else workflow_item_menu && break fi } function do_subchannels() { ask SUBCHANNELS \ question_prefix_subchannels question_choices_subchannels \ -h helptext_subchannels -f finish_subchannels } ### PORTNAME (qeth) function syntax_check_portname() { # - 1-8 characters, we convert it to upper case PORTNAME=$(echo $PORTNAME | tr '[:lower:]' '[:upper:]') local portname_len=${#PORTNAME} if [ "$portname_len" -ge 1 -a "$portname_len" -le 8 ]; then return 0 fi echo $"Incorrect string length [1..8] for portname (PORTNAME): $PORTNAME" return 1 } function handle_portname() { [ -n "$PORTNAME" ] || return 0 # - try to set portname right here w/ error handling if sysecho /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/portname "$PORTNAME"; then return 0 else echo $"Portname '$PORTNAME' could not be configured for $SUBCHANNELS" fi return 1 } function hint_portname() { if [ -f /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/portname ]; then local pname_hint read pname_hint < /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/portname if [ "$pname_hint" = "no portname required" ]; then echo $" * Your configuration does not require a portname. *" fi fi } function question_prefix_portname(){ echo -n $"Portname" } function question_choices_portname(){ echo $" (1..8 characters, or ? for help). Default is no portname:" } function helptext_portname(){ echo $" Help text for portname:" # updated text describing when portname is obsolete; # taken from: # SA22-7935-09, Open Systems Adapter-Express Customer's # Guide and Reference, 10th ed. May 2008, IBM, p.17f. # SC33-8411-00, Device Drivers, Features, and Commands, # 1st ed. May 2008, IBM, p.116. echo $" Portname of the OSA-Express feature in QDIO mode and z/VM Guest LAN." echo $" This parameter is optional with:" echo $" - z/VM 4.4.0 or z/VM 4.3.0 with APARs VM63308 and PQ73878" echo $" - z800, z900 with >= Driver 3G - EC stream J11204, MCL032 (OSA level 3.33)" echo $" - z890, z990, z9, z10 mainframes" hint_portname echo $" If portname is used, all operating systems sharing port must use same name." echo $" Input empty string if you don't want to enter a portname. [default]" } function exception_portname(){ [ -z "$PORTNAME" ] && break } function do_portname() { ask PORTNAME \ question_prefix_portname question_choices_portname \ -h helptext_portname \ -e exception_portname -s syntax_check_portname -c handle_portname } ### PORTNO (qeth) function syntax_check_qeth_portno() { case $PORTNO in 0|1) return 0 ;; esac echo $"Incorrect format or value for relative port number (PORTNO): $PORTNO" return 1 } function handle_qeth_portno() { if sysecho /sys/devices/qeth/$SCH_R_DEVBUSID/portno "$PORTNO"; then return 0 fi echo $"Could not configure relative port number $PORTNO for $SUBCHANNELS" return 1 } function question_prefix_portno() { echo -n $"Relative port number for OSA" } function question_choices_portno() { echo $" (0, 1, or ? for help). Default is 0:" } function helptext_portno() { echo $" Help text for relative port number for OSA with 2 ports per CHPID:" echo $" This applies to:" echo $" - OSA-Express3 Gigabit Ethernet on z10 systems" echo $" - OSA-Express ATM on zSeries 800 and 900 systems" echo $" 0 for relative port number 0 [default]" echo $" 1 for relative port number 1" echo $" Input empty string to not modify the default configuration." } function exception_portno() { # Writing portno of e.g. hipersockets device fails. # Therefore, do not configure on empty default value. [ -z "$PORTNO" ] && break } function do_portno() { ask PORTNO \ question_prefix_portno question_choices_portno \ -h helptext_portno -e exception_portno \ -s syntax_check_qeth_portno -c handle_qeth_portno } ### LAYER2 function syntax_check_layer2() { # - $LAYER2 \in {0,1} case $LAYER2 in 0|1) return 0 ;; esac echo $"Incorrect format or value for layer2 mode (LAYER2): $LAYER2" return 1 } function handle_layer2() { [ "$NETTYPE" == "qeth" ] || return 0 [ -n "$LAYER2" ] || return 0 # - try to set layer2 mode right here w/ error handling if sysecho /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/layer2 "$LAYER2"; then return 0 else echo $"Layer2 mode '$LAYER2' could not be configured for $SUBCHANNELS" fi return 1 } function question_prefix_layer2() { echo -n $"Layer mode" } function question_choices_layer2() { echo -n $" (0 for layer3, 1 for layer2, or ? for help)." if [ "$isLayer2Default" = "yes" ]; then echo $" Default is 1:" else echo $" Default is 0:" fi } function helptext_layer2() { echo $" Help text for OSA mode of operation: layer 2 vs. layer 3" if [ "$isLayer2Default" = "yes" ]; then echo $" 0 for layer 3 mode (may not work with dhcp, tcpdump, etc.)" echo $" 1 for layer 2 mode [default]" else echo $" 0 for layer 3 mode [default] (may not work with dhcp, tcpdump, etc.)" echo $" 1 for layer 2 mode" fi } function exception_layer2() { if [ -z "$LAYER2" ]; then isLayer2Default && LAYER2=1 || LAYER2=0 # do not break, always apply, default may differ from online layer mode #break fi } function do_layer2() { isLayer2Default && isLayer2Default=yes || isLayer2Default=no ask LAYER2 \ question_prefix_layer2 question_choices_layer2 \ -h helptext_layer2 -e exception_layer2 \ -s syntax_check_layer2 -c handle_layer2 } ### MACADDR function syntax_check_macaddr() { # - match against regex [[ "$MACADDR" =~ ^[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]$ ]] case $? in 0) # string matched the pattern return 0 ;; 1) # string did not match the pattern echo $"Incorrect format for mac address (MACADDR): $MACADDR" ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac return 1 } function handle_macaddr() { # - try to set macaddr right here w/ error handlg. # device needs to be online if debug ifconfig $DEVICE hw ether $MACADDR; then return 0 fi echo $"MAC address $MACADDR could not be configured for" echo $" $SUBCHANNELS (network device $DEVICE)" return 1 } function question_prefix_macaddr() { echo -n $"Unique MAC address" } function question_choices_macaddr() { macaddr_default=$(ifconfig $DEVICE | grep 'HWaddr' | sed 's/.*HWaddr \([[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]\).*/\1/') echo $" (e.g. 02:00:00:00:00:00, ? for help). Default is $macaddr_default:" } function helptext_macaddr() { echo $" Help text for MAC address:" if [ -z "${cardtype//OSD_*/}" ]; then echo $" For real OSA in layer 2 mode, a random MAC address is automatically assigned." else echo $" If connecting to a layer 2 VSWITCH, a MAC address is automatically assigned." fi echo $" You may accept the automatic MAC address with an empty input. [default]" echo $" If the automatic address is not unique, please provide a MAC address." [ -z "${cardtype//OSD_*/}" ] && \ echo $" For real OSA, the provided address must be different from that of the OSA." echo $" You may override the automatic MAC address with non-empty input." echo $" An example MAC address would be: 02:00:00:00:00:00" } function exception_macaddr() { if [ -z "$MACADDR" ]; then if [ -z "${cardtype//OSD_*/}" ]; then # keep random default MAC address of real OSA, # so the OSA comes up with the same MAC each time in the future MACADDR=$macaddr_default else # virtual OSA in layer2 is GuestLAN or VSWITCH VSWITCH=1 fi break fi } function do_macaddr() { ask MACADDR \ question_prefix_macaddr question_choices_macaddr \ -h helptext_macaddr -e exception_macaddr \ -s syntax_check_macaddr -c handle_macaddr } ### CTCPROT function syntax_check_ctcprot() { case "x$CTCPROT" in x|x0) unset CTCPROT return 0 ;; x1|x3) return 0 ;; x2) echo $"CTC tty's are not usable for this installation (CTCPROT)" ;; *) echo $"Incorrect format or value for CTC protocol (CTCPROT): $CTCPROT" ;; esac return 1 } function handle_ctcprot() { [ -n "$CTCPROT" ] || return 0 if sysecho /sys/devices/ctcm/${SCH_R_DEVBUSID}/protocol "$CTCPROT"; then return 0 fi echo $"Could not configure CTC protocol $CTCPROT for $SUBCHANNELS" return 1 } function question_prefix_ctcprot() { echo -n $"CTC protocol" } function question_choices_ctcprot() { echo $" (0, 1, 3, or ? for help). Default is 0:" } function helptext_ctcprot() { echo $" Help text for CTC protocol:" echo $" Protocol which should be used for the CTC interface" echo $" 0 for compatibility with p.e. VM TCP service machine [default]" echo $" 1 for enhanced package checking for Linux peers" echo $" 3 for compatibility with OS/390 or z/OS peers" } function do_ctcprot() { ask CTCPROT \ question_prefix_ctcprot question_choices_ctcprot \ -h helptext_ctcprot -s syntax_check_ctcprot -c handle_ctcprot } ### PORTNAME (LCS portno) function syntax_check_lcs_portno() { [[ "$PORTNAME" =~ ^[[:digit:]]+$ ]] case $? in 0) # string matched the pattern return 0 ;; 1) # string did not match the pattern ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac echo $"Incorrect format for LCS port number (PORTNAME): $PORTNAME" return 1 } function handle_lcs_portno() { [ -n "$PORTNAME" ] || return 0 if sysecho /sys/devices/lcs/$SCH_R_DEVBUSID/portno "$PORTNAME"; then return 0 fi echo $"Could not configure relative port number $PORTNAME for $SUBCHANNELS" return 1 } function question_prefix_lcs_portno() { echo -n $"Relative port number of your LCS device" } function question_choices_lcs_portno() { echo $" (number or ? for help). Default is 0:" } function helptext_lcs_portno() { echo $" Help text for relative port number of LCS device:" echo $" Required for OSA-Express ATM cards only." } function exception_lcs_portno() { [ -z "$PORTNAME" ] && break } function do_lcs_portno() { # LCS portno and QETH portname share the parameter variable PORTNAME. # For compatibility with existing parm files we keep this scheme. ask PORTNAME \ question_prefix_lcs_portno question_choices_lcs_portno \ -e exception_lcs_portno \ -h helptext_lcs_portno -s syntax_check_lcs_portno -c handle_lcs_portno } ### HOSTNAME function syntax_check_hostname() { syntax_check_domainname "$HOSTNAME" "Incorrect format for hostname (HOSTNAME): $HOSTNAME" } function handle_hostname() { if ! hostname $HOSTNAME; then echo $"Could not configure hostname $HOSTNAME" return 1 fi return 0 } function question_prefix_hostname() { echo -n $"Hostname of your new Linux guest" } function question_choices_hostname() { echo $" (FQDN e.g. s390.redhat.com or ? for help):" } function helptext_hostname() { echo $" Help text for hostname:" echo $" Enter the full qualified domain name of your host." } function do_hostname() { ask HOSTNAME \ question_prefix_hostname question_choices_hostname \ -h helptext_hostname -s syntax_check_hostname -c handle_hostname } ### IPADDR function syntax_check_ipaddr() { unset ipv4 unset ipv6 if checkipv4 $IPADDR; then ipv4="yes" return 0 elif [ "$ipv6_capable" = "yes" ] && checkipv6 $IPADDR; then ipv6="yes" return 0 fi echo $"Incorrect format for IP address (IPADDR): $IPADDR" return 1 } function question_prefix_ipaddr() { echo -n $"IPv4 address" [ "$ipv6_capable" = "yes" ] && echo -n $" / IPv6 addr." } function question_choices_ipaddr() { echo -n $" (e.g. 10.0.0.2" [ "$ipv6_capable" = "yes" ] && echo -n $" / 2001:0DB8::" echo $" or ? for help)" } function helptext_ipaddr() { echo $" Help text for IP address:" echo $" Enter a valid IPv4 address of your new Linux guest (e.g. 10.0.0.2)" if [ "$ipv6_capable" = "yes" ]; then echo $" or alternatively a valid IPv6 address without CIDR prefix (e.g. 2001:0DB8::)" echo $" IPv6 is supported on:" echo $" - Ethernet interfaces of the OSA-Express adapter running in QDIO mode." echo $" - HiperSockets interfaces" echo $" - z/VM guest LAN interfaces running in QDIO mode." echo $" IPv6 is not supported on HiperSockets guest LAN, OSA-Express Token Ring, ATM." fi } function do_ipaddr() { ipv6_capable && ipv6_capable=yes || ipv6_capable=no ask IPADDR \ question_prefix_ipaddr question_choices_ipaddr \ -h helptext_ipaddr -s syntax_check_ipaddr if [ "$ipv6" ]; then # qeth_l3 would load ipv6 automatically but not qeth_l2 modprobe ipv6 tv disable_ipv6_autoconf fi # no handling/configuring of IPADDR yet, since more parameters needed } ### NETMASK (IPv4) function syntax_check_netmask_v4() { # also support CIDR prefix if [[ "$NETMASK" =~ ^[[:digit:]]+$ ]]; then if [ "$NETMASK" -ge 1 -a "$NETMASK" -le 32 ]; then ipcalc_arg="$IPADDR/$NETMASK" return 0 fi echo $"Incorrect value for network prefix [1..32] (NETMASK): $NETMASK" return 1 elif checkipv4 $NETMASK; then ipcalc_arg="$IPADDR $NETMASK" return 0 fi echo $"Incorrect format or value for network mask (NETMASK): $NETMASK" return 1 } function question_prefix_netmask() { echo -n $"IPv4 netmask or CIDR prefix" } function hint_netmask_v4() { # default based on class a/b/c address local a b c d IFS=. read a b c d <<< "$IPADDR" unset IFS local ip=$(( ( a << 24 ) + ( b << 16 ) + ( c << 8 ) + ( d ) )) # <& /dev/null [ -z "$GATEWAY" ] && return 0 if ! tv route add default gw $GATEWAY dev $DEVICE; then echo $"Could net set default route on device $DEVICE via gateway $GATEWAY" return 1 fi # BH FIXME: Workaround for manual MACADDR, need ping to update arp table echo $"Trying to reach gateway $GATEWAY..." if [ "$NETTYPE" = "ctc" ]; then # (virtual) CTC(/A) seems to need some time to get functional local i=1 while : ; do ping -c 1 -w 5 $GATEWAY >& /dev/null && break i=$((i+1)) if [ "$i" -gt 3 ]; then echo $"Could not reach gateway $GATEWAY within timeout" return 1 fi done else if ! ping -c 1 -w 5 $GATEWAY >& /dev/null; then echo $"Could not reach your default gateway $GATEWAY" return 1 fi fi return 0 } function hint_ipv4_gateway() { # - provide default suggestion based on network, # for a class C network this would be either .1 or .254 at the end local a b c d IFS=. read a b c d <<< "$NETWORK" unset IFS local ip=$(( ( a << 24 ) + ( b << 16 ) + ( c << 8 ) + ( d ) )) # <> 24 )) local lo_b=$(( (lo & 0x00FF0000) >> 16 )) local lo_c=$(( (lo & 0x0000FF00) >> 8 )) local lo_d=$(( (lo & 0x000000FF) )) local hi=$(( ip | ( (2**(32 - PREFIX)) - 1 ) )) local hi_a=$(( (hi & 0xFF000000) >> 24 )) local hi_b=$(( (hi & 0x00FF0000) >> 16 )) local hi_c=$(( (hi & 0x0000FF00) >> 8 )) local hi_d=$(( (hi & 0x000000FE) )) echo $" Depending on your network design patterns, the default gateway" echo $" might be $lo_a.$lo_b.$lo_c.$lo_d or $hi_a.$hi_b.$hi_c.$hi_d" } function question_prefix_gateway() { echo -n $"IPv4 address of your default gateway" } function question_choices_gateway() { echo $" or ? for help:" } function helptext_gateway() { echo $" Help text for IPv4 default gateway:" echo $" For HiperSockets with internal traffic only you may want to leave this empty" echo $" and choose continue afterwards to go on without gateway." hint_ipv4_gateway } function finish_gateway() { if ! checkipv4 $GATEWAY; then # above checkipv4 is silent, so make up for syntax error echo $"Incorrect format for IPv4 address of gateway (GATEWAY): $GATEWAY" workflow_item_menu fi if configure_ipv4_gateway; then break else workflow_item_menu && break fi } # FIXME: allow empty/no gateway? function do_gateway() { ask GATEWAY \ question_prefix_gateway question_choices_gateway \ -h helptext_gateway -f finish_gateway } ### GATEWAY (IPv6) function configure_ipv6_gateway() { # FIXME: # - Strictly speaking we should first check reachability of gateway # and then configure the gateway route. # This would require a new intermediate workflow_item step # so that the user might continue despite unreachable gateway. # done: Only adding default route might add multiple undesired default # routes on redoing the parameter item, so delete default route # before adding a new one. ip -6 route del default dev $DEVICE >& /dev/null [ -z "$GATEWAY" ] && return 0 # IPv6 http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html_single/Linux+IPv6-HOWTO.html#AEN1147 # ip -6 route add ::/0 dev $DEVICE via $GATEWAY # (Could also be learned by autoconfiguration on the link: # after IP address setup and device up, # see if default route has been learned # ip -6 route show | grep ^default # However, we currently use manual IPv6 configuration only.) if ! debug ip -6 route add ::/0 dev $DEVICE via $GATEWAY; then echo $"Could net set default route on device $DEVICE" echo $" via gateway $GATEWAY" return 1 fi # BH FIXME: Workaround for manual MACADDR, need ping to update arp table echo $"Trying to reach gateway $GATEWAY..." if ! ping6 -c 1 $GATEWAY >& /dev/null; then echo $"Could not reach your default gateway $GATEWAY" return 1 fi return 0 } function question_prefix_gateway_v6() { echo -n $"IPv6 address of your default gateway" } function question_choices_gateway_v6() { echo $":" } function helptext_gateway_v6() { echo $" Help text for IPv6 default gateway:" echo $" For HiperSockets with internal traffic only you may want to leave this empty" echo $" and choose continue afterwards to go on without gateway." } function finish_gateway_v6() { if ! checkipv6 $GATEWAY; then # above checkipv6 is silent, so make up for syntax error echo $"Incorrect format for IPv6 address of gateway (GATEWAY): $GATEWAY" workflow_item_menu fi if configure_ipv6_gateway; then break else workflow_item_menu && break fi } # FIXME: allow empty/no gateway? function do_gateway_v6() { ask GATEWAY \ question_prefix_gateway_v6 question_choices_gateway_v6 \ -h helptext_gateway_v6 -f finish_gateway_v6 } ### GATEWAY (IPv4, point-to-point) function configure_ipv4_ptp() { # device needs to be online if debug ifconfig $DEVICE $IPADDR $MMTU pointopoint $GATEWAY; then configure_ipv4_gateway return $? fi echo $"Could not set IPv4 address $IPADDR for device $DEVICE" echo $" to peer $GATEWAY" [ -n "$MMTU" ] && echo $" and maximum transfer unit: $MMTU" return 1 } function question_prefix_ptp_gateway() { echo -n $"IPv4 address of your point-to-point partner" } function question_choices_ptp_gateway() { echo $" or ? for help:" # no hinting possible here } function helptext_ptp_gateway() { echo $" Help text for point-to-point partner:" echo $" IPv4 address of your CTC or ESCON point-to-point partner." } function finish_ptp_gateway() { if checkipv4 $GATEWAY; then if [ "$GATEWAY" = "$IPADDR" ]; then echo $"IPv4 address of partner should probably be different from the guest's address" workflow_item_menu && break else break fi else # above checkipv4 is silent, so make up for syntax error echo $"Incorrect format for IPv4 address of partner (GATEWAY): $GATEWAY" workflow_item_menu && break fi # too early to actually configure gateway } function do_ptp_gateway() { ask GATEWAY \ question_prefix_ptp_gateway question_choices_ptp_gateway \ -h helptext_ptp_gateway -f finish_ptp_gateway } ### DNS function syntax_check_dns() { if [ -z "$DNS" ]; then echo $"You might encounter problems without a nameserver, especially with FTP installs" return 1 fi local dnsitem local allgood="yes" if [ "$ipv6" ]; then while read dnsitem; do if ! checkipv6 $dnsitem; then echo $"Not a valid IPv6 address for DNS server: $dnsitem" allgood="no" fi done < <(echo $DNS | sed 's/,/\n/g') else while read dnsitem; do if ! checkipv4 $dnsitem; then echo $"Not a valid IPv4 address for DNS server: $dnsitem" allgood="no" fi done < <(echo $DNS | sed 's/:/\n/g') fi if [ "$allgood" = "yes" ]; then return 0 else return 1 fi } function handle_dns() { # - foreach DNS try if server is reachable by one ping [ -z "$DNS" ] && return 0 local dnsitem local allgood="yes" echo $"Trying to reach DNS servers..." if [ "$ipv6" ]; then while read dnsitem; do if ! ping6 -c 1 $dnsitem >& /dev/null; then echo $"Could not ping DNS server (might still serve DNS requests): $dnsitem" allgood="no" # this should not be a hard failure since some network # environments may prevent pings to DNS servers # => prevent workflow_item_menu in kickstart mode fi done < <(echo $DNS | sed 's/,/\n/g') else while read dnsitem; do # Some network environment may prevent a DNS server from being # reachable by ping, so it would make sense to use nslookup. # However, nslookup fails with "Resolver Error 0 (no error)" # at this stage of the setup progress => not useful if ! ping -c 1 -w 5 $dnsitem >& /dev/null; then echo $"Could not ping DNS server: $dnsitem" # if nslookup $dnsitem $dnsitem >& /dev/null; then # echo $" but could resolve DNS server with itself: $dnsitem" # else # echo $"Could not resolve DNS server with itself: $dnsitem" # allgood="no" # fi # elif ! nslookup $dnsitem $dnsitem >& /dev/null; then # echo $"Could not resolve DNS server with itself: $dnsitem" allgood="no" fi done < <(echo $DNS | sed 's/:/\n/g') fi if [ "$allgood" = "yes" ]; then return 0 else return 1 fi } function question_prefix_dns() { if [ "$ipv6" ]; then echo -n $"IPv6 addresses of DNS servers" else echo -n $"IPv4 addresses of DNS servers" fi } function question_choices_dns() { if [ "$ipv6" ]; then echo $" (separated by commas ',' or ? for help):" else echo $" (separated by colons ':' or ? for help):" fi } function helptext_dns() { echo $" Help text for DNS servers:" if [ "$ipv6" ]; then echo $" Enter IPv6 addresses of DNS servers separated by commas ','" else echo $" Enter IPv4 addresses of DNS servers separated by colons ':'" fi echo $" Default are no DNS servers at all." echo $" However, you might encounter problems without a nameserver," echo $" especially with FTP installs." if [ "$ipv6" ]; then echo $" An example with 2 servers would be: 2001:0DB8::42,2001:0DB8::BE:AF" else echo $" An example with 2 servers would be: 10.0.0.250:10.1.1.1" fi } function do_dns() { ask DNS \ question_prefix_dns question_choices_dns \ -h helptext_dns -s syntax_check_dns -c handle_dns } ### SEARCHDNS function syntax_check_searchdns() { [ -z "$SEARCHDNS" ] && return 0 local dnsitem local allgood="yes" while read dnsitem; do syntax_check_domainname "$dnsitem" $"Not a valid DNS search domain: $dnsitem" || allgood="no" done < <(echo $SEARCHDNS | sed 's/:/\n/g') if [ "$allgood" = "yes" ]; then return 0 else return 1 fi } function question_prefix_searchdns() { echo -n $"DNS search domains" } function question_choices_searchdns() { echo $" (separated by colons ':' or ? for help):" } function helptext_searchdns() { echo $" Help text for DNS search domains:" echo $" Enter search domains according to hostname syntax separated by colons." echo $" Default are no DNS search domains at all." echo $" An example would be: subdomain.domain.com:domain.com" } function do_searchdns() { ask SEARCHDNS \ question_prefix_searchdns question_choices_searchdns \ -h helptext_searchdns -s syntax_check_searchdns } ### DASD function parse_dasd() { local handle [ "$1" = "-h" ] && handle=yes || unset handle local dasditem local allgood="yes" while read dasditem; do unset range features range lo hi rangegood \ attrs devno lodevno hidevno devbusid sys case $dasditem in autodetect) [ -z "$handle" ] && continue local cio_wc=$(wc -c /proc/cio_ignore) read cio_wc_bytes cio_wc_filename cio_wc_foo <<< "$cio_wc" if [ "$cio_wc_bytes" != "0" ]; then echo $"Note: There is a device blacklist active! Only activating visible DASDs." fi local sys while read sys; do if ! sysecho $sys/online 1; then echo $"Could not set DASD ${sys##*/} online" fi done < <(find /sys/bus/ccw/drivers/dasd-eckd/ -name "*.?.????" 2>/dev/null;\ find /sys/bus/ccw/drivers/dasd-fba/ -name "*.?.????" 2>/dev/null) ;; probeonly|nopav|nofcx) if [ -z "$handle" ]; then echo $"DASD option $dasditem not supported by installer" fi ;; "") continue ;; # empty range *) local range features rangegood="yes" IFS='(' read range features <<< "$dasditem" unset IFS # parse: dev OR dev'-'dev local lo=${range%%-*} [[ "$lo" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]] case $? in 0) # string matched the pattern lo=$(canonicalize_devno $lo) ;; 1) # string did not match the pattern rangegood="no" if [ -z "$handle" ]; then echo $"Incorrect format for lower bound of DASD range $range: $lo" allgood="no" fi ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac if [ "${range//*-*/}" = "" ]; then local hi=${range##*-} [[ "$hi" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]] case $? in 0) # string matched the pattern hi=$(canonicalize_devno $hi) if [ "${lo%.*}" != "${hi%.*}" ]; then echo $"Prefixes of DASD range $range do not match: ${lo%.*} != ${hi%.*}" rangegood="no" allgood="no" fi ;; 1) # string did not match the pattern rangegood="no" if [ -z "$handle" ]; then echo $"Incorrect format for upper bound of DASD range $range: $hi" allgood="no" fi ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac fi if [ "$rangegood" = "yes" -a "$handle" = "yes" ]; then if ! sysecho /proc/cio_ignore "free $range"; then echo $"Could not free DASD device $range from device blacklist" allgood="no" else udevadm settle fi fi if [ "${features//*)/}" != "" ]; then if [ -z "$handle" ]; then echo $"Missing closing parenthesis at features of DASD range $range: ($features" allgood="no" fi fi local attrs="" if [ -n "$features" ]; then features="${features%)}" while read feature; do case $feature in ro) attrs=$attrs" readonly" ;; diag) attrs=$attrs" use_diag" ;; erplog|failfast) attrs=$attrs" "$feature ;; *) if [ -z "$handle" ]; then echo $"Unknown DASD feature for device range $range: $feature" allgood="no" fi ;; esac done < <(echo $features | sed 's/:/\n/g') fi [ "$rangegood" = "yes" ] || continue [ "$handle" = "yes" ] || continue # now apply $attrs and set DASDs $lo to $hi online [ -z "$hi" ] && hi=$lo local devno lodevno=$((0x${lo##*.})) hidevno=$((0x${hi##*.})) for ((devno=$lodevno; $devno <= $hidevno; ++devno)); do local devbusid=$(printf "%s.%04x" ${lo%.*} $devno) local sys="/sys/bus/ccw/devices/"$devbusid for attr in $attrs; do if [ "$attr" = "use_diag" ]; then # diag discipline cannot be auto-loaded modprobe dasd_diag_mod fi if [ ! -f $sys/$attr ]; then echo $"DASD $devbusid does not provide attribute $attr" continue fi if ! sysecho $sys/$attr 1; then echo $"Could not set attribute $attr for DASD $devbusid" fi done if [ ! -f $sys/online ]; then echo $"DASD $devbusid not found" continue fi if ! sysecho $sys/online 1; then echo $"Could not set DASD $devbusid online" fi done ;; esac done < <(echo $DASD | sed 's/,/\n/g') if [ "$handle" = "yes" ]; then udevadm settle dasd_settle_all || return 1 echo $"Activated DASDs:" cat /proc/dasd/devices | sed -e 's/ at ([^)]*) is//' -e 's/ at/,/' fi if [ "$allgood" = "yes" ]; then return 0 else return 1 fi } function syntax_check_dasd() { parse_dasd return $? } function handle_dasd() { parse_dasd -h return $? } function question_prefix_dasd() { echo -n $"DASD range" } function question_choices_dasd() { echo $" (e.g. 200-203,205 or ? for help). Default is autoprobing:" } function helptext_dasd() { echo $" Help text for DASD range:" echo $" Comma separated list of ranges of device bus IDs." echo $" Default is autoprobing (not recommended)." echo $" Examples would be: 200-203 or 200,201,202,203 or 0.0.0200-0.0.0203,0.0.0205" } function exception_dasd() { [ -z "$DASD" ] && DASD="autodetect" } function do_dasd() { ask DASD \ question_prefix_dasd question_choices_dasd \ -h helptext_dasd -e exception_dasd -s syntax_check_dasd -c handle_dasd } ### FCP function syntax_check_fcp() { local allgood="yes" local i for i in ${!FCP_*}; do local -a fcp local devno wwpn lun read -a fcp <<< "${!i}" case ${#fcp[@]} in 3) devno=${fcp[0]} wwpn=${fcp[1]} lun=${fcp[2]} ;; 5) devno=${fcp[0]} wwpn=${fcp[2]} lun=${fcp[4]} echo $"Deprecated number of FCP arguments (5 instead of 3): " echo $" $i=\"${!i}\"" echo $" should instead be: " echo $" $i=\"$devno $wwpn $lun\"" ;; *) echo $"Unsupported number of FCP arguments (${#fcp[@]} instead of 3) in:" echo $" $i=\"${!i}\"" allgood="no" continue ;; esac [[ "$devno" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]] case $? in 0) ;; # string matched the pattern 1) # string did not match the pattern echo $"Incorrect format for FCP device $devno in:" echo $" $i=\"${!i}\"" allgood="no" ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac # zfcp.py:class ZFCPDevice would also accept WWPN without leading 0x [[ "$wwpn" =~ ^0x[[:xdigit:]]{16}$ ]] case $? in 0) ;; # string matched the pattern 1) # string did not match the pattern echo $"Incorrect format for FCP WWPN $wwpn in:" echo $" $i=\"${!i}\"" allgood="no" ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac # zfcp.py:class ZFCPDevice would also accept LUN without leading 0x # zfcp.py:class ZFCPDevice would also accept 16 bit LUN and pads with 0 [[ "$lun" =~ ^0x[[:xdigit:]]{8}0{8}$ ]] case $? in 0) ;; # string matched the pattern 1) # string did not match the pattern echo $"Incorrect format for FCP LUN $lun in:" echo $" $i=\"${!i}\"" allgood="no" ;; 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;; *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;; esac done if [ "$allgood" = "yes" ]; then return 0 else return 1 fi } ### function show_parms() { # The only issue with this stateless approach to showing parameters based # on their content being non-empty is, that parameters with defaults # such as LAYER2, (PORTNAME,) CTCPROT, PORTNO (,MACADDR) won't be shown # if the user just hit enter, so the parm file would be "incomplete". # However this is not easy to fix in here, since it would require the # inter-parameter dependenies coded below in the main part, e.g. an # empty LAYER2 should only be printed with default value if $NETTYPE=qeth. # For the time being, at least the parameters LAYER2, PORTNAME, and CTCPROT # only get asked on being empty if not running in kickstart mode. cat << EOF NETTYPE=$NETTYPE IPADDR=$IPADDR NETMASK=$NETMASK GATEWAY=$GATEWAY HOSTNAME=$HOSTNAME EOF [ "$SUBCHANNELS" ] && echo "SUBCHANNELS=$SUBCHANNELS" [ "$LAYER2" ] && echo "LAYER2=$LAYER2" [ "$VSWITCH" ] && echo "VSWITCH=$VSWITCH" [ "$MACADDR" ] && echo "MACADDR=$MACADDR" [ "$PORTNAME" ] && echo "PORTNAME=$PORTNAME" [ "$PORTNO" ] && echo "PORTNO=$PORTNO" [ "$PEERID" ] && echo "PEERID=$PEERID" [ "$CTCPROT" ] && echo "CTCPROT=$CTCPROT" if [ -n "$mmtu_was_set" ]; then echo "MMTU=\"$MMTU\"" elif [ -n "$mtu_was_set" ]; then echo "MTU=$MTU" fi [ "$DNS" ] && echo "DNS=$DNS" [ "$SEARCHDNS" ] && echo "SEARCHDNS=$SEARCHDNS" [ "$DASD" ] && echo "DASD=$DASD" } function final_check() { # final check && break if [ -z "$interaction_happened" ]; then # if parm file was good enough just continue without interaction break return 0 fi while : ; do # optionally consider "continue" as default # but then again the user may inadvertently continue echo echo $"c) continue, p) parm file/configuration, n) network state, r) restart, s) shell" local answer read answer case $answer in c) return 0 ;; p) echo show_parms ;; n) # show interfaces and routing table ifconfig -a if [ "$ipv6" ]; then #route -n -A inet6 # the following produces more compact output for 80 columns ip -6 route show | grep -v "^unreachable " else route -n fi ;; d) # show active DASDs with some useful details echo $"Activated DASDs:" cat /proc/dasd/devices|sed -e 's/ at ([^)]*) is//' -e 's/ at/,/' ;; r) break ;; s) echo $"Enter 'exit' at the shell prompt to get back to the installation dialog." /bin/bash ;; esac done return 1 } ### MAIN ### init_main udev_setup # Parse configuration if [ -n "$CMSDASD" -a -n "$CMSCONFFILE" ]; then readcmsfile $CMSDASD $CMSCONFFILE source /tmp/$CMSCONFFILE #2>/dev/null fi if [ -r /sys/firmware/ipl/ipl_type ]; then #local ipl_type read ipl_type < /sys/firmware/ipl/ipl_type if [ "$ipl_type" = "fcp" ]; then while : ; do echo $"Your IPL device is set to FCP." echo $"Would you like to perform a CD-ROM/DVD-ROM installation? (y/n)" #local do_cd_install read do_cd_install case $do_cd_install in y|Y|[Yy][Ee][Ss]) # precondition: zfcp driver incl. dependencies loaded #local CD_DEVICE WWPN LUN read CD_DEVICE < /sys/firmware/ipl/device read WWPN < /sys/firmware/ipl/wwpn read LUN < /sys/firmware/ipl/lun if sysecho /proc/cio_ignore "free $CD_DEVICE"; then udevadm settle # even though device might now be online, some of its # sysfs attributes might not yet be available sleep 1 else echo $"Device $CD_DEVICE could not be cleared from device blacklist" fi sysecho /sys/bus/ccw/drivers/zfcp/$CD_DEVICE/online 1 \ || echo $"Could not set FCP device $CD_DEVICE online" udevadm settle # port (WWPN) appears automatically sysecho /sys/bus/ccw/drivers/zfcp/$CD_DEVICE/$WWPN/unit_add $LUN \ || echo $"Could not add LUN $LUN at WWPN $WWPN on FCP device $CD_DEVICE" udevadm settle break ;; n|N|[Nn][Oo]) break ;; *) echo echo $"*** INVALID ANSWER: $do_cd_install" echo unset do_cd_install ;; esac done fi fi # Perform a network installation [ -n "$MTU" ] && mtu_was_set=$MTU [ -n "$MMTU" ] && mmtu_was_set=$MMTU [ -n "$VSWITCH" ] && vswitch_was_set=$VSWITCH [ -n "$CHANDEV" ] && do_chandev [ -n "$NETWORK" ] && do_network [ -n "$BROADCAST" ] && do_broadcast # [ -z "${cardtype//OSD_*/}" ] can be used to check for real OSA # Check for missing parameters, prompt for them if necessary while : ; do # do not show list of possible network device configurations, if: # - running unattended install with kickstart # - relevant parameters have already been specified in parm file # (a possible optimization would be matching those parms to table entry) # - dialog has not been restarted [ -n "$reenter" \ -o -z "$RUNKS" -a \( -z "$NETTYPE" -o -z "$SUBCHANNELS" \) ] && \ dialog_network_table if isVM; then echo $"* NOTE: To enter default or empty values press enter twice. *" fi do_nettype # precondition: driver (qeth/lcs/ctcm) loaded incl. dependencies do_subchannels if [ "$NETTYPE" = "qeth" ]; then [ -z "$reenter" -a -n "$RUNKS" -a -z "$PORTNAME" ] || \ [ -n "${cardtype//OSD_*/}" ] || do_portname # See also https://bugzilla.redhat.com/show_bug.cgi?id=439461 # # If running in kickstart mode (unattended), we assume no # interaction and the user won't get asked for PORTNO. # Otherwise the user will be asked for PORTNO. # If the user specified PORTNO in parm/conf file, PORTNO gets # respected (or the user will be asked if it was wrong). if [ -f /sys/devices/qeth/$SCH_R_DEVBUSID/portno ]; then # driver support exists since RHEL5.2 [ -z "$reenter" -a -n "$RUNKS" -a -z "$PORTNO" ] || \ [ -n "${cardtype//OSD_*/}" ] || do_portno fi do_layer2 # set device online to know the device name # and to know if it's OSD/HiperSockets/GuestLAN BUT do not # try to ifconfig the device up since that requires # setting the mac address before (if applicable). set_device_online || workflow_item_menu noredo # MAC address handling is not part of # https://bugzilla.redhat.com/show_bug.cgi?id=233376 # Instead the additions come from # https://bugzilla.redhat.com/show_bug.cgi?id=248049 # The new parms VSWITCH and MACADDR are described in # the RHEL 5.1 release notes. if isLayer2; then if [ -z "$VSWITCH" -o "$VSWITCH" == 0 ]; then do_macaddr fi fi elif [ "$NETTYPE" = "ctc" ]; then [ -z "$reenter" -a -n "$RUNKS" -a -z "$CTCPROT" ] || do_ctcprot set_device_online || workflow_item_menu noredo elif [ "$NETTYPE" = "lcs" ]; then [ -n "$RUNKS" -a -z "$PORTNAME" ] && PORTNAME=0 do_lcs_portno set_device_online || workflow_item_menu noredo fi # device needs to be up before configuring with ifconfig/ip in # configure_ipv6_address/configure_ipv4_address/configure_ipv4_address set_device_up || workflow_item_menu noredo [ "$HOSTNAME" = "(none)" ] && unset HOSTNAME do_hostname # Note: The workflow_item_menu does a rollback_config on restart # dialog, i.e. hardware config has been reset and it is impossible to # only restart halfway at IPADDR. do_ipaddr if [ "$ipv6" ]; then # this branch is all IPv6 and at the same time also NETTYPE==qeth do_netmask_v6 handle_mtu configure_ipv6_address || workflow_item_menu noredo do_gateway_v6 else # Consider IPv4 as default, even for unknown IP versions # due to invalid input for IPADDR ignored by the user previously # (neither ipv6 nor ipv4 is set). # Otherwise we would skip things like NETMASK or GATEWAY # and jump forward to DNS which is probably not what we want. if [ "$NETTYPE" = "qeth" ] || [ "$NETTYPE" = "lcs" ]; then do_netmask handle_mtu configure_ipv4_address || workflow_item_menu noredo do_gateway else # ctc0 if [ -z "$NETMASK" ]; then # If the user did not supply netmask, we add the right one. # Netmask MUST be present, # or pumpSetupInterface() blows routes. NETMASK="255.255.255.255" fi # don't ask for MTU, but use it if set in the parm file # don't overwrite MMTU if it has been set for CTC [ "$NETTYPE" = "ctc" -a -z "$MTU" -a -z "$MMTU" ] && \ MMTU="mtu 1500" do_ptp_gateway configure_ipv4_ptp || workflow_item_menu noredo fi fi modprobe_alias do_dns [ -n "$DNS" ] && do_searchdns do_dasd echo $"Initial configuration completed." final_check && break rollback_config reenter="yes" done # outer dialog loop if [ -z "$testing" ]; then # convert to space-separated lists if [ -n "$SEARCHDNS" ]; then SEARCHDNS=$(echo $SEARCHDNS |sed -e 's/:/ /g') for i in "$SEARCHDNS"; do echo "search $i"; done >> /etc/resolv.conf fi if [ -n "$DNS" ]; then if [ "$ipv6" ]; then RESOLVDNS=$(echo $DNS |sed -e 's/,/ /g') else RESOLVDNS=$(echo $DNS |sed -e 's/:/ /g') fi for i in $RESOLVDNS; do echo "nameserver $i"; done >> /etc/resolv.conf fi # make sure we have an /etc/hosts file (originally required for telnetd) if [ ! -z "$HOSTNAME" -a ! -z "$IPADDR" ]; then echo -e "$IPADDR\t$HOSTNAME $(echo $HOSTNAME | cut -d '.' -f 1)" >> /etc/hosts fi fi # testing if [ -n "$DASD" ]; then echo "DASD=$DASD" > /tmp/dasd_ports fi # syntax check to give user early feedback on parameters provided in parm file # (he probably won't notice the logs written by anaconda later on) syntax_check_fcp # currently we ignore failed syntax checks since FCP parms are non-interactive for i in ${!FCP_*}; do echo "${!i}" >> /tmp/fcpconfig done # cio_ignore handling for FCP should happen when the content of /tmp/fcpconfig # will actually be processed which is in anaconda's zfcp.py ZFCP::readConfig() # TODO/FIXME: also need to pass IPv6 decision to loader/anaconda # [ "$ipv6" ] && echo "IPV6=yes" # transfer options into install environment cat > /tmp/install.cfg << EOF LANG="$LANG" S390ARCH="$S390ARCH" TEXTDOMAIN="$TEXTDOMAIN" TEXTDOMAINDIR="$TEXTDOMAINDIR" PORTNAME="$PORTNAME" HOSTNAME="$HOSTNAME" DEVICE="$DEVICE" NETTYPE="$NETTYPE" IPADDR="$IPADDR" GATEWAY="$GATEWAY" MTU="$MTU" NETWORK="$NETWORK" NETMASK="$NETMASK" BROADCAST="$BROADCAST" SEARCHDNS="$SEARCHDNS" PEERID="$PEERID" SUBCHANNELS="$SUBCHANNELS" ONBOOT="yes" CTCPROT="$CTCPROT" EOF if [ "$ipv6" ]; then DNS1=$(echo $DNS | cut -d ',' -f 1) echo DNS=\"$DNS1\" >> /tmp/install.cfg echo DNS1=\"$DNS1\" >> /tmp/install.cfg echo DNS2=\"$(echo $DNS | cut -d ',' -f 2)\" >> /tmp/install.cfg else DNS1=$(echo $DNS | cut -d ':' -f 1) echo DNS=\"$DNS1\" >> /tmp/install.cfg echo DNS1=\"$DNS1\" >> /tmp/install.cfg echo DNS2=\"$(echo $DNS | cut -d ':' -f 2)\" >> /tmp/install.cfg fi cat >> /tmp/install.cfg << EOF export LANG PORTNAME S390ARCH TEXTDOMAIN TEXTDOMAINDIR export HOSTNAME DEVICE NETTYPE IPADDR GATEWAY MTU export NETWORK NETMASK BROADCAST DNS DNS1 DNS2 SEARCHDNS export PEERID ONBOOT SUBCHANNELS CTCPROT EOF # immediately read it in again to export these into the shell below . /tmp/install.cfg if [ -z "$testing" ]; then cat /tmp/install.cfg >> /etc/profile fi # testing NETSCRIPTS="/etc/sysconfig/network-scripts" IFCFGFILE="$NETSCRIPTS/ifcfg-$DEVICE" if [ ! -d "$NETSCRIPTS" ]; then mkdir -p $NETSCRIPTS fi # to please NetworkManager on startup in loader before loader reconfigures net cat > /etc/sysconfig/network << EOF HOSTNAME=$HOSTNAME EOF cat > $IFCFGFILE << EOF DEVICE=$DEVICE ONBOOT=yes BOOTPROTO=static GATEWAY=$GATEWAY BROADCAST=$BROADCAST MTU=$MTU SUBCHANNELS=$SUBCHANNELS EOF if [ "$ipv6" ]; then cat >> $IFCFGFILE << EOF IPV6INIT=yes IPV6_AUTOCONF=no IPV6ADDR=$IPADDR/$NETMASK IPV6_DEFAULTGW=$GATEWAY EOF # FIXME: /etc/sysconfig/network:IPV6_DEFAULTGW=$GATEWAY # /etc/sysconfig/network:NETWORKING_IPV6=yes else cat >> $IFCFGFILE << EOF IPADDR=$IPADDR NETMASK=$NETMASK EOF fi [ "$DNS1" != "" ] && echo "DNS1=$DNS1" >> $IFCFGFILE [ "$DNS2" != "" ] && echo "DNS2=$DNS2" >> $IFCFGFILE # colons in SEARCHDNS already replaced with spaces above for /etc/resolv.conf [ "$SEARCHDNS" != "" ] && echo "DOMAIN=\"$SEARCHDNS\"" >> $IFCFGFILE [ "$NETTYPE" != "" ] && echo "NETTYPE=$NETTYPE" >> $IFCFGFILE [ "$PEERID" != "" ] && echo "PEERID=$PEERID" >> $IFCFGFILE [ "$PORTNAME" != "" ] && echo "PORTNAME=$PORTNAME" >> $IFCFGFILE [ "$CTCPROT" != "" ] && echo "CTCPROT=$CTCPROT" >> $IFCFGFILE [ "$MACADDR" != "" ] && echo "MACADDR=$MACADDR" >> $IFCFGFILE optstr="" for option in LAYER2 PORTNO; do [ -z "${!option}" ] && continue [ -n "$optstr" ] && optstr=${optstr}" " optstr=${optstr}$(echo ${option} | tr [[:upper:]] [[:lower:]])"="${!option} done # write single quotes since network.py removes double quotes but we need quotes echo "OPTIONS='$optstr'" >> $IFCFGFILE unset option unset optstr if [ -z "$testing" ]; then # so that the vars get propagated into the sshd shells mkdir /.ssh cat >> /.ssh/environment <> /etc/profile <> /etc/profile fi # I'm tired of typing this out... echo "loader" >> /.bash_history echo -n $$ > /var/run/init.pid # shutdown (halt) on SIGUSR1 trap doshutdown SIGUSR1 # reboot on SIGUSR2 trap doreboot SIGUSR2 startinetd if [ -n "$RUNKS" ]; then /sbin/loader fi doshutdown fi # testing # ;;; Local Variables: *** # ;;; mode: sh *** # ;;; end: ***